Merge "Update all Compose Dependencies to Compose 1.2.1" into androidx-main
diff --git a/ads/ads-identifier-common/build.gradle b/ads/ads-identifier-common/build.gradle
index 3dc9ae3..26cd193 100644
--- a/ads/ads-identifier-common/build.gradle
+++ b/ads/ads-identifier-common/build.gradle
@@ -28,7 +28,7 @@
     testImplementation(libs.testRunner)
     testImplementation(libs.junit)
     testImplementation(libs.truth)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.robolectric)
 }
 
diff --git a/appcompat/appcompat/proguard-rules.pro b/appcompat/appcompat/proguard-rules.pro
index 6f2fc96..64d22a8 100644
--- a/appcompat/appcompat/proguard-rules.pro
+++ b/appcompat/appcompat/proguard-rules.pro
@@ -12,10 +12,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# aapt is not able to read app::actionViewClass and app:actionProviderClass to produce proguard
-# keep rules. Add a commonly used SearchView to the keep list until b/109831488 is resolved.
--keep class androidx.appcompat.widget.SearchView { <init>(...); }
-
 # Never inline methods, but allow shrinking and obfuscation.
 -keepclassmembernames,allowobfuscation,allowshrinking class androidx.appcompat.widget.AppCompatTextViewAutoSizeHelper$Impl* {
   <methods>;
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/LocalesUpdateTestCase.kt b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/LocalesUpdateTestCase.kt
index 2ae4459..5bd08dc 100644
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/LocalesUpdateTestCase.kt
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/app/LocalesUpdateTestCase.kt
@@ -16,11 +16,13 @@
 
 package androidx.appcompat.app
 
+import android.util.LayoutDirection.RTL
 import android.webkit.WebView
 import androidx.appcompat.testutils.LocalesActivityTestRule
 import androidx.appcompat.testutils.LocalesUtils
 import androidx.appcompat.testutils.LocalesUtils.CUSTOM_LOCALE_LIST
 import androidx.appcompat.testutils.LocalesUtils.assertConfigurationLocalesEquals
+import androidx.appcompat.testutils.LocalesUtils.getRTLLocaleList
 import androidx.appcompat.testutils.LocalesUtils.setLocalesAndWait
 import androidx.appcompat.testutils.LocalesUtils.setLocalesAndWaitForRecreate
 import androidx.core.os.LocaleListCompat
@@ -113,4 +115,13 @@
         // Assert that onConfigurationChange was not called
         assertNull(activity.lastConfigurationChangeAndClear)
     }
+
+    @SdkSuppress(minSdkVersion = 17)
+    @Test
+    fun testLayoutDirectionAfterRecreating() {
+        setLocalesAndWaitForRecreate(rule, getRTLLocaleList())
+
+        // Now assert that the layoutDirection of decorView is RTL
+        assertEquals(rule.activity.window.decorView.layoutDirection, RTL)
+    }
 }
diff --git a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/testutils/LocalesUtils.kt b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/testutils/LocalesUtils.kt
index 36dd7ea..5b21295 100644
--- a/appcompat/appcompat/src/androidTest/java/androidx/appcompat/testutils/LocalesUtils.kt
+++ b/appcompat/appcompat/src/androidTest/java/androidx/appcompat/testutils/LocalesUtils.kt
@@ -50,6 +50,14 @@
         }
     }
 
+    fun getRTLLocaleList(): LocaleListCompat {
+        if (Build.VERSION.SDK_INT >= 24) {
+            return LocaleListCompat.forLanguageTags("ar-AE")
+        } else {
+            return LocaleListCompat.create(Locale("ar-AE"))
+        }
+    }
+
     fun assertConfigurationLocalesEquals(
         expectedLocales: LocaleListCompat,
         context: Context
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
index 491a901..7d3ae75 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
@@ -2832,6 +2832,15 @@
                 Log.d(TAG, "updateAppConfiguration attempting to recreate Activity: "
                         + mHost);
             }
+
+            // To workaround the android framework issue(b/242026447) which doesn't update the
+            // layout direction after recreating in Android S.
+            if (Build.VERSION.SDK_INT >= 31
+                    && (configChanges & ActivityInfo.CONFIG_LAYOUT_DIRECTION) != 0) {
+                Api17Impl.setLayoutDirection(
+                        ((Activity) mHost).getWindow().getDecorView(),
+                        Api17Impl.getLayoutDirection(overrideConfig));
+            }
             ActivityCompat.recreate((Activity) mHost);
             handled = true;
         } else if (DEBUG) {
@@ -3933,9 +3942,19 @@
         }
 
         @DoNotInline
+        static void setLayoutDirection(View view, int layoutDirection) {
+            view.setLayoutDirection(layoutDirection);
+        }
+
+        @DoNotInline
         static void setLocale(Configuration configuration, Locale loc) {
             configuration.setLocale(loc);
         }
+
+        @DoNotInline
+        static int getLayoutDirection(Configuration configuration) {
+            return configuration.getLayoutDirection();
+        }
     }
 
     @RequiresApi(21)
diff --git a/arch/core/core-common/build.gradle b/arch/core/core-common/build.gradle
index 57fcfca..450e35a 100644
--- a/arch/core/core-common/build.gradle
+++ b/arch/core/core-common/build.gradle
@@ -25,7 +25,7 @@
     api("androidx.annotation:annotation:1.1.0")
 
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
 }
 
 androidx {
diff --git a/benchmark/benchmark-darwin/src/commonMain/kotlin/androidx/benchmark/darwin/TestCase.kt b/benchmark/benchmark-darwin/src/commonMain/kotlin/androidx/benchmark/darwin/TestCase.kt
index 889b3b9..5a9ec8c 100644
--- a/benchmark/benchmark-darwin/src/commonMain/kotlin/androidx/benchmark/darwin/TestCase.kt
+++ b/benchmark/benchmark-darwin/src/commonMain/kotlin/androidx/benchmark/darwin/TestCase.kt
@@ -20,7 +20,7 @@
     /**
      * Provides an opportunity to customize initial state before a test case begins.
      */
-    abstract fun setUp()
+    open fun setUp() {}
 
     abstract fun benchmark(context: TestCaseContext)
 
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 972ed5f..90fc31d 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
@@ -47,7 +47,7 @@
 import org.junit.runners.Parameterized
 import org.junit.runners.Parameterized.Parameters
 
-private const val tracingPerfettoVersion = "1.0.0-alpha05" // TODO(224510255): get by 'reflection'
+private const val tracingPerfettoVersion = "1.0.0-alpha06" // TODO(224510255): get by 'reflection'
 private const val minSupportedSdk = Build.VERSION_CODES.R // TODO(234351579): Support API < 30
 
 @RunWith(Parameterized::class)
diff --git a/biometric/biometric/build.gradle b/biometric/biometric/build.gradle
index 9b82c05..a67bf13 100644
--- a/biometric/biometric/build.gradle
+++ b/biometric/biometric/build.gradle
@@ -48,7 +48,7 @@
     testImplementation(libs.testCore)
     testImplementation(libs.testRunner)
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.robolectric)
     testImplementation(libs.truth)
 
diff --git a/biometric/biometric/src/test/java/androidx/biometric/AuthenticationCallbackProviderTest.java b/biometric/biometric/src/test/java/androidx/biometric/AuthenticationCallbackProviderTest.java
index 6d44449..8051f34 100644
--- a/biometric/biometric/src/test/java/androidx/biometric/AuthenticationCallbackProviderTest.java
+++ b/biometric/biometric/src/test/java/androidx/biometric/AuthenticationCallbackProviderTest.java
@@ -21,7 +21,6 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.os.Build;
@@ -49,6 +48,7 @@
 
     private AuthenticationCallbackProvider mAuthenticationCallbackProvider;
 
+    @SuppressWarnings("deprecation") // b/251211046
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -102,7 +102,7 @@
         mAuthenticationCallbackProvider.getBiometricCallback()
                 .onAuthenticationHelp(helpCode, helpMessage);
 
-        verifyZeroInteractions(mListener);
+        verifyNoMoreInteractions(mListener);
     }
 
     @Test
diff --git a/biometric/biometric/src/test/java/androidx/biometric/BiometricManagerTest.java b/biometric/biometric/src/test/java/androidx/biometric/BiometricManagerTest.java
index 7dfa19d..7112cb4 100644
--- a/biometric/biometric/src/test/java/androidx/biometric/BiometricManagerTest.java
+++ b/biometric/biometric/src/test/java/androidx/biometric/BiometricManagerTest.java
@@ -55,6 +55,7 @@
 
     private Context mContext;
 
+    @SuppressWarnings("deprecation") // b/251211046
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/biometric/biometric/src/test/java/androidx/biometric/CancellationSignalProviderTest.java b/biometric/biometric/src/test/java/androidx/biometric/CancellationSignalProviderTest.java
index fe7c7be..69c3f64 100644
--- a/biometric/biometric/src/test/java/androidx/biometric/CancellationSignalProviderTest.java
+++ b/biometric/biometric/src/test/java/androidx/biometric/CancellationSignalProviderTest.java
@@ -47,6 +47,7 @@
     private CancellationSignalProvider.Injector mFieldMockInjector;
     private CancellationSignalProvider.Injector mNewMockInjector;
 
+    @SuppressWarnings("deprecation") // b/251211046
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/biometric/biometric/src/test/java/androidx/biometric/CryptoObjectUtilsTest.java b/biometric/biometric/src/test/java/androidx/biometric/CryptoObjectUtilsTest.java
index 2ef9fdf3..7b28b24 100644
--- a/biometric/biometric/src/test/java/androidx/biometric/CryptoObjectUtilsTest.java
+++ b/biometric/biometric/src/test/java/androidx/biometric/CryptoObjectUtilsTest.java
@@ -43,6 +43,7 @@
     @Mock private Mac mMac;
     @Mock private Signature mSignature;
 
+    @SuppressWarnings("deprecation") // b/251211046
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/biometric/biometric/src/test/java/androidx/biometric/KeyguardUtilsTest.java b/biometric/biometric/src/test/java/androidx/biometric/KeyguardUtilsTest.java
index 453f6a9..087c330 100644
--- a/biometric/biometric/src/test/java/androidx/biometric/KeyguardUtilsTest.java
+++ b/biometric/biometric/src/test/java/androidx/biometric/KeyguardUtilsTest.java
@@ -43,6 +43,7 @@
     @Mock private Context mContext;
     @Mock private KeyguardManager mKeyguardManager;
 
+    @SuppressWarnings("deprecation") // b/251211046
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/biometric/biometric/src/test/java/androidx/biometric/PackageUtilsTest.java b/biometric/biometric/src/test/java/androidx/biometric/PackageUtilsTest.java
index de5cebc..6c9e29f 100644
--- a/biometric/biometric/src/test/java/androidx/biometric/PackageUtilsTest.java
+++ b/biometric/biometric/src/test/java/androidx/biometric/PackageUtilsTest.java
@@ -40,6 +40,7 @@
     @Mock private Context mContext;
     @Mock private PackageManager mPackageManager;
 
+    @SuppressWarnings("deprecation") // b/251211046
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/browser/browser/build.gradle b/browser/browser/build.gradle
index 69034ed..31aa32b 100644
--- a/browser/browser/build.gradle
+++ b/browser/browser/build.gradle
@@ -31,7 +31,7 @@
     testImplementation(libs.testRunner)
     testImplementation(libs.junit)
     testImplementation(libs.robolectric)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
 
     androidTestImplementation("androidx.appcompat:appcompat:1.1.0")
     androidTestImplementation(libs.testExtJunit)
diff --git a/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsCallback.java b/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsCallback.java
index b56c6a7..3ac0a3c 100644
--- a/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsCallback.java
+++ b/browser/browser/src/main/java/androidx/browser/customtabs/CustomTabsCallback.java
@@ -161,11 +161,11 @@
             boolean result, @Nullable Bundle extras) {}
 
     /**
-     * To be called when CCT is resized in its height. This is applicable when users resize a CCT
+     * Called when the tab is resized in its height. This is applicable when users resize a tab
      * launched with {@link CustomTabsIntent#ACTIVITY_HEIGHT_ADJUSTABLE} for the {@link
      * CustomTabsIntent#ActivityResizeBehavior}.
      *
-     * @param size The updated size in height.
+     * @param size The updated height in px.
      * @param extras Reserved for future use.
      */
     public void onActivityResized(@Dimension(unit = PX) int size, @NonNull Bundle extras) {}
diff --git a/buildSrc/plugins/src/main/kotlin/androidx/build/AndroidXPlugin.kt b/buildSrc/plugins/src/main/kotlin/androidx/build/AndroidXPlugin.kt
index 9fb87d7..f389d94 100644
--- a/buildSrc/plugins/src/main/kotlin/androidx/build/AndroidXPlugin.kt
+++ b/buildSrc/plugins/src/main/kotlin/androidx/build/AndroidXPlugin.kt
@@ -43,7 +43,7 @@
          */
         @JvmStatic
         fun isPlayground(project: Project): Boolean {
-            return StudioType.isPlayground(project)
+            return ProjectLayoutType.isPlayground(project)
         }
     }
 }
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt
index c5d52ce..6d59c5f 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXComposeImplPlugin.kt
@@ -340,7 +340,7 @@
         // for Playground builds as well
         project.dependencies.add(
             COMPILER_PLUGIN_CONFIGURATION,
-            if (StudioType.isPlayground(project)) {
+            if (ProjectLayoutType.isPlayground(project)) {
                 AndroidXPlaygroundRootImplPlugin.projectOrArtifact(
                     project.rootProject,
                     ":compose:compiler:compiler"
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
index 497f8b5..089addc 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
@@ -148,7 +148,7 @@
     private fun Project.registerProjectOrArtifact() {
         // Add a method for each sub project where they can declare an optional
         // dependency on a project or its latest snapshot artifact.
-        if (!StudioType.isPlayground(this)) {
+        if (!ProjectLayoutType.isPlayground(this)) {
             // In AndroidX build, this is always enforced to the project
             extra.set(
                 PROJECT_OR_ARTIFACT_EXT_NAME,
@@ -257,7 +257,8 @@
                 task.finalizedBy(zipXmlTask)
             }
         }
-        if (!StudioType.isPlayground(project)) { // For non-playground setup use robolectric offline
+        // For non-playground setup, use robolectric offline
+        if (!ProjectLayoutType.isPlayground(project)) {
             task.systemProperty("robolectric.offline", "true")
             val robolectricDependencies =
                 File(
@@ -732,7 +733,7 @@
      * Sets the konan distribution url to the prebuilts directory.
      */
     private fun Project.configureKonanDirectory() {
-        if (StudioType.isPlayground(this)) {
+        if (ProjectLayoutType.isPlayground(this)) {
             return // playground does not use prebuilts
         }
         overrideKotlinNativeDistributionUrlToLocalDirectory()
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt
index 3675e62..672f99b 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXRootImplPlugin.kt
@@ -220,6 +220,10 @@
 
         registerStudioTask()
 
+        if (!ProjectLayoutType.isPlayground(project)) {
+            whenChangingOutputTextValidationMustInvalidateAllTasks()
+        }
+
         TaskUpToDateValidator.setup(project, registry)
 
         project.tasks.register("listTaskOutputs", ListTaskOutputsTask::class.java) { task ->
@@ -228,6 +232,19 @@
         }
     }
 
+    // If our output message validation configuration changes, invalidate all tasks to make sure
+    // all output messages get regenerated and re-validated
+    private fun Project.whenChangingOutputTextValidationMustInvalidateAllTasks() {
+        val configFile = project.file("development/build_log_simplifier/messages.ignore")
+        if (configFile.exists()) {
+            subprojects { subproject ->
+                subproject.tasks.configureEach { task ->
+                    task.inputs.file(configFile)
+                }
+            }
+        }
+    }
+
     private fun Project.setDependencyVersions() {
         androidx.build.dependencies.kotlinVersion = getVersionByName("kotlin")
         androidx.build.dependencies.kotlinNativeVersion = getVersionByName("kotlinNative")
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXKmpDocsImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXKmpDocsImplPlugin.kt
index 3dcc74b..756da39 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXKmpDocsImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXKmpDocsImplPlugin.kt
@@ -18,12 +18,14 @@
 
 import androidx.build.dokka.kmpDocs.DokkaCombinedDocsTask
 import androidx.build.dokka.kmpDocs.DokkaPartialDocsTask
+import androidx.build.getDistributionDirectory
 import javax.inject.Inject
 import org.gradle.api.Plugin
 import org.gradle.api.Project
 import org.gradle.api.artifacts.Configuration
 import org.gradle.api.attributes.LibraryElements
 import org.gradle.api.model.ObjectFactory
+import org.gradle.api.tasks.bundling.Zip
 import org.gradle.kotlin.dsl.named
 import org.gradle.kotlin.dsl.the
 import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
@@ -38,16 +40,28 @@
         val config = project.configurations.register(CONSUMER_CONFIGURATION_NAME) { config ->
             config.configureKmpDocsAttributes(objectFactory)
         }
-        DokkaCombinedDocsTask.register(
+        val combinedDocsTask = DokkaCombinedDocsTask.register(
             project,
             config
         )
+        project.tasks.register(ZIP_COMBINED_DOCS_TASK_NAME, Zip::class.java) {
+            it.from(
+                combinedDocsTask.map { it.outputDir }
+            )
+            it.destinationDirectory.set(
+                project.getDistributionDirectory().resolve("kmp-docs")
+            )
+            it.archiveBaseName.set(
+                "kmpDocs"
+            )
+        }
     }
 
     companion object {
         private const val CONSUMER_CONFIGURATION_NAME = "kmpDocs"
         private const val PRODUCER_CONFIGURATION_NAME = "partialKmpDocs"
         private const val ATTRIBUTE_NAME = "partial-docs"
+        private const val ZIP_COMBINED_DOCS_TASK_NAME = "zipCombinedKmpDocs"
 
         /**
          * Adds ability to generates partial dokka docs artifact for the Kotlin project.
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/dokka/kmpDocs/DokkaCombinedDocsTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/dokka/kmpDocs/DokkaCombinedDocsTask.kt
index ec65ae5..b41071ec 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/dokka/kmpDocs/DokkaCombinedDocsTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/dokka/kmpDocs/DokkaCombinedDocsTask.kt
@@ -139,9 +139,6 @@
             classpath = dokkaCliClasspath.get(),
             inputJson = docsJsonOutput.get().asFile
         )
-        logger.lifecycle(
-            "Written combined docs to $outputDir"
-        )
     }
 
     companion object {
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/dokka/kmpDocs/DokkaInputModels.kt b/buildSrc/private/src/main/kotlin/androidx/build/dokka/kmpDocs/DokkaInputModels.kt
index 9e1c3eee..4926781 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/dokka/kmpDocs/DokkaInputModels.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/dokka/kmpDocs/DokkaInputModels.kt
@@ -162,7 +162,7 @@
             /**
              * File name used when this is serialized into a gradle artifact.
              */
-            val FILE_NAME = "androidXPartialDocsMetadata.json"
+            const val FILE_NAME = "androidXPartialDocsMetadata.json"
         }
     }
 }
\ No newline at end of file
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/dokka/kmpDocs/DokkaPartialDocsTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/dokka/kmpDocs/DokkaPartialDocsTask.kt
index 4afaac2..30441be 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/dokka/kmpDocs/DokkaPartialDocsTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/dokka/kmpDocs/DokkaPartialDocsTask.kt
@@ -32,15 +32,16 @@
 import org.gradle.api.file.DirectoryProperty
 import org.gradle.api.file.FileCollection
 import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.provider.ListProperty
 import org.gradle.api.provider.Property
 import org.gradle.api.tasks.CacheableTask
 import org.gradle.api.tasks.Classpath
+import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.Internal
 import org.gradle.api.tasks.Nested
 import org.gradle.api.tasks.OutputDirectory
 import org.gradle.api.tasks.TaskAction
 import org.gradle.api.tasks.TaskProvider
-import org.gradle.kotlin.dsl.the
 import org.gradle.workers.WorkerExecutor
 import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
 import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
@@ -84,17 +85,26 @@
     @get:OutputDirectory
     abstract val outputDir: DirectoryProperty
 
+    @get:Input
+    abstract val moduleName: Property<String>
+
+    @get:Input
+    abstract val uniqueArtifactKey: Property<String>
+
+    @get:Nested
+    abstract val sourceSets: ListProperty<SourceSet>
+
     /**
      * The parameters we pass into dokka, also defining the inputs of this task.
      */
     @get:Nested
     val dokkaCliInput by lazy {
         PartialDocsInput(
-            moduleName = project.name,
+            moduleName = moduleName.get(),
             outputDir = outputDir.asFile.get(),
             pluginsClasspath = pluginsClasspath.get(),
             globalLinks = buildExternalDocLinks(project),
-            sourceSets = buildSourceSets(),
+            sourceSets = sourceSets.get(),
             pluginsConfiguration = listOf(
                 PluginsConfiguration.ANDROIDX_COPYRIGHT
             )
@@ -122,88 +132,16 @@
             gson.toJson(
                 DokkaInputModels.PartialDocsMetadata(
                     moduleName = dokkaCliInput.moduleName,
-                    artifactKey = project.group.toString() + "/" + project.name
+                    artifactKey = uniqueArtifactKey.get()
                 )
             )
         )
-        logger.lifecycle(
-            "Written partial docs to $outputDir"
-        )
-    }
-
-    private fun buildSourceSets(): List<SourceSet> {
-        val targets = when (val kotlinExtension = project.the<KotlinProjectExtension>()) {
-            is KotlinSingleTargetExtension<*> -> listOf(kotlinExtension.target)
-            is KotlinMultiplatformExtension -> kotlinExtension.targets
-            else -> error("unsupported kotlin extension")
-        }
-
-        // Find source sets that are used in compilations that we are interested in.
-        // Also associate them with the classpath of the compilation to be able to resolve
-        // dependencies.
-        val sourceSets = IdentityHashMap<KotlinSourceSet, DokkaSourceSetInput>()
-        targets.forEach { target ->
-            val platform = target.docsPlatform()
-            val compilation =
-                target.compilations.findByName(KotlinCompilation.MAIN_COMPILATION_NAME)
-                    // Android projects use "debug"
-                    ?: target.compilations.findByName("debug")
-            compilation?.allKotlinSourceSets?.forEach { kotlinSourceSet ->
-                val existing = sourceSets.getOrPut(kotlinSourceSet) {
-                    DokkaSourceSetInput(
-                        platform = platform,
-                        classpath = project.files()
-                    )
-                }
-                existing.platform += platform
-
-                val additionalClasspath =
-                    if (compilation.target.platformType == KotlinPlatformType.androidJvm) {
-                        // This is a workaround for https://youtrack.jetbrains.com/issue/KT-33893
-                        @Suppress("DEPRECATION") // for compatibility
-                        (compilation.compileKotlinTask as
-                            org.jetbrains.kotlin.gradle.tasks.KotlinCompile).classpath
-                    } else {
-                        compilation.compileDependencyFiles
-                    }
-                existing.classpath.from(
-                    additionalClasspath
-                )
-            }
-        }
-
-        return sourceSets.map { (sourceSet, docsPlatform) ->
-            val sourceDirectories = sourceSet.kotlin.sourceDirectories.filter {
-                it.exists()
-            }
-            SourceSet(
-                displayName = sourceSet.displayName(),
-                id = sourceSet.dokkaId(),
-                classpath = docsPlatform.classpath,
-                sourceRoots = sourceDirectories,
-                analysisPlatform = docsPlatform.platform.jsonName,
-                noStdlibLink = false,
-                noJdkLink = !docsPlatform.platform.androidOrJvm(),
-                noAndroidSdkLink = docsPlatform.platform != DokkaAnalysisPlatform.ANDROID,
-                dependentSourceSets = sourceSet.dependsOn.map {
-                    it.dokkaId()
-                },
-                externalDocumentationLinks = buildExternalDocLinks(project),
-                sourceLinks = sourceDirectories.map {
-                    SrcLink(
-                        localDirectory = it,
-                        remoteUrl = CS_ANDROID_SRC_ROOT +
-                            it.relativeTo(project.getSupportRootFolder()).path
-                    )
-                }
-            )
-        }
     }
 
     companion object {
         private const val TASK_NAME = "generateKmpDocs"
 
-        private val CS_ANDROID_SRC_ROOT =
+        private const val CS_ANDROID_SRC_ROOT =
             "https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:"
 
         private fun buildExternalDocLinks(project: Project): List<GlobalDocsLink> {
@@ -243,10 +181,6 @@
          */
         fun register(
             project: Project,
-            // Task requires it but we cannot pass it as a parameter to the task because it breaks
-            // the configuration cache. Hence, we require it to call register but don't use it
-            // inside the method. ¯\_(ツ)_/¯
-            @Suppress("UNUSED_PARAMETER")
             kotlinProjectExtension: KotlinProjectExtension
         ): TaskProvider<DokkaPartialDocsTask> {
             // these configurations are created eagerly due to
@@ -261,6 +195,19 @@
                 TASK_NAME,
                 DokkaPartialDocsTask::class.java
             ) { docsTask ->
+                docsTask.sourceSets.set(
+                    project.provider {
+                        buildSourceSets(project, kotlinProjectExtension)
+                    }
+                )
+                docsTask.moduleName.set(
+                    project.name
+                )
+                docsTask.uniqueArtifactKey.set(
+                    project.provider {
+                        project.group.toString() + "/" + project.name
+                    }
+                )
                 docsTask.dokkaCliClasspath.set(
                     cliJarConfiguration
                 )
@@ -275,55 +222,129 @@
                 )
             }
         }
-    }
 
-    private enum class DokkaAnalysisPlatform(val jsonName: String) {
-        JVM("jvm"),
-        ANDROID("jvm"), // intentionally same as JVM as dokka only support jvm
-        JS("js"),
-        NATIVE("native"),
-        COMMON("common");
+        private fun buildSourceSets(
+            project: Project,
+            kotlinExtension: KotlinProjectExtension
+        ): List<SourceSet> {
+            val targets = when (kotlinExtension) {
+                is KotlinSingleTargetExtension<*> -> listOf(kotlinExtension.target)
+                is KotlinMultiplatformExtension -> kotlinExtension.targets
+                else -> error("unsupported kotlin extension")
+            }
 
-        fun androidOrJvm() = this == JVM || this == ANDROID
-    }
+            // Find source sets that are used in compilations that we are interested in.
+            // Also associate them with the classpath of the compilation to be able to resolve
+            // dependencies.
+            val sourceSets = IdentityHashMap<KotlinSourceSet, DokkaSourceSetInput>()
+            targets.forEach { target ->
+                val platform = target.docsPlatform()
+                val compilation =
+                    target.compilations.findByName(KotlinCompilation.MAIN_COMPILATION_NAME)
+                    // Android projects use "debug"
+                        ?: target.compilations.findByName("debug")
+                compilation?.allKotlinSourceSets?.forEach { kotlinSourceSet ->
+                    val existing = sourceSets.getOrPut(kotlinSourceSet) {
+                        DokkaSourceSetInput(
+                            platform = platform,
+                            classpath = project.files()
+                        )
+                    }
+                    existing.platform += platform
 
-    private class DokkaSourceSetInput(
-        var platform: DokkaAnalysisPlatform,
-        val classpath: ConfigurableFileCollection
-    )
+                    val additionalClasspath =
+                        if (compilation.target.platformType == KotlinPlatformType.androidJvm) {
+                            // This is a workaround for https://youtrack.jetbrains.com/issue/KT-33893
+                            @Suppress("DEPRECATION") // for compatibility
+                            (compilation.compileKotlinTask as
+                                org.jetbrains.kotlin.gradle.tasks.KotlinCompile).classpath
+                        } else {
+                            compilation.compileDependencyFiles
+                        }
+                    existing.classpath.from(
+                        additionalClasspath
+                    )
+                }
+            }
 
-    private fun KotlinSourceSet.dokkaId() = SourceSetId(
-        sourceSetName = name,
-        scopeId = [email protected]
-    )
-
-    private fun KotlinSourceSet.displayName() = if (name.endsWith("Main")) {
-        name.substringBeforeLast("Main")
-    } else {
-        name
-    }
-
-    private fun KotlinTarget.docsPlatform() = when (platformType) {
-        KotlinPlatformType.common -> DokkaAnalysisPlatform.COMMON
-        KotlinPlatformType.jvm -> DokkaAnalysisPlatform.JVM
-        KotlinPlatformType.js -> DokkaAnalysisPlatform.JS
-        KotlinPlatformType.wasm -> DokkaAnalysisPlatform.JS
-        KotlinPlatformType.androidJvm -> DokkaAnalysisPlatform.ANDROID
-        KotlinPlatformType.native -> DokkaAnalysisPlatform.NATIVE
-    }
-
-    private operator fun DokkaAnalysisPlatform?.plus(
-        other: DokkaAnalysisPlatform
-    ): DokkaAnalysisPlatform {
-        if (this == null) {
-            return other
+            return sourceSets.map { (sourceSet, docsPlatform) ->
+                val sourceDirectories = sourceSet.kotlin.sourceDirectories.filter {
+                    it.exists()
+                }
+                SourceSet(
+                    displayName = sourceSet.displayName(),
+                    id = sourceSet.dokkaId(project),
+                    classpath = docsPlatform.classpath,
+                    sourceRoots = sourceDirectories,
+                    analysisPlatform = docsPlatform.platform.jsonName,
+                    noStdlibLink = false,
+                    noJdkLink = !docsPlatform.platform.androidOrJvm(),
+                    noAndroidSdkLink = docsPlatform.platform != DokkaAnalysisPlatform.ANDROID,
+                    dependentSourceSets = sourceSet.dependsOn.map {
+                        it.dokkaId(project)
+                    }.sortedBy { it.sourceSetName },
+                    externalDocumentationLinks = buildExternalDocLinks(project),
+                    sourceLinks = sourceDirectories.map {
+                        SrcLink(
+                            localDirectory = it,
+                            remoteUrl = CS_ANDROID_SRC_ROOT +
+                                it.relativeTo(project.getSupportRootFolder()).path
+                        )
+                    }.sortedBy {
+                        it.localDirectory
+                    }
+                )
+            }.sortedBy { it.displayName }
         }
-        if (this == other) {
-            return this
-        }
-        if (this.androidOrJvm() && other.androidOrJvm()) {
-            return DokkaAnalysisPlatform.JVM
-        }
-        return DokkaAnalysisPlatform.COMMON
     }
+}
+
+private enum class DokkaAnalysisPlatform(val jsonName: String) {
+    JVM("jvm"),
+    ANDROID("jvm"), // intentionally same as JVM as dokka only support jvm
+    JS("js"),
+    NATIVE("native"),
+    COMMON("common");
+
+    fun androidOrJvm() = this == JVM || this == ANDROID
+}
+
+private class DokkaSourceSetInput(
+    var platform: DokkaAnalysisPlatform,
+    val classpath: ConfigurableFileCollection
+)
+
+private fun KotlinSourceSet.dokkaId(project: Project) = SourceSetId(
+    sourceSetName = name,
+    scopeId = project.path
+)
+
+private fun KotlinSourceSet.displayName() = if (name.endsWith("Main")) {
+    name.substringBeforeLast("Main")
+} else {
+    name
+}
+
+private fun KotlinTarget.docsPlatform() = when (platformType) {
+    KotlinPlatformType.common -> DokkaAnalysisPlatform.COMMON
+    KotlinPlatformType.jvm -> DokkaAnalysisPlatform.JVM
+    KotlinPlatformType.js -> DokkaAnalysisPlatform.JS
+    KotlinPlatformType.wasm -> DokkaAnalysisPlatform.JS
+    KotlinPlatformType.androidJvm -> DokkaAnalysisPlatform.ANDROID
+    KotlinPlatformType.native -> DokkaAnalysisPlatform.NATIVE
+}
+
+private operator fun DokkaAnalysisPlatform?.plus(
+    other: DokkaAnalysisPlatform
+): DokkaAnalysisPlatform {
+    if (this == null) {
+        return other
+    }
+    if (this == other) {
+        return this
+    }
+    if (this.androidOrJvm() && other.androidOrJvm()) {
+        return DokkaAnalysisPlatform.JVM
+    }
+    return DokkaAnalysisPlatform.COMMON
 }
\ No newline at end of file
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/dokka/kmpDocs/DokkaWorkAction.kt b/buildSrc/private/src/main/kotlin/androidx/build/dokka/kmpDocs/DokkaWorkAction.kt
index acb005e..002f029 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/dokka/kmpDocs/DokkaWorkAction.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/dokka/kmpDocs/DokkaWorkAction.kt
@@ -16,8 +16,10 @@
 
 package androidx.build.dokka.kmpDocs
 
+import java.io.ByteArrayOutputStream
 import java.io.File
 import javax.inject.Inject
+import org.gradle.api.GradleException
 import org.gradle.api.file.ConfigurableFileCollection
 import org.gradle.api.file.FileCollection
 import org.gradle.api.file.RegularFileProperty
@@ -51,9 +53,16 @@
     private val execOperations: ExecOperations
 ) : WorkAction<DokkaParams> {
     override fun execute() {
-        execOperations.javaexec {
+        val outputStream = ByteArrayOutputStream()
+        val errorStream = ByteArrayOutputStream()
+        val result = execOperations.javaexec {
+            it.standardOutput = outputStream
+            it.errorOutput = errorStream
             it.classpath = parameters.classpath
             it.args(parameters.inputFile.get().asFile.canonicalPath)
         }
+        if (result.exitValue != 0) {
+            throw GradleException("Failed to run Dokka.\n ${errorStream.toString(Charsets.UTF_8)}")
+        }
     }
 }
\ No newline at end of file
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/studio/StudioTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/studio/StudioTask.kt
index 43d28eb..ec77a46 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/studio/StudioTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/studio/StudioTask.kt
@@ -16,7 +16,7 @@
 
 package androidx.build.studio
 
-import androidx.build.StudioType
+import androidx.build.ProjectLayoutType
 import androidx.build.getSupportRootFolder
 import androidx.build.getVersionByName
 import com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
@@ -280,9 +280,9 @@
         private const val STUDIO_TASK = "studio"
 
         fun Project.registerStudioTask() {
-            val studioTask = when (StudioType.from(this)) {
-                StudioType.ANDROIDX -> RootStudioTask::class.java
-                StudioType.PLAYGROUND -> PlaygroundStudioTask::class.java
+            val studioTask = when (ProjectLayoutType.from(this)) {
+                ProjectLayoutType.ANDROIDX -> RootStudioTask::class.java
+                ProjectLayoutType.PLAYGROUND -> PlaygroundStudioTask::class.java
             }
             tasks.register(STUDIO_TASK, studioTask)
         }
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/Multiplatform.kt b/buildSrc/public/src/main/kotlin/androidx/build/Multiplatform.kt
index 6fb9adb..326c96f 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/Multiplatform.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/Multiplatform.kt
@@ -40,7 +40,7 @@
         @JvmStatic
         fun isKotlinNativeEnabled(project: Project): Boolean {
             return "KMP".equals(System.getenv()["ANDROIDX_PROJECTS"], ignoreCase = true) ||
-                StudioType.isPlayground(project) ||
+                ProjectLayoutType.isPlayground(project) ||
                 project.providers.gradleProperty("androidx.kmp.native.enabled")
                     .orNull?.toBoolean() == true
         }
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/StudioType.kt b/buildSrc/public/src/main/kotlin/androidx/build/ProjectLayoutType.kt
similarity index 75%
rename from buildSrc/public/src/main/kotlin/androidx/build/StudioType.kt
rename to buildSrc/public/src/main/kotlin/androidx/build/ProjectLayoutType.kt
index a9c1f7d..3048823 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/StudioType.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/ProjectLayoutType.kt
@@ -18,20 +18,20 @@
 
 import org.gradle.api.Project
 
-enum class StudioType {
+enum class ProjectLayoutType {
     ANDROIDX,
     PLAYGROUND;
 
     companion object {
         /**
-         * Returns the Studio type for the project's studio task
+         * Returns the project layout type for the project (PLAYGROUND or ANDROIDX)
          */
         @JvmStatic
-        fun from(project: Project): StudioType {
+        fun from(project: Project): ProjectLayoutType {
             val value = project.findProperty(STUDIO_TYPE)?.toString()
             return when (value) {
-                "playground" -> StudioType.PLAYGROUND
-                null, "androidx" -> StudioType.ANDROIDX
+                "playground" -> ProjectLayoutType.PLAYGROUND
+                null, "androidx" -> ProjectLayoutType.ANDROIDX
                 else -> error("Invalid project type $value")
             }
         }
@@ -41,7 +41,7 @@
          */
         @JvmStatic
         fun isPlayground(project: Project): Boolean {
-            return StudioType.from(project) == StudioType.PLAYGROUND
+            return ProjectLayoutType.from(project) == ProjectLayoutType.PLAYGROUND
         }
     }
 }
diff --git a/busytown/androidx-native-mac.sh b/busytown/androidx-native-mac.sh
index f8c8376..b88496d 100755
--- a/busytown/androidx-native-mac.sh
+++ b/busytown/androidx-native-mac.sh
@@ -7,7 +7,7 @@
 # disable GCP cache, these machines don't have credentials.
 export USE_ANDROIDX_REMOTE_BUILD_CACHE=false
 
-impl/build.sh buildOnServer --no-configuration-cache
+impl/build.sh buildOnServer :docs-kmp:zipCombinedKmpDocs --no-configuration-cache
 
 # run a separate createArchive task to prepare a repository
 # folder in DIST.
diff --git a/camera/camera-camera2-pipe-integration/build.gradle b/camera/camera-camera2-pipe-integration/build.gradle
index 3272217..798dd71 100644
--- a/camera/camera-camera2-pipe-integration/build.gradle
+++ b/camera/camera-camera2-pipe-integration/build.gradle
@@ -59,7 +59,7 @@
     testImplementation(libs.testRunner)
     testImplementation(libs.junit)
     testImplementation(libs.truth)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.robolectric)
     testImplementation(libs.kotlinCoroutinesTest)
     testImplementation(project(":camera:camera-camera2-pipe-testing"))
diff --git a/camera/camera-camera2/build.gradle b/camera/camera-camera2/build.gradle
index 190ef69..8f89ced 100644
--- a/camera/camera-camera2/build.gradle
+++ b/camera/camera-camera2/build.gradle
@@ -38,7 +38,7 @@
     testImplementation(libs.junit)
     testImplementation(libs.truth)
     testImplementation(libs.robolectric)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.kotlinCoroutinesTest)
     testImplementation("androidx.annotation:annotation-experimental:1.1.0")
     testImplementation("androidx.concurrent:concurrent-futures-ktx:1.1.0")
diff --git a/camera/camera-core/build.gradle b/camera/camera-core/build.gradle
index de3bb7b..4a8647b 100644
--- a/camera/camera-core/build.gradle
+++ b/camera/camera-core/build.gradle
@@ -46,7 +46,7 @@
     testImplementation(libs.junit)
     testImplementation(libs.truth)
     testImplementation(libs.robolectric)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(project(":camera:camera-testing"), {
         exclude group: "androidx.camera", module: "camera-core"
     })
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/CameraEffect.java b/camera/camera-core/src/main/java/androidx/camera/core/CameraEffect.java
index 4d7c8ea..ce8cf2b 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/CameraEffect.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/CameraEffect.java
@@ -15,7 +15,6 @@
  */
 package androidx.camera.core;
 
-import static androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor;
 import static androidx.core.util.Preconditions.checkState;
 
 import android.os.Build;
@@ -81,15 +80,22 @@
     private final Executor mProcessorExecutor;
     @Nullable
     private final SurfaceProcessor mSurfaceProcessor;
+    @Nullable
+    private final ImageProcessor mImageProcessor;
 
     /**
-     * Private constructor as a workaround to allow @Nullable annotation on final fields.
+     * @param targets           the target {@link UseCase} to which this effect should be applied.
+     * @param processorExecutor the {@link Executor} on which the processor will be invoked.
+     * @param imageProcessor    a {@link ImageProcessor} implementation.
      */
-    @SuppressWarnings("UnusedMethod") // TODO: remove once we add {@link ImageProcessor}.
-    private CameraEffect(@Targets int targets) {
+    protected CameraEffect(
+            @Targets int targets,
+            @NonNull Executor processorExecutor,
+            @NonNull ImageProcessor imageProcessor) {
         mTargets = targets;
-        mProcessorExecutor = mainThreadExecutor();
+        mProcessorExecutor = processorExecutor;
         mSurfaceProcessor = null;
+        mImageProcessor = imageProcessor;
     }
 
     /**
@@ -104,6 +110,7 @@
         mTargets = targets;
         mProcessorExecutor = processorExecutor;
         mSurfaceProcessor = surfaceProcessor;
+        mImageProcessor = null;
     }
 
     /**
@@ -135,6 +142,16 @@
     }
 
     /**
+     * Gets the {@link ImageProcessor} associated with this effect.
+     *
+     * <p>This method returns the value set via {@link Builder#setImageProcessor}.
+     */
+    @Nullable
+    public ImageProcessor getImageProcessor() {
+        return mImageProcessor;
+    }
+
+    /**
      * Builder class for {@link CameraEffect}.
      */
     public static class Builder {
@@ -144,6 +161,8 @@
         private Executor mProcessorExecutor;
         @Nullable
         private SurfaceProcessor mSurfaceProcessor;
+        @Nullable
+        private ImageProcessor mImageProcessor;
 
         /**
          * @param targets the target {@link UseCase} of the Effect. e.g. if the
@@ -161,6 +180,9 @@
          * {@link SurfaceProcessor} on the {@link Executor}, and deliver the processed output
          * frames to the app.
          *
+         * <p>Only one processor can be set via {@code #setImageProcessor()} /
+         * {@code #setSurfaceProcessor}, or the {@link #build()} call will throw error.
+         *
          * @param executor  on which the {@link SurfaceProcessor} will be invoked.
          * @param processor the post processor to be injected into CameraX pipeline.
          */
@@ -173,6 +195,27 @@
         }
 
         /**
+         * Sets a {@link ImageProcessor} for the effect.
+         *
+         * <p>Once the effect is active, CameraX will send original camera frames to the
+         * {@link ImageProcessor} on the {@link Executor}, and deliver the processed output
+         * frames to the app.
+         *
+         * <p>Only one processor can be set via {@code #setImageProcessor()} /
+         * {@code #setSurfaceProcessor}, or the {@link #build()} call will throw error.
+         *
+         * @param executor  on which the {@link ImageProcessor} will be invoked.
+         * @param processor the post processor to be injected into CameraX pipeline.
+         */
+        @NonNull
+        public Builder setImageProcessor(@NonNull Executor executor,
+                @NonNull ImageProcessor processor) {
+            mProcessorExecutor = executor;
+            mImageProcessor = processor;
+            return this;
+        }
+
+        /**
          * Builds a {@link CameraEffect} instance.
          *
          * <p>CameraX supports a selected set of configuration/processor combinations. This method
@@ -181,9 +224,14 @@
          */
         @NonNull
         public CameraEffect build() {
-            checkState(mProcessorExecutor != null && mSurfaceProcessor != null,
-                    "Must set a processor.");
-            return new CameraEffect(mTargets, mProcessorExecutor, mSurfaceProcessor);
+            checkState(mProcessorExecutor != null, "Must have a executor");
+            checkState(mImageProcessor != null ^ mSurfaceProcessor != null,
+                    "Must have one and only one processor");
+            if (mSurfaceProcessor != null) {
+                return new CameraEffect(mTargets, mProcessorExecutor, mSurfaceProcessor);
+            } else {
+                return new CameraEffect(mTargets, mProcessorExecutor, mImageProcessor);
+            }
         }
     }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageProcessor.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageProcessor.java
index 03d7ef7..3ac9d76 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageProcessor.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageProcessor.java
@@ -16,14 +16,129 @@
 
 package androidx.camera.core;
 
+import android.graphics.PixelFormat;
+
+import androidx.annotation.AnyThread;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 
+import java.util.List;
+import java.util.concurrent.Executor;
+
 /**
- * Interface for injecting a {@link ImageProxy}-based post-processing effect into CameraX.
+ * Interface for injecting a {@link ImageProxy} effect into CameraX.
+ *
+ * <p>Implement {@link ImageProcessor} to inject an effect into CameraX pipeline. For example, to
+ * edit the {@link ImageCapture} result, add a {@link CameraEffect} with the
+ * {@link ImageProcessor} targeting {@link CameraEffect#IMAGE_CAPTURE}. Once injected,
+ * {@link ImageCapture} forwards camera frames to the implementation, and delivers the processed
+ * frames to the app.
+ *
+ * <p>Code sample for creating a {@link ImageCapture} object:
+ * <pre><code>
+ * class ImageEffect implements CameraEffect {
+ *     ImageEffect(Executor executor, ImageProcessor imageProcessorImpl) {
+ *         super(IMAGE_CAPTURE, executor, imageProcessorImpl);
+ *     }
+ * }
+ * </code></pre>
+ *
+ * <p>Code sample for injecting the effect into CameraX pipeline:
+ * <pre><code>
+ * UseCaseGroup useCaseGroup = UseCaseGroup.Builder()
+ *         .addUseCase(imageCapture)
+ *         .addEffect(new ImageEffect())
+ *         .build();
+ * cameraProvider.bindToLifecycle(lifecycleOwner, cameraFilter, useCaseGroup);
+ * </code></pre>
  *
  * @hide
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public interface ImageProcessor {
-    // TODO(b/229629890): create the public interface for post-processing images.
+
+    /**
+     * Accepts original frames from CameraX and returns processed frames.
+     *
+     * <p>CameraX invokes this method for each batch of images from the camera. It's invoked on the
+     * {@link Executor} provided in {@link CameraEffect}'s constructor. It might be called in
+     * parallel, should the {@link Executor} allow multi-threading. The implementation must block
+     * the current calling thread until the output image is returned.
+     *
+     * <p>The implementation must follow the instruction in the {@link Request} to process the
+     * image. For example, it must produce an output image with the format following the JavaDoc of
+     * {@link Request#getInputImages()}. Failing to do so might cause the processing to
+     * fail. For example, for {@link ImageCapture}, when the processing fails, the app will
+     * receive a {@link ImageCapture.OnImageSavedCallback#onError} or
+     * {@link ImageCapture.OnImageCapturedCallback#onError} callback.
+     *
+     * <p>The implementation should throw exceptions if it runs into any unrecoverable errors.
+     * CameraX will catch the error and deliver it to the app via the error callbacks.
+     *
+     * @param request a {@link Request} that contains original images.
+     * @return a {@link Response} that contains processed image.
+     */
+    @NonNull
+    Response process(@NonNull Request request);
+
+    /**
+     * A request for processing one or many {@link ImageProxy}.
+     */
+    interface Request {
+
+        /**
+         * Gets the input images from Camera.
+         *
+         * <p>It may return a single image captured by the camera, or multiple images from a
+         * burst of capture depending on the configuration in {@link CameraEffect}.
+         *
+         * <p>Currently this method only returns a single image.
+         *
+         * @return one or many input images.
+         */
+        @NonNull
+        @AnyThread
+        List<ImageProxy> getInputImages();
+
+        /**
+         * Gets the output image format.
+         *
+         * <p>The {@link Response}'s {@link ImageProxy} must follow the instruction in this
+         * JavaDoc, or CameraX may throw error.
+         *
+         * <p>For {@link PixelFormat#RGBA_8888}, the output image must contain a single plane
+         * with a pixel stride of 4 and a row stride of width * 4. e.g. each pixel is stored on 4
+         * bytes and each RGBA channel is stored with 8 bits of precision. For more details, see the
+         * JavaDoc of {@code Bitmap.Config#ARGB_8888}.
+         *
+         * <p>Currently this method only returns {@link PixelFormat#RGBA_8888}.
+         */
+        @AnyThread
+        int getOutputFormat();
+    }
+
+    /**
+     * A response for injecting an {@link ImageProxy} back to CameraX.
+     */
+    interface Response {
+
+        /**
+         * Gets the output image of the {@link ImageProcessor}
+         *
+         * <p>{@link ImageProcessor} should implement the {@link ImageProxy} and
+         * {@link ImageProxy.PlaneProxy} interfaces to create the {@link ImageProxy} instance.
+         * CameraX will inject the image back to the processing pipeline.
+         *
+         * <p>The {@link ImageProxy} must follow the instruction in the request, or CameraX may
+         * throw error. For example, the format must match the value of
+         * {@link Request#getOutputFormat()}, and the pixel stride must match the description for
+         * that format in {@link Request#getOutputFormat()}'s JavaDoc.
+         *
+         * @return the output image.
+         */
+        @Nullable
+        @AnyThread
+        ImageProxy getOutputImage();
+    }
 }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/RgbaImageProxy.java b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/RgbaImageProxy.java
new file mode 100644
index 0000000..ca9c8fc3
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/imagecapture/RgbaImageProxy.java
@@ -0,0 +1,278 @@
+/*
+ * 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.camera.core.imagecapture;
+
+import static androidx.camera.core.internal.utils.ImageUtil.DEFAULT_RGBA_PIXEL_STRIDE;
+import static androidx.camera.core.internal.utils.ImageUtil.createBitmapFromPlane;
+import static androidx.camera.core.internal.utils.ImageUtil.createDirectByteBuffer;
+import static androidx.core.util.Preconditions.checkState;
+
+import static java.util.Objects.requireNonNull;
+
+import android.graphics.Bitmap;
+import android.graphics.Matrix;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.media.Image;
+import android.os.Build;
+
+import androidx.annotation.GuardedBy;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
+import androidx.camera.core.ExperimentalGetImage;
+import androidx.camera.core.ImageInfo;
+import androidx.camera.core.ImageProxy;
+import androidx.camera.core.impl.TagBundle;
+import androidx.camera.core.impl.utils.ExifData;
+import androidx.camera.core.processing.Packet;
+
+import java.nio.ByteBuffer;
+
+/**
+ * A {@link ImageProxy} that is backed by a RGBA_8888 ByteBuffer.
+ *
+ * <p> This class is backed by a single {@link ByteBuffer}. The bytes are stored following the
+ * {@link Bitmap.Config#ARGB_8888}.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+public final class RgbaImageProxy implements ImageProxy {
+
+    private final Object mLock = new Object();
+
+    private final int mWidth;
+
+    private final int mHeight;
+
+    @NonNull
+    private final Rect mCropRect;
+
+    // Null if the ImageProxy is closed. Otherwise non-null.
+    @GuardedBy("mLock")
+    @Nullable
+    PlaneProxy[] mPlaneProxy;
+
+    @NonNull
+    private final ImageInfo mImageInfo;
+
+    /**
+     * Constructs the object from a {@link Packet}.
+     *
+     * <p>The wrapped {@link Bitmap} must be {@link Bitmap.Config#ARGB_8888}.
+     */
+    public RgbaImageProxy(@NonNull Packet<Bitmap> packet) {
+        this(packet.getData(),
+                packet.getCropRect(),
+                packet.getRotationDegrees(), packet.getSensorToBufferTransform(),
+                packet.getCameraCaptureResult().getTimestamp());
+    }
+
+    /**
+     * Constructs the object from a {@link Bitmap} and metadata.
+     *
+     * <p>The {@link Bitmap} must be {@link Bitmap.Config#ARGB_8888}.
+     */
+    @VisibleForTesting
+    RgbaImageProxy(@NonNull Bitmap bitmap, @NonNull Rect cropRect, int rotationDegrees,
+            @NonNull Matrix sensorToBuffer, long timestamp) {
+        this(createDirectByteBuffer(bitmap),
+                DEFAULT_RGBA_PIXEL_STRIDE,
+                bitmap.getWidth(),
+                bitmap.getHeight(),
+                cropRect,
+                rotationDegrees,
+                sensorToBuffer,
+                timestamp);
+    }
+
+    /**
+     * Constructs the object from a {@link ByteBuffer} and metadata.
+     *
+     * <p>The data {@link ByteBuffer} must has a pixel stride of 4 and a row stride of width * 4.
+     * Each pixel is stored in the order of R, G, B and A.For more details, see the JavaDoc of
+     * {@code Bitmap.Config#ARGB_8888}.
+     */
+    public RgbaImageProxy(@NonNull ByteBuffer byteBuffer, int pixelStride,
+            int width, int height, @NonNull Rect cropRect,
+            int rotationDegrees, @NonNull Matrix sensorToBuffer, long timestamp) {
+        mWidth = width;
+        mHeight = height;
+        mCropRect = cropRect;
+        mImageInfo = createImageInfo(timestamp, rotationDegrees, sensorToBuffer);
+        byteBuffer.rewind();
+        mPlaneProxy = new PlaneProxy[]{
+                createPlaneProxy(byteBuffer, width * pixelStride, pixelStride)
+        };
+    }
+
+    @Override
+    public void close() {
+        synchronized (mLock) {
+            checkNotClosed();
+            // Nullify so it can be GCed.
+            mPlaneProxy = null;
+        }
+    }
+
+    @NonNull
+    @Override
+    public Rect getCropRect() {
+        synchronized (mLock) {
+            checkNotClosed();
+            return mCropRect;
+        }
+    }
+
+    @Override
+    public void setCropRect(@Nullable Rect rect) {
+        synchronized (mLock) {
+            checkNotClosed();
+            if (rect != null) {
+                mCropRect.set(rect);
+            }
+        }
+    }
+
+    @Override
+    public int getFormat() {
+        synchronized (mLock) {
+            checkNotClosed();
+            return PixelFormat.RGBA_8888;
+        }
+    }
+
+    @Override
+    public int getHeight() {
+        synchronized (mLock) {
+            checkNotClosed();
+            return mHeight;
+        }
+    }
+
+    @Override
+    public int getWidth() {
+        synchronized (mLock) {
+            checkNotClosed();
+            return mWidth;
+        }
+    }
+
+    @NonNull
+    @Override
+    public PlaneProxy[] getPlanes() {
+        synchronized (mLock) {
+            checkNotClosed();
+            return requireNonNull(mPlaneProxy);
+        }
+    }
+
+    @NonNull
+    @Override
+    public ImageInfo getImageInfo() {
+        synchronized (mLock) {
+            checkNotClosed();
+            return mImageInfo;
+        }
+    }
+
+    @Nullable
+    @ExperimentalGetImage
+    @Override
+    public Image getImage() {
+        synchronized (mLock) {
+            checkNotClosed();
+            return null;
+        }
+    }
+
+    /**
+     * Creates a {@link Bitmap} form the value of the underlying {@link ByteBuffer}.
+     */
+    @NonNull
+    public Bitmap createBitmap() {
+        synchronized (mLock) {
+            checkNotClosed();
+            return createBitmapFromPlane(getPlanes(), getWidth(), getHeight());
+        }
+    }
+
+    private void checkNotClosed() {
+        synchronized (mLock) {
+            checkState(mPlaneProxy != null, "The image is closed.");
+        }
+    }
+
+    private static PlaneProxy createPlaneProxy(
+            @NonNull ByteBuffer byteBuffer, int rowStride, int pixelStride) {
+        return new PlaneProxy() {
+            @Override
+            public int getRowStride() {
+                return rowStride;
+            }
+
+            @Override
+            public int getPixelStride() {
+                return pixelStride;
+            }
+
+            @NonNull
+            @Override
+            public ByteBuffer getBuffer() {
+                return byteBuffer;
+            }
+        };
+    }
+
+    private static ImageInfo createImageInfo(
+            long timestamp, int rotationDegrees, @NonNull Matrix sensorToBuffer) {
+        return new ImageInfo() {
+            @NonNull
+            @Override
+            public TagBundle getTagBundle() {
+                throw new UnsupportedOperationException(
+                        "Custom ImageProxy does not contain TagBundle");
+            }
+
+            @Override
+            public long getTimestamp() {
+                return timestamp;
+            }
+
+            @Override
+            public int getRotationDegrees() {
+                return rotationDegrees;
+            }
+
+            @Override
+            @NonNull
+            public Matrix getSensorToBufferTransformMatrix() {
+                return new Matrix(sensorToBuffer);
+            }
+
+            @Override
+            public void populateExifData(@NonNull ExifData.Builder exifBuilder) {
+                throw new UnsupportedOperationException(
+                        "Custom ImageProxy does not contain Exif data.");
+            }
+        };
+    }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/utils/ImageUtil.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/utils/ImageUtil.java
index 6a7c9e7..8efac4a 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/utils/ImageUtil.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/utils/ImageUtil.java
@@ -16,6 +16,10 @@
 
 package androidx.camera.core.internal.utils;
 
+import static androidx.core.util.Preconditions.checkArgument;
+
+import static java.nio.ByteBuffer.allocateDirect;
+
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.BitmapRegionDecoder;
@@ -45,10 +49,48 @@
 public final class ImageUtil {
     private static final String TAG = "ImageUtil";
 
+    /**
+     * Default RGBA pixel stride used by CameraX, with R, G, B and A each occupies 1 byte.
+     */
+    public static final int DEFAULT_RGBA_PIXEL_STRIDE = 4;
+
     private ImageUtil() {
     }
 
     /**
+     * Creates a {@link Bitmap} from an {@link ImageProxy.PlaneProxy} array.
+     *
+     * <p>This method expects a single plane with a pixel stride of 4 and a row stride of (width *
+     * 4).
+     */
+    @NonNull
+    public static Bitmap createBitmapFromPlane(
+            @NonNull ImageProxy.PlaneProxy[] planes, int width, int height) {
+        checkArgument(planes.length == 1, "Expect a single plane");
+        checkArgument(planes[0].getPixelStride() == DEFAULT_RGBA_PIXEL_STRIDE,
+                "Expect pixelStride=" + DEFAULT_RGBA_PIXEL_STRIDE);
+        checkArgument(
+                planes[0].getRowStride() == DEFAULT_RGBA_PIXEL_STRIDE * width,
+                "Expect rowStride=width*" + DEFAULT_RGBA_PIXEL_STRIDE);
+        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        bitmap.copyPixelsFromBuffer(planes[0].getBuffer());
+        return bitmap;
+    }
+
+    /**
+     * Creates a direct {@link ByteBuffer} and copy the content of the {@link Bitmap}.
+     */
+    @NonNull
+    public static ByteBuffer createDirectByteBuffer(@NonNull Bitmap bitmap) {
+        checkArgument(bitmap.getConfig() == Bitmap.Config.ARGB_8888,
+                "Only accept Bitmap with ARGB_8888 format for now.");
+        ByteBuffer byteBuffer = allocateDirect(bitmap.getAllocationByteCount());
+        bitmap.copyPixelsToBuffer(byteBuffer);
+        byteBuffer.rewind();
+        return byteBuffer;
+    }
+
+    /**
      * Converts a {@link Size} to an float array of vertexes.
      */
     @NonNull
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/processing/ImageProcessorRequest.java b/camera/camera-core/src/main/java/androidx/camera/core/processing/ImageProcessorRequest.java
new file mode 100644
index 0000000..5d5af9c
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/processing/ImageProcessorRequest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.camera.core.processing;
+
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.ImageProcessor;
+import androidx.camera.core.ImageProxy;
+
+import java.util.List;
+
+/**
+ * Internal implementation of {@link ImageProcessor.Request} for sending {@link ImageProxy} to
+ * effect implementations.
+ */
+@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+public class ImageProcessorRequest implements ImageProcessor.Request {
+    @NonNull
+    private final List<ImageProxy> mImageProxies;
+    private final int mOutputFormat;
+
+    public ImageProcessorRequest(@NonNull List<ImageProxy> imageProxies, int outputFormat) {
+        mImageProxies = imageProxies;
+        mOutputFormat = outputFormat;
+    }
+
+    @NonNull
+    @Override
+    public List<ImageProxy> getInputImages() {
+        return mImageProxies;
+    }
+
+    @Override
+    public int getOutputFormat() {
+        return mOutputFormat;
+    }
+}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/processing/InternalImageProcessor.java b/camera/camera-core/src/main/java/androidx/camera/core/processing/InternalImageProcessor.java
new file mode 100644
index 0000000..7db28130
--- /dev/null
+++ b/camera/camera-core/src/main/java/androidx/camera/core/processing/InternalImageProcessor.java
@@ -0,0 +1,82 @@
+/*
+ * 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.camera.core.processing;
+
+import static androidx.camera.core.ImageCapture.ERROR_UNKNOWN;
+import static androidx.core.util.Preconditions.checkArgument;
+
+import static java.util.Objects.requireNonNull;
+
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.CameraEffect;
+import androidx.camera.core.ImageCaptureException;
+import androidx.camera.core.ImageProcessor;
+import androidx.concurrent.futures.CallbackToFutureAdapter;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+
+/**
+ * An internal {@link ImageProcessor} that wraps a {@link CameraEffect} targeting
+ * {@link CameraEffect#IMAGE_CAPTURE}.
+ *
+ * <p>This class wrap calls to {@link ImageProcessor} with the effect-provided {@link Executor}.
+ * It also provides additional from Camera
+ */
+@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+public class InternalImageProcessor {
+
+    @NonNull
+    private final Executor mExecutor;
+    @NonNull
+    private final ImageProcessor mImageProcessor;
+
+    public InternalImageProcessor(@NonNull CameraEffect cameraEffect) {
+        checkArgument(cameraEffect.getTargets() == CameraEffect.IMAGE_CAPTURE);
+        mExecutor = cameraEffect.getProcessorExecutor();
+        mImageProcessor = requireNonNull(cameraEffect.getImageProcessor());
+    }
+
+    /**
+     * Forwards the call to {@link ImageProcessor#process} on the effect-provided executor.
+     */
+    @NonNull
+    public ImageProcessor.Response safeProcess(@NonNull ImageProcessor.Request request)
+            throws ImageCaptureException {
+        try {
+            return CallbackToFutureAdapter.getFuture(
+                    (CallbackToFutureAdapter.Resolver<ImageProcessor.Response>) completer -> {
+                        mExecutor.execute(() -> {
+                            try {
+                                completer.set(mImageProcessor.process(request));
+                            } catch (Exception e) {
+                                // Catch all exceptions and forward it CameraX.
+                                completer.setException(e);
+                            }
+                        });
+                        return "InternalImageProcessor#process " + request.hashCode();
+                    }).get();
+        } catch (ExecutionException | InterruptedException e) {
+            Throwable cause = e.getCause() != null ? e.getCause() : e;
+            throw new ImageCaptureException(
+                    ERROR_UNKNOWN, "Failed to invoke ImageProcessor.", cause);
+        }
+    }
+}
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/RgbaImageProxyTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/RgbaImageProxyTest.kt
new file mode 100644
index 0000000..d79cff2
--- /dev/null
+++ b/camera/camera-core/src/test/java/androidx/camera/core/imagecapture/RgbaImageProxyTest.kt
@@ -0,0 +1,142 @@
+/*
+ * 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.camera.core.imagecapture
+
+import android.graphics.Bitmap
+import android.os.Build
+import androidx.camera.core.imagecapture.Utils.CAMERA_CAPTURE_RESULT
+import androidx.camera.core.imagecapture.Utils.CROP_RECT
+import androidx.camera.core.imagecapture.Utils.HEIGHT
+import androidx.camera.core.imagecapture.Utils.ROTATION_DEGREES
+import androidx.camera.core.imagecapture.Utils.SENSOR_TO_BUFFER
+import androidx.camera.core.imagecapture.Utils.TIMESTAMP
+import androidx.camera.core.imagecapture.Utils.WIDTH
+import androidx.camera.core.processing.Packet
+import androidx.camera.testing.ExifUtil.createExif
+import androidx.camera.testing.TestImageUtil.createBitmap
+import androidx.camera.testing.TestImageUtil.createJpegBytes
+import androidx.camera.testing.TestImageUtil.getAverageDiff
+import com.google.common.truth.Truth.assertThat
+import java.nio.ByteBuffer
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.internal.DoNotInstrument
+
+/**
+ * Unit tests for [RgbaImageProxy].
+ */
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class RgbaImageProxyTest {
+
+    @Test
+    fun createImageFromBitmap_verifyResult() {
+        // Arrange.
+        val bitmap = createBitmap(WIDTH, HEIGHT)
+        // Act.
+        val image = RgbaImageProxy(
+            Packet.of(
+                bitmap,
+                createExif(createJpegBytes(WIDTH, HEIGHT)),
+                CROP_RECT,
+                ROTATION_DEGREES,
+                SENSOR_TO_BUFFER,
+                CAMERA_CAPTURE_RESULT
+            )
+        )
+        // Assert.
+        val restoredBitmap = Bitmap.createBitmap(image.width, image.height, Bitmap.Config.ARGB_8888)
+        bitmap.copyPixelsFromBuffer(image.planes[0].buffer)
+        assertThat(getAverageDiff(bitmap, restoredBitmap)).isEqualTo(0)
+        assertThat(image.image).isNull()
+        assertThat(image.width).isEqualTo(WIDTH)
+        assertThat(image.height).isEqualTo(HEIGHT)
+        assertThat(image.cropRect).isEqualTo(CROP_RECT)
+        assertThat(image.imageInfo.rotationDegrees).isEqualTo(ROTATION_DEGREES)
+        assertThat(image.imageInfo.sensorToBufferTransformMatrix).isEqualTo(SENSOR_TO_BUFFER)
+        assertThat(image.imageInfo.timestamp).isEqualTo(TIMESTAMP)
+    }
+
+    @Test
+    fun createImageFromByteBuffer_verifyResult() {
+        // Arrange.
+        val bitmap = createBitmap(WIDTH, HEIGHT)
+        val byteBuffer = ByteBuffer.allocateDirect(bitmap.allocationByteCount)
+        bitmap.copyPixelsToBuffer(byteBuffer)
+        // Act.
+        val image = RgbaImageProxy(
+            byteBuffer,
+            4,
+            bitmap.width,
+            bitmap.height,
+            CROP_RECT,
+            ROTATION_DEGREES,
+            SENSOR_TO_BUFFER,
+            TIMESTAMP
+        )
+        // Assert.
+        val restoredBitmap = image.createBitmap()
+        assertThat(getAverageDiff(bitmap, restoredBitmap)).isEqualTo(0)
+        assertThat(image.width).isEqualTo(WIDTH)
+        assertThat(image.height).isEqualTo(HEIGHT)
+        assertThat(image.cropRect).isEqualTo(CROP_RECT)
+        assertThat(image.imageInfo.rotationDegrees).isEqualTo(ROTATION_DEGREES)
+        assertThat(image.imageInfo.sensorToBufferTransformMatrix).isEqualTo(SENSOR_TO_BUFFER)
+        assertThat(image.imageInfo.timestamp).isEqualTo(TIMESTAMP)
+    }
+
+    @Test
+    fun closeImage_invokingMethodsThrowsException() {
+        // Arrange.
+        val bitmap = createBitmap(WIDTH, HEIGHT)
+        val image = RgbaImageProxy(
+            Packet.of(
+                bitmap,
+                createExif(createJpegBytes(WIDTH, HEIGHT)),
+                CROP_RECT,
+                ROTATION_DEGREES,
+                SENSOR_TO_BUFFER,
+                CAMERA_CAPTURE_RESULT
+            )
+        )
+        // Act.
+        image.close()
+        // Assert
+        assertThat(hasException { image.close() }).isTrue()
+        assertThat(hasException { image.width }).isTrue()
+        assertThat(hasException { image.height }).isTrue()
+        assertThat(hasException { image.planes }).isTrue()
+        assertThat(hasException { image.cropRect }).isTrue()
+        assertThat(hasException { image.imageInfo }).isTrue()
+        assertThat(hasException { image.image }).isTrue()
+        assertThat(hasException { image.setCropRect(CROP_RECT) }).isTrue()
+        assertThat(hasException { image.format }).isTrue()
+        assertThat(hasException { image.createBitmap() }).isTrue()
+    }
+
+    private fun hasException(runnable: Runnable): Boolean {
+        try {
+            runnable.run()
+        } catch (exception: IllegalStateException) {
+            return true
+        }
+        return false
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/internal/utils/ImageUtilTest.java b/camera/camera-core/src/test/java/androidx/camera/core/internal/utils/ImageUtilTest.java
index ab51a62..cfc75f8 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/internal/utils/ImageUtilTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/internal/utils/ImageUtilTest.java
@@ -16,6 +16,12 @@
 
 package androidx.camera.core.internal.utils;
 
+import static androidx.camera.core.internal.utils.ImageUtil.DEFAULT_RGBA_PIXEL_STRIDE;
+import static androidx.camera.core.internal.utils.ImageUtil.createBitmapFromPlane;
+import static androidx.camera.core.internal.utils.ImageUtil.createDirectByteBuffer;
+import static androidx.camera.testing.TestImageUtil.createBitmap;
+import static androidx.camera.testing.TestImageUtil.getAverageDiff;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static junit.framework.TestCase.assertEquals;
@@ -36,6 +42,7 @@
 import androidx.camera.core.ImageProxy;
 import androidx.camera.testing.fakes.FakeImageInfo;
 import androidx.camera.testing.fakes.FakeImageProxy;
+import androidx.camera.testing.fakes.FakePlaneProxy;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -100,6 +107,55 @@
     }
 
     @Test
+    public void createBitmapFromPlane_bitmapCreated() {
+        // Arrange.
+        Bitmap original = createBitmap(WIDTH, HEIGHT);
+        ByteBuffer byteBuffer = createDirectByteBuffer(original);
+        ImageProxy.PlaneProxy planeProxy = new FakePlaneProxy(byteBuffer,
+                WIDTH * DEFAULT_RGBA_PIXEL_STRIDE, DEFAULT_RGBA_PIXEL_STRIDE);
+        // Act.
+        Bitmap restored = createBitmapFromPlane(
+                new ImageProxy.PlaneProxy[]{planeProxy},
+                WIDTH,
+                HEIGHT);
+        // Assert.
+        assertThat(getAverageDiff(original, restored)).isEqualTo(0);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void createBitmapWithMultiplePlanes_throwsException() {
+        // Arrange.
+        ImageProxy.PlaneProxy planeProxy = new FakePlaneProxy(
+                createDirectByteBuffer(createBitmap(WIDTH, HEIGHT)),
+                WIDTH * DEFAULT_RGBA_PIXEL_STRIDE,
+                DEFAULT_RGBA_PIXEL_STRIDE);
+        // Act.
+        createBitmapFromPlane(new ImageProxy.PlaneProxy[]{planeProxy, planeProxy}, WIDTH, HEIGHT);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void createBitmapWithWrongPixelStride_throwsException() {
+        // Arrange.
+        ImageProxy.PlaneProxy planeProxy = new FakePlaneProxy(
+                createDirectByteBuffer(createBitmap(WIDTH, HEIGHT)),
+                WIDTH * DEFAULT_RGBA_PIXEL_STRIDE,
+                3); // Wrong pixel stride.
+        // Act.
+        createBitmapFromPlane(new ImageProxy.PlaneProxy[]{planeProxy}, WIDTH, HEIGHT);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void createBitmapWithWrongRowStride_throwsException() {
+        // Arrange.
+        ImageProxy.PlaneProxy planeProxy = new FakePlaneProxy(
+                createDirectByteBuffer(createBitmap(WIDTH, HEIGHT)),
+                (WIDTH - 1) * DEFAULT_RGBA_PIXEL_STRIDE, // Wrong row stride.
+                DEFAULT_RGBA_PIXEL_STRIDE);
+        // Act.
+        createBitmapFromPlane(new ImageProxy.PlaneProxy[]{planeProxy}, WIDTH, HEIGHT);
+    }
+
+    @Test
     public void rotateAspectRatioFor90Degrees_rotated() {
         // Arrange.
         Rational aspectRatio = new Rational(3, 4);
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/processing/InternalImageProcessorTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/processing/InternalImageProcessorTest.kt
new file mode 100644
index 0000000..325bb1b
--- /dev/null
+++ b/camera/camera-core/src/test/java/androidx/camera/core/processing/InternalImageProcessorTest.kt
@@ -0,0 +1,91 @@
+/*
+ * 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.camera.core.processing
+
+import android.graphics.PixelFormat
+import androidx.camera.core.CameraEffect
+import androidx.camera.core.ImageCaptureException
+import androidx.camera.core.ImageProcessor
+import androidx.camera.core.ImageProcessor.Response
+import androidx.camera.core.impl.utils.executor.CameraXExecutors.highPriorityExecutor
+import androidx.camera.testing.fakes.FakeImageInfo
+import androidx.camera.testing.fakes.FakeImageProxy
+import com.google.common.truth.Truth.assertThat
+import java.lang.Thread.currentThread
+import java.util.concurrent.Executors.newSingleThreadExecutor
+import org.junit.Assert.fail
+import org.junit.Test
+
+/**
+ * Unit tests for [InternalImageProcessor].
+ */
+class InternalImageProcessorTest {
+
+    companion object {
+        private const val THREAD_NAME = "thread_name"
+    }
+
+    @Test
+    fun processorThrowsError_errorIsPropagatedToCameraX() {
+        // Arrange.
+        val exception = RuntimeException()
+        val cameraEffect = CameraEffect.Builder(CameraEffect.IMAGE_CAPTURE)
+            .setImageProcessor(highPriorityExecutor()) { throw exception }
+            .build()
+        val imageProcessor = InternalImageProcessor(cameraEffect)
+
+        // Act.
+        try {
+            imageProcessor.safeProcess(
+                ImageProcessorRequest(
+                    listOf(FakeImageProxy(FakeImageInfo())),
+                    PixelFormat.RGBA_8888
+                )
+            )
+            fail("Processor should throw exception")
+        } catch (ex: ImageCaptureException) {
+            // Assert.
+            assertThat(ex.cause).isEqualTo(exception)
+        }
+    }
+
+    @Test
+    fun process_appCallbackInvokedOnAppExecutor() {
+        // Arrange.
+        val imageToEffect = FakeImageProxy(FakeImageInfo())
+        val imageFromEffect = FakeImageProxy(FakeImageInfo())
+        var calledThreadName = ""
+        val processor = ImageProcessor {
+            calledThreadName = currentThread().name
+            Response { imageFromEffect }
+        }
+        val executor = newSingleThreadExecutor { Thread(it, THREAD_NAME) }
+        val cameraEffect = CameraEffect.Builder(CameraEffect.IMAGE_CAPTURE)
+            .setImageProcessor(executor, processor)
+            .build()
+        val imageProcessor = InternalImageProcessor(cameraEffect)
+
+        // Act.
+        val outputImage = imageProcessor.safeProcess(
+            ImageProcessorRequest(listOf(imageToEffect), PixelFormat.RGBA_8888)
+        ).outputImage
+
+        // Assert.
+        assertThat(outputImage).isEqualTo(imageFromEffect)
+        assertThat(calledThreadName).isEqualTo(THREAD_NAME)
+    }
+}
\ No newline at end of file
diff --git a/camera/camera-extensions/build.gradle b/camera/camera-extensions/build.gradle
index 6422e1e..f78b5e8 100644
--- a/camera/camera-extensions/build.gradle
+++ b/camera/camera-extensions/build.gradle
@@ -35,7 +35,7 @@
     compileOnly(project(":camera:camera-extensions-stub"))
 
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.robolectric)
     testImplementation(libs.truth)
     testImplementation(project(":camera:camera-testing"))
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/Version.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/Version.java
index d37eeb4..c740199 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/Version.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/Version.java
@@ -38,6 +38,7 @@
     public static final Version VERSION_1_0 = Version.create(1, 0, 0, "");
     public static final Version VERSION_1_1 = Version.create(1, 1, 0, "");
     public static final Version VERSION_1_2 = Version.create(1, 2, 0, "");
+    public static final Version VERSION_1_3 = Version.create(1, 3, 0, "");
 
     private static final Pattern VERSION_STRING_PATTERN =
             Pattern.compile("(\\d+)(?:\\.(\\d+))(?:\\.(\\d+))(?:\\-(.+))?");
diff --git a/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/AdaptingCaptureProcessorTest.kt b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/AdaptingCaptureProcessorTest.kt
index 801b221..54cb68d 100644
--- a/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/AdaptingCaptureProcessorTest.kt
+++ b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/AdaptingCaptureProcessorTest.kt
@@ -55,7 +55,7 @@
         callOnInitAndVerify()
         adaptingCaptureProcessor.close()
         adaptingCaptureProcessor.process(imageProxyBundle)
-        Mockito.verifyZeroInteractions(captureProcessorImpl)
+        Mockito.verifyNoMoreInteractions(captureProcessorImpl)
     }
 
     @Test
@@ -63,7 +63,7 @@
         adaptingCaptureProcessor.close()
         adaptingCaptureProcessor.onOutputSurface(Mockito.mock(Surface::class.java), 0)
         adaptingCaptureProcessor.onInit()
-        Mockito.verifyZeroInteractions(captureProcessorImpl)
+        Mockito.verifyNoMoreInteractions(captureProcessorImpl)
     }
 
     @Test
@@ -71,7 +71,7 @@
         adaptingCaptureProcessor.close()
         adaptingCaptureProcessor.onResolutionUpdate(Size(640, 480))
         adaptingCaptureProcessor.onInit()
-        Mockito.verifyZeroInteractions(captureProcessorImpl)
+        Mockito.verifyNoMoreInteractions(captureProcessorImpl)
     }
 
     @Test
@@ -86,7 +86,7 @@
         callOnInitAndVerify()
         adaptingCaptureProcessor.onDeInit()
         adaptingCaptureProcessor.process(imageProxyBundle)
-        Mockito.verifyZeroInteractions(captureProcessorImpl)
+        Mockito.verifyNoMoreInteractions(captureProcessorImpl)
     }
 
     private fun createFakeImageProxyBundle(
diff --git a/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/AdaptingPreviewProcessorTest.java b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/AdaptingPreviewProcessorTest.java
index f801b25..ba9d15b 100644
--- a/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/AdaptingPreviewProcessorTest.java
+++ b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/AdaptingPreviewProcessorTest.java
@@ -22,7 +22,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.media.Image;
 import android.os.Build;
@@ -73,7 +73,7 @@
         mAdaptingPreviewProcessor.process(mImageProxyBundle);
         mAdaptingPreviewProcessor.onInit();
 
-        verifyZeroInteractions(mImpl);
+        verifyNoMoreInteractions(mImpl);
     }
 
     @Test
@@ -82,7 +82,7 @@
         mAdaptingPreviewProcessor.onOutputSurface(mock(Surface.class), 0);
         mAdaptingPreviewProcessor.onInit();
 
-        verifyZeroInteractions(mImpl);
+        verifyNoMoreInteractions(mImpl);
     }
 
     @Test
@@ -91,7 +91,7 @@
         mAdaptingPreviewProcessor.onResolutionUpdate(new Size(640, 480));
         mAdaptingPreviewProcessor.onInit();
 
-        verifyZeroInteractions(mImpl);
+        verifyNoMoreInteractions(mImpl);
     }
 
     @Test
@@ -107,7 +107,7 @@
         mAdaptingPreviewProcessor.onDeInit();
         mAdaptingPreviewProcessor.process(mImageProxyBundle);
 
-        verifyZeroInteractions(mImpl);
+        verifyNoMoreInteractions(mImpl);
     }
 
     private void callOnInitAndVerify() {
diff --git a/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/AdaptingRequestUpdateProcessorTest.java b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/AdaptingRequestUpdateProcessorTest.java
index 832cfa0..ff8b784 100644
--- a/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/AdaptingRequestUpdateProcessorTest.java
+++ b/camera/camera-extensions/src/test/java/androidx/camera/extensions/internal/AdaptingRequestUpdateProcessorTest.java
@@ -18,7 +18,7 @@
 
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.hardware.camera2.CaptureResult;
@@ -73,7 +73,7 @@
 
         mAdaptingRequestUpdateProcessor.getCaptureStage();
 
-        verifyZeroInteractions(mPreviewExtenderImpl);
+        verifyNoMoreInteractions(mPreviewExtenderImpl);
     }
 
     @Test
@@ -82,7 +82,7 @@
 
         mAdaptingRequestUpdateProcessor.process(mImageInfo);
 
-        verifyZeroInteractions(mImpl);
+        verifyNoMoreInteractions(mImpl);
     }
 
     /**
diff --git a/camera/camera-mlkit-vision/build.gradle b/camera/camera-mlkit-vision/build.gradle
index e8c3d42..3715943 100644
--- a/camera/camera-mlkit-vision/build.gradle
+++ b/camera/camera-mlkit-vision/build.gradle
@@ -32,7 +32,7 @@
         }
     }
 
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.testRunner)
     testImplementation(libs.robolectric)
     testImplementation(libs.kotlinStdlib)
diff --git a/camera/camera-testing/build.gradle b/camera/camera-testing/build.gradle
index 0f1d9ca..ec539d7 100644
--- a/camera/camera-testing/build.gradle
+++ b/camera/camera-testing/build.gradle
@@ -46,7 +46,7 @@
     testImplementation(libs.junit)
     testImplementation(libs.truth)
     testImplementation(libs.robolectric)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
 }
 
 android {
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeJpegPlaneProxy.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeJpegPlaneProxy.java
index f7dcc57..7cbf949 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeJpegPlaneProxy.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeJpegPlaneProxy.java
@@ -25,6 +25,8 @@
 /**
  * Fake {@link ImageProxy.PlaneProxy} with JPEG format.
  *
+ * TODO: Rename this to FakeByteArrayPlaneProxy and inherit {@link FakePlaneProxy}.
+ *
  * @hide
  */
 @RequiresApi(21)
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakePlaneProxy.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakePlaneProxy.java
new file mode 100644
index 0000000..42a75bb
--- /dev/null
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakePlaneProxy.java
@@ -0,0 +1,57 @@
+/*
+ * 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.camera.testing.fakes;
+
+import androidx.annotation.NonNull;
+import androidx.camera.core.ImageProxy;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Fake {@link ImageProxy.PlaneProxy} for testing.
+ */
+public class FakePlaneProxy implements ImageProxy.PlaneProxy {
+
+    @NonNull
+    private final ByteBuffer mByteBuffer;
+
+    private final int mRowStride;
+
+    private final int mPixelStride;
+
+    public FakePlaneProxy(@NonNull ByteBuffer byteBuffer, int rowStride, int pixelStride) {
+        mByteBuffer = byteBuffer;
+        mRowStride = rowStride;
+        mPixelStride = pixelStride;
+    }
+
+    @Override
+    public int getRowStride() {
+        return mRowStride;
+    }
+
+    @Override
+    public int getPixelStride() {
+        return mPixelStride;
+    }
+
+    @NonNull
+    @Override
+    public ByteBuffer getBuffer() {
+        return mByteBuffer;
+    }
+}
diff --git a/camera/camera-video/build.gradle b/camera/camera-video/build.gradle
index ce37b7e..ea1bffd 100644
--- a/camera/camera-video/build.gradle
+++ b/camera/camera-video/build.gradle
@@ -43,7 +43,7 @@
     testImplementation(libs.junit)
     testImplementation(libs.truth)
     testImplementation(libs.robolectric)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation("androidx.core:core-ktx:1.1.0")
     testImplementation(project(":camera:camera-testing"), {
         exclude group: "androidx.camera", module: "camera-core"
diff --git a/camera/camera-view/build.gradle b/camera/camera-view/build.gradle
index 56a6b62..fc7bb75 100644
--- a/camera/camera-view/build.gradle
+++ b/camera/camera-view/build.gradle
@@ -40,7 +40,7 @@
     annotationProcessor(libs.autoValue)
 
     testImplementation(libs.testRunner)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.robolectric)
     testImplementation(libs.kotlinStdlib)
     testImplementation(libs.truth)
diff --git a/camera/camera-viewfinder/build.gradle b/camera/camera-viewfinder/build.gradle
index a0f4602d..873496e 100644
--- a/camera/camera-viewfinder/build.gradle
+++ b/camera/camera-viewfinder/build.gradle
@@ -39,7 +39,7 @@
     annotationProcessor(libs.autoValue)
 
     testImplementation(libs.testRunner)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.robolectric)
     testImplementation(libs.kotlinStdlib)
     testImplementation(libs.truth)
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/AdvancedExtenderValidation.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/AdvancedExtenderValidation.kt
new file mode 100644
index 0000000..783e7d8
--- /dev/null
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/AdvancedExtenderValidation.kt
@@ -0,0 +1,447 @@
+/*
+ * 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.camera.integration.extensions
+
+import android.content.Context
+import android.graphics.ImageFormat
+import android.graphics.SurfaceTexture
+import android.hardware.camera2.CameraCaptureSession
+import android.hardware.camera2.CameraCharacteristics
+import android.hardware.camera2.CameraDevice
+import android.hardware.camera2.CameraManager
+import android.hardware.camera2.CaptureRequest
+import android.hardware.camera2.params.OutputConfiguration
+import android.hardware.camera2.params.SessionConfiguration
+import android.media.ImageReader
+import android.util.Size
+import android.view.Surface
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.internal.compat.params.SessionConfigurationCompat
+import androidx.camera.camera2.interop.Camera2CameraInfo
+import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.camera.extensions.ExtensionsManager
+import androidx.camera.extensions.impl.advanced.AdvancedExtenderImpl
+import androidx.camera.extensions.impl.advanced.Camera2OutputConfigImpl
+import androidx.camera.extensions.impl.advanced.Camera2SessionConfigImpl
+import androidx.camera.extensions.impl.advanced.ImageReaderOutputConfigImpl
+import androidx.camera.extensions.impl.advanced.MultiResolutionImageReaderOutputConfigImpl
+import androidx.camera.extensions.impl.advanced.OutputSurfaceImpl
+import androidx.camera.extensions.impl.advanced.SurfaceOutputConfigImpl
+import androidx.camera.extensions.internal.ExtensionVersion
+import androidx.camera.extensions.internal.Version
+import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil
+import androidx.camera.integration.extensions.utils.CameraSelectorUtil
+import androidx.camera.lifecycle.ProcessCameraProvider
+import androidx.camera.testing.fakes.FakeLifecycleOwner
+import androidx.test.core.app.ApplicationProvider
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withContext
+import org.junit.Assume
+import org.junit.Assume.assumeTrue
+
+@RequiresApi(28)
+class AdvancedExtenderValidation(
+    private val cameraId: String,
+    private val extensionMode: Int
+) {
+    private val context = ApplicationProvider.getApplicationContext<Context>()
+    private lateinit var cameraProvider: ProcessCameraProvider
+    private lateinit var extensionsManager: ExtensionsManager
+    private lateinit var cameraCharacteristicsMap: Map<String, CameraCharacteristics>
+    private lateinit var advancedImpl: AdvancedExtenderImpl
+
+    fun setUp(): Unit = runBlocking {
+        cameraProvider =
+            ProcessCameraProvider.getInstance(context)[10000, TimeUnit.MILLISECONDS]
+        extensionsManager = ExtensionsManager.getInstanceAsync(
+            context,
+            cameraProvider
+        )[10000, TimeUnit.MILLISECONDS]
+        assumeTrue(CameraXExtensionsTestUtil.isAdvancedExtenderImplemented())
+        val baseCameraSelector = CameraSelectorUtil.createCameraSelectorById(cameraId)
+        assumeTrue(extensionsManager.isExtensionAvailable(baseCameraSelector, extensionMode))
+        val extensionCameraSelector = extensionsManager.getExtensionEnabledCameraSelector(
+            baseCameraSelector,
+            extensionMode
+        )
+        val cameraInfo = withContext(Dispatchers.Main) {
+            cameraProvider.bindToLifecycle(FakeLifecycleOwner(), extensionCameraSelector).cameraInfo
+        }
+        cameraCharacteristicsMap = Camera2CameraInfo.from(cameraInfo).cameraCharacteristicsMap
+        advancedImpl = CameraXExtensionsTestUtil
+            .createAdvancedExtenderImpl(extensionMode, cameraId, cameraInfo)
+    }
+
+    private val teardownFunctions = mutableListOf<() -> Unit>()
+
+    // Adding block to be invoked when tearing down. The last added will be invoked the first.
+    private fun addTearDown(teardown: () -> Unit) {
+        synchronized(teardownFunctions) {
+            teardownFunctions.add(0, teardown) // added to the head
+        }
+    }
+
+    fun tearDown(): Unit = runBlocking {
+        synchronized(teardownFunctions) {
+            for (teardownFunction in teardownFunctions) {
+                teardownFunction()
+            }
+            teardownFunctions.clear()
+        }
+        withContext(Dispatchers.Main) {
+            extensionsManager.shutdown()[10000, TimeUnit.MILLISECONDS]
+            cameraProvider.shutdown()[10000, TimeUnit.MILLISECONDS]
+        }
+    }
+
+    // Test
+    fun getSupportedPreviewOutputResolutions_returnValidData() {
+        val map = advancedImpl.getSupportedPreviewOutputResolutions(cameraId)
+
+        assertThat(map[ImageFormat.PRIVATE]).isNotEmpty()
+    }
+
+    // Test
+    fun getSupportedCaptureOutputResolutions_returnValidData() {
+        val map = advancedImpl.getSupportedCaptureOutputResolutions(cameraId)
+
+        assertThat(map[ImageFormat.JPEG]).isNotEmpty()
+        assertThat(map[ImageFormat.YUV_420_888]).isNotEmpty()
+    }
+
+    // Test
+    fun getAvailableCaptureRequestKeys_existAfter1_3() {
+        assumeTrue(ExtensionVersion.getRuntimeVersion()!! >= Version.VERSION_1_3)
+        advancedImpl.getAvailableCaptureRequestKeys()
+    }
+
+    // Test
+    fun getAvailableCaptureResultKeys_existAfter1_3() {
+        assumeTrue(ExtensionVersion.getRuntimeVersion()!! >= Version.VERSION_1_3)
+        advancedImpl.getAvailableCaptureResultKeys()
+    }
+
+    enum class SizeCategory {
+        MAXIMUM,
+        MEDIAN,
+        MINIMUM
+    }
+
+    private fun createPreviewOutput(
+        impl: AdvancedExtenderImpl,
+        sizeCategory: SizeCategory
+    ): OutputSurfaceImpl {
+        val previewSizeMap = impl.getSupportedPreviewOutputResolutions(cameraId)
+        assertThat(previewSizeMap[ImageFormat.PRIVATE]).isNotEmpty()
+
+        val previewSizes = previewSizeMap[ImageFormat.PRIVATE]!!
+        val previewSize = getSizeByClass(previewSizes, sizeCategory)
+        val surfaceTexture = SurfaceTexture(0)
+        surfaceTexture.setDefaultBufferSize(previewSize.width, previewSize.height)
+        val previewSurface = Surface(surfaceTexture)
+        addTearDown {
+            surfaceTexture.release()
+        }
+        return OutputSurface(previewSurface, previewSize, ImageFormat.PRIVATE)
+    }
+
+    private fun createCaptureOutput(
+        impl: AdvancedExtenderImpl,
+        sizeCategory: SizeCategory
+    ): OutputSurfaceImpl {
+        val captureSizeMap = impl.getSupportedCaptureOutputResolutions(cameraId)
+        assertThat(captureSizeMap[ImageFormat.JPEG]).isNotEmpty()
+
+        val captureSizes = captureSizeMap[ImageFormat.JPEG]!!
+        var captureSize = getSizeByClass(captureSizes, sizeCategory)
+        val imageReader = ImageReader.newInstance(
+            captureSize.width, captureSize.height, ImageFormat.JPEG, 1
+        )
+        addTearDown {
+            imageReader.close()
+        }
+        return OutputSurface(imageReader.surface, captureSize, ImageFormat.JPEG)
+    }
+
+    private fun getSizeByClass(
+        sizes: List<Size>,
+        sizeCategory: SizeCategory
+    ): Size {
+        val sortedList = sizes.sortedByDescending { it.width * it.height }
+        var size =
+            when (sizeCategory) {
+                SizeCategory.MAXIMUM -> {
+                    sortedList[0]
+                }
+                SizeCategory.MEDIAN -> {
+                    sortedList[sortedList.size / 2]
+                }
+                SizeCategory.MINIMUM -> {
+                    sortedList[sortedList.size - 1]
+                }
+            }
+        return size
+    }
+
+    private fun createAnalysisOutput(
+        impl: AdvancedExtenderImpl,
+        sizeCategory: SizeCategory
+    ): OutputSurfaceImpl {
+        val analysisSizes = impl.getSupportedYuvAnalysisResolutions(cameraId)
+        assertThat(analysisSizes).isNotEmpty()
+
+        var analysisSize = getSizeByClass(analysisSizes, sizeCategory)
+        val imageReader = ImageReader.newInstance(
+            analysisSize.width, analysisSize.height, ImageFormat.YUV_420_888, 1
+        )
+        addTearDown {
+            imageReader.close()
+        }
+        return OutputSurface(imageReader.surface, analysisSize, ImageFormat.YUV_420_888)
+    }
+
+    // Test
+    fun initSession_maxSize_canConfigureSession() = initSessionTest(
+        previewOutputSizeCategory = SizeCategory.MAXIMUM,
+        captureOutputSizeCategory = SizeCategory.MAXIMUM
+    )
+
+    // Test
+    fun initSession_minSize_canConfigureSession() = initSessionTest(
+        previewOutputSizeCategory = SizeCategory.MINIMUM,
+        captureOutputSizeCategory = SizeCategory.MINIMUM
+    )
+
+    // Test
+    fun initSession_medianSize_canConfigureSession() = initSessionTest(
+        previewOutputSizeCategory = SizeCategory.MEDIAN,
+        captureOutputSizeCategory = SizeCategory.MEDIAN
+    )
+
+    // Test
+    fun initSessionWithAnalysis_maxSize_canConfigureSession() = initSessionTest(
+        previewOutputSizeCategory = SizeCategory.MAXIMUM,
+        captureOutputSizeCategory = SizeCategory.MAXIMUM,
+        analysisOutputSizeCategory = SizeCategory.MAXIMUM
+    )
+
+    // Test
+    fun initSessionWithAnalysis_minSize_canConfigureSession() = initSessionTest(
+        previewOutputSizeCategory = SizeCategory.MINIMUM,
+        captureOutputSizeCategory = SizeCategory.MINIMUM,
+        analysisOutputSizeCategory = SizeCategory.MINIMUM
+    )
+
+    // Test
+    fun initSessionWithAnalysis_medianSize_canConfigureSession() = initSessionTest(
+        previewOutputSizeCategory = SizeCategory.MEDIAN,
+        captureOutputSizeCategory = SizeCategory.MEDIAN,
+        analysisOutputSizeCategory = SizeCategory.MEDIAN
+    )
+
+    fun initSessionTest(
+        previewOutputSizeCategory: SizeCategory,
+        captureOutputSizeCategory: SizeCategory,
+        analysisOutputSizeCategory: SizeCategory? = null
+    ): Unit = runBlocking {
+        if (analysisOutputSizeCategory != null) {
+            Assume.assumeFalse(
+                advancedImpl.getSupportedYuvAnalysisResolutions(cameraId).isNullOrEmpty()
+            )
+        }
+
+        val sessionProcessor = advancedImpl.createSessionProcessor()
+        val previewOutput = createPreviewOutput(advancedImpl, previewOutputSizeCategory)
+        val captureOutput = createCaptureOutput(advancedImpl, captureOutputSizeCategory)
+        val analysisOutput = analysisOutputSizeCategory?.let {
+            createAnalysisOutput(advancedImpl, analysisOutputSizeCategory)
+        }
+
+        addTearDown {
+            sessionProcessor.deInitSession()
+        }
+
+        var camera2SessionConfigImpl =
+            sessionProcessor.initSession(
+                cameraId,
+                cameraCharacteristicsMap,
+                context,
+                previewOutput,
+                captureOutput,
+                analysisOutput
+            )
+
+        verifyCamera2SessionConfig(camera2SessionConfigImpl)
+    }
+
+    private class OutputSurface(
+        private val surface: Surface,
+        private val size: Size,
+        private val imageFormat: Int
+    ) : OutputSurfaceImpl {
+        override fun getSurface() = surface
+        override fun getSize() = size
+        override fun getImageFormat() = imageFormat
+    }
+
+    private fun getOutputConfiguration(
+        outputConfigImpl: Camera2OutputConfigImpl
+    ): OutputConfiguration {
+        var outputConfiguration: OutputConfiguration
+        when (outputConfigImpl) {
+            is SurfaceOutputConfigImpl -> {
+                val surface = outputConfigImpl.surface
+                outputConfiguration = OutputConfiguration(outputConfigImpl.surfaceGroupId, surface)
+            }
+
+            is ImageReaderOutputConfigImpl -> {
+                val imageReader = ImageReader.newInstance(
+                    outputConfigImpl.size.width,
+                    outputConfigImpl.size.height,
+                    outputConfigImpl.imageFormat,
+                    outputConfigImpl.maxImages
+                )
+                val surface = imageReader.surface
+                addTearDown { imageReader.close() }
+                outputConfiguration = OutputConfiguration(outputConfigImpl.surfaceGroupId, surface)
+            }
+
+            is MultiResolutionImageReaderOutputConfigImpl ->
+                throw java.lang.UnsupportedOperationException(
+                    "MultiResolutionImageReaderOutputConfigImpl not supported"
+                )
+
+            else -> throw java.lang.UnsupportedOperationException(
+                "Output configuration type not supported"
+            )
+        }
+
+        if (outputConfigImpl.physicalCameraId != null) {
+            outputConfiguration.setPhysicalCameraId(outputConfigImpl.physicalCameraId)
+        }
+
+        if (outputConfigImpl.surfaceSharingOutputConfigs != null) {
+            for (surfaceSharingOutputConfig in outputConfigImpl.surfaceSharingOutputConfigs) {
+                val sharingOutputConfiguration = getOutputConfiguration(surfaceSharingOutputConfig)
+                outputConfiguration.addSurface(sharingOutputConfiguration.surface!!)
+                outputConfiguration.enableSurfaceSharing()
+            }
+        }
+
+        return outputConfiguration
+    }
+
+    private suspend fun openCameraDevice(cameraId: String): CameraDevice {
+        val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
+        val deferred = CompletableDeferred<CameraDevice>()
+        cameraManager.openCamera(
+            cameraId,
+            CameraXExecutors.ioExecutor(),
+            object : CameraDevice.StateCallback() {
+                override fun onOpened(cameraDevice: CameraDevice) {
+                    deferred.complete(cameraDevice)
+                }
+
+                override fun onClosed(camera: CameraDevice) {
+                    super.onClosed(camera)
+                }
+
+                override fun onDisconnected(cameraDevice: CameraDevice) {
+                    deferred.completeExceptionally(RuntimeException("Camera Disconnected"))
+                }
+
+                override fun onError(cameraDevice: CameraDevice, error: Int) {
+                    deferred.completeExceptionally(
+                        RuntimeException("Camera onError(error=$cameraDevice)")
+                    )
+                }
+            })
+        return deferred.await()
+    }
+
+    private suspend fun openCaptureSession(
+        cameraDevice: CameraDevice,
+        camera2SessionConfig: Camera2SessionConfigImpl
+    ): CameraCaptureSession {
+
+        val outputConfigurationList = mutableListOf<OutputConfiguration>()
+        for (outputConfig in camera2SessionConfig.outputConfigs) {
+            val outputConfiguration = getOutputConfiguration(outputConfig)
+            outputConfigurationList.add(outputConfiguration)
+        }
+
+        val sessionDeferred = CompletableDeferred<CameraCaptureSession>()
+        val sessionConfiguration = SessionConfiguration(
+            SessionConfigurationCompat.SESSION_REGULAR,
+            outputConfigurationList,
+            CameraXExecutors.ioExecutor(),
+            object : CameraCaptureSession.StateCallback() {
+                override fun onConfigured(session: CameraCaptureSession) {
+                    sessionDeferred.complete(session)
+                }
+
+                override fun onConfigureFailed(session: CameraCaptureSession) {
+                    sessionDeferred.completeExceptionally(RuntimeException("onConfigureFailed"))
+                }
+
+                override fun onReady(session: CameraCaptureSession) {
+                }
+
+                override fun onActive(session: CameraCaptureSession) {
+                }
+
+                override fun onCaptureQueueEmpty(session: CameraCaptureSession) {
+                }
+
+                override fun onClosed(session: CameraCaptureSession) {
+                    super.onClosed(session)
+                }
+
+                override fun onSurfacePrepared(session: CameraCaptureSession, surface: Surface) {
+                    super.onSurfacePrepared(session, surface)
+                }
+            }
+        )
+
+        val requestBuilder = cameraDevice.createCaptureRequest(
+            camera2SessionConfig.sessionTemplateId
+        )
+
+        camera2SessionConfig.sessionParameters.forEach { (key, value) ->
+            @Suppress("UNCHECKED_CAST")
+            requestBuilder.set(key as CaptureRequest.Key<Any>, value)
+        }
+        sessionConfiguration.sessionParameters = requestBuilder.build()
+
+        cameraDevice.createCaptureSession(sessionConfiguration)
+
+        return sessionDeferred.await()
+    }
+
+    private suspend fun verifyCamera2SessionConfig(camera2SessionConfig: Camera2SessionConfigImpl) {
+        val cameraDevice = openCameraDevice(cameraId)
+        assertThat(cameraDevice).isNotNull()
+        addTearDown { cameraDevice.close() }
+        val captureSession = openCaptureSession(cameraDevice, camera2SessionConfig)
+        assertThat(captureSession).isNotNull()
+        addTearDown { captureSession.close() }
+    }
+}
\ No newline at end of file
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/AdvancedExtenderValidationTest.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/AdvancedExtenderValidationTest.kt
new file mode 100644
index 0000000..7cdee86
--- /dev/null
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/AdvancedExtenderValidationTest.kt
@@ -0,0 +1,105 @@
+/*
+ * 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.camera.integration.extensions
+
+import androidx.camera.camera2.Camera2Config
+import androidx.camera.integration.extensions.util.CameraXExtensionsTestUtil
+import androidx.camera.testing.CameraUtil
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Validation test for OEMs' advanced extender implementation.
+ *
+ * <p>All testings are forwarded to AdvancedExtenderValidation. This is because JUnit test will
+ * check all types exist in the test class and on the devices that don't support extensions, the
+ * extensions interface classes doesn't exist and will cause a class-not-found exception. Accessing
+ * these extensions interface classes in the AdvancedExtenderValidation can avoid this issue.
+ */
+@LargeTest
+@RunWith(Parameterized::class)
+@SdkSuppress(minSdkVersion = 28)
+class AdvancedExtenderValidationTest(
+    private val cameraId: String,
+    private val extensionMode: Int
+) {
+    private val validation = AdvancedExtenderValidation(cameraId, extensionMode)
+
+    companion object {
+        @JvmStatic
+        @get:Parameterized.Parameters(name = "cameraId = {0}, extensionMode = {1}")
+        val parameters: Collection<Array<Any>>
+            get() = CameraXExtensionsTestUtil.getAllCameraIdExtensionModeCombinations()
+    }
+
+    @get:Rule
+    val useCamera = CameraUtil.grantCameraPermissionAndPreTest(
+        CameraUtil.PreTestCameraIdList(Camera2Config.defaultConfig())
+    )
+
+    @Before
+    fun setUp() = validation.setUp()
+
+    @After
+    fun tearDown() = validation.tearDown()
+
+    @Test
+    fun getSupportedPreviewOutputResolutions_returnValidData() =
+        validation.getSupportedPreviewOutputResolutions_returnValidData()
+
+    @Test
+    fun getSupportedCaptureOutputResolutions_returnValidData() =
+        validation.getSupportedCaptureOutputResolutions_returnValidData()
+
+    @Test
+    fun getAvailableCaptureRequestKeys_existAfter1_3() =
+        validation.getAvailableCaptureRequestKeys_existAfter1_3()
+
+    @Test
+    fun getAvailableCaptureResultKeys_existAfter1_3() =
+        validation.getAvailableCaptureResultKeys_existAfter1_3()
+
+    @Test
+    fun initSession_maxSize_canConfigureSession() =
+        validation.initSession_maxSize_canConfigureSession()
+
+    @Test
+    fun initSession_minSize_canConfigureSession() =
+        validation.initSession_minSize_canConfigureSession()
+
+    @Test
+    fun initSession_medianSize_canConfigureSession() =
+        validation.initSession_medianSize_canConfigureSession()
+
+    @Test
+    fun initSessionWithAnalysis_maxSize_canConfigureSession() =
+        validation.initSessionWithAnalysis_maxSize_canConfigureSession()
+
+    @Test
+    fun initSessionWithAnalysis_minSize_canConfigureSession() =
+        validation.initSessionWithAnalysis_minSize_canConfigureSession()
+
+    @Test
+    fun initSessionWithAnalysis_medianSize_canConfigureSession() =
+        validation.initSessionWithAnalysis_medianSize_canConfigureSession()
+}
\ No newline at end of file
diff --git a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/CameraXExtensionsTestUtil.kt b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/CameraXExtensionsTestUtil.kt
index 088da77..0e1fe04 100644
--- a/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/CameraXExtensionsTestUtil.kt
+++ b/camera/integration-tests/extensionstestapp/src/androidTest/java/androidx/camera/integration/extensions/util/CameraXExtensionsTestUtil.kt
@@ -20,6 +20,8 @@
 import android.content.Intent
 import android.hardware.camera2.CameraCharacteristics
 import android.os.Build
+import androidx.camera.camera2.interop.Camera2CameraInfo
+import androidx.camera.core.CameraInfo
 import androidx.camera.core.ImageAnalysis
 import androidx.camera.core.ImageCapture
 import androidx.camera.core.Preview
@@ -37,7 +39,14 @@
 import androidx.camera.extensions.impl.NightImageCaptureExtenderImpl
 import androidx.camera.extensions.impl.NightPreviewExtenderImpl
 import androidx.camera.extensions.impl.PreviewExtenderImpl
+import androidx.camera.extensions.impl.advanced.AdvancedExtenderImpl
+import androidx.camera.extensions.impl.advanced.AutoAdvancedExtenderImpl
+import androidx.camera.extensions.impl.advanced.BeautyAdvancedExtenderImpl
+import androidx.camera.extensions.impl.advanced.BokehAdvancedExtenderImpl
+import androidx.camera.extensions.impl.advanced.HdrAdvancedExtenderImpl
+import androidx.camera.extensions.impl.advanced.NightAdvancedExtenderImpl
 import androidx.camera.extensions.internal.ExtensionVersion
+import androidx.camera.extensions.internal.Version
 import androidx.camera.integration.extensions.CameraExtensionsActivity
 import androidx.camera.integration.extensions.IntentExtraKey
 import androidx.camera.integration.extensions.utils.CameraSelectorUtil.createCameraSelectorById
@@ -131,6 +140,32 @@
     }
 
     /**
+     * Creates a [AdvancedExtenderImpl] object for specific [ExtensionMode] and
+     * camera id.
+     *
+     * @param extensionMode The extension mode for the created object.
+     * @param cameraId The target camera id.
+     * @param cameraCharacteristics The camera characteristics of the target camera.
+     * @return A [AdvancedExtenderImpl] object.
+     */
+    @JvmStatic
+    fun createAdvancedExtenderImpl(
+        @ExtensionMode.Mode extensionMode: Int,
+        cameraId: String,
+        cameraInfo: CameraInfo
+    ): AdvancedExtenderImpl = when (extensionMode) {
+        ExtensionMode.HDR -> HdrAdvancedExtenderImpl()
+        ExtensionMode.BOKEH -> BokehAdvancedExtenderImpl()
+        ExtensionMode.FACE_RETOUCH -> BeautyAdvancedExtenderImpl()
+        ExtensionMode.NIGHT -> NightAdvancedExtenderImpl()
+        ExtensionMode.AUTO -> AutoAdvancedExtenderImpl()
+        else -> throw AssertionFailedError("No such Preview extender implementation")
+    }.apply {
+        val cameraCharacteristicsMap = Camera2CameraInfo.from(cameraInfo).cameraCharacteristicsMap
+        init(cameraId, cameraCharacteristicsMap)
+    }
+
+    /**
      * Returns whether the target camera device can support the test for a specific extension mode.
      */
     @JvmStatic
@@ -152,8 +187,10 @@
         extensionMode: Int
     ) {
         val cameraIdCameraSelector = createCameraSelectorById(cameraId)
-        assumeTrue("Extensions mode($extensionMode) not supported",
-            extensionsManager.isExtensionAvailable(cameraIdCameraSelector, extensionMode))
+        assumeTrue(
+            "Extensions mode($extensionMode) not supported",
+            extensionsManager.isExtensionAvailable(cameraIdCameraSelector, extensionMode)
+        )
     }
 
     @JvmStatic
@@ -191,6 +228,18 @@
     }
 
     @JvmStatic
+    fun isAdvancedExtenderImplemented(): Boolean {
+        if (!isTargetDeviceAvailableForExtensions()) {
+            return false
+        }
+        if (ExtensionVersion.getRuntimeVersion()!! < Version.VERSION_1_2) {
+            return false
+        }
+
+        return ExtensionVersion.isAdvancedExtenderSupported()
+    }
+
+    @JvmStatic
     fun launchCameraExtensionsActivity(
         cameraId: String,
         extensionMode: Int,
diff --git a/car/app/app-automotive/build.gradle b/car/app/app-automotive/build.gradle
index 1140f784..bac38c9 100644
--- a/car/app/app-automotive/build.gradle
+++ b/car/app/app-automotive/build.gradle
@@ -44,7 +44,7 @@
     testImplementation(libs.testRules)
     testImplementation(libs.testRunner)
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.robolectric)
     testImplementation(libs.truth)
     testImplementation("androidx.fragment:fragment-testing:1.2.3")
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/hardware/AutomotiveCarHardwareManagerTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/hardware/AutomotiveCarHardwareManagerTest.java
index 80e01da..b780ef0 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/hardware/AutomotiveCarHardwareManagerTest.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/hardware/AutomotiveCarHardwareManagerTest.java
@@ -60,6 +60,7 @@
     private Application mContext;
     private ShadowApplication mShadowApplication;
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/hardware/climate/AutomotiveCarClimateTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/hardware/climate/AutomotiveCarClimateTest.java
index 78cdd50..cc92e05 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/hardware/climate/AutomotiveCarClimateTest.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/hardware/climate/AutomotiveCarClimateTest.java
@@ -127,6 +127,7 @@
     @Mock
     private PropertyManager mPropertyManager;
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/hardware/common/MockedCarTestBase.java b/car/app/app-automotive/src/test/java/androidx/car/app/hardware/common/MockedCarTestBase.java
index 9b2f0da..3e51de2 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/hardware/common/MockedCarTestBase.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/hardware/common/MockedCarTestBase.java
@@ -115,6 +115,7 @@
     @Mock
     private CarPropertyManager mCarPropertyManagerMock;
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/hardware/common/PropertyManagerTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/hardware/common/PropertyManagerTest.java
index 38dd84e..3604d4d 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/hardware/common/PropertyManagerTest.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/hardware/common/PropertyManagerTest.java
@@ -52,6 +52,7 @@
             Collections.singletonList(CarZone.CAR_ZONE_GLOBAL);
     private PropertyManager mPropertyManager;
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setUp() {
         super.setUp();
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/hardware/common/PropertyRequestProcessorTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/hardware/common/PropertyRequestProcessorTest.java
index 94253fa..085fb11 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/hardware/common/PropertyRequestProcessorTest.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/hardware/common/PropertyRequestProcessorTest.java
@@ -54,6 +54,7 @@
 public class PropertyRequestProcessorTest extends MockedCarTestBase {
     private static final int WAIT_CALLBACK_MS = 50;
     private PropertyRequestProcessor mRequestProcessor;
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setUp() {
         super.setUp();
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/hardware/info/AutomotiveCarInfoTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/hardware/info/AutomotiveCarInfoTest.java
index b383736..52f3fa4 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/hardware/info/AutomotiveCarInfoTest.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/hardware/info/AutomotiveCarInfoTest.java
@@ -113,6 +113,7 @@
     private static final List<CarZone> GLOBAL_ZONE = Collections.singletonList(
             CarZone.CAR_ZONE_GLOBAL);
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/hardware/info/AutomotiveCarSensorsTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/hardware/info/AutomotiveCarSensorsTest.java
index 75f3e95..64594ba 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/hardware/info/AutomotiveCarSensorsTest.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/hardware/info/AutomotiveCarSensorsTest.java
@@ -52,6 +52,7 @@
     @Mock
     private OnCarDataAvailableListener<CarHardwareLocation> mCarHardwareLocationListener;
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/car/app/app-automotive/src/test/java/androidx/car/app/suggestion/SuggestionManagerTest.java b/car/app/app-automotive/src/test/java/androidx/car/app/suggestion/SuggestionManagerTest.java
index 0986998..9595975 100644
--- a/car/app/app-automotive/src/test/java/androidx/car/app/suggestion/SuggestionManagerTest.java
+++ b/car/app/app-automotive/src/test/java/androidx/car/app/suggestion/SuggestionManagerTest.java
@@ -66,6 +66,7 @@
             new Suggestion.Builder().setIdentifier(mIdentifier).setTitle(mTitle).setSubtitle(
                     mSubTitle).setIcon(mIcon).setAction(mPendingIntent).build();
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
diff --git a/car/app/app-projected/build.gradle b/car/app/app-projected/build.gradle
index bac1356..6ebd606a 100644
--- a/car/app/app-projected/build.gradle
+++ b/car/app/app-projected/build.gradle
@@ -31,7 +31,7 @@
     testImplementation(libs.testCore)
     testImplementation(libs.testRunner)
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.robolectric)
     testImplementation(libs.truth)
     testImplementation project(path: ':car:app:app-testing')
diff --git a/car/app/app-projected/src/test/java/androidx/car/app/hardware/common/CarHardwareHostDispatcherTest.java b/car/app/app-projected/src/test/java/androidx/car/app/hardware/common/CarHardwareHostDispatcherTest.java
index 006edc6..8f86245 100644
--- a/car/app/app-projected/src/test/java/androidx/car/app/hardware/common/CarHardwareHostDispatcherTest.java
+++ b/car/app/app-projected/src/test/java/androidx/car/app/hardware/common/CarHardwareHostDispatcherTest.java
@@ -57,6 +57,7 @@
             new CarHardwareHostDispatcher(mHostDispatcher);
     private TestCarHardwareHostStub mCarHardwareHost;
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
diff --git a/car/app/app-projected/src/test/java/androidx/car/app/hardware/common/CarResultStubMapTest.java b/car/app/app-projected/src/test/java/androidx/car/app/hardware/common/CarResultStubMapTest.java
index 0b75733..632a499 100644
--- a/car/app/app-projected/src/test/java/androidx/car/app/hardware/common/CarResultStubMapTest.java
+++ b/car/app/app-projected/src/test/java/androidx/car/app/hardware/common/CarResultStubMapTest.java
@@ -75,6 +75,7 @@
     @Mock
     OnCarDataAvailableListener<String> mMockCarDataStringListener;
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
diff --git a/car/app/app-projected/src/test/java/androidx/car/app/hardware/common/CarResultStubTest.java b/car/app/app-projected/src/test/java/androidx/car/app/hardware/common/CarResultStubTest.java
index 7215449..ca32748 100644
--- a/car/app/app-projected/src/test/java/androidx/car/app/hardware/common/CarResultStubTest.java
+++ b/car/app/app-projected/src/test/java/androidx/car/app/hardware/common/CarResultStubTest.java
@@ -60,6 +60,7 @@
     @Mock
     OnCarDataAvailableListener<Integer> mMockCarDataListener;
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
diff --git a/car/app/app-samples/showcase/automotive/src/main/AndroidManifest.xml b/car/app/app-samples/showcase/automotive/src/main/AndroidManifest.xml
index 8cf2dbe..62f68c8 100644
--- a/car/app/app-samples/showcase/automotive/src/main/AndroidManifest.xml
+++ b/car/app/app-samples/showcase/automotive/src/main/AndroidManifest.xml
@@ -87,17 +87,12 @@
     </service>
 
     <service
-        android:name=".common.navigation.NavigationNotificationService"
-        android:exported="true">
-    </service>
-
-    <service
         android:name=".common.screens.navigationdemos.NavigationNotificationService"
         android:exported="true">
     </service>
 
     <provider
-        android:name="androidx.car.app.sample.showcase.common.textandicons.DelayedFileProvider"
+        android:name="androidx.car.app.sample.showcase.common.screens.templatelayouts.listtemplates.DelayedFileProvider"
         android:authorities="com.showcase.fileprovider"
         android:exported="false"
         android:grantUriPermissions="true">
diff --git a/car/app/app-samples/showcase/automotive/src/main/AndroidManifestWithSdkVersion.xml b/car/app/app-samples/showcase/automotive/src/main/AndroidManifestWithSdkVersion.xml
index 13e311ad..b219ed9 100644
--- a/car/app/app-samples/showcase/automotive/src/main/AndroidManifestWithSdkVersion.xml
+++ b/car/app/app-samples/showcase/automotive/src/main/AndroidManifestWithSdkVersion.xml
@@ -95,12 +95,13 @@
     </service>
 
     <service
-        android:name=".common.navigation.NavigationNotificationService"
+        android:name=".common.screens.navigationdemos.NavigationNotificationService"
         android:exported="true">
     </service>
 
     <provider
-        android:name="androidx.car.app.sample.showcase.common.textandicons.DelayedFileProvider"
+        android:name="androidx.car.app.sample.showcase.common.screens.templatelayouts
+        .listtemplates.DelayedFileProvider"
         android:authorities="com.showcase.fileprovider"
         android:exported="false"
         android:grantUriPermissions="true">
diff --git a/car/app/app-samples/showcase/common/src/main/AndroidManifest.xml b/car/app/app-samples/showcase/common/src/main/AndroidManifest.xml
index f44bb20..2dc6ab5 100644
--- a/car/app/app-samples/showcase/common/src/main/AndroidManifest.xml
+++ b/car/app/app-samples/showcase/common/src/main/AndroidManifest.xml
@@ -38,6 +38,6 @@
 
     <application
         android:supportsRtl="true">
-        <activity android:name=".templates.SignInWithGoogleActivity" />
+        <activity android:name=".screens.templatelayouts.SignInWithGoogleActivity" />
     </application>
 </manifest>
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/SelectableListsDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/SelectableListsDemoScreen.java
deleted file mode 100644
index b7fd241..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/SelectableListsDemoScreen.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common;
-
-import static androidx.car.app.CarToast.LENGTH_LONG;
-import static androidx.car.app.model.Action.BACK;
-import static androidx.car.app.model.CarColor.YELLOW;
-
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.text.SpannableString;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.CarToast;
-import androidx.car.app.Screen;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.ActionStrip;
-import androidx.car.app.model.CarIcon;
-import androidx.car.app.model.ItemList;
-import androidx.car.app.model.ListTemplate;
-import androidx.car.app.model.Row;
-import androidx.car.app.model.SectionedItemList;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.common.Utils;
-import androidx.core.graphics.drawable.IconCompat;
-
-/** A screen demonstrating selectable lists. */
-public final class SelectableListsDemoScreen extends Screen {
-
-    public SelectableListsDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-    }
-
-    private boolean mIsEnabled = true;
-
-    // Get colorized spannable string
-    private static CharSequence getColoredString(String str, boolean isEnabled) {
-        if (isEnabled && !str.isEmpty()) {
-            SpannableString ss = new SpannableString(str);
-            Utils.colorize(ss, YELLOW, 0, str.length());
-            return ss;
-        }
-        return str;
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        ListTemplate.Builder templateBuilder = new ListTemplate.Builder();
-
-        // The Image to be displayed in a row
-        Resources resources = getCarContext().getResources();
-        Bitmap bitmap = BitmapFactory.decodeResource(resources, R.drawable.test_image_square);
-        IconCompat mImage = IconCompat.createWithBitmap(bitmap);
-
-        ItemList radioList =
-                new ItemList.Builder()
-                        .addItem(new Row.Builder()
-                                .setTitle(getCarContext()
-                                        .getString(R.string.option_row_radio_title))
-                                .addText(getCarContext().getString(
-                                        R.string.additional_text))
-                                .setEnabled(mIsEnabled)
-                                .build())
-                        .addItem(new Row.Builder()
-                                .setImage(
-                                        new CarIcon.Builder(
-                                                IconCompat.createWithResource(
-                                                        getCarContext(),
-                                                        R.drawable
-                                                                .ic_fastfood_white_48dp))
-                                                .build(),
-                                        Row.IMAGE_TYPE_ICON)
-                                .setTitle(getCarContext()
-                                        .getString(R.string.option_row_radio_icon_title))
-                                .addText(getCarContext().getString(
-                                        R.string.additional_text))
-                                .setEnabled(mIsEnabled)
-                                .build())
-                        .addItem(new Row.Builder()
-                                .setImage(
-                                        new CarIcon.Builder(mImage).build(),
-                                        Row.IMAGE_TYPE_LARGE)
-                                .setTitle(getCarContext()
-                                        .getString(
-                                                R.string.option_row_radio_icon_colored_text_title))
-                                .addText(getColoredString(
-                                        getCarContext().getString(
-                                                R.string.additional_text), mIsEnabled))
-                                .setEnabled(mIsEnabled)
-                                .build())
-                        .setOnSelectedListener(this::onSelected)
-                        .build();
-        templateBuilder.addSectionedList(
-                SectionedItemList.create(radioList,
-                        getCarContext().getString(R.string.sample_additional_list)));
-
-        return templateBuilder
-                .setTitle(getCarContext().getString(R.string.selectable_lists_demo_title))
-                .setActionStrip(
-                        new ActionStrip.Builder()
-                                .addAction(
-                                        new Action.Builder()
-                                                .setTitle(mIsEnabled
-                                                        ? getCarContext().getString(
-                                                        R.string.disable_all_rows)
-                                                        : getCarContext().getString(
-                                                                R.string.enable_all_rows))
-                                                .setOnClickListener(
-                                                        () -> {
-                                                            mIsEnabled = !mIsEnabled;
-                                                            invalidate();
-                                                        })
-                                                .build())
-                                .build())
-                .setHeaderAction(
-                        BACK).build();
-    }
-
-    private void onSelected(int index) {
-        CarToast.makeText(getCarContext(),
-                        getCarContext()
-                                .getString(R.string.changes_selection_to_index_toast_msg_prefix)
-                                + ":"
-                                + " " + index, LENGTH_LONG)
-                .show();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/ShowcaseSession.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/ShowcaseSession.java
index 1b2165a..5dd4719 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/ShowcaseSession.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/ShowcaseSession.java
@@ -30,13 +30,13 @@
 import androidx.car.app.Screen;
 import androidx.car.app.ScreenManager;
 import androidx.car.app.Session;
-import androidx.car.app.sample.showcase.common.misc.RequestPermissionScreen;
-import androidx.car.app.sample.showcase.common.misc.ResultDemoScreen;
-import androidx.car.app.sample.showcase.common.navigation.NavigationNotificationService;
-import androidx.car.app.sample.showcase.common.navigation.NavigationNotificationsDemoScreen;
-import androidx.car.app.sample.showcase.common.navigation.routing.NavigatingDemoScreen;
 import androidx.car.app.sample.showcase.common.renderer.Renderer;
 import androidx.car.app.sample.showcase.common.renderer.SurfaceController;
+import androidx.car.app.sample.showcase.common.screens.ResultDemoScreen;
+import androidx.car.app.sample.showcase.common.screens.navigationdemos.NavigationNotificationService;
+import androidx.car.app.sample.showcase.common.screens.navigationdemos.NavigationNotificationsDemoScreen;
+import androidx.car.app.sample.showcase.common.screens.navigationdemos.navigationtemplates.NavigatingDemoScreen;
+import androidx.car.app.sample.showcase.common.screens.userinteractions.RequestPermissionScreen;
 import androidx.lifecycle.DefaultLifecycleObserver;
 import androidx.lifecycle.Lifecycle;
 import androidx.lifecycle.LifecycleOwner;
@@ -76,6 +76,19 @@
             return new ResultDemoScreen(getCarContext());
         }
 
+        boolean shouldLoadScreen =
+                getCarContext()
+                        .getSharedPreferences(ShowcaseService.SHARED_PREF_KEY, Context.MODE_PRIVATE)
+                        .getBoolean(ShowcaseService.LOADING_KEY, false);
+        if (shouldLoadScreen) {
+            // Reset so that we don't require it next time
+            getCarContext()
+                    .getSharedPreferences(ShowcaseService.SHARED_PREF_KEY, Context.MODE_PRIVATE)
+                    .edit()
+                    .putBoolean(ShowcaseService.LOADING_KEY, false)
+                    .apply();
+        }
+
         // For demo purposes this uses a shared preference setting to store whether we should
         // pre-seed the screen back stack. This allows the app to have a way to go back to the
         // home/start screen making the home/start screen the 0th position.
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/TaskRestrictionDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/TaskRestrictionDemoScreen.java
deleted file mode 100644
index b517b4c..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/TaskRestrictionDemoScreen.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common;
-
-import static androidx.car.app.CarToast.LENGTH_LONG;
-import static androidx.car.app.model.Action.BACK;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.CarToast;
-import androidx.car.app.Screen;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.ActionStrip;
-import androidx.car.app.model.CarIcon;
-import androidx.car.app.model.ItemList;
-import androidx.car.app.model.ListTemplate;
-import androidx.car.app.model.MessageTemplate;
-import androidx.car.app.model.OnClickListener;
-import androidx.car.app.model.Row;
-import androidx.car.app.model.Template;
-import androidx.car.app.model.Toggle;
-import androidx.core.graphics.drawable.IconCompat;
-
-/** Screen for demonstrating task flow limitations. */
-public final class TaskRestrictionDemoScreen extends Screen {
-
-    private static final int MAX_STEPS_ALLOWED = 4;
-
-    private final int mStep;
-    private boolean mIsBackOperation = false;
-    private boolean mFirstToggleState = false;
-    private boolean mSecondToggleState = false;
-    private boolean mSecondToggleEnabled = false;
-    private int mImageType = Row.IMAGE_TYPE_ICON;
-
-    public TaskRestrictionDemoScreen(int step, @NonNull CarContext carContext) {
-        super(carContext);
-
-        this.mStep = step;
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        // Last step must either be a PaneTemplate, MessageTemplate or NavigationTemplate.
-        if (mStep == MAX_STEPS_ALLOWED) {
-            OnClickListener onClickListener = () ->
-                    getScreenManager()
-                            .pushForResult(
-                                    new TaskRestrictionDemoScreen(
-                                            mStep + 1,
-                                            getCarContext()),
-                                    result ->
-                                            mIsBackOperation = true);
-
-            return new MessageTemplate.Builder(
-                    getCarContext().getString(R.string.task_limit_reached_msg))
-                    .setHeaderAction(BACK)
-                    .addAction(
-                            new Action.Builder()
-                                    .setTitle(getCarContext().getString(
-                                            R.string.try_anyway_action_title))
-                                    .setOnClickListener(onClickListener)
-                                    .build())
-                    .build();
-        }
-
-        Toggle mFirstToggle = new Toggle.Builder((checked) -> {
-            mSecondToggleEnabled = checked;
-            if (checked) {
-                CarToast.makeText(getCarContext(), R.string.toggle_test_enabled,
-                        LENGTH_LONG).show();
-            } else {
-                CarToast.makeText(getCarContext(), R.string.toggle_test_disabled,
-                        LENGTH_LONG).show();
-            }
-            mFirstToggleState = !mFirstToggleState;
-            invalidate();
-        }).setChecked(mFirstToggleState).build();
-
-        Toggle mSecondToggle = new Toggle.Builder((checked) -> {
-            mSecondToggleState = !mSecondToggleState;
-            invalidate();
-        }).setChecked(mSecondToggleState).setEnabled(mSecondToggleEnabled).build();
-
-        ItemList.Builder builder = new ItemList.Builder();
-        builder.addItem(
-                        new Row.Builder()
-                                .setTitle(getCarContext().getString(R.string.task_step_of_title,
-                                        mStep,
-                                        MAX_STEPS_ALLOWED))
-                                .addText(getCarContext().getString(R.string.task_step_of_text))
-                                .setOnClickListener(
-                                        () ->
-                                                getScreenManager()
-                                                        .pushForResult(
-                                                                new TaskRestrictionDemoScreen(
-                                                                        mStep + 1, getCarContext()),
-                                                                result -> mIsBackOperation = true))
-                                .build())
-                .addItem(
-                        new Row.Builder()
-                                .setTitle(getCarContext().getString(
-                                        R.string.toggle_test_first_toggle_title))
-                                .addText(getCarContext().getString(
-                                        R.string.toggle_test_first_toggle_text))
-                                .setToggle(mFirstToggle)
-                                .build())
-                .addItem(
-                        new Row.Builder()
-                                .setTitle(getCarContext().getString(
-                                        R.string.toggle_test_second_toggle_title))
-                                .addText(getCarContext().getString(
-                                        R.string.toggle_test_second_toggle_text))
-                                .setToggle(mSecondToggle)
-                                .build())
-                .addItem(
-                        new Row.Builder()
-                                .setTitle(getCarContext().getString(R.string.image_test_title))
-                                .addText(getCarContext().getString(R.string.image_test_text))
-                                .setImage(
-                                        new CarIcon.Builder(
-                                                IconCompat.createWithResource(
-                                                        getCarContext(),
-                                                        R.drawable.ic_fastfood_yellow_48dp))
-                                                .build(),
-                                        mImageType)
-                                .setOnClickListener(
-                                        () -> {
-                                            mImageType =
-                                                    mImageType == Row.IMAGE_TYPE_ICON
-                                                            ? Row.IMAGE_TYPE_LARGE
-                                                            : Row.IMAGE_TYPE_ICON;
-                                            invalidate();
-                                        })
-                                .build());
-
-        if (mIsBackOperation) {
-            builder.addItem(
-                    new Row.Builder()
-                            .setTitle(getCarContext().getString(R.string.additional_data_title))
-                            .addText(getCarContext().getString(R.string.additional_data_text))
-                            .build());
-        }
-
-        return new ListTemplate.Builder()
-                .setSingleList(builder.build())
-                .setTitle(getCarContext().getString(R.string.task_restriction_demo_title))
-                .setHeaderAction(BACK)
-                .setActionStrip(
-                        new ActionStrip.Builder()
-                                .addAction(
-                                        new Action.Builder()
-                                                .setTitle(getCarContext().getString(
-                                                        R.string.home_caps_action_title))
-                                                .setOnClickListener(
-                                                        () -> getScreenManager().popToRoot())
-                                                .build())
-                                .build())
-                .build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/CarHardwareDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/CarHardwareDemoScreen.java
deleted file mode 100644
index de76440..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/CarHardwareDemoScreen.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.misc;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.Screen;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.ActionStrip;
-import androidx.car.app.model.CarIcon;
-import androidx.car.app.model.Template;
-import androidx.car.app.navigation.model.NavigationTemplate;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.sample.showcase.common.ShowcaseSession;
-import androidx.car.app.sample.showcase.common.renderer.CarHardwareRenderer;
-import androidx.core.graphics.drawable.IconCompat;
-import androidx.lifecycle.DefaultLifecycleObserver;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleOwner;
-
-/** Simple demo of how access car hardware information. */
-public final class CarHardwareDemoScreen extends Screen {
-
-    final CarHardwareRenderer mCarHardwareRenderer;
-
-    public CarHardwareDemoScreen(@NonNull CarContext carContext,
-            @NonNull ShowcaseSession showcaseSession) {
-        super(carContext);
-        mCarHardwareRenderer = new CarHardwareRenderer(carContext);
-        Lifecycle lifecycle = getLifecycle();
-        lifecycle.addObserver(new DefaultLifecycleObserver() {
-
-            @NonNull
-            final ShowcaseSession mShowcaseSession = showcaseSession;
-
-            @Override
-            public void onResume(@NonNull LifecycleOwner owner) {
-                // When this screen is visible set the SurfaceRenderer to show
-                // CarHardware information.
-                mShowcaseSession.overrideRenderer(mCarHardwareRenderer);
-            }
-
-            @Override
-            public void onPause(@NonNull LifecycleOwner owner) {
-                // When this screen is hidden set the SurfaceRenderer to show
-                // CarHardware information.
-                mShowcaseSession.overrideRenderer(null);
-            }
-        });
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        ActionStrip actionStrip =
-                new ActionStrip.Builder()
-                        // Add a Button to show the CarHardware info screen
-                        .addAction(new Action.Builder()
-                                .setIcon(
-                                        new CarIcon.Builder(
-                                                IconCompat.createWithResource(
-                                                        getCarContext(),
-                                                        R.drawable.info_gm_grey_24dp))
-                                                .build())
-                                .setOnClickListener(() -> getScreenManager().push(
-                                        new CarHardwareInfoScreen(getCarContext())))
-                                .build())
-                        .addAction(
-                                new Action.Builder()
-                                        .setTitle(getCarContext()
-                                                .getString(R.string.back_caps_action_title))
-                                        .setOnClickListener(this::finish)
-                                        .build())
-                        .build();
-
-        return new NavigationTemplate.Builder().setActionStrip(actionStrip).build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/CarHardwareInfoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/CarHardwareInfoScreen.java
deleted file mode 100644
index 9140932..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/CarHardwareInfoScreen.java
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.misc;
-
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.car.app.CarContext;
-import androidx.car.app.Screen;
-import androidx.car.app.hardware.CarHardwareManager;
-import androidx.car.app.hardware.common.CarValue;
-import androidx.car.app.hardware.common.OnCarDataAvailableListener;
-import androidx.car.app.hardware.info.CarInfo;
-import androidx.car.app.hardware.info.EnergyProfile;
-import androidx.car.app.hardware.info.Model;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.Pane;
-import androidx.car.app.model.PaneTemplate;
-import androidx.car.app.model.Row;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.core.content.ContextCompat;
-import androidx.lifecycle.DefaultLifecycleObserver;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleOwner;
-
-import java.util.concurrent.Executor;
-
-/**
- * Creates a screen that show the static information (such as model and energy profile) available
- * via CarHardware interfaces.
- */
-public final class CarHardwareInfoScreen extends Screen {
-    private static final String TAG = "showcase";
-
-    boolean mHasModelPermission;
-    boolean mHasEnergyProfilePermission;
-    final Executor mCarHardwareExecutor;
-
-    /**
-     * Value fetched from CarHardwareManager containing model information.
-     *
-     * <p>It is requested asynchronously and can be {@code null} until the response is
-     * received.
-     */
-    @Nullable
-    Model mModel;
-
-    /**
-     * Value fetched from CarHardwareManager containing what type of fuel/ports the car has.
-     *
-     * <p>It is requested asynchronously and can be {@code null} until the response is
-     * received.
-     */
-    @Nullable
-    EnergyProfile mEnergyProfile;
-
-    OnCarDataAvailableListener<Model> mModelListener = data -> {
-        synchronized (this) {
-            Log.i(TAG, "Received model information: " + data);
-            mModel = data;
-            invalidate();
-        }
-    };
-
-    OnCarDataAvailableListener<EnergyProfile> mEnergyProfileListener = data -> {
-        synchronized (this) {
-            Log.i(TAG, "Received energy profile information: " + data);
-            mEnergyProfile = data;
-            invalidate();
-        }
-    };
-
-    public CarHardwareInfoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-        mCarHardwareExecutor = ContextCompat.getMainExecutor(getCarContext());
-        Lifecycle lifecycle = getLifecycle();
-        lifecycle.addObserver(new DefaultLifecycleObserver() {
-
-            @Override
-            public void onCreate(@NonNull LifecycleOwner owner) {
-                CarHardwareManager carHardwareManager =
-                        getCarContext().getCarService(CarHardwareManager.class);
-                CarInfo carInfo = carHardwareManager.getCarInfo();
-
-                // Request any single shot values.
-                mModel = null;
-                try {
-                    carInfo.fetchModel(mCarHardwareExecutor, mModelListener);
-                    mHasModelPermission = true;
-                } catch (SecurityException e) {
-                    mHasModelPermission = false;
-                }
-
-                mEnergyProfile = null;
-                try {
-                    carInfo.fetchEnergyProfile(mCarHardwareExecutor, mEnergyProfileListener);
-                    mHasEnergyProfilePermission = true;
-                } catch (SecurityException e) {
-                    mHasEnergyProfilePermission = false;
-                }
-            }
-
-        });
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        Pane.Builder paneBuilder = new Pane.Builder();
-        if (allInfoAvailable()) {
-            Row.Builder modelRowBuilder = new Row.Builder()
-                    .setTitle(getCarContext().getString(R.string.model_info));
-            if (!mHasModelPermission) {
-                modelRowBuilder.addText(getCarContext().getString(R.string.no_model_permission));
-            } else {
-                StringBuilder info = new StringBuilder();
-                if (mModel.getManufacturer().getStatus() != CarValue.STATUS_SUCCESS) {
-                    info.append(getCarContext().getString(R.string.manufacturer_unavailable));
-                    info.append(", ");
-                } else {
-                    info.append(mModel.getManufacturer().getValue());
-                    info.append(", ");
-                }
-                if (mModel.getName().getStatus() != CarValue.STATUS_SUCCESS) {
-                    info.append(getCarContext().getString(R.string.model_unavailable));
-                    info.append(", ");
-                } else {
-                    info.append(mModel.getName().getValue());
-                    info.append(", ");
-                }
-                if (mModel.getYear().getStatus() != CarValue.STATUS_SUCCESS) {
-                    info.append(getCarContext().getString(R.string.year_unavailable));
-                } else {
-                    info.append(mModel.getYear().getValue());
-                }
-                modelRowBuilder.addText(info);
-            }
-            paneBuilder.addRow(modelRowBuilder.build());
-
-            Row.Builder energyProfileRowBuilder = new Row.Builder()
-                    .setTitle(getCarContext().getString(R.string.energy_profile));
-            if (!mHasEnergyProfilePermission) {
-                energyProfileRowBuilder.addText(getCarContext()
-                        .getString(R.string.no_energy_profile_permission));
-            } else {
-                StringBuilder fuelInfo = new StringBuilder();
-                if (mEnergyProfile.getFuelTypes().getStatus() != CarValue.STATUS_SUCCESS) {
-                    fuelInfo.append(getCarContext().getString(R.string.fuel_types));
-                    fuelInfo.append(": ");
-                    fuelInfo.append(getCarContext().getString(R.string.unavailable));
-                } else {
-                    fuelInfo.append(getCarContext().getString(R.string.fuel_types));
-                    fuelInfo.append(": ");
-                    for (int fuelType : mEnergyProfile.getFuelTypes().getValue()) {
-                        fuelInfo.append(fuelTypeAsString(fuelType));
-                        fuelInfo.append(" ");
-                    }
-                }
-                energyProfileRowBuilder.addText(fuelInfo);
-                StringBuilder evInfo = new StringBuilder();
-                if (mEnergyProfile.getEvConnectorTypes().getStatus() != CarValue.STATUS_SUCCESS) {
-                    evInfo.append(" ");
-                    evInfo.append(getCarContext().getString(R.string.ev_connector_types));
-                    evInfo.append(": ");
-                    evInfo.append(getCarContext().getString(R.string.unavailable));
-                } else {
-                    evInfo.append(getCarContext().getString(R.string.ev_connector_types));
-                    evInfo.append(": ");
-                    for (int connectorType : mEnergyProfile.getEvConnectorTypes().getValue()) {
-                        evInfo.append(evConnectorAsString(connectorType));
-                        evInfo.append(" ");
-                    }
-                }
-                energyProfileRowBuilder.addText(evInfo);
-            }
-            paneBuilder.addRow(energyProfileRowBuilder.build());
-        } else {
-            paneBuilder.setLoading(true);
-        }
-        return new PaneTemplate.Builder(paneBuilder.build())
-                .setHeaderAction(Action.BACK)
-                .setTitle(getCarContext().getString(R.string.car_hardware_info))
-                .build();
-    }
-
-    private boolean allInfoAvailable() {
-        if (mHasModelPermission && mModel == null) {
-            return false;
-        }
-        if (mHasEnergyProfilePermission && mEnergyProfile == null) {
-            return false;
-        }
-        return true;
-    }
-
-    private String fuelTypeAsString(int fuelType) {
-        switch (fuelType) {
-            case EnergyProfile.FUEL_TYPE_UNLEADED:
-                return "UNLEADED";
-            case EnergyProfile.FUEL_TYPE_LEADED:
-                return "LEADED";
-            case EnergyProfile.FUEL_TYPE_DIESEL_1:
-                return "DIESEL_1";
-            case EnergyProfile.FUEL_TYPE_DIESEL_2:
-                return "DIESEL_2";
-            case EnergyProfile.FUEL_TYPE_BIODIESEL:
-                return "BIODIESEL";
-            case EnergyProfile.FUEL_TYPE_E85:
-                return "E85";
-            case EnergyProfile.FUEL_TYPE_LPG:
-                return "LPG";
-            case EnergyProfile.FUEL_TYPE_CNG:
-                return "CNG";
-            case EnergyProfile.FUEL_TYPE_LNG:
-                return "LNG";
-            case EnergyProfile.FUEL_TYPE_ELECTRIC:
-                return "ELECTRIC";
-            case EnergyProfile.FUEL_TYPE_HYDROGEN:
-                return "HYDROGEN";
-            case EnergyProfile.FUEL_TYPE_OTHER:
-                return "OTHER";
-            case EnergyProfile.FUEL_TYPE_UNKNOWN:
-            default:
-                return "UNKNOWN";
-        }
-    }
-
-    private String evConnectorAsString(int evConnectorType) {
-        switch (evConnectorType) {
-            case EnergyProfile.EVCONNECTOR_TYPE_J1772:
-                return "J1772";
-            case EnergyProfile.EVCONNECTOR_TYPE_MENNEKES:
-                return "MENNEKES";
-            case EnergyProfile.EVCONNECTOR_TYPE_CHADEMO:
-                return "CHADEMO";
-            case EnergyProfile.EVCONNECTOR_TYPE_COMBO_1:
-                return "COMBO_1";
-            case EnergyProfile.EVCONNECTOR_TYPE_COMBO_2:
-                return "COMBO_2";
-            case EnergyProfile.EVCONNECTOR_TYPE_TESLA_ROADSTER:
-                return "TESLA_ROADSTER";
-            case EnergyProfile.EVCONNECTOR_TYPE_TESLA_HPWC:
-                return "TESLA_HPWC";
-            case EnergyProfile.EVCONNECTOR_TYPE_TESLA_SUPERCHARGER:
-                return "TESLA_SUPERCHARGER";
-            case EnergyProfile.EVCONNECTOR_TYPE_GBT:
-                return "GBT";
-            case EnergyProfile.EVCONNECTOR_TYPE_GBT_DC:
-                return "GBT_DC";
-            case EnergyProfile.EVCONNECTOR_TYPE_SCAME:
-                return "SCAME";
-            case EnergyProfile.EVCONNECTOR_TYPE_OTHER:
-                return "OTHER";
-            case EnergyProfile.EVCONNECTOR_TYPE_UNKNOWN:
-            default:
-                return "UNKNOWN";
-        }
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/ColorDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/ColorDemoScreen.java
deleted file mode 100644
index a447320..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/ColorDemoScreen.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.misc;
-
-import static androidx.car.app.model.Action.BACK;
-import static androidx.car.app.model.CarColor.BLUE;
-import static androidx.car.app.model.CarColor.GREEN;
-import static androidx.car.app.model.CarColor.PRIMARY;
-import static androidx.car.app.model.CarColor.RED;
-import static androidx.car.app.model.CarColor.SECONDARY;
-import static androidx.car.app.model.CarColor.YELLOW;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.Screen;
-import androidx.car.app.model.CarIcon;
-import androidx.car.app.model.ItemList;
-import androidx.car.app.model.ListTemplate;
-import androidx.car.app.model.Row;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.sample.showcase.common.common.Utils;
-import androidx.core.graphics.drawable.IconCompat;
-
-/** Creates a screen that demonstrate the usage of colored texts and icons in the library. */
-public final class ColorDemoScreen extends Screen {
-    public ColorDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        ItemList.Builder listBuilder = new ItemList.Builder();
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.example_title, 1))
-                        .addText(Utils.colorize(getCarContext().getString(R.string.example_1_text),
-                                RED, 16, 3))
-                        .setImage(new CarIcon.Builder(
-                                IconCompat.createWithResource(
-                                        getCarContext(),
-                                        R.drawable.ic_fastfood_white_48dp))
-                                .setTint(RED)
-                                .build())
-                        .build());
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.example_title, 2))
-                        .addText(Utils.colorize(getCarContext().getString(R.string.example_2_text),
-                                GREEN, 16, 5))
-                        .setImage(new CarIcon.Builder(
-                                IconCompat.createWithResource(
-                                        getCarContext(),
-                                        R.drawable.ic_fastfood_white_48dp))
-                                .setTint(GREEN)
-                                .build())
-                        .build());
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.example_title, 3))
-                        .addText(Utils.colorize(getCarContext().getString(R.string.example_3_text),
-                                BLUE, 16, 4))
-                        .setImage(new CarIcon.Builder(
-                                IconCompat.createWithResource(
-                                        getCarContext(),
-                                        R.drawable.ic_fastfood_white_48dp))
-                                .setTint(BLUE)
-                                .build())
-                        .build());
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.example_title, 4))
-                        .addText(Utils.colorize(getCarContext().getString(R.string.example_4_text),
-                                YELLOW, 16, 6))
-                        .setImage(new CarIcon.Builder(
-                                IconCompat.createWithResource(
-                                        getCarContext(),
-                                        R.drawable.ic_fastfood_white_48dp))
-                                .setTint(YELLOW)
-                                .build())
-                        .build());
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.example_title, 5))
-                        .addText(Utils.colorize(getCarContext().getString(R.string.example_5_text),
-                                PRIMARY, 19, 7))
-                        .setImage(new CarIcon.Builder(
-                                IconCompat.createWithResource(
-                                        getCarContext(),
-                                        R.drawable.ic_fastfood_white_48dp))
-                                .setTint(PRIMARY)
-                                .build())
-                        .build());
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.example_title, 6))
-                        .addText(
-                                Utils.colorize(
-                                        getCarContext().getString(R.string.example_6_text),
-                                        SECONDARY, 19, 9))
-                        .setImage(new CarIcon.Builder(
-                                IconCompat.createWithResource(
-                                        getCarContext(),
-                                        R.drawable.ic_fastfood_white_48dp))
-                                .setTint(SECONDARY)
-                                .build())
-                        .build());
-
-        return new ListTemplate.Builder()
-                .setSingleList(listBuilder.build())
-                .setTitle(getCarContext().getString(R.string.color_demo))
-                .setHeaderAction(BACK)
-                .build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/ContentLimitsDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/ContentLimitsDemoScreen.java
deleted file mode 100644
index d50f284..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/ContentLimitsDemoScreen.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.misc;
-
-import static androidx.car.app.model.Action.BACK;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.Screen;
-import androidx.car.app.constraints.ConstraintManager;
-import androidx.car.app.model.ItemList;
-import androidx.car.app.model.ListTemplate;
-import androidx.car.app.model.Row;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.R;
-
-/**
- * A {@link Screen} that shows examples on how to query for various content limits via the
- * {@lnk ConstraintManager} API.
- */
-public class ContentLimitsDemoScreen extends Screen {
-
-    public ContentLimitsDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        ConstraintManager manager = getCarContext().getCarService(ConstraintManager.class);
-        ItemList.Builder listBuilder = new ItemList.Builder();
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.list_limit))
-                        .addText(Integer.toString(manager.getContentLimit(
-                                ConstraintManager.CONTENT_LIMIT_TYPE_LIST)))
-                        .build());
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.grid_limit))
-                        .addText(Integer.toString(manager.getContentLimit(
-                                ConstraintManager.CONTENT_LIMIT_TYPE_GRID)))
-                        .build());
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.pane_limit))
-                        .addText(Integer.toString(manager.getContentLimit(
-                                ConstraintManager.CONTENT_LIMIT_TYPE_PANE)))
-                        .build());
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.place_list_limit))
-                        .addText(Integer.toString(manager.getContentLimit(
-                                ConstraintManager.CONTENT_LIMIT_TYPE_PLACE_LIST)))
-                        .build());
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.route_list_limit))
-                        .addText(Integer.toString(manager.getContentLimit(
-                                ConstraintManager.CONTENT_LIMIT_TYPE_ROUTE_LIST)))
-                        .build());
-
-
-        return new ListTemplate.Builder()
-                .setSingleList(listBuilder.build())
-                .setTitle(getCarContext().getString(R.string.content_limits))
-                .setHeaderAction(BACK)
-                .build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/LoadingDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/LoadingDemoScreen.java
deleted file mode 100644
index 1c23c76..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/LoadingDemoScreen.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.misc;
-
-import static androidx.car.app.model.Action.BACK;
-
-import android.os.Handler;
-import android.os.Looper;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.Screen;
-import androidx.car.app.model.Pane;
-import androidx.car.app.model.PaneTemplate;
-import androidx.car.app.model.Row;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.lifecycle.DefaultLifecycleObserver;
-import androidx.lifecycle.LifecycleOwner;
-
-/** Creates a screen that shows loading states in a pane. */
-public final class LoadingDemoScreen extends Screen implements DefaultLifecycleObserver {
-    private static final int LOADING_TIME_MILLIS = 2000;
-    private boolean mIsFinishedLoading = false;
-    private final Handler mHandler = new Handler(Looper.getMainLooper());
-
-    public LoadingDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-        getLifecycle().addObserver(this);
-    }
-
-    @Override
-    @SuppressWarnings({"FutureReturnValueIgnored"})
-    public void onStart(@NonNull LifecycleOwner owner) {
-        // Post a message that finishes loading the template after some time.
-        mHandler.postDelayed(
-                () -> {
-                    mIsFinishedLoading = true;
-                    invalidate();
-                },
-                LOADING_TIME_MILLIS);
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        Pane.Builder paneBuilder = new Pane.Builder();
-
-        if (!mIsFinishedLoading) {
-            paneBuilder.setLoading(true);
-        } else {
-            paneBuilder.addRow(new Row.Builder()
-                    .setTitle(getCarContext().getString(R.string.loading_demo_row_title))
-                    .build());
-        }
-
-        return new PaneTemplate.Builder(paneBuilder.build())
-                .setTitle(getCarContext().getString(R.string.loading_demo_title))
-                .setHeaderAction(BACK)
-                .build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/MiscDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/MiscDemoScreen.java
deleted file mode 100644
index 06ae337..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/MiscDemoScreen.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.misc;
-
-import static androidx.car.app.model.Action.BACK;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.Screen;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.ActionStrip;
-import androidx.car.app.model.ItemList;
-import androidx.car.app.model.ListTemplate;
-import androidx.car.app.model.Row;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.sample.showcase.common.ShowcaseSession;
-
-/** Creates a screen that has an assortment of API demos. */
-public final class MiscDemoScreen extends Screen {
-    static final String MARKER = "MiscDemoScreen";
-    private static final int MAX_PAGES = 2;
-
-    private final int mPage;
-
-    @NonNull
-    private final ShowcaseSession mShowcaseSession;
-
-    public MiscDemoScreen(@NonNull CarContext carContext,
-            @NonNull ShowcaseSession showcaseSession) {
-        this(carContext, showcaseSession, 0);
-    }
-
-    public MiscDemoScreen(@NonNull CarContext carContext,
-            @NonNull ShowcaseSession showcaseSession, int page) {
-        super(carContext);
-        setMarker(MARKER);
-        mShowcaseSession = showcaseSession;
-        mPage = page;
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        ItemList.Builder listBuilder = new ItemList.Builder();
-
-        switch (mPage) {
-            case 0:
-                listBuilder.addItem(createRow(getCarContext().getString(R.string.notification_demo),
-                        new NotificationDemoScreen(getCarContext())));
-                listBuilder.addItem(createRow(getCarContext().getString(R.string.pop_to_title),
-                        new PopToDemoScreen(getCarContext())));
-                listBuilder.addItem(
-                        createRow(getCarContext().getString(R.string.loading_demo_title),
-                                new LoadingDemoScreen(getCarContext())));
-                listBuilder.addItem(
-                        createRow(getCarContext().getString(R.string.request_permissions_title),
-                                new RequestPermissionScreen(getCarContext())));
-                listBuilder.addItem(
-                        createRow(getCarContext().getString(R.string.finish_app_demo_title),
-                                new FinishAppScreen(getCarContext())));
-                listBuilder.addItem(
-                        createRow(getCarContext().getString(R.string.car_hardware_demo_title),
-                                new CarHardwareDemoScreen(getCarContext(), mShowcaseSession)));
-                break;
-            case 1:
-                listBuilder.addItem(
-                        createRow(getCarContext().getString(R.string.content_limits_demo_title),
-                                new ContentLimitsDemoScreen(getCarContext())));
-                listBuilder.addItem(createRow(getCarContext().getString(R.string.color_demo),
-                        new ColorDemoScreen(getCarContext())));
-                break;
-
-        }
-
-        ListTemplate.Builder builder = new ListTemplate.Builder()
-                .setSingleList(listBuilder.build())
-                .setTitle(getCarContext().getString(R.string.misc_demo_title))
-                .setHeaderAction(BACK);
-
-        if (mPage + 1 < MAX_PAGES) {
-            builder.setActionStrip(new ActionStrip.Builder()
-                    .addAction(new Action.Builder()
-                            .setTitle(getCarContext().getString(R.string.more_action_title))
-                            .setOnClickListener(() -> {
-                                getScreenManager().push(
-                                        new MiscDemoScreen(getCarContext(), mShowcaseSession,
-                                                mPage + 1));
-                            })
-                            .build())
-                    .build());
-        }
-
-        return builder.build();
-    }
-
-    private Row createRow(String title, Screen screen) {
-        return new Row.Builder()
-                .setTitle(title)
-                .setOnClickListener(() -> getScreenManager().push(screen))
-                .build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/NotificationDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/NotificationDemoScreen.java
deleted file mode 100644
index ee883ad..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/NotificationDemoScreen.java
+++ /dev/null
@@ -1,361 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.misc;
-
-import static java.util.concurrent.TimeUnit.SECONDS;
-
-import android.annotation.SuppressLint;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.graphics.BitmapFactory;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.CarToast;
-import androidx.car.app.Screen;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.CarColor;
-import androidx.car.app.model.CarIcon;
-import androidx.car.app.model.GridItem;
-import androidx.car.app.model.GridTemplate;
-import androidx.car.app.model.ItemList;
-import androidx.car.app.model.Template;
-import androidx.car.app.notification.CarAppExtender;
-import androidx.car.app.notification.CarNotificationManager;
-import androidx.car.app.notification.CarPendingIntent;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.sample.showcase.common.ShowcaseService;
-import androidx.core.app.NotificationChannelCompat;
-import androidx.core.app.NotificationCompat;
-import androidx.core.graphics.drawable.IconCompat;
-import androidx.lifecycle.DefaultLifecycleObserver;
-import androidx.lifecycle.LifecycleOwner;
-
-/** A simple screen that demonstrates how to use notifications in a car app. */
-public final class NotificationDemoScreen extends Screen implements DefaultLifecycleObserver {
-
-    static final long NOTIFICATION_DELAY_IN_MILLIS = SECONDS.toMillis(1);
-    private static final String NOTIFICATION_CHANNEL_ID = "channel_00";
-    private static final CharSequence NOTIFICATION_CHANNEL_NAME = "Default Channel";
-    private static final int NOTIFICATION_ID = 1001;
-    private static final String NOTIFICATION_CHANNEL_HIGH_ID = "channel_01";
-    private static final CharSequence NOTIFICATION_CHANNEL_HIGH_NAME = "High Channel";
-    private static final String NOTIFICATION_CHANNEL_LOW_ID = "channel_02";
-    private static final CharSequence NOTIFICATION_CHANNEL_LOW_NAME = "Low Channel";
-    private static final String INTENT_ACTION_PRIMARY_PHONE =
-            "androidx.car.app.sample.showcase.common.INTENT_ACTION_PRIMARY_PHONE";
-    private static final String INTENT_ACTION_SECONDARY_PHONE =
-            "androidx.car.app.sample.showcase.common.INTENT_ACTION_SECONDARY_PHONE";
-    private static final int MSG_SEND_NOTIFICATION = 1;
-    final Handler mHandler = new Handler(Looper.getMainLooper(), new HandlerCallback());
-
-    private final IconCompat mIcon = IconCompat.createWithResource(getCarContext(),
-            R.drawable.ic_face_24px);
-    /** A broadcast receiver that can show a toast message upon receiving a broadcast. */
-    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            CarToast.makeText(
-                            getCarContext(),
-                            getCarContext().getString(R.string.triggered_toast_msg) + ": "
-                                    + intent.getAction(),
-                            CarToast.LENGTH_SHORT)
-                    .show();
-        }
-    };
-    private int mImportance = NotificationManager.IMPORTANCE_DEFAULT;
-    private boolean mIsNavCategory = false;
-    private boolean mSetOngoing = false;
-    private int mNotificationCount = 0;
-
-    public NotificationDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-        getLifecycle().addObserver(this);
-    }
-
-    @Override
-    public void onCreate(@NonNull LifecycleOwner owner) {
-        registerBroadcastReceiver();
-    }
-
-    @Override
-    public void onDestroy(@NonNull LifecycleOwner owner) {
-        mHandler.removeMessages(MSG_SEND_NOTIFICATION);
-        CarNotificationManager.from(getCarContext()).cancelAll();
-        unregisterBroadcastReceiver();
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        ItemList.Builder listBuilder = new ItemList.Builder();
-
-        // Send a single notification with the settings configured by other buttons.
-        listBuilder.addItem(
-                new GridItem.Builder()
-                        .setTitle(getCarContext().getString(R.string.send_notification_title))
-                        .setImage(new CarIcon.Builder(mIcon).build())
-                        .setOnClickListener(this::sendNotification)
-                        .build());
-
-        // Start a repeating notification with the settings configured by other buttons.
-        listBuilder.addItem(
-                new GridItem.Builder()
-                        .setTitle(getCarContext().getString(R.string.start_notifications_title))
-                        .setImage(new CarIcon.Builder(mIcon).build())
-                        .setOnClickListener(() -> mHandler.sendMessage(
-                                mHandler.obtainMessage(MSG_SEND_NOTIFICATION)))
-                        .build());
-
-        // Stop the repeating notification and reset the count.
-        listBuilder.addItem(
-                new GridItem.Builder()
-                        .setTitle(getCarContext().getString(R.string.stop_notifications_title))
-                        .setImage(new CarIcon.Builder(mIcon).build())
-                        .setOnClickListener(() -> {
-                            mHandler.removeMessages(MSG_SEND_NOTIFICATION);
-                            CarNotificationManager.from(getCarContext()).cancelAll();
-                            mNotificationCount = 0;
-                        })
-                        .build());
-
-        // Configure the notification importance.
-        listBuilder.addItem(
-                new GridItem.Builder()
-                        .setImage(new CarIcon.Builder(mIcon).build())
-                        .setTitle(getCarContext().getString(R.string.importance_title))
-                        .setText(getImportanceString())
-                        .setOnClickListener(() -> {
-                            setImportance();
-                            invalidate();
-                        })
-                        .build());
-
-        // Configure whether the notification's category is navigation.
-        listBuilder.addItem(
-                new GridItem.Builder()
-                        .setImage(new CarIcon.Builder(mIcon).build())
-                        .setTitle(getCarContext().getString(R.string.category_title))
-                        .setText(getCategoryString())
-                        .setOnClickListener(() -> {
-                            mIsNavCategory = !mIsNavCategory;
-                            invalidate();
-                        })
-                        .build());
-
-        // Configure whether the notification is an ongoing notification.
-        listBuilder.addItem(
-                new GridItem.Builder()
-                        .setImage(new CarIcon.Builder(mIcon).build())
-                        .setTitle(getCarContext().getString(R.string.ongoing_title))
-                        .setText(String.valueOf(mSetOngoing))
-                        .setOnClickListener(() -> {
-                            mSetOngoing = !mSetOngoing;
-                            invalidate();
-                        })
-                        .build());
-
-        return new GridTemplate.Builder()
-                .setSingleList(listBuilder.build())
-                .setTitle(getCarContext().getString(R.string.notification_demo))
-                .setHeaderAction(Action.BACK)
-                .build();
-    }
-
-    void sendNotification() {
-        mNotificationCount++;
-        String title =
-                getCarContext().getString(R.string.notification_title) + ": "
-                        + getImportanceString() + ", " + mNotificationCount;
-        String text = getCarContext().getString(R.string.category_title) + ": "
-                + getCategoryString() + ", "
-                + getCarContext().getString(R.string.ongoing_title) + ": " + mSetOngoing;
-
-        switch (mImportance) {
-            case NotificationManager.IMPORTANCE_HIGH:
-                sendNotification(title, text, NOTIFICATION_CHANNEL_HIGH_ID,
-                        NOTIFICATION_CHANNEL_HIGH_NAME, NOTIFICATION_ID,
-                        NotificationManager.IMPORTANCE_HIGH);
-                break;
-            case NotificationManager.IMPORTANCE_DEFAULT:
-                sendNotification(title, text, NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME,
-                        NOTIFICATION_ID, NotificationManager.IMPORTANCE_DEFAULT);
-                break;
-            case NotificationManager.IMPORTANCE_LOW:
-                sendNotification(title, text, NOTIFICATION_CHANNEL_LOW_ID,
-                        NOTIFICATION_CHANNEL_LOW_NAME, NOTIFICATION_ID,
-                        NotificationManager.IMPORTANCE_LOW);
-                break;
-            default:
-                break;
-        }
-    }
-
-    // Suppressing 'ObsoleteSdkInt' as this code is shared between APKs with different min SDK
-    // levels
-    @SuppressLint("ObsoleteSdkInt")
-    private void sendNotification(CharSequence title, CharSequence text, String channelId,
-            CharSequence channelName, int notificationId, int importance) {
-        CarNotificationManager carNotificationManager =
-                CarNotificationManager.from(getCarContext());
-
-        NotificationChannelCompat channel = new NotificationChannelCompat.Builder(channelId,
-                importance).setName(channelName).build();
-        carNotificationManager.createNotificationChannel(channel);
-
-        NotificationCompat.Builder builder;
-        builder = new NotificationCompat.Builder(getCarContext(), channelId);
-        if (mIsNavCategory) {
-            builder.setCategory(NotificationCompat.CATEGORY_NAVIGATION);
-        }
-        builder.setOngoing(mSetOngoing);
-
-        builder.setSmallIcon(R.drawable.ic_bug_report_24px)
-                .setContentTitle(title + " (phone)")
-                .setContentText(text + " (phone)")
-                .setColor(getCarContext().getColor(androidx.car.app.R.color.carColorGreen))
-                .setColorized(true)
-                .setLargeIcon(
-                        BitmapFactory.decodeResource(
-                                getCarContext().getResources(), R.drawable.ic_hi))
-                .addAction(
-                        new NotificationCompat.Action.Builder(
-                                R.drawable.ic_face_24px,
-                                "Action1 (phone)",
-                                createPendingIntent(INTENT_ACTION_PRIMARY_PHONE))
-                                .build())
-                .addAction(
-                        R.drawable.ic_commute_24px,
-                        "Action2 (phone)",
-                        createPendingIntent(INTENT_ACTION_SECONDARY_PHONE))
-                .extend(
-                        new CarAppExtender.Builder()
-                                .setContentTitle(title)
-                                .setContentText(text)
-                                .setContentIntent(
-                                        CarPendingIntent.getCarApp(getCarContext(), 0,
-                                                new Intent(Intent.ACTION_VIEW).setComponent(
-                                                        new ComponentName(getCarContext(),
-                                                                ShowcaseService.class)), 0))
-                                .setColor(CarColor.PRIMARY)
-                                .setSmallIcon(R.drawable.ic_bug_report_24px)
-                                .setLargeIcon(
-                                        BitmapFactory.decodeResource(
-                                                getCarContext().getResources(),
-                                                R.drawable.ic_hi))
-                                .addAction(
-                                        R.drawable.ic_commute_24px,
-                                        getCarContext().getString(R.string.navigate),
-                                        getPendingIntentForNavigation())
-                                .addAction(
-                                        R.drawable.ic_face_24px,
-                                        getCarContext().getString(R.string.call_action_title),
-                                        createPendingIntentForCall())
-                                .build());
-
-        carNotificationManager.notify(notificationId, builder);
-    }
-
-    private PendingIntent createPendingIntentForCall() {
-        Intent intent = new Intent(Intent.ACTION_DIAL).setData(Uri.parse("tel:+14257232350"));
-        return CarPendingIntent.getCarApp(getCarContext(), intent.hashCode(), intent, 0);
-    }
-
-    private PendingIntent getPendingIntentForNavigation() {
-        Intent intent = new Intent(CarContext.ACTION_NAVIGATE).setData(Uri.parse("geo:0,0?q=Home"));
-        return CarPendingIntent.getCarApp(getCarContext(), intent.hashCode(), intent, 0);
-    }
-
-    private String getImportanceString() {
-        switch (mImportance) {
-            case NotificationManager.IMPORTANCE_HIGH:
-                return getCarContext().getString(R.string.high_importance);
-            case NotificationManager.IMPORTANCE_DEFAULT:
-                return getCarContext().getString(R.string.default_importance);
-            case NotificationManager.IMPORTANCE_LOW:
-                return getCarContext().getString(R.string.low_importance);
-            default:
-                return getCarContext().getString(R.string.unknown_importance);
-        }
-    }
-
-    private String getCategoryString() {
-        return mIsNavCategory ? "Navigation" : "None";
-    }
-
-    /**
-     * Change the notification importance in a rotating sequence:
-     * Low -> Default -> High -> Low...
-     */
-    private void setImportance() {
-        switch (mImportance) {
-            case NotificationManager.IMPORTANCE_HIGH:
-                mImportance = NotificationManager.IMPORTANCE_LOW;
-                break;
-            case NotificationManager.IMPORTANCE_DEFAULT:
-                mImportance = NotificationManager.IMPORTANCE_HIGH;
-                break;
-            case NotificationManager.IMPORTANCE_LOW:
-                mImportance = NotificationManager.IMPORTANCE_DEFAULT;
-                break;
-            default:
-                break;
-        }
-    }
-
-    private void registerBroadcastReceiver() {
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(INTENT_ACTION_PRIMARY_PHONE);
-        filter.addAction(INTENT_ACTION_SECONDARY_PHONE);
-
-        getCarContext().registerReceiver(mBroadcastReceiver, filter);
-    }
-
-    private void unregisterBroadcastReceiver() {
-        getCarContext().unregisterReceiver(mBroadcastReceiver);
-    }
-
-    /** Returns a pending intent with the provided intent action. */
-    private PendingIntent createPendingIntent(String intentAction) {
-        Intent intent = new Intent(intentAction);
-        return PendingIntent.getBroadcast(getCarContext(), intentAction.hashCode(), intent,
-                PendingIntent.FLAG_IMMUTABLE);
-    }
-
-    final class HandlerCallback implements Handler.Callback {
-        @Override
-        public boolean handleMessage(Message msg) {
-            if (msg.what == MSG_SEND_NOTIFICATION) {
-                sendNotification();
-                mHandler.sendMessageDelayed(
-                        mHandler.obtainMessage(MSG_SEND_NOTIFICATION),
-                        NOTIFICATION_DELAY_IN_MILLIS);
-                return true;
-            }
-            return false;
-        }
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/PopToDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/PopToDemoScreen.java
deleted file mode 100644
index 06b3699..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/PopToDemoScreen.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.misc;
-
-import static androidx.car.app.model.Action.BACK;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.Screen;
-import androidx.car.app.model.ItemList;
-import androidx.car.app.model.ListTemplate;
-import androidx.car.app.model.Row;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.R;
-
-/**
- * A {@link Screen} that allows you to push deeper in the screen stack, or pop to previous marker,
- * or pop to the root {@link Screen}.
- */
-public class PopToDemoScreen extends Screen {
-    private final int mId;
-
-    public PopToDemoScreen(@NonNull CarContext carContext) {
-        this(carContext, 0);
-    }
-
-    public PopToDemoScreen(@NonNull CarContext carContext, int id) {
-        super(carContext);
-        this.mId = id;
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        ItemList.Builder listBuilder = new ItemList.Builder();
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.pop_to_root))
-                        .setOnClickListener(() -> getScreenManager().popToRoot())
-                        .build());
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.pop_to_marker))
-                        .setOnClickListener(() -> getScreenManager().popTo(MiscDemoScreen.MARKER))
-                        .build());
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.push_stack))
-                        .setOnClickListener(
-                                () ->
-                                        getScreenManager()
-                                                .push(
-                                                        new PopToDemoScreen(
-                                                                getCarContext(), mId + 1)))
-                        .build());
-
-        return new ListTemplate.Builder()
-                .setSingleList(listBuilder.build())
-                .setTitle(getCarContext().getString(R.string.pop_to_prefix) + mId)
-                .setHeaderAction(BACK)
-                .build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/RequestPermissionScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/RequestPermissionScreen.java
deleted file mode 100644
index 73840e7..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/RequestPermissionScreen.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.misc;
-
-import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.location.LocationManager;
-import android.provider.Settings;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarAppPermission;
-import androidx.car.app.CarContext;
-import androidx.car.app.CarToast;
-import androidx.car.app.Screen;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.CarColor;
-import androidx.car.app.model.LongMessageTemplate;
-import androidx.car.app.model.MessageTemplate;
-import androidx.car.app.model.OnClickListener;
-import androidx.car.app.model.ParkedOnlyOnClickListener;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.core.location.LocationManagerCompat;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A screen to show a request for a runtime permission from the user.
- *
- * <p>Scans through the possible dangerous permissions and shows which ones have not been
- * granted in the message. Clicking on the action button will launch the permission request on
- * the phone.
- *
- * <p>If all permissions are granted, corresponding message is displayed with a refresh button which
- * will scan again when clicked.
- */
-public class RequestPermissionScreen extends Screen {
-    // This field can and should be removed once b/192386096 and/or b/192385602 have been resolved.
-    private final boolean mPreSeedMode;
-
-    /**
-     * Action which invalidates the template.
-     *
-     * <p>This can give the user a chance to revoke the permissions and then refresh will pickup
-     * the permissions that need to be granted.
-     */
-    private final Action mRefreshAction = new Action.Builder()
-            .setTitle(getCarContext().getString(R.string.refresh_action_title))
-            .setBackgroundColor(CarColor.BLUE)
-            .setOnClickListener(this::invalidate)
-            .build();
-
-    public RequestPermissionScreen(@NonNull CarContext carContext) {
-        this(carContext, false);
-    }
-
-    public RequestPermissionScreen(@NonNull CarContext carContext, boolean preSeedMode) {
-        super(carContext);
-        this.mPreSeedMode = preSeedMode;
-    }
-
-    @NonNull
-    @Override
-    @SuppressWarnings("deprecation")
-    public Template onGetTemplate() {
-        final Action headerAction = mPreSeedMode ? Action.APP_ICON : Action.BACK;
-        List<String> permissions = new ArrayList<>();
-        String[] declaredPermissions;
-        try {
-            PackageInfo info =
-                    getCarContext().getPackageManager().getPackageInfo(
-                            getCarContext().getPackageName(),
-                            PackageManager.GET_PERMISSIONS);
-            declaredPermissions = info.requestedPermissions;
-        } catch (PackageManager.NameNotFoundException e) {
-            return new MessageTemplate.Builder(
-                    getCarContext().getString(R.string.package_not_found_error_msg))
-                    .setHeaderAction(headerAction)
-                    .addAction(mRefreshAction)
-                    .build();
-        }
-
-        if (declaredPermissions != null) {
-            for (String declaredPermission : declaredPermissions) {
-                // Don't include permissions against the car app host as they are all normal but
-                // show up as ungranted by the system.
-                if (declaredPermission.startsWith("androidx.car.app")) {
-                    continue;
-                }
-                try {
-                    CarAppPermission.checkHasPermission(getCarContext(), declaredPermission);
-                } catch (SecurityException e) {
-                    permissions.add(declaredPermission);
-                }
-            }
-        }
-        if (permissions.isEmpty()) {
-            return new MessageTemplate.Builder(
-                    getCarContext().getString(R.string.permissions_granted_msg))
-                    .setHeaderAction(headerAction)
-                    .addAction(new Action.Builder()
-                            .setTitle(getCarContext().getString(R.string.close_action_title))
-                            .setOnClickListener(this::finish)
-                            .build())
-                    .build();
-        }
-
-        StringBuilder message = new StringBuilder()
-                .append(getCarContext().getString(R.string.needs_access_msg_prefix));
-        for (String permission : permissions) {
-            message.append(permission);
-            message.append("\n");
-        }
-
-        OnClickListener listener = ParkedOnlyOnClickListener.create(() -> {
-            getCarContext().requestPermissions(
-                    permissions,
-                    (approved, rejected) -> {
-                        CarToast.makeText(
-                                getCarContext(),
-                                String.format("Approved: %s Rejected: %s", approved, rejected),
-                                CarToast.LENGTH_LONG).show();
-                        invalidate();
-                    });
-            if (!getCarContext().getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE)) {
-                CarToast.makeText(getCarContext(),
-                        getCarContext().getString(R.string.phone_screen_permission_msg),
-                        CarToast.LENGTH_LONG).show();
-            }
-        });
-
-        Action action = new Action.Builder()
-                .setTitle(getCarContext().getString(R.string.grant_access_action_title))
-                .setBackgroundColor(CarColor.BLUE)
-                .setOnClickListener(listener)
-                .build();
-
-
-        Action action2 = null;
-        LocationManager locationManager =
-                (LocationManager) getCarContext().getSystemService(Context.LOCATION_SERVICE);
-        if (!LocationManagerCompat.isLocationEnabled(locationManager)) {
-            message.append(
-                    getCarContext().getString(R.string.enable_location_permission_on_device_msg));
-            message.append("\n");
-            action2 = new Action.Builder()
-                    .setTitle(getCarContext().getString(R.string.enable_location_action_title))
-                    .setBackgroundColor(CarColor.BLUE)
-                    .setOnClickListener(ParkedOnlyOnClickListener.create(() -> {
-                        getCarContext().startActivity(
-                                new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS).addFlags(
-                                        Intent.FLAG_ACTIVITY_NEW_TASK));
-                        invalidate();
-                        if (!getCarContext().getPackageManager().hasSystemFeature(
-                                FEATURE_AUTOMOTIVE)) {
-                            CarToast.makeText(getCarContext(),
-                                    getCarContext().getString(
-                                            R.string.enable_location_permission_on_phone_msg),
-                                    CarToast.LENGTH_LONG).show();
-                        }
-                    }))
-                    .build();
-        }
-
-
-        LongMessageTemplate.Builder builder = new LongMessageTemplate.Builder(message)
-                .setTitle(getCarContext().getString(R.string.required_permissions_title))
-                .addAction(action)
-                .setHeaderAction(headerAction);
-
-        if (action2 != null) {
-            builder.addAction(action2);
-        }
-
-        return builder.build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/ReservationCancelledScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/ReservationCancelledScreen.java
deleted file mode 100644
index fb8d0b9..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/ReservationCancelledScreen.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.misc;
-
-import static androidx.car.app.model.Action.BACK;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.Screen;
-import androidx.car.app.model.Pane;
-import androidx.car.app.model.PaneTemplate;
-import androidx.car.app.model.Row;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.R;
-
-/** A screen that displays text about canceling the reservation */
-public final class ReservationCancelledScreen extends Screen {
-
-    public ReservationCancelledScreen(@NonNull CarContext carContext) {
-        super(carContext);
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        Pane pane = new Pane.Builder()
-                .addRow(new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.reservation_cancelled_msg))
-                        .build())
-                .build();
-
-        return new PaneTemplate.Builder(pane)
-                .setTitle(getCarContext().getString(R.string.cancel_reservation_title))
-                .setHeaderAction(BACK)
-                .build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/MapTemplateWithListDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/MapTemplateWithListDemoScreen.java
deleted file mode 100644
index 581f775..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/MapTemplateWithListDemoScreen.java
+++ /dev/null
@@ -1,171 +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.car.app.sample.showcase.common.navigation;
-
-import static androidx.car.app.CarToast.LENGTH_LONG;
-import static androidx.car.app.CarToast.LENGTH_SHORT;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.OptIn;
-import androidx.car.app.CarContext;
-import androidx.car.app.CarToast;
-import androidx.car.app.Screen;
-import androidx.car.app.annotations.ExperimentalCarApi;
-import androidx.car.app.constraints.ConstraintManager;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.ActionStrip;
-import androidx.car.app.model.CarIcon;
-import androidx.car.app.model.CarText;
-import androidx.car.app.model.Header;
-import androidx.car.app.model.ItemList;
-import androidx.car.app.model.ParkedOnlyOnClickListener;
-import androidx.car.app.model.Row;
-import androidx.car.app.model.Template;
-import androidx.car.app.navigation.model.MapController;
-import androidx.car.app.navigation.model.MapTemplate;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.sample.showcase.common.navigation.routing.RoutingDemoModels;
-import androidx.car.app.versioning.CarAppApiLevels;
-import androidx.core.graphics.drawable.IconCompat;
-
-/** Simple demo of how to present a map template with a list. */
-public class MapTemplateWithListDemoScreen extends Screen {
-    private static final int MAX_LIST_ITEMS = 100;
-    private boolean mIsFavorite = false;
-
-    protected MapTemplateWithListDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-    }
-
-    @OptIn(markerClass = ExperimentalCarApi.class)
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        ItemList.Builder listBuilder = new ItemList.Builder();
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setOnClickListener(
-                                ParkedOnlyOnClickListener.create(() -> onClick(
-                                        getCarContext().getString(R.string.parked_toast_msg))))
-                        .setTitle(getCarContext().getString(R.string.parked_only_title))
-                        .addText(getCarContext().getString(R.string.parked_only_text))
-                        .build());
-        // Some hosts may allow more items in the list than others, so create more.
-        if (getCarContext().getCarAppApiLevel() > CarAppApiLevels.LEVEL_1) {
-            int listLimit =
-                    Math.min(MAX_LIST_ITEMS,
-                            getCarContext().getCarService(ConstraintManager.class).getContentLimit(
-                                    ConstraintManager.CONTENT_LIMIT_TYPE_LIST));
-
-            for (int i = 2; i <= listLimit; ++i) {
-                // For row text, set text variants that fit best in different screen sizes.
-                String secondTextStr = getCarContext().getString(R.string.second_line_text);
-                CarText secondText =
-                        new CarText.Builder(
-                                "================= " + secondTextStr + " ================")
-                                .addVariant("--------------------- " + secondTextStr
-                                        + " ----------------------")
-                                .addVariant(secondTextStr)
-                                .build();
-                final String onClickText = getCarContext().getString(R.string.clicked_row_prefix)
-                        + ": " + i;
-                listBuilder.addItem(
-                        new Row.Builder()
-                                .setOnClickListener(() -> onClick(onClickText))
-                                .setTitle(
-                                        getCarContext().getString(R.string.title_prefix) + " " + i)
-                                .addText(getCarContext().getString(R.string.first_line_text))
-                                .addText(secondText)
-                                .build());
-            }
-        }
-
-        Header header = new Header.Builder()
-                .setStartHeaderAction(Action.BACK)
-                .addEndHeaderAction(new Action.Builder()
-                        .setIcon(
-                                new CarIcon.Builder(
-                                        IconCompat.createWithResource(
-                                                getCarContext(),
-                                                mIsFavorite
-                                                        ? R.drawable.ic_favorite_filled_white_24dp
-                                                        : R.drawable.ic_favorite_white_24dp))
-                                        .build())
-                        .setOnClickListener(() -> {
-                            mIsFavorite = !mIsFavorite;
-                            CarToast.makeText(
-                                            getCarContext(),
-                                            mIsFavorite
-                                                    ? getCarContext().getString(
-                                                    R.string.favorite_toast_msg)
-                                                    : getCarContext().getString(
-                                                            R.string.not_favorite_toast_msg),
-                                            LENGTH_SHORT)
-                                    .show();
-                            invalidate();
-                        })
-                        .build())
-                .addEndHeaderAction(new Action.Builder()
-                        .setOnClickListener(() -> finish())
-                        .setIcon(
-                                new CarIcon.Builder(
-                                        IconCompat.createWithResource(
-                                                getCarContext(),
-                                                R.drawable.ic_close_white_24dp))
-                                        .build())
-                        .build())
-                .setTitle(getCarContext().getString(R.string.map_template_list_demo_title))
-                .build();
-
-
-        MapController mapController = new MapController.Builder()
-                .setMapActionStrip(RoutingDemoModels.getMapActionStrip(getCarContext()))
-                .build();
-
-        ActionStrip actionStrip = new ActionStrip.Builder()
-                .addAction(
-                        new Action.Builder()
-                                .setOnClickListener(
-                                        () -> CarToast.makeText(
-                                                        getCarContext(),
-                                                        getCarContext().getString(
-                                                                R.string.bug_reported_toast_msg),
-                                                        CarToast.LENGTH_SHORT)
-                                                .show())
-                                .setIcon(
-                                        new CarIcon.Builder(
-                                                IconCompat.createWithResource(
-                                                        getCarContext(),
-                                                        R.drawable.ic_bug_report_24px))
-                                                .build())
-                                .setFlags(Action.FLAG_IS_PERSISTENT)
-                                .build())
-                .build();
-
-        MapTemplate.Builder builder = new MapTemplate.Builder()
-                .setItemList(listBuilder.build())
-                .setActionStrip(actionStrip)
-                .setHeader(header)
-                .setMapController(mapController);
-
-        return builder.build();
-    }
-
-    private void onClick(String text) {
-        CarToast.makeText(getCarContext(), text, LENGTH_LONG).show();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/MapTemplateWithPaneDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/MapTemplateWithPaneDemoScreen.java
deleted file mode 100644
index 1baa8c1..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/MapTemplateWithPaneDemoScreen.java
+++ /dev/null
@@ -1,200 +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.car.app.sample.showcase.common.navigation;
-
-import static androidx.car.app.CarToast.LENGTH_SHORT;
-import static androidx.car.app.model.Action.FLAG_PRIMARY;
-
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.OptIn;
-import androidx.car.app.CarContext;
-import androidx.car.app.CarToast;
-import androidx.car.app.Screen;
-import androidx.car.app.annotations.ExperimentalCarApi;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.ActionStrip;
-import androidx.car.app.model.CarColor;
-import androidx.car.app.model.CarIcon;
-import androidx.car.app.model.Header;
-import androidx.car.app.model.Pane;
-import androidx.car.app.model.Row;
-import androidx.car.app.model.Template;
-import androidx.car.app.navigation.model.MapController;
-import androidx.car.app.navigation.model.MapTemplate;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.sample.showcase.common.navigation.routing.RoutingDemoModels;
-import androidx.car.app.versioning.CarAppApiLevels;
-import androidx.core.graphics.drawable.IconCompat;
-
-/** Simple demo of how to present a map template with a pane. */
-public class MapTemplateWithPaneDemoScreen extends Screen {
-    @Nullable
-    private final IconCompat mPaneImage;
-
-    @Nullable
-    private final IconCompat mRowLargeIcon;
-
-    private boolean mIsFavorite = false;
-
-    protected MapTemplateWithPaneDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-        Resources resources = getCarContext().getResources();
-        Bitmap bitmap = BitmapFactory.decodeResource(resources, R.drawable.patio);
-        mPaneImage = IconCompat.createWithBitmap(bitmap);
-        mRowLargeIcon = IconCompat.createWithResource(getCarContext(),
-                R.drawable.ic_fastfood_white_48dp);
-    }
-
-    @OptIn(markerClass = ExperimentalCarApi.class)
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        int listLimit = 4;
-
-        Pane.Builder paneBuilder = new Pane.Builder();
-        for (int i = 0; i < listLimit; i++) {
-            paneBuilder.addRow(createRow(i));
-        }
-
-        // Also set a large image outside of the rows.
-        paneBuilder.setImage(new CarIcon.Builder(mPaneImage).build());
-
-        Action.Builder primaryActionBuilder = new Action.Builder()
-                .setTitle(getCarContext().getString(R.string.primary_action_title))
-                .setBackgroundColor(CarColor.BLUE)
-                .setOnClickListener(
-                        () -> CarToast.makeText(
-                                        getCarContext(),
-                                        getCarContext().getString(R.string.primary_toast_msg),
-                                        LENGTH_SHORT)
-                                .show());
-        if (getCarContext().getCarAppApiLevel() >= CarAppApiLevels.LEVEL_4) {
-            primaryActionBuilder.setFlags(FLAG_PRIMARY);
-        }
-
-        paneBuilder
-                .addAction(primaryActionBuilder.build())
-                .addAction(
-                        new Action.Builder()
-                                .setTitle(getCarContext().getString(R.string.options_action_title))
-                                .setOnClickListener(
-                                        () -> CarToast.makeText(
-                                                        getCarContext(),
-                                                        getCarContext().getString(
-                                                                R.string.options_toast_msg),
-                                                        LENGTH_SHORT)
-                                                .show())
-                                .build());
-
-        Header header = new Header.Builder()
-                .setStartHeaderAction(Action.BACK)
-                .addEndHeaderAction(new Action.Builder()
-                        .setIcon(
-                                new CarIcon.Builder(
-                                        IconCompat.createWithResource(
-                                                getCarContext(),
-                                                mIsFavorite
-                                                        ? R.drawable.ic_favorite_filled_white_24dp
-                                                        : R.drawable.ic_favorite_white_24dp))
-                                        .build())
-                        .setOnClickListener(() -> {
-                            mIsFavorite = !mIsFavorite;
-                            CarToast.makeText(
-                                            getCarContext(),
-                                            mIsFavorite
-                                                    ? getCarContext()
-                                                            .getString(R.string.favorite_toast_msg)
-                                                    : getCarContext().getString(
-                                                            R.string.not_favorite_toast_msg),
-                                            LENGTH_SHORT)
-                                    .show();
-                            invalidate();
-                        })
-                        .build())
-                .addEndHeaderAction(new Action.Builder()
-                        .setOnClickListener(() -> finish())
-                        .setIcon(
-                                new CarIcon.Builder(
-                                        IconCompat.createWithResource(
-                                                getCarContext(),
-                                                R.drawable.ic_close_white_24dp))
-                                        .build())
-                        .build())
-                .setTitle(getCarContext().getString(R.string.map_template_pane_demo_title))
-                .build();
-
-
-        MapController mapController = new MapController.Builder()
-                .setMapActionStrip(RoutingDemoModels.getMapActionStrip(getCarContext()))
-                .build();
-
-        ActionStrip actionStrip = new ActionStrip.Builder()
-                .addAction(
-                        new Action.Builder()
-                                .setOnClickListener(
-                                        () -> CarToast.makeText(
-                                                        getCarContext(),
-                                                        getCarContext().getString(
-                                                                R.string.bug_reported_toast_msg),
-                                                        CarToast.LENGTH_SHORT)
-                                                .show())
-                                .setIcon(
-                                        new CarIcon.Builder(
-                                                IconCompat.createWithResource(
-                                                        getCarContext(),
-                                                        R.drawable.ic_bug_report_24px))
-                                                .build())
-                                .setFlags(Action.FLAG_IS_PERSISTENT)
-                                .build())
-                .build();
-
-        MapTemplate.Builder builder = new MapTemplate.Builder()
-                .setActionStrip(actionStrip)
-                .setPane(paneBuilder.build())
-                .setHeader(header)
-                .setMapController(mapController);
-
-        return builder.build();
-    }
-
-    private Row createRow(int index) {
-        switch (index) {
-            case 0:
-                // Row with a large image.
-                return new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.first_row_title))
-                        .addText(getCarContext().getString(R.string.first_row_text))
-                        .addText(getCarContext().getString(R.string.first_row_text))
-                        .setImage(new CarIcon.Builder(mRowLargeIcon).build())
-                        .build();
-            default:
-                return new Row.Builder()
-                        .setTitle(
-                                getCarContext().getString(R.string.other_row_title_prefix) + (index
-                                        + 1))
-                        .addText(getCarContext().getString(R.string.other_row_text))
-                        .addText(getCarContext().getString(R.string.other_row_text))
-                        .build();
-
-        }
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/NavigationDemosScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/NavigationDemosScreen.java
deleted file mode 100644
index 47ab299..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/NavigationDemosScreen.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.navigation;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.Screen;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.ActionStrip;
-import androidx.car.app.model.CarIcon;
-import androidx.car.app.model.ItemList;
-import androidx.car.app.model.ListTemplate;
-import androidx.car.app.model.Row;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.sample.showcase.common.navigation.routing.NavigationTemplateDemoScreen;
-import androidx.core.graphics.drawable.IconCompat;
-
-/** A screen showing a list of navigation demos */
-public final class NavigationDemosScreen extends Screen {
-    private static final int MAX_PAGES = 2;
-
-    private final int mPage;
-
-    public NavigationDemosScreen(@NonNull CarContext carContext) {
-        this(carContext, /* page= */ 0);
-    }
-
-    public NavigationDemosScreen(@NonNull CarContext carContext, int page) {
-        super(carContext);
-        mPage = page;
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        ItemList.Builder listBuilder = new ItemList.Builder();
-
-        switch (mPage) {
-            case 0:
-                listBuilder.addItem(
-                        new Row.Builder()
-                                .setImage(
-                                        new CarIcon.Builder(
-                                                IconCompat.createWithResource(
-                                                        getCarContext(),
-                                                        R.drawable.ic_explore_white_24dp))
-                                                .build(),
-                                        Row.IMAGE_TYPE_ICON)
-                                .setTitle(getCarContext().getString(
-                                        R.string.nav_template_demos_title))
-                                .setOnClickListener(
-                                        () ->
-                                                getScreenManager()
-                                                        .push(
-                                                                new NavigationTemplateDemoScreen(
-                                                                        getCarContext())))
-                                .setBrowsable(true)
-                                .build());
-                listBuilder.addItem(createRow(
-                        getCarContext().getString(R.string.place_list_nav_template_demo_title),
-                        new PlaceListNavigationTemplateDemoScreen(getCarContext())));
-                listBuilder.addItem(createRow(
-                        getCarContext().getString(R.string.route_preview_template_demo_title),
-                        new RoutePreviewDemoScreen(getCarContext())));
-                listBuilder.addItem(createRow(
-                        getCarContext().getString(R.string.notification_template_demo_title),
-                        new NavigationNotificationsDemoScreen(getCarContext())));
-                listBuilder.addItem(
-                        createRow(getCarContext().getString(R.string.nav_map_template_demo_title),
-                                new NavigationMapOnlyScreen(getCarContext())));
-                listBuilder.addItem(
-                        createRow(getCarContext().getString(R.string.map_template_pane_demo_title),
-                                new MapTemplateWithPaneDemoScreen(getCarContext())));
-                break;
-            case 1:
-                listBuilder.addItem(
-                        createRow(getCarContext().getString(R.string.map_template_list_demo_title),
-                                new MapTemplateWithListDemoScreen(getCarContext())));
-                break;
-        }
-
-        ListTemplate.Builder builder = new ListTemplate.Builder()
-                .setSingleList(listBuilder.build())
-                .setTitle(getCarContext().getString(R.string.nav_demos_title))
-                .setHeaderAction(Action.BACK);
-
-        if (mPage + 1 < MAX_PAGES) {
-            builder.setActionStrip(new ActionStrip.Builder()
-                    .addAction(new Action.Builder()
-                            .setTitle(getCarContext().getString(R.string.more_action_title))
-                            .setOnClickListener(() -> {
-                                getScreenManager().push(
-                                        new NavigationDemosScreen(getCarContext(), mPage + 1));
-                            })
-                            .build())
-                    .build());
-        }
-
-        return builder.build();
-    }
-
-    private Row createRow(String title, Screen screen) {
-        return new Row.Builder()
-                .setTitle(title)
-                .setOnClickListener(() -> getScreenManager().push(screen))
-                .build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/NavigationMapOnlyScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/NavigationMapOnlyScreen.java
deleted file mode 100644
index b328da4..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/NavigationMapOnlyScreen.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.navigation;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.Screen;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.ActionStrip;
-import androidx.car.app.model.Template;
-import androidx.car.app.navigation.model.NavigationTemplate;
-import androidx.car.app.sample.showcase.common.R;
-
-/** Simple demo of how to present a navigation screen with only a map. */
-public final class NavigationMapOnlyScreen extends Screen {
-
-    public NavigationMapOnlyScreen(@NonNull CarContext carContext) {
-        super(carContext);
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        ActionStrip actionStrip =
-                new ActionStrip.Builder()
-                        .addAction(
-                                new Action.Builder()
-                                        .setTitle(getCarContext().getString(
-                                                R.string.back_caps_action_title))
-                                        .setOnClickListener(this::finish)
-                                        .build())
-                        .build();
-
-        return new NavigationTemplate.Builder().setActionStrip(actionStrip).build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/NavigationNotificationService.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/NavigationNotificationService.java
deleted file mode 100644
index d79a8e5..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/NavigationNotificationService.java
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.navigation;
-
-import static androidx.car.app.sample.showcase.common.ShowcaseService.INTENT_ACTION_NAV_NOTIFICATION_OPEN_APP;
-
-import static java.util.concurrent.TimeUnit.SECONDS;
-
-import android.annotation.SuppressLint;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.car.app.notification.CarAppExtender;
-import androidx.car.app.notification.CarNotificationManager;
-import androidx.car.app.notification.CarPendingIntent;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.sample.showcase.common.ShowcaseService;
-import androidx.core.app.NotificationChannelCompat;
-import androidx.core.app.NotificationCompat;
-import androidx.core.app.NotificationManagerCompat;
-
-import java.math.RoundingMode;
-import java.text.DecimalFormat;
-
-/**
- * A simple foreground service that imitates a client routing service posting navigation
- * notifications.
- */
-public final class NavigationNotificationService extends Service {
-    private static final int MSG_SEND_NOTIFICATION = 1;
-    private static final String NAV_NOTIFICATION_CHANNEL_ID = "nav_channel_00";
-    private static final CharSequence NAV_NOTIFICATION_CHANNEL_NAME = "Navigation Channel";
-    private static final int NAV_NOTIFICATION_ID = 10101;
-    static final long NAV_NOTIFICATION_DELAY_IN_MILLIS = SECONDS.toMillis(1);
-
-    /**
-     * The number of notifications fired so far.
-     *
-     * <p>We use this number to post notifications with a repeating list of directions. See {@link
-     * #getDirectionInfo(Context, int)} for details.
-     */
-    int mNotificationCount = 0;
-
-    /**
-     * A handler that posts notifications when given the message request. See {@link
-     * HandlerCallback} for details.
-     */
-    final Handler mHandler = new Handler(Looper.getMainLooper(), new HandlerCallback());
-
-    @Override
-    public int onStartCommand(@NonNull Intent intent, int flags, int startId) {
-        initNotifications(this);
-        startForeground(NAV_NOTIFICATION_ID,
-                getNavigationNotification(this, mNotificationCount).build());
-
-        // Start updating the notification continuously.
-        mHandler.sendMessageDelayed(
-                mHandler.obtainMessage(MSG_SEND_NOTIFICATION), NAV_NOTIFICATION_DELAY_IN_MILLIS);
-
-        return START_NOT_STICKY;
-    }
-
-    @Override
-    public void onDestroy() {
-        mHandler.removeMessages(MSG_SEND_NOTIFICATION);
-    }
-
-    @Nullable
-    @Override
-    public IBinder onBind(@NonNull Intent intent) {
-        return null;
-    }
-
-    /**
-     * Initializes the notifications, if needed.
-     *
-     * <p>{@link NotificationManager#IMPORTANCE_HIGH} is needed to show the alerts on top of the car
-     * screen. However, the rail widget at the bottom of the screen will show regardless of the
-     * importance setting.
-     */
-    // Suppressing 'ObsoleteSdkInt' as this code is shared between APKs with different min SDK
-    // levels
-    @SuppressLint({"ObsoleteSdkInt"})
-    private static void initNotifications(Context context) {
-        NotificationChannelCompat navChannel =
-                new NotificationChannelCompat.Builder(
-                        NAV_NOTIFICATION_CHANNEL_ID,
-                        NotificationManagerCompat.IMPORTANCE_HIGH)
-                        .setName(NAV_NOTIFICATION_CHANNEL_NAME).build();
-        CarNotificationManager.from(context).createNotificationChannel(navChannel);
-    }
-
-    /** Returns the navigation notification that corresponds to the given notification count. */
-    static NotificationCompat.Builder getNavigationNotification(
-            Context context, int notificationCount) {
-        NotificationCompat.Builder builder =
-                new NotificationCompat.Builder(context, NAV_NOTIFICATION_CHANNEL_ID);
-        DirectionInfo directionInfo = getDirectionInfo(context, notificationCount);
-
-        // Set an intent to open the car app. The app receives this intent when the user taps the
-        // heads-up notification or the rail widget.
-        PendingIntent pendingIntent = CarPendingIntent.getCarApp(
-                context,
-                INTENT_ACTION_NAV_NOTIFICATION_OPEN_APP.hashCode(),
-                new Intent(
-                        INTENT_ACTION_NAV_NOTIFICATION_OPEN_APP).setComponent(
-                        new ComponentName(context,
-                                ShowcaseService.class)).setData(
-                        ShowcaseService.createDeepLinkUri(
-                                INTENT_ACTION_NAV_NOTIFICATION_OPEN_APP)),
-                0);
-
-        return builder
-                // This title, text, and icon will be shown in both phone and car screen. These
-                // values can
-                // be overridden in the extender below, to customize notifications in the car
-                // screen.
-                .setContentTitle(directionInfo.mTitle)
-                .setContentText(directionInfo.mDistance)
-                .setSmallIcon(directionInfo.mIcon)
-
-                // The notification must be set to 'ongoing' and its category must be set to
-                // CATEGORY_NAVIGATION in order to show it in the rail widget when the app is
-                // navigating on
-                // the background.
-                // These values cannot be overridden in the extender.
-                .setOngoing(true)
-                .setCategory(NotificationCompat.CATEGORY_NAVIGATION)
-
-                // If set to true, the notification will only show the alert once in both phone and
-                // car screen. This value cannot be overridden in the extender.
-                .setOnlyAlertOnce(directionInfo.mOnlyAlertOnce)
-
-                // This extender must be set in order to display the notification in the car screen.
-                // The extender also allows various customizations, such as showing different title
-                // or icon on the car screen.
-                .extend(new CarAppExtender.Builder()
-                        .setContentIntent(pendingIntent)
-                        .build());
-    }
-
-    /**
-     * A {@link Handler.Callback} used to process the message queue for the notification service.
-     */
-    final class HandlerCallback implements Handler.Callback {
-        @Override
-        public boolean handleMessage(Message msg) {
-            if (msg.what == MSG_SEND_NOTIFICATION) {
-                Context context = NavigationNotificationService.this;
-                CarNotificationManager.from(context).notify(NAV_NOTIFICATION_ID,
-                        getNavigationNotification(context, mNotificationCount));
-                mNotificationCount++;
-                mHandler.sendMessageDelayed(
-                        mHandler.obtainMessage(MSG_SEND_NOTIFICATION),
-                        NAV_NOTIFICATION_DELAY_IN_MILLIS);
-                return true;
-            }
-            return false;
-        }
-    }
-
-    /**
-     * Returns a {@link DirectionInfo} that corresponds to the given notification count.
-     *
-     * <p>There are 5 directions, repeating in order. For each direction, the alert will only show
-     * once, but the distance will update on every count on the rail widget.
-     */
-    private static DirectionInfo getDirectionInfo(Context context, int notificationCount) {
-        DecimalFormat formatter = new DecimalFormat("#.##");
-        formatter.setRoundingMode(RoundingMode.DOWN);
-        int repeatingCount = notificationCount % 35;
-        if (0 <= repeatingCount && repeatingCount < 10) {
-            // Distance decreases from 1km to 0.1km
-            String distance = formatter.format((10 - repeatingCount) * 0.1) + "km";
-            return new DirectionInfo(
-                    context.getString(R.string.go_straight),
-                    distance,
-                    R.drawable.arrow_straight,
-                    /* onlyAlertOnce= */ repeatingCount > 0);
-        } else if (10 <= repeatingCount && repeatingCount < 20) {
-            // Distance decreases from 5km to 0.5km
-            String distance = formatter.format((20 - repeatingCount) * 0.5) + "km";
-            return new DirectionInfo(
-                    context.getString(R.string.turn_right),
-                    distance,
-                    R.drawable.arrow_right_turn,
-                    /* onlyAlertOnce= */ repeatingCount > 10);
-        } else if (20 <= repeatingCount && repeatingCount < 25) {
-            // Distance decreases from 200m to 40m
-            String distance = formatter.format((25 - repeatingCount) * 40) + "m";
-            return new DirectionInfo(
-                    context.getString(R.string.take_520),
-                    distance,
-                    R.drawable.ic_520,
-                    /* onlyAlertOnce= */ repeatingCount > 20);
-        } else {
-            // Distance decreases from 1km to 0.1km
-            String distance = formatter.format((35 - repeatingCount) * 0.1) + "km";
-            return new DirectionInfo(
-                    context.getString(R.string.gas_station),
-                    distance,
-                    R.drawable.ic_local_gas_station_white_48dp,
-                    repeatingCount > 25);
-        }
-    }
-
-    /**
-     * A container class that encapsulates the direction information to use in the notifications.
-     */
-    static final class DirectionInfo {
-        @NonNull
-        final String mTitle;
-        @NonNull
-        final String mDistance;
-        final int mIcon;
-        final boolean mOnlyAlertOnce;
-
-        DirectionInfo(@NonNull String title, @NonNull String distance, int icon,
-                boolean onlyAlertOnce) {
-            this.mTitle = title;
-            this.mDistance = distance;
-            this.mIcon = icon;
-            this.mOnlyAlertOnce = onlyAlertOnce;
-        }
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/NavigationNotificationsDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/NavigationNotificationsDemoScreen.java
deleted file mode 100644
index e183ab3..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/NavigationNotificationsDemoScreen.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.navigation;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Build.VERSION;
-import android.os.Build.VERSION_CODES;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.Screen;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.ItemList;
-import androidx.car.app.model.ListTemplate;
-import androidx.car.app.model.Row;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.R;
-
-/** A simple screen that demonstrates how to use navigation notifications in a car app. */
-public final class NavigationNotificationsDemoScreen extends Screen {
-
-    public NavigationNotificationsDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-    }
-
-    // Suppressing 'ObsoleteSdkInt' as this code is shared between APKs with different min SDK
-    // levels
-    @SuppressLint({"ObsoleteSdkInt"})
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        ItemList.Builder listBuilder = new ItemList.Builder();
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.start_notification_title))
-                        .setOnClickListener(
-                                () -> {
-                                    Context context = getCarContext();
-                                    Intent intent =
-                                            new Intent(
-                                                    context, NavigationNotificationService.class);
-                                    if (VERSION.SDK_INT >= VERSION_CODES.O) {
-                                        context.startForegroundService(intent);
-                                    } else {
-                                        context.startService(intent);
-                                    }
-                                })
-                        .build());
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.stop_notification_title))
-                        .setOnClickListener(
-                                () ->
-                                        getCarContext()
-                                                .stopService(
-                                                        new Intent(
-                                                                getCarContext(),
-                                                                NavigationNotificationService
-                                                                        .class)))
-                        .build());
-
-        return new ListTemplate.Builder()
-                .setSingleList(listBuilder.build())
-                .setTitle(getCarContext().getString(R.string.nav_notification_demo_title))
-                .setHeaderAction(Action.BACK)
-                .build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/PlaceListNavigationTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/PlaceListNavigationTemplateDemoScreen.java
deleted file mode 100644
index 80d0df2..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/PlaceListNavigationTemplateDemoScreen.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.navigation;
-
-import static androidx.car.app.CarToast.LENGTH_SHORT;
-
-import android.os.Handler;
-import android.os.Looper;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.CarToast;
-import androidx.car.app.Screen;
-import androidx.car.app.constraints.ConstraintManager;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.ActionStrip;
-import androidx.car.app.model.CarIcon;
-import androidx.car.app.model.Header;
-import androidx.car.app.model.Template;
-import androidx.car.app.navigation.model.PlaceListNavigationTemplate;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.sample.showcase.common.common.SamplePlaces;
-import androidx.car.app.sample.showcase.common.navigation.routing.RoutingDemoModels;
-import androidx.core.graphics.drawable.IconCompat;
-
-/** Creates a screen using the {@link PlaceListNavigationTemplate} */
-public final class PlaceListNavigationTemplateDemoScreen extends Screen {
-    private static final int NUMBER_OF_REFRESHES = 10;
-    private static final long SECOND_DELAY = 1000L;
-    private final SamplePlaces mPlaces;
-
-    private final Handler mHandler = new Handler(Looper.getMainLooper());
-
-    private boolean mIsAppRefresh = false;
-
-    private boolean mIsFavorite = false;
-
-    public PlaceListNavigationTemplateDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-        mPlaces = SamplePlaces.create(this);
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        boolean isAppDrivenRefreshEnabled = this.getCarContext().getCarService(
-                ConstraintManager.class).isAppDrivenRefreshEnabled();
-
-        if (isAppDrivenRefreshEnabled && !mIsAppRefresh) {
-            mIsAppRefresh = true;
-            for (int i = 1; i <= NUMBER_OF_REFRESHES; i++) {
-                mHandler.postDelayed(this::invalidate, i * SECOND_DELAY);
-            }
-        }
-
-        Header header = new Header.Builder()
-                .setStartHeaderAction(Action.BACK)
-                .addEndHeaderAction(new Action.Builder()
-                        .setIcon(
-                                new CarIcon.Builder(
-                                        IconCompat.createWithResource(
-                                                getCarContext(),
-                                                mIsFavorite
-                                                        ? R.drawable.ic_favorite_filled_white_24dp
-                                                        : R.drawable.ic_favorite_white_24dp))
-                                        .build())
-                        .setOnClickListener(() -> {
-                            mIsFavorite = !mIsFavorite;
-                            CarToast.makeText(
-                                            getCarContext(),
-                                            mIsFavorite
-                                                    ? getCarContext()
-                                                    .getString(R.string.favorite_toast_msg)
-                                                    : getCarContext().getString(
-                                                            R.string.not_favorite_toast_msg),
-                                            LENGTH_SHORT)
-                                    .show();
-                            invalidate();
-                        })
-                        .build())
-                .addEndHeaderAction(new Action.Builder()
-                        .setOnClickListener(this::finish)
-                        .setIcon(
-                                new CarIcon.Builder(
-                                        IconCompat.createWithResource(
-                                                getCarContext(),
-                                                R.drawable.ic_close_white_24dp))
-                                        .build())
-                        .build())
-                .setTitle(getCarContext().getString(R.string.place_list_nav_template_demo_title))
-                .build();
-
-        return new PlaceListNavigationTemplate.Builder()
-                .setItemList(mPlaces.getPlaceList(/* randomOrder =*/isAppDrivenRefreshEnabled))
-                .setHeader(header)
-                .setMapActionStrip(RoutingDemoModels.getMapActionStrip(getCarContext()))
-                .setActionStrip(
-                        new ActionStrip.Builder()
-                                .addAction(
-                                        new Action.Builder()
-                                                .setTitle(getCarContext().getString(
-                                                        R.string.search_action_title))
-                                                .setOnClickListener(() -> {
-                                                })
-                                                .build())
-                                .build())
-                .build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/RoutePreviewDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/RoutePreviewDemoScreen.java
deleted file mode 100644
index 1cd0ee0..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/RoutePreviewDemoScreen.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.navigation;
-
-import static androidx.car.app.CarToast.LENGTH_LONG;
-import static androidx.car.app.CarToast.LENGTH_SHORT;
-
-import android.text.SpannableString;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.CarToast;
-import androidx.car.app.Screen;
-import androidx.car.app.constraints.ConstraintManager;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.CarIcon;
-import androidx.car.app.model.CarText;
-import androidx.car.app.model.DurationSpan;
-import androidx.car.app.model.Header;
-import androidx.car.app.model.ItemList;
-import androidx.car.app.model.Row;
-import androidx.car.app.model.Template;
-import androidx.car.app.navigation.model.RoutePreviewNavigationTemplate;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.sample.showcase.common.navigation.routing.RoutingDemoModels;
-import androidx.car.app.versioning.CarAppApiLevels;
-import androidx.core.graphics.drawable.IconCompat;
-
-import java.util.concurrent.TimeUnit;
-
-/** Creates a screen using the {@link RoutePreviewNavigationTemplate} */
-public final class RoutePreviewDemoScreen extends Screen {
-    public RoutePreviewDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-    }
-
-    private boolean mIsFavorite = false;
-
-    private CarText createRouteText(int index) {
-        switch (index) {
-            case 0:
-                // Set text variants for the first route.
-                SpannableString shortRouteLongText = new SpannableString(
-                        "   \u00b7 ---------------- " + getCarContext().getString(
-                                R.string.short_route)
-                                + " -------------------");
-                shortRouteLongText.setSpan(DurationSpan.create(TimeUnit.HOURS.toSeconds(26)), 0, 1,
-                        0);
-                SpannableString firstRouteShortText = new SpannableString(
-                        "   \u00b7 " + getCarContext().getString(R.string.short_route));
-                firstRouteShortText.setSpan(DurationSpan.create(TimeUnit.HOURS.toSeconds(26)), 0, 1,
-                        0);
-                return new CarText.Builder(shortRouteLongText)
-                        .addVariant(firstRouteShortText)
-                        .build();
-            case 1:
-                SpannableString lessBusyRouteText =
-                        new SpannableString(
-                                "   \u00b7 " + getCarContext().getString(R.string.less_busy));
-                lessBusyRouteText.setSpan(DurationSpan.create(TimeUnit.HOURS.toSeconds(24)), 0, 1,
-                        0);
-                return new CarText.Builder(lessBusyRouteText).build();
-            case 2:
-                SpannableString hovRouteText =
-                        new SpannableString(
-                                "   \u00b7 " + getCarContext().getString(R.string.hov_friendly));
-                hovRouteText.setSpan(DurationSpan.create(TimeUnit.MINUTES.toSeconds(867)), 0, 1, 0);
-                return new CarText.Builder(hovRouteText).build();
-            default:
-                SpannableString routeText =
-                        new SpannableString(
-                                "   \u00b7 " + getCarContext().getString(R.string.long_route));
-                routeText.setSpan(DurationSpan.create(TimeUnit.MINUTES.toSeconds(867L + index)),
-                        0, 1, 0);
-                return new CarText.Builder(routeText).build();
-        }
-    }
-
-    private Row createRow(int index) {
-        CarText route = createRouteText(index);
-        String titleText = "Via NE " + (index + 4) + "th Street";
-
-        return new Row.Builder()
-                .setTitle(route)
-                .addText(titleText)
-                .build();
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        int itemLimit = 3;
-        // Adjust the item limit according to the car constrains.
-        if (getCarContext().getCarAppApiLevel() > CarAppApiLevels.LEVEL_1) {
-            itemLimit = getCarContext().getCarService(ConstraintManager.class).getContentLimit(
-                    ConstraintManager.CONTENT_LIMIT_TYPE_ROUTE_LIST);
-        }
-
-        ItemList.Builder itemListBuilder = new ItemList.Builder()
-                .setOnSelectedListener(this::onRouteSelected)
-                .setOnItemsVisibilityChangedListener(this::onRoutesVisible);
-
-        for (int i = 0; i < itemLimit; i++) {
-            itemListBuilder.addItem(createRow(i));
-        }
-
-        // Set text variants for the navigate action text.
-        CarText navigateActionText =
-                new CarText.Builder(getCarContext().getString(R.string.continue_start_nav))
-                        .addVariant(getCarContext().getString(R.string.continue_route))
-                        .build();
-
-        Header header = new Header.Builder()
-                .setStartHeaderAction(Action.BACK)
-                .addEndHeaderAction(new Action.Builder()
-                        .setIcon(
-                                new CarIcon.Builder(
-                                        IconCompat.createWithResource(
-                                                getCarContext(),
-                                                mIsFavorite
-                                                        ? R.drawable.ic_favorite_filled_white_24dp
-                                                        : R.drawable.ic_favorite_white_24dp))
-                                        .build())
-                        .setOnClickListener(() -> {
-                            mIsFavorite = !mIsFavorite;
-                            CarToast.makeText(
-                                            getCarContext(),
-                                            mIsFavorite
-                                                    ? getCarContext()
-                                                    .getString(R.string.favorite_toast_msg)
-                                                    : getCarContext().getString(
-                                                            R.string.not_favorite_toast_msg),
-                                            LENGTH_SHORT)
-                                    .show();
-                            invalidate();
-                        })
-                        .build())
-                .addEndHeaderAction(new Action.Builder()
-                        .setOnClickListener(() -> finish())
-                        .setIcon(
-                                new CarIcon.Builder(
-                                        IconCompat.createWithResource(
-                                                getCarContext(),
-                                                R.drawable.ic_close_white_24dp))
-                                        .build())
-                        .build())
-                .setTitle(getCarContext().getString(R.string.route_preview_template_demo_title))
-                .build();
-
-        return new RoutePreviewNavigationTemplate.Builder()
-                .setItemList(itemListBuilder.build())
-                .setNavigateAction(
-                        new Action.Builder()
-                                .setTitle(navigateActionText)
-                                .setOnClickListener(this::onNavigate)
-                                .build())
-                .setMapActionStrip(RoutingDemoModels.getMapActionStrip(getCarContext()))
-                .setHeader(header)
-                .build();
-    }
-
-    private void onNavigate() {
-        CarToast.makeText(getCarContext(),
-                getCarContext().getString(R.string.nav_requested_toast_msg),
-                LENGTH_LONG * 2).show();
-    }
-
-    private void onRouteSelected(int index) {
-        CarToast.makeText(getCarContext(),
-                getCarContext().getString(R.string.selected_route_toast_msg) + ": " + index,
-                LENGTH_LONG).show();
-    }
-
-    private void onRoutesVisible(int startIndex, int endIndex) {
-        CarToast.makeText(
-                        getCarContext(),
-                        getCarContext().getString(R.string.visible_routes_toast_msg)
-                                + ": [" + startIndex + "," + endIndex + "]",
-                        LENGTH_LONG)
-                .show();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/routing/ArrivedDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/routing/ArrivedDemoScreen.java
deleted file mode 100644
index 0368d0e..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/routing/ArrivedDemoScreen.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.navigation.routing;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.Screen;
-import androidx.car.app.model.CarColor;
-import androidx.car.app.model.CarIcon;
-import androidx.car.app.model.Template;
-import androidx.car.app.navigation.model.MessageInfo;
-import androidx.car.app.navigation.model.NavigationTemplate;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.core.graphics.drawable.IconCompat;
-import androidx.lifecycle.DefaultLifecycleObserver;
-
-/** A screen that shows the navigation template in arrived state. */
-public final class ArrivedDemoScreen extends Screen implements DefaultLifecycleObserver {
-    public ArrivedDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        return new NavigationTemplate.Builder()
-                .setNavigationInfo(
-                        new MessageInfo.Builder(
-                                getCarContext().getString(R.string.arrived_exclamation_msg))
-                                .setText(getCarContext().getString(R.string.arrived_address_msg))
-                                .setImage(
-                                        new CarIcon.Builder(
-                                                IconCompat.createWithResource(
-                                                        getCarContext(),
-                                                        R.drawable.ic_place_white_24dp))
-                                                .build())
-                                .build())
-                .setActionStrip(RoutingDemoModels.getActionStrip(getCarContext(), this::finish))
-                .setBackgroundColor(CarColor.SECONDARY)
-                .build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/routing/JunctionImageDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/routing/JunctionImageDemoScreen.java
deleted file mode 100644
index ac97861..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/routing/JunctionImageDemoScreen.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.navigation.routing;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.Screen;
-import androidx.car.app.model.CarIcon;
-import androidx.car.app.model.Distance;
-import androidx.car.app.model.Template;
-import androidx.car.app.navigation.model.NavigationTemplate;
-import androidx.car.app.navigation.model.RoutingInfo;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.core.graphics.drawable.IconCompat;
-import androidx.lifecycle.DefaultLifecycleObserver;
-
-/** A screen that shows the navigation template in routing state showing a junction image. */
-public final class JunctionImageDemoScreen extends Screen implements DefaultLifecycleObserver {
-    public JunctionImageDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        CarContext carContext = getCarContext();
-        return new NavigationTemplate.Builder()
-                .setNavigationInfo(
-                        new RoutingInfo.Builder()
-                                .setCurrentStep(
-                                        RoutingDemoModels.getCurrentStep(carContext),
-                                        Distance.create(200, Distance.UNIT_METERS))
-                                .setJunctionImage(
-                                        new CarIcon.Builder(
-                                                IconCompat.createWithResource(
-                                                        carContext,
-                                                        R.drawable.junction_image))
-                                                .build())
-                                .build())
-                .setDestinationTravelEstimate(RoutingDemoModels.getTravelEstimate(carContext))
-                .setActionStrip(RoutingDemoModels.getActionStrip(getCarContext(), this::finish))
-                .build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/routing/LoadingDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/routing/LoadingDemoScreen.java
deleted file mode 100644
index 2eb39a0..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/routing/LoadingDemoScreen.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.navigation.routing;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.Screen;
-import androidx.car.app.model.CarColor;
-import androidx.car.app.model.Template;
-import androidx.car.app.navigation.model.NavigationTemplate;
-import androidx.car.app.navigation.model.RoutingInfo;
-import androidx.lifecycle.DefaultLifecycleObserver;
-
-/** A screen that shows the navigation template in loading state. */
-public final class LoadingDemoScreen extends Screen implements DefaultLifecycleObserver {
-    public LoadingDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        return new NavigationTemplate.Builder()
-                .setNavigationInfo(new RoutingInfo.Builder().setLoading(true).build())
-                .setActionStrip(RoutingDemoModels.getActionStrip(getCarContext(), this::finish))
-                .setBackgroundColor(CarColor.SECONDARY)
-                .build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/routing/NavigatingDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/routing/NavigatingDemoScreen.java
deleted file mode 100644
index 533663c..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/routing/NavigatingDemoScreen.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.navigation.routing;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.Screen;
-import androidx.car.app.model.CarColor;
-import androidx.car.app.model.Distance;
-import androidx.car.app.model.Template;
-import androidx.car.app.navigation.model.NavigationTemplate;
-import androidx.car.app.navigation.model.RoutingInfo;
-import androidx.lifecycle.DefaultLifecycleObserver;
-
-/** A screen that shows the navigation template in routing state. */
-public final class NavigatingDemoScreen extends Screen implements DefaultLifecycleObserver {
-    public NavigatingDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        CarContext carContext = getCarContext();
-        return new NavigationTemplate.Builder()
-                .setNavigationInfo(
-                        new RoutingInfo.Builder()
-                                .setCurrentStep(
-                                        RoutingDemoModels.getCurrentStep(carContext),
-                                        Distance.create(200, Distance.UNIT_METERS))
-                                .setNextStep(RoutingDemoModels.getNextStep(carContext))
-                                .build())
-                .setDestinationTravelEstimate(RoutingDemoModels.getTravelEstimate(carContext))
-                .setActionStrip(RoutingDemoModels.getActionStrip(getCarContext(), this::finish))
-                .setMapActionStrip(RoutingDemoModels.getMapActionStrip(getCarContext()))
-                .setBackgroundColor(CarColor.SECONDARY)
-                .build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/routing/NavigationTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/routing/NavigationTemplateDemoScreen.java
deleted file mode 100644
index 7ed0a75..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/routing/NavigationTemplateDemoScreen.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.navigation.routing;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.Screen;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.ItemList;
-import androidx.car.app.model.ListTemplate;
-import androidx.car.app.model.Row;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.R;
-
-/** A screen showing a demos for the navigation template in different states. */
-public final class NavigationTemplateDemoScreen extends Screen {
-    public NavigationTemplateDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        ItemList.Builder listBuilder = new ItemList.Builder();
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.loading_demo_title))
-                        .setOnClickListener(
-                                () ->
-                                        getScreenManager()
-                                                .push(new LoadingDemoScreen(getCarContext())))
-                        .build());
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.navigating_demo_title))
-                        .setOnClickListener(
-                                () ->
-                                        getScreenManager()
-                                                .push(new NavigatingDemoScreen(getCarContext())))
-                        .build());
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.arrived_demo_title))
-                        .setOnClickListener(
-                                () ->
-                                        getScreenManager()
-                                                .push(new ArrivedDemoScreen(getCarContext())))
-                        .build());
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.junction_image_demo_title))
-                        .setOnClickListener(
-                                () ->
-                                        getScreenManager()
-                                                .push(new JunctionImageDemoScreen(getCarContext())))
-                        .build());
-
-        return new ListTemplate.Builder()
-                .setSingleList(listBuilder.build())
-                .setTitle(getCarContext().getString(R.string.nav_template_demos_title))
-                .setHeaderAction(Action.BACK)
-                .build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/routing/RoutingDemoModels.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/routing/RoutingDemoModels.java
deleted file mode 100644
index 6663f19..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/navigation/routing/RoutingDemoModels.java
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.navigation.routing;
-
-import static androidx.car.app.model.Action.FLAG_DEFAULT;
-import static androidx.car.app.model.Action.FLAG_PRIMARY;
-import static androidx.car.app.navigation.model.LaneDirection.SHAPE_NORMAL_RIGHT;
-import static androidx.car.app.navigation.model.LaneDirection.SHAPE_STRAIGHT;
-
-import android.text.SpannableString;
-import android.text.Spanned;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.AppManager;
-import androidx.car.app.CarContext;
-import androidx.car.app.CarToast;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.ActionStrip;
-import androidx.car.app.model.Alert;
-import androidx.car.app.model.AlertCallback;
-import androidx.car.app.model.CarColor;
-import androidx.car.app.model.CarIcon;
-import androidx.car.app.model.CarIconSpan;
-import androidx.car.app.model.CarText;
-import androidx.car.app.model.DateTimeWithZone;
-import androidx.car.app.model.Distance;
-import androidx.car.app.model.OnClickListener;
-import androidx.car.app.navigation.model.Lane;
-import androidx.car.app.navigation.model.LaneDirection;
-import androidx.car.app.navigation.model.Maneuver;
-import androidx.car.app.navigation.model.Step;
-import androidx.car.app.navigation.model.TravelEstimate;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.versioning.CarAppApiLevels;
-import androidx.core.graphics.drawable.IconCompat;
-
-import java.util.TimeZone;
-import java.util.concurrent.TimeUnit;
-
-/** A class that provides models for the routing demos. */
-public abstract class RoutingDemoModels {
-
-    private RoutingDemoModels() {
-    }
-
-    /** Returns a sample {@link Alert}. */
-    @NonNull
-    private static Alert createAlert(@NonNull CarContext carContext) {
-        CarText title =
-                CarText.create(carContext.getString(R.string.navigation_alert_title));
-        CarText subtitle =
-                CarText.create(carContext.getString(R.string.navigation_alert_subtitle));
-        CarIcon icon = CarIcon.ALERT;
-
-        CarText yesTitle = CarText.create(carContext.getString(R.string.yes_action_title));
-        Action yesAction = new Action.Builder().setTitle(yesTitle).setOnClickListener(
-                () -> CarToast.makeText(
-                                carContext,
-                                carContext.getString(
-                                        R.string.yes_action_toast_msg),
-                                CarToast.LENGTH_SHORT)
-                        .show()).setFlags(FLAG_PRIMARY).build();
-
-        CarText noTitle = CarText.create(carContext.getString(R.string.no_action_title));
-        Action noAction = new Action.Builder().setTitle(noTitle).setOnClickListener(
-                () -> CarToast.makeText(
-                                carContext,
-                                carContext.getString(
-                                        R.string.no_action_toast_msg),
-                                CarToast.LENGTH_SHORT)
-                        .show()).setFlags(FLAG_DEFAULT).build();
-
-        return new Alert.Builder(/* alertId: */ 0, title, /* durationMillis: */ 10000)
-                .setSubtitle(subtitle)
-                .setIcon(icon)
-                .addAction(yesAction)
-                .addAction(noAction).setCallback(new AlertCallback() {
-                    @Override
-                    public void onCancel(int reason) {
-                        if (reason == AlertCallback.REASON_TIMEOUT) {
-                            CarToast.makeText(
-                                            carContext,
-                                            carContext.getString(
-                                                    R.string.alert_timeout_toast_msg),
-                                            CarToast.LENGTH_SHORT)
-                                    .show();
-                        }
-                    }
-
-                    @Override
-                    public void onDismiss() {
-                    }
-                }).build();
-    }
-
-    /** Returns the current {@link Step} with information such as the cue text and images. */
-    @NonNull
-    public static Step getCurrentStep(@NonNull CarContext carContext) {
-        // Create the cue text, and span the "520" text with a highway sign image.
-        String currentStepCue = carContext.getString(R.string.current_step_cue);
-        SpannableString currentStepCueWithImage = new SpannableString(currentStepCue);
-        CarIconSpan highwaySign =
-                CarIconSpan.create(
-                        new CarIcon.Builder(
-                                IconCompat.createWithResource(
-                                        carContext, R.drawable.ic_520))
-                                .build(),
-                        CarIconSpan.ALIGN_CENTER);
-        currentStepCueWithImage.setSpan(highwaySign, 7, 10, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
-
-        CarIcon currentTurnIcon =
-                new CarIcon.Builder(
-                        IconCompat.createWithResource(
-                                carContext, R.drawable.arrow_right_turn))
-                        .build();
-        Maneuver currentManeuver =
-                new Maneuver.Builder(Maneuver.TYPE_TURN_NORMAL_RIGHT)
-                        .setIcon(currentTurnIcon)
-                        .build();
-
-        CarIcon lanesImage =
-                new CarIcon.Builder(IconCompat.createWithResource(carContext, R.drawable.lanes))
-                        .build();
-
-        Lane straightNormal =
-                new Lane.Builder()
-                        .addDirection(LaneDirection.create(SHAPE_STRAIGHT, false))
-                        .build();
-        Lane rightHighlighted =
-                new Lane.Builder()
-                        .addDirection(LaneDirection.create(SHAPE_NORMAL_RIGHT, true))
-                        .build();
-
-        return new Step.Builder(currentStepCueWithImage)
-                .setManeuver(currentManeuver)
-                .setLanesImage(lanesImage)
-                .addLane(straightNormal)
-                .addLane(straightNormal)
-                .addLane(straightNormal)
-                .addLane(straightNormal)
-                .addLane(rightHighlighted)
-                .build();
-    }
-
-    /** Returns the next {@link Step} with information such as the cue text and images. */
-    @NonNull
-    public static Step getNextStep(@NonNull CarContext carContext) {
-        // Create the cue text, and span the "I5" text with an image.
-        String nextStepCue = carContext.getString(R.string.next_step_cue);
-        SpannableString nextStepCueWithImage = new SpannableString(nextStepCue);
-        CarIconSpan highwaySign =
-                CarIconSpan.create(
-                        new CarIcon.Builder(
-                                IconCompat.createWithResource(carContext, R.drawable.ic_i5))
-                                .build(),
-                        CarIconSpan.ALIGN_CENTER);
-        nextStepCueWithImage.setSpan(highwaySign, 0, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-
-        CarIcon nextTurnIcon =
-                new CarIcon.Builder(
-                        IconCompat.createWithResource(
-                                carContext, R.drawable.arrow_straight))
-                        .build();
-        Maneuver nextManeuver =
-                new Maneuver.Builder(Maneuver.TYPE_STRAIGHT).setIcon(nextTurnIcon).build();
-
-        return new Step.Builder(nextStepCueWithImage).setManeuver(nextManeuver).build();
-    }
-
-    /**
-     * Returns the action strip that contains a "bug report" button and "stop navigation" button.
-     */
-    @NonNull
-    public static ActionStrip getActionStrip(
-            @NonNull CarContext carContext, @NonNull OnClickListener onStopNavigation) {
-        ActionStrip.Builder builder = new ActionStrip.Builder();
-        if (carContext.getCarAppApiLevel() >= CarAppApiLevels.LEVEL_5) {
-            builder.addAction(
-                    new Action.Builder()
-                            .setOnClickListener(
-                                    () ->  carContext.getCarService(AppManager.class)
-                                            .showAlert(createAlert(carContext)))
-                            .setIcon(new CarIcon.Builder(
-                                    IconCompat.createWithResource(
-                                            carContext,
-                                            R.drawable.ic_baseline_add_alert_24))
-                                    .build())
-                            .build());
-        }
-        builder.addAction(
-                new Action.Builder()
-                        .setOnClickListener(
-                                () -> CarToast.makeText(
-                                                carContext,
-                                                carContext.getString(
-                                                        R.string.bug_reported_toast_msg),
-                                                CarToast.LENGTH_SHORT)
-                                        .show())
-                        .setIcon(
-                                new CarIcon.Builder(
-                                        IconCompat.createWithResource(
-                                                carContext,
-                                                R.drawable.ic_bug_report_24px))
-                                        .build())
-                        .build());
-        builder.addAction(
-                new Action.Builder()
-                        .setTitle(carContext.getString(R.string.stop_action_title))
-                        .setOnClickListener(onStopNavigation)
-                        .setFlags(Action.FLAG_IS_PERSISTENT)
-                        .build());
-        return builder.build();
-    }
-
-    /**
-     * Returns the map action strip that contains pan and zoom buttons.
-     */
-    @NonNull
-    public static ActionStrip getMapActionStrip(
-            @NonNull CarContext carContext) {
-        return new ActionStrip.Builder()
-                .addAction(
-                        new Action.Builder()
-                                .setOnClickListener(
-                                        () -> CarToast.makeText(
-                                                        carContext,
-                                                        carContext.getString(
-                                                                R.string.zoomed_in_toast_msg),
-                                                        CarToast.LENGTH_SHORT)
-                                                .show())
-                                .setIcon(
-                                        new CarIcon.Builder(
-                                                IconCompat.createWithResource(
-                                                        carContext,
-                                                        R.drawable.ic_zoom_in_24))
-                                                .build())
-                                .build())
-                .addAction(
-                        new Action.Builder()
-                                .setOnClickListener(
-                                        () -> CarToast.makeText(
-                                                        carContext,
-                                                        carContext.getString(
-                                                                R.string.zoomed_out_toast_msg),
-                                                        CarToast.LENGTH_SHORT)
-                                                .show())
-                                .setIcon(
-                                        new CarIcon.Builder(
-                                                IconCompat.createWithResource(
-                                                        carContext,
-                                                        R.drawable.ic_zoom_out_24))
-                                                .build())
-                                .build())
-                .addAction(Action.PAN)
-                .build();
-    }
-
-    /** Returns the {@link TravelEstimate} with time and distance information. */
-    @NonNull
-    public static TravelEstimate getTravelEstimate(@NonNull CarContext carContext) {
-        // Calculate the time to destination from the current time.
-        long nowUtcMillis = System.currentTimeMillis();
-        long timeToDestinationMillis = TimeUnit.HOURS.toMillis(1) + TimeUnit.MINUTES.toMillis(55);
-
-        return new TravelEstimate.Builder(
-                // The estimated distance to the destination.
-                Distance.create(112, Distance.UNIT_KILOMETERS),
-
-                // Arrival time at the destination with the destination time zone.
-                DateTimeWithZone.create(
-                        nowUtcMillis + timeToDestinationMillis,
-                        TimeZone.getTimeZone("US/Eastern")))
-                .setRemainingTimeSeconds(TimeUnit.MILLISECONDS.toSeconds(timeToDestinationMillis))
-                .setRemainingTimeColor(CarColor.YELLOW)
-                .setRemainingDistanceColor(CarColor.RED)
-                .setTripText(CarText.create(carContext.getString(R.string.travel_est_trip_text)))
-                .setTripIcon(new CarIcon.Builder(
-                        IconCompat.createWithResource(
-                                carContext,
-                                R.drawable.ic_face_24px))
-                        .build())
-                .build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/ResultDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/ResultDemoScreen.java
similarity index 96%
rename from car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/ResultDemoScreen.java
rename to car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/ResultDemoScreen.java
index c46df85..e819d8f 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/ResultDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/ResultDemoScreen.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.car.app.sample.showcase.common.misc;
+package androidx.car.app.sample.showcase.common.screens;
 
 import android.app.Activity;
 import android.content.ComponentName;
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/MapTemplateWithListDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/MapTemplateWithListDemoScreen.java
index 8cea680..54df637 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/MapTemplateWithListDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/MapTemplateWithListDemoScreen.java
@@ -36,7 +36,7 @@
 import androidx.car.app.navigation.model.MapController;
 import androidx.car.app.navigation.model.MapTemplate;
 import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.sample.showcase.common.navigation.routing.RoutingDemoModels;
+import androidx.car.app.sample.showcase.common.screens.navigationdemos.navigationtemplates.RoutingDemoModels;
 import androidx.car.app.versioning.CarAppApiLevels;
 import androidx.core.graphics.drawable.IconCompat;
 
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/MapTemplateWithPaneDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/MapTemplateWithPaneDemoScreen.java
index 3160b02..520973f 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/MapTemplateWithPaneDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/MapTemplateWithPaneDemoScreen.java
@@ -39,7 +39,7 @@
 import androidx.car.app.navigation.model.MapController;
 import androidx.car.app.navigation.model.MapTemplate;
 import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.sample.showcase.common.navigation.routing.RoutingDemoModels;
+import androidx.car.app.sample.showcase.common.screens.navigationdemos.navigationtemplates.RoutingDemoModels;
 import androidx.car.app.versioning.CarAppApiLevels;
 import androidx.core.graphics.drawable.IconCompat;
 
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/PlaceListNavigationTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/PlaceListNavigationTemplateDemoScreen.java
index b9a192c..550aa51 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/PlaceListNavigationTemplateDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/PlaceListNavigationTemplateDemoScreen.java
@@ -34,7 +34,7 @@
 import androidx.car.app.navigation.model.PlaceListNavigationTemplate;
 import androidx.car.app.sample.showcase.common.R;
 import androidx.car.app.sample.showcase.common.common.SamplePlaces;
-import androidx.car.app.sample.showcase.common.navigation.routing.RoutingDemoModels;
+import androidx.car.app.sample.showcase.common.screens.navigationdemos.navigationtemplates.RoutingDemoModels;
 import androidx.core.graphics.drawable.IconCompat;
 
 /** Creates a screen using the {@link PlaceListNavigationTemplate} */
@@ -78,6 +78,7 @@
                                                         : R.drawable.ic_favorite_white_24dp))
                                         .build())
                         .setOnClickListener(() -> {
+                            mIsFavorite = !mIsFavorite;
                             CarToast.makeText(
                                             getCarContext(),
                                             mIsFavorite
@@ -87,7 +88,6 @@
                                                             R.string.not_favorite_toast_msg),
                                             LENGTH_SHORT)
                                     .show();
-                            mIsFavorite = !mIsFavorite;
                             invalidate();
                         })
                         .build())
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/PlaceListTemplateBrowseDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/PlaceListTemplateBrowseDemoScreen.java
index 8a6caac..3026fb0 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/PlaceListTemplateBrowseDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/PlaceListTemplateBrowseDemoScreen.java
@@ -37,7 +37,6 @@
 import androidx.car.app.model.Row;
 import androidx.car.app.model.Template;
 import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.sample.showcase.common.templates.PlaceListTemplateDemoScreen;
 import androidx.core.location.LocationListenerCompat;
 import androidx.lifecycle.DefaultLifecycleObserver;
 import androidx.lifecycle.LifecycleOwner;
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/RoutePreviewDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/RoutePreviewDemoScreen.java
index 8e2609d..0865df3 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/RoutePreviewDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/navigationdemos/RoutePreviewDemoScreen.java
@@ -36,7 +36,7 @@
 import androidx.car.app.model.Template;
 import androidx.car.app.navigation.model.RoutePreviewNavigationTemplate;
 import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.sample.showcase.common.navigation.routing.RoutingDemoModels;
+import androidx.car.app.sample.showcase.common.screens.navigationdemos.navigationtemplates.RoutingDemoModels;
 import androidx.core.graphics.drawable.IconCompat;
 
 import java.util.concurrent.TimeUnit;
@@ -134,6 +134,7 @@
                                                         : R.drawable.ic_favorite_white_24dp))
                                         .build())
                         .setOnClickListener(() -> {
+                            mIsFavorite = !mIsFavorite;
                             CarToast.makeText(
                                             getCarContext(),
                                             mIsFavorite
@@ -143,7 +144,6 @@
                                                             R.string.not_favorite_toast_msg),
                                             LENGTH_SHORT)
                                     .show();
-                            mIsFavorite = !mIsFavorite;
                             invalidate();
                         })
                         .build())
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/templatelayouts/GridTemplateMenuDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/templatelayouts/GridTemplateMenuDemoScreen.java
index 930f1b3..ea8c1f5 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/templatelayouts/GridTemplateMenuDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/templatelayouts/GridTemplateMenuDemoScreen.java
@@ -26,8 +26,8 @@
 import androidx.car.app.model.Row;
 import androidx.car.app.model.Template;
 import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.sample.showcase.common.misc.NotificationDemoScreen;
 import androidx.car.app.sample.showcase.common.screens.templatelayouts.gridtemplates.GridTemplateDemoScreen;
+import androidx.car.app.sample.showcase.common.screens.templatelayouts.gridtemplates.NotificationDemoScreen;
 import androidx.lifecycle.DefaultLifecycleObserver;
 
 /**
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/FinishAppScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/userinteractions/FinishAppScreen.java
similarity index 95%
rename from car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/FinishAppScreen.java
rename to car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/userinteractions/FinishAppScreen.java
index 2466224..344516a 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/misc/FinishAppScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/screens/userinteractions/FinishAppScreen.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.car.app.sample.showcase.common.misc;
+package androidx.car.app.sample.showcase.common.screens.userinteractions;
 
 import static androidx.car.app.model.Action.BACK;
 
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/GridTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/GridTemplateDemoScreen.java
deleted file mode 100644
index fd0a075..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/GridTemplateDemoScreen.java
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.templates;
-
-import static androidx.car.app.CarToast.LENGTH_SHORT;
-import static androidx.car.app.model.Action.BACK;
-
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.Handler;
-import android.os.Looper;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.car.app.CarContext;
-import androidx.car.app.CarToast;
-import androidx.car.app.Screen;
-import androidx.car.app.constraints.ConstraintManager;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.ActionStrip;
-import androidx.car.app.model.CarIcon;
-import androidx.car.app.model.GridItem;
-import androidx.car.app.model.GridTemplate;
-import androidx.car.app.model.ItemList;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.versioning.CarAppApiLevels;
-import androidx.core.graphics.drawable.IconCompat;
-import androidx.lifecycle.DefaultLifecycleObserver;
-import androidx.lifecycle.LifecycleOwner;
-
-/** Creates a screen that demonstrates usage of the full screen {@link GridTemplate}. */
-public final class GridTemplateDemoScreen extends Screen implements DefaultLifecycleObserver {
-    private static final int MAX_GRID_ITEMS = 100;
-    private static final int LOADING_TIME_MILLIS = 2000;
-    private final Handler mHandler = new Handler(Looper.getMainLooper());
-
-    @Nullable
-    private IconCompat mImage;
-    @Nullable
-    private IconCompat mIcon;
-    private boolean mIsFourthItemLoading;
-    private boolean mThirdItemToggleState;
-    private boolean mFourthItemToggleState;
-    private boolean mFifthItemToggleState;
-
-    public GridTemplateDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-        getLifecycle().addObserver(this);
-        mIsFourthItemLoading = false;
-        mThirdItemToggleState = false;
-        mFourthItemToggleState = true;
-        mFifthItemToggleState = false;
-    }
-
-    @Override
-    public void onCreate(@NonNull LifecycleOwner owner) {
-        Resources resources = getCarContext().getResources();
-        Bitmap bitmap = BitmapFactory.decodeResource(resources, R.drawable.test_image_square);
-        mImage = IconCompat.createWithBitmap(bitmap);
-        mIcon = IconCompat.createWithResource(getCarContext(), R.drawable.ic_fastfood_white_48dp);
-    }
-
-    @Override
-    @SuppressWarnings({"FutureReturnValueIgnored"})
-    public void onStart(@NonNull LifecycleOwner owner) {
-        mIsFourthItemLoading = false;
-
-        // Post a message that starts loading the fourth item for some time.
-        triggerFourthItemLoading();
-    }
-
-    private GridItem createGridItem(int index) {
-        switch (index) {
-            case 0:
-                // Grid item with an icon and a title.
-                return new GridItem.Builder()
-                        .setImage(new CarIcon.Builder(mIcon).build(), GridItem.IMAGE_TYPE_ICON)
-                        .setTitle(getCarContext().getString(R.string.non_actionable))
-                        .build();
-            case 1:
-                // Grid item with an icon, a title, onClickListener and no text.
-                return new GridItem.Builder()
-                        .setImage(new CarIcon.Builder(mIcon).build(), GridItem.IMAGE_TYPE_ICON)
-                        .setTitle(getCarContext().getString(R.string.second_item))
-                        .setOnClickListener(
-                                () -> CarToast.makeText(
-                                                getCarContext(),
-                                                getCarContext()
-                                                        .getString(R.string.second_item_toast_msg),
-                                                LENGTH_SHORT)
-                                        .show())
-                        .build();
-            case 2:
-                // Grid item with an icon marked as icon, a title, a text and a toggle in
-                // unchecked state.
-                return new GridItem.Builder()
-                        .setImage(new CarIcon.Builder(mIcon).build(), GridItem.IMAGE_TYPE_ICON)
-                        .setTitle(getCarContext().getString(R.string.third_item))
-                        .setText(mThirdItemToggleState
-                                ? getCarContext().getString(R.string.checked_action_title)
-                                : getCarContext().getString(R.string.unchecked_action_title))
-                        .setOnClickListener(
-                                () -> {
-                                    mThirdItemToggleState = !mThirdItemToggleState;
-                                    CarToast.makeText(
-                                                    getCarContext(),
-                                                    getCarContext().getString(
-                                                            R.string.third_item_checked_toast_msg)
-                                                            + ": " + mThirdItemToggleState,
-                                                    LENGTH_SHORT)
-                                            .show();
-                                    invalidate();
-                                })
-                        .build();
-            case 3:
-                // Grid item with an image, a title, a long text and a toggle that takes some
-                // time to
-                // update.
-                if (mIsFourthItemLoading) {
-                    return new GridItem.Builder()
-                            .setTitle(getCarContext().getString(R.string.fourth_item))
-                            .setText(mFourthItemToggleState
-                                    ? getCarContext().getString(R.string.on_action_title)
-                                    : getCarContext().getString(R.string.off_action_title))
-                            .setLoading(true)
-                            .build();
-                } else {
-                    return new GridItem.Builder()
-                            .setImage(new CarIcon.Builder(mImage).build())
-                            .setTitle(getCarContext().getString(R.string.fourth_item))
-                            .setText(mFourthItemToggleState
-                                    ? getCarContext().getString(R.string.on_action_title)
-                                    : getCarContext().getString(R.string.off_action_title))
-                            .setOnClickListener(this::triggerFourthItemLoading)
-                            .build();
-                }
-            case 4:
-                // Grid item with a large image, a long title, no text and a toggle in unchecked
-                // state.
-                return new GridItem.Builder()
-                        .setImage(new CarIcon.Builder(mImage).build(), GridItem.IMAGE_TYPE_LARGE)
-                        .setTitle(getCarContext().getString(R.string.fifth_item))
-                        .setOnClickListener(
-                                () -> {
-                                    mFifthItemToggleState = !mFifthItemToggleState;
-                                    CarToast.makeText(
-                                                    getCarContext(),
-                                                    getCarContext().getString(
-                                                            R.string.fifth_item_checked_toast_msg)
-                                                            + ": "
-                                                            + mFifthItemToggleState,
-                                                    LENGTH_SHORT)
-                                            .show();
-                                    invalidate();
-                                })
-                        .build();
-            case 5:
-                // Grid item with an image marked as an icon, a long title, a long text and
-                // onClickListener.
-                return
-                        new GridItem.Builder()
-                                .setImage(new CarIcon.Builder(mIcon).build(),
-                                        GridItem.IMAGE_TYPE_ICON)
-                                .setTitle(getCarContext().getString(R.string.sixth_item))
-                                .setText(getCarContext().getString(R.string.sixth_item))
-                                .setOnClickListener(
-                                        () -> CarToast.makeText(
-                                                        getCarContext(),
-                                                        getCarContext().getString(
-                                                                R.string.sixth_item_toast_msg),
-                                                        LENGTH_SHORT)
-                                                .show())
-                                .build();
-            default:
-                String titleText = (index + 1) + "th item";
-                String toastText = "Clicked " + (index + 1) + "th item";
-
-                return new GridItem.Builder()
-                        .setImage(new CarIcon.Builder(mIcon).build(),
-                                GridItem.IMAGE_TYPE_ICON)
-                        .setTitle(titleText)
-                        .setOnClickListener(
-                                () ->
-                                        CarToast.makeText(
-                                                        getCarContext(),
-                                                        toastText,
-                                                        LENGTH_SHORT)
-                                                .show())
-                        .build();
-        }
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        int itemLimit = 6;
-        // Adjust the item limit according to the car constrains.
-        if (getCarContext().getCarAppApiLevel() > CarAppApiLevels.LEVEL_1) {
-            itemLimit =
-                    Math.min(MAX_GRID_ITEMS,
-                            getCarContext().getCarService(ConstraintManager.class).getContentLimit(
-                                    ConstraintManager.CONTENT_LIMIT_TYPE_GRID));
-        }
-
-        ItemList.Builder gridItemListBuilder = new ItemList.Builder();
-        for (int i = 0; i <= itemLimit; i++) {
-            gridItemListBuilder.addItem(createGridItem(i));
-        }
-
-        Action settings = new Action.Builder()
-                .setTitle(getCarContext().getString(
-                        R.string.settings_action_title))
-                .setOnClickListener(
-                        () -> CarToast.makeText(
-                                        getCarContext(),
-                                        getCarContext().getString(R.string.settings_toast_msg),
-                                        LENGTH_SHORT)
-                                .show())
-                .build();
-        return new GridTemplate.Builder()
-                .setHeaderAction(Action.APP_ICON)
-                .setSingleList(gridItemListBuilder.build())
-                .setTitle(getCarContext().getString(R.string.grid_template_demo_title))
-                .setActionStrip(
-                        new ActionStrip.Builder()
-                                .addAction(settings)
-                                .build())
-                .setHeaderAction(BACK)
-                .build();
-    }
-
-    /**
-     * Changes the fourth item to a loading state for some time and changes it back to the loaded
-     * state.
-     */
-    private void triggerFourthItemLoading() {
-        mHandler.post(
-                () -> {
-                    mIsFourthItemLoading = true;
-                    invalidate();
-
-                    mHandler.postDelayed(
-                            () -> {
-                                mIsFourthItemLoading = false;
-                                mFourthItemToggleState = !mFourthItemToggleState;
-                                invalidate();
-                            },
-                            LOADING_TIME_MILLIS);
-                });
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/ListTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/ListTemplateDemoScreen.java
deleted file mode 100644
index 45fa97a..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/ListTemplateDemoScreen.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.templates;
-
-import static androidx.car.app.CarToast.LENGTH_LONG;
-import static androidx.car.app.model.Action.BACK;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.CarToast;
-import androidx.car.app.Screen;
-import androidx.car.app.constraints.ConstraintManager;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.ActionStrip;
-import androidx.car.app.model.CarText;
-import androidx.car.app.model.ItemList;
-import androidx.car.app.model.ListTemplate;
-import androidx.car.app.model.ParkedOnlyOnClickListener;
-import androidx.car.app.model.Row;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.versioning.CarAppApiLevels;
-import androidx.lifecycle.DefaultLifecycleObserver;
-
-/**
- * Creates a screen that demonstrates usage of the full screen {@link ListTemplate} to display a
- * full-screen list.
- */
-public final class ListTemplateDemoScreen extends Screen implements DefaultLifecycleObserver {
-    private static final int MAX_LIST_ITEMS = 100;
-
-    public ListTemplateDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-        getLifecycle().addObserver(this);
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        ItemList.Builder listBuilder = new ItemList.Builder();
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setOnClickListener(
-                                ParkedOnlyOnClickListener.create(() -> onClick(
-                                        getCarContext().getString(R.string.parked_toast_msg))))
-                        .setTitle(getCarContext().getString(R.string.parked_only_title))
-                        .addText(getCarContext().getString(R.string.parked_only_text))
-                        .build());
-
-        // Some hosts may allow more items in the list than others, so create more.
-        if (getCarContext().getCarAppApiLevel() > CarAppApiLevels.LEVEL_1) {
-            int listLimit =
-                    Math.min(MAX_LIST_ITEMS,
-                            getCarContext().getCarService(ConstraintManager.class).getContentLimit(
-                                    ConstraintManager.CONTENT_LIMIT_TYPE_LIST));
-
-            for (int i = 2; i <= listLimit; ++i) {
-                // For row text, set text variants that fit best in different screen sizes.
-                String secondTextStr = getCarContext().getString(R.string.second_line_text);
-                CarText secondText =
-                        new CarText.Builder(
-                                "================= " + secondTextStr + " ================")
-                                .addVariant("--------------------- " + secondTextStr
-                                        + " ----------------------")
-                                .addVariant(secondTextStr)
-                                .build();
-                final String onClickText = getCarContext().getString(R.string.clicked_row_prefix)
-                        + ": " + i;
-                Row.Builder rowBuilder = new Row.Builder()
-                        .setOnClickListener(() -> onClick(onClickText))
-                        .setTitle(
-                                getCarContext().getString(R.string.title_prefix) + " " + i);
-                if (i % 2 == 0) {
-                    rowBuilder.addText(getCarContext().getString(R.string.long_line_text));
-                } else {
-                    rowBuilder
-                            .addText(getCarContext().getString(R.string.first_line_text))
-                            .addText(secondText);
-                }
-                listBuilder.addItem(rowBuilder.build());
-            }
-        }
-
-        Action settings = new Action.Builder()
-                .setTitle(getCarContext().getString(
-                        R.string.settings_action_title))
-                .setOnClickListener(
-                        () -> CarToast.makeText(
-                                        getCarContext(),
-                                        getCarContext().getString(
-                                                R.string.settings_toast_msg),
-                                        LENGTH_LONG)
-                                .show())
-                .build();
-
-        return new ListTemplate.Builder()
-                .setSingleList(listBuilder.build())
-                .setTitle(getCarContext().getString(R.string.list_template_demo_title))
-                .setHeaderAction(BACK)
-                .setActionStrip(
-                        new ActionStrip.Builder()
-                                .addAction(settings)
-                                .build())
-                .build();
-    }
-
-    private void onClick(String text) {
-        CarToast.makeText(getCarContext(), text, LENGTH_LONG).show();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/LongMessageTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/LongMessageTemplateDemoScreen.java
deleted file mode 100644
index b9d310c..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/LongMessageTemplateDemoScreen.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.templates;
-
-import static androidx.car.app.CarToast.LENGTH_LONG;
-import static androidx.car.app.model.Action.BACK;
-import static androidx.car.app.model.Action.FLAG_PRIMARY;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.CarToast;
-import androidx.car.app.Screen;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.ActionStrip;
-import androidx.car.app.model.CarColor;
-import androidx.car.app.model.LongMessageTemplate;
-import androidx.car.app.model.MessageTemplate;
-import androidx.car.app.model.ParkedOnlyOnClickListener;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.versioning.CarAppApiLevels;
-
-/** A screen that demonstrates the long message template. */
-public class LongMessageTemplateDemoScreen extends Screen {
-    protected LongMessageTemplateDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        if (getCarContext().getCarAppApiLevel() < CarAppApiLevels.LEVEL_2) {
-            return new MessageTemplate.Builder(
-                    getCarContext().getString(R.string.long_msg_template_not_supported_text))
-                    .setTitle(getCarContext().getString(
-                            R.string.long_msg_template_not_supported_title))
-                    .setHeaderAction(Action.BACK)
-                    .build();
-        }
-
-        Action.Builder primaryActionBuilder = new Action.Builder()
-                .setOnClickListener(
-                        ParkedOnlyOnClickListener.create(() -> {
-                            getScreenManager().pop();
-                            CarToast.makeText(
-                                    getCarContext(),
-                                    getCarContext().getString(R.string.primary_action_title),
-                                    LENGTH_LONG
-                            ).show();
-                        }))
-                .setTitle(getCarContext().getString(R.string.accept_action_title));
-        if (getCarContext().getCarAppApiLevel() >= CarAppApiLevels.LEVEL_4) {
-            primaryActionBuilder.setFlags(FLAG_PRIMARY);
-        }
-
-        return new LongMessageTemplate.Builder(
-                getCarContext().getString(R.string.long_msg_template_text))
-                .setTitle(getCarContext().getString(R.string.long_msg_template_demo_title))
-                .setHeaderAction(BACK)
-                .addAction(primaryActionBuilder.build())
-                .addAction(new Action.Builder()
-                        .setBackgroundColor(CarColor.RED)
-                        .setOnClickListener(
-                                ParkedOnlyOnClickListener.create(() -> getScreenManager().pop()))
-                        .setTitle(getCarContext().getString(R.string.reject_action_title))
-                        .build())
-                .setActionStrip(new ActionStrip.Builder()
-                        .addAction(new Action.Builder()
-                                .setTitle(getCarContext().getString(R.string.more_action_title))
-                                .setOnClickListener(
-                                        () ->
-                                                CarToast.makeText(
-                                                                getCarContext(),
-                                                                getCarContext().getString(
-                                                                        R.string.more_toast_msg),
-                                                                LENGTH_LONG)
-                                                        .show())
-                                .build())
-                        .build())
-                .build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/MessageTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/MessageTemplateDemoScreen.java
deleted file mode 100644
index d2df37f..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/MessageTemplateDemoScreen.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.templates;
-
-import static androidx.car.app.CarToast.LENGTH_LONG;
-import static androidx.car.app.model.Action.BACK;
-import static androidx.car.app.model.Action.FLAG_PRIMARY;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.CarToast;
-import androidx.car.app.Screen;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.ActionStrip;
-import androidx.car.app.model.CarColor;
-import androidx.car.app.model.CarIcon;
-import androidx.car.app.model.MessageTemplate;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.versioning.CarAppApiLevels;
-import androidx.core.graphics.drawable.IconCompat;
-
-/** A screen that demonstrates the message template. */
-public class MessageTemplateDemoScreen extends Screen {
-
-    public MessageTemplateDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        Action.Builder primaryActionBuilder = new Action.Builder()
-                .setOnClickListener(() -> {
-                    CarToast.makeText(
-                            getCarContext(),
-                            getCarContext().getString(R.string.primary_action_title),
-                            LENGTH_LONG
-                    ).show();
-                })
-                .setTitle(getCarContext().getString(R.string.ok_action_title));
-        if (getCarContext().getCarAppApiLevel() >= CarAppApiLevels.LEVEL_4) {
-            primaryActionBuilder.setFlags(FLAG_PRIMARY);
-        }
-
-        Action settings = new Action.Builder()
-                .setTitle(getCarContext().getString(
-                        R.string.settings_action_title))
-                .setOnClickListener(
-                        () -> CarToast.makeText(
-                                        getCarContext(),
-                                        getCarContext().getString(
-                                                R.string.settings_toast_msg),
-                                        LENGTH_LONG)
-                                .show())
-                .build();
-
-        return new MessageTemplate.Builder(
-                getCarContext().getString(R.string.msg_template_demo_text))
-                .setTitle(getCarContext().getString(R.string.msg_template_demo_title))
-                .setIcon(
-                        new CarIcon.Builder(
-                                IconCompat.createWithResource(
-                                        getCarContext(),
-                                        R.drawable.ic_emoji_food_beverage_white_48dp))
-                                .setTint(CarColor.GREEN)
-                                .build())
-                .setHeaderAction(BACK)
-                .addAction(primaryActionBuilder.build())
-                .addAction(
-                        new Action.Builder()
-                                .setBackgroundColor(CarColor.RED)
-                                .setTitle(getCarContext().getString(R.string.throw_action_title))
-                                .setOnClickListener(
-                                        () -> {
-                                            throw new RuntimeException("Error");
-                                        })
-                                .build())
-
-                .setActionStrip(
-                        new ActionStrip.Builder()
-                                .addAction(settings)
-                                .build())
-                .build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/MiscTemplateDemosScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/MiscTemplateDemosScreen.java
deleted file mode 100644
index b717963..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/MiscTemplateDemosScreen.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package androidx.car.app.sample.showcase.common.templates;
-
-import static androidx.car.app.model.Action.BACK;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.Screen;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.ActionStrip;
-import androidx.car.app.model.ItemList;
-import androidx.car.app.model.ListTemplate;
-import androidx.car.app.model.Row;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.R;
-
-/** An assortment of demos for different templates. */
-public final class MiscTemplateDemosScreen extends Screen {
-    private static final int MAX_PAGES = 2;
-    private final int mPage;
-    private final int mItemLimit;
-
-    public MiscTemplateDemosScreen(@NonNull CarContext carContext, int page, int limit) {
-        super(carContext);
-        mPage = page;
-        mItemLimit = limit;
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        ItemList.Builder listBuilder = new ItemList.Builder();
-        Row[] screenArray = new Row[]{
-                createRow(getCarContext().getString(R.string.pane_template_demo_title),
-                        new PaneTemplateDemoScreen(getCarContext())),
-                createRow(getCarContext().getString(R.string.list_template_demo_title),
-                        new ListTemplateDemoScreen(getCarContext())),
-                createRow(getCarContext().getString(R.string.place_list_template_demo_title),
-                        new PlaceListTemplateBrowseDemoScreen(getCarContext())),
-                createRow(getCarContext().getString(R.string.search_template_demo_title),
-                        new SearchTemplateDemoScreen(getCarContext())),
-                createRow(getCarContext().getString(R.string.msg_template_demo_title),
-                        new MessageTemplateDemoScreen(getCarContext())),
-                createRow(getCarContext().getString(R.string.grid_template_demo_title),
-                        new GridTemplateDemoScreen(getCarContext())),
-                createRow(getCarContext().getString(R.string.sign_in_template_demo_title),
-                        new SignInTemplateDemoScreen(getCarContext())),
-                createRow(getCarContext().getString(R.string.long_msg_template_demo_title),
-                        new LongMessageTemplateDemoScreen(getCarContext()))
-        };
-        // If the screenArray size is under the limit, we will show all of them on the first page.
-        // Otherwise we will show them in multiple pages.
-        if (screenArray.length <= mItemLimit) {
-            for (int i = 0; i < screenArray.length; i++) {
-                listBuilder.addItem(screenArray[i]);
-            }
-        } else {
-            int currentItemStartIndex = mPage * mItemLimit;
-            int currentItemEndIndex = Math.min(currentItemStartIndex + mItemLimit,
-                    screenArray.length);
-            for (int i = currentItemStartIndex; i < currentItemEndIndex; i++) {
-                listBuilder.addItem(screenArray[i]);
-            }
-        }
-        ListTemplate.Builder builder = new ListTemplate.Builder()
-                .setSingleList(listBuilder.build())
-                .setTitle(getCarContext().getString(R.string.misc_templates_demos_title))
-                .setHeaderAction(BACK);
-        // If the current page does not cover the last item, we will show a More button
-        if ((mPage + 1) * mItemLimit < screenArray.length && mPage + 1 < MAX_PAGES) {
-            builder.setActionStrip(new ActionStrip.Builder()
-                    .addAction(new Action.Builder()
-                            .setTitle(getCarContext().getString(R.string.more_action_title))
-                            .setOnClickListener(() -> getScreenManager().push(
-                                    new MiscTemplateDemosScreen(getCarContext(), mPage + 1,
-                                            mItemLimit)))
-                            .build())
-                    .build());
-        }
-        return builder.build();
-    }
-
-    private Row createRow(String title, Screen screen) {
-        return new Row.Builder()
-                .setTitle(title)
-                .setOnClickListener(() -> getScreenManager().push(screen))
-                .build();
-    }
-}
\ No newline at end of file
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/PaneTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/PaneTemplateDemoScreen.java
deleted file mode 100644
index ea98c9f..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/PaneTemplateDemoScreen.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.templates;
-
-import static androidx.car.app.CarToast.LENGTH_SHORT;
-import static androidx.car.app.model.Action.FLAG_PRIMARY;
-
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.car.app.CarContext;
-import androidx.car.app.CarToast;
-import androidx.car.app.Screen;
-import androidx.car.app.constraints.ConstraintManager;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.ActionStrip;
-import androidx.car.app.model.CarColor;
-import androidx.car.app.model.CarIcon;
-import androidx.car.app.model.Pane;
-import androidx.car.app.model.PaneTemplate;
-import androidx.car.app.model.Row;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.versioning.CarAppApiLevels;
-import androidx.core.graphics.drawable.IconCompat;
-import androidx.lifecycle.DefaultLifecycleObserver;
-import androidx.lifecycle.LifecycleOwner;
-
-/**
- * Creates a screen that demonstrates usage of the full screen {@link PaneTemplate} to display a
- * details screen.
- */
-public final class PaneTemplateDemoScreen extends Screen implements DefaultLifecycleObserver {
-    @Nullable
-    private IconCompat mPaneImage;
-
-    @Nullable
-    private IconCompat mRowLargeIcon;
-
-    @Nullable
-    private IconCompat mCommuteIcon;
-
-    public PaneTemplateDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-        getLifecycle().addObserver(this);
-    }
-
-    @Override
-    public void onCreate(@NonNull LifecycleOwner owner) {
-        Resources resources = getCarContext().getResources();
-        Bitmap bitmap = BitmapFactory.decodeResource(resources, R.drawable.patio);
-        mPaneImage = IconCompat.createWithBitmap(bitmap);
-        mRowLargeIcon = IconCompat.createWithResource(getCarContext(),
-                R.drawable.ic_fastfood_white_48dp);
-        mCommuteIcon = IconCompat.createWithResource(getCarContext(), R.drawable.ic_commute_24px);
-    }
-
-    private Row createRow(int index) {
-        switch (index) {
-            case 0:
-                // Row with a large image.
-                return new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.first_row_title))
-                        .addText(getCarContext().getString(R.string.first_row_text))
-                        .addText(getCarContext().getString(R.string.first_row_text))
-                        .setImage(new CarIcon.Builder(mRowLargeIcon).build())
-                        .build();
-            default:
-                return new Row.Builder()
-                        .setTitle(
-                                getCarContext().getString(R.string.other_row_title_prefix) + (index
-                                        + 1))
-                        .addText(getCarContext().getString(R.string.other_row_text))
-                        .addText(getCarContext().getString(R.string.other_row_text))
-                        .build();
-        }
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        int listLimit = 4;
-
-        // Adjust the item limit according to the car constrains.
-        if (getCarContext().getCarAppApiLevel() > CarAppApiLevels.LEVEL_1) {
-            listLimit =
-                    getCarContext().getCarService(ConstraintManager.class).getContentLimit(
-                            ConstraintManager.CONTENT_LIMIT_TYPE_PANE);
-        }
-
-        Pane.Builder paneBuilder = new Pane.Builder();
-        for (int i = 0; i < listLimit; i++) {
-            paneBuilder.addRow(createRow(i));
-        }
-
-        // Also set a large image outside of the rows.
-        paneBuilder.setImage(new CarIcon.Builder(mPaneImage).build());
-
-        Action.Builder primaryActionBuilder = new Action.Builder()
-                .setTitle(getCarContext().getString(R.string.search_action_title))
-                .setBackgroundColor(CarColor.BLUE)
-                .setOnClickListener(
-                        () -> CarToast.makeText(
-                                        getCarContext(),
-                                        getCarContext().getString(R.string.search_toast_msg),
-                                        LENGTH_SHORT)
-                                .show());
-        if (getCarContext().getCarAppApiLevel() >= CarAppApiLevels.LEVEL_4) {
-            primaryActionBuilder.setFlags(FLAG_PRIMARY);
-        }
-
-        paneBuilder
-                .addAction(primaryActionBuilder.build())
-                .addAction(
-                        new Action.Builder()
-                                .setTitle(getCarContext().getString(R.string.options_action_title))
-                                .setOnClickListener(
-                                        () -> CarToast.makeText(
-                                                        getCarContext(),
-                                                        getCarContext().getString(
-                                                                R.string.options_toast_msg),
-                                                        LENGTH_SHORT)
-                                                .show())
-                                .build());
-
-        return new PaneTemplate.Builder(paneBuilder.build())
-                .setHeaderAction(Action.BACK)
-                .setActionStrip(
-                        new ActionStrip.Builder()
-                                .addAction(new Action.Builder()
-                                        .setTitle(getCarContext().getString(
-                                                R.string.commute_action_title))
-                                        .setIcon(
-                                                new CarIcon.Builder(mCommuteIcon)
-                                                        .setTint(CarColor.BLUE)
-                                                        .build())
-                                        .setOnClickListener(
-                                                () -> CarToast.makeText(
-                                                                getCarContext(),
-                                                                getCarContext().getString(
-                                                                        R.string.commute_toast_msg),
-                                                                LENGTH_SHORT)
-                                                        .show())
-                                        .build())
-                                .build())
-                .setTitle(getCarContext().getString(R.string.pane_template_demo_title))
-                .build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/PlaceListTemplateBrowseDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/PlaceListTemplateBrowseDemoScreen.java
deleted file mode 100644
index 1f595be..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/PlaceListTemplateBrowseDemoScreen.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.templates;
-
-import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
-import static android.Manifest.permission.ACCESS_FINE_LOCATION;
-
-import android.content.pm.PackageManager;
-import android.location.Location;
-import android.location.LocationManager;
-import android.os.HandlerThread;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.car.app.CarContext;
-import androidx.car.app.CarToast;
-import androidx.car.app.Screen;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.CarLocation;
-import androidx.car.app.model.ItemList;
-import androidx.car.app.model.Place;
-import androidx.car.app.model.PlaceListMapTemplate;
-import androidx.car.app.model.Row;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.core.location.LocationListenerCompat;
-import androidx.lifecycle.DefaultLifecycleObserver;
-import androidx.lifecycle.LifecycleOwner;
-
-/**
- * Creates a screen using the {@link PlaceListMapTemplate}.
- *
- * <p>This screen shows the ability to anchor the map around the current location when there are
- * no other POI markers present.
- */
-public final class PlaceListTemplateBrowseDemoScreen extends Screen {
-    private static final int LOCATION_UPDATE_MIN_INTERVAL_MILLIS = 1000;
-    private static final int LOCATION_UPDATE_MIN_DISTANCE_METER = 1;
-
-    final LocationListenerCompat mLocationListener;
-    final HandlerThread mLocationUpdateHandlerThread;
-    boolean mHasPermissionLocation;
-
-    @Nullable
-    private Location mCurrentLocation;
-
-    public PlaceListTemplateBrowseDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-
-        mHasPermissionLocation = carContext.checkSelfPermission(ACCESS_FINE_LOCATION)
-                == PackageManager.PERMISSION_GRANTED
-                || carContext.checkSelfPermission(ACCESS_COARSE_LOCATION)
-                == PackageManager.PERMISSION_GRANTED;
-
-        mLocationUpdateHandlerThread = new HandlerThread("LocationThread");
-        mLocationListener = location -> {
-            mCurrentLocation = location;
-            invalidate();
-        };
-
-        getLifecycle().addObserver(new DefaultLifecycleObserver() {
-            @Override
-            public void onResume(@NonNull LifecycleOwner owner) {
-                mHasPermissionLocation = carContext.checkSelfPermission(ACCESS_FINE_LOCATION)
-                        == PackageManager.PERMISSION_GRANTED
-                        || carContext.checkSelfPermission(ACCESS_COARSE_LOCATION)
-                        == PackageManager.PERMISSION_GRANTED;
-                if (mHasPermissionLocation) {
-                    LocationManager locationManager =
-                            carContext.getSystemService(LocationManager.class);
-                    locationManager.requestLocationUpdates(LocationManager.FUSED_PROVIDER,
-                            LOCATION_UPDATE_MIN_INTERVAL_MILLIS,
-                            LOCATION_UPDATE_MIN_DISTANCE_METER,
-                            mLocationListener,
-                            mLocationUpdateHandlerThread.getLooper());
-                } else {
-                    CarToast.makeText(carContext,
-                            getCarContext().getString(R.string.grant_location_permission_toast_msg),
-                            CarToast.LENGTH_LONG).show();
-                }
-            }
-
-            @Override
-            public void onPause(@NonNull LifecycleOwner owner) {
-                LocationManager locationManager =
-                        getCarContext().getSystemService(LocationManager.class);
-                locationManager.removeUpdates(mLocationListener);
-            }
-        });
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        PlaceListMapTemplate.Builder builder = new PlaceListMapTemplate.Builder()
-                .setItemList(new ItemList.Builder()
-                        .addItem(new Row.Builder()
-                                .setTitle(getCarContext().getString(R.string.browse_places_title))
-                                .setBrowsable(true)
-                                .setOnClickListener(
-                                        () -> getScreenManager().push(
-                                                new PlaceListTemplateDemoScreen(
-                                                        getCarContext()))).build())
-                        .build())
-                .setTitle(getCarContext().getString(R.string.place_list_template_demo_title))
-                .setHeaderAction(Action.BACK)
-                .setCurrentLocationEnabled(mHasPermissionLocation);
-
-        if (mCurrentLocation != null) {
-            builder.setAnchor(new Place.Builder(CarLocation.create(mCurrentLocation)).build());
-        }
-
-        return builder.build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/PlaceListTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/PlaceListTemplateDemoScreen.java
deleted file mode 100644
index eaaa686..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/PlaceListTemplateDemoScreen.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.templates;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.Screen;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.PlaceListMapTemplate;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.sample.showcase.common.common.SamplePlaces;
-
-/** Creates a screen using the {@link PlaceListMapTemplate} */
-public final class PlaceListTemplateDemoScreen extends Screen {
-    private final SamplePlaces mPlaces;
-
-    public PlaceListTemplateDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-        mPlaces = SamplePlaces.create(this);
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        return new PlaceListMapTemplate.Builder()
-                .setItemList(mPlaces.getPlaceList(/* randomOrder =*/false))
-                .setTitle(getCarContext().getString(R.string.place_list_template_demo_title))
-                .setHeaderAction(Action.BACK)
-                .build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SearchTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SearchTemplateDemoScreen.java
deleted file mode 100644
index 81e729d..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SearchTemplateDemoScreen.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.templates;
-
-import static androidx.car.app.CarToast.LENGTH_LONG;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.CarToast;
-import androidx.car.app.Screen;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.ActionStrip;
-import androidx.car.app.model.ItemList;
-import androidx.car.app.model.Row;
-import androidx.car.app.model.SearchTemplate;
-import androidx.car.app.model.SearchTemplate.SearchCallback;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.R;
-
-/** A screen that demonstrates the search template. */
-public class SearchTemplateDemoScreen extends Screen {
-
-    public SearchTemplateDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        ItemList.Builder listBuilder = new ItemList.Builder();
-        for (int i = 1; i <= 6; ++i) {
-            listBuilder.addItem(
-                    new Row.Builder()
-                            .setTitle(getCarContext().getString(R.string.title_prefix) + " " + i)
-                            .addText(getCarContext().getString(R.string.first_line_text))
-                            .addText(getCarContext().getString(R.string.second_line_text))
-                            .build());
-        }
-
-        SearchCallback searchListener =
-                new SearchCallback() {
-                    @Override
-                    public void onSearchTextChanged(@NonNull String searchText) {
-                    }
-
-                    @Override
-                    public void onSearchSubmitted(@NonNull String searchText) {
-                    }
-                };
-
-        ActionStrip actionStrip = new ActionStrip.Builder()
-                .addAction(
-                        new Action.Builder()
-                                .setTitle(getCarContext().getString(R.string.settings_action_title))
-                                .setOnClickListener(
-                                        () -> CarToast.makeText(
-                                                        getCarContext(),
-                                                        getCarContext().getString(
-                                                                R.string.settings_toast_msg),
-                                                        LENGTH_LONG)
-                                                .show())
-                                .build())
-                .build();
-
-        return new SearchTemplate.Builder(searchListener)
-                .setSearchHint(getCarContext().getString(R.string.search_hint))
-                .setHeaderAction(Action.BACK)
-                .setShowKeyboardByDefault(false)
-                .setItemList(listBuilder.build())
-                .setActionStrip(actionStrip)
-                .build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SignInTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SignInTemplateDemoScreen.java
deleted file mode 100644
index 6722f14..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SignInTemplateDemoScreen.java
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.templates;
-
-import static androidx.car.app.CarToast.LENGTH_LONG;
-
-import android.graphics.Color;
-import android.net.Uri;
-
-import androidx.activity.OnBackPressedCallback;
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.CarToast;
-import androidx.car.app.Screen;
-import androidx.car.app.model.Action;
-import androidx.car.app.model.CarColor;
-import androidx.car.app.model.CarIcon;
-import androidx.car.app.model.InputCallback;
-import androidx.car.app.model.MessageTemplate;
-import androidx.car.app.model.ParkedOnlyOnClickListener;
-import androidx.car.app.model.Template;
-import androidx.car.app.model.signin.InputSignInMethod;
-import androidx.car.app.model.signin.PinSignInMethod;
-import androidx.car.app.model.signin.ProviderSignInMethod;
-import androidx.car.app.model.signin.QRCodeSignInMethod;
-import androidx.car.app.model.signin.SignInTemplate;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.sample.showcase.common.common.Utils;
-import androidx.car.app.versioning.CarAppApiLevels;
-import androidx.core.graphics.drawable.IconCompat;
-
-/** A screen that demonstrates the sign-in template. */
-public class SignInTemplateDemoScreen extends Screen {
-    private static final String EMAIL_REGEXP = "^(.+)@(.+)$";
-    private static final String EXPECTED_PASSWORD = "password";
-    private static final int MIN_USERNAME_LENGTH = 5;
-    private final CharSequence mAdditionalText;
-    private final Action mProviderSignInAction;
-    private final Action mPinSignInAction;
-    private final Action mQRCodeSignInAction;
-    // package private to avoid synthetic accessor
-    State mState = State.USERNAME;
-    String mLastErrorMessage = ""; // last displayed error message
-    String mErrorMessage = "";
-    String mUsername = null;
-
-    public SignInTemplateDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-
-        // Handle back pressed events manually, as we use them to navigate between templates within
-        // the same screen.
-        OnBackPressedCallback callback = new OnBackPressedCallback(true) {
-            @Override
-            public void handleOnBackPressed() {
-                mErrorMessage = "";
-                if (mState == State.USERNAME || mState == State.SIGNED_IN) {
-                    getScreenManager().pop();
-                } else {
-                    mState = State.USERNAME;
-                    invalidate();
-                }
-            }
-        };
-        carContext.getOnBackPressedDispatcher().addCallback(this, callback);
-
-        mAdditionalText = Utils.clickable(getCarContext().getString(R.string.additional_text), 18,
-                16,
-                () -> getScreenManager().push(new LongMessageTemplateDemoScreen(getCarContext())));
-
-        mProviderSignInAction = new Action.Builder()
-                .setTitle(getCarContext().getString(R.string.google_sign_in))
-                .setOnClickListener(ParkedOnlyOnClickListener.create(() -> {
-                    mState = State.PROVIDER;
-                    invalidate();
-                }))
-                .build();
-
-        mPinSignInAction = new Action.Builder()
-                .setTitle(getCarContext().getString(R.string.use_pin))
-                .setOnClickListener(ParkedOnlyOnClickListener.create(() -> {
-                    mState = State.PIN;
-                    invalidate();
-                }))
-                .build();
-
-        mQRCodeSignInAction = new Action.Builder()
-                .setTitle(getCarContext().getString(R.string.qr_code))
-                .setOnClickListener(ParkedOnlyOnClickListener.create(() -> {
-                    mState = State.QR_CODE;
-                    invalidate();
-                }))
-                .build();
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        if (getCarContext().getCarAppApiLevel() < CarAppApiLevels.LEVEL_2) {
-            return new MessageTemplate.Builder(
-                    getCarContext().getString(R.string.sign_in_template_not_supported_text))
-                    .setTitle(getCarContext().getString(
-                            R.string.sign_in_template_not_supported_title))
-                    .setHeaderAction(Action.BACK)
-                    .build();
-        }
-        switch (mState) {
-            case USERNAME:
-                return getUsernameSignInTemplate();
-            case PASSWORD:
-                return getPasswordSignInTemplate();
-            case PIN:
-                return getPinSignInTemplate();
-            case PROVIDER:
-                return getProviderSignInTemplate();
-            case QR_CODE:
-                return getQRCodeSignInTemplate();
-            case SIGNED_IN:
-                return getSignInCompletedMessageTemplate();
-        }
-        throw new IllegalStateException("Invalid state: " + mState);
-    }
-
-    private Template getUsernameSignInTemplate() {
-        InputCallback listener = new InputCallback() {
-            @Override
-            public void onInputSubmitted(@NonNull String text) {
-                if (mState == State.USERNAME) {
-                    mUsername = text;
-                    submitUsername();
-                }
-            }
-
-            @Override
-            public void onInputTextChanged(@NonNull String text) {
-                // This callback demonstrates how to use handle the text changed event.
-                // In this case, we check that the user name doesn't exceed a certain length.
-                if (mState == State.USERNAME) {
-                    mUsername = text;
-                    mErrorMessage = validateUsername();
-
-                    // Invalidate the template (and hence possibly update the error message) only
-                    // if clearing up the error string, or if the error is changing.
-                    if (!mLastErrorMessage.isEmpty()
-                            && (mErrorMessage.isEmpty()
-                            || !mLastErrorMessage.equals(mErrorMessage))) {
-                        invalidate();
-                    }
-                }
-            }
-        };
-
-        InputSignInMethod.Builder builder = new InputSignInMethod.Builder(listener)
-                .setHint(getCarContext().getString(R.string.email_hint))
-                .setKeyboardType(InputSignInMethod.KEYBOARD_EMAIL);
-        if (mErrorMessage != null) {
-            builder.setErrorMessage(mErrorMessage);
-            mLastErrorMessage = mErrorMessage;
-        }
-        if (mUsername != null) {
-            builder.setDefaultValue(mUsername);
-        }
-        InputSignInMethod signInMethod = builder.build();
-
-        return new SignInTemplate.Builder(signInMethod)
-                .addAction(mProviderSignInAction)
-                .addAction(getCarContext().getCarAppApiLevel() > CarAppApiLevels.LEVEL_3
-                        ? mQRCodeSignInAction : mPinSignInAction)
-                .setTitle(getCarContext().getString(R.string.sign_in_title))
-                .setInstructions(getCarContext().getString(R.string.sign_in_instructions))
-                .setHeaderAction(Action.BACK)
-                .setAdditionalText(mAdditionalText)
-                .build();
-    }
-
-    /**
-     * Validates the currently entered user name and returns an error message string if invalid,
-     * or an empty string otherwise.
-     */
-    String validateUsername() {
-        if (mUsername == null || mUsername.length() < MIN_USERNAME_LENGTH) {
-            return getCarContext().getString(R.string.invalid_length_error_msg,
-                    Integer.toString(MIN_USERNAME_LENGTH));
-        } else if (!mUsername.matches(EMAIL_REGEXP)) {
-            return getCarContext().getString(R.string.invalid_email_error_msg);
-        } else {
-            return "";
-        }
-    }
-
-    /**
-     * Moves to the password screen if the user name currently entered is valid, or displays
-     * an error message otherwise.
-     */
-    void submitUsername() {
-        mErrorMessage = validateUsername();
-
-        boolean isError = !mErrorMessage.isEmpty();
-        if (!isError) {
-            // If there's no error, go to the password screen.
-            mState = State.PASSWORD;
-        }
-
-        // Invalidate the template so that we either display an error, or go to the password screen.
-        invalidate();
-    }
-
-    private Template getPasswordSignInTemplate() {
-        InputCallback callback = new InputCallback() {
-            @Override
-            public void onInputSubmitted(@NonNull String text) {
-                // Mocked password validation
-                if (!EXPECTED_PASSWORD.equals(text)) {
-                    mErrorMessage = getCarContext().getString(R.string.invalid_password_error_msg);
-                } else {
-                    mErrorMessage = "";
-                    mState = State.SIGNED_IN;
-                }
-                invalidate();
-            }
-        };
-        InputSignInMethod.Builder builder = new InputSignInMethod.Builder(callback)
-                .setHint(getCarContext().getString(R.string.password_hint))
-                .setInputType(InputSignInMethod.INPUT_TYPE_PASSWORD);
-        if (mErrorMessage != null) {
-            builder.setErrorMessage(mErrorMessage);
-        }
-        InputSignInMethod signInMethod = builder.build();
-
-        return new SignInTemplate.Builder(signInMethod)
-                .addAction(mProviderSignInAction)
-                .addAction(getCarContext().getCarAppApiLevel() > CarAppApiLevels.LEVEL_3
-                        ? mQRCodeSignInAction : mPinSignInAction)
-                .setTitle(getCarContext().getString(R.string.sign_in_title))
-                .setInstructions(
-                        getCarContext().getString(R.string.password_sign_in_instruction_prefix)
-                                + ": " + mUsername)
-                .setHeaderAction(Action.BACK)
-                .setAdditionalText(mAdditionalText)
-                .build();
-    }
-
-    private Template getPinSignInTemplate() {
-        PinSignInMethod pinSignInMethod = new PinSignInMethod("123456789ABC");
-        return new SignInTemplate.Builder(pinSignInMethod)
-                .setTitle(getCarContext().getString(R.string.sign_in_title))
-                .setInstructions(getCarContext().getString(R.string.pin_sign_in_instruction))
-                .setHeaderAction(Action.BACK)
-                .setAdditionalText(mAdditionalText)
-                .build();
-    }
-
-    private Template getQRCodeSignInTemplate() {
-        QRCodeSignInMethod qrCodeSignInMethod = new QRCodeSignInMethod(Uri.parse("https://www"
-                + ".youtube.com/watch?v=dQw4w9WgXcQ"));
-        return new SignInTemplate.Builder(qrCodeSignInMethod)
-                .setTitle(getCarContext().getString(R.string.qr_code_sign_in_title))
-                .setHeaderAction(Action.BACK)
-                .setAdditionalText(mAdditionalText)
-                .addAction(mPinSignInAction)
-                .addAction(mProviderSignInAction)
-                .build();
-    }
-
-    private Template getProviderSignInTemplate() {
-        IconCompat providerIcon = IconCompat.createWithResource(getCarContext(),
-                R.drawable.ic_googleg);
-        CarColor noTint = CarColor.createCustom(Color.TRANSPARENT, Color.TRANSPARENT);
-
-        ProviderSignInMethod providerSignInMethod = new ProviderSignInMethod(
-                new Action.Builder()
-                        .setTitle(Utils.colorize(
-                                getCarContext().getString(R.string.sign_in_with_google_title),
-                                CarColor.createCustom(Color.BLACK, Color.BLACK), 0, 19))
-                        .setBackgroundColor(CarColor.createCustom(Color.WHITE, Color.WHITE))
-                        .setIcon(new CarIcon.Builder(providerIcon)
-                                .setTint(noTint)
-                                .build())
-                        .setOnClickListener(ParkedOnlyOnClickListener.create(
-                                this::performSignInWithGoogleFlow)).build());
-
-        return new SignInTemplate.Builder(providerSignInMethod)
-                .setTitle(getCarContext().getString(R.string.sign_in_title))
-                .setInstructions(getCarContext().getString(R.string.provider_sign_in_instruction))
-                .setHeaderAction(Action.BACK)
-                .setAdditionalText(mAdditionalText)
-                .build();
-    }
-
-    private void performSignInWithGoogleFlow() {
-        // This is here for demonstration purposes, if the APK is not signed with a signature
-        // that has been registered for sign in with Google flow, the sign in will fail at runtime.
-
-//        Bundle extras = new Bundle(1);
-//        extras.putBinder(BINDER_KEY, new SignInWithGoogleActivity.OnSignInComplete() {
-//            @Override
-//            public void onSignInComplete(@Nullable GoogleSignInAccount account) {
-//                if (account == null) {
-//                    CarToast.makeText(getCarContext(), "Error signing in", LENGTH_LONG).show();
-//                    return;
-//                }
-//
-//                // Use the account
-//                CarToast.makeText(getCarContext(),
-//                        account.getGivenName() + " signed in", LENGTH_LONG).show();
-//            }
-//        });
-//        getCarContext().startActivity(
-//                new Intent()
-//                        .setClass(getCarContext(), SignInWithGoogleActivity.class)
-//                        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-//                        .putExtras(extras));
-        CarToast.makeText(getCarContext(),
-                        getCarContext().getString(R.string.sign_in_with_google_toast_msg),
-                        LENGTH_LONG)
-                .show();
-    }
-
-    private MessageTemplate getSignInCompletedMessageTemplate() {
-        return new MessageTemplate.Builder(
-                getCarContext().getString(R.string.sign_in_complete_text))
-                .setTitle(getCarContext().getString(R.string.sign_in_complete_title))
-                .setHeaderAction(Action.BACK)
-                .addAction(new Action.Builder()
-                        .setTitle(getCarContext().getString(R.string.sign_out_action_title))
-                        .setOnClickListener(() -> {
-                            mState = State.USERNAME;
-                            invalidate();
-                        })
-                        .build())
-                .build();
-    }
-
-    private enum State {
-        USERNAME,
-        PASSWORD,
-        PIN,
-        PROVIDER,
-        QR_CODE,
-        SIGNED_IN,
-    }
-
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SignInWithGoogleActivity.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SignInWithGoogleActivity.java
deleted file mode 100644
index d7bf59f..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SignInWithGoogleActivity.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.templates;
-
-import android.os.Bundle;
-
-import androidx.activity.ComponentActivity;
-import androidx.annotation.Nullable;
-
-/**
- * An activity for use by the car app library to perform actions such as requesting permissions.
- */
-public class SignInWithGoogleActivity extends ComponentActivity {
-    static final String BINDER_KEY = "binder";
-
-    @Override
-    protected void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-//        OnSignInComplete signInCompleteCallback =
-//                (OnSignInComplete) getIntent().getExtras().getBinder(BINDER_KEY);
-//
-//        ActivityResultLauncher<Intent> activityResultLauncher =
-//                registerForActivityResult(
-//                        new ActivityResultContracts.StartActivityForResult(),
-//                        result -> {
-//                            GoogleSignInAccount account =
-//                                    GoogleSignIn.getSignedInAccountFromIntent(
-//                                            result.getData()).getResult();
-//                            signInCompleteCallback.onSignInComplete(account);
-//                            finish();
-//                        });
-//
-//        GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(this);
-//        if (account != null) {
-//            signInCompleteCallback.onSignInComplete(account);
-//            finish();
-//        }
-//
-//        GoogleSignInOptions gso =
-//                new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
-//                        .requestEmail()
-//                        .requestProfile()
-//                        .build();
-//        GoogleSignInClient signInClient = GoogleSignIn.getClient(this, gso);
-//        activityResultLauncher.launch(signInClient.getSignInIntent());
-    }
-
-
-//    /**
-//     * Binder callback to provide to the sign in activity.
-//     */
-//    abstract static class OnSignInComplete extends Binder implements IBinder {
-//        /**
-//         * Notifies that sign in flow completed.
-//         *
-//         * @param account the account signed in or {@code null} if there were issues signing in.
-//         */
-//        public abstract void onSignInComplete(@Nullable GoogleSignInAccount account);
-//    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/textandicons/ContentProviderIconsDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/textandicons/ContentProviderIconsDemoScreen.java
deleted file mode 100644
index cc9a480..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/textandicons/ContentProviderIconsDemoScreen.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.textandicons;
-
-import static androidx.car.app.model.Action.BACK;
-
-import android.net.Uri;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.car.app.CarContext;
-import androidx.car.app.HostInfo;
-import androidx.car.app.Screen;
-import androidx.car.app.model.CarIcon;
-import androidx.car.app.model.ItemList;
-import androidx.car.app.model.ListTemplate;
-import androidx.car.app.model.Row;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.core.graphics.drawable.IconCompat;
-
-/** Creates a screen that demonstrate the image loading in the library using a content provider. */
-public final class ContentProviderIconsDemoScreen extends Screen {
-    private static final int[] ICON_DRAWABLES = {
-            R.drawable.arrow_right_turn, R.drawable.arrow_straight, R.drawable.ic_i5,
-            R.drawable.ic_520
-    };
-    @Nullable
-    private final String mHostPackageName;
-
-    public ContentProviderIconsDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-
-        HostInfo hostInfo = carContext.getHostInfo();
-        mHostPackageName = hostInfo == null ? null : hostInfo.getPackageName();
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        ItemList.Builder listBuilder = new ItemList.Builder();
-
-        String hostPackageName = mHostPackageName;
-        if (hostPackageName == null) {
-            // Cannot get the host package name, show an error message.
-            listBuilder.setNoItemsMessage(
-                    getCarContext().getString(R.string.images_unknown_host_error));
-        } else {
-            for (int i = 0; i < ICON_DRAWABLES.length; i++) {
-                int resId = ICON_DRAWABLES[i];
-                Uri uri = DelayedFileProvider.getUriForResource(getCarContext(), hostPackageName,
-                        resId);
-                listBuilder.addItem(
-                        new Row.Builder()
-                                .setImage(
-                                        new CarIcon.Builder(
-                                                IconCompat.createWithContentUri(uri))
-                                                .build())
-                                .setTitle(
-                                        getCarContext().getString(R.string.icon_title_prefix) + " "
-                                                + i)
-                                .build());
-            }
-        }
-
-
-        return new ListTemplate.Builder()
-                .setSingleList(listBuilder.build())
-                .setTitle(getCarContext().getString(R.string.content_provider_icons_demo_title))
-                .setHeaderAction(BACK)
-                .build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/textandicons/DelayedFileProvider.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/textandicons/DelayedFileProvider.java
deleted file mode 100644
index 38f7673..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/textandicons/DelayedFileProvider.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.textandicons;
-
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.CompressFormat;
-import android.graphics.BitmapFactory;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-
-import androidx.annotation.NonNull;
-import androidx.core.content.FileProvider;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.concurrent.ThreadLocalRandom;
-
-/** A simple file provider that returns files after a random delay. */
-public class DelayedFileProvider extends FileProvider {
-    private static final String FILE_PROVIDER_AUTHORITY = "com.showcase.fileprovider";
-    private static final String RESOURCE_DIR = "res";
-    private static final long MIN_DELAY_MILLIS = 1000;
-    private static final long MAX_DELAY_MILLIS = 3000;
-
-    /** Creates a file from the given resource id and returns the URI for it. */
-    @NonNull
-    public static Uri getUriForResource(@NonNull Context context,
-            @NonNull String hostPackageName, int resId) {
-        File resourceFile =
-                new File(context.getFilesDir().getAbsolutePath(), RESOURCE_DIR + "/" + resId);
-        if (!resourceFile.exists()) {
-            resourceFile.getParentFile().mkdir();
-
-            Bitmap bm = BitmapFactory.decodeResource(context.getResources(), resId);
-            try (FileOutputStream fos = new FileOutputStream(resourceFile)) {
-                bm.compress(CompressFormat.PNG, 10, fos);
-            } catch (IOException ex) {
-                throw new IllegalArgumentException("Invalid resource " + resId);
-            }
-        }
-        Uri uri = getUriForFile(context, FILE_PROVIDER_AUTHORITY, resourceFile);
-
-        // FileProvider requires the app to grant temporary access to the car hosts for the file.
-        // A URI from a content provider may not need to do this if its contents are public.
-        context.grantUriPermission(hostPackageName, uri,
-                Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
-        return uri;
-    }
-
-    @Override
-    @NonNull
-    public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode)
-            throws FileNotFoundException {
-        try {
-            // Wait for a random period between the minimum and maximum delay.
-            Thread.sleep(ThreadLocalRandom.current().nextLong(MIN_DELAY_MILLIS, MAX_DELAY_MILLIS));
-        } catch (InterruptedException e) {
-            throw new FileNotFoundException(e.getMessage());
-        }
-
-        return super.openFile(uri, mode);
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/textandicons/IconsDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/textandicons/IconsDemoScreen.java
deleted file mode 100644
index dbd49b9..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/textandicons/IconsDemoScreen.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.textandicons;
-
-import static androidx.car.app.model.Action.BACK;
-import static androidx.car.app.model.CarColor.GREEN;
-
-import android.graphics.BitmapFactory;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.Screen;
-import androidx.car.app.model.CarIcon;
-import androidx.car.app.model.ItemList;
-import androidx.car.app.model.ListTemplate;
-import androidx.car.app.model.Row;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.core.graphics.drawable.IconCompat;
-
-/** Creates a screen that demonstrate the usage of icons in the library. */
-public final class IconsDemoScreen extends Screen {
-    public IconsDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        ItemList.Builder listBuilder = new ItemList.Builder();
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setImage(new CarIcon.Builder(CarIcon.APP_ICON).build())
-                        .setTitle(getCarContext().getString(R.string.app_icon_title))
-                        .build());
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setImage(
-                                new CarIcon.Builder(
-                                        IconCompat.createWithResource(
-                                                getCarContext(),
-                                                R.drawable.ic_fastfood_white_48dp))
-                                        .build(),
-                                Row.IMAGE_TYPE_ICON)
-                        .setTitle(getCarContext().getString(R.string.vector_no_tint_title))
-                        .build());
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setImage(
-                                new CarIcon.Builder(
-                                        IconCompat.createWithResource(
-                                                getCarContext(),
-                                                R.drawable.ic_fastfood_white_48dp))
-                                        .setTint(GREEN)
-                                        .build())
-                        .setTitle(getCarContext().getString(R.string.vector_with_tint_title))
-                        .build());
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setImage(
-                                new CarIcon.Builder(
-                                        IconCompat.createWithResource(
-                                                getCarContext(),
-                                                R.drawable.ic_themed_icon_48dp))
-                                        .build())
-                        .setTitle(getCarContext()
-                                .getString(R.string.vector_with_app_theme_attr_title))
-                        .build());
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setImage(
-                                new CarIcon.Builder(
-                                        IconCompat.createWithResource(
-                                                getCarContext(), R.drawable.banana))
-                                        .build())
-                        .setTitle(getCarContext().getString(R.string.png_res_title))
-                        .build());
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setImage(
-                                new CarIcon.Builder(
-                                        IconCompat.createWithBitmap(
-                                                BitmapFactory.decodeResource(
-                                                        getCarContext().getResources(),
-                                                        R.drawable.banana)))
-                                        .build())
-                        .setTitle(getCarContext().getString(R.string.png_bitmap_title))
-                        .build());
-
-        return new ListTemplate.Builder()
-                .setSingleList(listBuilder.build())
-                .setTitle(getCarContext().getString(R.string.icons_demo_title))
-                .setHeaderAction(BACK)
-                .build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/textandicons/RowDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/textandicons/RowDemoScreen.java
deleted file mode 100644
index 643a8fb..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/textandicons/RowDemoScreen.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.textandicons;
-
-import static androidx.car.app.model.Action.BACK;
-import static androidx.car.app.model.CarColor.YELLOW;
-
-import android.text.SpannableString;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.Screen;
-import androidx.car.app.model.CarIcon;
-import androidx.car.app.model.ItemList;
-import androidx.car.app.model.ListTemplate;
-import androidx.car.app.model.Row;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.R;
-import androidx.car.app.sample.showcase.common.common.Utils;
-import androidx.core.graphics.drawable.IconCompat;
-import androidx.lifecycle.DefaultLifecycleObserver;
-
-/** Creates a screen that shows different types of rows in a list */
-public final class RowDemoScreen extends Screen implements DefaultLifecycleObserver {
-    private static final String FULL_STAR = "\u2605";
-    private static final String HALF_STAR = "\u00BD";
-
-    public RowDemoScreen(@NonNull CarContext carContext) {
-        super(carContext);
-        getLifecycle().addObserver(this);
-    }
-
-    private static CharSequence getRatingsString(Double ratings) {
-        String s;
-        double r;
-        for (s = "", r = ratings; r > 0; --r) {
-            s += r < 1 ? HALF_STAR : FULL_STAR;
-        }
-        SpannableString ss = new SpannableString(s + " ratings: " + ratings);
-        if (!s.isEmpty()) {
-            Utils.colorize(ss, YELLOW, 0, s.length());
-        }
-        return ss;
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        ItemList.Builder listBuilder = new ItemList.Builder();
-
-        listBuilder.addItem(new Row.Builder()
-                .setTitle(getCarContext().getString(R.string.just_row_title))
-                .build());
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.title_with_app_icon_row_title))
-                        .setImage(CarIcon.APP_ICON)
-                        .build());
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(
-                                R.string.title_with_res_id_image_row_title))
-                        .setImage(
-                                new CarIcon.Builder(
-                                        IconCompat.createWithResource(
-                                                getCarContext(),
-                                                R.drawable.ic_fastfood_white_48dp))
-                                        .build(),
-                                Row.IMAGE_TYPE_ICON)
-                        .build());
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(
-                                getCarContext().getString(R.string.title_with_svg_image_row_title))
-                        .setImage(
-                                new CarIcon.Builder(
-                                        IconCompat.createWithResource(
-                                                getCarContext(),
-                                                R.drawable
-                                                        .ic_emoji_food_beverage_white_48dp))
-                                        .build(),
-                                Row.IMAGE_TYPE_ICON)
-                        .build());
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(
-                                R.string.title_with_secondary_lines_row_title))
-                        .addText(getCarContext().getString(
-                                R.string.title_with_secondary_lines_row_text_1))
-                        .addText(getCarContext().getString(
-                                R.string.title_with_secondary_lines_row_text_2))
-                        .build());
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.colored_secondary_row_title))
-                        .addText(getRatingsString(3.5))
-                        .build());
-
-        return new ListTemplate.Builder()
-                .setSingleList(listBuilder.build())
-                .setTitle(getCarContext().getString(R.string.rows_demo_title))
-                .setHeaderAction(BACK)
-                .build();
-    }
-}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/textandicons/TextAndIconsDemosScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/textandicons/TextAndIconsDemosScreen.java
deleted file mode 100644
index 39b81ba..0000000
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/textandicons/TextAndIconsDemosScreen.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.car.app.sample.showcase.common.textandicons;
-
-import static androidx.car.app.model.Action.BACK;
-
-import androidx.annotation.NonNull;
-import androidx.car.app.CarContext;
-import androidx.car.app.Screen;
-import androidx.car.app.model.ItemList;
-import androidx.car.app.model.ListTemplate;
-import androidx.car.app.model.Row;
-import androidx.car.app.model.Template;
-import androidx.car.app.sample.showcase.common.R;
-
-/** Creates a screen that shows different types of texts and icons. */
-public final class TextAndIconsDemosScreen extends Screen {
-    public TextAndIconsDemosScreen(@NonNull CarContext carContext) {
-        super(carContext);
-    }
-
-    @NonNull
-    @Override
-    public Template onGetTemplate() {
-        ItemList.Builder listBuilder = new ItemList.Builder();
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.icons_demo_title))
-                        .setOnClickListener(
-                                () -> getScreenManager().push(new IconsDemoScreen(getCarContext())))
-                        .build());
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(
-                                R.string.content_provider_icons_demo_title))
-                        .setOnClickListener(
-                                () ->
-                                        getScreenManager()
-                                                .push(
-                                                        new ContentProviderIconsDemoScreen(
-                                                                getCarContext())))
-                        .build());
-
-        listBuilder.addItem(
-                new Row.Builder()
-                        .setTitle(getCarContext().getString(R.string.row_text_icons_demo_title))
-                        .setOnClickListener(
-                                () -> getScreenManager().push(new RowDemoScreen(getCarContext())))
-                        .build());
-
-        return new ListTemplate.Builder()
-                .setSingleList(listBuilder.build())
-                .setTitle(getCarContext().getString(R.string.text_icons_demo_title))
-                .setHeaderAction(BACK)
-                .build();
-    }
-}
diff --git a/car/app/app-samples/showcase/mobile/src/main/AndroidManifest.xml b/car/app/app-samples/showcase/mobile/src/main/AndroidManifest.xml
index 3e1f7f2..d72c73a 100644
--- a/car/app/app-samples/showcase/mobile/src/main/AndroidManifest.xml
+++ b/car/app/app-samples/showcase/mobile/src/main/AndroidManifest.xml
@@ -73,12 +73,12 @@
     </service>
 
     <service
-        android:name=".common.navigation.NavigationNotificationService"
+        android:name=".common.screens.navigationdemos.NavigationNotificationService"
         android:exported="true">
     </service>
 
     <provider
-        android:name=".common.textandicons.DelayedFileProvider"
+        android:name=".common.screens.templatelayouts.listtemplates.DelayedFileProvider"
         android:authorities="com.showcase.fileprovider"
         android:exported="false"
         android:grantUriPermissions="true">
diff --git a/car/app/app-testing/build.gradle b/car/app/app-testing/build.gradle
index 1458882..5262400 100644
--- a/car/app/app-testing/build.gradle
+++ b/car/app/app-testing/build.gradle
@@ -37,7 +37,7 @@
     testImplementation(libs.testCore)
     testImplementation(libs.testRunner)
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.robolectric)
     testImplementation(libs.truth)
 
diff --git a/car/app/app-testing/src/test/java/androidx/car/app/testing/ScreenControllerTest.java b/car/app/app-testing/src/test/java/androidx/car/app/testing/ScreenControllerTest.java
index 2c21047..7bc4a81 100644
--- a/car/app/app-testing/src/test/java/androidx/car/app/testing/ScreenControllerTest.java
+++ b/car/app/app-testing/src/test/java/androidx/car/app/testing/ScreenControllerTest.java
@@ -51,6 +51,7 @@
     private ScreenController mScreenController;
     private TestCarContext mCarContext;
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
diff --git a/car/app/app-testing/src/test/java/androidx/car/app/testing/SessionControllerTest.java b/car/app/app-testing/src/test/java/androidx/car/app/testing/SessionControllerTest.java
index f52934e..af90720 100644
--- a/car/app/app-testing/src/test/java/androidx/car/app/testing/SessionControllerTest.java
+++ b/car/app/app-testing/src/test/java/androidx/car/app/testing/SessionControllerTest.java
@@ -54,6 +54,7 @@
     private Intent mIntent;
     private Intent mScreenIntent;
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
diff --git a/car/app/app/build.gradle b/car/app/app/build.gradle
index a6b6f9b..da527c4 100644
--- a/car/app/app/build.gradle
+++ b/car/app/app/build.gradle
@@ -68,7 +68,7 @@
     testImplementation(libs.testCore)
     testImplementation(libs.testRunner)
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.robolectric)
     testImplementation(libs.truth)
     testImplementation project(path: ':car:app:app-testing')
diff --git a/car/app/app/src/test/java/androidx/car/app/AppManagerTest.java b/car/app/app/src/test/java/androidx/car/app/AppManagerTest.java
index dea3532..975bfcd 100644
--- a/car/app/app/src/test/java/androidx/car/app/AppManagerTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/AppManagerTest.java
@@ -91,6 +91,7 @@
 
     private AppManager mAppManager;
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
diff --git a/car/app/app/src/test/java/androidx/car/app/CarAppBinderTest.java b/car/app/app/src/test/java/androidx/car/app/CarAppBinderTest.java
index 1c46b90..aaf0b6e 100644
--- a/car/app/app/src/test/java/androidx/car/app/CarAppBinderTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/CarAppBinderTest.java
@@ -94,6 +94,7 @@
     private CarAppBinder mCarAppBinder;
     private Intent mIntentSet;
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/car/app/app/src/test/java/androidx/car/app/CarAppPermissionActivityTest.java b/car/app/app/src/test/java/androidx/car/app/CarAppPermissionActivityTest.java
index 3ef8603..0e858c2 100644
--- a/car/app/app/src/test/java/androidx/car/app/CarAppPermissionActivityTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/CarAppPermissionActivityTest.java
@@ -57,6 +57,7 @@
     private ActivityScenario<CarAppPermissionActivity> mActivity;
     private Application mApplication;
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
diff --git a/car/app/app/src/test/java/androidx/car/app/CarContextTest.java b/car/app/app/src/test/java/androidx/car/app/CarContextTest.java
index f3e6583..608d91d 100644
--- a/car/app/app/src/test/java/androidx/car/app/CarContextTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/CarContextTest.java
@@ -95,6 +95,7 @@
     private Screen mScreen2;
     private final TestLifecycleOwner mLifecycleOwner = new TestLifecycleOwner();
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
diff --git a/car/app/app/src/test/java/androidx/car/app/HostDispatcherTest.java b/car/app/app/src/test/java/androidx/car/app/HostDispatcherTest.java
index bb67654..bf127d0 100644
--- a/car/app/app/src/test/java/androidx/car/app/HostDispatcherTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/HostDispatcherTest.java
@@ -55,6 +55,7 @@
     private ISuggestionHost mSuggestionHost;
     private HostDispatcher mHostDispatcher = new HostDispatcher();
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
diff --git a/car/app/app/src/test/java/androidx/car/app/ScreenManagerTest.java b/car/app/app/src/test/java/androidx/car/app/ScreenManagerTest.java
index 3035593..fcc4022 100644
--- a/car/app/app/src/test/java/androidx/car/app/ScreenManagerTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/ScreenManagerTest.java
@@ -75,6 +75,7 @@
 
     private ScreenManager mScreenManager;
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/car/app/app/src/test/java/androidx/car/app/ScreenTest.java b/car/app/app/src/test/java/androidx/car/app/ScreenTest.java
index 4f9bb46..a6dfd53 100644
--- a/car/app/app/src/test/java/androidx/car/app/ScreenTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/ScreenTest.java
@@ -54,6 +54,7 @@
 
     private Screen mScreen;
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/car/app/app/src/test/java/androidx/car/app/connection/AutomotiveCarConnectionTypeLiveDataTest.java b/car/app/app/src/test/java/androidx/car/app/connection/AutomotiveCarConnectionTypeLiveDataTest.java
index aeb08a8..ae1edef 100644
--- a/car/app/app/src/test/java/androidx/car/app/connection/AutomotiveCarConnectionTypeLiveDataTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/connection/AutomotiveCarConnectionTypeLiveDataTest.java
@@ -34,6 +34,7 @@
 public class AutomotiveCarConnectionTypeLiveDataTest {
     @Mock private Observer<Integer> mMockObserver;
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/car/app/app/src/test/java/androidx/car/app/connection/CarConnectionTypeLiveDataTest.java b/car/app/app/src/test/java/androidx/car/app/connection/CarConnectionTypeLiveDataTest.java
index 9262c44..1d22cb9 100644
--- a/car/app/app/src/test/java/androidx/car/app/connection/CarConnectionTypeLiveDataTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/connection/CarConnectionTypeLiveDataTest.java
@@ -64,6 +64,7 @@
     private CarConnectionTypeLiveData mCarConnectionTypeLiveData;
     private TestContentProvider mContentProvider;
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/car/app/app/src/test/java/androidx/car/app/constraints/ConstraintManagerTest.java b/car/app/app/src/test/java/androidx/car/app/constraints/ConstraintManagerTest.java
index 4a905e5..fb4244d 100644
--- a/car/app/app/src/test/java/androidx/car/app/constraints/ConstraintManagerTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/constraints/ConstraintManagerTest.java
@@ -59,6 +59,7 @@
 
     private ConstraintManager mConstraintManager;
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
diff --git a/car/app/app/src/test/java/androidx/car/app/model/ItemListTest.java b/car/app/app/src/test/java/androidx/car/app/model/ItemListTest.java
index b947182..5d3abf3 100644
--- a/car/app/app/src/test/java/androidx/car/app/model/ItemListTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/model/ItemListTest.java
@@ -58,6 +58,7 @@
     @Mock
     private IOnDoneCallback.Stub mMockOnDoneCallback;
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/car/app/app/src/test/java/androidx/car/app/navigation/NavigationManagerTest.java b/car/app/app/src/test/java/androidx/car/app/navigation/NavigationManagerTest.java
index 4916000..1b47039 100644
--- a/car/app/app/src/test/java/androidx/car/app/navigation/NavigationManagerTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/navigation/NavigationManagerTest.java
@@ -89,6 +89,7 @@
                     .build();
     private TestCarContext mTestCarContext;
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
diff --git a/car/app/app/src/test/java/androidx/car/app/validation/HostValidatorTest.java b/car/app/app/src/test/java/androidx/car/app/validation/HostValidatorTest.java
index 92c4f48..4c9edf9 100644
--- a/car/app/app/src/test/java/androidx/car/app/validation/HostValidatorTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/validation/HostValidatorTest.java
@@ -73,6 +73,7 @@
     @Mock
     private Resources mResources;
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/car/app/app/src/test/java/androidx/car/app/validation/HostValidatorTestApi28.java b/car/app/app/src/test/java/androidx/car/app/validation/HostValidatorTestApi28.java
index cdff5ce..79d481b 100644
--- a/car/app/app/src/test/java/androidx/car/app/validation/HostValidatorTestApi28.java
+++ b/car/app/app/src/test/java/androidx/car/app/validation/HostValidatorTestApi28.java
@@ -67,6 +67,7 @@
     @Mock
     private Resources mResources;
 
+    @SuppressWarnings("deprecation") // b/239955611
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
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 af3f6e6..1c40b5c 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
@@ -17,17 +17,20 @@
 package androidx.compose.compiler.plugins.kotlin
 
 import androidx.compose.compiler.plugins.kotlin.lower.dumpSrc
+import java.io.File
 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.backend.common.extensions.IrPluginContextImpl
 import org.jetbrains.kotlin.backend.common.ir.BuiltinSymbolsBase
+import org.jetbrains.kotlin.backend.common.serialization.DescriptorByIdSignatureFinderImpl
 import org.jetbrains.kotlin.backend.jvm.JvmGeneratorExtensionsImpl
 import org.jetbrains.kotlin.backend.jvm.JvmIrTypeSystemContext
 import org.jetbrains.kotlin.backend.jvm.serialization.JvmIdSignatureDescriptor
 import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
 import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
 import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
+import org.jetbrains.kotlin.cli.jvm.config.configureJdkClasspathRoots
 import org.jetbrains.kotlin.config.JVMConfigurationKeys
 import org.jetbrains.kotlin.config.JvmTarget
 import org.jetbrains.kotlin.config.LanguageFeature
@@ -52,6 +55,7 @@
 import org.jetbrains.kotlin.ir.util.ReferenceSymbolTable
 import org.jetbrains.kotlin.ir.util.SymbolTable
 import org.jetbrains.kotlin.ir.util.TypeTranslator
+import org.jetbrains.kotlin.ir.util.createParameterDeclarations
 import org.jetbrains.kotlin.ir.util.dump
 import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource
 import org.jetbrains.kotlin.psi.KtFile
@@ -62,10 +66,6 @@
 import org.jetbrains.kotlin.resolve.AnalyzingUtils
 import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
 import org.jetbrains.kotlin.utils.addToStdlib.safeAs
-import java.io.File
-import org.jetbrains.kotlin.backend.common.serialization.DescriptorByIdSignatureFinderImpl
-import org.jetbrains.kotlin.cli.jvm.config.configureJdkClasspathRoots
-import org.jetbrains.kotlin.ir.util.createParameterDeclarations
 
 @Suppress("LeakingThis")
 abstract class ComposeIrTransformTest : AbstractIrTransformTest() {
@@ -78,9 +78,6 @@
     open val metricsDestination: String? get() = null
 
     protected var extension: ComposeIrGenerationExtension? = null
-    // Some tests require the plugin context in order to perform assertions, for example, a
-    // context is required to determine the stability of a type using the StabilityInferencer.
-    var pluginContext: IrPluginContext? = null
 
     override fun setUp() {
         super.setUp()
@@ -100,7 +97,6 @@
         module: IrModuleFragment,
         context: IrPluginContext
     ) {
-        pluginContext = context
         extension!!.generate(
             module,
             context
@@ -108,7 +104,6 @@
     }
 
     override fun tearDown() {
-        pluginContext = null
         extension = null
         super.tearDown()
     }
@@ -520,6 +515,11 @@
                 expectDescriptorToSymbol = null
             )
             irLinker.postProcess()
+
+            // See JvmIrCodegenFactory. This is necessary to avoid unbound symbols when inspecting
+            // lazy declarations in `irModuleFragment`.
+            stubGenerator.unboundSymbolGeneration = true
+
             return irModuleFragment
         }
     }
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 415f0a1..7ebff19 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
@@ -16,8 +16,8 @@
 
 package androidx.compose.compiler.plugins.kotlin
 
-import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
 import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
+import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
 
 abstract class AbstractMetricsTransformTest : ComposeIrTransformTest() {
     override val metricsDestination: String?
@@ -27,7 +27,7 @@
         module: IrModuleFragment,
         context: IrPluginContext
     ) {
-        extension!!.metrics = ModuleMetricsImpl(module.name.asString(), context)
+        extension!!.metrics = ModuleMetricsImpl(module.name.asString())
         super.postProcessingStep(module, context)
     }
 
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 a597c21..ce51b33 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
@@ -16,7 +16,7 @@
 
 package androidx.compose.compiler.plugins.kotlin
 
-import androidx.compose.compiler.plugins.kotlin.analysis.StabilityInferencer
+import androidx.compose.compiler.plugins.kotlin.analysis.stabilityOf
 import org.intellij.lang.annotations.Language
 import org.jetbrains.kotlin.ir.declarations.IrClass
 import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
@@ -1196,8 +1196,7 @@
         )
         val irModule = JvmCompilation().compile(files)
         val irClass = irModule.files.last().declarations.first() as IrClass
-        val classStability = StabilityInferencer(pluginContext!!)
-            .stabilityOf(irClass.defaultType as IrType)
+        val classStability = stabilityOf(irClass.defaultType as IrType)
 
         assertEquals(
             stability,
@@ -1215,8 +1214,7 @@
     ) {
         val irModule = buildModule(externalSrc, classDefSrc, dumpClasses)
         val irClass = irModule.files.last().declarations.first() as IrClass
-        val classStability = StabilityInferencer(pluginContext!!)
-            .stabilityOf(irClass.defaultType as IrType)
+        val classStability = stabilityOf(irClass.defaultType as IrType)
 
         assertEquals(
             stability,
@@ -1253,7 +1251,7 @@
             is IrExpression -> lastStatement
             else -> error("unexpected statement: $lastStatement")
         }
-        val exprStability = StabilityInferencer(pluginContext!!).stabilityOf(irExpr)
+        val exprStability = stabilityOf(irExpr)
 
         assertEquals(
             stability,
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 4aa8dad..f2c4d0d 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
@@ -568,7 +568,7 @@
             keyVisitor,
             context,
             symbolRemapper,
-            ModuleMetricsImpl("temp", context)
+            ModuleMetricsImpl("temp")
         ) {
             override fun makeKeySet(): MutableSet<String> {
                 return super.makeKeySet().also { builtKeys = it }
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 4436c8a..380be60 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
@@ -570,7 +570,7 @@
             keyVisitor,
             context,
             symbolRemapper,
-            ModuleMetricsImpl("temp", context)
+            ModuleMetricsImpl("temp")
         ) {
             override fun makeKeySet(): MutableSet<String> {
                 return super.makeKeySet().also { builtKeys = it }
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/BuildMetrics.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/BuildMetrics.kt
index bae2c51..c11e7ee 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/BuildMetrics.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/BuildMetrics.kt
@@ -17,13 +17,13 @@
 package androidx.compose.compiler.plugins.kotlin
 
 import androidx.compose.compiler.plugins.kotlin.analysis.Stability
-import androidx.compose.compiler.plugins.kotlin.analysis.StabilityInferencer
 import androidx.compose.compiler.plugins.kotlin.analysis.knownStable
 import androidx.compose.compiler.plugins.kotlin.analysis.knownUnstable
+import androidx.compose.compiler.plugins.kotlin.analysis.stabilityOf
 import androidx.compose.compiler.plugins.kotlin.lower.ComposableFunctionBodyTransformer
 import androidx.compose.compiler.plugins.kotlin.lower.IrSourcePrinterVisitor
 import androidx.compose.compiler.plugins.kotlin.lower.isUnitOrNullableUnit
-import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
+import java.io.File
 import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
 import org.jetbrains.kotlin.ir.declarations.IrClass
 import org.jetbrains.kotlin.ir.declarations.IrField
@@ -37,7 +37,6 @@
 import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
 import org.jetbrains.kotlin.name.FqName
 import org.jetbrains.kotlin.psi.KtParameter
-import java.io.File
 
 interface FunctionMetrics {
     val isEmpty: Boolean get() = false
@@ -190,9 +189,7 @@
 
 class ModuleMetricsImpl(
     var name: String,
-    context: IrPluginContext
 ) : ModuleMetrics {
-    val stabilityInferencer = StabilityInferencer(context)
     private var skippableComposables = 0
     private var restartableComposables = 0
     private var readonlyComposables = 0
@@ -250,7 +247,7 @@
                 }
                 if (field.name == KtxNameConventions.STABILITY_FLAG) continue
                 append("  ")
-                val fieldStability = stabilityInferencer.stabilityOf(field.type)
+                val fieldStability = stabilityOf(field.type)
                 append("${fieldStability.simpleHumanReadable()} ")
                 append(if (isVar) "var " else "val ")
                 append(field.name.asString())
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 7bae866..71cf52f 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,15 +19,15 @@
 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.ComposableTargetAnnotationsTransformer
 import androidx.compose.compiler.plugins.kotlin.lower.ComposableSymbolRemapper
+import androidx.compose.compiler.plugins.kotlin.lower.ComposableTargetAnnotationsTransformer
 import androidx.compose.compiler.plugins.kotlin.lower.ComposerIntrinsicTransformer
 import androidx.compose.compiler.plugins.kotlin.lower.ComposerLambdaMemoization
 import androidx.compose.compiler.plugins.kotlin.lower.ComposerParamTransformer
 import androidx.compose.compiler.plugins.kotlin.lower.CopyDefaultValuesFromExpectLowering
+import androidx.compose.compiler.plugins.kotlin.lower.DurableFunctionKeyTransformer
 import androidx.compose.compiler.plugins.kotlin.lower.DurableKeyVisitor
 import androidx.compose.compiler.plugins.kotlin.lower.KlibAssignableParamTransformer
-import androidx.compose.compiler.plugins.kotlin.lower.DurableFunctionKeyTransformer
 import androidx.compose.compiler.plugins.kotlin.lower.LiveLiteralTransformer
 import androidx.compose.compiler.plugins.kotlin.lower.WrapJsComposableLambdaLowering
 import androidx.compose.compiler.plugins.kotlin.lower.decoys.CreateDecoysTransformer
@@ -75,10 +75,7 @@
         val symbolRemapper = ComposableSymbolRemapper()
 
         if (metricsDestination != null || reportsDestination != null) {
-            metrics = ModuleMetricsImpl(
-                moduleFragment.name.asString(),
-                pluginContext
-            )
+            metrics = ModuleMetricsImpl(moduleFragment.name.asString())
         }
 
         ClassStabilityTransformer(
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 c37ad85e..7e5a40d 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
@@ -106,7 +106,7 @@
          * The maven version string of this compiler. This string should be updated before/after every
          * release.
          */
-        const val compilerVersion: String = "1.3.2"
+        const val compilerVersion: String = "1.3.3"
         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 4e67881..33c8b87 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,7 +18,6 @@
 
 import androidx.compose.compiler.plugins.kotlin.ComposeFqNames
 import androidx.compose.compiler.plugins.kotlin.lower.annotationClass
-import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
 import org.jetbrains.kotlin.backend.jvm.ir.isInlineClassType
 import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
 import org.jetbrains.kotlin.ir.declarations.IrClass
@@ -32,7 +31,6 @@
 import org.jetbrains.kotlin.ir.expressions.IrConst
 import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
 import org.jetbrains.kotlin.ir.expressions.IrExpression
-import org.jetbrains.kotlin.ir.expressions.IrGetObjectValue
 import org.jetbrains.kotlin.ir.expressions.IrGetValue
 import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
 import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
@@ -56,9 +54,11 @@
 import org.jetbrains.kotlin.ir.types.isUnit
 import org.jetbrains.kotlin.ir.types.makeNotNull
 import org.jetbrains.kotlin.ir.util.defaultType
+import org.jetbrains.kotlin.ir.util.findAnnotation
 import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
 import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
 import org.jetbrains.kotlin.ir.util.getInlineClassUnderlyingType
+import org.jetbrains.kotlin.ir.util.hasAnnotation
 import org.jetbrains.kotlin.ir.util.isEnumClass
 import org.jetbrains.kotlin.ir.util.isEnumEntry
 import org.jetbrains.kotlin.ir.util.isFinalClass
@@ -201,301 +201,278 @@
     }
 }
 
-class StabilityInferencer(val context: IrPluginContext) {
-    private val stableMarker = context.referenceClass(ComposeFqNames.StableMarker)
-    private val stabilityInferred = context.referenceClass(ComposeFqNames.StabilityInferred)
-    private val stable = context.referenceClass(ComposeFqNames.Stable)
+fun IrAnnotationContainer.hasStableMarker(): Boolean =
+    annotations.any { it.isStableMarker() }
 
-    fun IrAnnotationContainer.hasStableAnnotation(): Boolean {
-        return annotations.any { it.annotationClass == stable }
+private fun IrConstructorCall.isStableMarker(): Boolean =
+    annotationClass?.owner?.hasAnnotation(ComposeFqNames.StableMarker) == true
+
+private fun IrClass.hasStableMarkedDescendant(): Boolean {
+    if (hasStableMarker()) return true
+    return superTypes.any {
+        !it.isAny() && it.classOrNull?.owner?.hasStableMarkedDescendant() == true
+    }
+}
+
+private fun IrClass.isProtobufType(): Boolean {
+    // Quick exit as all protos are final
+    if (!isFinalClass) return false
+    val directParentClassName =
+        superTypes.lastOrNull { !it.isInterface() }
+            ?.classOrNull?.owner?.fqNameWhenAvailable?.toString()
+    return directParentClassName == "com.google.protobuf.GeneratedMessageLite" ||
+        directParentClassName == "com.google.protobuf.GeneratedMessage"
+}
+
+private fun IrAnnotationContainer.stabilityParamBitmask(): Int? =
+    (annotations.findAnnotation(ComposeFqNames.StabilityInferred)
+        ?.getValueArgument(0) as? IrConst<*>
+        )?.value as? Int
+
+// TODO: FunctionReference
+private val stableBuiltinTypes = mapOf(
+    "kotlin.Pair" to 0b11,
+    "kotlin.Triple" to 0b111,
+    "kotlin.Comparator" to 0,
+    "kotlin.Result" to 0b1,
+    "kotlin.ranges.ClosedRange" to 0b1,
+    "kotlin.ranges.ClosedFloatingPointRange" to 0b1,
+    // Guava
+    "com.google.common.collect.ImmutableList" to 0b1,
+    "com.google.common.collect.ImmutableEnumMap" to 0b11,
+    "com.google.common.collect.ImmutableMap" to 0b11,
+    "com.google.common.collect.ImmutableEnumSet" to 0b1,
+    "com.google.common.collect.ImmutableSet" to 0b1,
+    // Kotlinx immutable
+    "kotlinx.collections.immutable.ImmutableList" to 0b1,
+    "kotlinx.collections.immutable.ImmutableSet" to 0b1,
+    "kotlinx.collections.immutable.ImmutableMap" to 0b11,
+    // Dagger
+    "dagger.Lazy" to 0b1,
+)
+
+// TODO: buildList, buildMap, buildSet, etc.
+private val stableProducingFunctions = mapOf(
+    "kotlin.collections.CollectionsKt.emptyList" to 0,
+    "kotlin.collections.CollectionsKt.listOf" to 0b1,
+    "kotlin.collections.CollectionsKt.listOfNotNull" to 0b1,
+    "kotlin.collections.MapsKt.mapOf" to 0b11,
+    "kotlin.collections.MapsKt.emptyMap" to 0,
+    "kotlin.collections.SetsKt.setOf" to 0b1,
+    "kotlin.collections.SetsKt.emptySet" to 0,
+)
+
+fun stabilityOf(irType: IrType): Stability =
+    stabilityOf(irType, emptyMap(), emptySet())
+
+private fun stabilityOf(
+    declaration: IrClass,
+    substitutions: Map<IrTypeParameterSymbol, IrTypeArgument>,
+    currentlyAnalyzing: Set<IrClassifierSymbol>
+): Stability {
+    val symbol = declaration.symbol
+    if (currentlyAnalyzing.contains(symbol)) return Stability.Unstable
+    if (declaration.hasStableMarkedDescendant()) return Stability.Stable
+    if (declaration.isEnumClass || declaration.isEnumEntry) return Stability.Stable
+    if (declaration.defaultType.isPrimitiveType()) return Stability.Stable
+    if (declaration.isProtobufType()) return Stability.Stable
+
+    if (declaration.origin == IrDeclarationOrigin.IR_BUILTINS_STUB) {
+        error("Builtins Stub: ${declaration.name}")
     }
 
-    fun IrAnnotationContainer.hasStableMarker(): Boolean {
-        return annotations.any { it.isStableMarker() }
-    }
+    val analyzing = currentlyAnalyzing + symbol
 
-    fun IrConstructorCall.isStableMarker(): Boolean {
-        val symbol = annotationClass ?: return false
-        val owner = if (symbol.isBound) symbol.owner else return false
-        return owner.annotations.any { it.annotationClass == stableMarker }
-    }
-
-    fun IrClass.hasStableMarkedDescendant(): Boolean {
-        if (hasStableMarker()) return true
-        return superTypes.any {
-            !it.isAny() && it.classOrNull?.owner?.hasStableMarkedDescendant() == true
-        }
-    }
-
-    private fun IrClass.isProtobufType(): Boolean {
-        // Quick exit as all protos are final
-        if (!isFinalClass) return false
-        val directParentClassName =
-            superTypes.lastOrNull { !it.isInterface() }
-                ?.classOrNull?.owner?.fqNameWhenAvailable?.toString()
-        return directParentClassName == "com.google.protobuf.GeneratedMessageLite" ||
-            directParentClassName == "com.google.protobuf.GeneratedMessage"
-    }
-
-    fun IrAnnotationContainer.stabilityParamBitmask(): Int? {
-        @Suppress("UNCHECKED_CAST")
-        return (
-            annotations.firstOrNull {
-                it.type.classOrNull == stabilityInferred
-            }?.getValueArgument(0) as? IrConst<Int>
-            )?.value
-    }
-
-    // TODO: FunctionReference
-    private val stableBuiltinTypes = mapOf(
-        "kotlin.Pair" to 0b11,
-        "kotlin.Triple" to 0b111,
-        "kotlin.Comparator" to 0,
-        "kotlin.Result" to 0b1,
-        "kotlin.ranges.ClosedRange" to 0b1,
-        "kotlin.ranges.ClosedFloatingPointRange" to 0b1,
-        // Guava
-        "com.google.common.collect.ImmutableList" to 0b1,
-        "com.google.common.collect.ImmutableEnumMap" to 0b11,
-        "com.google.common.collect.ImmutableMap" to 0b11,
-        "com.google.common.collect.ImmutableEnumSet" to 0b1,
-        "com.google.common.collect.ImmutableSet" to 0b1,
-        // Kotlinx immutable
-        "kotlinx.collections.immutable.ImmutableList" to 0b1,
-        "kotlinx.collections.immutable.ImmutableSet" to 0b1,
-        "kotlinx.collections.immutable.ImmutableMap" to 0b11,
-        // Dagger
-        "dagger.Lazy" to 0b1,
-    )
-
-    // TODO: buildList, buildMap, buildSet, etc.
-    private val stableProducingFunctions = mapOf(
-        "kotlin.collections.CollectionsKt.emptyList" to 0,
-        "kotlin.collections.CollectionsKt.listOf" to 0b1,
-        "kotlin.collections.CollectionsKt.listOfNotNull" to 0b1,
-        "kotlin.collections.MapsKt.mapOf" to 0b11,
-        "kotlin.collections.MapsKt.emptyMap" to 0,
-        "kotlin.collections.SetsKt.setOf" to 0b1,
-        "kotlin.collections.SetsKt.emptySet" to 0,
-    )
-
-    fun stabilityOf(
-        declaration: IrClass,
-        substitutions: Map<IrTypeParameterSymbol, IrTypeArgument> = emptyMap(),
-        currentlyAnalyzing: Set<IrClassifierSymbol> = emptySet()
-    ): Stability {
-        val symbol = declaration.symbol
-        if (currentlyAnalyzing.contains(symbol)) return Stability.Unstable
-        if (declaration.hasStableMarkedDescendant()) return Stability.Stable
-        if (declaration.isEnumClass || declaration.isEnumEntry) return Stability.Stable
-        if (declaration.defaultType.isPrimitiveType()) return Stability.Stable
-        if (declaration.isProtobufType()) return Stability.Stable
-
-        if (declaration.origin == IrDeclarationOrigin.IR_BUILTINS_STUB) {
-            error("Builtins Stub: ${declaration.name}")
-        }
-
-        val analyzing = currentlyAnalyzing + symbol
-
-        if (canInferStability(declaration)) {
-            val fqName = declaration.fqNameWhenAvailable?.toString() ?: ""
-            val stability: Stability
-            val mask: Int
-            if (stableBuiltinTypes.contains(fqName)) {
-                mask = stableBuiltinTypes[fqName] ?: 0
-                stability = Stability.Stable
-            } else {
-                mask = declaration.stabilityParamBitmask() ?: return Stability.Unstable
-                stability = Stability.Runtime(declaration)
-            }
-            return when (mask) {
-                0 -> stability
-                else -> stability + Stability.Combined(
-                    declaration.typeParameters.mapIndexedNotNull { index, irTypeParameter ->
-                        if (mask and (0b1 shl index) != 0) {
-                            val sub = substitutions.get(irTypeParameter.symbol)
-                            if (sub != null)
-                                stabilityOf(sub, substitutions, analyzing)
-                            else
-                                Stability.Parameter(irTypeParameter)
-                        } else null
-                    }
-                )
-            }
-        } else if (declaration.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB) {
-            return Stability.Unstable
-        }
-
-        if (declaration.isInterface) {
-            return Stability.Unknown(declaration)
-        }
-
-        var stability = Stability.Stable
-
-        for (member in declaration.declarations) {
-            when (member) {
-                is IrProperty -> {
-                    member.backingField?.let {
-                        if (member.isVar && !member.isDelegated) return Stability.Unstable
-                        stability += stabilityOf(it.type, substitutions, analyzing)
-                    }
-                }
-                is IrField -> {
-                    stability += stabilityOf(member.type, substitutions, analyzing)
-                }
-            }
-        }
-
-        return stability
-    }
-
-    private fun canInferStability(declaration: IrClass): Boolean {
+    if (canInferStability(declaration)) {
         val fqName = declaration.fqNameWhenAvailable?.toString() ?: ""
-        return stableBuiltinTypes.contains(fqName) ||
-            declaration.origin == IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB
-    }
-
-    fun stabilityOf(
-        classifier: IrClassifierSymbol,
-        substitutions: Map<IrTypeParameterSymbol, IrTypeArgument> = emptyMap(),
-        currentlyAnalyzing: Set<IrClassifierSymbol> = emptySet()
-    ): Stability {
-        // if isEnum, return true
-        // class hasStableAnnotation()
-        val owner = classifier.owner
-        return when (owner) {
-            is IrClass -> stabilityOf(owner, substitutions, currentlyAnalyzing)
-            is IrTypeParameter -> Stability.Unstable
-            else -> error("Unexpected IrClassifier: $owner")
+        val stability: Stability
+        val mask: Int
+        if (stableBuiltinTypes.contains(fqName)) {
+            mask = stableBuiltinTypes[fqName] ?: 0
+            stability = Stability.Stable
+        } else {
+            mask = declaration.stabilityParamBitmask() ?: return Stability.Unstable
+            stability = Stability.Runtime(declaration)
         }
-    }
-
-    fun stabilityOf(
-        argument: IrTypeArgument,
-        substitutions: Map<IrTypeParameterSymbol, IrTypeArgument> = emptyMap(),
-        currentlyAnalyzing: Set<IrClassifierSymbol> = emptySet()
-    ): Stability {
-        return when (argument) {
-            is IrStarProjection -> Stability.Unstable
-            is IrTypeProjection -> stabilityOf(argument.type, substitutions, currentlyAnalyzing)
-            else -> error("Unexpected IrTypeArgument: $argument")
-        }
-    }
-
-    fun stabilityOf(
-        type: IrType,
-        substitutions: Map<IrTypeParameterSymbol, IrTypeArgument> = emptyMap(),
-        currentlyAnalyzing: Set<IrClassifierSymbol> = emptySet()
-    ): Stability {
-        return when {
-            type is IrErrorType -> Stability.Unstable
-            type is IrDynamicType -> Stability.Unstable
-
-            type.isUnit() ||
-                type.isPrimitiveType() ||
-                type.isFunctionOrKFunction() ||
-                type.isString() -> Stability.Stable
-
-            type.isTypeParameter() -> {
-                val arg = substitutions.get(type.classifierOrNull as IrTypeParameterSymbol)
-                if (arg != null) {
-                    stabilityOf(arg, substitutions, currentlyAnalyzing)
-                } else {
-                    Stability.Parameter(
-                        type.classifierOrFail.owner as IrTypeParameter
-                    )
-                }
-            }
-
-            type.isNullable() -> stabilityOf(
-                type.makeNotNull(),
-                substitutions,
-                currentlyAnalyzing
-            )
-            type.isInlineClassType() -> stabilityOf(
-                type.getInlinedClass()!!,
-                substitutions,
-                currentlyAnalyzing
-            )
-            type is IrSimpleType -> {
-                stabilityOf(
-                    type.classifier,
-                    substitutions + type.substitutionMap(),
-                    currentlyAnalyzing
-                )
-            }
-            type is IrTypeAbbreviation -> {
-                val aliased = type.typeAlias.owner.expandedType
-                // TODO(lmr): figure out how type.arguments plays in here
-                stabilityOf(aliased, substitutions, currentlyAnalyzing)
-            }
-            else -> error("Unexpected IrType: $type")
-        }
-    }
-
-    fun IrSimpleType.substitutionMap(): Map<IrTypeParameterSymbol, IrTypeArgument> {
-        val cls = classOrNull ?: return emptyMap()
-        val params = cls.owner.typeParameters.map { it.symbol }
-        val args = arguments
-        return params.zip(args).filter { (param, arg) ->
-            param != (arg as? IrSimpleType)?.classifier
-        }.toMap()
-    }
-
-    fun stabilityOf(expr: IrCall): Stability {
-        val function = expr.symbol.owner
-        val fqName = function.fqNameForIrSerialization
-
-        val baseStability = stabilityOf(expr.type)
-        val mask = stableProducingFunctions[fqName.asString()]
         return when (mask) {
-            null -> baseStability
-            0 -> Stability.Stable
-            else -> Stability.Combined(
-                (0 until expr.typeArgumentsCount).mapNotNull { index ->
+            0 -> stability
+            else -> stability + Stability.Combined(
+                declaration.typeParameters.mapIndexedNotNull { index, irTypeParameter ->
                     if (mask and (0b1 shl index) != 0) {
-                        val sub = expr.getTypeArgument(index)
+                        val sub = substitutions[irTypeParameter.symbol]
                         if (sub != null)
-                            stabilityOf(sub)
+                            stabilityOf(sub, substitutions, analyzing)
                         else
-                            Stability.Unstable
+                            Stability.Parameter(irTypeParameter)
                     } else null
                 }
             )
         }
+    } else if (declaration.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB) {
+        return Stability.Unstable
     }
 
-    fun stabilityOf(expr: IrExpression): Stability {
-        // look at type first. if type is stable, whole expression is
-        val stability = stabilityOf(expr.type)
-        if (stability.knownStable()) return stability
-        return when (expr) {
-            is IrConst<*> -> Stability.Stable
-            is IrGetObjectValue ->
-                if (stabilityOf(expr.symbol.owner).knownStable())
-                    Stability.Stable
-                else
-                    Stability.Unstable
-            is IrCall -> stabilityOf(expr)
-            is IrGetValue -> {
-                val owner = expr.symbol.owner
-                if (owner is IrVariable && !owner.isVar) {
-                    owner.initializer?.let { stabilityOf(it) } ?: stability
-                } else {
-                    stability
+    if (declaration.isInterface) {
+        return Stability.Unknown(declaration)
+    }
+
+    var stability = Stability.Stable
+
+    for (member in declaration.declarations) {
+        when (member) {
+            is IrProperty -> {
+                member.backingField?.let {
+                    if (member.isVar && !member.isDelegated) return Stability.Unstable
+                    stability += stabilityOf(it.type, substitutions, analyzing)
                 }
             }
-            // some default parameters and consts can be wrapped in composite
-            is IrComposite -> {
-                if (expr.statements.all { it is IrExpression && stabilityOf(it).knownStable() }) {
-                    Stability.Stable
-                } else {
-                    stability
-                }
+            is IrField -> {
+                stability += stabilityOf(member.type, substitutions, analyzing)
             }
-            else -> stability
         }
     }
+
+    return stability
+}
+
+private fun canInferStability(declaration: IrClass): Boolean {
+    val fqName = declaration.fqNameWhenAvailable?.toString() ?: ""
+    return stableBuiltinTypes.contains(fqName) ||
+        declaration.origin == IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB
+}
+
+private fun stabilityOf(
+    classifier: IrClassifierSymbol,
+    substitutions: Map<IrTypeParameterSymbol, IrTypeArgument>,
+    currentlyAnalyzing: Set<IrClassifierSymbol>
+): Stability {
+    // if isEnum, return true
+    // class hasStableAnnotation()
+    return when (val owner = classifier.owner) {
+        is IrClass -> stabilityOf(owner, substitutions, currentlyAnalyzing)
+        is IrTypeParameter -> Stability.Unstable
+        else -> error("Unexpected IrClassifier: $owner")
+    }
+}
+
+private fun stabilityOf(
+    argument: IrTypeArgument,
+    substitutions: Map<IrTypeParameterSymbol, IrTypeArgument>,
+    currentlyAnalyzing: Set<IrClassifierSymbol>
+): Stability {
+    return when (argument) {
+        is IrStarProjection -> Stability.Unstable
+        is IrTypeProjection -> stabilityOf(argument.type, substitutions, currentlyAnalyzing)
+        else -> error("Unexpected IrTypeArgument: $argument")
+    }
+}
+
+private fun stabilityOf(
+    type: IrType,
+    substitutions: Map<IrTypeParameterSymbol, IrTypeArgument>,
+    currentlyAnalyzing: Set<IrClassifierSymbol>
+): Stability {
+    return when {
+        type is IrErrorType -> Stability.Unstable
+        type is IrDynamicType -> Stability.Unstable
+
+        type.isUnit() ||
+            type.isPrimitiveType() ||
+            type.isFunctionOrKFunction() ||
+            type.isString() -> Stability.Stable
+
+        type.isTypeParameter() -> {
+            val arg = substitutions[type.classifierOrNull as IrTypeParameterSymbol]
+            if (arg != null) {
+                stabilityOf(arg, substitutions, currentlyAnalyzing)
+            } else {
+                Stability.Parameter(
+                    type.classifierOrFail.owner as IrTypeParameter
+                )
+            }
+        }
+
+        type.isNullable() -> stabilityOf(
+            type.makeNotNull(),
+            substitutions,
+            currentlyAnalyzing
+        )
+        type.isInlineClassType() -> stabilityOf(
+            type.getInlinedClass()!!,
+            substitutions,
+            currentlyAnalyzing
+        )
+        type is IrSimpleType -> {
+            stabilityOf(
+                type.classifier,
+                substitutions + type.substitutionMap(),
+                currentlyAnalyzing
+            )
+        }
+        type is IrTypeAbbreviation -> {
+            val aliased = type.typeAlias.owner.expandedType
+            // TODO(lmr): figure out how type.arguments plays in here
+            stabilityOf(aliased, substitutions, currentlyAnalyzing)
+        }
+        else -> error("Unexpected IrType: $type")
+    }
+}
+
+private fun IrSimpleType.substitutionMap(): Map<IrTypeParameterSymbol, IrTypeArgument> {
+    val cls = classOrNull ?: return emptyMap()
+    val params = cls.owner.typeParameters.map { it.symbol }
+    val args = arguments
+    return params.zip(args).filter { (param, arg) ->
+        param != (arg as? IrSimpleType)?.classifier
+    }.toMap()
+}
+
+private fun stabilityOf(expr: IrCall, baseStability: Stability): Stability {
+    val function = expr.symbol.owner
+    val fqName = function.fqNameForIrSerialization
+
+    return when (val mask = stableProducingFunctions[fqName.asString()]) {
+        null -> baseStability
+        0 -> Stability.Stable
+        else -> Stability.Combined(
+            (0 until expr.typeArgumentsCount).mapNotNull { index ->
+                if (mask and (0b1 shl index) != 0) {
+                    val sub = expr.getTypeArgument(index)
+                    if (sub != null)
+                        stabilityOf(sub)
+                    else
+                        Stability.Unstable
+                } else null
+            }
+        )
+    }
+}
+
+fun stabilityOf(expr: IrExpression): Stability {
+    // look at type first. if type is stable, whole expression is
+    val stability = stabilityOf(expr.type)
+    if (stability.knownStable()) return stability
+    return when (expr) {
+        is IrConst<*> -> Stability.Stable
+        is IrCall -> stabilityOf(expr, stability)
+        is IrGetValue -> {
+            val owner = expr.symbol.owner
+            if (owner is IrVariable && !owner.isVar) {
+                owner.initializer?.let { stabilityOf(it) } ?: stability
+            } else {
+                stability
+            }
+        }
+        // some default parameters and consts can be wrapped in composite
+        is IrComposite -> {
+            if (expr.statements.all { it is IrExpression && stabilityOf(it).knownStable() }) {
+                Stability.Stable
+            } else {
+                stability
+            }
+        }
+        else -> stability
+    }
 }
 
 private fun IrType.getInlinedClass(): IrClass? {
@@ -508,13 +485,10 @@
 }
 
 // From Kotin's InlineClasses.kt
-private tailrec fun erase(type: IrType): IrClass? {
-    val classifier = type.classifierOrFail
-
-    return when (classifier) {
+private tailrec fun erase(type: IrType): IrClass? =
+    when (val classifier = type.classifierOrFail) {
         is IrClassSymbol -> classifier.owner
         is IrScriptSymbol -> null // TODO: check if correct
         is IrTypeParameterSymbol -> erase(classifier.owner.superTypes.first())
         else -> error(classifier)
     }
-}
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 4e6580f..f8c2c8a 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
@@ -22,8 +22,8 @@
 import androidx.compose.compiler.plugins.kotlin.ModuleMetrics
 import androidx.compose.compiler.plugins.kotlin.analysis.ComposeWritableSlices
 import androidx.compose.compiler.plugins.kotlin.analysis.Stability
-import androidx.compose.compiler.plugins.kotlin.analysis.StabilityInferencer
 import androidx.compose.compiler.plugins.kotlin.analysis.knownStable
+import androidx.compose.compiler.plugins.kotlin.analysis.stabilityOf
 import androidx.compose.compiler.plugins.kotlin.irTrace
 import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
 import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
@@ -130,12 +130,12 @@
 import org.jetbrains.kotlin.ir.types.typeWith
 import org.jetbrains.kotlin.ir.util.DeepCopySymbolRemapper
 import org.jetbrains.kotlin.ir.util.SYNTHETIC_OFFSET
-import org.jetbrains.kotlin.ir.util.constructedClass
 import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
 import org.jetbrains.kotlin.ir.util.functions
 import org.jetbrains.kotlin.ir.util.getArguments
 import org.jetbrains.kotlin.ir.util.getPrimitiveArrayElementType
 import org.jetbrains.kotlin.ir.util.getPropertyGetter
+import org.jetbrains.kotlin.ir.util.hasAnnotation
 import org.jetbrains.kotlin.ir.util.isCrossinline
 import org.jetbrains.kotlin.ir.util.isFunction
 import org.jetbrains.kotlin.ir.util.isNoinline
@@ -170,20 +170,6 @@
 
     protected val builtIns = context.irBuiltIns
 
-    protected val stabilityInferencer = StabilityInferencer(context)
-
-    fun stabilityOf(expr: IrExpression) = stabilityInferencer.stabilityOf(expr)
-    fun stabilityOf(type: IrType) = stabilityInferencer.stabilityOf(type)
-    fun stabilityOf(cls: IrClass) = stabilityInferencer.stabilityOf(cls)
-
-    fun IrAnnotationContainer.hasStableMarker(): Boolean = with(stabilityInferencer) {
-        hasStableMarker()
-    }
-
-    fun IrAnnotationContainer.hasStableAnnotation(): Boolean = with(stabilityInferencer) {
-        hasStableAnnotation()
-    }
-
     private val _composerIrClass =
         context.referenceClass(ComposeFqNames.Composer)?.owner
             ?: error("Cannot find the Composer class in the classpath")
@@ -1070,7 +1056,7 @@
             // type of that object is Stable. (`Modifier` for instance is a common example)
             is IrGetObjectValue -> {
                 if (symbol.owner.isCompanion) true
-                else stabilityOf(symbol.owner).knownStable()
+                else stabilityOf(type).knownStable()
             }
             is IrConstructorCall -> isStatic()
             is IrCall -> isStatic()
@@ -1130,8 +1116,8 @@
                     return true
                 }
 
-                val getterIsStable = prop.hasStableAnnotation() ||
-                    function.hasStableAnnotation()
+                val getterIsStable = prop.hasAnnotation(ComposeFqNames.Stable) ||
+                    function.hasAnnotation(ComposeFqNames.Stable)
 
                 if (
                     getterIsStable &&
@@ -1161,7 +1147,7 @@
                 // immutable operations so the overall result is static if the operands are
                 // also static
                 val isStableOperator = fqName.topLevelName() == "kotlin" ||
-                    function.hasStableAnnotation()
+                    function.hasAnnotation(ComposeFqNames.Stable)
 
                 val typeIsStable = stabilityOf(type).knownStable()
                 if (!typeIsStable) return false
@@ -1198,7 +1184,7 @@
                 }
                 // normal function call. If the function is marked as Stable and the result
                 // is Stable, then the static-ness of it is the static-ness of its arguments
-                val isStable = symbol.owner.hasStableAnnotation()
+                val isStable = symbol.owner.hasAnnotation(ComposeFqNames.Stable)
                 if (!isStable) return false
 
                 val typeIsStable = stabilityOf(type).knownStable()
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ClassStabilityTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ClassStabilityTransformer.kt
index f882de8..5203f4d 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ClassStabilityTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ClassStabilityTransformer.kt
@@ -16,11 +16,13 @@
 
 package androidx.compose.compiler.plugins.kotlin.lower
 
-import androidx.compose.compiler.plugins.kotlin.ModuleMetrics
 import androidx.compose.compiler.plugins.kotlin.ComposeFqNames
+import androidx.compose.compiler.plugins.kotlin.ModuleMetrics
 import androidx.compose.compiler.plugins.kotlin.analysis.Stability
-import androidx.compose.compiler.plugins.kotlin.analysis.normalize
 import androidx.compose.compiler.plugins.kotlin.analysis.forEach
+import androidx.compose.compiler.plugins.kotlin.analysis.hasStableMarker
+import androidx.compose.compiler.plugins.kotlin.analysis.normalize
+import androidx.compose.compiler.plugins.kotlin.analysis.stabilityOf
 import org.jetbrains.kotlin.backend.common.ClassLoweringPass
 import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
 import org.jetbrains.kotlin.backend.jvm.ir.isInlineClassType
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 134e3f3..ffd6daa 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
@@ -24,6 +24,7 @@
 import androidx.compose.compiler.plugins.kotlin.analysis.Stability
 import androidx.compose.compiler.plugins.kotlin.analysis.knownStable
 import androidx.compose.compiler.plugins.kotlin.analysis.knownUnstable
+import androidx.compose.compiler.plugins.kotlin.analysis.stabilityOf
 import androidx.compose.compiler.plugins.kotlin.hasExplicitGroupsAnnotation
 import androidx.compose.compiler.plugins.kotlin.hasNonRestartableComposableAnnotation
 import androidx.compose.compiler.plugins.kotlin.hasReadonlyComposableAnnotation
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 ac344b1..2ad1a72 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
@@ -20,6 +20,7 @@
 import androidx.compose.compiler.plugins.kotlin.ModuleMetrics
 import androidx.compose.compiler.plugins.kotlin.analysis.ComposeWritableSlices
 import androidx.compose.compiler.plugins.kotlin.analysis.knownStable
+import androidx.compose.compiler.plugins.kotlin.analysis.stabilityOf
 import androidx.compose.compiler.plugins.kotlin.irTrace
 import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
 import org.jetbrains.kotlin.backend.common.extensions.IrPluginContextImpl
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/WindowInsetsDeviceTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/WindowInsetsDeviceTest.kt
index 8b651bd..ac6162f 100644
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/WindowInsetsDeviceTest.kt
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/WindowInsetsDeviceTest.kt
@@ -326,6 +326,11 @@
      */
     @Test
     fun insetsSetAtStart() {
+        rule.runOnUiThread {
+            @Suppress("UNNECESSARY_NOT_NULL_ASSERTION")
+            WindowCompat.getInsetsController(rule.activity.window, rule.activity.window.decorView)!!
+                .show(WindowInsetsCompat.Type.ime() or WindowInsetsCompat.Type.systemBars())
+        }
         var leftInset = 0
         var topInset = 0
         var rightInset = 0
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/WindowInsetsPaddingTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/WindowInsetsPaddingTest.kt
index 246095f..4ee3c8a 100644
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/WindowInsetsPaddingTest.kt
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/WindowInsetsPaddingTest.kt
@@ -171,9 +171,7 @@
             WindowInsetsCompat.Type.navigationBars(),
             Modifier.navigationBarsPadding(),
             sentInsets = AndroidXInsets.of(10, 0, 0, 0)
-        ) { width, height ->
-            Rect(10f, 0f, width.toFloat(), height.toFloat())
-        }
+        )
     }
 
     @Test
@@ -182,9 +180,7 @@
             WindowInsetsCompat.Type.navigationBars(),
             Modifier.navigationBarsPadding(),
             sentInsets = AndroidXInsets.of(0, 0, 12, 0)
-        ) { width, height ->
-            Rect(0f, 0f, width - 12f, height.toFloat())
-        }
+        )
     }
 
     @Test
@@ -193,9 +189,7 @@
             WindowInsetsCompat.Type.navigationBars(),
             Modifier.navigationBarsPadding(),
             sentInsets = AndroidXInsets.of(0, 0, 0, 13)
-        ) { width, height ->
-            Rect(0f, 0f, width.toFloat(), height - 13f)
-        }
+        )
     }
 
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
@@ -338,12 +332,11 @@
 
         dispatchApplyWindowInsets(insets)
 
-        rule.runOnIdle {
+        rule.waitUntil {
             val view = insetsView.findComposeView()
             val width = view.width
             val height = view.height
-            assertThat(coordinates.boundsInRoot())
-                .isEqualTo(Rect(0f, 10f, width.toFloat(), height - 15f))
+            coordinates.boundsInRoot() == Rect(0f, 10f, width.toFloat(), height - 15f)
         }
     }
 
@@ -373,12 +366,11 @@
 
         dispatchApplyWindowInsets(insets)
 
-        rule.runOnIdle {
+        rule.waitUntil {
             val view = insetsView.findComposeView()
             val width = view.width
             val height = view.height
-            assertThat(coordinates.boundsInRoot())
-                .isEqualTo(Rect(10f, 11f, width - 12f, height - 13f))
+            coordinates.boundsInRoot() == Rect(10f, 11f, width - 12f, height - 13f)
         }
     }
 
@@ -406,12 +398,11 @@
 
         dispatchApplyWindowInsets(insets)
 
-        rule.runOnIdle {
+        rule.waitUntil {
             val view = insetsView.findComposeView()
             val width = view.width
             val height = view.height
-            assertThat(coordinates.boundsInRoot())
-                .isEqualTo(Rect(10f, 11f, width - 12f, height - 13f))
+            coordinates.boundsInRoot() == Rect(10f, 11f, width - 12f, height - 13f)
         }
     }
 
@@ -456,7 +447,12 @@
         modifier: Modifier,
         sentInsets: AndroidXInsets = AndroidXInsets.of(10, 11, 12, 13),
         expected: (Int, Int) -> Rect = { width, height ->
-            Rect(10f, 11f, width - 12f, height - 13f)
+            Rect(
+                sentInsets.left.toFloat(),
+                sentInsets.top.toFloat(),
+                width - sentInsets.right.toFloat(),
+                height - sentInsets.bottom.toFloat()
+            )
         }
     ) {
         testInsetsPadding(type, sentInsets, expected) { modifier }
@@ -475,12 +471,12 @@
         val insets = sendInsets(type, sentInsets)
         insets.assertIsConsumed(type)
 
-        rule.runOnIdle {
+        rule.waitUntil {
             val view = insetsView.findComposeView()
             val width = view.width
             val height = view.height
             val expectedRect = expected(width, height)
-            assertThat(coordinates.boundsInRoot()).isEqualTo(expectedRect)
+            coordinates.boundsInRoot() == expectedRect
         }
     }
 
@@ -526,7 +522,12 @@
             sendInsets(WindowInsetsCompat.Type.systemBars())
 
             val view = insetsView.findComposeView()
-            val animation = sendImeStart(view)
+            val animation =
+                sendImeStart(
+                    view,
+                    AndroidXInsets.of(10, 11, 12, 13),
+                    WindowInsetsCompat.Type.systemBars()
+                )
 
             val width = view.width
             val height = view.height
@@ -578,12 +579,12 @@
         val insets = sendInsets(WindowInsetsCompat.Type.systemBars())
         insets.assertIsConsumed(WindowInsetsCompat.Type.systemBars())
 
-        rule.runOnIdle {
+        rule.waitUntil {
             val view = insetsView.findComposeView()
             val width = view.width
             val height = view.height
             val expectedRect = Rect(10f, 11f, width - 12f, height - 13f)
-            assertThat(coordinates.boundsInRoot()).isEqualTo(expectedRect)
+            coordinates.boundsInRoot() == expectedRect
         }
     }
 
@@ -882,7 +883,7 @@
 private class Api30Methods(
     val rule: AndroidComposeTestRule<ActivityScenarioRule<ComponentActivity>, ComponentActivity>
 ) {
-    fun sendImeStart(view: View): WindowInsetsAnimation {
+    fun sendImeStart(view: View, otherInsets: AndroidXInsets, type: Int): WindowInsetsAnimation {
         return rule.runOnIdle {
             val animation =
                 WindowInsetsAnimation(AndroidWindowInsets.Type.ime(), LinearInterpolator(), 100L)
@@ -894,6 +895,11 @@
                 imeInsets
             )
             view.dispatchWindowInsetsAnimationStart(animation, bounds)
+            val targetInsets = android.view.WindowInsets.Builder()
+                .setInsets(android.view.WindowInsets.Type.ime(), imeInsets)
+                .setInsets(type, otherInsets.toPlatformInsets())
+                .build()
+            view.dispatchApplyWindowInsets(targetInsets)
             animation
         }
     }
@@ -951,4 +957,4 @@
             return null
         }
     }
-}
\ No newline at end of file
+}
diff --git a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/WindowInsetsSizeTest.kt b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/WindowInsetsSizeTest.kt
index 15f486b..a6990b7 100644
--- a/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/WindowInsetsSizeTest.kt
+++ b/compose/foundation/foundation-layout/src/androidAndroidTest/kotlin/androidx/compose/foundation/layout/WindowInsetsSizeTest.kt
@@ -108,11 +108,11 @@
             androidx.core.graphics.Insets.of(25, 0, 0, 0)
         )
 
-        rule.runOnIdle {
+        rule.waitUntil {
             val view = findComposeView()
             val height = view.height
             val expectedSize = IntSize(15, height)
-            assertThat(coordinates.size).isEqualTo(expectedSize)
+            coordinates.size == expectedSize
         }
     }
 
@@ -157,11 +157,11 @@
             androidx.core.graphics.Insets.of(0, 0, 0, 25)
         )
 
-        rule.runOnIdle {
+        rule.waitUntil {
             val view = findComposeView()
             val width = view.width
             val expectedSize = IntSize(width, 15)
-            assertThat(coordinates.size).isEqualTo(expectedSize)
+            coordinates.size == expectedSize
         }
     }
 
@@ -281,8 +281,8 @@
             view.dispatchApplyWindowInsets(insets.toWindowInsets())
         }
 
-        rule.runOnIdle {
-            assertThat(coordinates.size).isEqualTo(IntSize(view.width, 10))
+        rule.waitUntil {
+            coordinates.size == IntSize(view.width, 10)
         }
     }
 
@@ -334,12 +334,12 @@
         val insets = sendInsets(type, sentInsets)
         assertThat(insets.isConsumed)
 
-        rule.runOnIdle {
+        rule.waitUntil {
             val view = findComposeView()
             val width = view.width
             val height = view.height
             val expectedSize = expected(IntSize(width, height))
-            assertThat(coordinates.size).isEqualTo(expectedSize)
+            coordinates.size == expectedSize
         }
     }
 
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 60e951f..1ea0f98 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
@@ -457,11 +457,7 @@
             }
             view.addOnAttachStateChangeListener(insetsListener)
 
-            // We don't need animation callbacks on earlier versions, so don't bother adding
-            // the listener. ViewCompat calls the animation callbacks superfluously.
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
-                ViewCompat.setWindowInsetsAnimationCallback(view, insetsListener)
-            }
+            ViewCompat.setWindowInsetsAnimationCallback(view, insetsListener)
         }
         accessCount++
     }
@@ -619,10 +615,16 @@
      */
     var prepared = false
 
+    /**
+     * `true` if there is an animation in progress.
+     */
+    var runningAnimation = false
+
     var savedInsets: WindowInsetsCompat? = null
 
     override fun onPrepare(animation: WindowInsetsAnimationCompat) {
         prepared = true
+        runningAnimation = true
         super.onPrepare(animation)
     }
 
@@ -644,18 +646,20 @@
 
     override fun onEnd(animation: WindowInsetsAnimationCompat) {
         prepared = false
+        runningAnimation = false
         val insets = savedInsets
         if (animation.durationMillis != 0L && insets != null) {
-            composeInsets.update(insets, animation.typeMask)
+            composeInsets.update(insets)
         }
         savedInsets = null
         super.onEnd(animation)
     }
 
     override fun onApplyWindowInsets(view: View, insets: WindowInsetsCompat): WindowInsetsCompat {
+        // Keep track of the most recent insets we've seen, to ensure onEnd will always use the
+        // most recently acquired insets
+        savedInsets = insets
         if (prepared) {
-            savedInsets = insets
-
             // There may be no callback on R if the animation is canceled after onPrepare(),
             // so we won't know if the onPrepare() was canceled or if this is an
             // onApplyWindowInsets() after the cancelation. We'll just post the value
@@ -663,9 +667,12 @@
             if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) {
                 view.post(this)
             }
-            return insets
+        } else if (!runningAnimation) {
+            // If an animation is running, rely on onProgress() to update the insets
+            // On APIs less than 30 where the IME animation is backported, this avoids reporting
+            // the final insets for a frame while the animation is running.
+            composeInsets.update(insets)
         }
-        composeInsets.update(insets)
         return if (composeInsets.consumes) WindowInsetsCompat.CONSUMED else insets
     }
 
@@ -679,6 +686,7 @@
     override fun run() {
         if (prepared) {
             prepared = false
+            runningAnimation = false
             savedInsets?.let {
                 composeInsets.update(it)
                 savedInsets = null
diff --git a/compose/foundation/foundation/build.gradle b/compose/foundation/foundation/build.gradle
index 3c02bb4..1d0a744 100644
--- a/compose/foundation/foundation/build.gradle
+++ b/compose/foundation/foundation/build.gradle
@@ -52,9 +52,9 @@
         testImplementation(libs.truth)
         testImplementation(libs.kotlinCoroutinesTest)
         testImplementation(libs.kotlinTest)
-        testImplementation(libs.mockitoCore)
+        testImplementation(libs.mockitoCore4)
         testImplementation(libs.kotlinReflect)
-        testImplementation(libs.mockitoKotlin)
+        testImplementation(libs.mockitoKotlin4)
 
         androidTestImplementation(project(":compose:test-utils"))
         androidTestImplementation(project(":internal-testutils-fonts"))
@@ -116,10 +116,10 @@
                 implementation(libs.testRules)
                 implementation(libs.testRunner)
                 implementation(libs.junit)
-                implementation(libs.mockitoCore)
+                implementation(libs.mockitoCore4)
                 implementation(libs.truth)
                 implementation(libs.kotlinReflect)
-                implementation(libs.mockitoKotlin)
+                implementation(libs.mockitoKotlin4)
             }
 
             commonTest.dependencies {
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/WindowInsetsDemo.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/WindowInsetsDemo.kt
index 22000fb..2f682ff 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/WindowInsetsDemo.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/WindowInsetsDemo.kt
@@ -20,8 +20,9 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.WindowInsets
-import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.safeDrawing
 import androidx.compose.foundation.layout.wrapContentSize
@@ -52,11 +53,13 @@
                 " Note that IME insets are only supported on API 23 and above."
         )
 
+        Spacer(Modifier.weight(1f))
+
         BasicTextField(
             value = "Click to show keyboard",
             onValueChange = {},
             modifier = Modifier
-                .fillMaxSize()
+                .fillMaxWidth()
                 .wrapContentSize(),
             textStyle = TextStyle(color = Color.Black.copy(alpha = 0.5f))
         ) { field ->
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextControllerTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextControllerTest.kt
index 29a5f39..9ff9b59 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextControllerTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextControllerTest.kt
@@ -18,8 +18,8 @@
 
 import androidx.compose.ui.text.AnnotatedString
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.whenever
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextDelegateTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextDelegateTest.kt
index ed21815..d2ceef2 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextDelegateTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextDelegateTest.kt
@@ -22,7 +22,7 @@
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.Density
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.mock
+import org.mockito.kotlin.mock
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextFieldBringIntoViewTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextFieldBringIntoViewTest.kt
index 9296223..f936cd5 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextFieldBringIntoViewTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextFieldBringIntoViewTest.kt
@@ -33,12 +33,12 @@
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.LayoutDirection
-import com.nhaarman.mockitokotlin2.any
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.reset
-import com.nhaarman.mockitokotlin2.verify
-import com.nhaarman.mockitokotlin2.verifyBlocking
-import com.nhaarman.mockitokotlin2.whenever
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.reset
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyBlocking
+import org.mockito.kotlin.whenever
 import kotlinx.coroutines.runBlocking
 import org.junit.Before
 import org.junit.Test
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextFieldDelegateTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextFieldDelegateTest.kt
index d586f8f..3ced499 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextFieldDelegateTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextFieldDelegateTest.kt
@@ -38,13 +38,13 @@
 import androidx.compose.ui.text.input.VisualTransformation
 import androidx.compose.ui.text.style.TextDecoration
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.any
-import com.nhaarman.mockitokotlin2.eq
-import com.nhaarman.mockitokotlin2.inOrder
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.times
-import com.nhaarman.mockitokotlin2.verify
-import com.nhaarman.mockitokotlin2.whenever
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.inOrder
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextFieldStateTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextFieldStateTest.kt
index fb19cb8..80a615d 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextFieldStateTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextFieldStateTest.kt
@@ -18,7 +18,7 @@
 
 import androidx.compose.runtime.snapshots.Snapshot
 import com.google.common.truth.Truth
-import com.nhaarman.mockitokotlin2.mock
+import org.mockito.kotlin.mock
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextLayoutHelperTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextLayoutHelperTest.kt
index 1d6a86b..4420a2b 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextLayoutHelperTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextLayoutHelperTest.kt
@@ -33,8 +33,8 @@
 import androidx.compose.ui.unit.em
 import androidx.compose.ui.unit.sp
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.whenever
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextSelectionLongPressDragTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextSelectionLongPressDragTest.kt
index 05d810f..832cddb 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextSelectionLongPressDragTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextSelectionLongPressDragTest.kt
@@ -33,12 +33,12 @@
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.LayoutDirection
-import com.nhaarman.mockitokotlin2.doReturn
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.spy
-import com.nhaarman.mockitokotlin2.times
-import com.nhaarman.mockitokotlin2.verify
-import com.nhaarman.mockitokotlin2.whenever
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextStateTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextStateTest.kt
index 236dcab..9fddbbd 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextStateTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/TextStateTest.kt
@@ -18,7 +18,7 @@
 
 import androidx.compose.runtime.snapshots.Snapshot
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.mock
+import org.mockito.kotlin.mock
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionAdjustmentTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionAdjustmentTest.kt
index fa633cf..1c52b7c 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionAdjustmentTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionAdjustmentTest.kt
@@ -29,8 +29,8 @@
 import androidx.compose.ui.util.packInts
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.any
-import com.nhaarman.mockitokotlin2.mock
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionManagerDragTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionManagerDragTest.kt
index dcadda7..6f49d4c 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionManagerDragTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionManagerDragTest.kt
@@ -24,10 +24,10 @@
 import androidx.compose.ui.text.style.ResolvedTextDirection
 import androidx.compose.ui.unit.IntSize
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.spy
-import com.nhaarman.mockitokotlin2.times
-import com.nhaarman.mockitokotlin2.verify
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionManagerTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionManagerTest.kt
index c9f6abe..2e206d7 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionManagerTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionManagerTest.kt
@@ -26,17 +26,17 @@
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.style.ResolvedTextDirection
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.any
-import com.nhaarman.mockitokotlin2.argThat
-import com.nhaarman.mockitokotlin2.doReturn
-import com.nhaarman.mockitokotlin2.eq
-import com.nhaarman.mockitokotlin2.isNull
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.never
-import com.nhaarman.mockitokotlin2.spy
-import com.nhaarman.mockitokotlin2.times
-import com.nhaarman.mockitokotlin2.verify
-import com.nhaarman.mockitokotlin2.whenever
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argThat
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.isNull
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrarImplTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrarImplTest.kt
index 77af370..0c8f63b 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrarImplTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/SelectionRegistrarImplTest.kt
@@ -20,8 +20,8 @@
 
 import androidx.compose.ui.layout.LayoutCoordinates
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.whenever
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
diff --git a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManagerTest.kt b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManagerTest.kt
index 5bdc120..c0a54f5 100644
--- a/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManagerTest.kt
+++ b/compose/foundation/foundation/src/test/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionManagerTest.kt
@@ -45,14 +45,14 @@
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.util.packInts
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.any
-import com.nhaarman.mockitokotlin2.anyOrNull
-import com.nhaarman.mockitokotlin2.isNull
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.spy
-import com.nhaarman.mockitokotlin2.times
-import com.nhaarman.mockitokotlin2.verify
-import com.nhaarman.mockitokotlin2.whenever
+import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.isNull
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
diff --git a/compose/integration-tests/demos/build.gradle b/compose/integration-tests/demos/build.gradle
index d1e40f6..7b6b0ae 100644
--- a/compose/integration-tests/demos/build.gradle
+++ b/compose/integration-tests/demos/build.gradle
@@ -18,6 +18,7 @@
     implementation(libs.kotlinReflect)
     implementation(libs.kotlinStdlib)
 
+    implementation(project(":core:core-ktx"))
     implementation(project(":compose:foundation:foundation"))
     implementation(project(":compose:foundation:foundation-layout"))
     implementation(project(":compose:integration-tests:demos:common"))
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ListSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ListSamples.kt
index 652d8d6..dfe5161 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ListSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ListSamples.kt
@@ -36,6 +36,7 @@
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.unit.dp
 
 @Sampled
@@ -54,6 +55,7 @@
                 )
             },
             modifier = Modifier.toggleable(
+                role = Role.Switch,
                 value = switched,
                 onValueChange = onSwitchedChange
             )
@@ -70,6 +72,7 @@
                 )
             },
             modifier = Modifier.toggleable(
+                role = Role.Checkbox,
                 value = checked,
                 onValueChange = onCheckedChange
             )
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/SelectionControlsSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/SelectionControlsSamples.kt
index 8550650..cdad91a 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/SelectionControlsSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/SelectionControlsSamples.kt
@@ -28,6 +28,8 @@
 import androidx.compose.foundation.selection.selectable
 import androidx.compose.foundation.selection.selectableGroup
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.selection.toggleable
 import androidx.compose.material.Checkbox
 import androidx.compose.material.CheckboxDefaults
 import androidx.compose.material.MaterialTheme
@@ -47,6 +49,7 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalInputModeManager
+import androidx.compose.ui.semantics.role
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.state.ToggleableState
 import androidx.compose.ui.unit.dp
@@ -130,6 +133,22 @@
         checked = checkedState.value,
         onCheckedChange = { checkedState.value = it }
     )
+
+    var pineappleOnPizza by remember { mutableStateOf(true) }
+
+    Row(
+        Modifier
+            .padding(16.dp)
+            .toggleable(
+                role = Role.Switch,
+                value = pineappleOnPizza,
+                onValueChange = { pineappleOnPizza = it },
+            )
+    ) {
+        Switch(checked = pineappleOnPizza, onCheckedChange = null)
+        Spacer(Modifier.width(8.dp))
+        Text("Pineapple on pizza?")
+    }
 }
 
 @Sampled
diff --git a/compose/ui/ui-test/build.gradle b/compose/ui/ui-test/build.gradle
index 84540e9..72e371e 100644
--- a/compose/ui/ui-test/build.gradle
+++ b/compose/ui/ui-test/build.gradle
@@ -62,8 +62,8 @@
         testImplementation(project(":compose:test-utils"))
         testImplementation(libs.truth)
         testImplementation(libs.robolectric)
-        testImplementation(libs.mockitoCore)
-        testImplementation(libs.mockitoKotlin)
+        testImplementation(libs.mockitoCore4)
+        testImplementation(libs.mockitoKotlin4)
 
         androidTestImplementation("androidx.activity:activity-compose:1.3.1")
         androidTestImplementation(project(":compose:material:material"))
@@ -115,8 +115,6 @@
             androidCommonTest.dependencies {
                 implementation(project(":compose:test-utils"))
                 implementation(libs.truth)
-                implementation(libs.mockitoCore)
-                implementation(libs.mockitoKotlin)
             }
 
             // TODO(b/214407011): These dependencies leak into instrumented tests as well. If you
@@ -124,13 +122,16 @@
             //  level dependencies block instead:
             //  `dependencies { testImplementation(libs.robolectric) }`
             androidTest.dependencies {
-                // Empty, but kept for the sake of the comment above
+                implementation(libs.mockitoCore4)
+                implementation(libs.mockitoKotlin4)
             }
 
             androidAndroidTest.dependencies {
                 implementation(project(":compose:material:material"))
                 implementation(project(":compose:ui:ui-test-junit4"))
                 implementation("androidx.activity:activity-compose:1.3.1")
+                implementation(libs.mockitoCore)
+                implementation(libs.mockitoKotlin)
                 implementation(libs.dexmakerMockito)
             }
 
diff --git a/compose/ui/ui-test/src/test/kotlin/androidx/compose/ui/test/inputdispatcher/InputDispatcherTest.kt b/compose/ui/ui-test/src/test/kotlin/androidx/compose/ui/test/inputdispatcher/InputDispatcherTest.kt
index 46e80d4..a86efdb 100644
--- a/compose/ui/ui-test/src/test/kotlin/androidx/compose/ui/test/inputdispatcher/InputDispatcherTest.kt
+++ b/compose/ui/ui-test/src/test/kotlin/androidx/compose/ui/test/inputdispatcher/InputDispatcherTest.kt
@@ -31,9 +31,9 @@
 import androidx.compose.ui.test.util.InputEventRecorder
 import androidx.compose.ui.test.util.assertNoTouchGestureInProgress
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.any
-import com.nhaarman.mockitokotlin2.doReturn
-import com.nhaarman.mockitokotlin2.mock
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
 import org.junit.After
 
 @OptIn(InternalTestApi::class)
diff --git a/compose/ui/ui-text/build.gradle b/compose/ui/ui-text/build.gradle
index 06c673e..49606fa 100644
--- a/compose/ui/ui-text/build.gradle
+++ b/compose/ui/ui-text/build.gradle
@@ -51,11 +51,11 @@
         testImplementation(libs.testRules)
         testImplementation(libs.testRunner)
         testImplementation(libs.junit)
-        testImplementation(libs.mockitoCore)
+        testImplementation(libs.mockitoCore4)
         testImplementation(libs.truth)
         testImplementation(libs.kotlinReflect)
         testImplementation(libs.kotlinTest)
-        testImplementation(libs.mockitoKotlin)
+        testImplementation(libs.mockitoKotlin4)
 
         androidTestImplementation(project(":internal-testutils-fonts"))
         androidTestImplementation(project(":compose:ui:ui-test-junit4"))
@@ -144,11 +144,11 @@
                 implementation(libs.testRules)
                 implementation(libs.testRunner)
                 implementation(libs.junit)
-                implementation(libs.mockitoCore)
+                implementation(libs.mockitoCore4)
                 implementation(libs.truth)
                 implementation(libs.kotlinReflect)
                 implementation(libs.kotlinTest)
-                implementation(libs.mockitoKotlin)
+                implementation(libs.mockitoKotlin4)
             }
 
             androidAndroidTest.dependencies {
diff --git a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/MultiParagraphTest.kt b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/MultiParagraphTest.kt
index bf1b5e1..01b844a 100644
--- a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/MultiParagraphTest.kt
+++ b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/MultiParagraphTest.kt
@@ -17,7 +17,7 @@
 package androidx.compose.ui.text
 
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.mock
+import org.mockito.kotlin.mock
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
diff --git a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextInputServiceTest.kt b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextInputServiceTest.kt
index 10b25c6..998ac9c 100644
--- a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextInputServiceTest.kt
+++ b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/TextInputServiceTest.kt
@@ -24,12 +24,12 @@
 import androidx.compose.ui.text.input.TextFieldValue
 import androidx.compose.ui.text.input.TextInputService
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.any
-import com.nhaarman.mockitokotlin2.eq
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.never
-import com.nhaarman.mockitokotlin2.times
-import com.nhaarman.mockitokotlin2.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
diff --git a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/input/EditProcessorTest.kt b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/input/EditProcessorTest.kt
index 61f0a3b..dc71dd4 100644
--- a/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/input/EditProcessorTest.kt
+++ b/compose/ui/ui-text/src/test/java/androidx/compose/ui/text/input/EditProcessorTest.kt
@@ -19,14 +19,14 @@
 import androidx.compose.ui.text.InternalTextApi
 import androidx.compose.ui.text.TextRange
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.any
-import com.nhaarman.mockitokotlin2.argumentCaptor
-import com.nhaarman.mockitokotlin2.eq
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.never
-import com.nhaarman.mockitokotlin2.reset
-import com.nhaarman.mockitokotlin2.times
-import com.nhaarman.mockitokotlin2.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.reset
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
 import kotlin.test.assertFailsWith
 import org.junit.Test
 import org.junit.runner.RunWith
diff --git a/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/PreviewActivity.kt b/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/PreviewActivity.kt
index ec5b8d9..f83c6ec 100644
--- a/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/PreviewActivity.kt
+++ b/compose/ui/ui-tooling/src/androidMain/kotlin/androidx/compose/ui/tooling/PreviewActivity.kt
@@ -21,6 +21,8 @@
 import android.util.Log
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
 import androidx.compose.material.ExtendedFloatingActionButton
 import androidx.compose.material.Scaffold
 import androidx.compose.material.Text
@@ -28,6 +30,7 @@
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
 
 /**
  * Activity used to run `@Composable` previews from Android Studio.
@@ -107,13 +110,15 @@
                 val index = remember { mutableStateOf(0) }
 
                 Scaffold(
-                    content = {
-                        ComposableInvoker.invokeComposable(
-                            className,
-                            methodName,
-                            currentComposer,
-                            previewParameters[index.value]
-                        )
+                    content = { padding ->
+                        Box(Modifier.padding(padding)) {
+                            ComposableInvoker.invokeComposable(
+                                className,
+                                methodName,
+                                currentComposer,
+                                previewParameters[index.value]
+                            )
+                        }
                     },
                     floatingActionButton = {
                         ExtendedFloatingActionButton(
diff --git a/compose/ui/ui/build.gradle b/compose/ui/ui/build.gradle
index 3935c83..ca9a3c7 100644
--- a/compose/ui/ui/build.gradle
+++ b/compose/ui/ui/build.gradle
@@ -72,8 +72,8 @@
         testImplementation(libs.kotlinCoroutinesTest)
         testImplementation(libs.junit)
         testImplementation(libs.truth)
-        testImplementation(libs.mockitoCore)
-        testImplementation(libs.mockitoKotlin)
+        testImplementation(libs.mockitoCore4)
+        testImplementation(libs.mockitoKotlin4)
         testImplementation(libs.robolectric)
         testImplementation(project(":compose:ui:ui-test-junit4"))
         testImplementation(project(":compose:test-utils"))
@@ -190,8 +190,8 @@
                 implementation(libs.kotlinCoroutinesTest)
                 implementation(libs.junit)
                 implementation(libs.truth)
-                implementation(libs.mockitoCore)
-                implementation(libs.mockitoKotlin)
+                implementation(libs.mockitoCore4)
+                implementation(libs.mockitoKotlin4)
                 implementation(project(":compose:ui:ui-test-junit4"))
                 implementation(project(":internal-testutils-fonts"))
                 implementation(project(":compose:test-utils"))
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
index b614ce0..f9428e5 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
@@ -257,7 +257,9 @@
         // We temporary send Switch role as a separate fake node
         val switchRoleNode = toggleableNode.replacedChildren.last()
         val switchRoleNodeInfo = provider.createAccessibilityNodeInfo(switchRoleNode.id)!!
-        assertEquals("android.widget.Switch", switchRoleNodeInfo.className)
+        assertEquals("android.view.View", switchRoleNodeInfo.className)
+// TODO(aelias)
+        // assertEquals("Switch", switchRoleNodeInfo.roleDescription)
 
         val stateDescription = when {
             Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {
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 77e5d51..d4129d0 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
@@ -361,7 +361,16 @@
             role = Role.Switch
         }
         accessibilityDelegate.populateAccessibilityNodeInfoProperties(1, info, semanticsNode)
-        assertEquals("android.widget.Switch", info.className)
+        assertEquals("android.view.View", info.className)
+    }
+
+    @Test
+    fun testPopulateAccessibilityNodeInfoProperties_switchRoleDescription() {
+        val semanticsNode = createSemanticsNodeWithProperties(1, true) {
+            role = Role.Switch
+        }
+        accessibilityDelegate.populateAccessibilityNodeInfoProperties(1, info, semanticsNode)
+        assertEquals("Switch", info.roleDescription)
     }
 
     @Test
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 e8d1c75..46de247 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
@@ -404,11 +404,12 @@
             if (semanticsNode.isFake || semanticsNode.replacedChildren.isEmpty()) {
                 if (role == Role.Tab) {
                     info.roleDescription = view.context.resources.getString(R.string.tab)
+                } else if (role == Role.Switch) {
+                    info.roleDescription = view.context.resources.getString(R.string.switch_role)
                 } else {
                     val className = when (it) {
                         Role.Button -> "android.widget.Button"
                         Role.Checkbox -> "android.widget.CheckBox"
-                        Role.Switch -> "android.widget.Switch"
                         Role.RadioButton -> "android.widget.RadioButton"
                         Role.Image -> "android.widget.ImageView"
                         else -> null
diff --git a/compose/ui/ui/src/androidMain/res/values/strings.xml b/compose/ui/ui/src/androidMain/res/values/strings.xml
index 3c0c382..8524e9e 100644
--- a/compose/ui/ui/src/androidMain/res/values/strings.xml
+++ b/compose/ui/ui/src/androidMain/res/values/strings.xml
@@ -22,6 +22,8 @@
     <string name="on">On</string>
     <!-- Spoken description of off state of a switch -->
     <string name="off">Off</string>
+    <!-- Spoken description naming a Switch Material UI widget -->
+    <string name="switch_role">Switch</string>
     <!-- Spoken description of selected state of a selectable item -->
     <string name="selected">Selected</string>
     <!-- Spoken description of not selected state of a switch -->
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/hapticfeedback/HapticFeedbackTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/hapticfeedback/HapticFeedbackTest.kt
index 28672b7..60064cf 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/hapticfeedback/HapticFeedbackTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/hapticfeedback/HapticFeedbackTest.kt
@@ -18,10 +18,10 @@
 
 import android.view.HapticFeedbackConstants
 import android.view.View
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.spy
-import com.nhaarman.mockitokotlin2.times
-import com.nhaarman.mockitokotlin2.verify
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/RecordingInputConnectionInactiveTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/RecordingInputConnectionInactiveTest.kt
index 12e99e7..fa9d451 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/RecordingInputConnectionInactiveTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/RecordingInputConnectionInactiveTest.kt
@@ -21,7 +21,7 @@
 import androidx.compose.ui.text.input.RecordingInputConnection
 import androidx.compose.ui.text.input.TextFieldValue
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.mock
+import org.mockito.kotlin.mock
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/RecordingInputConnectionUpdateTextFieldValueTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/RecordingInputConnectionUpdateTextFieldValueTest.kt
index bb39d23..7a9486c 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/RecordingInputConnectionUpdateTextFieldValueTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/RecordingInputConnectionUpdateTextFieldValueTest.kt
@@ -25,13 +25,13 @@
 import androidx.compose.ui.text.input.RecordingInputConnection
 import androidx.compose.ui.text.input.TextFieldValue
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.any
-import com.nhaarman.mockitokotlin2.argumentCaptor
-import com.nhaarman.mockitokotlin2.eq
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.never
-import com.nhaarman.mockitokotlin2.times
-import com.nhaarman.mockitokotlin2.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroidCommandDebouncingTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroidCommandDebouncingTest.kt
index 861b58e..32854e2 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroidCommandDebouncingTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroidCommandDebouncingTest.kt
@@ -20,8 +20,8 @@
 import android.view.View
 import android.view.inputmethod.ExtractedText
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.whenever
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.cancel
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestJavaTest.java
new file mode 100644
index 0000000..db1a2a9
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestJavaTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class CreatePublicKeyCredentialRequestJavaTest {
+
+    @Test
+    public void constructor_emptyJson_throwsIllegalArgumentException() {
+        assertThrows("Expected empty Json to throw error",
+                IllegalArgumentException.class,
+                () -> new CreatePublicKeyCredentialRequest("")
+        );
+    }
+
+    @Test
+    public void constructor_nullJson_throwsNullPointerException() {
+        assertThrows("Expected null Json to throw NPE",
+                NullPointerException.class,
+                () -> new CreatePublicKeyCredentialRequest(null)
+        );
+    }
+
+    @Test
+    public void constructor_success()  {
+        new CreatePublicKeyCredentialRequest(
+                "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}");
+    }
+
+    @Test
+    public void constructor_setsAllowHybridToTrueByDefault()  {
+        CreatePublicKeyCredentialRequest createPublicKeyCredentialRequest =
+                new CreatePublicKeyCredentialRequest(
+                        "JSON");
+        boolean allowHybridActual = createPublicKeyCredentialRequest.allowHybrid();
+        assertThat(allowHybridActual).isTrue();
+    }
+
+    @Test
+    public void constructor_setsAllowHybridToFalse()  {
+        boolean allowHybridExpected = false;
+        CreatePublicKeyCredentialRequest createPublicKeyCredentialRequest =
+                new CreatePublicKeyCredentialRequest("testJson",
+                        allowHybridExpected);
+        boolean allowHybridActual = createPublicKeyCredentialRequest.allowHybrid();
+        assertThat(allowHybridActual).isEqualTo(allowHybridExpected);
+    }
+
+    @Test
+    public void getter_requestJson_success() {
+        String testJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}";
+        CreatePublicKeyCredentialRequest createPublicKeyCredentialReq =
+                new CreatePublicKeyCredentialRequest(testJsonExpected);
+
+        String testJsonActual = createPublicKeyCredentialReq.getRequestJson();
+        assertThat(testJsonActual).isEqualTo(testJsonExpected);
+    }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedFailureInputsJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedFailureInputsJavaTest.java
new file mode 100644
index 0000000..7bf3ba1
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedFailureInputsJavaTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertThrows;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+/**
+ * Combines with {@link CreatePublicKeyCredentialRequestPrivilegedJavaTest} for full tests.
+ */
+@RunWith(Parameterized.class)
+public class CreatePublicKeyCredentialRequestPrivilegedFailureInputsJavaTest {
+    private String mRequestJson;
+    private String mRp;
+    private String mClientDataHash;
+
+    private String mNullRequestJson;
+    private String mNullRp;
+    private String mNullClientDataHash;
+
+    public CreatePublicKeyCredentialRequestPrivilegedFailureInputsJavaTest(String requestJson,
+            String rp, String clientDataHash, String mNullRequestJson, String mNullRp,
+            String mNullClientDataHash) {
+        this.mRequestJson = requestJson;
+        this.mRp = rp;
+        this.mClientDataHash = clientDataHash;
+        this.mNullRequestJson = mNullRequestJson;
+        this.mNullRp = mNullRp;
+        this.mNullClientDataHash = mNullClientDataHash;
+    }
+
+    @Parameterized.Parameters
+    public static Collection failureCases() {
+        // Allows checking improper formations for builder and normal construction.
+        // Handles both null and empty cases.
+        // For successful cases, see the non parameterized tests.
+        return Arrays.asList(new Object[][] {
+                { "{\"hi\":21}", "rp", "", null, "rp", "hash"},
+                { "", "rp", "clientDataHash", "{\"hi\":21}", null, "hash"},
+                { "{\"hi\":21}", "", "clientDataHash", "{\"hi\":21}", "rp", null}
+        });
+    }
+
+    @Test
+    public void constructor_emptyInput_throwsIllegalArgumentException() {
+        assertThrows("If at least one arg empty, should throw IllegalArgumentException",
+                IllegalArgumentException.class,
+                () -> new CreatePublicKeyCredentialRequestPrivileged(this.mRequestJson, this.mRp,
+                        this.mClientDataHash)
+        );
+    }
+
+    @Test
+    public void builder_build_emptyInput_IllegalArgumentException() {
+        CreatePublicKeyCredentialRequestPrivileged.Builder builder =
+                new CreatePublicKeyCredentialRequestPrivileged.Builder(mRequestJson, mRp,
+                        mClientDataHash);
+        assertThrows("If at least one arg empty to builder, should throw "
+                        + "IllegalArgumentException",
+                IllegalArgumentException.class,
+                () -> builder.build()
+        );
+    }
+
+    @Test
+    public void constructor_nullInput_throwsNullPointerException() {
+        assertThrows("If at least one arg null, should throw NullPointerException",
+                NullPointerException.class,
+                () -> new CreatePublicKeyCredentialRequestPrivileged(this.mNullRequestJson,
+                        this.mNullRp,
+                        this.mNullClientDataHash)
+        );
+    }
+
+    @Test
+    public void builder_build_nullInput_throwsNullPointerException() {
+        assertThrows(
+                "If at least one arg null to builder, should throw NullPointerException",
+                NullPointerException.class,
+                () -> new CreatePublicKeyCredentialRequestPrivileged.Builder(mNullRequestJson,
+                                mNullRp, mNullClientDataHash).build()
+        );
+    }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedFailureInputsTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedFailureInputsTest.kt
new file mode 100644
index 0000000..7708b22
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedFailureInputsTest.kt
@@ -0,0 +1,61 @@
+/*
+ * 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
+
+import androidx.testutils.assertThrows
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Combines with [CreatePublicKeyCredentialRequestPrivilegedTest] for full tests.
+ */
+@RunWith(Parameterized::class)
+class CreatePublicKeyCredentialRequestPrivilegedFailureInputsTest(
+    val requestJson: String,
+    val rp: String,
+    val clientDataHash: String
+    ) {
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters
+        fun failureCases(): Collection<Array<Any>> {
+            return listOf(
+                arrayOf("{\"hi\":21}", "rp", ""),
+                arrayOf("{\"hi\":21}", "", "clientDataHash"),
+                arrayOf("", "rp", "clientDataHash")
+            ) // coverage is complete, null is not a problem in Kotlin.
+        }
+    }
+
+    @Test
+    fun constructor_emptyInput_throwsIllegalArgumentException() {
+        assertThrows<IllegalArgumentException> {
+            CreatePublicKeyCredentialRequestPrivileged(requestJson, rp, clientDataHash)
+        }
+    }
+
+    @Test
+    fun builder_build_emptyInput_throwsIllegalArgumentException() {
+        var builder = CreatePublicKeyCredentialRequestPrivileged.Builder(requestJson,
+            rp, clientDataHash)
+        assertThrows<IllegalArgumentException> {
+            builder.build()
+        }
+    }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedJavaTest.java
new file mode 100644
index 0000000..82adc07
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedJavaTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Combines with {@link CreatePublicKeyCredentialRequestPrivilegedFailureInputsJavaTest}
+ * for full tests.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class CreatePublicKeyCredentialRequestPrivilegedJavaTest {
+
+    @Test
+    public void constructor_success() {
+        new CreatePublicKeyCredentialRequestPrivileged(
+                "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}",
+                "RP", "ClientDataHash");
+    }
+
+    @Test
+    public void constructor_setsAllowHybridToTrueByDefault() {
+        CreatePublicKeyCredentialRequestPrivileged createPublicKeyCredentialRequestPrivileged =
+                new CreatePublicKeyCredentialRequestPrivileged(
+                        "JSON", "RP", "HASH");
+        boolean allowHybridActual = createPublicKeyCredentialRequestPrivileged.allowHybrid();
+        assertThat(allowHybridActual).isTrue();
+    }
+
+    @Test
+    public void constructor_setsAllowHybridToFalse() {
+        boolean allowHybridExpected = false;
+        CreatePublicKeyCredentialRequestPrivileged createPublicKeyCredentialRequestPrivileged =
+                new CreatePublicKeyCredentialRequestPrivileged("JSON", "RP", "HASH",
+                        allowHybridExpected);
+        boolean allowHybridActual = createPublicKeyCredentialRequestPrivileged.allowHybrid();
+        assertThat(allowHybridActual).isEqualTo(allowHybridExpected);
+    }
+
+    @Test
+    public void builder_build_defaultAllowHybrid_true() {
+        CreatePublicKeyCredentialRequestPrivileged defaultPrivilegedRequest = new
+                CreatePublicKeyCredentialRequestPrivileged.Builder("{\"Data\":5}",
+                "RP", "HASH").build();
+        assertThat(defaultPrivilegedRequest.allowHybrid()).isTrue();
+    }
+
+    @Test
+    public void builder_build_nonDefaultAllowHybrid_false() {
+        boolean allowHybridExpected = false;
+        CreatePublicKeyCredentialRequestPrivileged createPublicKeyCredentialRequestPrivileged =
+                new CreatePublicKeyCredentialRequestPrivileged.Builder("JSON", "RP", "HASH")
+                        .setAllowHybrid(allowHybridExpected).build();
+        boolean allowHybridActual = createPublicKeyCredentialRequestPrivileged.allowHybrid();
+        assertThat(allowHybridActual).isEqualTo(allowHybridExpected);
+    }
+
+    @Test
+    public void getter_requestJson_success() {
+        String testJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}";
+        CreatePublicKeyCredentialRequestPrivileged createPublicKeyCredentialReqPriv =
+                new CreatePublicKeyCredentialRequestPrivileged(testJsonExpected, "RP", "HASH");
+        String testJsonActual = createPublicKeyCredentialReqPriv.getRequestJson();
+        assertThat(testJsonActual).isEqualTo(testJsonExpected);
+    }
+
+    @Test
+    public void getter_rp_success() {
+        String testRpExpected = "RP";
+        CreatePublicKeyCredentialRequestPrivileged createPublicKeyCredentialRequestPrivileged =
+                new CreatePublicKeyCredentialRequestPrivileged(
+                        "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}",
+                        testRpExpected, "X342%4dfd7&");
+        String testRpActual = createPublicKeyCredentialRequestPrivileged.getRp();
+        assertThat(testRpActual).isEqualTo(testRpExpected);
+    }
+
+    @Test
+    public void getter_clientDataHash_success() {
+        String clientDataHashExpected = "X342%4dfd7&";
+        CreatePublicKeyCredentialRequestPrivileged createPublicKeyCredentialRequestPrivileged =
+                new CreatePublicKeyCredentialRequestPrivileged(
+                        "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}",
+                         "RP", clientDataHashExpected);
+        String clientDataHashActual =
+                createPublicKeyCredentialRequestPrivileged.getClientDataHash();
+        assertThat(clientDataHashActual).isEqualTo(clientDataHashExpected);
+    }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedTest.kt
new file mode 100644
index 0000000..9f127f8
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivilegedTest.kt
@@ -0,0 +1,113 @@
+/*
+ * 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
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Combines with [CreatePublicKeyCredentialRequestPrivilegedFailureInputsTest] for full tests.
+ */
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class CreatePublicKeyCredentialRequestPrivilegedTest {
+
+    @Test
+    fun constructor_success() {
+        CreatePublicKeyCredentialRequestPrivileged(
+            "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}",
+            "RP", "ClientDataHash"
+        )
+    }
+
+    @Test
+    fun constructor_setsAllowHybridToTrueByDefault() {
+        val createPublicKeyCredentialRequestPrivileged = CreatePublicKeyCredentialRequestPrivileged(
+            "JSON", "RP", "HASH"
+        )
+        val allowHybridActual = createPublicKeyCredentialRequestPrivileged.allowHybrid
+        Truth.assertThat(allowHybridActual).isTrue()
+    }
+
+    @Test
+    fun constructor_setsAllowHybridToFalse() {
+        val allowHybridExpected = false
+        val createPublicKeyCredentialRequestPrivileged = CreatePublicKeyCredentialRequestPrivileged(
+            "testJson",
+            "RP", "Hash", allowHybridExpected
+        )
+        val allowHybridActual = createPublicKeyCredentialRequestPrivileged.allowHybrid
+        Truth.assertThat(allowHybridActual).isEqualTo(allowHybridExpected)
+    }
+
+    @Test
+    fun builder_build_defaultAllowHybrid_true() {
+        val defaultPrivilegedRequest = CreatePublicKeyCredentialRequestPrivileged.Builder(
+            "{\"Data\":5}",
+            "RP", "HASH"
+        ).build()
+        Truth.assertThat(defaultPrivilegedRequest.allowHybrid).isTrue()
+    }
+
+    @Test
+    fun builder_build_nonDefaultAllowHybrid_false() {
+        val allowHybridExpected = false
+        val createPublicKeyCredentialRequestPrivileged = CreatePublicKeyCredentialRequestPrivileged
+            .Builder(
+                "testJson",
+                "RP", "Hash"
+            ).setAllowHybrid(allowHybridExpected).build()
+        val allowHybridActual = createPublicKeyCredentialRequestPrivileged.allowHybrid
+        Truth.assertThat(allowHybridActual).isEqualTo(allowHybridExpected)
+    }
+
+    @Test
+    fun getter_requestJson_success() {
+        val testJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}"
+        val createPublicKeyCredentialReqPriv =
+            CreatePublicKeyCredentialRequestPrivileged(testJsonExpected, "RP", "HASH")
+        val testJsonActual = createPublicKeyCredentialReqPriv.requestJson
+        Truth.assertThat(testJsonActual).isEqualTo(testJsonExpected)
+    }
+
+    @Test
+    fun getter_rp_success() {
+        val testRpExpected = "RP"
+        val createPublicKeyCredentialRequestPrivileged =
+            CreatePublicKeyCredentialRequestPrivileged(
+                "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}",
+                testRpExpected, "X342%4dfd7&"
+            )
+        val testRpActual = createPublicKeyCredentialRequestPrivileged.rp
+        Truth.assertThat(testRpActual).isEqualTo(testRpExpected)
+    }
+
+    @Test
+    fun getter_clientDataHash_success() {
+        val clientDataHashExpected = "X342%4dfd7&"
+        val createPublicKeyCredentialRequestPrivileged =
+            CreatePublicKeyCredentialRequestPrivileged(
+                "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}",
+                "RP", clientDataHashExpected
+            )
+        val clientDataHashActual = createPublicKeyCredentialRequestPrivileged.clientDataHash
+        Truth.assertThat(clientDataHashActual).isEqualTo(clientDataHashExpected)
+    }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestTest.kt
new file mode 100644
index 0000000..7cb51a7
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialRequestTest.kt
@@ -0,0 +1,72 @@
+/*
+ * 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
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class CreatePublicKeyCredentialRequestTest {
+
+    @Test
+    fun constructor_emptyJson_throwsIllegalArgumentException() {
+        Assert.assertThrows(
+            "Expected empty Json to throw error",
+            IllegalArgumentException::class.java
+        ) { CreatePublicKeyCredentialRequest("") }
+    }
+
+    @Test
+    fun constructor_success() {
+        CreatePublicKeyCredentialRequest(
+            "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}"
+        )
+    }
+
+    @Test
+    fun constructor_setsAllowHybridToTrueByDefault() {
+        val createPublicKeyCredentialRequest = CreatePublicKeyCredentialRequest(
+            "JSON"
+        )
+        val allowHybridActual = createPublicKeyCredentialRequest.allowHybrid
+        Truth.assertThat(allowHybridActual).isTrue()
+    }
+
+    @Test
+    fun constructor_setsAllowHybridToFalse() {
+        val allowHybridExpected = false
+        val createPublicKeyCredentialRequest = CreatePublicKeyCredentialRequest(
+            "testJson",
+            allowHybridExpected
+        )
+        val allowHybridActual = createPublicKeyCredentialRequest.allowHybrid
+        Truth.assertThat(allowHybridActual).isEqualTo(allowHybridExpected)
+    }
+
+    @Test
+    fun getter_requestJson_success() {
+        val testJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}"
+        val createPublicKeyCredentialReq = CreatePublicKeyCredentialRequest(testJsonExpected)
+        val testJsonActual = createPublicKeyCredentialReq.requestJson
+        Truth.assertThat(testJsonActual).isEqualTo(testJsonExpected)
+    }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialResponseJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialResponseJavaTest.java
new file mode 100644
index 0000000..dc7bdb26
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialResponseJavaTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class CreatePublicKeyCredentialResponseJavaTest {
+
+    @Test
+    public void constructor_emptyJson_throwsIllegalArgumentException() {
+        assertThrows("Expected empty Json to throw error",
+                IllegalArgumentException.class,
+                () -> new CreatePublicKeyCredentialResponse("")
+        );
+    }
+
+    @Test
+    public void constructor_nullJson_throwsNullPointerException() {
+        assertThrows("Expected null Json to throw NullPointerException",
+                NullPointerException.class,
+                () -> new CreatePublicKeyCredentialResponse(null)
+        );
+    }
+
+    @Test
+    public void constructor_success()  {
+        new CreatePublicKeyCredentialResponse("{\"hi\":1}");
+    }
+
+    @Test
+    public void getter_registrationResponseJson_success() {
+        String testJsonExpected = "{\"input\":5}";
+        CreatePublicKeyCredentialResponse createPublicKeyCredentialResponse =
+                new CreatePublicKeyCredentialResponse(testJsonExpected);
+        String testJsonActual = createPublicKeyCredentialResponse.getRegistrationResponseJson();
+        assertThat(testJsonActual).isEqualTo(testJsonExpected);
+    }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialResponseTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialResponseTest.kt
new file mode 100644
index 0000000..fb42fe6
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CreatePublicKeyCredentialResponseTest.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.credentials
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class CreatePublicKeyCredentialResponseTest {
+
+    @Test
+    fun constructor_emptyJson_throwsIllegalArgumentException() {
+        Assert.assertThrows(
+            "Expected empty Json to throw error",
+            IllegalArgumentException::class.java
+        ) { CreatePublicKeyCredentialResponse("") }
+    }
+
+    @Test
+    fun constructor_success() {
+        CreatePublicKeyCredentialResponse("{\"hi\":1}")
+    }
+
+    @Test
+    fun getter_registrationResponseJson_success() {
+        val testJsonExpected = "{\"input\":5}"
+        val createPublicKeyCredentialResponse = CreatePublicKeyCredentialResponse(testJsonExpected)
+        val testJsonActual = createPublicKeyCredentialResponse.registrationResponseJson
+        Truth.assertThat(testJsonActual).isEqualTo(testJsonExpected)
+    }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerTest.kt
index 493cad8..b573302 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerTest.kt
@@ -38,7 +38,7 @@
     }
 
     @Test
-    fun testCreateCredential() = runBlocking<Unit> {
+    fun createCredential() = runBlocking<Unit> {
         assertThrows<UnsupportedOperationException> {
             credentialManager.executeCreateCredential(
                 CreatePasswordRequest("test-user-id", "test-password")
@@ -47,7 +47,7 @@
     }
 
     @Test
-    fun testGetCredential() = runBlocking<Unit> {
+    fun getCredential() = runBlocking<Unit> {
         val request = GetCredentialRequest.Builder()
             .addGetCredentialOption(GetPasswordOption())
             .build()
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionJavaTest.java
new file mode 100644
index 0000000..ec3083c
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionJavaTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import org.junit.Test;
+
+public class GetPublicKeyCredentialOptionJavaTest {
+
+    @Test
+    public void constructor_emptyJson_throwsIllegalArgumentException() {
+        assertThrows("Expected empty Json to throw error",
+                IllegalArgumentException.class,
+                () -> new GetPublicKeyCredentialOption("")
+        );
+    }
+
+    @Test
+    public void constructor_nullJson_throwsNullPointerException() {
+        assertThrows("Expected null Json to throw NPE",
+                NullPointerException.class,
+                () -> new GetPublicKeyCredentialOption(null)
+        );
+    }
+
+    @Test
+    public void constructor_success()  {
+        new GetPublicKeyCredentialOption(
+                        "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}");
+    }
+
+    @Test
+    public void constructor_setsAllowHybridToTrueByDefault() {
+        GetPublicKeyCredentialOption getPublicKeyCredentialOpt =
+                new GetPublicKeyCredentialOption(
+                        "JSON");
+        boolean allowHybridActual = getPublicKeyCredentialOpt.allowHybrid();
+        assertThat(allowHybridActual).isTrue();
+    }
+
+    @Test
+    public void constructor_setsAllowHybridToFalse() {
+        boolean allowHybridExpected = false;
+        GetPublicKeyCredentialOption getPublicKeyCredentialOpt =
+                new GetPublicKeyCredentialOption(
+                        "JSON", allowHybridExpected);
+        boolean allowHybridActual = getPublicKeyCredentialOpt.allowHybrid();
+        assertThat(allowHybridActual).isEqualTo(allowHybridExpected);
+    }
+
+    @Test
+    public void getter_requestJson_success() {
+        String testJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}";
+        GetPublicKeyCredentialOption getPublicKeyCredentialOpt =
+                new GetPublicKeyCredentialOption(testJsonExpected);
+        String testJsonActual = getPublicKeyCredentialOpt.getRequestJson();
+        assertThat(testJsonActual).isEqualTo(testJsonExpected);
+    }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedFailureInputsJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedFailureInputsJavaTest.java
new file mode 100644
index 0000000..9984393
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedFailureInputsJavaTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertThrows;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+/**
+ * Combines with {@link GetPublicKeyCredentialOptionPrivilegedJavaTest} for full tests.
+ */
+@RunWith(Parameterized.class)
+public class GetPublicKeyCredentialOptionPrivilegedFailureInputsJavaTest {
+    private String mRequestJson;
+    private String mRp;
+    private String mClientDataHash;
+
+    private String mNullRequestJson;
+    private String mNullRp;
+    private String mNullClientDataHash;
+
+    public GetPublicKeyCredentialOptionPrivilegedFailureInputsJavaTest(String requestJson,
+            String rp, String clientDataHash, String mNullRequestJson, String mNullRp,
+            String mNullClientDataHash) {
+        this.mRequestJson = requestJson;
+        this.mRp = rp;
+        this.mClientDataHash = clientDataHash;
+        this.mNullRequestJson = mNullRequestJson;
+        this.mNullRp = mNullRp;
+        this.mNullClientDataHash = mNullClientDataHash;
+    }
+
+    @Parameterized.Parameters
+    public static Collection failureCases() {
+        // Allows checking improper formations for builder and normal construction.
+        // Handles both null and empty cases.
+        // For successful cases, see the non parameterized privileged tests.
+        return Arrays.asList(new Object[][] {
+                { "{\"hi\":21}", "rp", "", null, "rp", "hash"},
+                { "", "rp", "clientDataHash", "{\"hi\":21}", null, "hash"},
+                { "{\"hi\":21}", "", "clientDataHash", "{\"hi\":21}", "rp", null}
+        });
+    }
+
+    @Test
+    public void constructor_emptyInput_throwsIllegalArgumentException() {
+        assertThrows("If at least one arg empty, should throw IllegalArgumentException",
+                IllegalArgumentException.class,
+                () -> new GetPublicKeyCredentialOptionPrivileged(this.mRequestJson, this.mRp,
+                        this.mClientDataHash)
+        );
+    }
+
+    @Test
+    public void builder_build_emptyInput_IllegalArgumentException() {
+        GetPublicKeyCredentialOptionPrivileged.Builder builder =
+                new GetPublicKeyCredentialOptionPrivileged.Builder(mRequestJson, mRp,
+                        mClientDataHash);
+        assertThrows("If at least one arg empty to builder, should throw "
+                        + "IllegalArgumentException",
+                IllegalArgumentException.class,
+                () -> builder.build()
+        );
+    }
+
+    @Test
+    public void constructor_nullInput_throwsNullPointerException() {
+        assertThrows(
+                "If at least one arg null, should throw NullPointerException",
+                NullPointerException.class,
+                () -> new GetPublicKeyCredentialOptionPrivileged(this.mNullRequestJson,
+                        this.mNullRp,
+                        this.mNullClientDataHash)
+        );
+    }
+
+    @Test
+    public void builder_build_nullInput_throwsNullPointerException() {
+        assertThrows(
+                "If at least one arg null to builder, should throw NullPointerException",
+                NullPointerException.class,
+                () -> new GetPublicKeyCredentialOptionPrivileged.Builder(mNullRequestJson,
+                        mNullRp, mNullClientDataHash).build()
+        );
+    }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedFailureInputsTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedFailureInputsTest.kt
new file mode 100644
index 0000000..5490b0c
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedFailureInputsTest.kt
@@ -0,0 +1,61 @@
+/*
+ * 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
+
+import androidx.testutils.assertThrows
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Combines with [GetPublicKeyCredentialOptionPrivilegedTest] for full tests.
+ */
+@RunWith(Parameterized::class)
+class GetPublicKeyCredentialOptionPrivilegedFailureInputsTest(
+    val requestJson: String,
+    val rp: String,
+    val clientDataHash: String
+    ) {
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters
+        fun failureCases(): Collection<Array<Any>> {
+            return listOf(
+                arrayOf("{\"hi\":21}", "rp", ""),
+                arrayOf("{\"hi\":21}", "", "clientDataHash"),
+                arrayOf("", "rp", "clientDataHash")
+            ) // coverage is complete
+        }
+    }
+
+    @Test
+    fun constructor_emptyInput_throwsIllegalArgumentException() {
+        assertThrows<IllegalArgumentException> {
+            GetPublicKeyCredentialOptionPrivileged(requestJson, rp, clientDataHash)
+        }
+    }
+
+    @Test
+    fun builder_build_emptyInput_throwsIllegalArgumentException() {
+        var builder = GetPublicKeyCredentialOptionPrivileged.Builder(requestJson,
+            rp, clientDataHash)
+        assertThrows<IllegalArgumentException> {
+            builder.build()
+        }
+    }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedJavaTest.java
new file mode 100644
index 0000000..2b18b0d
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedJavaTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Combines with {@link GetPublicKeyCredentialOptionPrivilegedFailureInputsJavaTest}
+ * for full tests.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class GetPublicKeyCredentialOptionPrivilegedJavaTest {
+
+    @Test
+    public void constructor_success() {
+        new GetPublicKeyCredentialOptionPrivileged(
+                        "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}",
+                        "RP", "ClientDataHash");
+    }
+
+    @Test
+    public void constructor_setsAllowHybridToTrueByDefault() {
+        GetPublicKeyCredentialOptionPrivileged getPublicKeyCredentialOptionPrivileged =
+                new GetPublicKeyCredentialOptionPrivileged(
+                        "JSON", "RP", "HASH");
+        boolean allowHybridActual = getPublicKeyCredentialOptionPrivileged.allowHybrid();
+        assertThat(allowHybridActual).isTrue();
+    }
+
+    @Test
+    public void constructor_setsAllowHybridToFalse() {
+        boolean allowHybridExpected = false;
+        GetPublicKeyCredentialOptionPrivileged getPublicKeyCredentialOptionPrivileged =
+                new GetPublicKeyCredentialOptionPrivileged("testJson",
+                        "RP", "Hash", allowHybridExpected);
+        boolean getAllowHybridActual = getPublicKeyCredentialOptionPrivileged.allowHybrid();
+        assertThat(getAllowHybridActual).isEqualTo(allowHybridExpected);
+    }
+
+    @Test
+    public void builder_build_defaultAllowHybrid_success() {
+        GetPublicKeyCredentialOptionPrivileged defaultPrivilegedRequest = new
+                GetPublicKeyCredentialOptionPrivileged.Builder("{\"Data\":5}",
+                "RP", "HASH").build();
+        assertThat(defaultPrivilegedRequest.allowHybrid()).isTrue();
+    }
+
+    @Test
+    public void builder_build_nonDefaultAllowHybrid_success() {
+        boolean allowHybridExpected = false;
+        GetPublicKeyCredentialOptionPrivileged getPublicKeyCredentialOptionPrivileged =
+                new GetPublicKeyCredentialOptionPrivileged.Builder("testJson",
+                        "RP", "Hash").setAllowHybrid(allowHybridExpected).build();
+        boolean getAllowHybridActual = getPublicKeyCredentialOptionPrivileged.allowHybrid();
+        assertThat(getAllowHybridActual).isEqualTo(allowHybridExpected);
+    }
+
+    @Test
+    public void getter_requestJson_success() {
+        String testJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}";
+        GetPublicKeyCredentialOptionPrivileged getPublicKeyCredentialOptionPrivileged =
+                new GetPublicKeyCredentialOptionPrivileged(testJsonExpected, "RP", "HASH");
+        String testJsonActual = getPublicKeyCredentialOptionPrivileged.getRequestJson();
+        assertThat(testJsonActual).isEqualTo(testJsonExpected);
+    }
+
+    @Test
+    public void getter_rp_success() {
+        String testRpExpected = "RP";
+        GetPublicKeyCredentialOptionPrivileged getPublicKeyCredentialOptionPrivileged =
+                new GetPublicKeyCredentialOptionPrivileged(
+                        "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}",
+                        testRpExpected, "X342%4dfd7&");
+        String testRpActual = getPublicKeyCredentialOptionPrivileged.getRp();
+        assertThat(testRpActual).isEqualTo(testRpExpected);
+    }
+
+    @Test
+    public void getter_clientDataHash_success() {
+        String clientDataHashExpected = "X342%4dfd7&";
+        GetPublicKeyCredentialOptionPrivileged getPublicKeyCredentialOptionPrivileged =
+                new GetPublicKeyCredentialOptionPrivileged(
+                        "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}",
+                        "RP", clientDataHashExpected);
+        String clientDataHashActual = getPublicKeyCredentialOptionPrivileged.getClientDataHash();
+        assertThat(clientDataHashActual).isEqualTo(clientDataHashExpected);
+    }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedTest.kt
new file mode 100644
index 0000000..6c10626
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionPrivilegedTest.kt
@@ -0,0 +1,110 @@
+/*
+ * 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
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Combines with [GetPublicKeyCredentialOptionPrivilegedFailureInputsTest] for full tests.
+ */
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class GetPublicKeyCredentialOptionPrivilegedTest {
+
+    @Test
+    fun constructor_success() {
+        GetPublicKeyCredentialOptionPrivileged(
+            "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}",
+            "RP", "ClientDataHash"
+        )
+    }
+
+    @Test
+    fun constructor_setsAllowHybridToTrueByDefault() {
+        val getPublicKeyCredentialOptionPrivileged = GetPublicKeyCredentialOptionPrivileged(
+            "JSON", "RP", "HASH"
+        )
+        val allowHybridActual = getPublicKeyCredentialOptionPrivileged.allowHybrid
+        Truth.assertThat(allowHybridActual).isTrue()
+    }
+
+    @Test
+    fun constructor_setsAllowHybridFalse() {
+        val allowHybridExpected = false
+        val getPublicKeyCredentialOptPriv = GetPublicKeyCredentialOptionPrivileged(
+            "JSON", "RP", "HASH", allowHybridExpected
+        )
+        val getAllowHybridActual = getPublicKeyCredentialOptPriv.allowHybrid
+        Truth.assertThat(getAllowHybridActual).isEqualTo(allowHybridExpected)
+    }
+
+    @Test
+    fun builder_build_nonDefaultAllowHybrid_false() {
+        val allowHybridExpected = false
+        val getPublicKeyCredentialOptionPrivileged = GetPublicKeyCredentialOptionPrivileged
+            .Builder(
+                "testJson",
+                "RP", "Hash",
+            ).setAllowHybrid(allowHybridExpected).build()
+        val getAllowHybridActual = getPublicKeyCredentialOptionPrivileged.allowHybrid
+        Truth.assertThat(getAllowHybridActual).isEqualTo(allowHybridExpected)
+    }
+
+    @Test
+    fun builder_build_defaultAllowHybrid_true() {
+        val defaultPrivilegedRequest = GetPublicKeyCredentialOptionPrivileged.Builder(
+            "{\"Data\":5}",
+            "RP", "HASH"
+        ).build()
+        Truth.assertThat(defaultPrivilegedRequest.allowHybrid).isTrue()
+    }
+
+    @Test
+    fun getter_requestJson_success() {
+        val testJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}"
+        val getPublicKeyCredentialOptionPrivileged =
+            GetPublicKeyCredentialOptionPrivileged(testJsonExpected, "RP", "HASH")
+        val testJsonActual = getPublicKeyCredentialOptionPrivileged.requestJson
+        Truth.assertThat(testJsonActual).isEqualTo(testJsonExpected)
+    }
+
+    @Test
+    fun getter_rp_success() {
+        val testRpExpected = "RP"
+        val getPublicKeyCredentialOptionPrivileged = GetPublicKeyCredentialOptionPrivileged(
+            "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}",
+            testRpExpected, "X342%4dfd7&"
+        )
+        val testRpActual = getPublicKeyCredentialOptionPrivileged.rp
+        Truth.assertThat(testRpActual).isEqualTo(testRpExpected)
+    }
+
+    @Test
+    fun getter_clientDataHash_success() {
+        val clientDataHashExpected = "X342%4dfd7&"
+        val getPublicKeyCredentialOptionPrivileged = GetPublicKeyCredentialOptionPrivileged(
+            "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}",
+            "RP", clientDataHashExpected
+        )
+        val clientDataHashActual = getPublicKeyCredentialOptionPrivileged.clientDataHash
+        Truth.assertThat(clientDataHashActual).isEqualTo(clientDataHashExpected)
+    }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionTest.kt
new file mode 100644
index 0000000..e9cfa7b
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionTest.kt
@@ -0,0 +1,71 @@
+/*
+ * 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
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class GetPublicKeyCredentialOptionTest {
+
+    @Test
+    fun constructor_emptyJson_throwsIllegalArgumentException() {
+        Assert.assertThrows(
+            "Expected empty Json to throw error",
+            IllegalArgumentException::class.java
+        ) { GetPublicKeyCredentialOption("") }
+    }
+
+    @Test
+    fun constructor_success() {
+        GetPublicKeyCredentialOption(
+            "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}"
+        )
+    }
+
+    @Test
+    fun constructor_setsAllowHybridToTrueByDefault() {
+        val getPublicKeyCredentialOpt = GetPublicKeyCredentialOption(
+            "JSON"
+        )
+        val allowHybridActual = getPublicKeyCredentialOpt.allowHybrid
+        Truth.assertThat(allowHybridActual).isTrue()
+    }
+
+    @Test
+    fun constructor_setsAllowHybridFalse() {
+        val allowHybridExpected = false
+        val getPublicKeyCredentialOpt = GetPublicKeyCredentialOption(
+            "JSON", allowHybridExpected
+        )
+        val allowHybridActual = getPublicKeyCredentialOpt.allowHybrid
+        Truth.assertThat(allowHybridActual).isEqualTo(allowHybridExpected)
+    }
+
+    @Test
+    fun getter_requestJson_success() {
+        val testJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}"
+        val createPublicKeyCredentialReq = GetPublicKeyCredentialOption(testJsonExpected)
+        val testJsonActual = createPublicKeyCredentialReq.requestJson
+        Truth.assertThat(testJsonActual).isEqualTo(testJsonExpected)
+    }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialJavaTest.java
new file mode 100644
index 0000000..57d09d4
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialJavaTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Note that "PublicKeyCredential" and "Passkey" are used interchangeably.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class PublicKeyCredentialJavaTest {
+
+    @Test
+    public void constructor_emptyJson_throwsIllegalArgumentException() {
+        assertThrows("Expected empty Json to throw IllegalArgumentException",
+                IllegalArgumentException.class,
+                () -> new PublicKeyCredential("")
+        );
+    }
+
+    @Test
+    public void constructor_nullJson_throwsNullPointerException() {
+        assertThrows("Expected null Json to throw NullPointerException",
+                NullPointerException.class,
+                () -> new PublicKeyCredential(null)
+        );
+    }
+
+    @Test
+    public void constructor_success() {
+        new PublicKeyCredential(
+                "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}");
+    }
+
+    @Test
+    public void getter_authJson_success() {
+        String testJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}";
+        PublicKeyCredential publicKeyCredential = new PublicKeyCredential(testJsonExpected);
+        String testJsonActual = publicKeyCredential.getAuthenticationResponseJson();
+        assertThat(testJsonActual).isEqualTo(testJsonExpected);
+    }
+}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialTest.kt
new file mode 100644
index 0000000..2c3882f
--- /dev/null
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/PublicKeyCredentialTest.kt
@@ -0,0 +1,55 @@
+/*
+ * 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
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth
+import org.junit.Assert
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Note that "PublicKeyCredential" and "Passkey" are used interchangeably.
+ */
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class PublicKeyCredentialTest {
+
+    @Test
+    fun constructor_emptyJson_throwsIllegalArgumentException() {
+        Assert.assertThrows(
+            "Expected empty Json to throw IllegalArgumentException",
+            IllegalArgumentException::class.java
+        ) { PublicKeyCredential("") }
+    }
+
+    @Test
+    fun constructor_success() {
+        PublicKeyCredential(
+            "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}"
+        )
+    }
+
+    @Test
+    fun getter_authJson_success() {
+        val testJsonExpected = "{\"hi\":{\"there\":{\"lol\":\"Value\"}}}"
+        val publicKeyCredential = PublicKeyCredential(testJsonExpected)
+        val testJsonActual = publicKeyCredential.authenticationResponseJson
+        Truth.assertThat(testJsonActual).isEqualTo(testJsonExpected)
+    }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialBaseRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialBaseRequest.kt
new file mode 100644
index 0000000..e78b5ac
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialBaseRequest.kt
@@ -0,0 +1,39 @@
+/*
+ * 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
+
+/**
+ * Base request class for registering a public key credential.
+ *
+ * An application can construct a subtype request and call [CredentialManager.executeCreateCredential] to
+ * launch framework UI flows to collect consent and any other metadata needed from the user to
+ * register a new user credential.
+ *
+ * @property requestJson The request in JSON format
+ * @throws NullPointerException If [requestJson] is null. This is handled by the Kotlin runtime
+ * @throws IllegalArgumentException If [requestJson] is empty
+ *
+ * @hide
+ */
+abstract class CreatePublicKeyCredentialBaseRequest constructor(
+    val requestJson: String
+) : CreateCredentialRequest() {
+
+    init {
+        require(requestJson.isNotEmpty()) { "request json must not be empty" }
+    }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequest.kt b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequest.kt
new file mode 100644
index 0000000..d0b2d24
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequest.kt
@@ -0,0 +1,35 @@
+/*
+ * 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
+
+/**
+ * A request to register a passkey from the user's public key credential provider.
+ *
+ * @property requestJson the request in JSON format
+ * @property allowHybrid defines whether hybrid credentials are allowed to fulfill this request,
+ * true by default
+ * @throws NullPointerException If [requestJson] or [allowHybrid] is null. This is handled by the
+ * Kotlin runtime
+ * @throws IllegalArgumentException If [requestJson] is empty
+ *
+ * @hide
+ */
+class CreatePublicKeyCredentialRequest @JvmOverloads constructor(
+    requestJson: String,
+    @get:JvmName("allowHybrid")
+    val allowHybrid: Boolean = true
+) : CreatePublicKeyCredentialBaseRequest(requestJson)
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivileged.kt b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivileged.kt
new file mode 100644
index 0000000..23abbfb
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialRequestPrivileged.kt
@@ -0,0 +1,91 @@
+/*
+ * 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
+
+/**
+ * A privileged request to register a passkey from the user’s public key credential provider, where
+ * the caller can modify the rp. Only callers with privileged permission, e.g. user’s default
+ * brower, caBLE, can use this.
+ *
+ * @property requestJson the privileged request in JSON format
+ * @property allowHybrid defines whether hybrid credentials are allowed to fulfill this request,
+ * true by default
+ * @property rp the expected true RP ID which will override the one in the [requestJson]
+ * @property clientDataHash a hash that is used to verify the [rp] Identity
+ * @throws NullPointerException If any of [allowHybrid], [requestJson], [rp], or [clientDataHash] is
+ * null. This is handled by the Kotlin runtime
+ * @throws IllegalArgumentException If any of [requestJson], [rp], or [clientDataHash] is empty
+ *
+ * @hide
+ */
+class CreatePublicKeyCredentialRequestPrivileged @JvmOverloads constructor(
+    requestJson: String,
+    val rp: String,
+    val clientDataHash: String,
+    @get:JvmName("allowHybrid")
+    val allowHybrid: Boolean = true
+) : CreatePublicKeyCredentialBaseRequest(requestJson) {
+
+    init {
+        require(rp.isNotEmpty()) { "rp must not be empty" }
+        require(clientDataHash.isNotEmpty()) { "clientDataHash must not be empty" }
+    }
+
+    /** A builder for [CreatePublicKeyCredentialRequestPrivileged]. */
+    class Builder(var requestJson: String, var rp: String, var clientDataHash: String) {
+
+        private var allowHybrid: Boolean = true
+
+        /**
+         * Sets the privileged request in JSON format.
+         */
+        fun setRequestJson(requestJson: String): Builder {
+            this.requestJson = requestJson
+            return this
+        }
+
+        /**
+         * Sets whether hybrid credentials are allowed to fulfill this request, true by default.
+         */
+        fun setAllowHybrid(allowHybrid: Boolean): Builder {
+            this.allowHybrid = allowHybrid
+            return this
+        }
+
+        /**
+         * Sets the expected true RP ID which will override the one in the [requestJson].
+         */
+        fun setRp(rp: String): Builder {
+            this.rp = rp
+            return this
+        }
+
+        /**
+         * Sets a hash that is used to verify the [rp] Identity.
+         */
+        fun setClientDataHash(clientDataHash: String): Builder {
+            this.clientDataHash = clientDataHash
+            return this
+        }
+
+        /** Builds a [CreatePublicKeyCredentialRequestPrivileged]. */
+        fun build(): CreatePublicKeyCredentialRequestPrivileged {
+            return CreatePublicKeyCredentialRequestPrivileged(this.requestJson,
+                this.rp, this.clientDataHash, this.allowHybrid)
+        }
+    }
+}
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialResponse.kt b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialResponse.kt
new file mode 100644
index 0000000..f4cf4b9
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/CreatePublicKeyCredentialResponse.kt
@@ -0,0 +1,37 @@
+/*
+ * 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
+
+/**
+ * A response of a public key credential (passkey) flow.
+ *
+ * @property registrationResponseJson the public key credential registration response in JSON format
+ * @throws NullPointerException If [registrationResponseJson] is null. This is handled by the Kotlin
+ * runtime
+ * @throws IllegalArgumentException If [registrationResponseJson] is blank
+ *
+ * @hide
+ */
+class CreatePublicKeyCredentialResponse(
+    val registrationResponseJson: String
+) : CreateCredentialResponse() {
+
+    init {
+        require(registrationResponseJson.isNotEmpty()) { "registrationResponseJson must not be " +
+            "empty" }
+    }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialBaseOption.kt b/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialBaseOption.kt
new file mode 100644
index 0000000..d42c089
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialBaseOption.kt
@@ -0,0 +1,36 @@
+/*
+ * 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
+
+/**
+ * Base request class for getting a registered public key credential.
+ *
+ * @property requestJson the request in JSON format
+ * @throws NullPointerException If [requestJson] is null - auto handled by the
+ * Kotlin runtime
+ * @throws IllegalArgumentException If [requestJson] is empty
+ *
+ * @hide
+ */
+abstract class GetPublicKeyCredentialBaseOption constructor(
+    val requestJson: String
+) : GetCredentialOption() {
+
+    init {
+        require(requestJson.isNotEmpty()) { "request json must not be empty" }
+    }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOption.kt b/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOption.kt
new file mode 100644
index 0000000..d372ffa
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOption.kt
@@ -0,0 +1,33 @@
+package androidx.credentials/*
+ * 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.
+ */
+
+/**
+ * A request to get passkeys from the user's public key credential provider.
+ *
+ * @property requestJson the request in JSON format
+ * @property allowHybrid defines whether hybrid credentials are allowed to fulfill this request,
+ * true by default
+ * @throws NullPointerException If [requestJson] or [allowHybrid] is null. It is handled by the
+ * Kotlin runtime
+ * @throws IllegalArgumentException If [requestJson] is empty
+ *
+ * @hide
+ */
+class GetPublicKeyCredentialOption @JvmOverloads constructor(
+    requestJson: String,
+    @get:JvmName("allowHybrid")
+    val allowHybrid: Boolean = true,
+) : GetPublicKeyCredentialBaseOption(requestJson)
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOptionPrivileged.kt b/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOptionPrivileged.kt
new file mode 100644
index 0000000..a8fc6a2
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/GetPublicKeyCredentialOptionPrivileged.kt
@@ -0,0 +1,91 @@
+/*
+ * 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
+
+/**
+ * A privileged request to get passkeys from the user's public key credential provider. The caller
+ * can modify the RP. Only callers with privileged permission (e.g. user's public browser or caBLE)
+ * can use this.
+ *
+ * @property requestJson the privileged request in JSON format
+ * @property allowHybrid defines whether hybrid credentials are allowed to fulfill this request,
+ * true by default
+ * @property rp the expected true RP ID which will override the one in the [requestJson]
+ * @property clientDataHash a hash that is used to verify the [rp] Identity
+ * @throws NullPointerException If any of [allowHybrid], [requestJson], [rp], or [clientDataHash]
+ * is null. This is handled by the Kotlin runtime
+ * @throws IllegalArgumentException If any of [requestJson], [rp], or [clientDataHash] is empty
+ *
+ * @hide
+ */
+class GetPublicKeyCredentialOptionPrivileged @JvmOverloads constructor(
+    requestJson: String,
+    val rp: String,
+    val clientDataHash: String,
+    @get:JvmName("allowHybrid")
+    val allowHybrid: Boolean = true
+) : GetPublicKeyCredentialBaseOption(requestJson) {
+
+    init {
+        require(rp.isNotEmpty()) { "rp must not be empty" }
+        require(clientDataHash.isNotEmpty()) { "clientDataHash must not be empty" }
+    }
+
+    /** A builder for [GetPublicKeyCredentialOptionPrivileged]. */
+    class Builder(var requestJson: String, var rp: String, var clientDataHash: String) {
+
+        private var allowHybrid: Boolean = true
+
+        /**
+         * Sets the privileged request in JSON format.
+         */
+        fun setRequestJson(requestJson: String): Builder {
+            this.requestJson = requestJson
+            return this
+        }
+
+        /**
+         * Sets whether hybrid credentials are allowed to fulfill this request, true by default.
+         */
+        fun setAllowHybrid(allowHybrid: Boolean): Builder {
+            this.allowHybrid = allowHybrid
+            return this
+        }
+
+        /**
+         * Sets the expected true RP ID which will override the one in the [requestJson].
+         */
+        fun setRp(rp: String): Builder {
+            this.rp = rp
+            return this
+        }
+
+        /**
+         * Sets a hash that is used to verify the [rp] Identity.
+         */
+        fun setClientDataHash(clientDataHash: String): Builder {
+            this.clientDataHash = clientDataHash
+            return this
+        }
+
+        /** Builds a [GetPublicKeyCredentialOptionPrivileged]. */
+        fun build(): GetPublicKeyCredentialOptionPrivileged {
+            return GetPublicKeyCredentialOptionPrivileged(this.requestJson,
+                this.rp, this.clientDataHash, this.allowHybrid)
+        }
+    }
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/PublicKeyCredential.kt b/credentials/credentials/src/main/java/androidx/credentials/PublicKeyCredential.kt
new file mode 100644
index 0000000..16ecae6
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/PublicKeyCredential.kt
@@ -0,0 +1,39 @@
+/*
+ * 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
+
+/**
+ * Represents the user's passkey credential granted by the user for app sign-in.
+ *
+ * @property authenticationResponseJson the public key credential authentication response in
+ * JSON format that follows the standard webauthn json format shown at
+ * [this w3c link](https://w3c.github.io/webauthn/#dictdef-authenticationresponsejson)
+ * @throws NullPointerException If [authenticationResponseJson] is null. This is handled by the
+ * kotlin runtime
+ * @throws IllegalArgumentException If [authenticationResponseJson] is empty
+ *
+ * @hide
+ */
+class PublicKeyCredential constructor(
+    val authenticationResponseJson: String
+) : Credential() {
+
+    init {
+        require(authenticationResponseJson.isNotEmpty()) {
+            "authentication response JSON must not be empty" }
+    }
+}
\ No newline at end of file
diff --git a/development/update-verification-metadata.sh b/development/update-verification-metadata.sh
index e47d227..8a08119 100755
--- a/development/update-verification-metadata.sh
+++ b/development/update-verification-metadata.sh
@@ -13,11 +13,11 @@
 }
 
 # This script regenerates signature-related information (dependency-verification-metadata and keyring)
-function regenerateTrustedKeys() {
-  echo "regenerating list of trusted keys"
+function regenerateVerificationMetadata() {
+  echo "regenerating verification metadata and keyring"
   # regenerate metadata
   # Need to run a clean build, https://github.com/gradle/gradle/issues/19228
-  runGradle --write-verification-metadata pgp,sha256 --dry-run --clean bOS
+  runGradle --write-verification-metadata pgp,sha256 --export-keys --dry-run --clean bOS
   # extract and keep only the <trusted-keys> section
   WORK_DIR=gradle/update-keys-temp
   rm -rf "$WORK_DIR"
@@ -38,21 +38,9 @@
   grep -B 10000 "<trusted-keys>" gradle/verification-metadata.xml > "$WORK_DIR/old.head"
   grep -A 10000 "</trusted-keys>" gradle/verification-metadata.xml > "$WORK_DIR/old.tail"
 
-  # update file
+  # update verification metadata file
   cat "$WORK_DIR/old.head" "$WORK_DIR/new.middle" "$WORK_DIR/old.tail" > gradle/verification-metadata.xml
 
-  # remove temporary files
-  rm -rf "$WORK_DIR"
-  rm -rf gradle/verification-metadata.dryrun.xml
-}
-regenerateTrustedKeys
-
-# updates the keyring, including sorting entries and removing duplicates
-function regenerateKeyring() {
-  # a separate step from regenerating the verification metadata, https://github.com/gradle/gradle/issues/20138
-  echo "regenerating keyring"
-  runGradle --write-verification-metadata sha256 --export-keys --dry-run bOS
-
   echo "sorting keyring and removing duplicates"
   # sort and unique the keyring
   # https://github.com/gradle/gradle/issues/20140
@@ -72,12 +60,13 @@
     | sed 's/NEWLINE/\n/g' \
     > gradle/verification-keyring.keys
 
-  # remove unused files
+  # remove temporary files
+  rm -rf "$WORK_DIR"
   rm -f gradle/verification-keyring-dryrun.gpg
   rm -f gradle/verification-keyring-dryrun.keys
   rm -f gradle/verification-metadata.dryrun.xml
 }
-regenerateKeyring
+regenerateVerificationMetadata
 
 echo
 echo "Done. Please check that these changes look correct ('git diff')"
diff --git a/docs-tip-of-tree/build.gradle b/docs-tip-of-tree/build.gradle
index 6550261..e07ebcb 100644
--- a/docs-tip-of-tree/build.gradle
+++ b/docs-tip-of-tree/build.gradle
@@ -337,6 +337,7 @@
     docs(project(":wear:watchface:watchface-style"))
     docs(project(":webkit:webkit"))
     docs(project(":window:window"))
+    docs(project(":window:window-core"))
     docs(project(":window:window-java"))
     docs(project(":window:window-rxjava2"))
     docs(project(":window:window-rxjava3"))
diff --git a/drawerlayout/drawerlayout/src/androidTest/java/androidx/drawerlayout/widget/DrawerBackHandlingTest.kt b/drawerlayout/drawerlayout/src/androidTest/java/androidx/drawerlayout/widget/DrawerBackHandlingTest.kt
index 4a24651..6e49b6c 100644
--- a/drawerlayout/drawerlayout/src/androidTest/java/androidx/drawerlayout/widget/DrawerBackHandlingTest.kt
+++ b/drawerlayout/drawerlayout/src/androidTest/java/androidx/drawerlayout/widget/DrawerBackHandlingTest.kt
@@ -28,6 +28,7 @@
 import androidx.testutils.PollingCheck
 import androidx.testutils.withActivity
 import org.junit.Assert
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -39,6 +40,7 @@
         DrawerSingleStartActivity::class.java
     )
 
+    @Ignore
     @Test
     @SmallTest
     public fun testBackPress() {
@@ -57,15 +59,6 @@
         }
         listener.reset()
 
-        // Make sure we have focus, which in turn ensures we receive key events.
-        activityScenarioRule.withActivity {
-            drawerLayout.requestFocus()
-        }
-
-        PollingCheck.waitFor {
-            drawerLayout.hasFocus()
-        }
-
         // Ensure that back pressed dispatcher callback is registered on T+.
         if (Build.VERSION.SDK_INT >= 33) {
             Assert.assertTrue(drawerLayout.isBackInvokedCallbackRegistered)
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentManagerTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentManagerTest.kt
index 95b10aa..a062eea 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentManagerTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentManagerTest.kt
@@ -146,48 +146,6 @@
     }
 
     @Test
-    fun findFragmentWithoutChildFragment() {
-       withUse(ActivityScenario.launch(FragmentTestActivity::class.java)) {
-            val fm = withActivity {
-                setContentView(R.layout.simple_container)
-                supportFragmentManager
-            }
-            val outerFragment = StrictViewFragment(R.layout.scene1)
-            val innerFragment = StrictViewFragment(R.layout.fragment_a)
-
-            fm.beginTransaction()
-                .add(R.id.fragmentContainer, outerFragment)
-                .setReorderingAllowed(false)
-                .commit()
-            // Here we add childFragment to a layout within parentFragment, but we
-            // specifically don't use parentFragment.childFragmentManager
-            fm.beginTransaction()
-                .add(R.id.squareContainer, innerFragment)
-                .setReorderingAllowed(false)
-                .commit()
-            executePendingTransactions()
-
-            val outerRootView = outerFragment.requireView()
-            val innerRootView = innerFragment.requireView()
-            assertThat(FragmentManager.findFragment<Fragment>(outerRootView))
-                .isEqualTo(outerFragment)
-            assertThat(FragmentManager.findFragment<Fragment>(innerRootView))
-                .isEqualTo(innerFragment)
-
-            fm.beginTransaction()
-                .remove(outerFragment)
-                .commit()
-            executePendingTransactions()
-
-            // Check that even after removal, findFragment still returns the right Fragment
-            assertThat(FragmentManager.findFragment<Fragment>(outerRootView))
-                .isEqualTo(outerFragment)
-            assertThat(FragmentManager.findFragment<Fragment>(innerRootView))
-                .isEqualTo(innerFragment)
-        }
-    }
-
-    @Test
     fun findFragmentManagerChildFragment() {
        withUse(ActivityScenario.launch(FragmentTestActivity::class.java)) {
             val fm = withActivity {
@@ -248,60 +206,6 @@
     }
 
     @Test
-    fun findFragmentManagerWithoutChildFragment() {
-       withUse(ActivityScenario.launch(FragmentTestActivity::class.java)) {
-            val fm = withActivity {
-                setContentView(R.layout.simple_container)
-                supportFragmentManager
-            }
-            val outerFragment = StrictViewFragment(R.layout.scene1)
-            val innerFragment = StrictViewFragment(R.layout.fragment_a)
-
-            fm.beginTransaction()
-                .add(R.id.fragmentContainer, outerFragment)
-                .setReorderingAllowed(false)
-                .commit()
-            // Here we add childFragment to a layout within parentFragment, but we
-            // specifically don't use parentFragment.childFragmentManager
-            fm.beginTransaction()
-                .add(R.id.squareContainer, innerFragment)
-                .setReorderingAllowed(false)
-                .commit()
-            executePendingTransactions()
-
-            val outerChildFragmentManager = outerFragment.childFragmentManager
-            val outerRootView = outerFragment.requireView()
-            val innerChildFragmentManager = innerFragment.childFragmentManager
-            val innerRootView = innerFragment.requireView()
-            assertThat(FragmentManager.findFragmentManager(outerRootView))
-                .isEqualTo(outerChildFragmentManager)
-            assertThat(FragmentManager.findFragmentManager(innerRootView))
-                .isEqualTo(innerChildFragmentManager)
-
-            fm.beginTransaction()
-                .remove(outerFragment)
-                .commit()
-            executePendingTransactions()
-
-            try {
-                FragmentManager.findFragmentManager(outerRootView)
-                fail("findFragmentManager on the removed outerRootView should throw")
-            } catch (expected: IllegalStateException) {
-                assertThat(expected).hasMessageThat()
-                    .isEqualTo(
-                        "The Fragment $outerFragment that owns View " +
-                            "$outerRootView has already been destroyed. Nested fragments " +
-                            "should always use the child FragmentManager."
-                    )
-            }
-            // The inner Fragment is still added, so it should still return its
-            // childFragmentManager, despite its View being detached
-            assertThat(FragmentManager.findFragmentManager(innerRootView))
-                .isEqualTo(innerChildFragmentManager)
-        }
-    }
-
-    @Test
     fun addRemoveReorderingAllowedWithoutExecutePendingTransactions() {
        withUse(ActivityScenario.launch(FragmentTestActivity::class.java)) {
             val fm = withActivity {
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTest.kt
index d127d7c..c5a62e7 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTest.kt
@@ -50,12 +50,10 @@
     @get:Rule
     var activityRule = androidx.test.rule.ActivityTestRule(FragmentTestActivity::class.java)
 
-    private lateinit var activity: FragmentTestActivity
     private lateinit var instrumentation: Instrumentation
 
     @Before
     fun setup() {
-        activity = activityRule.activity
         instrumentation = InstrumentationRegistry.getInstrumentation()
     }
 
@@ -63,6 +61,7 @@
     @UiThreadTest
     @Test
     fun testRequireView() {
+        val activity = activityRule.activity
         val fragment1 = StrictViewFragment()
         activity.supportFragmentManager.beginTransaction().add(R.id.content, fragment1).commitNow()
         assertThat(fragment1.requireView()).isNotNull()
@@ -72,6 +71,7 @@
     @UiThreadTest
     @Test(expected = IllegalStateException::class)
     fun testRequireViewWithoutView() {
+        val activity = activityRule.activity
         val fragment1 = StrictFragment()
         activity.supportFragmentManager.beginTransaction().add(fragment1, "fragment").commitNow()
         fragment1.requireView()
@@ -81,6 +81,7 @@
     @UiThreadTest
     @Test
     fun testOnCreateOrder() {
+        val activity = activityRule.activity
         val fragment1 = OrderFragment()
         val fragment2 = OrderFragment()
         activity.supportFragmentManager.beginTransaction()
@@ -95,6 +96,7 @@
     @Test
     @SdkSuppress(minSdkVersion = 16) // waitForHalfFadeIn requires API 16
     fun testChildFragmentManagerGone() {
+        val activity = activityRule.activity
         val fragmentA = FragmentA()
         val fragmentB = FragmentB()
         activityRule.runOnUiThread {
@@ -139,6 +141,7 @@
     @Test
     @SdkSuppress(minSdkVersion = 16) // waitForHalfFadeIn requires API 16
     fun testRemoveUnrelatedDuringAnimation() {
+        val activity = activityRule.activity
         val unrelatedFragment = StrictFragment()
         val fragmentA = FragmentA()
         val fragmentB = FragmentB()
@@ -225,6 +228,7 @@
     @UiThreadTest
     @Test
     fun testViewOrder() {
+        val activity = activityRule.activity
         val fragmentA = FragmentA()
         val fragmentB = FragmentB()
         val fragmentC = FragmentC()
@@ -242,6 +246,7 @@
     @UiThreadTest
     @Test
     fun testRequireParentFragment() {
+        val activity = activityRule.activity
         val parentFragment = StrictFragment()
         activity.supportFragmentManager.beginTransaction().add(parentFragment, "parent").commitNow()
 
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTransactionTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTransactionTest.kt
index 95b9313..e779648 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTransactionTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTransactionTest.kt
@@ -52,27 +52,30 @@
     @get:Rule
     var activityRule = androidx.test.rule.ActivityTestRule(FragmentTestActivity::class.java)
 
-    private lateinit var activity: FragmentTestActivity
     private var onBackStackChangedTimes: Int = 0
     private lateinit var onBackStackChangedListener: FragmentManager.OnBackStackChangedListener
 
     @Before
     fun setUp() {
-        activity = activityRule.activity
         onBackStackChangedTimes = 0
         onBackStackChangedListener =
             FragmentManager.OnBackStackChangedListener { onBackStackChangedTimes++ }
-        activity.supportFragmentManager.addOnBackStackChangedListener(onBackStackChangedListener)
+        activityRule.activity.supportFragmentManager.addOnBackStackChangedListener(
+            onBackStackChangedListener
+        )
     }
 
     @After
     fun tearDown() {
-        activity.supportFragmentManager.removeOnBackStackChangedListener(onBackStackChangedListener)
+        activityRule.activity.supportFragmentManager.removeOnBackStackChangedListener(
+            onBackStackChangedListener
+        )
     }
 
     @Test
     @UiThreadTest
     fun testAddTransactionWithValidFragment() {
+        val activity = activityRule.activity
         val fragment = CorrectFragment()
         activity.supportFragmentManager.beginTransaction()
             .add(R.id.content, fragment)
@@ -86,6 +89,7 @@
     @Test
     @UiThreadTest
     fun testAddTransactionByClassName() {
+        val activity = activityRule.activity
         val args = Bundle()
         activity.supportFragmentManager.beginTransaction()
             .add(R.id.content, CorrectFragment::class.java, args)
@@ -103,6 +107,7 @@
     @Test
     @UiThreadTest
     fun testAddTransactionWithPrivateFragment() {
+        val activity = activityRule.activity
         val fragment = PrivateFragment()
         try {
             activity.supportFragmentManager.beginTransaction()
@@ -126,6 +131,7 @@
     @Test
     @UiThreadTest
     fun testAddTransactionWithPackagePrivateFragment() {
+        val activity = activityRule.activity
         val fragment = OuterPackagePrivateFragment.PackagePrivateFragment()
         try {
             activity.supportFragmentManager.beginTransaction()
@@ -148,6 +154,7 @@
     @Test
     @UiThreadTest
     fun testAddTransactionWithAnonymousFragment() {
+        val activity = activityRule.activity
         val fragment = object : Fragment() {}
         try {
             activity.supportFragmentManager.beginTransaction()
@@ -169,6 +176,7 @@
     @Test
     @UiThreadTest
     fun testGetLayoutInflater() {
+        val activity = activityRule.activity
         val fragment1 = OnGetLayoutInflaterFragment()
         assertThat(fragment1.onGetLayoutInflaterCalls).isEqualTo(0)
         activity.supportFragmentManager.beginTransaction()
@@ -222,6 +230,7 @@
     @Test
     @UiThreadTest
     fun testAddTransactionWithNonStaticFragment() {
+        val activity = activityRule.activity
         val fragment = NonStaticFragment()
         try {
             activity.supportFragmentManager.beginTransaction()
@@ -243,6 +252,7 @@
     @Test
     @UiThreadTest
     fun testReplaceTransactionByClassName() {
+        val activity = activityRule.activity
         val firstFragment = StrictFragment()
         activity.supportFragmentManager.beginTransaction()
             .add(R.id.content, firstFragment)
@@ -295,6 +305,7 @@
     // Ensure that getFragments() works during transactions, even if it is run off thread
     @Test
     fun getFragmentsOffThread() {
+        val activity = activityRule.activity
         val fm = activity.supportFragmentManager
 
         // Make sure that adding a fragment works
@@ -384,6 +395,7 @@
      */
     @Test
     fun commitAllowStateLossDetached() {
+        val activity = activityRule.activity
         val fragment1 = CorrectFragment()
         activity.supportFragmentManager
             .beginTransaction()
@@ -424,6 +436,7 @@
      */
     @Test
     fun newIntentUnlocks() {
+        val activity = activityRule.activity
         val instrumentation = InstrumentationRegistry.getInstrumentation()
         val intent1 = Intent(activity, NewIntentActivity::class.java)
             .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
@@ -453,6 +466,7 @@
      */
     @Test
     fun newIntentProviderUnlocks() {
+        val activity = activityRule.activity
         val instrumentation = InstrumentationRegistry.getInstrumentation()
         val intent1 = Intent(activity, NewIntentProviderActivity::class.java)
             .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
@@ -482,7 +496,7 @@
 
         do {
             assertThat(SystemClock.uptimeMillis() < endTime).isTrue()
-        } while (activity.supportFragmentManager.fragments.size != expectedSize)
+        } while (activityRule.activity.supportFragmentManager.fragments.size != expectedSize)
     }
 
     class CorrectFragment : Fragment()
diff --git a/glance/glance-appwidget/build.gradle b/glance/glance-appwidget/build.gradle
index 6b1a161..6896b81 100644
--- a/glance/glance-appwidget/build.gradle
+++ b/glance/glance-appwidget/build.gradle
@@ -59,8 +59,8 @@
     testImplementation(libs.junit)
     testImplementation(libs.kotlinCoroutinesTest)
     testImplementation(libs.kotlinTest)
-    testImplementation(libs.mockitoCore)
-    testImplementation(libs.mockitoKotlin)
+    testImplementation(libs.mockitoCore4)
+    testImplementation(libs.mockitoKotlin4)
     testImplementation(libs.robolectric)
     testImplementation(libs.testCore)
     testImplementation(libs.testRules)
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/template/GlanceTemplateAppWidget.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/template/GlanceTemplateAppWidget.kt
index fa89286..9762c26 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/template/GlanceTemplateAppWidget.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/template/GlanceTemplateAppWidget.kt
@@ -23,7 +23,7 @@
 import androidx.glance.LocalSize
 import androidx.glance.appwidget.GlanceAppWidget
 import androidx.glance.appwidget.SizeMode
-import androidx.glance.color.dynamicThemeColorProviders
+import androidx.glance.color.DynamicThemeColorProviders
 import androidx.glance.state.GlanceStateDefinition
 import androidx.glance.state.PreferencesGlanceStateDefinition
 import androidx.glance.template.LocalTemplateColors
@@ -45,7 +45,7 @@
     final override fun Content() {
         // TODO: Add other local values
         val mode = mode()
-        val colors = dynamicThemeColorProviders()
+        val colors = DynamicThemeColorProviders
         CompositionLocalProvider(
             LocalTemplateMode provides mode,
             LocalTemplateColors provides colors
diff --git a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/GlanceAppWidgetTest.kt b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/GlanceAppWidgetTest.kt
index a44968f..3979e10 100644
--- a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/GlanceAppWidgetTest.kt
+++ b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/GlanceAppWidgetTest.kt
@@ -36,7 +36,7 @@
 import androidx.glance.text.Text
 import androidx.test.core.app.ApplicationProvider
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.mock
+import org.mockito.kotlin.mock
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.test.TestScope
diff --git a/glance/glance-wear-tiles/build.gradle b/glance/glance-wear-tiles/build.gradle
index c5964c5..cd8d37f 100644
--- a/glance/glance-wear-tiles/build.gradle
+++ b/glance/glance-wear-tiles/build.gradle
@@ -50,8 +50,8 @@
     testImplementation(libs.kotlinCoroutinesTest)
     testImplementation(libs.robolectric)
     testImplementation(libs.kotlinReflect)
-    testImplementation(libs.mockitoCore)
-    testImplementation(libs.mockitoKotlin)
+    testImplementation(libs.mockitoCore4)
+    testImplementation(libs.mockitoKotlin4)
     testImplementation("androidx.core:core-ktx:1.7.0")
     testImplementation("androidx.wear.tiles:tiles-testing:1.0.0")
 
diff --git a/glance/glance-wear-tiles/src/test/kotlin/androidx/glance/wear/tiles/BorderTest.kt b/glance/glance-wear-tiles/src/test/kotlin/androidx/glance/wear/tiles/BorderTest.kt
index f251633..b38fd4e 100644
--- a/glance/glance-wear-tiles/src/test/kotlin/androidx/glance/wear/tiles/BorderTest.kt
+++ b/glance/glance-wear-tiles/src/test/kotlin/androidx/glance/wear/tiles/BorderTest.kt
@@ -24,8 +24,8 @@
 import androidx.glance.findModifier
 import androidx.glance.unit.ColorProvider
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.doReturn
-import com.nhaarman.mockitokotlin2.mock
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
 import org.junit.Test
 
 class BorderTest {
diff --git a/glance/glance/api/current.txt b/glance/glance/api/current.txt
index 01c8711..ec2705e 100644
--- a/glance/glance/api/current.txt
+++ b/glance/glance/api/current.txt
@@ -156,34 +156,33 @@
 
 package androidx.glance.color {
 
-  public final class ColorProviders {
-    ctor public ColorProviders(androidx.glance.unit.ColorProvider primary, androidx.glance.unit.ColorProvider onPrimary, androidx.glance.unit.ColorProvider primaryContainer, androidx.glance.unit.ColorProvider onPrimaryContainer, androidx.glance.unit.ColorProvider secondary, androidx.glance.unit.ColorProvider onSecondary, androidx.glance.unit.ColorProvider secondaryContainer, androidx.glance.unit.ColorProvider onSecondaryContainer, androidx.glance.unit.ColorProvider tertiary, androidx.glance.unit.ColorProvider onTertiary, androidx.glance.unit.ColorProvider tertiaryContainer, androidx.glance.unit.ColorProvider onTertiaryContainer, androidx.glance.unit.ColorProvider error, androidx.glance.unit.ColorProvider errorContainer, androidx.glance.unit.ColorProvider onError, androidx.glance.unit.ColorProvider onErrorContainer, androidx.glance.unit.ColorProvider background, androidx.glance.unit.ColorProvider onBackground, androidx.glance.unit.ColorProvider surface, androidx.glance.unit.ColorProvider onSurface, androidx.glance.unit.ColorProvider surfaceVariant, androidx.glance.unit.ColorProvider onSurfaceVariant, androidx.glance.unit.ColorProvider outline, androidx.glance.unit.ColorProvider inverseOnSurface, androidx.glance.unit.ColorProvider inverseSurface, androidx.glance.unit.ColorProvider inversePrimary);
-    method public androidx.glance.unit.ColorProvider getBackground();
-    method public androidx.glance.unit.ColorProvider getError();
-    method public androidx.glance.unit.ColorProvider getErrorContainer();
-    method public androidx.glance.unit.ColorProvider getInverseOnSurface();
-    method public androidx.glance.unit.ColorProvider getInversePrimary();
-    method public androidx.glance.unit.ColorProvider getInverseSurface();
-    method public androidx.glance.unit.ColorProvider getOnBackground();
-    method public androidx.glance.unit.ColorProvider getOnError();
-    method public androidx.glance.unit.ColorProvider getOnErrorContainer();
-    method public androidx.glance.unit.ColorProvider getOnPrimary();
-    method public androidx.glance.unit.ColorProvider getOnPrimaryContainer();
-    method public androidx.glance.unit.ColorProvider getOnSecondary();
-    method public androidx.glance.unit.ColorProvider getOnSecondaryContainer();
-    method public androidx.glance.unit.ColorProvider getOnSurface();
-    method public androidx.glance.unit.ColorProvider getOnSurfaceVariant();
-    method public androidx.glance.unit.ColorProvider getOnTertiary();
-    method public androidx.glance.unit.ColorProvider getOnTertiaryContainer();
-    method public androidx.glance.unit.ColorProvider getOutline();
-    method public androidx.glance.unit.ColorProvider getPrimary();
-    method public androidx.glance.unit.ColorProvider getPrimaryContainer();
-    method public androidx.glance.unit.ColorProvider getSecondary();
-    method public androidx.glance.unit.ColorProvider getSecondaryContainer();
-    method public androidx.glance.unit.ColorProvider getSurface();
-    method public androidx.glance.unit.ColorProvider getSurfaceVariant();
-    method public androidx.glance.unit.ColorProvider getTertiary();
-    method public androidx.glance.unit.ColorProvider getTertiaryContainer();
+  public abstract sealed class ColorProviders {
+    method public final androidx.glance.unit.ColorProvider getBackground();
+    method public final androidx.glance.unit.ColorProvider getError();
+    method public final androidx.glance.unit.ColorProvider getErrorContainer();
+    method public final androidx.glance.unit.ColorProvider getInverseOnSurface();
+    method public final androidx.glance.unit.ColorProvider getInversePrimary();
+    method public final androidx.glance.unit.ColorProvider getInverseSurface();
+    method public final androidx.glance.unit.ColorProvider getOnBackground();
+    method public final androidx.glance.unit.ColorProvider getOnError();
+    method public final androidx.glance.unit.ColorProvider getOnErrorContainer();
+    method public final androidx.glance.unit.ColorProvider getOnPrimary();
+    method public final androidx.glance.unit.ColorProvider getOnPrimaryContainer();
+    method public final androidx.glance.unit.ColorProvider getOnSecondary();
+    method public final androidx.glance.unit.ColorProvider getOnSecondaryContainer();
+    method public final androidx.glance.unit.ColorProvider getOnSurface();
+    method public final androidx.glance.unit.ColorProvider getOnSurfaceVariant();
+    method public final androidx.glance.unit.ColorProvider getOnTertiary();
+    method public final androidx.glance.unit.ColorProvider getOnTertiaryContainer();
+    method public final androidx.glance.unit.ColorProvider getOutline();
+    method public final androidx.glance.unit.ColorProvider getPrimary();
+    method public final androidx.glance.unit.ColorProvider getPrimaryContainer();
+    method public final androidx.glance.unit.ColorProvider getSecondary();
+    method public final androidx.glance.unit.ColorProvider getSecondaryContainer();
+    method public final androidx.glance.unit.ColorProvider getSurface();
+    method public final androidx.glance.unit.ColorProvider getSurfaceVariant();
+    method public final androidx.glance.unit.ColorProvider getTertiary();
+    method public final androidx.glance.unit.ColorProvider getTertiaryContainer();
     property public final androidx.glance.unit.ColorProvider background;
     property public final androidx.glance.unit.ColorProvider error;
     property public final androidx.glance.unit.ColorProvider errorContainer;
@@ -213,6 +212,7 @@
   }
 
   public final class ColorProvidersKt {
+    method public static androidx.glance.color.ColorProviders colorProviders(androidx.glance.unit.ColorProvider primary, androidx.glance.unit.ColorProvider onPrimary, androidx.glance.unit.ColorProvider primaryContainer, androidx.glance.unit.ColorProvider onPrimaryContainer, androidx.glance.unit.ColorProvider secondary, androidx.glance.unit.ColorProvider onSecondary, androidx.glance.unit.ColorProvider secondaryContainer, androidx.glance.unit.ColorProvider onSecondaryContainer, androidx.glance.unit.ColorProvider tertiary, androidx.glance.unit.ColorProvider onTertiary, androidx.glance.unit.ColorProvider tertiaryContainer, androidx.glance.unit.ColorProvider onTertiaryContainer, androidx.glance.unit.ColorProvider error, androidx.glance.unit.ColorProvider errorContainer, androidx.glance.unit.ColorProvider onError, androidx.glance.unit.ColorProvider onErrorContainer, androidx.glance.unit.ColorProvider background, androidx.glance.unit.ColorProvider onBackground, androidx.glance.unit.ColorProvider surface, androidx.glance.unit.ColorProvider onSurface, androidx.glance.unit.ColorProvider surfaceVariant, androidx.glance.unit.ColorProvider onSurfaceVariant, androidx.glance.unit.ColorProvider outline, androidx.glance.unit.ColorProvider inverseOnSurface, androidx.glance.unit.ColorProvider inverseSurface, androidx.glance.unit.ColorProvider inversePrimary);
   }
 
 }
diff --git a/glance/glance/api/public_plus_experimental_current.txt b/glance/glance/api/public_plus_experimental_current.txt
index 01c8711..ec2705e 100644
--- a/glance/glance/api/public_plus_experimental_current.txt
+++ b/glance/glance/api/public_plus_experimental_current.txt
@@ -156,34 +156,33 @@
 
 package androidx.glance.color {
 
-  public final class ColorProviders {
-    ctor public ColorProviders(androidx.glance.unit.ColorProvider primary, androidx.glance.unit.ColorProvider onPrimary, androidx.glance.unit.ColorProvider primaryContainer, androidx.glance.unit.ColorProvider onPrimaryContainer, androidx.glance.unit.ColorProvider secondary, androidx.glance.unit.ColorProvider onSecondary, androidx.glance.unit.ColorProvider secondaryContainer, androidx.glance.unit.ColorProvider onSecondaryContainer, androidx.glance.unit.ColorProvider tertiary, androidx.glance.unit.ColorProvider onTertiary, androidx.glance.unit.ColorProvider tertiaryContainer, androidx.glance.unit.ColorProvider onTertiaryContainer, androidx.glance.unit.ColorProvider error, androidx.glance.unit.ColorProvider errorContainer, androidx.glance.unit.ColorProvider onError, androidx.glance.unit.ColorProvider onErrorContainer, androidx.glance.unit.ColorProvider background, androidx.glance.unit.ColorProvider onBackground, androidx.glance.unit.ColorProvider surface, androidx.glance.unit.ColorProvider onSurface, androidx.glance.unit.ColorProvider surfaceVariant, androidx.glance.unit.ColorProvider onSurfaceVariant, androidx.glance.unit.ColorProvider outline, androidx.glance.unit.ColorProvider inverseOnSurface, androidx.glance.unit.ColorProvider inverseSurface, androidx.glance.unit.ColorProvider inversePrimary);
-    method public androidx.glance.unit.ColorProvider getBackground();
-    method public androidx.glance.unit.ColorProvider getError();
-    method public androidx.glance.unit.ColorProvider getErrorContainer();
-    method public androidx.glance.unit.ColorProvider getInverseOnSurface();
-    method public androidx.glance.unit.ColorProvider getInversePrimary();
-    method public androidx.glance.unit.ColorProvider getInverseSurface();
-    method public androidx.glance.unit.ColorProvider getOnBackground();
-    method public androidx.glance.unit.ColorProvider getOnError();
-    method public androidx.glance.unit.ColorProvider getOnErrorContainer();
-    method public androidx.glance.unit.ColorProvider getOnPrimary();
-    method public androidx.glance.unit.ColorProvider getOnPrimaryContainer();
-    method public androidx.glance.unit.ColorProvider getOnSecondary();
-    method public androidx.glance.unit.ColorProvider getOnSecondaryContainer();
-    method public androidx.glance.unit.ColorProvider getOnSurface();
-    method public androidx.glance.unit.ColorProvider getOnSurfaceVariant();
-    method public androidx.glance.unit.ColorProvider getOnTertiary();
-    method public androidx.glance.unit.ColorProvider getOnTertiaryContainer();
-    method public androidx.glance.unit.ColorProvider getOutline();
-    method public androidx.glance.unit.ColorProvider getPrimary();
-    method public androidx.glance.unit.ColorProvider getPrimaryContainer();
-    method public androidx.glance.unit.ColorProvider getSecondary();
-    method public androidx.glance.unit.ColorProvider getSecondaryContainer();
-    method public androidx.glance.unit.ColorProvider getSurface();
-    method public androidx.glance.unit.ColorProvider getSurfaceVariant();
-    method public androidx.glance.unit.ColorProvider getTertiary();
-    method public androidx.glance.unit.ColorProvider getTertiaryContainer();
+  public abstract sealed class ColorProviders {
+    method public final androidx.glance.unit.ColorProvider getBackground();
+    method public final androidx.glance.unit.ColorProvider getError();
+    method public final androidx.glance.unit.ColorProvider getErrorContainer();
+    method public final androidx.glance.unit.ColorProvider getInverseOnSurface();
+    method public final androidx.glance.unit.ColorProvider getInversePrimary();
+    method public final androidx.glance.unit.ColorProvider getInverseSurface();
+    method public final androidx.glance.unit.ColorProvider getOnBackground();
+    method public final androidx.glance.unit.ColorProvider getOnError();
+    method public final androidx.glance.unit.ColorProvider getOnErrorContainer();
+    method public final androidx.glance.unit.ColorProvider getOnPrimary();
+    method public final androidx.glance.unit.ColorProvider getOnPrimaryContainer();
+    method public final androidx.glance.unit.ColorProvider getOnSecondary();
+    method public final androidx.glance.unit.ColorProvider getOnSecondaryContainer();
+    method public final androidx.glance.unit.ColorProvider getOnSurface();
+    method public final androidx.glance.unit.ColorProvider getOnSurfaceVariant();
+    method public final androidx.glance.unit.ColorProvider getOnTertiary();
+    method public final androidx.glance.unit.ColorProvider getOnTertiaryContainer();
+    method public final androidx.glance.unit.ColorProvider getOutline();
+    method public final androidx.glance.unit.ColorProvider getPrimary();
+    method public final androidx.glance.unit.ColorProvider getPrimaryContainer();
+    method public final androidx.glance.unit.ColorProvider getSecondary();
+    method public final androidx.glance.unit.ColorProvider getSecondaryContainer();
+    method public final androidx.glance.unit.ColorProvider getSurface();
+    method public final androidx.glance.unit.ColorProvider getSurfaceVariant();
+    method public final androidx.glance.unit.ColorProvider getTertiary();
+    method public final androidx.glance.unit.ColorProvider getTertiaryContainer();
     property public final androidx.glance.unit.ColorProvider background;
     property public final androidx.glance.unit.ColorProvider error;
     property public final androidx.glance.unit.ColorProvider errorContainer;
@@ -213,6 +212,7 @@
   }
 
   public final class ColorProvidersKt {
+    method public static androidx.glance.color.ColorProviders colorProviders(androidx.glance.unit.ColorProvider primary, androidx.glance.unit.ColorProvider onPrimary, androidx.glance.unit.ColorProvider primaryContainer, androidx.glance.unit.ColorProvider onPrimaryContainer, androidx.glance.unit.ColorProvider secondary, androidx.glance.unit.ColorProvider onSecondary, androidx.glance.unit.ColorProvider secondaryContainer, androidx.glance.unit.ColorProvider onSecondaryContainer, androidx.glance.unit.ColorProvider tertiary, androidx.glance.unit.ColorProvider onTertiary, androidx.glance.unit.ColorProvider tertiaryContainer, androidx.glance.unit.ColorProvider onTertiaryContainer, androidx.glance.unit.ColorProvider error, androidx.glance.unit.ColorProvider errorContainer, androidx.glance.unit.ColorProvider onError, androidx.glance.unit.ColorProvider onErrorContainer, androidx.glance.unit.ColorProvider background, androidx.glance.unit.ColorProvider onBackground, androidx.glance.unit.ColorProvider surface, androidx.glance.unit.ColorProvider onSurface, androidx.glance.unit.ColorProvider surfaceVariant, androidx.glance.unit.ColorProvider onSurfaceVariant, androidx.glance.unit.ColorProvider outline, androidx.glance.unit.ColorProvider inverseOnSurface, androidx.glance.unit.ColorProvider inverseSurface, androidx.glance.unit.ColorProvider inversePrimary);
   }
 
 }
diff --git a/glance/glance/api/restricted_current.txt b/glance/glance/api/restricted_current.txt
index 01c8711..ec2705e 100644
--- a/glance/glance/api/restricted_current.txt
+++ b/glance/glance/api/restricted_current.txt
@@ -156,34 +156,33 @@
 
 package androidx.glance.color {
 
-  public final class ColorProviders {
-    ctor public ColorProviders(androidx.glance.unit.ColorProvider primary, androidx.glance.unit.ColorProvider onPrimary, androidx.glance.unit.ColorProvider primaryContainer, androidx.glance.unit.ColorProvider onPrimaryContainer, androidx.glance.unit.ColorProvider secondary, androidx.glance.unit.ColorProvider onSecondary, androidx.glance.unit.ColorProvider secondaryContainer, androidx.glance.unit.ColorProvider onSecondaryContainer, androidx.glance.unit.ColorProvider tertiary, androidx.glance.unit.ColorProvider onTertiary, androidx.glance.unit.ColorProvider tertiaryContainer, androidx.glance.unit.ColorProvider onTertiaryContainer, androidx.glance.unit.ColorProvider error, androidx.glance.unit.ColorProvider errorContainer, androidx.glance.unit.ColorProvider onError, androidx.glance.unit.ColorProvider onErrorContainer, androidx.glance.unit.ColorProvider background, androidx.glance.unit.ColorProvider onBackground, androidx.glance.unit.ColorProvider surface, androidx.glance.unit.ColorProvider onSurface, androidx.glance.unit.ColorProvider surfaceVariant, androidx.glance.unit.ColorProvider onSurfaceVariant, androidx.glance.unit.ColorProvider outline, androidx.glance.unit.ColorProvider inverseOnSurface, androidx.glance.unit.ColorProvider inverseSurface, androidx.glance.unit.ColorProvider inversePrimary);
-    method public androidx.glance.unit.ColorProvider getBackground();
-    method public androidx.glance.unit.ColorProvider getError();
-    method public androidx.glance.unit.ColorProvider getErrorContainer();
-    method public androidx.glance.unit.ColorProvider getInverseOnSurface();
-    method public androidx.glance.unit.ColorProvider getInversePrimary();
-    method public androidx.glance.unit.ColorProvider getInverseSurface();
-    method public androidx.glance.unit.ColorProvider getOnBackground();
-    method public androidx.glance.unit.ColorProvider getOnError();
-    method public androidx.glance.unit.ColorProvider getOnErrorContainer();
-    method public androidx.glance.unit.ColorProvider getOnPrimary();
-    method public androidx.glance.unit.ColorProvider getOnPrimaryContainer();
-    method public androidx.glance.unit.ColorProvider getOnSecondary();
-    method public androidx.glance.unit.ColorProvider getOnSecondaryContainer();
-    method public androidx.glance.unit.ColorProvider getOnSurface();
-    method public androidx.glance.unit.ColorProvider getOnSurfaceVariant();
-    method public androidx.glance.unit.ColorProvider getOnTertiary();
-    method public androidx.glance.unit.ColorProvider getOnTertiaryContainer();
-    method public androidx.glance.unit.ColorProvider getOutline();
-    method public androidx.glance.unit.ColorProvider getPrimary();
-    method public androidx.glance.unit.ColorProvider getPrimaryContainer();
-    method public androidx.glance.unit.ColorProvider getSecondary();
-    method public androidx.glance.unit.ColorProvider getSecondaryContainer();
-    method public androidx.glance.unit.ColorProvider getSurface();
-    method public androidx.glance.unit.ColorProvider getSurfaceVariant();
-    method public androidx.glance.unit.ColorProvider getTertiary();
-    method public androidx.glance.unit.ColorProvider getTertiaryContainer();
+  public abstract sealed class ColorProviders {
+    method public final androidx.glance.unit.ColorProvider getBackground();
+    method public final androidx.glance.unit.ColorProvider getError();
+    method public final androidx.glance.unit.ColorProvider getErrorContainer();
+    method public final androidx.glance.unit.ColorProvider getInverseOnSurface();
+    method public final androidx.glance.unit.ColorProvider getInversePrimary();
+    method public final androidx.glance.unit.ColorProvider getInverseSurface();
+    method public final androidx.glance.unit.ColorProvider getOnBackground();
+    method public final androidx.glance.unit.ColorProvider getOnError();
+    method public final androidx.glance.unit.ColorProvider getOnErrorContainer();
+    method public final androidx.glance.unit.ColorProvider getOnPrimary();
+    method public final androidx.glance.unit.ColorProvider getOnPrimaryContainer();
+    method public final androidx.glance.unit.ColorProvider getOnSecondary();
+    method public final androidx.glance.unit.ColorProvider getOnSecondaryContainer();
+    method public final androidx.glance.unit.ColorProvider getOnSurface();
+    method public final androidx.glance.unit.ColorProvider getOnSurfaceVariant();
+    method public final androidx.glance.unit.ColorProvider getOnTertiary();
+    method public final androidx.glance.unit.ColorProvider getOnTertiaryContainer();
+    method public final androidx.glance.unit.ColorProvider getOutline();
+    method public final androidx.glance.unit.ColorProvider getPrimary();
+    method public final androidx.glance.unit.ColorProvider getPrimaryContainer();
+    method public final androidx.glance.unit.ColorProvider getSecondary();
+    method public final androidx.glance.unit.ColorProvider getSecondaryContainer();
+    method public final androidx.glance.unit.ColorProvider getSurface();
+    method public final androidx.glance.unit.ColorProvider getSurfaceVariant();
+    method public final androidx.glance.unit.ColorProvider getTertiary();
+    method public final androidx.glance.unit.ColorProvider getTertiaryContainer();
     property public final androidx.glance.unit.ColorProvider background;
     property public final androidx.glance.unit.ColorProvider error;
     property public final androidx.glance.unit.ColorProvider errorContainer;
@@ -213,6 +212,7 @@
   }
 
   public final class ColorProvidersKt {
+    method public static androidx.glance.color.ColorProviders colorProviders(androidx.glance.unit.ColorProvider primary, androidx.glance.unit.ColorProvider onPrimary, androidx.glance.unit.ColorProvider primaryContainer, androidx.glance.unit.ColorProvider onPrimaryContainer, androidx.glance.unit.ColorProvider secondary, androidx.glance.unit.ColorProvider onSecondary, androidx.glance.unit.ColorProvider secondaryContainer, androidx.glance.unit.ColorProvider onSecondaryContainer, androidx.glance.unit.ColorProvider tertiary, androidx.glance.unit.ColorProvider onTertiary, androidx.glance.unit.ColorProvider tertiaryContainer, androidx.glance.unit.ColorProvider onTertiaryContainer, androidx.glance.unit.ColorProvider error, androidx.glance.unit.ColorProvider errorContainer, androidx.glance.unit.ColorProvider onError, androidx.glance.unit.ColorProvider onErrorContainer, androidx.glance.unit.ColorProvider background, androidx.glance.unit.ColorProvider onBackground, androidx.glance.unit.ColorProvider surface, androidx.glance.unit.ColorProvider onSurface, androidx.glance.unit.ColorProvider surfaceVariant, androidx.glance.unit.ColorProvider onSurfaceVariant, androidx.glance.unit.ColorProvider outline, androidx.glance.unit.ColorProvider inverseOnSurface, androidx.glance.unit.ColorProvider inverseSurface, androidx.glance.unit.ColorProvider inversePrimary);
   }
 
 }
diff --git a/glance/glance/build.gradle b/glance/glance/build.gradle
index 182bfb3..4fb08c4 100644
--- a/glance/glance/build.gradle
+++ b/glance/glance/build.gradle
@@ -50,8 +50,8 @@
     testImplementation(libs.kotlinCoroutinesTest)
     testImplementation(libs.kotlinTest)
     testImplementation(libs.kotlinReflect)
-    testImplementation(libs.mockitoCore)
-    testImplementation(libs.mockitoKotlin)
+    testImplementation(libs.mockitoCore4)
+    testImplementation(libs.mockitoKotlin4)
     testImplementation("androidx.datastore:datastore-core:1.0.0")
     testImplementation("androidx.datastore:datastore-preferences-core:1.0.0")
     testImplementation("androidx.datastore:datastore-preferences:1.0.0-rc02")
diff --git a/glance/glance/src/androidMain/kotlin/androidx/glance/color/ColorProviders.kt b/glance/glance/src/androidMain/kotlin/androidx/glance/color/ColorProviders.kt
index 46ba370..73815bc 100644
--- a/glance/glance/src/androidMain/kotlin/androidx/glance/color/ColorProviders.kt
+++ b/glance/glance/src/androidMain/kotlin/androidx/glance/color/ColorProviders.kt
@@ -24,7 +24,7 @@
  * Holds a set of Glance specific [ColorProvider] that can be used to represent a Material 3 color
  * scheme.
  */
-class ColorProviders(
+sealed class ColorProviders(
     val primary: ColorProvider,
     val onPrimary: ColorProvider,
     val primaryContainer: ColorProvider,
@@ -51,7 +51,7 @@
     val inverseOnSurface: ColorProvider,
     val inverseSurface: ColorProvider,
     val inversePrimary: ColorProvider
-    ) {
+) {
 
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
@@ -155,33 +155,143 @@
  * devices this falls back to the Material baseline theme.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-fun dynamicThemeColorProviders(): ColorProviders {
-    return ColorProviders(
-        primary = ColorProvider(R.color.glance_colorPrimary),
-        onPrimary = ColorProvider(R.color.glance_colorOnPrimary),
-        primaryContainer = ColorProvider(R.color.glance_colorPrimaryContainer),
-        onPrimaryContainer = ColorProvider(R.color.glance_colorOnPrimaryContainer),
-        secondary = ColorProvider(R.color.glance_colorSecondary),
-        onSecondary = ColorProvider(R.color.glance_colorOnSecondary),
-        secondaryContainer = ColorProvider(R.color.glance_colorSecondaryContainer),
-        onSecondaryContainer = ColorProvider(R.color.glance_colorOnSecondaryContainer),
-        tertiary = ColorProvider(R.color.glance_colorTertiary),
-        onTertiary = ColorProvider(R.color.glance_colorOnTertiary),
-        tertiaryContainer = ColorProvider(R.color.glance_colorTertiaryContainer),
-        onTertiaryContainer = ColorProvider(R.color.glance_colorOnTertiaryContainer),
-        error = ColorProvider(R.color.glance_colorError),
-        errorContainer = ColorProvider(R.color.glance_colorErrorContainer),
-        onError = ColorProvider(R.color.glance_colorOnError),
-        onErrorContainer = ColorProvider(R.color.glance_colorOnErrorContainer),
-        background = ColorProvider(R.color.glance_colorBackground),
-        onBackground = ColorProvider(R.color.glance_colorOnBackground),
-        surface = ColorProvider(R.color.glance_colorSurface),
-        onSurface = ColorProvider(R.color.glance_colorOnSurface),
-        surfaceVariant = ColorProvider(R.color.glance_colorSurfaceVariant),
-        onSurfaceVariant = ColorProvider(R.color.glance_colorOnSurfaceVariant),
-        outline = ColorProvider(R.color.glance_colorOutline),
-        inverseOnSurface = ColorProvider(R.color.glance_colorOnSurfaceInverse),
-        inverseSurface = ColorProvider(R.color.glance_colorSurfaceInverse),
-        inversePrimary = ColorProvider(R.color.glance_colorPrimaryInverse),
-    )
-}
+object DynamicThemeColorProviders : ColorProviders(
+    primary = ColorProvider(R.color.glance_colorPrimary),
+    onPrimary = ColorProvider(R.color.glance_colorOnPrimary),
+    primaryContainer = ColorProvider(R.color.glance_colorPrimaryContainer),
+    onPrimaryContainer = ColorProvider(R.color.glance_colorOnPrimaryContainer),
+    secondary = ColorProvider(R.color.glance_colorSecondary),
+    onSecondary = ColorProvider(R.color.glance_colorOnSecondary),
+    secondaryContainer = ColorProvider(R.color.glance_colorSecondaryContainer),
+    onSecondaryContainer = ColorProvider(R.color.glance_colorOnSecondaryContainer),
+    tertiary = ColorProvider(R.color.glance_colorTertiary),
+    onTertiary = ColorProvider(R.color.glance_colorOnTertiary),
+    tertiaryContainer = ColorProvider(R.color.glance_colorTertiaryContainer),
+    onTertiaryContainer = ColorProvider(R.color.glance_colorOnTertiaryContainer),
+    error = ColorProvider(R.color.glance_colorError),
+    errorContainer = ColorProvider(R.color.glance_colorErrorContainer),
+    onError = ColorProvider(R.color.glance_colorOnError),
+    onErrorContainer = ColorProvider(R.color.glance_colorOnErrorContainer),
+    background = ColorProvider(R.color.glance_colorBackground),
+    onBackground = ColorProvider(R.color.glance_colorOnBackground),
+    surface = ColorProvider(R.color.glance_colorSurface),
+    onSurface = ColorProvider(R.color.glance_colorOnSurface),
+    surfaceVariant = ColorProvider(R.color.glance_colorSurfaceVariant),
+    onSurfaceVariant = ColorProvider(R.color.glance_colorOnSurfaceVariant),
+    outline = ColorProvider(R.color.glance_colorOutline),
+    inverseOnSurface = ColorProvider(R.color.glance_colorOnSurfaceInverse),
+    inverseSurface = ColorProvider(R.color.glance_colorSurfaceInverse),
+    inversePrimary = ColorProvider(R.color.glance_colorPrimaryInverse),
+)
+
+internal class CustomColorProviders(
+    primary: ColorProvider,
+    onPrimary: ColorProvider,
+    primaryContainer: ColorProvider,
+    onPrimaryContainer: ColorProvider,
+    secondary: ColorProvider,
+    onSecondary: ColorProvider,
+    secondaryContainer: ColorProvider,
+    onSecondaryContainer: ColorProvider,
+    tertiary: ColorProvider,
+    onTertiary: ColorProvider,
+    tertiaryContainer: ColorProvider,
+    onTertiaryContainer: ColorProvider,
+    error: ColorProvider,
+    errorContainer: ColorProvider,
+    onError: ColorProvider,
+    onErrorContainer: ColorProvider,
+    background: ColorProvider,
+    onBackground: ColorProvider,
+    surface: ColorProvider,
+    onSurface: ColorProvider,
+    surfaceVariant: ColorProvider,
+    onSurfaceVariant: ColorProvider,
+    outline: ColorProvider,
+    inverseOnSurface: ColorProvider,
+    inverseSurface: ColorProvider,
+    inversePrimary: ColorProvider
+) : ColorProviders(
+    primary = primary,
+    onPrimary = onPrimary,
+    primaryContainer = primaryContainer,
+    onPrimaryContainer = onPrimaryContainer,
+    secondary = secondary,
+    onSecondary = onSecondary,
+    secondaryContainer = secondaryContainer,
+    onSecondaryContainer = onSecondaryContainer,
+    tertiary = tertiary,
+    onTertiary = onTertiary,
+    tertiaryContainer = tertiaryContainer,
+    onTertiaryContainer = onTertiaryContainer,
+    error = error,
+    errorContainer = errorContainer,
+    onError = onError,
+    onErrorContainer = onErrorContainer,
+    background = background,
+    onBackground = onBackground,
+    surface = surface,
+    onSurface = onSurface,
+    surfaceVariant = surfaceVariant,
+    onSurfaceVariant = onSurfaceVariant,
+    outline = outline,
+    inverseOnSurface = inverseOnSurface,
+    inverseSurface = inverseSurface,
+    inversePrimary = inversePrimary
+)
+
+fun colorProviders(
+    primary: ColorProvider,
+    onPrimary: ColorProvider,
+    primaryContainer: ColorProvider,
+    onPrimaryContainer: ColorProvider,
+    secondary: ColorProvider,
+    onSecondary: ColorProvider,
+    secondaryContainer: ColorProvider,
+    onSecondaryContainer: ColorProvider,
+    tertiary: ColorProvider,
+    onTertiary: ColorProvider,
+    tertiaryContainer: ColorProvider,
+    onTertiaryContainer: ColorProvider,
+    error: ColorProvider,
+    errorContainer: ColorProvider,
+    onError: ColorProvider,
+    onErrorContainer: ColorProvider,
+    background: ColorProvider,
+    onBackground: ColorProvider,
+    surface: ColorProvider,
+    onSurface: ColorProvider,
+    surfaceVariant: ColorProvider,
+    onSurfaceVariant: ColorProvider,
+    outline: ColorProvider,
+    inverseOnSurface: ColorProvider,
+    inverseSurface: ColorProvider,
+    inversePrimary: ColorProvider
+): ColorProviders = CustomColorProviders(
+    primary = primary,
+    onPrimary = onPrimary,
+    primaryContainer = primaryContainer,
+    onPrimaryContainer = onPrimaryContainer,
+    secondary = secondary,
+    onSecondary = onSecondary,
+    secondaryContainer = secondaryContainer,
+    onSecondaryContainer = onSecondaryContainer,
+    tertiary = tertiary,
+    onTertiary = onTertiary,
+    tertiaryContainer = tertiaryContainer,
+    onTertiaryContainer = onTertiaryContainer,
+    error = error,
+    errorContainer = errorContainer,
+    onError = onError,
+    onErrorContainer = onErrorContainer,
+    background = background,
+    onBackground = onBackground,
+    surface = surface,
+    onSurface = onSurface,
+    surfaceVariant = surfaceVariant,
+    onSurfaceVariant = onSurfaceVariant,
+    outline = outline,
+    inverseOnSurface = inverseOnSurface,
+    inverseSurface = inverseSurface,
+    inversePrimary = inversePrimary
+)
diff --git a/glance/glance/src/test/kotlin/androidx/glance/color/ColorProvidersTest.kt b/glance/glance/src/test/kotlin/androidx/glance/color/ColorProvidersTest.kt
index a8bdf7a..8ca45c7 100644
--- a/glance/glance/src/test/kotlin/androidx/glance/color/ColorProvidersTest.kt
+++ b/glance/glance/src/test/kotlin/androidx/glance/color/ColorProvidersTest.kt
@@ -19,9 +19,12 @@
 import android.content.Context
 import android.os.Build
 import androidx.annotation.ColorRes
+import androidx.compose.ui.graphics.Color
 import androidx.core.content.ContextCompat
+import androidx.glance.unit.ColorProvider
 import androidx.test.core.app.ApplicationProvider
 import com.google.common.truth.Truth.assertWithMessage
+import kotlin.test.assertIs
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.robolectric.RobolectricTestRunner
@@ -294,4 +297,39 @@
 
         assertWithMessage(message).that(sourceColor).isEqualTo(targetColor)
     }
+
+    @Test
+    fun ensureCustomColorSchemesArePossible() {
+        val testColor = ColorProvider(Color.Magenta)
+        assertIs<CustomColorProviders>(
+            colorProviders(
+                primary = testColor,
+                onPrimary = testColor,
+                primaryContainer = testColor,
+                onPrimaryContainer = testColor,
+                secondary = testColor,
+                onSecondary = testColor,
+                secondaryContainer = testColor,
+                onSecondaryContainer = testColor,
+                tertiary = testColor,
+                onTertiary = testColor,
+                tertiaryContainer = testColor,
+                onTertiaryContainer = testColor,
+                error = testColor,
+                errorContainer = testColor,
+                onError = testColor,
+                onErrorContainer = testColor,
+                background = testColor,
+                onBackground = testColor,
+                surface = testColor,
+                onSurface = testColor,
+                surfaceVariant = testColor,
+                onSurfaceVariant = testColor,
+                outline = testColor,
+                inverseOnSurface = testColor,
+                inverseSurface = testColor,
+                inversePrimary = testColor
+            )
+        )
+    }
 }
\ No newline at end of file
diff --git a/glance/glance/src/test/kotlin/androidx/glance/layout/PaddingTest.kt b/glance/glance/src/test/kotlin/androidx/glance/layout/PaddingTest.kt
index 1e9e060..8b6a366 100644
--- a/glance/glance/src/test/kotlin/androidx/glance/layout/PaddingTest.kt
+++ b/glance/glance/src/test/kotlin/androidx/glance/layout/PaddingTest.kt
@@ -21,8 +21,8 @@
 import androidx.glance.GlanceModifier
 import androidx.glance.findModifier
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.doReturn
-import com.nhaarman.mockitokotlin2.mock
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
 import org.junit.Test
 
 class PaddingTest {
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index 0353bdc..b700dd4 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -486,6 +486,14 @@
       </trusted-keys>
    </configuration>
    <components>
+      <component group="com.google.camerax.effects" name="portrait" version="0.0.1" androidx:reason="Unsigned. Used by CameraX. b/250921323">
+         <artifact name="portrait-0.0.1.pom">
+            <sha256 value="057a0111636ba5d261050fd8f02f4c84226613666e7378da2456ca6976eb7dff" origin="Generated by Gradle"/>
+         </artifact>
+         <artifact name="portrait-0.0.1.aar">
+            <sha256 value="86d3e99a6123e3f830bf3f70392b30fc8d238374104ce00a02de20d8613af607" origin="Generated by Gradle"/>
+         </artifact>
+      </component>
       <component group="com.github.gundy" name="semver4j" version="0.16.4" androidx:reason="Unsigned https://github.com/gundy/semver4j/issues/6">
          <artifact name="semver4j-0.16.4-nodeps.jar">
             <sha256 value="3f59eca516374ccd4fd3551625bf50f8a4b191f700508f7ce4866460a6128af0" origin="Generated by Gradle"/>
diff --git a/graphics/graphics-core/api/current.txt b/graphics/graphics-core/api/current.txt
index 865bb0f..cfaa745 100644
--- a/graphics/graphics-core/api/current.txt
+++ b/graphics/graphics-core/api/current.txt
@@ -1,7 +1,7 @@
 // Signature format: 4.0
 package androidx.graphics.lowlatency {
 
-  @RequiresApi(android.os.Build.VERSION_CODES.O) public final class FrameBuffer {
+  @RequiresApi(android.os.Build.VERSION_CODES.O) public final class FrameBuffer implements java.lang.AutoCloseable {
     ctor public FrameBuffer(androidx.graphics.opengl.egl.EGLSpec egl, android.hardware.HardwareBuffer hardwareBuffer);
     method public void close();
     method public android.hardware.HardwareBuffer getHardwareBuffer();
diff --git a/graphics/graphics-core/api/public_plus_experimental_current.txt b/graphics/graphics-core/api/public_plus_experimental_current.txt
index 865bb0f..cfaa745 100644
--- a/graphics/graphics-core/api/public_plus_experimental_current.txt
+++ b/graphics/graphics-core/api/public_plus_experimental_current.txt
@@ -1,7 +1,7 @@
 // Signature format: 4.0
 package androidx.graphics.lowlatency {
 
-  @RequiresApi(android.os.Build.VERSION_CODES.O) public final class FrameBuffer {
+  @RequiresApi(android.os.Build.VERSION_CODES.O) public final class FrameBuffer implements java.lang.AutoCloseable {
     ctor public FrameBuffer(androidx.graphics.opengl.egl.EGLSpec egl, android.hardware.HardwareBuffer hardwareBuffer);
     method public void close();
     method public android.hardware.HardwareBuffer getHardwareBuffer();
diff --git a/graphics/graphics-core/api/restricted_current.txt b/graphics/graphics-core/api/restricted_current.txt
index f4257dd..5e7c876 100644
--- a/graphics/graphics-core/api/restricted_current.txt
+++ b/graphics/graphics-core/api/restricted_current.txt
@@ -1,7 +1,7 @@
 // Signature format: 4.0
 package androidx.graphics.lowlatency {
 
-  @RequiresApi(android.os.Build.VERSION_CODES.O) public final class FrameBuffer {
+  @RequiresApi(android.os.Build.VERSION_CODES.O) public final class FrameBuffer implements java.lang.AutoCloseable {
     ctor public FrameBuffer(androidx.graphics.opengl.egl.EGLSpec egl, android.hardware.HardwareBuffer hardwareBuffer);
     method public void close();
     method public android.hardware.HardwareBuffer getHardwareBuffer();
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/FrameBuffer.kt b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/FrameBuffer.kt
index fa3e6ba..4f238d0 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/FrameBuffer.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/FrameBuffer.kt
@@ -29,16 +29,24 @@
  * creating a frame buffer object from it by leveraging Android
  * specific EGL extensions to create an [EGLImageKHR] object
  * that is loaded as a texture.
+ *
+ * @param egl [EGLSpec] used to specify EGL version and call various EGL methods
+ * @property hardwareBuffer the [HardwareBuffer] that this class wraps and used to generate a
+ * [EGLImageKHR] object
  */
 @RequiresApi(Build.VERSION_CODES.O)
 class FrameBuffer(
     private val egl: EGLSpec,
     val hardwareBuffer: HardwareBuffer,
-) {
+) : AutoCloseable {
 
     private var eglImage: EGLImageKHR?
     private var texture: Int = -1
     private var frameBuffer: Int = -1
+
+    /**
+     * Boolean that tells if the frame buffer is currently closed
+     */
     var isClosed = false
         private set
 
@@ -60,6 +68,10 @@
         frameBuffer = buffer[0]
     }
 
+    /**
+     * Binds this frame buffer to the read and draw framebuffer targets if it's not closed.
+     * If the frame buffer is already closed this method will do nothing.
+     */
     fun makeCurrent() {
         if (!isClosed) {
             GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffer)
@@ -73,7 +85,11 @@
         }
     }
 
-    fun close() {
+    /**
+     * Closes out the frame buffer, freeing all resources within it. This should be done only
+     * when the frame buffer is no longer needed or being accessed.
+     */
+    override fun close() {
         buffer[0] = frameBuffer
         GLES20.glDeleteBuffers(1, buffer, 0)
         frameBuffer = -1
diff --git a/health/connect/connect-client/build.gradle b/health/connect/connect-client/build.gradle
index 1055380..3d22950 100644
--- a/health/connect/connect-client/build.gradle
+++ b/health/connect/connect-client/build.gradle
@@ -50,7 +50,7 @@
     testImplementation(libs.truth)
     testImplementation(libs.kotlinCoroutinesTest)
     androidTestImplementation(libs.testRules)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.robolectric)
     testImplementation(libs.testExtJunit)
     testImplementation(libs.kotlinTest)
diff --git a/health/health-services-client/api/1.0.0-beta01.txt b/health/health-services-client/api/1.0.0-beta01.txt
index 8254bfb..0eb1f5c 100644
--- a/health/health-services-client/api/1.0.0-beta01.txt
+++ b/health/health-services-client/api/1.0.0-beta01.txt
@@ -208,7 +208,6 @@
     field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> RESTING_EXERCISE_DURATION_TOTAL;
     field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> RUNNING_STEPS;
     field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> RUNNING_STEPS_TOTAL;
-    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> SKIN_TEMPERATURE;
     field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> SPEED;
     field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> SPEED_STATS;
     field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> STEPS;
@@ -740,11 +739,9 @@
   }
 
   public final class PassiveGoal {
-    ctor public PassiveGoal(androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition, int triggerFrequency);
+    ctor public PassiveGoal(androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition);
     method public androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> getDataTypeCondition();
-    method public int getTriggerFrequency();
     property public final androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition;
-    property public final int triggerFrequency;
   }
 
   public final class PassiveListenerConfig extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveListenerConfig> {
diff --git a/health/health-services-client/api/current.txt b/health/health-services-client/api/current.txt
index 8254bfb..0eb1f5c 100644
--- a/health/health-services-client/api/current.txt
+++ b/health/health-services-client/api/current.txt
@@ -208,7 +208,6 @@
     field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> RESTING_EXERCISE_DURATION_TOTAL;
     field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> RUNNING_STEPS;
     field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> RUNNING_STEPS_TOTAL;
-    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> SKIN_TEMPERATURE;
     field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> SPEED;
     field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> SPEED_STATS;
     field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> STEPS;
@@ -740,11 +739,9 @@
   }
 
   public final class PassiveGoal {
-    ctor public PassiveGoal(androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition, int triggerFrequency);
+    ctor public PassiveGoal(androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition);
     method public androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> getDataTypeCondition();
-    method public int getTriggerFrequency();
     property public final androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition;
-    property public final int triggerFrequency;
   }
 
   public final class PassiveListenerConfig extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveListenerConfig> {
diff --git a/health/health-services-client/api/public_plus_experimental_1.0.0-beta01.txt b/health/health-services-client/api/public_plus_experimental_1.0.0-beta01.txt
index 8254bfb..0eb1f5c 100644
--- a/health/health-services-client/api/public_plus_experimental_1.0.0-beta01.txt
+++ b/health/health-services-client/api/public_plus_experimental_1.0.0-beta01.txt
@@ -208,7 +208,6 @@
     field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> RESTING_EXERCISE_DURATION_TOTAL;
     field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> RUNNING_STEPS;
     field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> RUNNING_STEPS_TOTAL;
-    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> SKIN_TEMPERATURE;
     field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> SPEED;
     field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> SPEED_STATS;
     field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> STEPS;
@@ -740,11 +739,9 @@
   }
 
   public final class PassiveGoal {
-    ctor public PassiveGoal(androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition, int triggerFrequency);
+    ctor public PassiveGoal(androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition);
     method public androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> getDataTypeCondition();
-    method public int getTriggerFrequency();
     property public final androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition;
-    property public final int triggerFrequency;
   }
 
   public final class PassiveListenerConfig extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveListenerConfig> {
diff --git a/health/health-services-client/api/public_plus_experimental_current.txt b/health/health-services-client/api/public_plus_experimental_current.txt
index 8254bfb..0eb1f5c 100644
--- a/health/health-services-client/api/public_plus_experimental_current.txt
+++ b/health/health-services-client/api/public_plus_experimental_current.txt
@@ -208,7 +208,6 @@
     field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> RESTING_EXERCISE_DURATION_TOTAL;
     field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> RUNNING_STEPS;
     field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> RUNNING_STEPS_TOTAL;
-    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> SKIN_TEMPERATURE;
     field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> SPEED;
     field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> SPEED_STATS;
     field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> STEPS;
@@ -740,11 +739,9 @@
   }
 
   public final class PassiveGoal {
-    ctor public PassiveGoal(androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition, int triggerFrequency);
+    ctor public PassiveGoal(androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition);
     method public androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> getDataTypeCondition();
-    method public int getTriggerFrequency();
     property public final androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition;
-    property public final int triggerFrequency;
   }
 
   public final class PassiveListenerConfig extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveListenerConfig> {
diff --git a/health/health-services-client/api/restricted_1.0.0-beta01.txt b/health/health-services-client/api/restricted_1.0.0-beta01.txt
index 8254bfb..0eb1f5c 100644
--- a/health/health-services-client/api/restricted_1.0.0-beta01.txt
+++ b/health/health-services-client/api/restricted_1.0.0-beta01.txt
@@ -208,7 +208,6 @@
     field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> RESTING_EXERCISE_DURATION_TOTAL;
     field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> RUNNING_STEPS;
     field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> RUNNING_STEPS_TOTAL;
-    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> SKIN_TEMPERATURE;
     field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> SPEED;
     field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> SPEED_STATS;
     field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> STEPS;
@@ -740,11 +739,9 @@
   }
 
   public final class PassiveGoal {
-    ctor public PassiveGoal(androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition, int triggerFrequency);
+    ctor public PassiveGoal(androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition);
     method public androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> getDataTypeCondition();
-    method public int getTriggerFrequency();
     property public final androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition;
-    property public final int triggerFrequency;
   }
 
   public final class PassiveListenerConfig extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveListenerConfig> {
diff --git a/health/health-services-client/api/restricted_current.txt b/health/health-services-client/api/restricted_current.txt
index 8254bfb..0eb1f5c 100644
--- a/health/health-services-client/api/restricted_current.txt
+++ b/health/health-services-client/api/restricted_current.txt
@@ -208,7 +208,6 @@
     field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> RESTING_EXERCISE_DURATION_TOTAL;
     field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> RUNNING_STEPS;
     field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Long,androidx.health.services.client.data.CumulativeDataPoint<java.lang.Long>> RUNNING_STEPS_TOTAL;
-    field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> SKIN_TEMPERATURE;
     field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Double,androidx.health.services.client.data.SampleDataPoint<java.lang.Double>> SPEED;
     field public static final androidx.health.services.client.data.AggregateDataType<java.lang.Double,androidx.health.services.client.data.StatisticalDataPoint<java.lang.Double>> SPEED_STATS;
     field public static final androidx.health.services.client.data.DeltaDataType<java.lang.Long,androidx.health.services.client.data.IntervalDataPoint<java.lang.Long>> STEPS;
@@ -740,11 +739,9 @@
   }
 
   public final class PassiveGoal {
-    ctor public PassiveGoal(androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition, int triggerFrequency);
+    ctor public PassiveGoal(androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition);
     method public androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> getDataTypeCondition();
-    method public int getTriggerFrequency();
     property public final androidx.health.services.client.data.DataTypeCondition<? extends java.lang.Number,? extends androidx.health.services.client.data.DeltaDataType<? extends java.lang.Number,?>> dataTypeCondition;
-    property public final int triggerFrequency;
   }
 
   public final class PassiveListenerConfig extends androidx.health.services.client.data.ProtoParcelable<androidx.health.services.client.proto.DataProto.PassiveListenerConfig> {
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/DataType.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/DataType.kt
index 9e4f97b..4334cd4 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/DataType.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/DataType.kt
@@ -473,11 +473,6 @@
         val RUNNING_STEPS_TOTAL: AggregateDataType<Long, CumulativeDataPoint<Long>> =
             createCumulativeDataType("Running Steps")
 
-        /** Temperature at the surface of the skin in Celsius. */
-        @JvmField
-        public val SKIN_TEMPERATURE: DeltaDataType<Double, SampleDataPoint<Double>> =
-            createSampleDataType("Skin temperature")
-
         /** Step rate in steps/minute at a given point in time. */
         @JvmField
         val STEPS_PER_MINUTE: DeltaDataType<Long, SampleDataPoint<Long>> =
@@ -638,7 +633,6 @@
             REP_COUNT,
             RESTING_EXERCISE_DURATION,
             RUNNING_STEPS,
-            SKIN_TEMPERATURE,
             SPEED,
             STEPS,
             STEPS_PER_MINUTE,
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/LocationAccuracy.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/LocationAccuracy.kt
index 0aab15a..04225c2 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/LocationAccuracy.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/LocationAccuracy.kt
@@ -25,7 +25,8 @@
 /** Accuracy for a [DataType.LOCATION] data point. */
 @Suppress("ParcelCreator")
 public class LocationAccuracy(
-    /** Represents the estimated horizontal accuracy of the location, radial, in meters.
+    /**
+     * Represents the estimated horizontal accuracy of the location, radial, in meters.
      * Range starting from 0.0.
      *
      * @throws IllegalArgumentException if [horizontalPositionErrorMeters] is negative
@@ -33,8 +34,9 @@
     @FloatRange(from = 0.0) public val horizontalPositionErrorMeters: Double,
 
     /**
-     * Represents the estimated vertical accuracy of the location, radial, in meters, or it will
-     * be <code>{@value Double#MAX_VALUE}</code> if it cannot be provided. Range starting from 0.0.
+     * Represents the estimated vertical accuracy corresponding to the altitude of the location,
+     * radial, in meters, or it will be [Double.MAX_VALUE] if it cannot be provided. Range starting
+     * from 0.0.
      *
      * @throws IllegalArgumentException if [verticalPositionErrorMeters] is negative
      */
diff --git a/health/health-services-client/src/main/java/androidx/health/services/client/data/PassiveGoal.kt b/health/health-services-client/src/main/java/androidx/health/services/client/data/PassiveGoal.kt
index d0ff73e..72c8603 100644
--- a/health/health-services-client/src/main/java/androidx/health/services/client/data/PassiveGoal.kt
+++ b/health/health-services-client/src/main/java/androidx/health/services/client/data/PassiveGoal.kt
@@ -21,15 +21,27 @@
 import androidx.annotation.RestrictTo
 import androidx.health.services.client.data.PassiveGoal.TriggerFrequency.Companion.toProto
 
-/** Defines an passive goal that will be triggered when the specified condition is met. */
+/**
+ * Defines a passive goal that will be triggered when the specified condition is met which will
+ * repeat daily.
+ */
 @Suppress("ParcelCreator")
-class PassiveGoal(
+class PassiveGoal private constructor(
     /** [DataTypeCondition] which must be met for the passive goal to be triggered. */
     val dataTypeCondition: DataTypeCondition<out Number, out DeltaDataType<out Number, *>>,
-    /** Frequency this goal should trigger, which is expected to be a  */
-    @TriggerFrequency val triggerFrequency: Int,
+    /** Frequency this goal should trigger, which is expected to be a [TriggerFrequency]. */
+    @TriggerFrequency internal val triggerFrequency: Int,
 ) {
 
+    /**
+     * Constructs a new [PassiveGoal] with the given [dataTypeCondition]. This goal will
+     * automatically repeat daily.
+     */
+    public constructor(
+        /** [DataTypeCondition] which must be met for the passive goal to be triggered. */
+        dataTypeCondition: DataTypeCondition<out Number, out DeltaDataType<out Number, *>>
+    ) : this(dataTypeCondition, TriggerFrequency.REPEATED)
+
     internal constructor(
         proto: PassiveGoalProto
     ) : this(
@@ -69,7 +81,7 @@
         TriggerFrequency.ONCE,
         TriggerFrequency.REPEATED,
     )
-    annotation class TriggerFrequency {
+    internal annotation class TriggerFrequency {
 
         companion object {
             /** TriggerFrequency is an unknown or unexpected value. */
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/DataTypeTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/DataTypeTest.kt
index 8f13f70..4bc8db2 100644
--- a/health/health-services-client/src/test/java/androidx/health/services/client/data/DataTypeTest.kt
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/DataTypeTest.kt
@@ -27,7 +27,6 @@
 import androidx.health.services.client.data.DataType.Companion.STEPS_DAILY
 import androidx.health.services.client.data.DataType.Companion.FORMAT_BYTE_ARRAY
 import androidx.health.services.client.data.DataType.Companion.LOCATION
-import androidx.health.services.client.data.DataType.Companion.SKIN_TEMPERATURE
 import androidx.health.services.client.data.DataType.Companion.STEPS
 import androidx.health.services.client.data.DataType.Companion.SWIMMING_LAP_COUNT
 import androidx.health.services.client.data.DataType.TimeType.Companion.UNKNOWN
@@ -188,7 +187,6 @@
             remove(DISTANCE_DAILY)
             remove(FLOORS_DAILY)
             remove(STEPS_DAILY)
-            remove(SKIN_TEMPERATURE)
         }.map { it.name }
 
         assertThat(aggregateNames).containsExactlyElementsIn(deltaNames)
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/data/PassiveGoalTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/data/PassiveGoalTest.kt
index 4675680..f7b7185 100644
--- a/health/health-services-client/src/test/java/androidx/health/services/client/data/PassiveGoalTest.kt
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/data/PassiveGoalTest.kt
@@ -19,6 +19,7 @@
 import androidx.health.services.client.data.ComparisonType.Companion.GREATER_THAN
 import androidx.health.services.client.data.DataType.Companion.STEPS
 import androidx.health.services.client.data.DataType.Companion.STEPS_DAILY
+import androidx.health.services.client.proto.DataProto
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -28,42 +29,34 @@
 class PassiveGoalTest {
     @Test
     fun protoRoundTrip() {
-        val proto = PassiveGoal(
-            DataTypeCondition(STEPS_DAILY, 400, GREATER_THAN),
-            PassiveGoal.TriggerFrequency.ONCE
-        ).proto
+        val proto = PassiveGoal(DataTypeCondition(STEPS_DAILY, 400, GREATER_THAN)).proto
 
         val goal = PassiveGoal(proto)
 
         assertThat(goal.dataTypeCondition.dataType).isEqualTo(STEPS_DAILY)
         assertThat(goal.dataTypeCondition.threshold).isEqualTo(400)
         assertThat(goal.dataTypeCondition.comparisonType).isEqualTo(GREATER_THAN)
-        assertThat(goal.triggerFrequency).isEqualTo(PassiveGoal.TriggerFrequency.ONCE)
+        assertThat(goal.triggerFrequency).isEqualTo(PassiveGoal.TriggerFrequency.REPEATED)
     }
 
     @Test
     fun shouldEqual() {
-        val goal1 = PassiveGoal(
-            DataTypeCondition(STEPS_DAILY, 400, GREATER_THAN),
-            PassiveGoal.TriggerFrequency.ONCE
-        )
-        val goal2 = PassiveGoal(
-            DataTypeCondition(STEPS_DAILY, 400, GREATER_THAN),
-            PassiveGoal.TriggerFrequency.ONCE
-        )
+        val goal1 = PassiveGoal(DataTypeCondition(STEPS_DAILY, 400, GREATER_THAN))
+        val goal2 = PassiveGoal(DataTypeCondition(STEPS_DAILY, 400, GREATER_THAN))
 
         assertThat(goal1).isEqualTo(goal2)
     }
 
     @Test
     fun shouldNotEqual_differentTriggerFrequency() {
-        val goal1 = PassiveGoal(
-            DataTypeCondition(STEPS_DAILY, 400, GREATER_THAN),
-            PassiveGoal.TriggerFrequency.ONCE
-        )
+        // This case isn't expected to happen for clients, but it _could_ happen from the service
+        // side for old clients. Using proto constructor because triggerFrequency constructor is
+        // private.
+        val goal1 = PassiveGoal(DataTypeCondition(STEPS_DAILY, 400, GREATER_THAN))
         val goal2 = PassiveGoal(
-            DataTypeCondition(STEPS_DAILY, 400, GREATER_THAN),
-            PassiveGoal.TriggerFrequency.REPEATED
+            goal1.proto.toBuilder()
+                .setTriggerFrequency(DataProto.PassiveGoal.TriggerFrequency.TRIGGER_FREQUENCY_ONCE)
+                .build()
         )
 
         assertThat(goal1).isNotEqualTo(goal2)
@@ -71,28 +64,16 @@
 
     @Test
     fun shouldNotEqual_differentThreshold() {
-        val goal1 = PassiveGoal(
-            DataTypeCondition(STEPS_DAILY, 400, GREATER_THAN),
-            PassiveGoal.TriggerFrequency.ONCE
-        )
-        val goal2 = PassiveGoal(
-            DataTypeCondition(STEPS_DAILY, 800, GREATER_THAN),
-            PassiveGoal.TriggerFrequency.ONCE
-        )
+        val goal1 = PassiveGoal(DataTypeCondition(STEPS_DAILY, 400, GREATER_THAN))
+        val goal2 = PassiveGoal(DataTypeCondition(STEPS_DAILY, 800, GREATER_THAN))
 
         assertThat(goal1).isNotEqualTo(goal2)
     }
 
     @Test
     fun shouldNotEqual_differentDataType() {
-        val goal1 = PassiveGoal(
-            DataTypeCondition(STEPS_DAILY, 400, GREATER_THAN),
-            PassiveGoal.TriggerFrequency.ONCE
-        )
-        val goal2 = PassiveGoal(
-            DataTypeCondition(STEPS, 400, GREATER_THAN),
-            PassiveGoal.TriggerFrequency.ONCE
-        )
+        val goal1 = PassiveGoal(DataTypeCondition(STEPS_DAILY, 400, GREATER_THAN))
+        val goal2 = PassiveGoal(DataTypeCondition(STEPS, 400, GREATER_THAN))
 
         assertThat(goal1).isNotEqualTo(goal2)
     }
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/impl/ServiceBackedPassiveMonitoringClientTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/impl/ServiceBackedPassiveMonitoringClientTest.kt
index 104817f..637de44 100644
--- a/health/health-services-client/src/test/java/androidx/health/services/client/impl/ServiceBackedPassiveMonitoringClientTest.kt
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/impl/ServiceBackedPassiveMonitoringClientTest.kt
@@ -187,7 +187,7 @@
             dataTypes = setOf(STEPS_DAILY),
             shouldRequestUserActivityState = false,
             dailyGoals = setOf(
-                PassiveGoal(DataTypeCondition(STEPS_DAILY, 87, GREATER_THAN), ONCE)
+                PassiveGoal(DataTypeCondition(STEPS_DAILY, 87, GREATER_THAN))
             ),
             healthEventTypes = setOf()
         )
@@ -259,7 +259,7 @@
             dataTypes = setOf(STEPS_DAILY),
             shouldRequestUserActivityState = false,
             dailyGoals = setOf(
-                PassiveGoal(DataTypeCondition(STEPS_DAILY, 87, GREATER_THAN), ONCE)
+                PassiveGoal(DataTypeCondition(STEPS_DAILY, 87, GREATER_THAN))
             ),
             healthEventTypes = setOf()
         )
@@ -372,4 +372,4 @@
             statusCallbackAction.invoke(statusCallback)
         }
     }
-}
\ No newline at end of file
+}
diff --git a/health/health-services-client/src/test/java/androidx/health/services/client/impl/response/PassiveMonitoringGoalResponseTest.kt b/health/health-services-client/src/test/java/androidx/health/services/client/impl/response/PassiveMonitoringGoalResponseTest.kt
index 042f45d..54ee821 100644
--- a/health/health-services-client/src/test/java/androidx/health/services/client/impl/response/PassiveMonitoringGoalResponseTest.kt
+++ b/health/health-services-client/src/test/java/androidx/health/services/client/impl/response/PassiveMonitoringGoalResponseTest.kt
@@ -30,10 +30,7 @@
     @Test
     fun protoRoundTrip() {
         val proto = PassiveMonitoringGoalResponse(
-            PassiveGoal(
-                DataTypeCondition(STEPS_DAILY, 1000, ComparisonType.GREATER_THAN),
-                PassiveGoal.TriggerFrequency.REPEATED
-            )
+            PassiveGoal(DataTypeCondition(STEPS_DAILY, 1000, ComparisonType.GREATER_THAN))
         ).proto
 
         val response = PassiveMonitoringGoalResponse(proto)
diff --git a/libraryversions.toml b/libraryversions.toml
index c740cc5..bf07b12 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -19,7 +19,7 @@
 CAR_APP = "1.3.0-beta02"
 COLLECTION = "1.3.0-dev01"
 COMPOSE = "1.4.0-alpha01"
-COMPOSE_COMPILER = "1.3.2"
+COMPOSE_COMPILER = "1.3.3"
 COMPOSE_MATERIAL3 = "1.1.0-alpha01"
 COMPOSE_RUNTIME_TRACING = "1.0.0-alpha01"
 CONSTRAINTLAYOUT = "2.2.0-alpha05"
@@ -95,7 +95,7 @@
 RECYCLERVIEW_SELECTION = "1.2.0-alpha02"
 REMOTECALLBACK = "1.0.0-alpha02"
 RESOURCEINSPECTION = "1.1.0-alpha01"
-ROOM = "2.5.0-rc01"
+ROOM = "2.6.0-alpha01"
 SAVEDSTATE = "1.3.0-alpha01"
 SECURITY = "1.1.0-alpha04"
 SECURITY_APP_AUTHENTICATOR = "1.0.0-alpha03"
@@ -108,7 +108,7 @@
 SLICE_BUILDERS_KTX = "1.0.0-alpha08"
 SLICE_REMOTECALLBACK = "1.0.0-alpha01"
 SLIDINGPANELAYOUT = "1.3.0-alpha01"
-SQLITE = "2.3.0-rc01"
+SQLITE = "2.4.0-alpha01"
 SQLITE_INSPECTOR = "2.1.0-alpha01"
 STARTUP = "1.2.0-alpha02"
 SWIPEREFRESHLAYOUT = "1.2.0-alpha01"
@@ -117,7 +117,7 @@
 TEST_UIAUTOMATOR = "2.3.0-alpha02"
 TEXT = "1.0.0-alpha01"
 TRACING = "1.2.0-alpha02"
-TRACING_PERFETTO = "1.0.0-alpha05"
+TRACING_PERFETTO = "1.0.0-alpha06"
 TRANSITION = "1.5.0-alpha01"
 TV = "1.0.0-alpha02"
 TVPROVIDER = "1.1.0-alpha02"
diff --git a/lifecycle/integration-tests/testapp/build.gradle b/lifecycle/integration-tests/testapp/build.gradle
index c1e3d0a..1b3b2a2 100644
--- a/lifecycle/integration-tests/testapp/build.gradle
+++ b/lifecycle/integration-tests/testapp/build.gradle
@@ -40,7 +40,7 @@
     androidTestImplementation(libs.truth)
 
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testAnnotationProcessor(project(":lifecycle:lifecycle-compiler"))
 }
 
diff --git a/lifecycle/lifecycle-common-java8/build.gradle b/lifecycle/lifecycle-common-java8/build.gradle
index 29e84d3..f4af9fe 100644
--- a/lifecycle/lifecycle-common-java8/build.gradle
+++ b/lifecycle/lifecycle-common-java8/build.gradle
@@ -26,7 +26,7 @@
     api("androidx.annotation:annotation:1.1.0")
 
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
 }
 
 androidx {
diff --git a/lifecycle/lifecycle-common/build.gradle b/lifecycle/lifecycle-common/build.gradle
index 644cc9a..70a07f9 100644
--- a/lifecycle/lifecycle-common/build.gradle
+++ b/lifecycle/lifecycle-common/build.gradle
@@ -25,7 +25,7 @@
     api("androidx.annotation:annotation:1.1.0")
 
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
 
     constraints {
         implementation(project(":lifecycle:lifecycle-common-java8"))
diff --git a/lifecycle/lifecycle-extensions/build.gradle b/lifecycle/lifecycle-extensions/build.gradle
index 81acc67..cf246c3 100644
--- a/lifecycle/lifecycle-extensions/build.gradle
+++ b/lifecycle/lifecycle-extensions/build.gradle
@@ -36,7 +36,7 @@
 
     testImplementation("androidx.arch.core:core-testing:2.1.0")
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
 
     androidTestImplementation(libs.kotlinStdlib)
     androidTestImplementation(libs.truth)
diff --git a/lifecycle/lifecycle-livedata-core-ktx/build.gradle b/lifecycle/lifecycle-livedata-core-ktx/build.gradle
index dc35984..bb14934 100644
--- a/lifecycle/lifecycle-livedata-core-ktx/build.gradle
+++ b/lifecycle/lifecycle-livedata-core-ktx/build.gradle
@@ -37,12 +37,7 @@
     lintPublish(project(":lifecycle:lifecycle-livedata-core-ktx-lint"))
 
     constraints {
-        // this syntax mirrors the temporary workaround in lifecycle-livedata-core
-        // dependency constraints which allows lifecycle-livedata-core-ktx to have
-        // a project constraint on compose and wear, even though they are not within
-        // the 'MAIN' project-set.
-        // update syntax when b/239979823 is fixed.
-        implementation("androidx.lifecycle:lifecycle-livedata-core:{androidx.LibraryVersions.LIFECYCLE}")
+        implementation(project(":lifecycle:lifecycle-livedata-core"))
     }
 }
 
diff --git a/lifecycle/lifecycle-livedata-core-truth/build.gradle b/lifecycle/lifecycle-livedata-core-truth/build.gradle
index 9ffdaa9..d42633e 100644
--- a/lifecycle/lifecycle-livedata-core-truth/build.gradle
+++ b/lifecycle/lifecycle-livedata-core-truth/build.gradle
@@ -28,7 +28,7 @@
     api(libs.truth)
     api(libs.kotlinStdlib)
     testImplementation(libs.truth)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation("androidx.arch.core:core-testing:2.1.0")
     testImplementation(project(":internal-testutils-truth"))
 }
diff --git a/lifecycle/lifecycle-livedata-core/build.gradle b/lifecycle/lifecycle-livedata-core/build.gradle
index 5558583..786db12 100644
--- a/lifecycle/lifecycle-livedata-core/build.gradle
+++ b/lifecycle/lifecycle-livedata-core/build.gradle
@@ -31,20 +31,12 @@
     testImplementation(project(":lifecycle:lifecycle-runtime-testing"))
     testImplementation(libs.kotlinCoroutinesTest)
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
 
     constraints {
-        // this syntax mirrors the temporary workaround in lifecycle-livedata
-        // dependency constraints which allows lifecycle-livedata to have a
-        // project constraint on compose, even though compose is not within
-        // the 'MAIN' project-set.
-        // update syntax when b/239979823 is fixed.
-        implementation("androidx.lifecycle:lifecycle-livedata:{androidx.LibraryVersions.LIFECYCLE}")
-        // this syntax is a temporary workout to allow project dependency on cross-project-set
-        // i.e. COMPOSE + MAIN project sets
-        // update syntax when b/239979823 is fixed
-        implementation("androidx.lifecycle:lifecycle-livedata-core-ktx:{androidx.LibraryVersions.LIFECYCLE}")
         implementation(project(":lifecycle:lifecycle-common"))
+        implementation(project(":lifecycle:lifecycle-livedata"))
+        implementation(project(":lifecycle:lifecycle-livedata-core-ktx"))
         implementation(project(":lifecycle:lifecycle-viewmodel-savedstate"))
     }
 }
diff --git a/lifecycle/lifecycle-livedata/build.gradle b/lifecycle/lifecycle-livedata/build.gradle
index 1b8932d..01b408a 100644
--- a/lifecycle/lifecycle-livedata/build.gradle
+++ b/lifecycle/lifecycle-livedata/build.gradle
@@ -30,14 +30,11 @@
     testImplementation("androidx.arch.core:core-testing:2.1.0")
     testImplementation(libs.kotlinCoroutinesTest)
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.truth)
 
     constraints {
-        // this syntax is a temporary workout to allow project dependency on cross-project-set
-        // i.e. COMPOSE + MAIN project sets
-        // update syntax when b/239979823 is fixed
-        implementation("androidx.lifecycle:lifecycle-livedata-core:{androidx.LibraryVersions.LIFECYCLE}")
+        implementation(project(":lifecycle:lifecycle-livedata-core"))
         implementation(project(":lifecycle:lifecycle-livedata-ktx"))
     }
 }
diff --git a/lifecycle/lifecycle-process/build.gradle b/lifecycle/lifecycle-process/build.gradle
index 186b49b..f339712 100644
--- a/lifecycle/lifecycle-process/build.gradle
+++ b/lifecycle/lifecycle-process/build.gradle
@@ -35,7 +35,7 @@
     api("androidx.annotation:annotation:1.2.0")
 
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
 }
 
 androidx {
diff --git a/lifecycle/lifecycle-runtime-compose/build.gradle b/lifecycle/lifecycle-runtime-compose/build.gradle
index effe393..532249b 100644
--- a/lifecycle/lifecycle-runtime-compose/build.gradle
+++ b/lifecycle/lifecycle-runtime-compose/build.gradle
@@ -42,15 +42,6 @@
     androidTestImplementation(libs.truth)
 
     samples(projectOrArtifact(":lifecycle:lifecycle-runtime-compose:lifecycle-runtime-compose-samples"))
-
-    constraints {
-        // this syntax mirrors the temporary workaround in lifecycle-runtime
-        // dependency constraints which allows lifecycle-runtime to have a
-        // project constraint on compose, even though compose is not within
-        // the 'MAIN' project-set.
-        // update syntax when b/239979823 is fixed.
-        implementation("androidx.lifecycle:lifecycle-runtime:{androidx.LibraryVersions.LIFECYCLE}")
-    }
 }
 
 androidx {
diff --git a/lifecycle/lifecycle-runtime/build.gradle b/lifecycle/lifecycle-runtime/build.gradle
index 38f1a2b..03f363c 100644
--- a/lifecycle/lifecycle-runtime/build.gradle
+++ b/lifecycle/lifecycle-runtime/build.gradle
@@ -21,7 +21,7 @@
     implementation("androidx.arch.core:core-runtime:2.1.0")
 
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
 
     androidTestImplementation(libs.junit)
     androidTestImplementation(libs.testExtJunit)
@@ -32,10 +32,6 @@
         implementation(project(":lifecycle:lifecycle-common"))
         implementation(project(":lifecycle:lifecycle-runtime-ktx"))
         implementation(project(":lifecycle:lifecycle-runtime-testing"))
-        // this syntax is a temporary workout to allow project dependency on cross-project-set
-        // i.e. COMPOSE + MAIN project sets
-        // update syntax when b/239979823 is fixed
-        implementation("androidx.lifecycle:lifecycle-runtime-compose:{androidx.LibraryVersions.LIFECYCLE}")
     }
 }
 
diff --git a/lifecycle/lifecycle-viewmodel-compose/build.gradle b/lifecycle/lifecycle-viewmodel-compose/build.gradle
index 9c372fa..9ca789e 100644
--- a/lifecycle/lifecycle-viewmodel-compose/build.gradle
+++ b/lifecycle/lifecycle-viewmodel-compose/build.gradle
@@ -55,15 +55,6 @@
     androidTestImplementation(projectOrArtifact(":lifecycle:lifecycle-runtime-testing"))
 
     samples(projectOrArtifact(":lifecycle:lifecycle-viewmodel-compose:lifecycle-viewmodel-compose-samples"))
-
-    constraints {
-        // this syntax mirrors the temporary workaround in lifecycle-viewmodel-savedstate
-        // dependency constraints which allows lifecycle-viewmodel-savedstate to have a
-        // project constraint on compose, even though compose is not within the 'MAIN'
-        // project-set.
-        // update syntax when b/239979823 is fixed.
-        implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:{androidx.LibraryVersions.LIFECYCLE}")
-    }
 }
 
 androidx {
diff --git a/lifecycle/lifecycle-viewmodel-ktx/build.gradle b/lifecycle/lifecycle-viewmodel-ktx/build.gradle
index aa4f8f2..01883e7 100644
--- a/lifecycle/lifecycle-viewmodel-ktx/build.gradle
+++ b/lifecycle/lifecycle-viewmodel-ktx/build.gradle
@@ -34,12 +34,7 @@
     androidTestImplementation(libs.testRunner)
 
     constraints {
-        // this syntax mirrors the temporary workaround in lifecycle-viewmodel
-        // dependency constraints which allows lifecycle-viewmodel-ktx to have a
-        // project constraint on compose, even though compose is not within
-        // the 'MAIN' project-set.
-        // update syntax when b/239979823 is fixed.
-        implementation("androidx.lifecycle:lifecycle-viewmodel:{androidx.LibraryVersions.LIFECYCLE}")
+        implementation(project(":lifecycle:lifecycle-viewmodel"))
     }
 }
 
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/build.gradle b/lifecycle/lifecycle-viewmodel-savedstate/build.gradle
index 2f5146a..6ac5072 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/build.gradle
+++ b/lifecycle/lifecycle-viewmodel-savedstate/build.gradle
@@ -52,10 +52,6 @@
     constraints {
         implementation(project(":lifecycle:lifecycle-livedata-core"))
         implementation(project(":lifecycle:lifecycle-viewmodel"))
-        // this syntax is a temporary workout to allow project dependency on cross-project-set
-        // i.e. COMPOSE + MAIN project sets
-        // update syntax when b/239979823 is fixed
-        implementation("androidx.lifecycle:lifecycle-viewmodel-compose:{androidx.LibraryVersions.LIFECYCLE}")
     }
 }
 
diff --git a/lifecycle/lifecycle-viewmodel/build.gradle b/lifecycle/lifecycle-viewmodel/build.gradle
index 58ca6cad..21eb489 100644
--- a/lifecycle/lifecycle-viewmodel/build.gradle
+++ b/lifecycle/lifecycle-viewmodel/build.gradle
@@ -35,14 +35,11 @@
     api("androidx.annotation:annotation:1.1.0")
     api(libs.kotlinStdlib)
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.truth)
 
     constraints {
-        // this syntax is a temporary workout to allow project dependency on cross-project-set
-        // i.e. COMPOSE + MAIN project sets
-        // update syntax when b/239979823 is fixed
-        implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:{androidx.LibraryVersions.LIFECYCLE}")
+        implementation(project(":lifecycle:lifecycle-viewmodel-ktx"))
         implementation(project(":lifecycle:lifecycle-viewmodel-savedstate"))
     }
 
diff --git a/media/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java b/media/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java
index 1b5aeef..39d6092 100644
--- a/media/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java
+++ b/media/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java
@@ -63,8 +63,11 @@
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Allows an app to interact with an ongoing media session. Media buttons and
@@ -211,10 +214,7 @@
     private final MediaSessionCompat.Token mToken;
     // This set is used to keep references to registered callbacks to prevent them being GCed,
     // since we only keep weak references for callbacks in this class and its inner classes.
-    // It is actually a map not a set. Ignore the values and treat the keys as a set.
-    @SuppressLint("BanConcurrentHashMap")
-    private final java.util.concurrent.ConcurrentHashMap<Callback, Boolean> mRegisteredCallbacks =
-            new java.util.concurrent.ConcurrentHashMap<>();
+    private final Set<Callback> mRegisteredCallbacks;
 
     /**
      * Creates a media controller from a session.
@@ -235,6 +235,7 @@
         if (sessionToken == null) {
             throw new IllegalArgumentException("sessionToken must not be null");
         }
+        mRegisteredCallbacks = Collections.synchronizedSet(new HashSet<>());
         mToken = sessionToken;
 
         if (Build.VERSION.SDK_INT >= 29) {
@@ -559,7 +560,7 @@
         if (callback == null) {
             throw new IllegalArgumentException("callback must not be null");
         }
-        if (mRegisteredCallbacks.putIfAbsent(callback, true) != null) {
+        if(!mRegisteredCallbacks.add(callback)) {
             Log.w(TAG, "the callback has already been registered");
             return;
         }
@@ -580,7 +581,7 @@
         if (callback == null) {
             throw new IllegalArgumentException("callback must not be null");
         }
-        if (mRegisteredCallbacks.remove(callback) == null) {
+        if (!mRegisteredCallbacks.remove(callback)) {
             Log.w(TAG, "the callback has never been registered");
             return;
         }
diff --git a/navigation/integration-tests/safeargs-testapp/build.gradle b/navigation/integration-tests/safeargs-testapp/build.gradle
index 93ebf64e..623dd80 100644
--- a/navigation/integration-tests/safeargs-testapp/build.gradle
+++ b/navigation/integration-tests/safeargs-testapp/build.gradle
@@ -64,5 +64,5 @@
 dependencies {
     implementation "${LibraryGroups.NAVIGATION}:navigation-runtime:${LibraryVersions.NAVIGATION}"
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
 }
diff --git a/navigation/navigation-common/build.gradle b/navigation/navigation-common/build.gradle
index 1428843..31e3381 100644
--- a/navigation/navigation-common/build.gradle
+++ b/navigation/navigation-common/build.gradle
@@ -42,7 +42,7 @@
     testImplementation(project(":navigation:navigation-testing"))
     testImplementation("androidx.arch.core:core-testing:2.1.0")
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.truth)
     testImplementation(libs.kotlinStdlib)
     testImplementation(libs.kotlinCoroutinesTest)
diff --git a/navigation/navigation-dynamic-features-fragment/build.gradle b/navigation/navigation-dynamic-features-fragment/build.gradle
index bd7895b..8631ad3 100644
--- a/navigation/navigation-dynamic-features-fragment/build.gradle
+++ b/navigation/navigation-dynamic-features-fragment/build.gradle
@@ -38,7 +38,7 @@
     testImplementation(libs.testExtJunit)
     testImplementation(libs.testRunner)
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.robolectric)
     testImplementation(libs.truth)
 
diff --git a/navigation/navigation-dynamic-features-runtime/build.gradle b/navigation/navigation-dynamic-features-runtime/build.gradle
index 20863e1..4adba73 100644
--- a/navigation/navigation-dynamic-features-runtime/build.gradle
+++ b/navigation/navigation-dynamic-features-runtime/build.gradle
@@ -40,7 +40,7 @@
     testImplementation(libs.testExtJunit)
     testImplementation(libs.testRunner)
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.robolectric)
     testImplementation(libs.truth)
     testImplementation(libs.kotlinCoroutinesTest)
diff --git a/paging/paging-common/build.gradle b/paging/paging-common/build.gradle
index e674f69..88a3769 100644
--- a/paging/paging-common/build.gradle
+++ b/paging/paging-common/build.gradle
@@ -38,10 +38,8 @@
     api(libs.kotlinCoroutinesCore)
 
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
-    testImplementation(libs.mockitoKotlin, {
-        exclude group: "org.mockito" // to keep control on the mockito version
-    })
+    testImplementation(libs.mockitoCore4)
+    testImplementation(libs.mockitoKotlin4)
     testImplementation(project(":internal-testutils-common"))
     testImplementation(project(":internal-testutils-ktx"))
     testImplementation(project(":internal-testutils-paging"))
diff --git a/paging/paging-common/src/test/kotlin/androidx/paging/ContiguousPagedListTest.kt b/paging/paging-common/src/test/kotlin/androidx/paging/ContiguousPagedListTest.kt
index 5e5857f..1a4637b 100644
--- a/paging/paging-common/src/test/kotlin/androidx/paging/ContiguousPagedListTest.kt
+++ b/paging/paging-common/src/test/kotlin/androidx/paging/ContiguousPagedListTest.kt
@@ -29,11 +29,10 @@
 import androidx.paging.PagedList.Config
 import androidx.paging.PagingSource.LoadResult.Page
 import androidx.testutils.TestDispatcher
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.reset
-import com.nhaarman.mockitokotlin2.verify
-import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
-import com.nhaarman.mockitokotlin2.verifyZeroInteractions
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.reset
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyNoMoreInteractions
 import kotlinx.coroutines.runBlocking
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertSame
@@ -329,7 +328,7 @@
         val callback = mock<Callback>()
         pagedList.addWeakCallback(callback)
         verifyRange(0, 40, pagedList)
-        verifyZeroInteractions(callback)
+        verifyNoMoreInteractions(callback)
 
         pagedList.loadAround(35)
         drain()
@@ -345,7 +344,7 @@
         val callback = mock<Callback>()
         pagedList.addWeakCallback(callback)
         verifyRange(0, 40, pagedList)
-        verifyZeroInteractions(callback)
+        verifyNoMoreInteractions(callback)
 
         pagedList.loadAround(35)
         // return a LoadResult.Invalid
@@ -369,7 +368,7 @@
         val callback = mock<Callback>()
         pagedList.addWeakCallback(callback)
         verifyRange(60, 40, pagedList)
-        verifyZeroInteractions(callback)
+        verifyNoMoreInteractions(callback)
 
         pagedList.loadAround(if (placeholdersEnabled) 65 else 5)
         drain()
@@ -385,7 +384,7 @@
         val callback = mock<Callback>()
         pagedList.addWeakCallback(callback)
         verifyRange(60, 40, pagedList)
-        verifyZeroInteractions(callback)
+        verifyNoMoreInteractions(callback)
 
         pagedList.loadAround(if (placeholdersEnabled) 65 else 5)
         // return a LoadResult.Invalid
@@ -409,7 +408,7 @@
         val callback = mock<Callback>()
         pagedList.addWeakCallback(callback)
         verifyRange(20, 40, pagedList)
-        verifyZeroInteractions(callback)
+        verifyNoMoreInteractions(callback)
 
         pagedList.loadAround(if (placeholdersEnabled) 55 else 35)
         drain()
@@ -496,7 +495,7 @@
         val callback = mock<Callback>()
         pagedList.addWeakCallback(callback)
         verifyRange(0, 20, pagedList)
-        verifyZeroInteractions(callback)
+        verifyNoMoreInteractions(callback)
 
         // load 2nd page
         pagedList.loadAround(19)
@@ -533,7 +532,7 @@
         val callback = mock<Callback>()
         pagedList.addWeakCallback(callback)
         verifyRange(80, 20, pagedList)
-        verifyZeroInteractions(callback)
+        verifyNoMoreInteractions(callback)
 
         // load 4th page
         pagedList.loadAround(if (placeholdersEnabled) 80 else 0)
@@ -586,7 +585,7 @@
 
         // but before page received, access near end of list
         pagedList.loadAround(if (placeholdersEnabled) 3 else 2)
-        verifyZeroInteractions(callback)
+        verifyNoMoreInteractions(callback)
         mainThread.executeAll()
         // and the load at the beginning is dropped without signaling callback
         verifyNoMoreInteractions(callback)
@@ -628,7 +627,7 @@
 
         // but before page received, access near front of list
         pagedList.loadAround(if (placeholdersEnabled) 1 else 0)
-        verifyZeroInteractions(callback)
+        verifyNoMoreInteractions(callback)
         mainThread.executeAll()
         // and the load at the end is dropped without signaling callback
         verifyNoMoreInteractions(callback)
@@ -878,7 +877,7 @@
         val callback = mock<Callback>()
         pagedList.addWeakCallback(callback)
         verifyRange(0, 10, pagedList)
-        verifyZeroInteractions(callback)
+        verifyNoMoreInteractions(callback)
 
         pagedList.loadAround(5)
         drain()
@@ -1060,9 +1059,9 @@
         verifyRange(80, 20, pagedList)
 
         // nothing yet
-        verifyZeroInteractions(boundaryCallback)
+        verifyNoMoreInteractions(boundaryCallback)
         drain()
-        verifyZeroInteractions(boundaryCallback)
+        verifyNoMoreInteractions(boundaryCallback)
 
         // loading around last item causes onItemAtEndLoaded
         pagedList.loadAround(if (placeholdersEnabled) 99 else 19)
@@ -1075,7 +1074,7 @@
         pagedList.loadAround(if (placeholdersEnabled) 80 else 0)
         drain()
         verifyRange(60, 40, pagedList)
-        verifyZeroInteractions(boundaryCallback)
+        verifyNoMoreInteractions(boundaryCallback)
 
         // ...load rest of data, still no dispatch...
         pagedList.loadAround(if (placeholdersEnabled) 60 else 0)
@@ -1085,7 +1084,7 @@
         pagedList.loadAround(if (placeholdersEnabled) 20 else 0)
         drain()
         verifyRange(0, 100, pagedList)
-        verifyZeroInteractions(boundaryCallback)
+        verifyNoMoreInteractions(boundaryCallback)
 
         // ... finally try prepend, see 0 items, which will dispatch front callback
         pagedList.loadAround(0)
diff --git a/paging/paging-common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt b/paging/paging-common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt
index 13182ab..7af01a1 100644
--- a/paging/paging-common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt
+++ b/paging/paging-common/src/test/kotlin/androidx/paging/ItemKeyedDataSourceTest.kt
@@ -17,8 +17,8 @@
 package androidx.paging
 
 import androidx.paging.PagingSource.LoadResult.Page.Companion.COUNT_UNDEFINED
-import com.nhaarman.mockitokotlin2.capture
-import com.nhaarman.mockitokotlin2.mock
+import org.mockito.kotlin.capture
+import org.mockito.kotlin.mock
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.runBlocking
 import org.junit.Assert.assertEquals
diff --git a/paging/paging-common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt b/paging/paging-common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt
index fecb3e1..8c62bec 100644
--- a/paging/paging-common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt
+++ b/paging/paging-common/src/test/kotlin/androidx/paging/PageKeyedDataSourceTest.kt
@@ -31,7 +31,6 @@
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.Mockito.verifyZeroInteractions
 import kotlin.coroutines.EmptyCoroutineContext
 import kotlin.test.assertFailsWith
 
@@ -258,7 +257,7 @@
 
         pagedList.loadAround(0)
 
-        verifyZeroInteractions(boundaryCallback)
+        verifyNoMoreInteractions(boundaryCallback)
 
         dispatcher.executeAll()
 
@@ -311,7 +310,7 @@
 
         pagedList.loadAround(0)
 
-        verifyZeroInteractions(boundaryCallback)
+        verifyNoMoreInteractions(boundaryCallback)
 
         dispatcher.executeAll()
 
diff --git a/paging/paging-testing/src/main/java/androidx/paging/testing/StaticListPagingSource.kt b/paging/paging-testing/src/main/java/androidx/paging/testing/StaticListPagingSource.kt
new file mode 100644
index 0000000..5e28a95
--- /dev/null
+++ b/paging/paging-testing/src/main/java/androidx/paging/testing/StaticListPagingSource.kt
@@ -0,0 +1,124 @@
+/*
+ * 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.paging.testing
+
+import androidx.paging.Pager
+import androidx.paging.PagingSource.LoadParams
+import androidx.paging.PagingSource.LoadParams.Append
+import androidx.paging.PagingSource.LoadParams.Prepend
+import androidx.paging.PagingSource.LoadParams.Refresh
+import androidx.paging.PagingSource
+import androidx.paging.PagingState
+
+/**
+ * An implementation of [PagingSource] that loads data from a static [dataList]. The source
+ * of data is expected to be immutable. This [PagingSource] should be be invalidated
+ * externally whenever the [dataList] passed to this [PagingSource] becomes obsolete.
+ */
+internal class StaticListPagingSource<Value : Any>(
+    private val dataList: List<Value>
+) : PagingSource<Int, Value>() {
+
+    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Value> {
+        val key = params.key ?: 0
+        val indexStart = computeIndexStart(params, key)
+        val indexEnd = computeIndexEnd(params, key, indexStart)
+        val data = dataList.slice(indexStart..indexEnd)
+        return LoadResult.Page(
+            data = data,
+            prevKey = if (indexStart <= 0 || data.isEmpty()) null else indexStart - 1,
+            nextKey = if (indexEnd >= dataList.lastIndex || data.isEmpty()) null else indexEnd + 1,
+            itemsBefore = indexStart,
+            itemsAfter = dataList.lastIndex - indexEnd
+        )
+    }
+
+    /**
+     * Returns the index to start loading from the [dataList] for each [load] call.
+     *
+     * Prepend: The [key] represents the index before the first loaded Page's first index, i.e.
+     * if first loaded page contains items in index[25, 26, 27], then [key] = 24. The `startIndex`
+     * is offset negatively by [LoadParams.loadSize]. For example, if [key] = 24 and loadSize = 5,
+     * then the startIndex = 19, such that items in index[20, 21, 22, 23, 24] are loaded.
+     *
+     * Refresh: The [key] may derive from either [getRefreshKey] or from the `initialKey` passed
+     * in to [Pager]. If the [key] is larger than [dataList].lastIndex (i.e. an initialKey
+     * that is out of bounds), the `startIndex` will be offset negatively from the lastIndex
+     * by [LoadParams.loadSize] such that it will start loading from the last page.
+     * For example if index[0 - 49] is available with [key] = 55 and loadSize = 5,
+     * the `startIndex` will be offset to 45 such that items in index[45, 46, 47, 48, 49] are
+     * loaded. The largest possible `startIndex` that can be returned for
+     * Refresh is [dataList].lastIndex.
+     *
+     * Negative startIndices are clipped to 0.
+     */
+    private fun computeIndexStart(params: LoadParams<Int>, key: Int): Int {
+        return when (params) {
+            is Prepend -> (key - params.loadSize + 1)
+            is Append -> key
+            is Refresh ->
+                if (key >= dataList.lastIndex) {
+                    (dataList.size - params.loadSize)
+                } else {
+                    key
+                }
+        }.coerceAtLeast(0)
+    }
+
+    /**
+     * Returns the index to stop loading from the [dataList] for each [load] call.
+     *
+     * Prepend: The [key] represents the index before the first loaded Page's first index, i.e.
+     * if first loaded page contains items in index[25, 26, 27], then [key] = 24. If loadSize
+     * exceeds available data to load, i.e. loadSize = 5 with only items in index[0, 1, 2] are
+     * available, the `endIndex` is clipped to the value of [key]. Reusing example with [key] = 2
+     * and items[0, 1, 2] available, the `startIndex` = 0 and the `endIndex` of `4` is clipped
+     * to `2` such that only items [0, 1, 2] are loaded.
+     *
+     * Refresh: The [key] may derive from either [getRefreshKey] or from the `initialKey` passed
+     * in to [Pager]. As long as the `endIndex` is within bounds of [dataList] indices, `endIndex`
+     * will load the maximum amount of available data as requested by [LoadParams.loadSize]. If
+     * the [key] is out of bounds, it will be clipped to a maximum value of [dataList].lastIndex.
+     */
+    private fun computeIndexEnd(params: LoadParams<Int>, key: Int, startIndex: Int): Int {
+        val defaultOffset = startIndex + params.loadSize - 1
+        return when (params) {
+            is Prepend -> defaultOffset.coerceAtMost(key)
+            else -> defaultOffset.coerceAtMost(dataList.lastIndex)
+        }
+    }
+
+    /**
+     * Returns the key to be used as the [LoadParams.key] for the next generation's Refresh load.
+     *
+     * It is unknown whether anchorPosition represents the item at the top of the screen or item at
+     * the bottom of the screen. To ensure the number of items loaded is enough to fill up the
+     * screen, half of loadSize is loaded before the anchorPosition and the other half is
+     * loaded after the anchorPosition -- anchorPosition becomes the middle item.
+     *
+     * Negative refreshKeys are clipped to 0.
+    */
+    override fun getRefreshKey(state: PagingState<Int, Value>): Int? {
+        return when (val anchorPosition = state.anchorPosition) {
+            null -> null
+            else -> (anchorPosition - state.config.initialLoadSize / 2).coerceAtLeast(0)
+        }
+    }
+
+    override val jumpingSupported: Boolean
+        get() = true
+}
\ No newline at end of file
diff --git a/paging/paging-testing/src/test/kotlin/androidx/paging/testing/StaticListPagingSourceTest.kt b/paging/paging-testing/src/test/kotlin/androidx/paging/testing/StaticListPagingSourceTest.kt
new file mode 100644
index 0000000..0ab9d08
--- /dev/null
+++ b/paging/paging-testing/src/test/kotlin/androidx/paging/testing/StaticListPagingSourceTest.kt
@@ -0,0 +1,297 @@
+/*
+ * 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.paging.testing
+
+import androidx.paging.PagingConfig
+import androidx.paging.PagingSource
+import androidx.paging.PagingSource.LoadResult
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class StaticListPagingSourceTest {
+
+    private val DATA = List(100) { it }
+    private val CONFIG = PagingConfig(
+        pageSize = 3,
+        initialLoadSize = 5,
+    )
+
+    @Test
+    fun refresh() = runPagingSourceTest { _, pager ->
+        val result = pager.refresh() as LoadResult.Page
+        assertThat(result).isEqualTo(
+            LoadResult.Page(
+                data = listOf(0, 1, 2, 3, 4),
+                prevKey = null,
+                nextKey = 5,
+                itemsBefore = 0,
+                itemsAfter = 95
+            )
+        )
+    }
+
+    @Test
+    fun refresh_withEmptyData() = runPagingSourceTest(StaticListPagingSource(emptyList())) {
+            _, pager ->
+
+        val result = pager.refresh() as LoadResult.Page
+        assertThat(result).isEqualTo(
+            LoadResult.Page(
+                data = emptyList(),
+                prevKey = null,
+                nextKey = null,
+                itemsBefore = 0,
+                itemsAfter = 0
+            )
+        )
+    }
+
+    @Test
+    fun refresh_initialKey() = runPagingSourceTest { _, pager ->
+        val result = pager.refresh(initialKey = 20) as LoadResult.Page
+        assertThat(result).isEqualTo(
+            LoadResult.Page(
+                data = listOf(20, 21, 22, 23, 24),
+                prevKey = 19,
+                nextKey = 25,
+                itemsBefore = 20,
+                itemsAfter = 75
+            )
+        )
+    }
+
+    @Test
+    fun refresh_initialKey_withEmptyData() = runPagingSourceTest(
+        StaticListPagingSource(emptyList())
+    ) { _, pager ->
+
+        val result = pager.refresh(initialKey = 20) as LoadResult.Page
+        assertThat(result).isEqualTo(
+            LoadResult.Page(
+                data = emptyList(),
+                prevKey = null,
+                nextKey = null,
+                itemsBefore = 0,
+                itemsAfter = 0
+            )
+        )
+    }
+
+    @Test
+    fun refresh_negativeKeyClippedToZero() = runPagingSourceTest { _, pager ->
+        val result = pager.refresh(initialKey = -1) as LoadResult.Page
+        // loads first page
+        assertThat(result).isEqualTo(
+            listOf(0, 1, 2, 3, 4).asPage()
+        )
+    }
+
+    @Test
+    fun refresh_KeyLargerThanDataSize_loadsLastPage() = runPagingSourceTest { _, pager ->
+        val result = pager.refresh(initialKey = 140) as LoadResult.Page
+        // loads last page
+        assertThat(result).isEqualTo(
+            listOf(95, 96, 97, 98, 99).asPage()
+        )
+    }
+
+    @Test
+    fun append() = runPagingSourceTest { _, pager ->
+        pager.run {
+            refresh()
+            append()
+        }
+        assertThat(pager.getPages()).containsExactlyElementsIn(
+            listOf(
+                LoadResult.Page(
+                    data = listOf(0, 1, 2, 3, 4),
+                    prevKey = null,
+                    nextKey = 5,
+                    itemsBefore = 0,
+                    itemsAfter = 95
+                ),
+                LoadResult.Page(
+                    data = listOf(5, 6, 7),
+                    prevKey = 4,
+                    nextKey = 8,
+                    itemsBefore = 5,
+                    itemsAfter = 92
+                )
+            )
+        ).inOrder()
+    }
+
+    @Test
+    fun append_consecutively() = runPagingSourceTest { _, pager ->
+        pager.run {
+            refresh()
+            append()
+            append()
+            append()
+        }
+        assertThat(pager.getPages()).containsExactlyElementsIn(
+            listOf(
+                listOf(0, 1, 2, 3, 4).asPage(),
+                listOf(5, 6, 7).asPage(),
+                listOf(8, 9, 10).asPage(),
+                listOf(11, 12, 13).asPage(),
+            )
+        ).inOrder()
+    }
+
+    @Test
+    fun append_loadSizeLargerThanAvailableData() = runPagingSourceTest { _, pager ->
+        val result = pager.run {
+            refresh(initialKey = 94)
+            append() as LoadResult.Page
+        }
+        assertThat(result).isEqualTo(
+            LoadResult.Page(
+                data = listOf(99),
+                prevKey = 98,
+                nextKey = null,
+                itemsBefore = 99,
+                itemsAfter = 0
+            )
+        )
+    }
+
+    @Test
+    fun prepend() = runPagingSourceTest { _, pager ->
+        pager.run {
+            refresh(20)
+            prepend()
+        }
+        assertThat(pager.getPages()).containsExactlyElementsIn(
+            listOf(
+                LoadResult.Page(
+                    data = listOf(17, 18, 19),
+                    prevKey = 16,
+                    nextKey = 20,
+                    itemsBefore = 17,
+                    itemsAfter = 80
+                ),
+                LoadResult.Page(
+                    data = listOf(20, 21, 22, 23, 24),
+                    prevKey = 19,
+                    nextKey = 25,
+                    itemsBefore = 20,
+                    itemsAfter = 75
+                ),
+            )
+        ).inOrder()
+    }
+
+    @Test
+    fun prepend_consecutively() = runPagingSourceTest { _, pager ->
+        pager.run {
+            refresh(initialKey = 50)
+            prepend()
+            prepend()
+            prepend()
+        }
+        assertThat(pager.getPages()).containsExactlyElementsIn(
+            listOf(
+                listOf(41, 42, 43).asPage(),
+                listOf(44, 45, 46).asPage(),
+                listOf(47, 48, 49).asPage(),
+                listOf(50, 51, 52, 53, 54).asPage(),
+            )
+        ).inOrder()
+    }
+
+    @Test
+    fun prepend_loadSizeLargerThanAvailableData() = runPagingSourceTest { _, pager ->
+        val result = pager.run {
+            refresh(initialKey = 2)
+            prepend()
+        }
+        assertThat(result).isEqualTo(
+            LoadResult.Page(
+                data = listOf(0, 1),
+                prevKey = null,
+                nextKey = 2,
+                itemsBefore = 0,
+                itemsAfter = 98
+            )
+        )
+    }
+
+    @Test
+    fun jump_enabled() {
+        val source = StaticListPagingSource(DATA)
+        assertThat(source.jumpingSupported).isTrue()
+    }
+
+    @Test
+    fun refreshKey() = runPagingSourceTest { pagingSource, pager ->
+        val state = pager.run {
+            refresh() // [0, 1, 2, 3, 4]
+            append() // [5, 6, 7]
+            // the anchorPos should be 7
+            getPagingState(anchorPosition = 7)
+        }
+
+        val refreshKey = pagingSource.getRefreshKey(state)
+        val expected = 7 - (CONFIG.initialLoadSize / 2)
+        assertThat(expected).isEqualTo(5)
+        assertThat(refreshKey).isEqualTo(expected)
+    }
+
+    @Test
+    fun refreshKey_negativeKeyClippedToZero() = runPagingSourceTest { pagingSource, pager ->
+        val state = pager.run {
+            refresh(2) // [2, 3, 4, 5, 6]
+            prepend() // [0, 1]
+            getPagingState(anchorPosition = 1)
+        }
+        // before clipping, refreshKey = 1 - (CONFIG.initialLoadSize / 2) = -1
+        val refreshKey = pagingSource.getRefreshKey(state)
+        assertThat(refreshKey).isEqualTo(0)
+    }
+
+    @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+    private fun runPagingSourceTest(
+        source: PagingSource<Int, Int> = StaticListPagingSource(DATA),
+        pager: TestPager<Int, Int> = TestPager(source, CONFIG),
+        block: suspend (pagingSource: PagingSource<Int, Int>, pager: TestPager<Int, Int>) -> Unit
+    ) {
+        runTest {
+            block(source, pager)
+        }
+    }
+
+    private fun List<Int>.asPage(): LoadResult.Page<Int, Int> {
+        val indexStart = firstOrNull()
+        val indexEnd = lastOrNull()
+        return LoadResult.Page(
+            data = this,
+            prevKey = indexStart?.let {
+                if (indexStart <= 0 || isEmpty()) null else indexStart - 1
+            },
+            nextKey = indexEnd?.let {
+                if (indexEnd >= DATA.lastIndex || isEmpty()) null else indexEnd + 1
+            },
+            itemsBefore = indexStart ?: -1,
+            itemsAfter = if (indexEnd == null) -1 else DATA.lastIndex - indexEnd
+        )
+    }
+}
\ No newline at end of file
diff --git a/playground-common/playground.properties b/playground-common/playground.properties
index 9b176ff..2277543 100644
--- a/playground-common/playground.properties
+++ b/playground-common/playground.properties
@@ -25,7 +25,7 @@
 kotlin.code.style=official
 # Disable docs
 androidx.enableDocumentation=false
-androidx.playground.snapshotBuildId=9126977
+androidx.playground.snapshotBuildId=9138201
 androidx.playground.metalavaBuildId=8993580
 androidx.playground.dokkaBuildId=7472101
 androidx.studio.type=playground
diff --git a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/StubDelegatesGenerator.kt b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/StubDelegatesGenerator.kt
index c8750fe..24bc632 100644
--- a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/StubDelegatesGenerator.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/generator/StubDelegatesGenerator.kt
@@ -17,6 +17,7 @@
 package androidx.privacysandbox.tools.apicompiler.generator
 
 import androidx.privacysandbox.tools.core.generator.AidlGenerator
+import androidx.privacysandbox.tools.core.generator.SpecNames
 import androidx.privacysandbox.tools.core.generator.addCode
 import androidx.privacysandbox.tools.core.generator.addControlFlow
 import androidx.privacysandbox.tools.core.generator.aidlName
@@ -77,12 +78,6 @@
 
         val fileSpec = FileSpec.builder(service.type.packageName, className).build {
             addType(classSpec)
-            if (service.methods.any(Method::isSuspend)) {
-                addImport("kotlinx.coroutines", "CoroutineScope")
-                addImport("kotlinx.coroutines", "Dispatchers")
-                addImport("kotlinx.coroutines", "GlobalScope")
-                addImport("kotlinx.coroutines", "launch")
-            }
         }
         codeGenerator.createNewFile(
             Dependencies(false), service.type.packageName, className
@@ -101,7 +96,12 @@
             addModifiers(KModifier.OVERRIDE)
             addParameters(getParameters(method))
             addCode {
-                addControlFlow("val job = GlobalScope.launch(Dispatchers.Main)") {
+                addControlFlow(
+                    "val job = %T.%M(%T)",
+                    SpecNames.globalScopeClass,
+                    SpecNames.launchMethod,
+                    SpecNames.dispatchersMainClass
+                ) {
                     addControlFlow("try") {
                         addStatement("val result = $resultStatement")
                         addStatement("transactionCallback.onSuccess(result)")
diff --git a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/parser/ApiParser.kt b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/parser/ApiParser.kt
index 7a156fe..ca16985 100644
--- a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/parser/ApiParser.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/parser/ApiParser.kt
@@ -16,14 +16,19 @@
 
 package androidx.privacysandbox.tools.apicompiler.parser
 
+import androidx.privacysandbox.tools.PrivacySandboxCallback
 import androidx.privacysandbox.tools.PrivacySandboxService
 import androidx.privacysandbox.tools.PrivacySandboxValue
 import androidx.privacysandbox.tools.core.model.AnnotatedInterface
 import androidx.privacysandbox.tools.core.model.AnnotatedValue
 import androidx.privacysandbox.tools.core.model.ParsedApi
+import androidx.privacysandbox.tools.core.validator.ModelValidator
 import com.google.devtools.ksp.processing.KSPLogger
 import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.symbol.ClassKind
+import com.google.devtools.ksp.symbol.KSClassDeclaration
 import com.google.devtools.ksp.symbol.KSName
+import kotlin.reflect.KClass
 
 /** Convenience extension to get the full qualifier + name from a [KSName]. */
 internal fun KSName.getFullName(): String {
@@ -33,7 +38,7 @@
 
 /** Top-level entry point to parse a complete user-defined sandbox SDK API into a [ParsedApi]. */
 class ApiParser(private val resolver: Resolver, private val logger: KSPLogger) {
-    private val interfaceParser = InterfaceParser(resolver, logger)
+    private val interfaceParser = InterfaceParser(logger)
     private val valueParser = ValueParser(logger)
 
     fun parseApi(): ParsedApi {
@@ -41,13 +46,14 @@
         if (services.count() > 1) {
             logger.error(
                 "Multiple interfaces annotated with @PrivacySandboxService are not supported (${
-                services.joinToString {
-                    it.type.simpleName
-                }
-            }).")
+                    services.joinToString {
+                        it.type.simpleName
+                    }
+                }).")
         }
         val values = parseAllValues()
-        return ParsedApi(services, values)
+        val callbacks = parseAllCallbacks()
+        return ParsedApi(services, values, callbacks).also(::validate)
     }
 
     private fun parseAllValues(): Set<AnnotatedValue> {
@@ -56,7 +62,35 @@
     }
 
     private fun parseAllServices(): Set<AnnotatedInterface> {
-        return resolver.getSymbolsWithAnnotation(PrivacySandboxService::class.qualifiedName!!)
-            .mapNotNull(interfaceParser::parseInterface).toSet()
+        return getInterfacesWithAnnotation(PrivacySandboxService::class)
+            .map(interfaceParser::parseInterface)
+            .toSet()
+    }
+
+    private fun parseAllCallbacks(): Set<AnnotatedInterface> {
+        return getInterfacesWithAnnotation(PrivacySandboxCallback::class)
+            .map(interfaceParser::parseInterface)
+            .toSet()
+    }
+
+    private fun getInterfacesWithAnnotation(annotationName: KClass<*>):
+        Sequence<KSClassDeclaration> {
+        val symbolsWithAnnotation =
+            resolver.getSymbolsWithAnnotation(annotationName.qualifiedName!!)
+        if (symbolsWithAnnotation.any {
+                it !is KSClassDeclaration ||
+                    it.classKind != ClassKind.INTERFACE
+            }) {
+            logger.error("Only interfaces can be annotated with @${annotationName.simpleName}.")
+            return emptySequence()
+        }
+        return symbolsWithAnnotation.filterIsInstance<KSClassDeclaration>()
+    }
+
+    private fun validate(api: ParsedApi) {
+        val validationResult = ModelValidator.validate(api)
+        for (error in validationResult.errors) {
+            logger.error(error)
+        }
     }
 }
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/parser/InterfaceParser.kt b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/parser/InterfaceParser.kt
index 47bebf4..79c69c9 100644
--- a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/parser/InterfaceParser.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/parser/InterfaceParser.kt
@@ -23,11 +23,8 @@
 import com.google.devtools.ksp.getDeclaredFunctions
 import com.google.devtools.ksp.getDeclaredProperties
 import com.google.devtools.ksp.isPublic
-import com.google.devtools.ksp.processing.KSBuiltIns
 import com.google.devtools.ksp.processing.KSPLogger
-import com.google.devtools.ksp.processing.Resolver
 import com.google.devtools.ksp.symbol.ClassKind
-import com.google.devtools.ksp.symbol.KSAnnotated
 import com.google.devtools.ksp.symbol.KSClassDeclaration
 import com.google.devtools.ksp.symbol.KSFunctionDeclaration
 import com.google.devtools.ksp.symbol.KSType
@@ -35,18 +32,13 @@
 import com.google.devtools.ksp.symbol.Modifier
 import com.google.devtools.ksp.symbol.Nullability
 
-internal class InterfaceParser(resolver: Resolver, private val logger: KSPLogger) {
-    private val primitiveTypes = getPrimitiveTypes(resolver.builtIns)
+internal class InterfaceParser(private val logger: KSPLogger) {
     private val validInterfaceModifiers = setOf(Modifier.PUBLIC)
     private val validMethodModifiers = setOf(Modifier.PUBLIC, Modifier.SUSPEND)
 
-    fun parseInterface(interfaceDeclaration: KSAnnotated): AnnotatedInterface? {
-        if (interfaceDeclaration !is KSClassDeclaration ||
-            interfaceDeclaration.classKind != ClassKind.INTERFACE) {
-            logger.error(
-                "Only interfaces can be annotated with @PrivacySandboxService."
-            )
-            return null
+    fun parseInterface(interfaceDeclaration: KSClassDeclaration): AnnotatedInterface {
+        check(interfaceDeclaration.classKind == ClassKind.INTERFACE) {
+            "${interfaceDeclaration.qualifiedName} is not an interface."
         }
         val name = interfaceDeclaration.qualifiedName?.getFullName()
             ?: interfaceDeclaration.simpleName.getFullName()
@@ -145,21 +137,6 @@
         if (type.nullability == Nullability.NULLABLE) {
             logger.error("Error in $name: nullable types are not supported.")
         }
-        if (!primitiveTypes.contains(type)) {
-            logger.error("Error in $name: only primitive types are supported.")
-        }
         return Converters.typeFromDeclaration(type.declaration)
     }
-
-    private fun getPrimitiveTypes(builtIns: KSBuiltIns) = listOf(
-        builtIns.booleanType,
-        builtIns.shortType,
-        builtIns.intType,
-        builtIns.longType,
-        builtIns.floatType,
-        builtIns.doubleType,
-        builtIns.charType,
-        builtIns.stringType,
-        builtIns.unitType,
-    )
 }
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/parser/ValueParser.kt b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/parser/ValueParser.kt
index 4edec6a..39d8a26 100644
--- a/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/parser/ValueParser.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/main/java/androidx/privacysandbox/tools/apicompiler/parser/ValueParser.kt
@@ -72,6 +72,9 @@
             logger.error("Error in $name: properties cannot be mutable.")
         }
         val type = property.type.resolve()
+        if (type.isError) {
+            logger.error("Failed to resolve type for property $name.")
+        }
         if (type.nullability == Nullability.NULLABLE) {
             logger.error("Error in $name: nullable types are not supported.")
         }
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/parser/InterfaceParserTest.kt b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/parser/InterfaceParserTest.kt
index 87320c2..c9c7a11 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/parser/InterfaceParserTest.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/parser/InterfaceParserTest.kt
@@ -17,7 +17,7 @@
 package androidx.privacysandbox.tools.apicompiler.parser
 
 import androidx.privacysandbox.tools.apicompiler.util.checkSourceFails
-import androidx.privacysandbox.tools.apicompiler.util.parseSource
+import androidx.privacysandbox.tools.apicompiler.util.parseSources
 import androidx.privacysandbox.tools.core.model.AnnotatedInterface
 import androidx.privacysandbox.tools.core.model.Method
 import androidx.privacysandbox.tools.core.model.Parameter
@@ -36,7 +36,8 @@
     @Test
     fun parseServiceInterface_ok() {
         val source = Source.kotlin(
-            "com/mysdk/MySdk.kt", """
+            "com/mysdk/MySdk.kt",
+            """
                     package com.mysdk
                     import androidx.privacysandbox.tools.PrivacySandboxService
                     @PrivacySandboxService
@@ -44,9 +45,9 @@
                         suspend fun doStuff(x: Int, y: Int): String
                         fun doMoreStuff()
                     }
-                """
+                """,
         )
-        assertThat(parseSource(source)).isEqualTo(
+        assertThat(parseSources(source)).isEqualTo(
             ParsedApi(
                 services = mutableSetOf(
                     AnnotatedInterface(
@@ -120,6 +121,7 @@
                     "supported (MySdk, MySdk2)."
             )
     }
+
     @Test
     fun privateInterface_fails() {
         checkSourceFails(
@@ -218,7 +220,9 @@
     fun parameterWithGenerics_fails() {
         checkSourceFails(serviceMethod("suspend fun foo(x: MutableList<Int>)"))
             .containsExactlyErrors(
-                "Error in com.mysdk.MySdk.foo: only primitive types are supported."
+                "Error in com.mysdk.MySdk.foo: only primitives, data classes annotated with " +
+                    "@PrivacySandboxValue and interfaces annotated with @PrivacySandboxCallback " +
+                    "are supported as parameter types."
             )
     }
 
@@ -226,7 +230,9 @@
     fun parameterLambda_fails() {
         checkSourceFails(serviceMethod("suspend fun foo(x: (Int) -> Int)"))
             .containsExactlyErrors(
-                "Error in com.mysdk.MySdk.foo: only primitive types are supported."
+                "Error in com.mysdk.MySdk.foo: only primitives, data classes annotated with " +
+                    "@PrivacySandboxValue and interfaces annotated with @PrivacySandboxCallback " +
+                    "are supported as parameter types."
             )
     }
 
@@ -245,7 +251,8 @@
                 """
         )
         checkSourceFails(source).containsExactlyErrors(
-            "Error in com.mysdk.MySdk.foo: only primitive types are supported."
+            "Error in com.mysdk.MySdk.foo: only primitives and data classes annotated with " +
+                "@PrivacySandboxValue are supported as return types."
         )
     }
 
@@ -257,6 +264,63 @@
             )
     }
 
+    @Test
+    fun parseCallbackInterface_ok() {
+        val serviceSource =
+            Source.kotlin(
+                "com/mysdk/MySdk.kt",
+                """
+                    package com.mysdk
+                    import androidx.privacysandbox.tools.PrivacySandboxService
+                    @PrivacySandboxService
+                    interface MySdk
+                """
+            )
+        val callbackSource =
+            Source.kotlin(
+                "com/mysdk/MyCallback.kt",
+                """
+                    package com.mysdk
+                    import androidx.privacysandbox.tools.PrivacySandboxCallback
+                    @PrivacySandboxCallback
+                    interface MyCallback {
+                        fun onComplete(x: Int, y: Int)
+                    }
+                """
+            )
+        assertThat(parseSources(serviceSource, callbackSource)).isEqualTo(
+            ParsedApi(
+                services = setOf(
+                    AnnotatedInterface(
+                        type = Type(packageName = "com.mysdk", simpleName = "MySdk"),
+                    )
+                ),
+                callbacks = setOf(
+                    AnnotatedInterface(
+                        type = Type(packageName = "com.mysdk", simpleName = "MyCallback"),
+                        methods = listOf(
+                            Method(
+                                name = "onComplete",
+                                parameters = listOf(
+                                    Parameter(
+                                        name = "x",
+                                        type = Types.int,
+                                    ),
+                                    Parameter(
+                                        name = "y",
+                                        type = Types.int,
+                                    )
+                                ),
+                                returnType = Types.unit,
+                                isSuspend = false,
+                            ),
+                        )
+                    )
+                )
+            )
+        )
+    }
+
     private fun serviceInterface(declaration: String) = Source.kotlin(
         "com/mysdk/MySdk.kt", """
             package com.mysdk
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/parser/ValueParserTest.kt b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/parser/ValueParserTest.kt
index 194d2e8..abdec6c 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/parser/ValueParserTest.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/parser/ValueParserTest.kt
@@ -17,9 +17,11 @@
 package androidx.privacysandbox.tools.apicompiler.parser
 
 import androidx.privacysandbox.tools.apicompiler.util.checkSourceFails
-import androidx.privacysandbox.tools.apicompiler.util.parseSource
+import androidx.privacysandbox.tools.apicompiler.util.parseSources
 import androidx.privacysandbox.tools.core.model.AnnotatedInterface
 import androidx.privacysandbox.tools.core.model.AnnotatedValue
+import androidx.privacysandbox.tools.core.model.Method
+import androidx.privacysandbox.tools.core.model.Parameter
 import androidx.privacysandbox.tools.core.model.ParsedApi
 import androidx.privacysandbox.tools.core.model.Type
 import androidx.privacysandbox.tools.core.model.Types
@@ -41,18 +43,33 @@
                     import androidx.privacysandbox.tools.PrivacySandboxService
                     import androidx.privacysandbox.tools.PrivacySandboxValue
                     @PrivacySandboxService
-                    interface MySdk
+                    interface MySdk {
+                        suspend fun doStuff(request: MySdkRequest): MySdkResponse
+                    }
                     @PrivacySandboxValue
                     data class MySdkRequest(val id: Int, val message: String)
                     @PrivacySandboxValue
-                    data class MySdkResponse(val magicNumber: Long, val isTrulyMagic: Boolean)
+                    data class MySdkResponse(
+                        val magicPayload: MagicPayload, val isTrulyMagic: Boolean)
+                    @PrivacySandboxValue
+                    data class MagicPayload(val magicNumber: Long)
                 """
         )
-        assertThat(parseSource(source)).isEqualTo(
+        assertThat(parseSources(source)).isEqualTo(
             ParsedApi(
                 services = mutableSetOf(
                     AnnotatedInterface(
                         type = Type(packageName = "com.mysdk", simpleName = "MySdk"),
+                        methods = listOf(
+                            Method(
+                                name = "doStuff",
+                                parameters = listOf(
+                                    Parameter("request", Type("com.mysdk", "MySdkRequest"))
+                                ),
+                                returnType = Type("com.mysdk", "MySdkResponse"),
+                                isSuspend = true
+                            )
+                        )
                     )
                 ),
                 values = setOf(
@@ -63,14 +80,22 @@
                             ValueProperty("message", Types.string),
                         )
                     ),
-
                     AnnotatedValue(
                         type = Type(packageName = "com.mysdk", simpleName = "MySdkResponse"),
                         properties = listOf(
-                            ValueProperty("magicNumber", Types.long),
+                            ValueProperty(
+                                "magicPayload",
+                                Type(packageName = "com.mysdk", simpleName = "MagicPayload")
+                            ),
                             ValueProperty("isTrulyMagic", Types.boolean),
                         )
-                    )
+                    ),
+                    AnnotatedValue(
+                        type = Type(packageName = "com.mysdk", simpleName = "MagicPayload"),
+                        properties = listOf(
+                            ValueProperty("magicNumber", Types.long),
+                        )
+                    ),
                 )
             )
         )
@@ -91,49 +116,71 @@
     @Test
     fun privateValue_fails() {
         checkSourceFails(annotatedValue("private data class MySdkRequest(val id: Int)"))
-            .containsExactlyErrors("Error in com.mysdk.MySdkRequest: " +
-                "annotated values should be public.")
+            .containsExactlyErrors(
+                "Error in com.mysdk.MySdkRequest: annotated values should be public."
+            )
     }
 
     @Test
     fun dataClassWithCompanionObject_fails() {
-        val dataClass = annotatedValue("""
+        val dataClass = annotatedValue(
+            """
             |data class MySdkRequest(val id: Int) {
             |   companion object {
             |       val someConstant = 12
             |   }
             |}
-        """.trimMargin())
+        """.trimMargin()
+        )
         checkSourceFails(dataClass)
-            .containsExactlyErrors("Error in com.mysdk.MySdkRequest: " +
-                "annotated values cannot declare companion objects.")
+            .containsExactlyErrors(
+                "Error in com.mysdk.MySdkRequest: annotated values cannot declare companion " +
+                    "objects."
+            )
     }
 
     @Test
     fun dataClassWithTypeParameters_fails() {
-        val dataClass = annotatedValue(
-            "data class MySdkRequest<T>(val id: Int, val data: T)")
+        val dataClass = annotatedValue("data class MySdkRequest<T>(val id: Int, val data: T)")
         checkSourceFails(dataClass)
-            .containsError("Error in com.mysdk.MySdkRequest: " +
-                "annotated values cannot declare type parameters (T).")
+            .containsError(
+                "Error in com.mysdk.MySdkRequest: annotated values cannot declare type " +
+                    "parameters (T)."
+            )
     }
 
     @Test
     fun dataClassWithMutableProperty_fails() {
         val dataClass = annotatedValue(
-            "data class MySdkRequest(val id: Int, var data: String)")
+            "data class MySdkRequest(val id: Int, var data: String)"
+        )
         checkSourceFails(dataClass)
-            .containsExactlyErrors("Error in com.mysdk.MySdkRequest.data: " +
-                "properties cannot be mutable.")
+            .containsExactlyErrors(
+                "Error in com.mysdk.MySdkRequest.data: properties cannot be mutable."
+            )
     }
 
     @Test
     fun dataClassWithNullableProperty_fails() {
         val dataClass = annotatedValue(
-            "data class MySdkRequest(val id: Int, val data: String?)")
+            "data class MySdkRequest(val id: Int, val data: String?)"
+        )
         checkSourceFails(dataClass)
-            .containsExactlyErrors("Error in com.mysdk.MySdkRequest.data: " +
-                "nullable types are not supported.")
+            .containsExactlyErrors(
+                "Error in com.mysdk.MySdkRequest.data: nullable types are not supported."
+            )
+    }
+
+    @Test
+    fun dataClassWithInvalidPropertyType_fails() {
+        val dataClass = annotatedValue(
+            "data class MySdkRequest(val foo: IntArray)"
+        )
+        checkSourceFails(dataClass)
+            .containsExactlyErrors(
+                "Error in com.mysdk.MySdkRequest.foo: only primitives and data classes " +
+                    "annotated with @PrivacySandboxValue are supported as properties."
+            )
     }
 
     private fun annotatedValue(declaration: String) = Source.kotlin(
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/util/KspTestRunner.kt b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/util/KspTestRunner.kt
index a042edf..c14e1fe 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/util/KspTestRunner.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/java/androidx/privacysandbox/tools/apicompiler/util/KspTestRunner.kt
@@ -34,13 +34,13 @@
 /**
  * Helper to run KSP processing functionality in tests.
  */
-fun parseSource(source: Source): ParsedApi {
+fun parseSources(vararg sources: Source): ParsedApi {
     val provider = CapturingSymbolProcessor.Provider()
     assertThat(
         compile(
             Files.createTempDirectory("test").toFile(),
             TestCompilationArguments(
-                sources = listOf(source),
+                sources = sources.toList(),
                 symbolProcessorProviders = listOf(provider),
             )
         )
diff --git a/privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/MySdkStubDelegate.kt b/privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/MySdkStubDelegate.kt
index a6d8695..ff15a02 100644
--- a/privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/MySdkStubDelegate.kt
+++ b/privacysandbox/tools/tools-apicompiler/src/test/test-data/testinterface/output/com/mysdk/MySdkStubDelegate.kt
@@ -2,7 +2,6 @@
 
 import kotlin.Int
 import kotlin.Unit
-import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.GlobalScope
 import kotlinx.coroutines.launch
diff --git a/privacysandbox/tools/tools-apigenerator/src/main/java/androidx/privacysandbox/tools/apigenerator/parser/ApiStubParser.kt b/privacysandbox/tools/tools-apigenerator/src/main/java/androidx/privacysandbox/tools/apigenerator/parser/ApiStubParser.kt
index 14ba33a..38142be 100644
--- a/privacysandbox/tools/tools-apigenerator/src/main/java/androidx/privacysandbox/tools/apigenerator/parser/ApiStubParser.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/main/java/androidx/privacysandbox/tools/apigenerator/parser/ApiStubParser.kt
@@ -22,6 +22,7 @@
 import androidx.privacysandbox.tools.core.model.Parameter
 import androidx.privacysandbox.tools.core.model.ParsedApi
 import androidx.privacysandbox.tools.core.model.Type
+import androidx.privacysandbox.tools.core.validator.ModelValidator
 import java.nio.file.Path
 import java.util.zip.ZipEntry
 import java.util.zip.ZipInputStream
@@ -55,10 +56,10 @@
             .filter { it.isPrivacySandboxService }
             .map(::parseClass)
             .toSet()
-        if (services.isEmpty()) throw IllegalArgumentException(
+        if (services.isEmpty()) throw PrivacySandboxParsingException(
             "Unable to find valid interfaces annotated with @PrivacySandboxService."
         )
-        return ParsedApi(services)
+        return ParsedApi(services).also(::validate)
     }
 
     private fun unzipClasses(stubClassPath: Path): List<ClassNode> =
@@ -86,14 +87,14 @@
         val type = kotlinMetadata.name.parsedType()
 
         if (!Flag.Class.IS_INTERFACE(kotlinMetadata.flags)) {
-            throw IllegalArgumentException(
+            throw PrivacySandboxParsingException(
                 "${type.qualifiedName} is not a Kotlin interface but it's annotated with " +
                     "@PrivacySandboxService."
             )
         }
 
         if (type.simpleName.contains('.')) {
-            throw IllegalArgumentException(
+            throw PrivacySandboxParsingException(
                 "${type.qualifiedName} is an inner interface so it can't be annotated with " +
                     "@PrivacySandboxService."
             )
@@ -108,7 +109,7 @@
     private fun parseKotlinMetadata(classNode: ClassNode): KmClass {
         val metadataValues =
             classNode.visibleAnnotationsWithType<Metadata>().firstOrNull()?.attributeMap
-                ?: throw IllegalArgumentException(
+                ?: throw PrivacySandboxParsingException(
                     "Missing Kotlin metadata annotation in ${classNode.name}. " +
                         "Is this a valid Kotlin class?"
                 )
@@ -128,7 +129,7 @@
 
         return when (val metadata = KotlinClassMetadata.read(header)) {
             is KotlinClassMetadata.Class -> metadata.toKmClass()
-            else -> throw IllegalArgumentException(
+            else -> throw PrivacySandboxParsingException(
                 "Unable to parse Kotlin metadata from ${classNode.name}. " +
                     "Is this a valid Kotlin class?"
             )
@@ -147,11 +148,21 @@
     private fun parseType(type: KmType): Type {
         return when (val classifier = type.classifier) {
             is KmClassifier.Class -> classifier.name.parsedType()
-            else -> throw IllegalArgumentException(
+            else -> throw PrivacySandboxParsingException(
                 "Unsupported type in API description: $type"
             )
         }
     }
+
+    private fun validate(api: ParsedApi) {
+        val validationResult = ModelValidator.validate(api)
+        if (validationResult.isFailure) {
+            throw PrivacySandboxParsingException(
+                "Invalid API descriptors:\n" +
+                    validationResult.errors.joinToString("\n")
+            )
+        }
+    }
 }
 
 internal val ClassNode.isPrivacySandboxService: Boolean get() {
@@ -183,3 +194,5 @@
         }
         return attributes
     }
+
+class PrivacySandboxParsingException(message: String) : Exception(message)
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/PrivacySandboxApiGeneratorTest.kt b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/PrivacySandboxApiGeneratorTest.kt
index 9bb5810..589c6cc 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/PrivacySandboxApiGeneratorTest.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/PrivacySandboxApiGeneratorTest.kt
@@ -20,6 +20,7 @@
 import androidx.privacysandbox.tools.testing.loadSourcesFromDirectory
 import androidx.privacysandbox.tools.testing.CompilationTestHelper.assertCompiles
 import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import java.io.File
 import java.nio.file.Files
 import kotlin.io.path.Path
@@ -59,8 +60,8 @@
 
         val outputSourceMap = outputSources.associateBy(Source::relativePath)
         for (expected in expectedSources) {
-            assertThat(outputSourceMap[expected.relativePath]?.contents)
-                .isEqualTo(expected.contents)
+            assertWithMessage("Contents of file ${expected.relativePath} don't match.")
+                .that(outputSourceMap[expected.relativePath]?.contents).isEqualTo(expected.contents)
         }
     }
 
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/parser/ApiStubParserTest.kt b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/parser/ApiStubParserTest.kt
index 79c8386..0b61a0a 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/parser/ApiStubParserTest.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/java/androidx/privacysandbox/tools/apigenerator/parser/ApiStubParserTest.kt
@@ -39,8 +39,8 @@
                     import androidx.privacysandbox.tools.PrivacySandboxService
                     @PrivacySandboxService
                     interface MySdk {
-                      suspend fun doSomething(magicNumber: Int, awesomeString: String)
-                      fun returnMagicNumber(): Int
+                      fun doSomething(magicNumber: Int, awesomeString: String)
+                      suspend fun returnMagicNumber(): Int
                     }
                 """
         )
@@ -59,13 +59,13 @@
                                 Parameter("awesomeString", Types.string)
                             ),
                             returnType = Types.unit,
-                            isSuspend = true,
+                            isSuspend = false,
                         ),
                         Method(
                             name = "returnMagicNumber",
                             parameters = listOf(),
                             returnType = Types.int,
-                            isSuspend = false,
+                            isSuspend = true,
                         )
                     )
                 )
@@ -141,7 +141,7 @@
                 """
         )
 
-        assertThrows<IllegalArgumentException> {
+        assertThrows<PrivacySandboxParsingException> {
             compileAndParseApi(source)
         }.hasMessageThat().contains(
             "Missing Kotlin metadata annotation in com/mysdk/MySdk. Is this a valid Kotlin class?"
@@ -159,7 +159,7 @@
                 """
         )
 
-        assertThrows<IllegalArgumentException> {
+        assertThrows<PrivacySandboxParsingException> {
             compileAndParseApi(source)
         }.hasMessageThat().contains(
             "com.mysdk.MySdk is not a Kotlin interface but it's annotated with " +
@@ -180,7 +180,7 @@
                 """
         )
 
-        assertThrows<IllegalArgumentException> {
+        assertThrows<PrivacySandboxParsingException> {
             compileAndParseApi(source)
         }.hasMessageThat().contains(
             "com.mysdk.OuterMySdk.InnerMySdk is an inner interface so it can't be annotated with " +
@@ -197,7 +197,7 @@
                 """
         )
 
-        assertThrows<IllegalArgumentException> {
+        assertThrows<PrivacySandboxParsingException> {
             compileAndParseApi(source)
         }.hasMessageThat().contains(
             "Unable to find valid interfaces annotated with @PrivacySandboxService."
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/input/com/mysdk/TestSandboxSdk.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/input/com/mysdk/TestSandboxSdk.kt
index 976194c..666c0c3 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/input/com/mysdk/TestSandboxSdk.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/input/com/mysdk/TestSandboxSdk.kt
@@ -4,19 +4,19 @@
 
 @PrivacySandboxService
 interface TestSandboxSdk {
-    fun echoBoolean(input: Boolean): Boolean
+    fun echoBoolean(input: Boolean)
 
-    fun echoInt(input: Int): Int
+    fun echoInt(input: Int)
 
-    fun echoLong(input: Long): Long
+    fun echoLong(input: Long)
 
-    fun echoFloat(input: Float): Float
+    fun echoFloat(input: Float)
 
-    fun echoDouble(input: Double): Double
+    fun echoDouble(input: Double)
 
-    fun echoChar(input: Char): Char
+    fun echoChar(input: Char)
 
-    fun echoString(input: String): String
+    fun echoString(input: String)
 
     fun receiveMultipleArguments(first: Int, second: String, third: Long)
 
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/output/com/mysdk/TestSandboxSdk.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/output/com/mysdk/TestSandboxSdk.kt
index c8fddcf..d0f8209 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/output/com/mysdk/TestSandboxSdk.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/output/com/mysdk/TestSandboxSdk.kt
@@ -7,19 +7,19 @@
     third: Long,
   ): Boolean
 
-  public fun echoBoolean(input: Boolean): Boolean
+  public fun echoBoolean(input: Boolean): Unit
 
-  public fun echoChar(input: Char): Char
+  public fun echoChar(input: Char): Unit
 
-  public fun echoDouble(input: Double): Double
+  public fun echoDouble(input: Double): Unit
 
-  public fun echoFloat(input: Float): Float
+  public fun echoFloat(input: Float): Unit
 
-  public fun echoInt(input: Int): Int
+  public fun echoInt(input: Int): Unit
 
-  public fun echoLong(input: Long): Long
+  public fun echoLong(input: Long): Unit
 
-  public fun echoString(input: String): String
+  public fun echoString(input: String): Unit
 
   public fun receiveAndReturnNothing(): Unit
 
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/output/com/mysdk/TestSandboxSdkFactory.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/output/com/mysdk/TestSandboxSdkFactory.kt
index 42359e9..33dd00f 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/output/com/mysdk/TestSandboxSdkFactory.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/output/com/mysdk/TestSandboxSdkFactory.kt
@@ -57,19 +57,33 @@
     }
   }
 
-  public override fun echoBoolean(input: Boolean) = remote.echoBoolean(input)
+  public override fun echoBoolean(input: Boolean): Unit {
+    remote.echoBoolean(input)
+  }
 
-  public override fun echoChar(input: Char) = remote.echoChar(input)
+  public override fun echoChar(input: Char): Unit {
+    remote.echoChar(input)
+  }
 
-  public override fun echoDouble(input: Double) = remote.echoDouble(input)
+  public override fun echoDouble(input: Double): Unit {
+    remote.echoDouble(input)
+  }
 
-  public override fun echoFloat(input: Float) = remote.echoFloat(input)
+  public override fun echoFloat(input: Float): Unit {
+    remote.echoFloat(input)
+  }
 
-  public override fun echoInt(input: Int) = remote.echoInt(input)
+  public override fun echoInt(input: Int): Unit {
+    remote.echoInt(input)
+  }
 
-  public override fun echoLong(input: Long) = remote.echoLong(input)
+  public override fun echoLong(input: Long): Unit {
+    remote.echoLong(input)
+  }
 
-  public override fun echoString(input: String) = remote.echoString(input)
+  public override fun echoString(input: String): Unit {
+    remote.echoString(input)
+  }
 
   public override fun receiveAndReturnNothing(): Unit {
     remote.receiveAndReturnNothing()
diff --git a/privacysandbox/tools/tools-core/build.gradle b/privacysandbox/tools/tools-core/build.gradle
index 8b050e3..d8d9102 100644
--- a/privacysandbox/tools/tools-core/build.gradle
+++ b/privacysandbox/tools/tools-core/build.gradle
@@ -28,9 +28,10 @@
     api(libs.kotlinStdlib)
     implementation(libs.kotlinPoet)
 
-    testImplementation(project(":room:room-compiler-processing-testing"))
     testImplementation(libs.junit)
     testImplementation(libs.truth)
+    testImplementation(project(":privacysandbox:tools:tools-testing"))
+    testImplementation(project(":room:room-compiler-processing-testing"))
     // Include android jar for compilation of generated sources.
     testImplementation(fileTree(
             dir: "${SdkHelperKt.getSdkPath(project)}/platforms/$SupportConfig.COMPILE_SDK_VERSION/",
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/AidlGenerator.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/AidlGenerator.kt
index 2db722c..6561f0f 100644
--- a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/AidlGenerator.kt
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/AidlGenerator.kt
@@ -16,11 +16,19 @@
 
 package androidx.privacysandbox.tools.core.generator
 
+import androidx.privacysandbox.tools.core.generator.poet.AidlFileSpec
+import androidx.privacysandbox.tools.core.generator.poet.AidlInterfaceSpec
+import androidx.privacysandbox.tools.core.generator.poet.AidlInterfaceSpec.Companion.aidlInterface
+import androidx.privacysandbox.tools.core.generator.poet.AidlMethodSpec
+import androidx.privacysandbox.tools.core.generator.poet.AidlParcelableSpec
+import androidx.privacysandbox.tools.core.generator.poet.AidlTypeSpec
 import androidx.privacysandbox.tools.core.model.AnnotatedInterface
+import androidx.privacysandbox.tools.core.model.AnnotatedValue
 import androidx.privacysandbox.tools.core.model.Method
 import androidx.privacysandbox.tools.core.model.Parameter
 import androidx.privacysandbox.tools.core.model.ParsedApi
 import androidx.privacysandbox.tools.core.model.Type
+import androidx.privacysandbox.tools.core.model.Types
 import androidx.privacysandbox.tools.core.model.getOnlyService
 import java.io.File
 import java.nio.file.Path
@@ -35,6 +43,8 @@
         check(api.services.count() <= 1) { "Multiple services are not supported." }
     }
 
+    private val valueMap = api.values.associateBy { it.type }
+
     companion object {
         fun generate(
             aidlCompiler: AidlCompiler,
@@ -58,8 +68,8 @@
             val aidlFile = getAidlFile(workingDir, it)
             aidlFile.parentFile.mkdirs()
             aidlFile.createNewFile()
-            aidlFile.writeText(it.fileContents)
-            GeneratedSource(it.packageName, it.interfaceName, aidlFile)
+            aidlFile.writeText(it.getFileContent())
+            GeneratedSource(it.type.packageName, it.type.simpleName, aidlFile)
         }
         return aidlSources
     }
@@ -81,87 +91,75 @@
         return javaSources
     }
 
-    private fun generateAidlContent(): List<InMemorySource> {
-        // TODO: implement better tooling to generate AIDL (AidlPoet).
+    private fun generateAidlContent(): List<AidlFileSpec> {
+        val values = api.values.map(::generateValue)
         val transactionCallbacks = generateTransactionCallbacks()
         val service =
-            InMemorySource(
-                packageName(),
-                api.getOnlyService().aidlName(),
-                generateAidlService(transactionCallbacks)
-            )
-        return transactionCallbacks + generateICancellationSignal() + service
+            aidlInterface(Type(packageName(), api.getOnlyService().aidlName()), oneway = false) {
+                api.getOnlyService().methods.forEach { addMethod(it) }
+            }
+        return transactionCallbacks + generateICancellationSignal() + service + values
     }
 
-    private fun generateAidlService(
-        transactionCallbacks: List<InMemorySource>
-    ): String {
-        val transactionCallbackImports =
-            transactionCallbacks.map {
-                "import ${it.packageName}.${it.interfaceName};"
-            }.sorted().joinToString(separator = "\n|")
-        val generatedMethods = api.getOnlyService().methods.map(::generateAidlMethod).sorted()
-            .joinToString("\n|    ")
-        return """
-                |package ${packageName()};
-                |$transactionCallbackImports
-                |interface ${api.getOnlyService().aidlName()} {
-                |    $generatedMethods
-                |}
-            """.trimMargin()
-    }
-
-    private fun generateAidlMethod(method: Method): String {
-        val parameters = buildList {
-            addAll(method.parameters.map(::generateAidlParameter))
+    private fun AidlInterfaceSpec.Builder.addMethod(method: Method) {
+        addMethod(method.name) {
+            method.parameters.forEach { addParameter(it) }
             if (method.isSuspend) {
-                add("${method.returnType.transactionCallbackName()} transactionCallback")
+                addParameter("transactionCallback", transactionCallback(method.returnType))
             }
         }
-        // TODO remove return type.
-        val returnType = if (method.isSuspend) "void" else method.returnType.toAidlType()
-        return "$returnType ${method.name}(${parameters.joinToString()});"
     }
 
-    private fun generateAidlParameter(parameter: Parameter) =
-        // TODO validate that parameter type is not Unit
-        "${parameter.type.toAidlType()} ${parameter.name}"
+    private fun AidlMethodSpec.Builder.addParameter(parameter: Parameter) {
+        check(parameter.type != Types.unit) {
+            "Void cannot be a parameter type."
+        }
+        addParameter(
+            parameter.name,
+            getAidlTypeDeclaration(parameter.type),
+            isIn = valueMap.containsKey(parameter.type)
+        )
+    }
 
-    private fun generateTransactionCallbacks(): List<InMemorySource> {
+    private fun generateTransactionCallbacks(): List<AidlFileSpec> {
         return api.getOnlyService().methods.filter(Method::isSuspend)
             .map(Method::returnType).toSet()
             .map { generateTransactionCallback(it) }
     }
 
-    private fun generateTransactionCallback(type: Type): InMemorySource {
-        val interfaceName = type.transactionCallbackName()
-        val onSuccessParameter = type.toAidlType().let { if (it == "void") "" else "$it result" }
-        return InMemorySource(
-            packageName = packageName(), interfaceName = interfaceName, fileContents = """
-                    package ${packageName()};
-                    import ${packageName()}.$cancellationSignalName;
-                    oneway interface $interfaceName {
-                        void onCancellable($cancellationSignalName cancellationSignal);
-                        void onSuccess($onSuccessParameter);
-                        void onFailure(int errorCode, String errorMessage);
-                    }
-                """.trimIndent()
-        )
+    private fun generateTransactionCallback(type: Type): AidlFileSpec {
+        return aidlInterface(Type(packageName(), type.transactionCallbackName())) {
+            addMethod("onCancellable") {
+                addParameter("cancellationSignal", cancellationSignalType())
+            }
+            addMethod("onSuccess") {
+                if (type != Types.unit) addParameter(Parameter("result", type))
+            }
+            addMethod("onFailure") {
+                addParameter("errorCode", primitive("int"))
+                addParameter("errorMessage", primitive("String"))
+            }
+        }
     }
 
-    private fun generateICancellationSignal() = InMemorySource(
-        packageName = packageName(), interfaceName = "$cancellationSignalName", fileContents = """
-                package ${packageName()};
-                oneway interface $cancellationSignalName {
-                    void cancel();
-                }
-            """.trimIndent()
-    )
+    private fun generateICancellationSignal() = aidlInterface(cancellationSignalType().innerType) {
+        addMethod("cancel")
+    }
 
-    private fun getAidlFile(rootPath: Path, aidlSource: InMemorySource) = Paths.get(
+    private fun generateValue(value: AnnotatedValue): AidlFileSpec {
+        val typesToImport =
+            value.properties.mapNotNull { valueMap[it.type]?.aidlType()?.innerType }.toSet()
+        return AidlParcelableSpec(type = value.aidlType().innerType,
+            typesToImport = typesToImport,
+            properties = value.properties.map {
+                "${getAidlTypeDeclaration(it.type)} ${it.name};"
+            })
+    }
+
+    private fun getAidlFile(rootPath: Path, aidlSource: AidlFileSpec) = Paths.get(
         rootPath.toString(),
-        *aidlSource.packageName.split(".").toTypedArray(),
-        aidlSource.interfaceName + ".aidl"
+        *aidlSource.type.packageName.split(".").toTypedArray(),
+        aidlSource.type.simpleName + ".aidl"
     ).toFile()
 
     private fun getJavaFileForAidlFile(aidlFile: File): File {
@@ -172,19 +170,31 @@
     }
 
     private fun packageName() = api.getOnlyService().type.packageName
+    private fun cancellationSignalType() = AidlTypeSpec(Type(packageName(), cancellationSignalName))
+    private fun transactionCallback(type: Type) =
+        AidlTypeSpec(Type(api.getOnlyService().type.packageName, type.transactionCallbackName()))
+
+    private fun getAidlTypeDeclaration(type: Type): AidlTypeSpec {
+        valueMap[type]?.let { return it.aidlType() }
+        return when (type.qualifiedName) {
+            Boolean::class.qualifiedName -> primitive("boolean")
+            Int::class.qualifiedName -> primitive("int")
+            Long::class.qualifiedName -> primitive("long")
+            Float::class.qualifiedName -> primitive("float")
+            Double::class.qualifiedName -> primitive("double")
+            String::class.qualifiedName -> primitive("String")
+            Char::class.qualifiedName -> primitive("char")
+            // TODO: AIDL doesn't support short, make sure it's handled correctly.
+            Short::class.qualifiedName -> primitive("int")
+            Unit::class.qualifiedName -> primitive("void")
+            else -> throw IllegalArgumentException(
+                "Unsupported type conversion ${type.qualifiedName}"
+            )
+        }
+    }
 }
 
-data class InMemorySource(
-    val packageName: String,
-    val interfaceName: String,
-    val fileContents: String
-)
-
-data class GeneratedSource(
-    val packageName: String,
-    val interfaceName: String,
-    val file: File
-)
+data class GeneratedSource(val packageName: String, val interfaceName: String, val file: File)
 
 internal fun File.ensureDirectory() {
     check(exists()) {
@@ -199,16 +209,7 @@
 
 fun Type.transactionCallbackName() = "I${simpleName}TransactionCallback"
 
-internal fun Type.toAidlType() = when (qualifiedName) {
-    Boolean::class.qualifiedName -> "boolean"
-    Int::class.qualifiedName -> "int"
-    Long::class.qualifiedName -> "long"
-    Float::class.qualifiedName -> "float"
-    Double::class.qualifiedName -> "double"
-    String::class.qualifiedName -> "String"
-    Char::class.qualifiedName -> "char"
-    // TODO: AIDL doesn't support short, make sure it's handled correctly.
-    Short::class.qualifiedName -> "int"
-    Unit::class.qualifiedName -> "void"
-    else -> throw IllegalArgumentException("Unsupported type conversion ${this.qualifiedName}")
-}
+internal fun AnnotatedValue.aidlType() =
+    AidlTypeSpec(Type(type.packageName, "Parcelable${type.simpleName}"))
+
+internal fun primitive(name: String) = AidlTypeSpec(Type("", name), requiresImport = false)
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/KotlinPoetSpecs.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/KotlinPoetSpecs.kt
index b1edb88..b4825d2 100644
--- a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/KotlinPoetSpecs.kt
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/KotlinPoetSpecs.kt
@@ -23,6 +23,7 @@
 import com.squareup.kotlinpoet.FileSpec
 import com.squareup.kotlinpoet.FunSpec
 import com.squareup.kotlinpoet.KModifier
+import com.squareup.kotlinpoet.MemberName
 import com.squareup.kotlinpoet.ParameterSpec
 import com.squareup.kotlinpoet.PropertySpec
 import com.squareup.kotlinpoet.TypeName
@@ -96,3 +97,9 @@
     block()
     endControlFlow()
 }
+
+object SpecNames {
+    val dispatchersMainClass = ClassName("kotlinx.coroutines", "Dispatchers", "Main")
+    val globalScopeClass = ClassName("kotlinx.coroutines", "GlobalScope")
+    val launchMethod = MemberName("kotlinx.coroutines", "launch", isExtension = true)
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/poet/AidlFileSpec.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/poet/AidlFileSpec.kt
new file mode 100644
index 0000000..6d61221
--- /dev/null
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/poet/AidlFileSpec.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.privacysandbox.tools.core.generator.poet
+
+import androidx.privacysandbox.tools.core.model.Type
+
+/** Describes the contents of an AIDL file. */
+internal interface AidlFileSpec {
+    val type: Type
+    val typesToImport: Set<Type>
+    val innerContent: String
+
+    fun getFileContent(): String = buildList {
+        add("package ${type.packageName};")
+
+        if (typesToImport.isNotEmpty()) {
+            add(typesToImport.map {
+                "import ${it.qualifiedName};"
+            }.sorted().joinToString(separator = "\n"))
+        }
+
+        add(innerContent)
+    }.joinToString(separator = "\n\n")
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/poet/AidlInterfaceSpec.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/poet/AidlInterfaceSpec.kt
new file mode 100644
index 0000000..7ea20e9
--- /dev/null
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/poet/AidlInterfaceSpec.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.privacysandbox.tools.core.generator.poet
+
+import androidx.privacysandbox.tools.core.model.Type
+
+/** AIDL file with a single interface. */
+internal data class AidlInterfaceSpec(
+    override val type: Type,
+    val methods: List<AidlMethodSpec>,
+    val oneway: Boolean = true,
+) : AidlFileSpec {
+    companion object {
+        fun aidlInterface(
+            type: Type,
+            oneway: Boolean = true,
+            block: Builder.() -> Unit = {}
+        ): AidlInterfaceSpec {
+            return Builder(type, oneway).also(block).build()
+        }
+    }
+
+    override val typesToImport: Set<Type>
+        get() {
+            return methods.flatMap { method ->
+                method.parameters.map { it.type }.filter { it.requiresImport }.map { it.innerType }
+            }.toSet()
+        }
+
+    override val innerContent: String
+        get() {
+            val modifiers = if (oneway) "oneway " else ""
+            val body = methods.map { it.toString() }.sorted().joinToString("\n|    ")
+            return """
+                |${modifiers}interface ${type.simpleName} {
+                |    $body
+                |}
+            """.trimMargin()
+        }
+
+    class Builder(val type: Type, val oneway: Boolean = true) {
+        val methods = mutableListOf<AidlMethodSpec>()
+
+        fun addMethod(method: AidlMethodSpec) {
+            methods.add(method)
+        }
+
+        fun addMethod(name: String, block: AidlMethodSpec.Builder.() -> Unit = {}) {
+            addMethod(AidlMethodSpec.Builder(name).also(block).build())
+        }
+
+        fun build() = AidlInterfaceSpec(type, methods, oneway)
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/poet/AidlMethodSpec.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/poet/AidlMethodSpec.kt
new file mode 100644
index 0000000..db27169
--- /dev/null
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/poet/AidlMethodSpec.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.privacysandbox.tools.core.generator.poet
+
+internal data class AidlMethodSpec(val name: String, val parameters: List<AidlParameterSpec>) {
+    override fun toString() = "void $name(${parameters.joinToString(", ")});"
+
+    class Builder(val name: String) {
+        val parameters = mutableListOf<AidlParameterSpec>()
+
+        fun addParameter(parameter: AidlParameterSpec) {
+            parameters.add(parameter)
+        }
+
+        fun addParameter(name: String, type: AidlTypeSpec, isIn: Boolean = false) {
+            addParameter(AidlParameterSpec(name, type, isIn))
+        }
+
+        fun build() = AidlMethodSpec(name, parameters)
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/poet/AidlParameterSpec.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/poet/AidlParameterSpec.kt
new file mode 100644
index 0000000..894c7a3
--- /dev/null
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/poet/AidlParameterSpec.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.privacysandbox.tools.core.generator.poet
+
+internal data class AidlParameterSpec(
+    val name: String,
+    val type: AidlTypeSpec,
+    val isIn: Boolean = false
+) {
+    companion object {
+        fun parameter(name: String, vararg parameters: AidlParameterSpec) =
+            AidlMethodSpec(name, parameters.toList())
+    }
+
+    override fun toString() = "${if (isIn) "in " else ""}$type $name"
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/poet/AidlParcelableSpec.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/poet/AidlParcelableSpec.kt
new file mode 100644
index 0000000..9c7cf46
--- /dev/null
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/poet/AidlParcelableSpec.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.privacysandbox.tools.core.generator.poet
+
+import androidx.privacysandbox.tools.core.model.Type
+
+/** AIDL file with a single parcelable defined. */
+internal data class AidlParcelableSpec(
+    override val type: Type,
+    val properties: List<String>,
+    override val typesToImport: Set<Type> = emptySet(),
+) : AidlFileSpec {
+    override val innerContent: String
+        get() {
+            val body = properties.sorted().joinToString("\n|    ")
+            return """
+                |parcelable ${type.simpleName} {
+                |    $body
+                |}
+                """.trimMargin()
+        }
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/poet/AidlTypeSpec.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/poet/AidlTypeSpec.kt
new file mode 100644
index 0000000..99433e0
--- /dev/null
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/poet/AidlTypeSpec.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.privacysandbox.tools.core.generator.poet
+
+import androidx.privacysandbox.tools.core.model.Type
+
+internal data class AidlTypeSpec(val innerType: Type, val requiresImport: Boolean = true) {
+    override fun toString() = innerType.simpleName
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/model/ParsedApi.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/model/ParsedApi.kt
index ed89d72..4b26982 100644
--- a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/model/ParsedApi.kt
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/model/ParsedApi.kt
@@ -19,5 +19,6 @@
 /** Result of parsing a full developer-defined API for an SDK. */
 data class ParsedApi(
     val services: Set<AnnotatedInterface>,
-    val values: Set<AnnotatedValue> = setOf(),
-)
\ No newline at end of file
+    val values: Set<AnnotatedValue> = emptySet(),
+    val callbacks: Set<AnnotatedInterface> = emptySet(),
+)
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/validator/ModelValidator.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/validator/ModelValidator.kt
index aa8a10f..131a8f2 100644
--- a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/validator/ModelValidator.kt
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/validator/ModelValidator.kt
@@ -16,6 +16,8 @@
 
 package androidx.privacysandbox.tools.core.validator
 
+import androidx.privacysandbox.tools.core.model.AnnotatedInterface
+import androidx.privacysandbox.tools.core.model.AnnotatedValue
 import androidx.privacysandbox.tools.core.model.ParsedApi
 import androidx.privacysandbox.tools.core.model.Types
 
@@ -30,6 +32,9 @@
         validateSingleService()
         validateNonSuspendFunctionsReturnUnit()
         validateParameterAndReturnValueTypes()
+        validateValuePropertyTypes()
+        callbackMethodsAreFireAndForget()
+        callbacksDontReceiveCallbacks()
         return ValidationResult(errors)
     }
 
@@ -56,23 +61,75 @@
     }
 
     private fun validateParameterAndReturnValueTypes() {
-        // TODO: allow data classes.
+        val allowedParameterTypes =
+            (api.values.map(AnnotatedValue::type) +
+                api.callbacks.map(AnnotatedInterface::type) +
+                Types.primitiveTypes).toSet()
+        val allowedReturnValueTypes =
+            (api.values.map(AnnotatedValue::type) + Types.primitiveTypes).toSet()
         for (service in api.services) {
             for (method in service.methods) {
-                val isAnyTypeInvalid = (method.parameters.map { it.type } + method.returnType).any {
-                    !Types.primitiveTypes.contains(it)
-                }
-                if (isAnyTypeInvalid) {
+                if (method.parameters.any { !allowedParameterTypes.contains(it.type) }) {
                     errors.add(
                         "Error in ${service.type.qualifiedName}.${method.name}: " +
-                            "only primitive types are supported."
+                            "only primitives, data classes annotated with @PrivacySandboxValue " +
+                            "and interfaces annotated with @PrivacySandboxCallback are supported " +
+                            "as parameter types."
+                    )
+                }
+                if (!allowedReturnValueTypes.contains(method.returnType)) {
+                    errors.add(
+                        "Error in ${service.type.qualifiedName}.${method.name}: " +
+                            "only primitives and data classes annotated with " +
+                            "@PrivacySandboxValue are supported as return types."
                     )
                 }
             }
         }
     }
 
-    // TODO: check that callback methods are fire-and-forget
+    private fun validateValuePropertyTypes() {
+        val allowedValuePropertyTypes =
+            (api.values.map(AnnotatedValue::type) + Types.primitiveTypes).toSet()
+        for (value in api.values) {
+            for (property in value.properties) {
+                if (!allowedValuePropertyTypes.contains(property.type)) {
+                    errors.add(
+                        "Error in ${value.type.qualifiedName}.${property.name}: " +
+                            "only primitives and data classes annotated with " +
+                            "@PrivacySandboxValue are supported as properties."
+                    )
+                }
+            }
+        }
+    }
+
+    private fun callbackMethodsAreFireAndForget() {
+        for (callback in api.callbacks) {
+            for (method in callback.methods) {
+                if (method.returnType != Types.unit || method.isSuspend) {
+                    errors.add(
+                        "Error in ${callback.type.qualifiedName}.${method.name}: callback " +
+                            "methods should be non-suspending and have no return values."
+                    )
+                }
+            }
+        }
+    }
+
+    private fun callbacksDontReceiveCallbacks() {
+        val callbackTypes = api.callbacks.map { it.type }.toSet()
+        for (callback in api.callbacks) {
+            for (method in callback.methods) {
+                if (method.parameters.any { callbackTypes.contains(it.type) }) {
+                    errors.add(
+                        "Error in ${callback.type.qualifiedName}.${method.name}: callback " +
+                            "methods cannot receive other callbacks as arguments."
+                    )
+                }
+            }
+        }
+    }
 }
 
 data class ValidationResult(val errors: List<String>) {
diff --git a/privacysandbox/tools/tools-core/src/test/java/androidx/privacysandbox/tools/core/generator/AidlGeneratorTest.kt b/privacysandbox/tools/tools-core/src/test/java/androidx/privacysandbox/tools/core/generator/AidlGeneratorTest.kt
deleted file mode 100644
index 2515bba2..0000000
--- a/privacysandbox/tools/tools-core/src/test/java/androidx/privacysandbox/tools/core/generator/AidlGeneratorTest.kt
+++ /dev/null
@@ -1,185 +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.privacysandbox.tools.core.generator
-
-import androidx.privacysandbox.tools.core.model.AnnotatedInterface
-import androidx.privacysandbox.tools.core.model.Method
-import androidx.privacysandbox.tools.core.model.Parameter
-import androidx.privacysandbox.tools.core.model.ParsedApi
-import androidx.privacysandbox.tools.core.model.Type
-import androidx.privacysandbox.tools.core.model.Types
-import androidx.room.compiler.processing.util.Source
-import androidx.room.compiler.processing.util.compiler.TestCompilationArguments
-import androidx.room.compiler.processing.util.compiler.compile
-import com.google.common.truth.Truth.assertThat
-import com.google.common.truth.Truth.assertWithMessage
-import java.nio.file.Files.createTempDirectory
-import javax.tools.Diagnostic
-import kotlin.io.path.Path
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-
-@RunWith(JUnit4::class)
-class AidlGeneratorTest {
-    @Test
-    fun generate() {
-        val api = ParsedApi(
-            services = mutableSetOf(
-                AnnotatedInterface(
-                    type = Type(packageName = "com.mysdk", simpleName = "MySdk"),
-                    methods = listOf(
-                        Method(
-                            name = "suspendMethodWithReturnValue",
-                            parameters = listOf(
-                                Parameter(
-                                    name = "a",
-                                    type = Types.boolean,
-                                ),
-                                Parameter(
-                                    name = "b",
-                                    type = Types.int,
-                                ),
-                                Parameter(
-                                    name = "c",
-                                    type = Types.long,
-                                ),
-                                Parameter(
-                                    name = "d",
-                                    type = Types.float,
-                                ),
-                                Parameter(
-                                    name = "e",
-                                    type = Types.double,
-                                ),
-                                Parameter(
-                                    name = "f",
-                                    type = Types.char,
-                                ),
-                                Parameter(
-                                    name = "g",
-                                    type = Types.int,
-                                )
-                            ),
-                            returnType = Types.string,
-                            isSuspend = true,
-                        ),
-                        Method(
-                            name = "suspendMethodWithoutReturnValue",
-                            parameters = listOf(),
-                            returnType = Types.unit,
-                            isSuspend = true,
-                        ),
-                        Method(
-                            name = "methodWithReturnValue",
-                            parameters = listOf(),
-                            returnType = Types.int,
-                            isSuspend = false,
-                        ),
-                        Method(
-                            name = "methodWithoutReturnValue",
-                            parameters = listOf(),
-                            returnType = Types.unit,
-                            isSuspend = false,
-                        )
-                    )
-                )
-            )
-        )
-        val aidlPath = System.getProperty("aidl_compiler_path")?.let(::Path)
-            ?: throw IllegalArgumentException("aidl_compiler_path flag not set.")
-        val tmpDir = createTempDirectory("test")
-        val aidlCompiler = AidlCompiler(aidlPath)
-
-        val javaGeneratedSources = AidlGenerator.generate(aidlCompiler, api, tmpDir)
-
-        // Check expected java sources were generated.
-        assertThat(javaGeneratedSources.map { it.packageName to it.interfaceName })
-            .containsExactly(
-                "com.mysdk" to "IMySdk",
-                "com.mysdk" to "IStringTransactionCallback",
-                "com.mysdk" to "IUnitTransactionCallback",
-                "com.mysdk" to "ICancellationSignal",
-            )
-
-        // Check the contents of the AIDL generated files.
-        val aidlGeneratedFiles = tmpDir.toFile().walk().filter { it.extension == "aidl" }
-            .map { it.name to it.readText() }.toList()
-        assertThat(aidlGeneratedFiles).containsExactly(
-            "IMySdk.aidl" to """
-                package com.mysdk;
-                import com.mysdk.IStringTransactionCallback;
-                import com.mysdk.IUnitTransactionCallback;
-                interface IMySdk {
-                    int methodWithReturnValue();
-                    void methodWithoutReturnValue();
-                    void suspendMethodWithReturnValue(boolean a, int b, long c, float d, double e, char f, int g, IStringTransactionCallback transactionCallback);
-                    void suspendMethodWithoutReturnValue(IUnitTransactionCallback transactionCallback);
-                }
-            """.trimIndent(),
-            "ICancellationSignal.aidl" to """
-                package com.mysdk;
-                oneway interface ICancellationSignal {
-                    void cancel();
-                }
-            """.trimIndent(),
-            "IStringTransactionCallback.aidl" to """
-                package com.mysdk;
-                import com.mysdk.ICancellationSignal;
-                oneway interface IStringTransactionCallback {
-                    void onCancellable(ICancellationSignal cancellationSignal);
-                    void onSuccess(String result);
-                    void onFailure(int errorCode, String errorMessage);
-                }
-            """.trimIndent(),
-            "IUnitTransactionCallback.aidl" to """
-                package com.mysdk;
-                import com.mysdk.ICancellationSignal;
-                oneway interface IUnitTransactionCallback {
-                    void onCancellable(ICancellationSignal cancellationSignal);
-                    void onSuccess();
-                    void onFailure(int errorCode, String errorMessage);
-                }
-            """.trimIndent(),
-        )
-
-        // Check that the Java generated sources compile.
-        ensureCompiles(
-            javaGeneratedSources.map {
-                Source.java(
-                    "${it.packageName.replace('.', '/')}/${it.interfaceName}",
-                    it.file.readText()
-                )
-            }.toList()
-        )
-    }
-
-    private fun ensureCompiles(sources: List<Source>) {
-        val result = compile(
-            createTempDirectory("compile").toFile(),
-            TestCompilationArguments(
-                sources = sources,
-            )
-        )
-        assertWithMessage(
-            "Compilation of java files generated from AIDL failed with errors: " +
-                "${result.diagnostics[Diagnostic.Kind.ERROR]?.joinToString("\n") { it.msg }}"
-        ).that(
-            result.success
-        ).isTrue()
-    }
-}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-core/src/test/java/androidx/privacysandbox/tools/core/generator/AidlServiceGeneratorTest.kt b/privacysandbox/tools/tools-core/src/test/java/androidx/privacysandbox/tools/core/generator/AidlServiceGeneratorTest.kt
new file mode 100644
index 0000000..1921b17
--- /dev/null
+++ b/privacysandbox/tools/tools-core/src/test/java/androidx/privacysandbox/tools/core/generator/AidlServiceGeneratorTest.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.privacysandbox.tools.core.generator
+
+import androidx.privacysandbox.tools.core.model.AnnotatedInterface
+import androidx.privacysandbox.tools.core.model.Method
+import androidx.privacysandbox.tools.core.model.Parameter
+import androidx.privacysandbox.tools.core.model.ParsedApi
+import androidx.privacysandbox.tools.core.model.Type
+import androidx.privacysandbox.tools.core.model.Types
+import androidx.privacysandbox.tools.testing.loadFilesFromDirectory
+import com.google.common.truth.Truth.assertThat
+import java.io.File
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class AidlServiceGeneratorTest {
+    @Test
+    fun generate() {
+        val api = ParsedApi(
+            services = setOf(
+                AnnotatedInterface(
+                    type = Type(packageName = "com.mysdk", simpleName = "MySdk"),
+                    methods = listOf(
+                        Method(
+                            name = "suspendMethodWithReturnValue",
+                            parameters = listOf(
+                                Parameter("a", Types.boolean),
+                                Parameter("b", Types.int),
+                                Parameter("c", Types.long),
+                                Parameter("d", Types.float),
+                                Parameter("e", Types.double),
+                                Parameter("f", Types.char),
+                                Parameter("g", Types.int)
+                            ),
+                            returnType = Types.string,
+                            isSuspend = true,
+                        ),
+                        Method(
+                            name = "suspendMethodWithoutReturnValue",
+                            parameters = listOf(),
+                            returnType = Types.unit,
+                            isSuspend = true,
+                        ),
+                        Method(
+                            name = "methodWithoutReturnValue",
+                            parameters = listOf(),
+                            returnType = Types.unit,
+                            isSuspend = false,
+                        )
+                    )
+                )
+            )
+        )
+
+        val (aidlGeneratedSources, javaGeneratedSources) = AidlTestHelper.runGenerator(api)
+        assertThat(javaGeneratedSources.map { it.packageName to it.interfaceName })
+            .containsExactly(
+                "com.mysdk" to "IMySdk",
+                "com.mysdk" to "IStringTransactionCallback",
+                "com.mysdk" to "IUnitTransactionCallback",
+                "com.mysdk" to "ICancellationSignal",
+            )
+
+        val outputTestDataDir = File("src/test/test-data/aidlservicegeneratortest/output")
+        val expectedSources = loadFilesFromDirectory(outputTestDataDir)
+
+        assertThat(aidlGeneratedSources.map { it.relativePath to it.content })
+            .containsExactlyElementsIn(expectedSources)
+    }
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-core/src/test/java/androidx/privacysandbox/tools/core/generator/AidlTestHelper.kt b/privacysandbox/tools/tools-core/src/test/java/androidx/privacysandbox/tools/core/generator/AidlTestHelper.kt
new file mode 100644
index 0000000..5887b99
--- /dev/null
+++ b/privacysandbox/tools/tools-core/src/test/java/androidx/privacysandbox/tools/core/generator/AidlTestHelper.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.privacysandbox.tools.core.generator
+
+import androidx.privacysandbox.tools.core.model.ParsedApi
+import androidx.privacysandbox.tools.core.validator.ModelValidator
+import androidx.privacysandbox.tools.testing.CompilationTestHelper.assertCompiles
+import androidx.room.compiler.processing.util.Source
+import com.google.common.truth.Truth.assertWithMessage
+import java.nio.file.Files
+import kotlin.io.path.Path
+
+internal object AidlTestHelper {
+    fun runGenerator(api: ParsedApi): AidlGenerationOutput {
+        ModelValidator.validate(api).also {
+            assertWithMessage("Tried to generate AIDL code for invalid interface:\n" +
+                it.errors.joinToString("\n")
+            ).that(it.isSuccess).isTrue()
+        }
+        val tmpDir = Files.createTempDirectory("aidlGenerationTest")
+        val aidlPath = System.getProperty("aidl_compiler_path")?.let(::Path)
+            ?: throw IllegalArgumentException("aidl_compiler_path flag not set.")
+        val aidlCompiler = AidlCompiler(aidlPath)
+
+        val javaGeneratedSources = AidlGenerator.generate(aidlCompiler, api, tmpDir)
+        assertCompiles(
+            javaGeneratedSources.map {
+                Source.java(
+                    "${it.packageName.replace('.', '/')}/${it.interfaceName}",
+                    it.file.readText()
+                )
+            }.toList()
+        )
+
+        val aidlGeneratedSources = tmpDir.toFile().walk().filter { it.extension == "aidl" }
+            .map { AidlSource(tmpDir.relativize(it.toPath()).toString(), it.readText()) }.toList()
+        return AidlGenerationOutput(aidlGeneratedSources, javaGeneratedSources)
+    }
+}
+
+data class AidlSource(val relativePath: String, val content: String)
+data class AidlGenerationOutput(
+    val aidlSources: List<AidlSource>,
+    val javaSources: List<GeneratedSource>,
+)
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-core/src/test/java/androidx/privacysandbox/tools/core/generator/AidlValueGeneratorTest.kt b/privacysandbox/tools/tools-core/src/test/java/androidx/privacysandbox/tools/core/generator/AidlValueGeneratorTest.kt
new file mode 100644
index 0000000..bca3e3b
--- /dev/null
+++ b/privacysandbox/tools/tools-core/src/test/java/androidx/privacysandbox/tools/core/generator/AidlValueGeneratorTest.kt
@@ -0,0 +1,107 @@
+/*
+ * 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.privacysandbox.tools.core.generator
+
+import androidx.privacysandbox.tools.core.model.AnnotatedInterface
+import androidx.privacysandbox.tools.core.model.AnnotatedValue
+import androidx.privacysandbox.tools.core.model.Method
+import androidx.privacysandbox.tools.core.model.Parameter
+import androidx.privacysandbox.tools.core.model.ParsedApi
+import androidx.privacysandbox.tools.core.model.Type
+import androidx.privacysandbox.tools.core.model.Types
+import androidx.privacysandbox.tools.core.model.ValueProperty
+import androidx.privacysandbox.tools.testing.loadFilesFromDirectory
+import com.google.common.truth.Truth.assertThat
+import java.io.File
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class AidlValueGeneratorTest {
+    @Test
+    fun generate() {
+        val innerValue = AnnotatedValue(
+            Type(packageName = "com.mysdk", simpleName = "InnerValue"),
+            listOf(
+                ValueProperty("intProperty", Types.int),
+                ValueProperty("booleanProperty", Types.boolean),
+                ValueProperty("longProperty", Types.long),
+            )
+        )
+        val outerValue = AnnotatedValue(
+            Type(packageName = "com.mysdk", simpleName = "OuterValue"),
+            listOf(
+                ValueProperty("innerValue", innerValue.type),
+                ValueProperty("anotherInnerValue", innerValue.type),
+            )
+        )
+
+        val api = ParsedApi(
+            services = setOf(
+                AnnotatedInterface(
+                    type = Type(packageName = "com.mysdk", simpleName = "MySdk"),
+                    methods = listOf(
+                        Method(
+                            name = "suspendMethodThatReturnsValue",
+                            parameters = listOf(),
+                            returnType = outerValue.type,
+                            isSuspend = true,
+                        ),
+                        Method(
+                            name = "suspendMethodReceivingValue",
+                            parameters = listOf(
+                                Parameter("inputValue", outerValue.type)
+                            ),
+                            returnType = Types.unit,
+                            isSuspend = true,
+                        ),
+                        Method(
+                            name = "methodReceivingValue",
+                            parameters = listOf(
+                                Parameter(
+                                    name = "value",
+                                    type = outerValue.type,
+                                ),
+                            ),
+                            returnType = Types.unit,
+                            isSuspend = false,
+                        )
+                    )
+                )
+            ),
+            values = setOf(innerValue, outerValue)
+        )
+
+        val (aidlGeneratedSources, javaGeneratedSources) = AidlTestHelper.runGenerator(api)
+        assertThat(javaGeneratedSources.map { it.packageName to it.interfaceName })
+            .containsExactly(
+                "com.mysdk" to "IMySdk",
+                "com.mysdk" to "ParcelableOuterValue",
+                "com.mysdk" to "ParcelableInnerValue",
+                "com.mysdk" to "IUnitTransactionCallback",
+                "com.mysdk" to "IOuterValueTransactionCallback",
+                "com.mysdk" to "ICancellationSignal",
+            )
+
+        val outputTestDataDir = File("src/test/test-data/aidlvaluegeneratortest/output")
+        val expectedSources = loadFilesFromDirectory(outputTestDataDir)
+
+        assertThat(aidlGeneratedSources.map { it.relativePath to it.content })
+            .containsExactlyElementsIn(expectedSources)
+    }
+}
diff --git a/privacysandbox/tools/tools-core/src/test/java/androidx/privacysandbox/tools/core/validator/ModelValidatorTest.kt b/privacysandbox/tools/tools-core/src/test/java/androidx/privacysandbox/tools/core/validator/ModelValidatorTest.kt
index 0683364..604cb6f 100644
--- a/privacysandbox/tools/tools-core/src/test/java/androidx/privacysandbox/tools/core/validator/ModelValidatorTest.kt
+++ b/privacysandbox/tools/tools-core/src/test/java/androidx/privacysandbox/tools/core/validator/ModelValidatorTest.kt
@@ -17,11 +17,13 @@
 package androidx.privacysandbox.tools.core.validator
 
 import androidx.privacysandbox.tools.core.model.AnnotatedInterface
+import androidx.privacysandbox.tools.core.model.AnnotatedValue
 import androidx.privacysandbox.tools.core.model.Method
 import androidx.privacysandbox.tools.core.model.Parameter
 import androidx.privacysandbox.tools.core.model.ParsedApi
 import androidx.privacysandbox.tools.core.model.Type
 import androidx.privacysandbox.tools.core.model.Types
+import androidx.privacysandbox.tools.core.model.ValueProperty
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -44,21 +46,61 @@
                                     type = Types.int
                                 ),
                                 Parameter(
-                                    name = "y",
-                                    type = Types.int
-                                )
+                                    name = "foo",
+                                    type = Type(packageName = "com.mysdk", simpleName = "Foo")
+                                ),
+                                Parameter(
+                                    name = "callback",
+                                    type = Type(
+                                        packageName = "com.mysdk",
+                                        simpleName = "MySdkCallback"
+                                    )
+                                ),
                             ),
                             returnType = Types.string,
                             isSuspend = true,
                         ),
                         Method(
-                            name = "doMoreStuff",
+                            name = "fireAndForget",
                             parameters = listOf(),
                             returnType = Types.unit,
                             isSuspend = false,
                         )
                     )
                 )
+            ),
+            values = setOf(
+                AnnotatedValue(
+                    type = Type(packageName = "com.mysdk", simpleName = "Foo"),
+                    properties = listOf(
+                        ValueProperty(
+                            name = "bar",
+                            type = Type(packageName = "com.mysdk", simpleName = "Bar"),
+                        )
+                    ),
+                ),
+                AnnotatedValue(
+                    type = Type(packageName = "com.mysdk", simpleName = "Bar"),
+                    properties = emptyList(),
+                )
+            ),
+            callbacks = setOf(
+                AnnotatedInterface(
+                    type = Type(packageName = "com.mysdk", simpleName = "MySdkCallback"),
+                    methods = listOf(
+                        Method(
+                            name = "onComplete",
+                            parameters = listOf(
+                                Parameter(
+                                    name = "result",
+                                    type = Types.int
+                                ),
+                            ),
+                            returnType = Types.unit,
+                            isSuspend = false,
+                        ),
+                    )
+                )
             )
         )
         assertThat(ModelValidator.validate(api).isSuccess).isTrue()
@@ -155,8 +197,100 @@
         val validationResult = ModelValidator.validate(api)
         assertThat(validationResult.isFailure).isTrue()
         assertThat(validationResult.errors).containsExactly(
-            "Error in com.mysdk.MySdk.returnFoo: only primitive types are supported.",
-            "Error in com.mysdk.MySdk.receiveFoo: only primitive types are supported."
+            "Error in com.mysdk.MySdk.returnFoo: only primitives and data classes annotated with " +
+                "@PrivacySandboxValue are supported as return types.",
+            "Error in com.mysdk.MySdk.receiveFoo: only primitives, data classes annotated with " +
+                "@PrivacySandboxValue and interfaces annotated with @PrivacySandboxCallback are " +
+                "supported as parameter types."
+        )
+    }
+
+    @Test
+    fun valueWithIllegalProperty_throws() {
+        val api = ParsedApi(
+            services = setOf(
+                AnnotatedInterface(type = Type(packageName = "com.mysdk", simpleName = "MySdk")),
+            ),
+            values = setOf(
+                AnnotatedValue(
+                    type = Type(packageName = "com.mysdk", simpleName = "Foo"),
+                    properties = listOf(
+                        ValueProperty("bar", Type("com.mysdk", "Bar"))
+                    )
+                )
+            )
+        )
+        val validationResult = ModelValidator.validate(api)
+        assertThat(validationResult.isFailure).isTrue()
+        assertThat(validationResult.errors).containsExactly(
+            "Error in com.mysdk.Foo.bar: only primitives and data classes annotated with " +
+                "@PrivacySandboxValue are supported as properties."
+        )
+    }
+
+    @Test
+    fun callbackWithNonFireAndForgetMethod_throws() {
+        val api = ParsedApi(
+            services = setOf(
+                AnnotatedInterface(type = Type(packageName = "com.mysdk", simpleName = "MySdk")),
+            ),
+            callbacks = setOf(
+                AnnotatedInterface(
+                    type = Type(packageName = "com.mysdk", simpleName = "MySdkCallback"),
+                    methods = listOf(
+                        Method(
+                            name = "suspendMethod",
+                            parameters = listOf(),
+                            returnType = Types.unit,
+                            isSuspend = true,
+                        ),
+                        Method(
+                            name = "methodWithReturnValue",
+                            parameters = listOf(),
+                            returnType = Types.int,
+                            isSuspend = false,
+                        ),
+                    )
+                )
+            )
+        )
+        val validationResult = ModelValidator.validate(api)
+        assertThat(validationResult.isFailure).isTrue()
+        assertThat(validationResult.errors).containsExactly(
+            "Error in com.mysdk.MySdkCallback.suspendMethod: callback methods should be " +
+                "non-suspending and have no return values.",
+            "Error in com.mysdk.MySdkCallback.methodWithReturnValue: callback methods should be " +
+                "non-suspending and have no return values.",
+        )
+    }
+
+    @Test
+    fun callbackReceivingCallbacks_throws() {
+        val api = ParsedApi(
+            services = setOf(
+                AnnotatedInterface(type = Type(packageName = "com.mysdk", simpleName = "MySdk")),
+            ),
+            callbacks = setOf(
+                AnnotatedInterface(
+                    type = Type(packageName = "com.mysdk", simpleName = "MySdkCallback"),
+                    methods = listOf(
+                        Method(
+                            name = "foo",
+                            parameters = listOf(
+                                Parameter("otherCallback", Type("com.mysdk", "MySdkCallback"))
+                            ),
+                            returnType = Types.unit,
+                            isSuspend = false,
+                        ),
+                    )
+                )
+            )
+        )
+        val validationResult = ModelValidator.validate(api)
+        assertThat(validationResult.isFailure).isTrue()
+        assertThat(validationResult.errors).containsExactly(
+            "Error in com.mysdk.MySdkCallback.foo: callback methods cannot receive other " +
+                "callbacks as arguments."
         )
     }
 }
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-core/src/test/test-data/aidlservicegeneratortest/output/com/mysdk/ICancellationSignal.aidl b/privacysandbox/tools/tools-core/src/test/test-data/aidlservicegeneratortest/output/com/mysdk/ICancellationSignal.aidl
new file mode 100644
index 0000000..e336980
--- /dev/null
+++ b/privacysandbox/tools/tools-core/src/test/test-data/aidlservicegeneratortest/output/com/mysdk/ICancellationSignal.aidl
@@ -0,0 +1,5 @@
+package com.mysdk;
+
+oneway interface ICancellationSignal {
+    void cancel();
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-core/src/test/test-data/aidlservicegeneratortest/output/com/mysdk/IMySdk.aidl b/privacysandbox/tools/tools-core/src/test/test-data/aidlservicegeneratortest/output/com/mysdk/IMySdk.aidl
new file mode 100644
index 0000000..7969d51
--- /dev/null
+++ b/privacysandbox/tools/tools-core/src/test/test-data/aidlservicegeneratortest/output/com/mysdk/IMySdk.aidl
@@ -0,0 +1,10 @@
+package com.mysdk;
+
+import com.mysdk.IStringTransactionCallback;
+import com.mysdk.IUnitTransactionCallback;
+
+interface IMySdk {
+    void methodWithoutReturnValue();
+    void suspendMethodWithReturnValue(boolean a, int b, long c, float d, double e, char f, int g, IStringTransactionCallback transactionCallback);
+    void suspendMethodWithoutReturnValue(IUnitTransactionCallback transactionCallback);
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-core/src/test/test-data/aidlservicegeneratortest/output/com/mysdk/IStringTransactionCallback.aidl b/privacysandbox/tools/tools-core/src/test/test-data/aidlservicegeneratortest/output/com/mysdk/IStringTransactionCallback.aidl
new file mode 100644
index 0000000..11bba23
--- /dev/null
+++ b/privacysandbox/tools/tools-core/src/test/test-data/aidlservicegeneratortest/output/com/mysdk/IStringTransactionCallback.aidl
@@ -0,0 +1,9 @@
+package com.mysdk;
+
+import com.mysdk.ICancellationSignal;
+
+oneway interface IStringTransactionCallback {
+    void onCancellable(ICancellationSignal cancellationSignal);
+    void onFailure(int errorCode, String errorMessage);
+    void onSuccess(String result);
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-core/src/test/test-data/aidlservicegeneratortest/output/com/mysdk/IUnitTransactionCallback.aidl b/privacysandbox/tools/tools-core/src/test/test-data/aidlservicegeneratortest/output/com/mysdk/IUnitTransactionCallback.aidl
new file mode 100644
index 0000000..a777df5
--- /dev/null
+++ b/privacysandbox/tools/tools-core/src/test/test-data/aidlservicegeneratortest/output/com/mysdk/IUnitTransactionCallback.aidl
@@ -0,0 +1,9 @@
+package com.mysdk;
+
+import com.mysdk.ICancellationSignal;
+
+oneway interface IUnitTransactionCallback {
+    void onCancellable(ICancellationSignal cancellationSignal);
+    void onFailure(int errorCode, String errorMessage);
+    void onSuccess();
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/ICancellationSignal.aidl b/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/ICancellationSignal.aidl
new file mode 100644
index 0000000..e336980
--- /dev/null
+++ b/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/ICancellationSignal.aidl
@@ -0,0 +1,5 @@
+package com.mysdk;
+
+oneway interface ICancellationSignal {
+    void cancel();
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/IMySdk.aidl b/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/IMySdk.aidl
new file mode 100644
index 0000000..3f3e4b5
--- /dev/null
+++ b/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/IMySdk.aidl
@@ -0,0 +1,11 @@
+package com.mysdk;
+
+import com.mysdk.IOuterValueTransactionCallback;
+import com.mysdk.IUnitTransactionCallback;
+import com.mysdk.ParcelableOuterValue;
+
+interface IMySdk {
+    void methodReceivingValue(in ParcelableOuterValue value);
+    void suspendMethodReceivingValue(in ParcelableOuterValue inputValue, IUnitTransactionCallback transactionCallback);
+    void suspendMethodThatReturnsValue(IOuterValueTransactionCallback transactionCallback);
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/IOuterValueTransactionCallback.aidl b/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/IOuterValueTransactionCallback.aidl
new file mode 100644
index 0000000..89a0b1e
--- /dev/null
+++ b/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/IOuterValueTransactionCallback.aidl
@@ -0,0 +1,10 @@
+package com.mysdk;
+
+import com.mysdk.ICancellationSignal;
+import com.mysdk.ParcelableOuterValue;
+
+oneway interface IOuterValueTransactionCallback {
+    void onCancellable(ICancellationSignal cancellationSignal);
+    void onFailure(int errorCode, String errorMessage);
+    void onSuccess(in ParcelableOuterValue result);
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/IUnitTransactionCallback.aidl b/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/IUnitTransactionCallback.aidl
new file mode 100644
index 0000000..a777df5
--- /dev/null
+++ b/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/IUnitTransactionCallback.aidl
@@ -0,0 +1,9 @@
+package com.mysdk;
+
+import com.mysdk.ICancellationSignal;
+
+oneway interface IUnitTransactionCallback {
+    void onCancellable(ICancellationSignal cancellationSignal);
+    void onFailure(int errorCode, String errorMessage);
+    void onSuccess();
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/ParcelableInnerValue.aidl b/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/ParcelableInnerValue.aidl
new file mode 100644
index 0000000..f160690
--- /dev/null
+++ b/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/ParcelableInnerValue.aidl
@@ -0,0 +1,7 @@
+package com.mysdk;
+
+parcelable ParcelableInnerValue {
+    boolean booleanProperty;
+    int intProperty;
+    long longProperty;
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/ParcelableOuterValue.aidl b/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/ParcelableOuterValue.aidl
new file mode 100644
index 0000000..9627006
--- /dev/null
+++ b/privacysandbox/tools/tools-core/src/test/test-data/aidlvaluegeneratortest/output/com/mysdk/ParcelableOuterValue.aidl
@@ -0,0 +1,8 @@
+package com.mysdk;
+
+import com.mysdk.ParcelableInnerValue;
+
+parcelable ParcelableOuterValue {
+    ParcelableInnerValue anotherInnerValue;
+    ParcelableInnerValue innerValue;
+}
\ No newline at end of file
diff --git a/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/CompilationTestHelper.kt b/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/CompilationTestHelper.kt
index b34a80b..0c15531 100644
--- a/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/CompilationTestHelper.kt
+++ b/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/CompilationTestHelper.kt
@@ -115,10 +115,10 @@
     private fun contentsHighlightingLine(contents: String, line: Int): String {
         var lineCountdown = line
         // Insert a "->" in the beginning of the highlighted line.
-        return contents.split("\n").map {
+        return contents.split("\n").joinToString("\n") {
             val lineHeader = if (--lineCountdown == 0) "->" else "  "
             "$lineHeader$it"
-        }.joinToString("\n")
+        }
     }
 }
 
diff --git a/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/TestDataHelper.kt b/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/TestDataHelper.kt
index da5bd3c..c75b770 100644
--- a/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/TestDataHelper.kt
+++ b/privacysandbox/tools/tools-testing/src/main/java/androidx/privacysandbox/tools/testing/TestDataHelper.kt
@@ -19,6 +19,7 @@
 import androidx.room.compiler.processing.util.Source
 import java.io.File
 
+/** Load files in the given directory as Room's [Source]s. */
 fun loadSourcesFromDirectory(directory: File): List<Source> {
     check(directory.exists()) { "${directory.path} doesn't exist." }
     check(directory.isDirectory) { "${directory.path} is not a directory." }
@@ -28,3 +29,12 @@
         Source.load(file = it, qName = qualifiedName, relativePath = relativePath)
     }.toList()
 }
+
+/** Load files in the given directory as pairs <relative path, content>. */
+fun loadFilesFromDirectory(directory: File): List<Pair<String, String>> {
+    check(directory.exists()) { "${directory.path} doesn't exist." }
+    check(directory.isDirectory) { "${directory.path} is not a directory." }
+    return directory.walk().filter { it.isFile }.map {
+        directory.toPath().relativize(it.toPath()).toString() to it.readText()
+    }.toList()
+}
diff --git a/privacysandbox/tools/tools/src/main/java/androidx/privacysandbox/tools/PrivacySandboxCallback.kt b/privacysandbox/tools/tools/src/main/java/androidx/privacysandbox/tools/PrivacySandboxCallback.kt
new file mode 100644
index 0000000..4a83830
--- /dev/null
+++ b/privacysandbox/tools/tools/src/main/java/androidx/privacysandbox/tools/PrivacySandboxCallback.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.privacysandbox.tools
+
+/**
+ * Annotated callbacks that can be passed to an SDK running in the privacy sandbox.
+ *
+ * Callbacks should be public interfaces that only declare functions without implementation.
+ * Callback functions should be fire-and-forget: non-suspending functions that have no return value.
+ */
+@Target(AnnotationTarget.CLASS)
+@Retention(AnnotationRetention.BINARY)
+annotation class PrivacySandboxCallback()
\ No newline at end of file
diff --git a/recyclerview/recyclerview/build.gradle b/recyclerview/recyclerview/build.gradle
index d1a017d..991478a 100644
--- a/recyclerview/recyclerview/build.gradle
+++ b/recyclerview/recyclerview/build.gradle
@@ -31,7 +31,7 @@
     androidTestImplementation(libs.kotlinCoroutinesAndroid)
 
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.kotlinStdlib)
     lintPublish(project(":recyclerview:recyclerview-lint"))
 }
diff --git a/recyclerview/recyclerview/src/test/java/androidx/recyclerview/widget/SortedListBatchedCallbackTest.java b/recyclerview/recyclerview/src/test/java/androidx/recyclerview/widget/SortedListBatchedCallbackTest.java
index 5766fc8..99800ba 100644
--- a/recyclerview/recyclerview/src/test/java/androidx/recyclerview/widget/SortedListBatchedCallbackTest.java
+++ b/recyclerview/recyclerview/src/test/java/androidx/recyclerview/widget/SortedListBatchedCallbackTest.java
@@ -18,14 +18,12 @@
 
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 import org.mockito.Mockito;
-
 @SuppressWarnings("unchecked")
 @RunWith(JUnit4.class)
 public class SortedListBatchedCallbackTest {
@@ -41,7 +39,7 @@
     @Test
     public void onChange() {
         mBatchedCallback.onChanged(1, 2);
-        verifyZeroInteractions(mMockCallback);
+        verifyNoMoreInteractions(mMockCallback);
         mBatchedCallback.dispatchLastEvent();
         verify(mMockCallback).onChanged(1, 2, null);
         verifyNoMoreInteractions(mMockCallback);
@@ -51,7 +49,7 @@
     public void onChangeWithPayload() {
         final Object payload = 7;
         mBatchedCallback.onChanged(1, 2, payload);
-        verifyZeroInteractions(mMockCallback);
+        verifyNoMoreInteractions(mMockCallback);
         mBatchedCallback.dispatchLastEvent();
         verify(mMockCallback).onChanged(1, 2, payload);
         verifyNoMoreInteractions(mMockCallback);
@@ -60,7 +58,7 @@
     @Test
     public void onRemoved() {
         mBatchedCallback.onRemoved(2, 3);
-        verifyZeroInteractions(mMockCallback);
+        verifyNoMoreInteractions(mMockCallback);
         mBatchedCallback.dispatchLastEvent();
         verify(mMockCallback).onRemoved(2, 3);
         verifyNoMoreInteractions(mMockCallback);
diff --git a/room/integration-tests/kotlintestapp/build.gradle b/room/integration-tests/kotlintestapp/build.gradle
index 35f2d71..bc91f86 100644
--- a/room/integration-tests/kotlintestapp/build.gradle
+++ b/room/integration-tests/kotlintestapp/build.gradle
@@ -122,7 +122,7 @@
     androidTestImplementation(projectOrArtifact(":lifecycle:lifecycle-runtime-testing"))
     androidTestImplementation(libs.rxjava2)
     androidTestImplementation(libs.kotlinCoroutinesTest)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
 }
 
 // KSP does not support argument per flavor so we set schema location globally, for other per
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/migration/MigrationKotlinTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/migration/MigrationKotlinTest.kt
index 8c72847..c874d53 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/migration/MigrationKotlinTest.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/migration/MigrationKotlinTest.kt
@@ -225,8 +225,8 @@
                 TEST_DB,
                 7, false,
                 object : Migration(6, 7) {
-                    override fun migrate(database: SupportSQLiteDatabase) {
-                        database.execSQL(
+                    override fun migrate(db: SupportSQLiteDatabase) {
+                        db.execSQL(
                             "CREATE TABLE Entity4 (`id` INTEGER, `name` TEXT," +
                                 " PRIMARY KEY(`id`))"
                         )
@@ -273,8 +273,8 @@
     }
 
     internal val MIGRATION_1_2: Migration = object : Migration(1, 2) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL(
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL(
                 "CREATE TABLE IF NOT EXISTS `Entity2` (`id` INTEGER NOT NULL," +
                     " `name` TEXT, PRIMARY KEY(`id`))"
             )
@@ -282,8 +282,8 @@
     }
 
     internal val MIGRATION_2_3: Migration = object : Migration(2, 3) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL(
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL(
                 "ALTER TABLE " + MigrationDbKotlin.Entity2.TABLE_NAME +
                     " ADD COLUMN addedInV3 TEXT"
             )
@@ -291,8 +291,8 @@
     }
 
     internal val MIGRATION_3_4: Migration = object : Migration(3, 4) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL(
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL(
                 "CREATE TABLE IF NOT EXISTS `Entity3` (`id` INTEGER NOT NULL," +
                     " `removedInV5` TEXT, `name` TEXT, PRIMARY KEY(`id`))"
             )
@@ -300,36 +300,36 @@
     }
 
     internal val MIGRATION_4_5: Migration = object : Migration(4, 5) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL(
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL(
                 "CREATE TABLE IF NOT EXISTS `Entity3_New` (`id` INTEGER NOT NULL," +
                     " `name` TEXT, PRIMARY KEY(`id`))"
             )
-            database.execSQL(
+            db.execSQL(
                 "INSERT INTO Entity3_New(`id`, `name`) " +
                     "SELECT `id`, `name` FROM Entity3"
             )
-            database.execSQL("DROP TABLE Entity3")
-            database.execSQL("ALTER TABLE Entity3_New RENAME TO Entity3")
+            db.execSQL("DROP TABLE Entity3")
+            db.execSQL("ALTER TABLE Entity3_New RENAME TO Entity3")
         }
     }
 
     internal val MIGRATION_5_6: Migration = object : Migration(5, 6) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL("DROP TABLE " + MigrationDbKotlin.Entity3.TABLE_NAME)
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("DROP TABLE " + MigrationDbKotlin.Entity3.TABLE_NAME)
         }
     }
 
     internal val MIGRATION_6_7: Migration = object : Migration(6, 7) {
-        override fun migrate(database: SupportSQLiteDatabase) {
-            database.execSQL(
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL(
                 "CREATE TABLE IF NOT EXISTS " +
                     MigrationDbKotlin.Entity4.TABLE_NAME +
                     " (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`)," +
                     " FOREIGN KEY(`name`) REFERENCES `Entity1`(`name`)" +
                     " ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED)"
             )
-            database.execSQL(
+            db.execSQL(
                 "CREATE UNIQUE INDEX `index_entity1` ON " +
                     MigrationDbKotlin.Entity1.TABLE_NAME + " (`name`)"
             )
@@ -344,7 +344,7 @@
     internal class EmptyMigration(startVersion: Int, endVersion: Int) :
         Migration(startVersion, endVersion) {
 
-        override fun migrate(database: SupportSQLiteDatabase) {
+        override fun migrate(db: SupportSQLiteDatabase) {
             // do nothing
         }
     }
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/AlteredTableColumnOrderTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/AlteredTableColumnOrderTest.kt
index 624daee..d873f1c 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/AlteredTableColumnOrderTest.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/AlteredTableColumnOrderTest.kt
@@ -60,8 +60,8 @@
             Room.databaseBuilder(context, TestDatabase::class.java, "migrated_foo.db")
                 .createFromAsset("databases/foo_v1.db")
                 .addMigrations(object : Migration(1, 2) {
-                    override fun migrate(database: SupportSQLiteDatabase) {
-                        database.execSQL("ALTER TABLE Foo ADD COLUMN X TEXT NOT NULL DEFAULT 'X';")
+                    override fun migrate(db: SupportSQLiteDatabase) {
+                        db.execSQL("ALTER TABLE Foo ADD COLUMN X TEXT NOT NULL DEFAULT 'X';")
                     }
                 })
                 .build()
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/AutoMigrationTest.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/AutoMigrationTest.java
index fa768a95..9df26de 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/AutoMigrationTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/AutoMigrationTest.java
@@ -157,14 +157,14 @@
 
     private static final Migration MIGRATION_1_2 = new Migration(1, 2) {
         @Override
-        public void migrate(@NonNull SupportSQLiteDatabase database) {
-            database.execSQL("ALTER TABLE `Entity0` ADD COLUMN `addedInV2` INTEGER NOT NULL "
+        public void migrate(@NonNull SupportSQLiteDatabase db) {
+            db.execSQL("ALTER TABLE `Entity0` ADD COLUMN `addedInV2` INTEGER NOT NULL "
                     + "DEFAULT 2");
         }
     };
 
     private static final Migration MIGRATION_1_0 = new Migration(1, 0) {
         @Override
-        public void migrate(@NonNull SupportSQLiteDatabase database) { }
+        public void migrate(@NonNull SupportSQLiteDatabase db) { }
     };
 }
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/FtsMigrationTest.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/FtsMigrationTest.java
index 6a5c6e3..2ba57a6 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/FtsMigrationTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/FtsMigrationTest.java
@@ -217,23 +217,23 @@
 
     private static final Migration MIGRATION_1_2 = new Migration(1, 2) {
         @Override
-        public void migrate(@NonNull SupportSQLiteDatabase database) {
-            database.execSQL("ALTER TABLE Book RENAME TO Book_old");
-            database.execSQL("CREATE VIRTUAL TABLE IF NOT EXISTS `Book` USING FTS4("
+        public void migrate(@NonNull SupportSQLiteDatabase db) {
+            db.execSQL("ALTER TABLE Book RENAME TO Book_old");
+            db.execSQL("CREATE VIRTUAL TABLE IF NOT EXISTS `Book` USING FTS4("
                     + "`title`, `author`, `numOfPages`, `text`, matchinfo=fts3)");
-            database.execSQL("INSERT INTO Book SELECT * FROM Book_old");
-            database.execSQL("DROP TABLE Book_old");
+            db.execSQL("INSERT INTO Book SELECT * FROM Book_old");
+            db.execSQL("DROP TABLE Book_old");
         }
     };
 
     private static final Migration MIGRATION_2_3 = new Migration(2, 3) {
         @Override
-        public void migrate(@NonNull SupportSQLiteDatabase database) {
-            database.execSQL(
+        public void migrate(@NonNull SupportSQLiteDatabase db) {
+            db.execSQL(
                     "CREATE TABLE IF NOT EXISTS `Person` (`id` INTEGER NOT NULL, "
                             + "`firstName` TEXT, `lastName` TEXT, `line1` TEXT, `line2` TEXT, "
                             + "`state` TEXT, `zipcode` INTEGER, PRIMARY KEY(`id`))");
-            database.execSQL(
+            db.execSQL(
                     "CREATE VIRTUAL TABLE IF NOT EXISTS `AddressFts` USING FTS4(`line1` TEXT, "
                             + "`line2` TEXT, `state` TEXT, `zipcode` INTEGER, content=`Person`)");
         }
@@ -241,13 +241,13 @@
 
     private static final Migration MIGRATION_3_4 = new Migration(3, 4) {
         @Override
-        public void migrate(@NonNull SupportSQLiteDatabase database) {
-            database.execSQL("ALTER TABLE `Person` RENAME TO `User`");
-            database.execSQL("DROP TABLE `AddressFts`");
-            database.execSQL(
+        public void migrate(@NonNull SupportSQLiteDatabase db) {
+            db.execSQL("ALTER TABLE `Person` RENAME TO `User`");
+            db.execSQL("DROP TABLE `AddressFts`");
+            db.execSQL(
                     "CREATE VIRTUAL TABLE IF NOT EXISTS `AddressFts` USING FTS4(`line1` TEXT, "
                             + "`line2` TEXT, `state` TEXT, `zipcode` INTEGER, content=`User`)");
-            database.execSQL(
+            db.execSQL(
                     "INSERT INTO `AddressFts` (`docid`, `line1`, `line2`, `state`, `zipcode`) "
                             + "SELECT `rowid`, `line1`, `line2`, `state`, `zipcode` FROM `User`");
         }
@@ -255,26 +255,26 @@
 
     private static final Migration MIGRATION_4_5 = new Migration(4, 5) {
         @Override
-        public void migrate(@NonNull SupportSQLiteDatabase database) {
-            database.execSQL("CREATE VIRTUAL TABLE IF NOT EXISTS `Mail` USING FTS4("
+        public void migrate(@NonNull SupportSQLiteDatabase db) {
+            db.execSQL("CREATE VIRTUAL TABLE IF NOT EXISTS `Mail` USING FTS4("
                     + "`content` TEXT NOT NULL)");
         }
     };
 
     private static final Migration MIGRATION_5_6 = new Migration(5, 6) {
         @Override
-        public void migrate(@NonNull SupportSQLiteDatabase database) {
-            database.execSQL("DROP TABLE `Mail`");
-            database.execSQL("CREATE VIRTUAL TABLE IF NOT EXISTS `Mail` USING FTS4("
+        public void migrate(@NonNull SupportSQLiteDatabase db) {
+            db.execSQL("DROP TABLE `Mail`");
+            db.execSQL("CREATE VIRTUAL TABLE IF NOT EXISTS `Mail` USING FTS4("
                     + "`content` TEXT NOT NULL, languageid=`lid`)");
         }
     };
 
     private static final Migration BAD_MIGRATION_1_2 = new Migration(1, 2) {
         @Override
-        public void migrate(@NonNull SupportSQLiteDatabase database) {
-            database.execSQL("DROP TABLE Book");
-            database.execSQL("CREATE VIRTUAL TABLE `Book` USING FTS4("
+        public void migrate(@NonNull SupportSQLiteDatabase db) {
+            db.execSQL("DROP TABLE Book");
+            db.execSQL("CREATE VIRTUAL TABLE `Book` USING FTS4("
                     + "`title`, `author`, `numOfPages`, `text`)");
         }
     };
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/JournalDbPostMigrationTest.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/JournalDbPostMigrationTest.java
index eb380d1..ea499ce 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/JournalDbPostMigrationTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/JournalDbPostMigrationTest.java
@@ -114,16 +114,16 @@
 
     private static Migration sMigrationV1toV2 = new Migration(1, 2) {
         @Override
-        public void migrate(@NonNull SupportSQLiteDatabase database) {
-            database.execSQL("ALTER TABLE user ADD COLUMN `address` TEXT");
-            Cursor cursor = database.query("SELECT * FROM user");
+        public void migrate(@NonNull SupportSQLiteDatabase db) {
+            db.execSQL("ALTER TABLE user ADD COLUMN `address` TEXT");
+            Cursor cursor = db.query("SELECT * FROM user");
             while (cursor.moveToNext()) {
                 String authCode = cursor.getString(cursor.getColumnIndex("address"));
                 if (TextUtils.isEmpty(authCode)) {
                     int id = cursor.getInt(cursor.getColumnIndex("uid"));
                     ContentValues values = new ContentValues();
                     values.put("address", UUID.randomUUID().toString());
-                    database.update(
+                    db.update(
                             "user", SQLiteDatabase.CONFLICT_IGNORE, values,
                             "uid = " + id, null);
                 }
@@ -132,8 +132,8 @@
     };
     private static Migration sMigrationV2toV3 = new Migration(2, 3) {
         @Override
-        public void migrate(@NonNull SupportSQLiteDatabase database) {
-            database.execSQL("ALTER TABLE user ADD COLUMN `age` INTEGER NOT NULL DEFAULT 0");
+        public void migrate(@NonNull SupportSQLiteDatabase db) {
+            db.execSQL("ALTER TABLE user ADD COLUMN `age` INTEGER NOT NULL DEFAULT 0");
         }
     };
 
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/MigrationTest.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/MigrationTest.java
index 350ddad..b83df2c 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/MigrationTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/MigrationTest.java
@@ -216,8 +216,8 @@
             helper.runMigrationsAndValidate(TEST_DB,
                     7, false, new Migration(6, 7) {
                         @Override
-                        public void migrate(@NonNull SupportSQLiteDatabase database) {
-                            database.execSQL("CREATE TABLE Entity4 (`id` INTEGER NOT NULL,"
+                        public void migrate(@NonNull SupportSQLiteDatabase db) {
+                            db.execSQL("CREATE TABLE Entity4 (`id` INTEGER NOT NULL,"
                                     + " `name` TEXT, PRIMARY KEY(`id`))");
                         }
                     });
@@ -636,8 +636,8 @@
 
     private static final Migration MIGRATION_1_2 = new Migration(1, 2) {
         @Override
-        public void migrate(@NonNull SupportSQLiteDatabase database) {
-            database.execSQL("CREATE TABLE IF NOT EXISTS `Entity2` ("
+        public void migrate(@NonNull SupportSQLiteDatabase db) {
+            db.execSQL("CREATE TABLE IF NOT EXISTS `Entity2` ("
                     + "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
                     + " `name` TEXT)");
         }
@@ -645,55 +645,55 @@
 
     private static final Migration MIGRATION_2_3 = new Migration(2, 3) {
         @Override
-        public void migrate(@NonNull SupportSQLiteDatabase database) {
-            database.execSQL("ALTER TABLE " + MigrationDb.Entity2.TABLE_NAME
+        public void migrate(@NonNull SupportSQLiteDatabase db) {
+            db.execSQL("ALTER TABLE " + MigrationDb.Entity2.TABLE_NAME
                     + " ADD COLUMN addedInV3 TEXT");
         }
     };
 
     private static final Migration MIGRATION_3_4 = new Migration(3, 4) {
         @Override
-        public void migrate(@NonNull SupportSQLiteDatabase database) {
-            database.execSQL("CREATE TABLE IF NOT EXISTS `Entity3` (`id` INTEGER NOT NULL,"
+        public void migrate(@NonNull SupportSQLiteDatabase db) {
+            db.execSQL("CREATE TABLE IF NOT EXISTS `Entity3` (`id` INTEGER NOT NULL,"
                     + " `removedInV5` TEXT, `name` TEXT, PRIMARY KEY(`id`))");
         }
     };
 
     private static final Migration MIGRATION_4_5 = new Migration(4, 5) {
         @Override
-        public void migrate(@NonNull SupportSQLiteDatabase database) {
-            database.execSQL("CREATE TABLE IF NOT EXISTS `Entity3_New` (`id` INTEGER NOT NULL,"
+        public void migrate(@NonNull SupportSQLiteDatabase db) {
+            db.execSQL("CREATE TABLE IF NOT EXISTS `Entity3_New` (`id` INTEGER NOT NULL,"
                     + " `name` TEXT, PRIMARY KEY(`id`))");
-            database.execSQL("INSERT INTO Entity3_New(`id`, `name`) "
+            db.execSQL("INSERT INTO Entity3_New(`id`, `name`) "
                     + "SELECT `id`, `name` FROM Entity3");
-            database.execSQL("DROP TABLE Entity3");
-            database.execSQL("ALTER TABLE Entity3_New RENAME TO Entity3");
+            db.execSQL("DROP TABLE Entity3");
+            db.execSQL("ALTER TABLE Entity3_New RENAME TO Entity3");
         }
     };
 
     private static final Migration MIGRATION_5_6 = new Migration(5, 6) {
         @Override
-        public void migrate(@NonNull SupportSQLiteDatabase database) {
-            database.execSQL("DROP TABLE " + MigrationDb.Entity3.TABLE_NAME);
+        public void migrate(@NonNull SupportSQLiteDatabase db) {
+            db.execSQL("DROP TABLE " + MigrationDb.Entity3.TABLE_NAME);
         }
     };
 
     private static final Migration MIGRATION_6_7 = new Migration(6, 7) {
         @Override
-        public void migrate(@NonNull SupportSQLiteDatabase database) {
-            database.execSQL("CREATE TABLE IF NOT EXISTS " + MigrationDb.Entity4.TABLE_NAME
+        public void migrate(@NonNull SupportSQLiteDatabase db) {
+            db.execSQL("CREATE TABLE IF NOT EXISTS " + MigrationDb.Entity4.TABLE_NAME
                     + " (`id` INTEGER NOT NULL, `name` TEXT COLLATE NOCASE, PRIMARY KEY(`id`),"
                     + " FOREIGN KEY(`name`) REFERENCES `Entity1`(`name`)"
                     + " ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED)");
-            database.execSQL("CREATE UNIQUE INDEX `index_entity1` ON "
+            db.execSQL("CREATE UNIQUE INDEX `index_entity1` ON "
                     + MigrationDb.Entity1.TABLE_NAME + " (`name`)");
         }
     };
 
     private static final Migration MIGRATION_7_8 = new Migration(7, 8) {
         @Override
-        public void migrate(@NonNull SupportSQLiteDatabase database) {
-            database.execSQL("CREATE VIEW IF NOT EXISTS `" + MigrationDb.View1.VIEW_NAME
+        public void migrate(@NonNull SupportSQLiteDatabase db) {
+            db.execSQL("CREATE VIEW IF NOT EXISTS `" + MigrationDb.View1.VIEW_NAME
                     + "` AS SELECT Entity4.id, Entity4.name, Entity1.id AS entity1Id"
                     + " FROM Entity4 INNER JOIN Entity1 ON Entity4.name = Entity1.name");
         }
@@ -701,46 +701,46 @@
 
     private static final Migration MIGRATION_8_9 = new Migration(8, 9) {
         @Override
-        public void migrate(@NonNull SupportSQLiteDatabase database) {
+        public void migrate(@NonNull SupportSQLiteDatabase db) {
             // Add a column along with a DEFAULT unknown to Room
-            database.execSQL("ALTER TABLE Entity2 ADD COLUMN `addedInV9` TEXT DEFAULT ''");
+            db.execSQL("ALTER TABLE Entity2 ADD COLUMN `addedInV9` TEXT DEFAULT ''");
         }
     };
 
     private static final Migration MIGRATION_9_10 = new Migration(9, 10) {
         @Override
-        public void migrate(@NonNull SupportSQLiteDatabase database) {
-            database.execSQL("ALTER TABLE Entity1 "
+        public void migrate(@NonNull SupportSQLiteDatabase db) {
+            db.execSQL("ALTER TABLE Entity1 "
                     + "ADD COLUMN addedInV10 INTEGER NOT NULL DEFAULT 0");
         }
     };
 
     private static final Migration MIGRATION_10_11 = new Migration(10, 11) {
         @Override
-        public void migrate(@NonNull SupportSQLiteDatabase database) {
+        public void migrate(@NonNull SupportSQLiteDatabase db) {
             // Add DEFAULT constraint to Entity2.name.
-            database.execSQL("ALTER TABLE Entity2 RENAME TO save_Entity2");
-            database.execSQL("CREATE TABLE IF NOT EXISTS Entity2 "
+            db.execSQL("ALTER TABLE Entity2 RENAME TO save_Entity2");
+            db.execSQL("CREATE TABLE IF NOT EXISTS Entity2 "
                     + "(`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "
                     + "`addedInV3` TEXT, `name` TEXT DEFAULT 'Unknown', `addedInV9` TEXT)");
-            database.execSQL("INSERT INTO Entity2 (id, addedInV3, name, addedInV9) "
+            db.execSQL("INSERT INTO Entity2 (id, addedInV3, name, addedInV9) "
                     + "SELECT id, addedInV3, name, addedInV9 FROM save_Entity2");
-            database.execSQL("DROP TABLE save_Entity2");
+            db.execSQL("DROP TABLE save_Entity2");
         }
     };
 
     private static final Migration MIGRATION_11_12 = new Migration(11, 12) {
         @Override
-        public void migrate(@NonNull SupportSQLiteDatabase database) {
-            database.execSQL("CREATE INDEX IF NOT EXISTS `index_Entity1_addedInV10` "
+        public void migrate(@NonNull SupportSQLiteDatabase db) {
+            db.execSQL("CREATE INDEX IF NOT EXISTS `index_Entity1_addedInV10` "
                     + "ON `Entity1` (`addedInV10`)");
         }
     };
 
     private static final Migration MIGRATION_12_13 = new Migration(12, 13) {
         @Override
-        public void migrate(@NonNull SupportSQLiteDatabase database) {
-            database.execSQL("ALTER TABLE Entity1 "
+        public void migrate(@NonNull SupportSQLiteDatabase db) {
+            db.execSQL("ALTER TABLE Entity1 "
                     + "ADD COLUMN added1InV13 INTEGER NOT NULL DEFAULT (0)");
         }
     };
@@ -759,9 +759,9 @@
     private static final Migration MIGRATION_MAX_LATEST = new Migration(
             MigrationDb.MAX_VERSION, MigrationDb.LATEST_VERSION) {
         @Override
-        public void migrate(@NonNull SupportSQLiteDatabase database) {
+        public void migrate(@NonNull SupportSQLiteDatabase db) {
             // Drop Entity1 since its possible that LATEST_VERSION schema defines it differently.
-            database.execSQL("DROP TABLE IF EXISTS " + MigrationDb.Entity1.TABLE_NAME);
+            db.execSQL("DROP TABLE IF EXISTS " + MigrationDb.Entity1.TABLE_NAME);
 
             try {
                 Context testContext = InstrumentationRegistry.getInstrumentation().getContext();
@@ -769,17 +769,17 @@
                         + "/" + MigrationDb.LATEST_VERSION + ".json");
                 SchemaBundle schemaBundle = SchemaBundle.deserialize(input);
                 for (String query : schemaBundle.getDatabase().buildCreateQueries()) {
-                    database.execSQL(query);
+                    db.execSQL(query);
                 }
             } catch (IOException e) {
                 // Re-throw as a runtime exception so test fails if something goes wrong.
                 throw new RuntimeException(e);
             }
 
-            database.execSQL("CREATE TABLE IF NOT EXISTS `NoOp` (`id` INTEGER NOT NULL,"
+            db.execSQL("CREATE TABLE IF NOT EXISTS `NoOp` (`id` INTEGER NOT NULL,"
                     + " PRIMARY KEY(`id`))");
-            database.execSQL("INSERT INTO `NoOp` (`id`) VALUES (1)");
-            database.execSQL("INSERT INTO `NoOp` (`id`) VALUES (2)");
+            db.execSQL("INSERT INTO `NoOp` (`id`) VALUES (1)");
+            db.execSQL("INSERT INTO `NoOp` (`id`) VALUES (2)");
         }
     };
 
@@ -794,7 +794,7 @@
         }
 
         @Override
-        public void migrate(@NonNull SupportSQLiteDatabase database) {
+        public void migrate(@NonNull SupportSQLiteDatabase db) {
             // do nothing
         }
     }
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/PrepackageTest.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/PrepackageTest.java
index af3cc30..9a3a183 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/PrepackageTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/PrepackageTest.java
@@ -236,8 +236,8 @@
                 .createFromAsset("databases/products_v1.db")
                 .addMigrations(new Migration(1, 2) {
                     @Override
-                    public void migrate(@NonNull SupportSQLiteDatabase database) {
-                        database.execSQL(
+                    public void migrate(@NonNull SupportSQLiteDatabase db) {
+                        db.execSQL(
                                 "INSERT INTO Products (id, name) VALUES (null, 'Mofongo')");
                     }
                 })
@@ -338,8 +338,8 @@
                 .createFromAsset("databases/products_v1.db")
                 .addMigrations(new Migration(1, 2) {
                     @Override
-                    public void migrate(@NonNull SupportSQLiteDatabase database) {
-                        database.execSQL(
+                    public void migrate(@NonNull SupportSQLiteDatabase db) {
+                        db.execSQL(
                                 "INSERT INTO Products (id, name) VALUES (null, 'Mofongo')");
                     }
                 })
diff --git a/room/room-common/build.gradle b/room/room-common/build.gradle
index da2c1c9..64092d5 100644
--- a/room/room-common/build.gradle
+++ b/room/room-common/build.gradle
@@ -32,7 +32,7 @@
     api("androidx.annotation:annotation:1.3.0")
     api(libs.kotlinStdlibJdk8)
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.guava)
     testImplementation(libs.truth)
 }
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/PoetExt.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/PoetExt.kt
index 03d4446..9e34117 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/PoetExt.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/PoetExt.kt
@@ -30,6 +30,7 @@
 typealias KCodeBlockBuilder = com.squareup.kotlinpoet.CodeBlock.Builder
 typealias KAnnotationSpecBuilder = com.squareup.kotlinpoet.AnnotationSpec.Builder
 typealias KTypeSpecBuilder = com.squareup.kotlinpoet.TypeSpec.Builder
+typealias KMemberName = com.squareup.kotlinpoet.MemberName
 typealias JArrayTypeName = com.squareup.javapoet.ArrayTypeName
 
 // TODO(b/127483380): Recycle to room-compiler?
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XCodeBlock.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XCodeBlock.kt
index c681bed..0ec904ca 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XCodeBlock.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XCodeBlock.kt
@@ -19,6 +19,25 @@
 import androidx.room.compiler.codegen.java.JavaCodeBlock
 import androidx.room.compiler.codegen.kotlin.KotlinCodeBlock
 
+/**
+ * A fragment of a .java or .kt file, potentially containing declarations, statements.
+ *
+ * Code blocks support placeholders like [java.text.Format]. This uses a percent sign `%` but has
+ * its own set of permitted placeholders:
+ *
+ *  * `%L` emits a *literal* value with no escaping. Arguments for literals may be strings,
+ *    primitives, [type declarations][XTypeSpec], [annotations][XAnnotationSpec] and even other code
+ *    blocks.
+ *  * `%N` emits a *name*, using name collision avoidance where necessary. Arguments for names may
+ *    be strings (actually any [character sequence][CharSequence]), [parameters][XParameterSpec],
+ *    [properties][XPropertySpec], [functions][XFunSpec], and [types][XTypeSpec].
+ *  * `%S` escapes the value as a *string*, wraps it with double quotes, and emits that.
+ *  * `%T` emits a *type* reference. Types will be imported if possible. Arguments for types are
+ *    their [names][XTypeName].
+ *  * `%M` emits a *member* reference. A member is either a function or a property. If the member is
+ *    importable, e.g. it's a top-level function or a property declared inside an object, the import
+ *    will be resolved if possible. Arguments for members must be of type [XMemberName].
+ */
 interface XCodeBlock : TargetLanguage {
 
     interface Builder : TargetLanguage {
@@ -43,6 +62,25 @@
         fun build(): XCodeBlock
 
         companion object {
+            /**
+             * Convenience local immutable variable emitter.
+             *
+             * Shouldn't contain declaration, only right hand assignment expression.
+             */
+            fun Builder.addLocalVal(
+                name: String,
+                typeName: XTypeName,
+                assignExprFormat: String,
+                vararg assignExprArgs: Any
+            ) {
+                addLocalVariable(
+                    name = name,
+                    typeName = typeName,
+                    isMutable = false,
+                    assignExpr = of(language, assignExprFormat, *assignExprArgs)
+                )
+            }
+
             fun Builder.apply(
                 javaCodeBuilder: com.squareup.javapoet.CodeBlock.Builder.() -> Unit,
                 kotlinCodeBuilder: com.squareup.kotlinpoet.CodeBlock.Builder.() -> Unit,
@@ -112,5 +150,18 @@
                 }
             }.build()
         }
+
+        /**
+         * Convenience code block of a Java class literal.
+         */
+        fun ofJavaClassLiteral(
+            language: CodeLanguage,
+            typeName: XClassName,
+        ): XCodeBlock {
+            return when (language) {
+                CodeLanguage.JAVA -> of(language, "%T.class", typeName)
+                CodeLanguage.KOTLIN -> of(language, "%T::class.java", typeName)
+            }
+        }
     }
 }
\ No newline at end of file
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XFunSpec.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XFunSpec.kt
index 898c8d1..745d82c 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XFunSpec.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XFunSpec.kt
@@ -20,13 +20,22 @@
 import androidx.room.compiler.codegen.java.toJavaVisibilityModifier
 import androidx.room.compiler.codegen.kotlin.KotlinFunSpec
 import androidx.room.compiler.codegen.kotlin.toKotlinVisibilityModifier
+import androidx.room.compiler.processing.FunSpecHelper
+import androidx.room.compiler.processing.MethodSpecHelper
+import androidx.room.compiler.processing.XMethodElement
+import androidx.room.compiler.processing.XType
 import com.squareup.javapoet.MethodSpec
 import com.squareup.kotlinpoet.FunSpec
 import com.squareup.kotlinpoet.KModifier
 
 interface XFunSpec : TargetLanguage {
 
+    val name: String
+
     interface Builder : TargetLanguage {
+
+        fun addAnnotation(annotation: XAnnotationSpec)
+
         // TODO(b/247247442): Maybe make a XParameterSpec ?
         fun addParameter(
             typeName: XTypeName,
@@ -48,6 +57,22 @@
             fun Builder.addStatement(format: String, vararg args: Any?) = addCode(
                 XCodeBlock.builder(language).addStatement(format, *args).build()
             )
+
+            fun Builder.apply(
+                javaMethodBuilder: MethodSpec.Builder.() -> Unit,
+                kotlinFunctionBuilder: FunSpec.Builder.() -> Unit,
+            ): Builder = apply {
+                when (language) {
+                    CodeLanguage.JAVA -> {
+                        check(this is JavaFunSpec.Builder)
+                        this.actual.javaMethodBuilder()
+                    }
+                    CodeLanguage.KOTLIN -> {
+                        check(this is KotlinFunSpec.Builder)
+                        this.actual.kotlinFunctionBuilder()
+                    }
+                }
+            }
         }
     }
 
@@ -62,6 +87,7 @@
             return when (language) {
                 CodeLanguage.JAVA -> {
                     JavaFunSpec.Builder(
+                        name,
                         MethodSpec.methodBuilder(name).apply {
                             addModifiers(visibility.toJavaVisibilityModifier())
                             // TODO(b/247242374) Add nullability annotations for non-private params
@@ -76,6 +102,7 @@
                 }
                 CodeLanguage.KOTLIN -> {
                     KotlinFunSpec.Builder(
+                        name,
                         FunSpec.builder(name).apply {
                             addModifiers(visibility.toKotlinVisibilityModifier())
                             if (isOpen) {
@@ -94,9 +121,11 @@
             language: CodeLanguage,
             visibility: VisibilityModifier
         ): Builder {
+            val name = "<init>"
             return when (language) {
                 CodeLanguage.JAVA -> {
                     JavaFunSpec.Builder(
+                        name,
                         MethodSpec.constructorBuilder().apply {
                             addModifiers(visibility.toJavaVisibilityModifier())
                         }
@@ -104,6 +133,7 @@
                 }
                 CodeLanguage.KOTLIN -> {
                     KotlinFunSpec.Builder(
+                        name,
                         FunSpec.constructorBuilder().apply {
                             addModifiers(visibility.toKotlinVisibilityModifier())
                         }
@@ -111,5 +141,22 @@
                 }
             }
         }
+
+        fun overridingBuilder(
+            language: CodeLanguage,
+            element: XMethodElement,
+            owner: XType
+        ): Builder {
+            return when (language) {
+                CodeLanguage.JAVA -> JavaFunSpec.Builder(
+                    name = element.jvmName,
+                    actual = MethodSpecHelper.overridingWithFinalParams(element, owner)
+                )
+                CodeLanguage.KOTLIN -> KotlinFunSpec.Builder(
+                    name = element.name,
+                    actual = FunSpecHelper.overriding(element, owner)
+                )
+            }
+        }
     }
 }
\ No newline at end of file
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XMemberName.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XMemberName.kt
new file mode 100644
index 0000000..8573ba6
--- /dev/null
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XMemberName.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.room.compiler.codegen
+
+import com.squareup.kotlinpoet.MemberName.Companion.member
+
+/**
+ * Represents the name of a member (such as a function or a property).
+ *
+ * @property enclosingClassName The enclosing class name or null if this is a package member,
+ * such as a top-level function.
+ */
+class XMemberName internal constructor(
+    val enclosingClassName: XClassName?,
+    internal val java: JCodeBlock,
+    internal val kotlin: KMemberName
+) {
+    val simpleName = kotlin.simpleName
+
+    companion object {
+        /**
+         * Creates a [XMemberName] that is contained by the receiving class's companion object.
+         *
+         * @param isJvmStatic if the companion object member is annotated with [JvmStatic] or not.
+         * This will cause generated code to be slightly different in Java since the member can be
+         * referenced without the companion object's `INSTANCE`.
+         */
+        fun XClassName.companionMember(
+            simpleName: String,
+            isJvmStatic: Boolean = false
+        ): XMemberName {
+            return XMemberName(
+                enclosingClassName = this,
+                java = if (isJvmStatic) {
+                    JCodeBlock.of("$T.$L", this.java, simpleName)
+                } else {
+                    JCodeBlock.of("$T.INSTANCE.$L", this.java.nestedClass("Companion"), simpleName)
+                },
+                kotlin = this.kotlin.nestedClass("Companion").member(simpleName)
+            )
+        }
+
+        /**
+         * Creates a [XMemberName] is that is contained by the receiving class's package, i.e.
+         * a top-level function or property.
+         *
+         * @see [androidx.room.compiler.processing.XMemberContainer.asClassName]
+         */
+        fun XClassName.packageMember(simpleName: String): XMemberName {
+            return XMemberName(
+                enclosingClassName = null,
+                java = JCodeBlock.of("$T.$L", this.java, simpleName),
+                kotlin = KMemberName(this.packageName, simpleName)
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XPropertySpec.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XPropertySpec.kt
new file mode 100644
index 0000000..36bf690
--- /dev/null
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XPropertySpec.kt
@@ -0,0 +1,84 @@
+/*
+ * 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.room.compiler.codegen
+
+import androidx.room.compiler.codegen.java.JavaPropertySpec
+import androidx.room.compiler.codegen.java.NONNULL_ANNOTATION
+import androidx.room.compiler.codegen.java.NULLABLE_ANNOTATION
+import androidx.room.compiler.codegen.java.toJavaVisibilityModifier
+import androidx.room.compiler.codegen.kotlin.KotlinPropertySpec
+import androidx.room.compiler.codegen.kotlin.toKotlinVisibilityModifier
+import androidx.room.compiler.processing.XNullability
+import com.squareup.javapoet.FieldSpec
+import com.squareup.kotlinpoet.PropertySpec
+import javax.lang.model.element.Modifier
+
+interface XPropertySpec : TargetLanguage {
+
+    val name: String
+
+    interface Builder : TargetLanguage {
+        fun addAnnotation(annotation: XAnnotationSpec): Builder
+        fun initializer(initExpr: XCodeBlock): Builder
+        fun build(): XPropertySpec
+    }
+
+    companion object {
+        fun builder(
+            language: CodeLanguage,
+            name: String,
+            typeName: XTypeName,
+            visibility: VisibilityModifier,
+            isMutable: Boolean = false,
+        ): Builder {
+            return when (language) {
+                CodeLanguage.JAVA -> JavaPropertySpec.Builder(
+                    name,
+                    FieldSpec.builder(typeName.java, name).apply {
+                        val visibilityModifier = visibility.toJavaVisibilityModifier()
+                        // TODO(b/247242374) Add nullability annotations for non-private fields
+                        if (visibilityModifier != Modifier.PRIVATE) {
+                            if (typeName.nullability == XNullability.NULLABLE) {
+                                addAnnotation(NULLABLE_ANNOTATION)
+                            } else if (typeName.nullability == XNullability.NONNULL) {
+                                addAnnotation(NONNULL_ANNOTATION)
+                            }
+                        }
+                        addModifiers(visibilityModifier)
+                        if (!isMutable) {
+                            addModifiers(Modifier.FINAL)
+                        }
+                    }
+                )
+                CodeLanguage.KOTLIN -> KotlinPropertySpec.Builder(
+                    name,
+                    PropertySpec.builder(name, typeName.kotlin).apply {
+                        mutable(isMutable)
+                        addModifiers(visibility.toKotlinVisibilityModifier())
+                    }
+                )
+            }
+        }
+    }
+}
+
+// TODO(b/127483380): Temporary API for XPoet migration.
+// @Deprecated("Temporary API for XPoet migration.")
+fun XPropertySpec.toJavaPoet(): FieldSpec {
+    check(this is JavaPropertySpec)
+    return this.actual
+}
\ No newline at end of file
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 67c01fc..36b9c9e 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
@@ -17,9 +17,11 @@
 package androidx.room.compiler.codegen
 
 import androidx.room.compiler.processing.XNullability
+import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
 import com.squareup.kotlinpoet.asClassName
 import com.squareup.kotlinpoet.asTypeName
 import com.squareup.kotlinpoet.javapoet.JClassName
+import com.squareup.kotlinpoet.javapoet.JParameterizedTypeName
 import com.squareup.kotlinpoet.javapoet.JTypeName
 import com.squareup.kotlinpoet.javapoet.KClassName
 import com.squareup.kotlinpoet.javapoet.KTypeName
@@ -42,6 +44,14 @@
     val isPrimitive: Boolean
         get() = java.isPrimitive
 
+    open fun copy(nullable: Boolean): XTypeName {
+        return XTypeName(
+            java = java,
+            kotlin = kotlin.copy(nullable = nullable),
+            nullability = if (nullable) XNullability.NULLABLE else XNullability.NONNULL
+        )
+    }
+
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other !is XTypeName) return false
@@ -114,7 +124,16 @@
     val simpleNames: List<String> = java.simpleNames()
     val canonicalName: String = java.canonicalName()
 
-    fun copy(nullable: Boolean): XClassName {
+    fun parametrizedBy(
+        vararg typeArguments: XTypeName,
+    ): XTypeName {
+        return XTypeName(
+            java = JParameterizedTypeName.get(java, *typeArguments.map { it.java }.toTypedArray()),
+            kotlin = kotlin.parameterizedBy(typeArguments.map { it.kotlin })
+        )
+    }
+
+    override fun copy(nullable: Boolean): XClassName {
         return XClassName(
             java = java,
             kotlin = kotlin.copy(nullable = nullable) as KClassName,
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XTypeSpec.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XTypeSpec.kt
index 506d1c6..06a281a 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XTypeSpec.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/XTypeSpec.kt
@@ -16,10 +16,15 @@
 
 package androidx.room.compiler.codegen
 
+import androidx.room.compiler.codegen.java.JavaCodeBlock
 import androidx.room.compiler.codegen.java.JavaTypeSpec
+import androidx.room.compiler.codegen.kotlin.KotlinCodeBlock
 import androidx.room.compiler.codegen.kotlin.KotlinTypeSpec
 import androidx.room.compiler.processing.XElement
 import androidx.room.compiler.processing.addOriginatingElement
+import com.squareup.kotlinpoet.javapoet.JTypeSpec
+import com.squareup.kotlinpoet.javapoet.KTypeSpec
+import javax.lang.model.element.Modifier
 
 interface XTypeSpec : TargetLanguage {
 
@@ -27,22 +32,43 @@
 
     interface Builder : TargetLanguage {
         fun superclass(typeName: XTypeName): Builder
+        fun addSuperinterface(typeName: XTypeName): Builder
         fun addAnnotation(annotation: XAnnotationSpec)
-
-        // TODO(b/247241418): Maybe make a XPropertySpec ?
-        fun addProperty(
-            typeName: XTypeName,
-            name: String,
-            visibility: VisibilityModifier,
-            isMutable: Boolean = false,
-            initExpr: XCodeBlock? = null,
-            annotations: List<XAnnotationSpec> = emptyList()
-        ): Builder
-
+        fun addProperty(propertySpec: XPropertySpec): Builder
         fun addFunction(functionSpec: XFunSpec): Builder
+        fun addType(typeSpec: XTypeSpec): Builder
+        fun setVisibility(visibility: VisibilityModifier)
         fun build(): XTypeSpec
 
         companion object {
+
+            fun XTypeSpec.Builder.addOriginatingElement(element: XElement) = apply {
+                when (language) {
+                    CodeLanguage.JAVA -> {
+                        check(this is JavaTypeSpec.Builder)
+                        actual.addOriginatingElement(element)
+                    }
+                    CodeLanguage.KOTLIN -> {
+                        check(this is KotlinTypeSpec.Builder)
+                        actual.addOriginatingElement(element)
+                    }
+                }
+            }
+
+            fun Builder.addProperty(
+                name: String,
+                typeName: XTypeName,
+                visibility: VisibilityModifier,
+                isMutable: Boolean = false,
+                initExpr: XCodeBlock? = null,
+            ) = apply {
+                val builder = XPropertySpec.builder(language, name, typeName, visibility, isMutable)
+                if (initExpr != null) {
+                    builder.initializer(initExpr)
+                }
+                addProperty(builder.build())
+            }
+
             fun Builder.apply(
                 javaTypeBuilder: com.squareup.javapoet.TypeSpec.Builder.() -> Unit,
                 kotlinTypeBuilder: com.squareup.kotlinpoet.TypeSpec.Builder.() -> Unit,
@@ -66,26 +92,59 @@
             return when (language) {
                 CodeLanguage.JAVA -> JavaTypeSpec.Builder(
                     className = className,
-                    actual = com.squareup.javapoet.TypeSpec.classBuilder(className.java)
+                    actual = JTypeSpec.classBuilder(className.java)
+                        .addModifiers(Modifier.FINAL)
                 )
                 CodeLanguage.KOTLIN -> KotlinTypeSpec.Builder(
                     className = className,
-                    actual = com.squareup.kotlinpoet.TypeSpec.classBuilder(className.kotlin)
+                    actual = KTypeSpec.classBuilder(className.kotlin)
                 )
             }
         }
-    }
-}
 
-fun XTypeSpec.Builder.addOriginatingElement(element: XElement) = apply {
-    when (language) {
-        CodeLanguage.JAVA -> {
-            check(this is JavaTypeSpec.Builder)
-            actual.addOriginatingElement(element)
+        fun anonymousClassBuilder(
+            language: CodeLanguage,
+            argsFormat: String = "",
+            vararg args: Any
+        ): Builder {
+            return when (language) {
+                CodeLanguage.JAVA -> JavaTypeSpec.Builder(
+                    className = null,
+                    actual = JTypeSpec.anonymousClassBuilder(
+                        XCodeBlock.of(language, argsFormat, args).let {
+                            check(it is JavaCodeBlock)
+                            it.actual
+                        }
+                    )
+                )
+                CodeLanguage.KOTLIN -> KotlinTypeSpec.Builder(
+                    className = null,
+                    actual = KTypeSpec.anonymousClassBuilder().apply {
+                        if (args.isNotEmpty()) {
+                            addSuperclassConstructorParameter(
+                                XCodeBlock.of(language, argsFormat, args).let {
+                                    check(it is KotlinCodeBlock)
+                                    it.actual
+                                }
+                            )
+                        }
+                    }
+                )
+            }
         }
-        CodeLanguage.KOTLIN -> {
-            check(this is KotlinTypeSpec.Builder)
-            actual.addOriginatingElement(element)
+
+        fun companionObjectBuilder(language: CodeLanguage): Builder {
+            return when (language) {
+                CodeLanguage.JAVA -> JavaTypeSpec.Builder(
+                    className = null,
+                    actual = JTypeSpec.classBuilder("Companion")
+                        .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
+                )
+                CodeLanguage.KOTLIN -> KotlinTypeSpec.Builder(
+                    className = null,
+                    actual = KTypeSpec.companionObjectBuilder()
+                )
+            }
         }
     }
 }
\ No newline at end of file
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/java/JavaCodeBlock.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/java/JavaCodeBlock.kt
index 937afb5..5881201 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/java/JavaCodeBlock.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/java/JavaCodeBlock.kt
@@ -21,6 +21,8 @@
 import androidx.room.compiler.codegen.TargetLanguage
 import androidx.room.compiler.codegen.XCodeBlock
 import androidx.room.compiler.codegen.XFunSpec
+import androidx.room.compiler.codegen.XMemberName
+import androidx.room.compiler.codegen.XPropertySpec
 import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.codegen.XTypeSpec
 
@@ -90,9 +92,11 @@
 
         // Converts '%' place holders to '$' for JavaPoet
         private fun processFormatString(format: String): String {
-            // TODO(b/247241415): Very simple replace for now, but this will not work when emitting
-            //  modulo expressions!
-            return format.replace('%', '$')
+            // Replace KPoet's member name placeholder for a JPoet literal for a XMemberName arg.
+            return format.replace("%M", "\$L")
+                // TODO(b/247241415): Very simple replace for now, but this will not work when
+                //  emitting modulo expressions!
+                .replace('%', '$')
         }
 
         // Unwraps room.compiler.codegen types to their JavaPoet actual
@@ -105,7 +109,9 @@
                 }
                 when (arg) {
                     is XTypeName -> arg.java
+                    is XMemberName -> arg.java
                     is XTypeSpec -> (arg as JavaTypeSpec).actual
+                    is XPropertySpec -> (arg as JavaPropertySpec).actual
                     is XFunSpec -> (arg as JavaFunSpec).actual
                     is XCodeBlock -> (arg as JavaCodeBlock).actual
                     else -> arg
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/java/JavaFunSpec.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/java/JavaFunSpec.kt
index a8113df..470f3b7 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/java/JavaFunSpec.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/java/JavaFunSpec.kt
@@ -31,13 +31,20 @@
 import javax.lang.model.element.Modifier
 
 internal class JavaFunSpec(
+    override val name: String,
     internal val actual: MethodSpec
 ) : JavaLang(), XFunSpec {
 
     internal class Builder(
+        private val name: String,
         internal val actual: MethodSpec.Builder
     ) : JavaLang(), XFunSpec.Builder {
 
+        override fun addAnnotation(annotation: XAnnotationSpec) {
+            require(annotation is JavaAnnotationSpec)
+            actual.addAnnotation(annotation.actual)
+        }
+
         override fun addCode(code: XCodeBlock) = apply {
             require(code is JavaCodeBlock)
             actual.addCode(code.actual)
@@ -89,7 +96,7 @@
             actual.returns(typeName.java)
         }
 
-        override fun build() = JavaFunSpec(actual.build())
+        override fun build() = JavaFunSpec(name, actual.build())
     }
 }
 
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/java/JavaPropertySpec.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/java/JavaPropertySpec.kt
new file mode 100644
index 0000000..9b2a707
--- /dev/null
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/java/JavaPropertySpec.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.room.compiler.codegen.java
+
+import androidx.room.compiler.codegen.XAnnotationSpec
+import androidx.room.compiler.codegen.XCodeBlock
+import androidx.room.compiler.codegen.XPropertySpec
+import com.squareup.javapoet.FieldSpec
+
+internal class JavaPropertySpec(
+    override val name: String,
+    internal val actual: FieldSpec
+) : JavaLang(), XPropertySpec {
+
+    internal class Builder(
+        private val name: String,
+        internal val actual: FieldSpec.Builder
+    ) : JavaLang(), XPropertySpec.Builder {
+
+        override fun addAnnotation(annotation: XAnnotationSpec) = apply {
+            require(annotation is JavaAnnotationSpec)
+            actual.addAnnotation(annotation.actual)
+        }
+
+        override fun initializer(initExpr: XCodeBlock) = apply {
+            require(initExpr is JavaCodeBlock)
+            actual.initializer(initExpr.actual)
+        }
+
+        override fun build(): XPropertySpec {
+            return JavaPropertySpec(name, actual.build())
+        }
+    }
+}
\ No newline at end of file
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/java/JavaTypeSpec.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/java/JavaTypeSpec.kt
index 952ef06..f281154 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/java/JavaTypeSpec.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/java/JavaTypeSpec.kt
@@ -20,63 +20,43 @@
 import androidx.room.compiler.codegen.VisibilityModifier
 import androidx.room.compiler.codegen.XAnnotationSpec
 import androidx.room.compiler.codegen.XClassName
-import androidx.room.compiler.codegen.XCodeBlock
 import androidx.room.compiler.codegen.XFunSpec
+import androidx.room.compiler.codegen.XPropertySpec
 import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.codegen.XTypeSpec
-import androidx.room.compiler.processing.XNullability
-import com.squareup.javapoet.FieldSpec
 import com.squareup.kotlinpoet.javapoet.JTypeSpec
-import javax.lang.model.element.Modifier
 
 internal class JavaTypeSpec(
-    override val className: XClassName,
+    private val _className: XClassName?,
     internal val actual: JTypeSpec
 ) : JavaLang(), XTypeSpec {
 
+    override val className: XClassName
+        get() {
+            checkNotNull(_className) { "Anonymous classes have no name." }
+            return _className
+        }
+
     internal class Builder(
-        private val className: XClassName,
+        private val className: XClassName?,
         internal val actual: JTypeSpecBuilder
     ) : JavaLang(), XTypeSpec.Builder {
         override fun superclass(typeName: XTypeName) = apply {
             actual.superclass(typeName.java)
         }
 
+        override fun addSuperinterface(typeName: XTypeName) = apply {
+            actual.addSuperinterface(typeName.java)
+        }
+
         override fun addAnnotation(annotation: XAnnotationSpec) {
             require(annotation is JavaAnnotationSpec)
             actual.addAnnotation(annotation.actual)
         }
 
-        override fun addProperty(
-            typeName: XTypeName,
-            name: String,
-            visibility: VisibilityModifier,
-            isMutable: Boolean,
-            initExpr: XCodeBlock?,
-            annotations: List<XAnnotationSpec>
-        ) = apply {
-            actual.addField(
-                FieldSpec.builder(typeName.java, name).apply {
-                    val visibilityModifier = visibility.toJavaVisibilityModifier()
-                    // TODO(b/247242374) Add nullability annotations for non-private fields
-                    if (visibilityModifier != Modifier.PRIVATE) {
-                        if (typeName.nullability == XNullability.NULLABLE) {
-                            addAnnotation(NULLABLE_ANNOTATION)
-                        } else if (typeName.nullability == XNullability.NONNULL) {
-                            addAnnotation(NONNULL_ANNOTATION)
-                        }
-                    }
-                    addModifiers(visibilityModifier)
-                    if (!isMutable) {
-                        addModifiers(Modifier.FINAL)
-                    }
-                    initExpr?.let {
-                        require(it is JavaCodeBlock)
-                        initializer(it.actual)
-                    }
-                    // TODO(b/247247439): Add other annotations
-                }.build()
-            )
+        override fun addProperty(propertySpec: XPropertySpec) = apply {
+            require(propertySpec is JavaPropertySpec)
+            actual.addField(propertySpec.actual)
         }
 
         override fun addFunction(functionSpec: XFunSpec) = apply {
@@ -84,6 +64,15 @@
             actual.addMethod(functionSpec.actual)
         }
 
+        override fun addType(typeSpec: XTypeSpec) = apply {
+            require(typeSpec is JavaTypeSpec)
+            actual.addType(typeSpec.actual)
+        }
+
+        override fun setVisibility(visibility: VisibilityModifier) {
+            actual.addModifiers(visibility.toJavaVisibilityModifier())
+        }
+
         override fun build(): XTypeSpec {
             return JavaTypeSpec(className, actual.build())
         }
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/kotlin/KotlinCodeBlock.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/kotlin/KotlinCodeBlock.kt
index 1daab89..5c29965 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/kotlin/KotlinCodeBlock.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/kotlin/KotlinCodeBlock.kt
@@ -22,6 +22,8 @@
 import androidx.room.compiler.codegen.TargetLanguage
 import androidx.room.compiler.codegen.XCodeBlock
 import androidx.room.compiler.codegen.XFunSpec
+import androidx.room.compiler.codegen.XMemberName
+import androidx.room.compiler.codegen.XPropertySpec
 import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.codegen.XTypeSpec
 
@@ -113,7 +115,9 @@
                 }
                 when (arg) {
                     is XTypeName -> arg.kotlin
+                    is XMemberName -> arg.kotlin
                     is XTypeSpec -> (arg as KotlinTypeSpec).actual
+                    is XPropertySpec -> (arg as KotlinPropertySpec).actual
                     is XFunSpec -> (arg as KotlinFunSpec).actual
                     is XCodeBlock -> (arg as KotlinCodeBlock).actual
                     else -> arg
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/kotlin/KotlinFunSpec.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/kotlin/KotlinFunSpec.kt
index fdcb73f..4c90427 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/kotlin/KotlinFunSpec.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/kotlin/KotlinFunSpec.kt
@@ -27,13 +27,20 @@
 import com.squareup.kotlinpoet.UNIT
 
 internal class KotlinFunSpec(
+    override val name: String,
     internal val actual: FunSpec
 ) : KotlinLang(), XFunSpec {
 
     internal class Builder(
+        private val name: String,
         internal val actual: FunSpec.Builder
     ) : KotlinLang(), XFunSpec.Builder {
 
+        override fun addAnnotation(annotation: XAnnotationSpec) {
+            require(annotation is KotlinAnnotationSpec)
+            actual.addAnnotation(annotation.actual)
+        }
+
         override fun addCode(code: XCodeBlock) = apply {
             require(code is KotlinCodeBlock)
             actual.addCode(code.actual)
@@ -67,7 +74,7 @@
             actual.returns(typeName.kotlin)
         }
 
-        override fun build() = KotlinFunSpec(actual.build())
+        override fun build() = KotlinFunSpec(name, actual.build())
     }
 }
 
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/kotlin/KotlinPropertySpec.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/kotlin/KotlinPropertySpec.kt
new file mode 100644
index 0000000..66348a5
--- /dev/null
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/kotlin/KotlinPropertySpec.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.room.compiler.codegen.kotlin
+
+import androidx.room.compiler.codegen.XAnnotationSpec
+import androidx.room.compiler.codegen.XCodeBlock
+import androidx.room.compiler.codegen.XPropertySpec
+import com.squareup.kotlinpoet.PropertySpec
+
+internal class KotlinPropertySpec(
+    override val name: String,
+    internal val actual: PropertySpec
+) : KotlinLang(), XPropertySpec {
+
+    internal class Builder(
+        private val name: String,
+        internal val actual: PropertySpec.Builder
+    ) : KotlinLang(), XPropertySpec.Builder {
+
+        override fun addAnnotation(annotation: XAnnotationSpec) = apply {
+            require(annotation is KotlinAnnotationSpec)
+            actual.addAnnotation(annotation.actual)
+        }
+
+        override fun initializer(initExpr: XCodeBlock) = apply {
+            require(initExpr is KotlinCodeBlock)
+            actual.initializer(initExpr.actual)
+        }
+
+        override fun build(): XPropertySpec {
+            return KotlinPropertySpec(name, actual.build())
+        }
+    }
+}
\ No newline at end of file
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/kotlin/KotlinTypeSpec.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/kotlin/KotlinTypeSpec.kt
index da898fc..425a758 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/kotlin/KotlinTypeSpec.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/codegen/kotlin/KotlinTypeSpec.kt
@@ -20,50 +20,43 @@
 import androidx.room.compiler.codegen.VisibilityModifier
 import androidx.room.compiler.codegen.XAnnotationSpec
 import androidx.room.compiler.codegen.XClassName
-import androidx.room.compiler.codegen.XCodeBlock
 import androidx.room.compiler.codegen.XFunSpec
+import androidx.room.compiler.codegen.XPropertySpec
 import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.codegen.XTypeSpec
-import com.squareup.kotlinpoet.PropertySpec
 import com.squareup.kotlinpoet.javapoet.KTypeSpec
 
 internal class KotlinTypeSpec(
-    override val className: XClassName,
+    private val _className: XClassName?,
     internal val actual: KTypeSpec
 ) : KotlinLang(), XTypeSpec {
 
+    override val className: XClassName
+        get() {
+            checkNotNull(_className) { "Anonymous classes have no name." }
+            return _className
+        }
+
     internal class Builder(
-        private val className: XClassName,
+        private val className: XClassName?,
         internal val actual: KTypeSpecBuilder
     ) : KotlinLang(), XTypeSpec.Builder {
         override fun superclass(typeName: XTypeName) = apply {
             actual.superclass(typeName.kotlin)
         }
 
+        override fun addSuperinterface(typeName: XTypeName) = apply {
+            actual.addSuperinterface(typeName.kotlin)
+        }
+
         override fun addAnnotation(annotation: XAnnotationSpec) {
             check(annotation is KotlinAnnotationSpec)
             actual.addAnnotation(annotation.actual)
         }
 
-        override fun addProperty(
-            typeName: XTypeName,
-            name: String,
-            visibility: VisibilityModifier,
-            isMutable: Boolean,
-            initExpr: XCodeBlock?,
-            annotations: List<XAnnotationSpec>
-        ) = apply {
-            actual.addProperty(
-                PropertySpec.builder(name, typeName.kotlin).apply {
-                    mutable(isMutable)
-                    addModifiers(visibility.toKotlinVisibilityModifier())
-                    initExpr?.let {
-                        require(it is KotlinCodeBlock)
-                        initializer(it.actual)
-                    }
-                    // TODO(b/247247439): Add other annotations
-                }.build()
-            )
+        override fun addProperty(propertySpec: XPropertySpec) = apply {
+            require(propertySpec is KotlinPropertySpec)
+            actual.addProperty(propertySpec.actual)
         }
 
         override fun addFunction(functionSpec: XFunSpec) = apply {
@@ -71,6 +64,15 @@
             actual.addFunction(functionSpec.actual)
         }
 
+        override fun addType(typeSpec: XTypeSpec) = apply {
+            require(typeSpec is KotlinTypeSpec)
+            actual.addType(typeSpec.actual)
+        }
+
+        override fun setVisibility(visibility: VisibilityModifier) {
+            actual.addModifiers(visibility.toKotlinVisibilityModifier())
+        }
+
         override fun build(): XTypeSpec {
             return KotlinTypeSpec(className, actual.build())
         }
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/KotlinPoetExt.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/KotlinPoetExt.kt
index 407495d..d058b81 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/KotlinPoetExt.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/KotlinPoetExt.kt
@@ -16,6 +16,8 @@
 
 package androidx.room.compiler.processing
 
+import com.squareup.kotlinpoet.FunSpec
+import com.squareup.kotlinpoet.KModifier
 import com.squareup.kotlinpoet.OriginatingElementsHolder
 import com.squareup.kotlinpoet.ParameterizedTypeName
 import com.squareup.kotlinpoet.TypeName
@@ -41,4 +43,46 @@
     } else {
         this
     }
+}
+
+object FunSpecHelper {
+    fun overriding(
+        elm: XMethodElement,
+        owner: XType
+    ): FunSpec.Builder {
+        val asMember = elm.asMemberOf(owner)
+        return overriding(
+            executableElement = elm,
+            resolvedType = asMember
+        )
+    }
+
+    private fun overriding(
+        executableElement: XMethodElement,
+        resolvedType: XMethodType
+    ): FunSpec.Builder {
+        return FunSpec.builder(executableElement.name).apply {
+            addModifiers(KModifier.OVERRIDE)
+            if (executableElement.isPublic()) {
+                addModifiers(KModifier.PUBLIC)
+            } else if (executableElement.isProtected()) {
+                addModifiers(KModifier.PROTECTED)
+            }
+            // TODO(b/251316420): Add type variable names
+            val isVarArgs = executableElement.isVarArgs()
+            resolvedType.parameterTypes.forEachIndexed { index, paramType ->
+                val modifiers = if (isVarArgs && index == resolvedType.parameterTypes.size - 1) {
+                    arrayOf(KModifier.VARARG)
+                } else {
+                    emptyArray()
+                }
+                addParameter(
+                    executableElement.parameters[index].name,
+                    paramType.asTypeName().kotlin,
+                    *modifiers
+                )
+            }
+            returns(resolvedType.returnType.asTypeName().kotlin)
+        }
+    }
 }
\ No newline at end of file
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacProcessingEnv.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacProcessingEnv.kt
index 0d0a3b0..da6d0de 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacProcessingEnv.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacProcessingEnv.kt
@@ -101,6 +101,14 @@
                 elementNullability = XNullability.NONNULL
             )
         }
+        // check no types, such as 'void'
+        NO_TYPES[qName]?.let {
+            return wrap(
+                typeMirror = typeUtils.getNoType(it),
+                kotlinType = null,
+                elementNullability = XNullability.NONNULL
+            )
+        }
         return findTypeElement(qName)?.type
     }
 
@@ -299,5 +307,11 @@
         }.associateBy {
             it.name.lowercase(Locale.US)
         }
+        val NO_TYPES = listOf(
+            TypeKind.VOID,
+            TypeKind.NONE
+        ).associateBy {
+            it.name.lowercase(Locale.US)
+        }
     }
 }
diff --git a/room/room-compiler/build.gradle b/room/room-compiler/build.gradle
index 9a343d5..15f305c 100644
--- a/room/room-compiler/build.gradle
+++ b/room/room-compiler/build.gradle
@@ -110,7 +110,7 @@
     testImplementation(project(":room:room-compiler-processing-testing"))
     testImplementation(libs.junit)
     testImplementation(libs.jsr250)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.antlr4)
     testImplementation(libs.kotlinCompilerEmbeddable)
     testImplementation(fileTree(
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/DatabaseProcessingStep.kt b/room/room-compiler/src/main/kotlin/androidx/room/DatabaseProcessingStep.kt
index ba54bdb..acad3d6 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/DatabaseProcessingStep.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/DatabaseProcessingStep.kt
@@ -94,7 +94,7 @@
                 DaoWriter(
                     daoMethod.dao,
                     db.element,
-                    CodeLanguage.JAVA
+                    context.codeLanguage
                 ).write(context.processingEnv)
             }
         }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/ext/javapoet_ext.kt b/room/room-compiler/src/main/kotlin/androidx/room/ext/javapoet_ext.kt
index dbabd96..a0dc5fc 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/ext/javapoet_ext.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/ext/javapoet_ext.kt
@@ -54,24 +54,24 @@
 }
 
 object RoomTypeNames {
-    val STRING_UTIL: ClassName = ClassName.get("$ROOM_PACKAGE.util", "StringUtil")
-    val ROOM_DB: ClassName = ClassName.get(ROOM_PACKAGE, "RoomDatabase")
+    val STRING_UTIL: XClassName = XClassName.get("$ROOM_PACKAGE.util", "StringUtil")
+    val ROOM_DB: XClassName = XClassName.get(ROOM_PACKAGE, "RoomDatabase")
     val ROOM_DB_KT: ClassName = ClassName.get(ROOM_PACKAGE, "RoomDatabaseKt")
     val ROOM_DB_CONFIG: ClassName = ClassName.get(ROOM_PACKAGE, "DatabaseConfiguration")
-    val INSERTION_ADAPTER: ClassName =
-        ClassName.get(ROOM_PACKAGE, "EntityInsertionAdapter")
-    val UPSERTION_ADAPTER: ClassName =
-        ClassName.get(ROOM_PACKAGE, "EntityUpsertionAdapter")
-    val DELETE_OR_UPDATE_ADAPTER: ClassName =
-        ClassName.get(ROOM_PACKAGE, "EntityDeletionOrUpdateAdapter")
-    val SHARED_SQLITE_STMT: ClassName =
-        ClassName.get(ROOM_PACKAGE, "SharedSQLiteStatement")
+    val INSERTION_ADAPTER: XClassName =
+        XClassName.get(ROOM_PACKAGE, "EntityInsertionAdapter")
+    val UPSERTION_ADAPTER: XClassName =
+        XClassName.get(ROOM_PACKAGE, "EntityUpsertionAdapter")
+    val DELETE_OR_UPDATE_ADAPTER: XClassName =
+        XClassName.get(ROOM_PACKAGE, "EntityDeletionOrUpdateAdapter")
+    val SHARED_SQLITE_STMT: XClassName =
+        XClassName.get(ROOM_PACKAGE, "SharedSQLiteStatement")
     val INVALIDATION_TRACKER: ClassName =
         ClassName.get(ROOM_PACKAGE, "InvalidationTracker")
     val INVALIDATION_OBSERVER: ClassName =
         ClassName.get("$ROOM_PACKAGE.InvalidationTracker", "Observer")
-    val ROOM_SQL_QUERY: ClassName =
-        ClassName.get(ROOM_PACKAGE, "RoomSQLiteQuery")
+    val ROOM_SQL_QUERY: XClassName =
+        XClassName.get(ROOM_PACKAGE, "RoomSQLiteQuery")
     val OPEN_HELPER: ClassName =
         ClassName.get(ROOM_PACKAGE, "RoomOpenHelper")
     val OPEN_HELPER_DELEGATE: ClassName =
@@ -92,17 +92,17 @@
         ClassName.get("$ROOM_PACKAGE.util", "ViewInfo")
     val LIMIT_OFFSET_DATA_SOURCE: ClassName =
         ClassName.get("$ROOM_PACKAGE.paging", "LimitOffsetDataSource")
-    val DB_UTIL: ClassName =
-        ClassName.get("$ROOM_PACKAGE.util", "DBUtil")
-    val CURSOR_UTIL: ClassName =
-        ClassName.get("$ROOM_PACKAGE.util", "CursorUtil")
+    val DB_UTIL: XClassName =
+        XClassName.get("$ROOM_PACKAGE.util", "DBUtil")
+    val CURSOR_UTIL: XClassName =
+        XClassName.get("$ROOM_PACKAGE.util", "CursorUtil")
     val MIGRATION: ClassName = ClassName.get("$ROOM_PACKAGE.migration", "Migration")
     val AUTO_MIGRATION_SPEC: ClassName = ClassName.get(
         "$ROOM_PACKAGE.migration",
         "AutoMigrationSpec"
     )
-    val UUID_UTIL: ClassName =
-        ClassName.get("$ROOM_PACKAGE.util", "UUIDUtil")
+    val UUID_UTIL: XClassName =
+        XClassName.get("$ROOM_PACKAGE.util", "UUIDUtil")
     val AMBIGUOUS_COLUMN_RESOLVER: ClassName =
         ClassName.get(ROOM_PACKAGE, "AmbiguousColumnResolver")
 }
@@ -152,9 +152,6 @@
     val STRING = ClassName.get("java.lang", "String")
     val INTEGER = ClassName.get("java.lang", "Integer")
     val OPTIONAL = ClassName.get("java.util", "Optional")
-    val ILLEGAL_ARG_EXCEPTION = ClassName.get(
-        "java.lang", "IllegalArgumentException"
-    )
     val UUID = ClassName.get("java.util", "UUID")
 }
 
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/CustomConverterProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/CustomConverterProcessor.kt
index c1a858d..1183281 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/CustomConverterProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/CustomConverterProcessor.kt
@@ -35,7 +35,6 @@
 import androidx.room.solver.types.CustomTypeConverterWrapper
 import androidx.room.vo.BuiltInConverterFlags
 import androidx.room.vo.CustomTypeConverter
-import java.util.LinkedHashSet
 
 /**
  * Processes classes that are referenced in TypeConverters annotations.
@@ -133,7 +132,7 @@
         }
         return converterMethods.mapNotNull {
             processMethod(
-                container = element.type,
+                container = element,
                 isContainerKotlinObject = isKotlinObjectDeclaration,
                 methodElement = it,
                 isProvidedConverter = isProvidedConverter
@@ -142,12 +141,12 @@
     }
 
     private fun processMethod(
-        container: XType,
+        container: XTypeElement,
         methodElement: XMethodElement,
         isContainerKotlinObject: Boolean,
         isProvidedConverter: Boolean
     ): CustomTypeConverter? {
-        val asMember = methodElement.asMemberOf(container)
+        val asMember = methodElement.asMemberOf(container.type)
         val returnType = asMember.returnType
         val invalidReturnType = returnType.isInvalidReturnType()
         context.checker.check(
@@ -168,7 +167,7 @@
             return null
         }
         val param = params.map {
-            it.asMemberOf(container)
+            it.asMemberOf(container.type)
         }.first()
         context.checker.notUnbound(param.typeName, params[0], TYPE_CONVERTER_UNBOUND_GENERIC)
         return CustomTypeConverter(
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/DaoProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/DaoProcessor.kt
index f4f41f4..898a915 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/DaoProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/DaoProcessor.kt
@@ -18,12 +18,12 @@
 
 import androidx.room.Delete
 import androidx.room.Insert
-import androidx.room.Upsert
 import androidx.room.Query
 import androidx.room.RawQuery
 import androidx.room.SkipQueryVerification
 import androidx.room.Transaction
 import androidx.room.Update
+import androidx.room.Upsert
 import androidx.room.compiler.processing.XConstructorElement
 import androidx.room.compiler.processing.XMethodElement
 import androidx.room.compiler.processing.XType
@@ -217,7 +217,7 @@
                 it.parameters[0].type.isAssignableFrom(dbType)
         }
         val constructorParamType = if (goodConstructor != null) {
-            goodConstructor.parameters[0].type.typeName
+            goodConstructor.parameters[0].type.asTypeName()
         } else {
             validateEmptyConstructor(constructors)
             null
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/DatabaseProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/DatabaseProcessor.kt
index 52f0e4a..2c15c2a 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/DatabaseProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/DatabaseProcessor.kt
@@ -23,7 +23,7 @@
 import androidx.room.compiler.processing.XElement
 import androidx.room.compiler.processing.XType
 import androidx.room.compiler.processing.XTypeElement
-import androidx.room.ext.RoomTypeNames
+import androidx.room.ext.RoomTypeNames.ROOM_DB
 import androidx.room.migration.bundle.DatabaseBundle
 import androidx.room.migration.bundle.SchemaBundle
 import androidx.room.processor.ProcessorErrors.AUTO_MIGRATION_FOUND_BUT_EXPORT_SCHEMA_OFF
@@ -53,7 +53,7 @@
 
     val roomDatabaseType: XType by lazy {
         context.processingEnv.requireType(
-            RoomTypeNames.ROOM_DB.packageName() + "." + RoomTypeNames.ROOM_DB.simpleName()
+            ROOM_DB.toJavaPoet().packageName() + "." + ROOM_DB.toJavaPoet().simpleName()
         )
     }
 
@@ -94,7 +94,7 @@
             it.isAbstract()
         }.filterNot {
             // remove methods that belong to room
-            it.enclosingElement.className == RoomTypeNames.ROOM_DB
+            it.enclosingElement.className == ROOM_DB.toJavaPoet()
         }.mapNotNull { executable ->
             // TODO when we add support for non Dao return types (e.g. database), this code needs
             // to change
@@ -376,12 +376,12 @@
         daoMethods.forEach { daoMethod ->
             daoMethod.dao.deleteOrUpdateShortcutMethods.forEach { method ->
                 method.entities.forEach {
-                    check(method.element, daoMethod.dao, it.value.entityTypeName)
+                    check(method.element, daoMethod.dao, it.value.entityTypeName.toJavaPoet())
                 }
             }
             daoMethod.dao.insertOrUpsertShortcutMethods.forEach { method ->
                 method.entities.forEach {
-                    check(method.element, daoMethod.dao, it.value.entityTypeName)
+                    check(method.element, daoMethod.dao, it.value.entityTypeName.toJavaPoet())
                 }
             }
         }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
index e951537..5e293f9 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
@@ -18,13 +18,14 @@
 
 import androidx.room.Delete
 import androidx.room.Insert
-import androidx.room.Upsert
 import androidx.room.Query
 import androidx.room.RawQuery
 import androidx.room.RewriteQueriesToDropUnusedColumns
 import androidx.room.Update
+import androidx.room.Upsert
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.ext.KotlinTypeNames
-import androidx.room.ext.RoomTypeNames
+import androidx.room.ext.RoomTypeNames.ROOM_DB
 import androidx.room.ext.SupportDbTypeNames
 import androidx.room.parser.QueryType
 import androidx.room.parser.SQLTypeAffinity
@@ -225,7 +226,7 @@
         "annotated with @Entity or a collection/array of it."
 
     val DB_MUST_EXTEND_ROOM_DB = "Classes annotated with @Database should extend " +
-        RoomTypeNames.ROOM_DB
+        ROOM_DB.toJavaPoet()
 
     val OBSERVABLE_QUERY_NOTHING_TO_OBSERVE = "Observable query return type (LiveData, Flowable" +
         ", DataSource, DataSourceFactory etc) can only be used with SELECT queries that" +
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/CodeGenScope.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/CodeGenScope.kt
index c9ffebb..9d622a4 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/CodeGenScope.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/CodeGenScope.kt
@@ -16,11 +16,11 @@
 
 package androidx.room.solver
 
+import androidx.annotation.VisibleForTesting
 import androidx.room.compiler.codegen.JCodeBlockBuilder
 import androidx.room.compiler.codegen.XCodeBlock
 import androidx.room.compiler.codegen.XCodeBlock.Builder.Companion.apply
 import androidx.room.writer.TypeWriter
-import com.google.common.annotations.VisibleForTesting
 
 /**
  * Defines a code generation scope where we can provide temporary variables, global variables etc
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
index 70b2209..f14d8d7 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.solver
 
+import androidx.annotation.VisibleForTesting
 import androidx.room.compiler.processing.XType
 import androidx.room.compiler.processing.isArray
 import androidx.room.compiler.processing.isEnum
@@ -77,7 +78,7 @@
 import androidx.room.solver.query.result.QueryResultBinder
 import androidx.room.solver.query.result.RowAdapter
 import androidx.room.solver.query.result.SingleColumnRowAdapter
-import androidx.room.solver.query.result.SingleEntityQueryResultAdapter
+import androidx.room.solver.query.result.SingleItemQueryResultAdapter
 import androidx.room.solver.query.result.SingleNamedColumnRowAdapter
 import androidx.room.solver.shortcut.binder.DeleteOrUpdateMethodBinder
 import androidx.room.solver.shortcut.binder.InsertOrUpsertMethodBinder
@@ -112,7 +113,6 @@
 import androidx.room.vo.MapInfo
 import androidx.room.vo.ShortcutQueryParameter
 import androidx.room.vo.isEnabled
-import com.google.common.annotations.VisibleForTesting
 import com.google.common.collect.ImmutableList
 import com.google.common.collect.ImmutableListMultimap
 import com.google.common.collect.ImmutableMap
@@ -132,7 +132,7 @@
      * first type adapter has the highest priority
      */
     private val columnTypeAdapters: List<ColumnTypeAdapter>,
-    @VisibleForTesting
+    @get:VisibleForTesting
     internal val typeConverterStore: TypeConverterStore,
     private val builtInConverterFlags: BuiltInConverterFlags
 ) {
@@ -370,7 +370,7 @@
         val typeElement = type.typeElement
         return when {
             builtInConverterFlags.enums.isEnabled() &&
-                typeElement?.isEnum() == true -> EnumColumnTypeAdapter(typeElement)
+                typeElement?.isEnum() == true -> EnumColumnTypeAdapter(typeElement, type)
             builtInConverterFlags.uuid.isEnabled() &&
                 type.isUUID() -> UuidColumnTypeAdapter(type)
             else -> null
@@ -482,7 +482,7 @@
             return ArrayQueryResultAdapter(rowAdapter)
         } else if (typeMirror.typeArguments.isEmpty()) {
             val rowAdapter = findRowAdapter(typeMirror, query) ?: return null
-            return SingleEntityQueryResultAdapter(rowAdapter)
+            return SingleItemQueryResultAdapter(rowAdapter)
         } else if (typeMirror.rawType.typeName == GuavaBaseTypeNames.OPTIONAL) {
             // Handle Guava Optional by unpacking its generic type argument and adapting that.
             // The Optional adapter will reappend the Optional type.
@@ -492,7 +492,7 @@
             val rowAdapter = findRowAdapter(typeArg.makeNullable(), query) ?: return null
             return GuavaOptionalQueryResultAdapter(
                 typeArg = typeArg,
-                resultAdapter = SingleEntityQueryResultAdapter(rowAdapter)
+                resultAdapter = SingleItemQueryResultAdapter(rowAdapter)
             )
         } else if (typeMirror.rawType.typeName == CommonTypeNames.OPTIONAL) {
             // Handle java.util.Optional similarly.
@@ -502,7 +502,7 @@
             val rowAdapter = findRowAdapter(typeArg.makeNullable(), query) ?: return null
             return OptionalQueryResultAdapter(
                 typeArg = typeArg,
-                resultAdapter = SingleEntityQueryResultAdapter(rowAdapter)
+                resultAdapter = SingleItemQueryResultAdapter(rowAdapter)
             )
         } else if (typeMirror.isTypeOf(ImmutableList::class)) {
             val typeArg = typeMirror.typeArguments.first().extendsBoundOrSelf()
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/prepared/binder/InstantPreparedQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/prepared/binder/InstantPreparedQueryResultBinder.kt
index 231ead9..b280628 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/prepared/binder/InstantPreparedQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/prepared/binder/InstantPreparedQueryResultBinder.kt
@@ -16,10 +16,8 @@
 
 package androidx.room.solver.prepared.binder
 
-import androidx.room.ext.N
 import androidx.room.solver.CodeGenScope
 import androidx.room.solver.prepared.result.PreparedQueryResultAdapter
-import androidx.room.writer.DaoWriter
 import com.squareup.javapoet.FieldSpec
 
 /**
@@ -34,8 +32,8 @@
         dbField: FieldSpec,
         scope: CodeGenScope
     ) {
-        scope.builder().apply {
-            addStatement("$N.assertNotSuspendingTransaction()", DaoWriter.dbField)
+        scope.builder.apply {
+            addStatement("%N.assertNotSuspendingTransaction()", dbField)
         }
         adapter?.executeAndReturn(
             stmtQueryVal = scope.prepareQueryStmtBlock(),
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/BaseObservableQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/BaseObservableQueryResultBinder.kt
index 903180c..8a912f0 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/BaseObservableQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/BaseObservableQueryResultBinder.kt
@@ -20,7 +20,7 @@
 import androidx.room.ext.AndroidTypeNames.CURSOR
 import androidx.room.ext.L
 import androidx.room.ext.N
-import androidx.room.ext.RoomTypeNames
+import androidx.room.ext.RoomTypeNames.DB_UTIL
 import androidx.room.ext.T
 import androidx.room.solver.CodeGenScope
 import com.squareup.javapoet.FieldSpec
@@ -31,8 +31,9 @@
  * Base class for query result binders that observe the database. It includes common functionality
  * like creating a finalizer to release the query or creating the actual adapter call code.
  */
-abstract class BaseObservableQueryResultBinder(adapter: QueryResultAdapter?) :
-    QueryResultBinder(adapter) {
+abstract class BaseObservableQueryResultBinder(
+    adapter: QueryResultAdapter?
+) : QueryResultBinder(adapter) {
 
     protected fun createFinalizeMethod(roomSQLiteQueryVar: String): MethodSpec {
         return MethodSpec.methodBuilder("finalize").apply {
@@ -64,7 +65,7 @@
                 "final $T $L = $T.query($N, $L, $L, $L)",
                 CURSOR.toJavaPoet(),
                 cursorVar,
-                RoomTypeNames.DB_UTIL,
+                DB_UTIL.toJavaPoet(),
                 dbField,
                 roomSQLiteQueryVar,
                 if (shouldCopyCursor) "true" else "false",
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineFlowResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineFlowResultBinder.kt
index ce7b50f..d0dc71d 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineFlowResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineFlowResultBinder.kt
@@ -16,15 +16,16 @@
 
 package androidx.room.solver.query.result
 
+import androidx.room.compiler.codegen.XPropertySpec
+import androidx.room.compiler.codegen.toJavaPoet
+import androidx.room.compiler.processing.XType
 import androidx.room.ext.CallableTypeSpecBuilder
 import androidx.room.ext.L
 import androidx.room.ext.N
 import androidx.room.ext.RoomCoroutinesTypeNames
 import androidx.room.ext.T
 import androidx.room.ext.arrayTypeName
-import androidx.room.compiler.processing.XType
 import androidx.room.solver.CodeGenScope
-import com.squareup.javapoet.FieldSpec
 
 /**
  * Binds the result of a of a Kotlin Coroutine Flow<T>
@@ -38,10 +39,11 @@
     override fun convertAndReturn(
         roomSQLiteQueryVar: String,
         canReleaseQuery: Boolean,
-        dbField: FieldSpec,
+        dbProperty: XPropertySpec,
         inTransaction: Boolean,
         scope: CodeGenScope
     ) {
+        val dbField = dbProperty.toJavaPoet()
         val callableImpl = CallableTypeSpecBuilder(typeArg.typeName) {
             createRunQueryAndReturnStatements(
                 builder = this,
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineResultBinder.kt
index 74142bc..4239b12 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CoroutineResultBinder.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.solver.query.result
 
+import androidx.room.compiler.codegen.XPropertySpec
 import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.XType
 import androidx.room.ext.AndroidTypeNames
@@ -24,7 +25,7 @@
 import androidx.room.ext.L
 import androidx.room.ext.N
 import androidx.room.ext.RoomCoroutinesTypeNames
-import androidx.room.ext.RoomTypeNames
+import androidx.room.ext.RoomTypeNames.DB_UTIL
 import androidx.room.ext.T
 import androidx.room.solver.CodeGenScope
 import com.squareup.javapoet.FieldSpec
@@ -42,16 +43,17 @@
     override fun convertAndReturn(
         roomSQLiteQueryVar: String,
         canReleaseQuery: Boolean,
-        dbField: FieldSpec,
+        dbProperty: XPropertySpec,
         inTransaction: Boolean,
         scope: CodeGenScope
     ) {
+        val dbField = dbProperty.toJavaPoet()
         val cancellationSignalVar = scope.getTmpVar("_cancellationSignal")
         scope.builder().addStatement(
             "final $T $L = $T.createCancellationSignal()",
             AndroidTypeNames.CANCELLATION_SIGNAL,
             cancellationSignalVar,
-            RoomTypeNames.DB_UTIL
+            DB_UTIL.toJavaPoet()
         )
 
         val callableImpl = CallableTypeSpecBuilder(typeArg.typeName) {
@@ -102,7 +104,7 @@
                 "final $T $L = $T.query($N, $L, $L, $L)",
                 CURSOR.toJavaPoet(),
                 cursorVar,
-                RoomTypeNames.DB_UTIL,
+                DB_UTIL.toJavaPoet(),
                 dbField,
                 roomSQLiteQueryVar,
                 if (shouldCopyCursor) "true" else "false",
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CursorQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CursorQueryResultBinder.kt
index 48fed6b..d8afaf5 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CursorQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/CursorQueryResultBinder.kt
@@ -16,37 +16,38 @@
 
 package androidx.room.solver.query.result
 
-import androidx.room.compiler.codegen.toJavaPoet
-import androidx.room.ext.AndroidTypeNames.CURSOR
-import androidx.room.ext.L
-import androidx.room.ext.N
-import androidx.room.ext.T
+import androidx.room.compiler.codegen.XCodeBlock.Builder.Companion.addLocalVal
+import androidx.room.compiler.codegen.XPropertySpec
+import androidx.room.ext.AndroidTypeNames
 import androidx.room.solver.CodeGenScope
-import com.squareup.javapoet.FieldSpec
 
 class CursorQueryResultBinder : QueryResultBinder(NO_OP_RESULT_ADAPTER) {
     override fun convertAndReturn(
         roomSQLiteQueryVar: String,
         canReleaseQuery: Boolean,
-        dbField: FieldSpec,
+        dbProperty: XPropertySpec,
         inTransaction: Boolean,
         scope: CodeGenScope
     ) {
-        val builder = scope.builder()
         val transactionWrapper = if (inTransaction) {
-            builder.transactionWrapper(dbField)
+            scope.builder.transactionWrapper(dbProperty.name)
         } else {
             null
         }
         transactionWrapper?.beginTransactionWithControlFlow()
-        val resultName = scope.getTmpVar("_tmpResult")
-        builder.addStatement(
-            "final $T $L = $N.query($L)", CURSOR.toJavaPoet(), resultName,
-            dbField, roomSQLiteQueryVar
-        )
-        transactionWrapper?.commitTransaction()
-        builder.addStatement("return $L", resultName)
-        transactionWrapper?.endTransactionWithControlFlow()
+        scope.builder.apply {
+            val resultName = scope.getTmpVar("_tmpResult")
+            addLocalVal(
+                resultName,
+                AndroidTypeNames.CURSOR,
+                "%N.query(%L)",
+                dbProperty,
+                roomSQLiteQueryVar
+            )
+            transactionWrapper?.commitTransaction()
+            addStatement("return %L", resultName)
+            transactionWrapper?.endTransactionWithControlFlow()
+        }
     }
 
     companion object {
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/DataSourceFactoryQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/DataSourceFactoryQueryResultBinder.kt
index 042d9ac..e8246ad 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/DataSourceFactoryQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/DataSourceFactoryQueryResultBinder.kt
@@ -16,16 +16,16 @@
 
 package androidx.room.solver.query.result
 
+import androidx.room.compiler.codegen.XPropertySpec
 import androidx.room.ext.L
 import androidx.room.ext.PagingTypeNames
+import androidx.room.ext.typeName
 import androidx.room.solver.CodeGenScope
-import com.squareup.javapoet.FieldSpec
 import com.squareup.javapoet.MethodSpec
 import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
 import com.squareup.javapoet.TypeSpec
 import javax.lang.model.element.Modifier
-import androidx.room.ext.typeName
-import com.squareup.javapoet.TypeName
 
 class DataSourceFactoryQueryResultBinder(
     val positionalDataSourceQueryResultBinder: PositionalDataSourceQueryResultBinder
@@ -36,7 +36,7 @@
     override fun convertAndReturn(
         roomSQLiteQueryVar: String,
         canReleaseQuery: Boolean,
-        dbField: FieldSpec,
+        dbProperty: XPropertySpec,
         inTransaction: Boolean,
         scope: CodeGenScope
     ) {
@@ -54,7 +54,7 @@
                     addMethod(
                         createCreateMethod(
                             roomSQLiteQueryVar = roomSQLiteQueryVar,
-                            dbField = dbField,
+                            dbProperty = dbProperty,
                             inTransaction = inTransaction,
                             scope = scope
                         )
@@ -67,7 +67,7 @@
 
     private fun createCreateMethod(
         roomSQLiteQueryVar: String,
-        dbField: FieldSpec,
+        dbProperty: XPropertySpec,
         inTransaction: Boolean,
         scope: CodeGenScope
     ): MethodSpec = MethodSpec.methodBuilder("create").apply {
@@ -78,7 +78,7 @@
         positionalDataSourceQueryResultBinder.convertAndReturn(
             roomSQLiteQueryVar = roomSQLiteQueryVar,
             canReleaseQuery = true,
-            dbField = dbField,
+            dbProperty = dbProperty,
             inTransaction = inTransaction,
             scope = countedBinderScope
         )
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/EntityRowAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/EntityRowAdapter.kt
index 258ddf2..0139594 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/EntityRowAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/EntityRowAdapter.kt
@@ -16,12 +16,12 @@
 
 package androidx.room.solver.query.result
 
+import androidx.room.compiler.codegen.XFunSpec
 import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.ext.AndroidTypeNames.CURSOR
 import androidx.room.ext.CommonTypeNames
 import androidx.room.ext.L
 import androidx.room.ext.N
-import androidx.room.ext.RoomTypeNames
 import androidx.room.ext.RoomTypeNames.CURSOR_UTIL
 import androidx.room.ext.S
 import androidx.room.ext.T
@@ -32,14 +32,13 @@
 import androidx.room.vo.columnNames
 import androidx.room.writer.EntityCursorConverterWriter
 import com.squareup.javapoet.CodeBlock
-import com.squareup.javapoet.MethodSpec
 import com.squareup.javapoet.TypeName
 
 class EntityRowAdapter(val entity: Entity) : QueryMappedRowAdapter(entity.type) {
 
     override val mapping = EntityMapping(entity)
 
-    private lateinit var methodSpec: MethodSpec
+    private lateinit var functionSpec: XFunSpec
 
     private var cursorDelegateVarName: String? = null
 
@@ -53,7 +52,7 @@
                     column = it,
                     indexVar = CodeBlock.of(
                         "$T.getColumnIndex($N, $S)",
-                        CURSOR_UTIL, cursorVarName, it
+                        CURSOR_UTIL.toJavaPoet(), cursorVarName, it
                     ).toString()
                 )
             }
@@ -91,19 +90,19 @@
                 "final $T $N = $T.wrapMappedColumns($N, $L, $L)",
                 CURSOR.toJavaPoet(),
                 cursorDelegateVarName,
-                RoomTypeNames.CURSOR_UTIL,
+                CURSOR_UTIL.toJavaPoet(),
                 cursorVarName,
                 entityColumnNamesParam,
                 entityColumnIndicesParam
             )
         }
-        methodSpec = scope.writer.getOrCreateMethod(EntityCursorConverterWriter(entity))
+        functionSpec = scope.writer.getOrCreateFunction(EntityCursorConverterWriter(entity))
     }
 
     override fun convert(outVarName: String, cursorVarName: String, scope: CodeGenScope) {
-        scope.builder().addStatement(
-            "$L = $N($L)",
-            outVarName, methodSpec, cursorDelegateVarName ?: cursorVarName
+        scope.builder.addStatement(
+            "%L = %N(%L)",
+            outVarName, functionSpec, cursorDelegateVarName ?: cursorVarName
         )
     }
 
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/GuavaListenableFutureQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/GuavaListenableFutureQueryResultBinder.kt
index 34bbc54..896a121 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/GuavaListenableFutureQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/GuavaListenableFutureQueryResultBinder.kt
@@ -16,16 +16,17 @@
 
 package androidx.room.solver.query.result
 
+import androidx.room.compiler.codegen.XPropertySpec
+import androidx.room.compiler.codegen.toJavaPoet
+import androidx.room.compiler.processing.XType
 import androidx.room.ext.AndroidTypeNames
 import androidx.room.ext.CallableTypeSpecBuilder
 import androidx.room.ext.L
 import androidx.room.ext.N
 import androidx.room.ext.RoomGuavaTypeNames
-import androidx.room.ext.RoomTypeNames
+import androidx.room.ext.RoomTypeNames.DB_UTIL
 import androidx.room.ext.T
-import androidx.room.compiler.processing.XType
 import androidx.room.solver.CodeGenScope
-import com.squareup.javapoet.FieldSpec
 
 /**
  * A ResultBinder that emits a ListenableFuture<T> where T is the input {@code typeArg}.
@@ -40,16 +41,17 @@
     override fun convertAndReturn(
         roomSQLiteQueryVar: String,
         canReleaseQuery: Boolean,
-        dbField: FieldSpec,
+        dbProperty: XPropertySpec,
         inTransaction: Boolean,
         scope: CodeGenScope
     ) {
+        val dbField = dbProperty.toJavaPoet()
         val cancellationSignalVar = scope.getTmpVar("_cancellationSignal")
         scope.builder().addStatement(
             "final $T $L = $T.createCancellationSignal()",
             AndroidTypeNames.CANCELLATION_SIGNAL,
             cancellationSignalVar,
-            RoomTypeNames.DB_UTIL
+            DB_UTIL.toJavaPoet()
         )
 
         // Callable<T> // Note that this callable does not release the query object.
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/GuavaOptionalQueryResultAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/GuavaOptionalQueryResultAdapter.kt
index 78ac7559..190b633 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/GuavaOptionalQueryResultAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/GuavaOptionalQueryResultAdapter.kt
@@ -29,7 +29,7 @@
  */
 class GuavaOptionalQueryResultAdapter(
     private val typeArg: XType,
-    private val resultAdapter: SingleEntityQueryResultAdapter
+    private val resultAdapter: SingleItemQueryResultAdapter
 ) : QueryResultAdapter(resultAdapter.rowAdapters) {
     override fun convert(
         outVarName: String,
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/InstantQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/InstantQueryResultBinder.kt
index d9abfaf..095a6dd 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/InstantQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/InstantQueryResultBinder.kt
@@ -15,15 +15,12 @@
  */
 package androidx.room.solver.query.result
 
-import androidx.room.compiler.codegen.toJavaPoet
-import androidx.room.ext.AndroidTypeNames.CURSOR
-import androidx.room.ext.L
-import androidx.room.ext.N
+import androidx.room.compiler.codegen.XCodeBlock
+import androidx.room.compiler.codegen.XMemberName.Companion.packageMember
+import androidx.room.compiler.codegen.XPropertySpec
+import androidx.room.ext.AndroidTypeNames
 import androidx.room.ext.RoomTypeNames
-import androidx.room.ext.T
 import androidx.room.solver.CodeGenScope
-import androidx.room.writer.DaoWriter
-import com.squareup.javapoet.FieldSpec
 
 /**
  * Instantly runs and returns the query.
@@ -32,42 +29,45 @@
     override fun convertAndReturn(
         roomSQLiteQueryVar: String,
         canReleaseQuery: Boolean,
-        dbField: FieldSpec,
+        dbProperty: XPropertySpec,
         inTransaction: Boolean,
         scope: CodeGenScope
     ) {
-        scope.builder().apply {
-            addStatement("$N.assertNotSuspendingTransaction()", DaoWriter.dbField)
+        scope.builder.apply {
+            addStatement("%N.assertNotSuspendingTransaction()", dbProperty)
         }
         val transactionWrapper = if (inTransaction) {
-            scope.builder().transactionWrapper(dbField)
+            scope.builder.transactionWrapper(dbProperty.name)
         } else {
             null
         }
         transactionWrapper?.beginTransactionWithControlFlow()
-        scope.builder().apply {
+        scope.builder.apply {
             val shouldCopyCursor = adapter?.shouldCopyCursor() == true
             val outVar = scope.getTmpVar("_result")
             val cursorVar = scope.getTmpVar("_cursor")
-            addStatement(
-                "final $T $L = $T.query($N, $L, $L, $L)",
-                CURSOR.toJavaPoet(),
-                cursorVar,
-                RoomTypeNames.DB_UTIL,
-                dbField,
-                roomSQLiteQueryVar,
-                if (shouldCopyCursor) "true" else "false",
-                "null"
+            addLocalVariable(
+                name = cursorVar,
+                typeName = AndroidTypeNames.CURSOR,
+                assignExpr = XCodeBlock.of(
+                    language,
+                    "%M(%N, %L, %L, %L)",
+                    RoomTypeNames.DB_UTIL.packageMember("query"),
+                    dbProperty,
+                    roomSQLiteQueryVar,
+                    if (shouldCopyCursor) "true" else "false",
+                    "null"
+                )
             )
             beginControlFlow("try").apply {
                 adapter?.convert(outVar, cursorVar, scope)
                 transactionWrapper?.commitTransaction()
-                addStatement("return $L", outVar)
+                addStatement("return %L", outVar)
             }
             nextControlFlow("finally").apply {
-                addStatement("$L.close()", cursorVar)
+                addStatement("%L.close()", cursorVar)
                 if (canReleaseQuery) {
-                    addStatement("$L.release()", roomSQLiteQueryVar)
+                    addStatement("%L.release()", roomSQLiteQueryVar)
                 }
             }
             endControlFlow()
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/LiveDataQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/LiveDataQueryResultBinder.kt
index 33e7687..6e5dc1a 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/LiveDataQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/LiveDataQueryResultBinder.kt
@@ -16,14 +16,15 @@
 
 package androidx.room.solver.query.result
 
+import androidx.room.compiler.codegen.XPropertySpec
+import androidx.room.compiler.codegen.toJavaPoet
+import androidx.room.compiler.processing.XType
 import androidx.room.ext.CallableTypeSpecBuilder
 import androidx.room.ext.L
 import androidx.room.ext.N
 import androidx.room.ext.T
 import androidx.room.ext.arrayTypeName
-import androidx.room.compiler.processing.XType
 import androidx.room.solver.CodeGenScope
-import com.squareup.javapoet.FieldSpec
 
 /**
  * Converts the query into a LiveData and returns it. No query is run until necessary.
@@ -37,10 +38,11 @@
     override fun convertAndReturn(
         roomSQLiteQueryVar: String,
         canReleaseQuery: Boolean,
-        dbField: FieldSpec,
+        dbProperty: XPropertySpec,
         inTransaction: Boolean,
         scope: CodeGenScope
     ) {
+        val dbField = dbProperty.toJavaPoet()
         val callableImpl = CallableTypeSpecBuilder(typeArg.typeName) {
             createRunQueryAndReturnStatements(
                 builder = this,
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MultiTypedPagingSourceQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MultiTypedPagingSourceQueryResultBinder.kt
index 5daba2a..2320bba 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MultiTypedPagingSourceQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MultiTypedPagingSourceQueryResultBinder.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.solver.query.result
 
+import androidx.room.compiler.codegen.XPropertySpec
 import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.ext.AndroidTypeNames.CURSOR
 import androidx.room.ext.CommonTypeNames
@@ -23,7 +24,6 @@
 import androidx.room.ext.N
 import androidx.room.solver.CodeGenScope
 import com.squareup.javapoet.ClassName
-import com.squareup.javapoet.FieldSpec
 import com.squareup.javapoet.MethodSpec
 import com.squareup.javapoet.ParameterSpec
 import com.squareup.javapoet.ParameterizedTypeName
@@ -52,10 +52,11 @@
     override fun convertAndReturn(
         roomSQLiteQueryVar: String,
         canReleaseQuery: Boolean,
-        dbField: FieldSpec,
+        dbProperty: XPropertySpec,
         inTransaction: Boolean,
         scope: CodeGenScope
     ) {
+        val dbField = dbProperty.toJavaPoet()
         scope.builder().apply {
             val tableNamesList = tableNames.joinToString(", ") { "\"$it\"" }
             val pagingSourceSpec = TypeSpec.anonymousClassBuilder(
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/OptionalQueryResultAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/OptionalQueryResultAdapter.kt
index 72b83de0..72001e2 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/OptionalQueryResultAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/OptionalQueryResultAdapter.kt
@@ -31,7 +31,7 @@
  */
 class OptionalQueryResultAdapter(
     private val typeArg: XType,
-    private val resultAdapter: SingleEntityQueryResultAdapter
+    private val resultAdapter: SingleItemQueryResultAdapter
 ) : QueryResultAdapter(resultAdapter.rowAdapters) {
     override fun convert(
         outVarName: String,
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PojoIndexAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PojoIndexAdapter.kt
index bd54421..0e695f3 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PojoIndexAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PojoIndexAdapter.kt
@@ -16,10 +16,11 @@
 
 package androidx.room.solver.query.result
 
-import androidx.room.ext.L
-import androidx.room.ext.RoomTypeNames
-import androidx.room.ext.S
-import androidx.room.ext.T
+import androidx.room.compiler.codegen.XCodeBlock
+import androidx.room.compiler.codegen.XCodeBlock.Builder.Companion.addLocalVal
+import androidx.room.compiler.codegen.XMemberName.Companion.packageMember
+import androidx.room.compiler.codegen.XTypeName
+import androidx.room.ext.RoomTypeNames.CURSOR_UTIL
 import androidx.room.ext.capitalize
 import androidx.room.ext.stripNonJava
 import androidx.room.parser.ParsedQuery
@@ -27,7 +28,6 @@
 import androidx.room.solver.CodeGenScope
 import androidx.room.verifier.QueryResultInfo
 import androidx.room.vo.ColumnIndexVar
-import com.squareup.javapoet.TypeName
 import java.util.Locale
 
 /**
@@ -58,9 +58,11 @@
                         "'${it.columnName}'. Query: ${query.original}. Please file a bug at " +
                         ProcessorErrors.ISSUE_TRACKER_LINK
                 }
-                scope.builder().addStatement(
-                    "final $T $L = $L",
-                    TypeName.INT, indexVar, infoIndex
+                scope.builder.addLocalVal(
+                    indexVar,
+                    XTypeName.PRIMITIVE_INT,
+                    "%L",
+                    infoIndex
                 )
             } else {
                 val indexMethod = if (info == null) {
@@ -68,10 +70,17 @@
                 } else {
                     "getColumnIndexOrThrow"
                 }
-                scope.builder().addStatement(
-                    "final $T $L = $T.$L($L, $S)",
-                    TypeName.INT, indexVar, RoomTypeNames.CURSOR_UTIL, indexMethod, cursorVarName,
-                    it.columnName
+                scope.builder.addLocalVariable(
+                    name = indexVar,
+                    typeName = XTypeName.PRIMITIVE_INT,
+                    assignExpr = XCodeBlock.of(
+                        scope.language,
+                        "%M(%L, %S)",
+                        CURSOR_UTIL.packageMember(indexMethod),
+                        cursorVarName,
+                        it.columnName
+
+                    )
                 )
             }
             ColumnIndexVar(it.columnName, indexVar)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PojoRowAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PojoRowAdapter.kt
index 14b74ce..2d1ac5e 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PojoRowAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PojoRowAdapter.kt
@@ -16,18 +16,9 @@
 
 package androidx.room.solver.query.result
 
-import androidx.room.compiler.codegen.CodeLanguage
-import androidx.room.compiler.codegen.VisibilityModifier
-import androidx.room.compiler.codegen.XClassName
-import androidx.room.compiler.codegen.XFunSpec
-import androidx.room.compiler.codegen.XTypeName
-import androidx.room.compiler.codegen.XTypeSpec
-import androidx.room.compiler.codegen.addOriginatingElement
 import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.XType
-import androidx.room.ext.AndroidTypeNames
 import androidx.room.ext.L
-import androidx.room.ext.T
 import androidx.room.parser.ParsedQuery
 import androidx.room.processor.Context
 import androidx.room.processor.ProcessorErrors
@@ -39,8 +30,6 @@
 import androidx.room.vo.Pojo
 import androidx.room.vo.RelationCollector
 import androidx.room.writer.FieldReadWriteWriter
-import androidx.room.writer.TypeWriter
-import kotlin.math.abs
 
 /**
  * Creates the entity from the given info.
@@ -146,62 +135,14 @@
     }
 
     override fun convert(outVarName: String, cursorVarName: String, scope: CodeGenScope) {
-        fun doReadFromCursor(outVarName: String, scope: CodeGenScope) {
-            FieldReadWriteWriter.readFromCursor(
-                outVar = outVarName,
-                outPojo = pojo,
-                cursorVar = cursorVarName,
-                fieldsWithIndices = fieldsWithIndices,
-                relationCollectors = relationCollectors,
-                scope = scope
-            )
-        }
-        // TODO(b/127483380): Inline in code gen scope once Kotlin code gen progresses.
-        if (relationCollectors.isEmpty() && context.codeLanguage == CodeLanguage.KOTLIN) {
-            // The name of the class is based on the query, possible to be collisions, but good,
-            // enough for now.
-            val nameHash = abs(query?.original?.hashCode() ?: out.asTypeName().hashCode())
-            val className = XClassName.get("androidx.room.temp", "PojoRowAdapter_$nameHash")
-            object : TypeWriter(CodeLanguage.KOTLIN) {
-                override fun createTypeSpecBuilder(): XTypeSpec.Builder {
-                    val readFunction = XFunSpec.builder(
-                        CodeLanguage.KOTLIN,
-                        "readFromCursor",
-                        VisibilityModifier.PUBLIC
-                    )
-                        .returns(out.asTypeName())
-                        .addParameter(AndroidTypeNames.CURSOR, cursorVarName)
-                        .apply {
-                            fieldsWithIndices.forEach {
-                                addParameter(XTypeName.PRIMITIVE_INT, it.indexVar)
-                            }
-                        }
-                        .addCode(
-                            CodeGenScope(this).apply {
-                                builder.addLocalVariable(outVarName, out.asTypeName())
-                                doReadFromCursor(outVarName, this)
-                                builder.addStatement("return %L", outVarName)
-                            }.generate()
-                        )
-                        .build()
-                    return XTypeSpec.classBuilder(codeLanguage, className)
-                        .addOriginatingElement(pojo.element)
-                        .addFunction(readFunction)
-                }
-            }.write(context.processingEnv)
-            scope.builder().apply {
-                addStatement("$L = new $T().readFromCursor($L, $L)",
-                    outVarName,
-                    className.toJavaPoet(),
-                    cursorVarName,
-                    fieldsWithIndices.joinToString { it.indexVar }
-                )
-            }
-        } else {
-            scope.builder().apply {
-                doReadFromCursor(outVarName, scope)
-            }
-        }
+        FieldReadWriteWriter.readFromCursor(
+            outVar = outVarName,
+            outPojo = pojo,
+            cursorVar = cursorVarName,
+            fieldsWithIndices = fieldsWithIndices,
+            relationCollectors = relationCollectors,
+            scope = scope
+        )
     }
 
     override fun getDefaultIndexAdapter() = indexAdapter
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PositionalDataSourceQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PositionalDataSourceQueryResultBinder.kt
index 9c8df06..58bc4d6 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PositionalDataSourceQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/PositionalDataSourceQueryResultBinder.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.solver.query.result
 
+import androidx.room.compiler.codegen.XPropertySpec
 import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.ext.AndroidTypeNames.CURSOR
 import androidx.room.ext.CommonTypeNames
@@ -23,7 +24,6 @@
 import androidx.room.ext.N
 import androidx.room.ext.RoomTypeNames
 import androidx.room.solver.CodeGenScope
-import com.squareup.javapoet.FieldSpec
 import com.squareup.javapoet.MethodSpec
 import com.squareup.javapoet.ParameterSpec
 import com.squareup.javapoet.ParameterizedTypeName
@@ -47,10 +47,11 @@
     override fun convertAndReturn(
         roomSQLiteQueryVar: String,
         canReleaseQuery: Boolean,
-        dbField: FieldSpec,
+        dbProperty: XPropertySpec,
         inTransaction: Boolean,
         scope: CodeGenScope
     ) {
+        val dbField = dbProperty.toJavaPoet()
         // first comma for table names comes from the string since it might be empty in which case
         // we don't need a comma. If list is empty, this prevents generating bad code (it is still
         // an error to have empty list but that is already reported while item is processed)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/QueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/QueryResultBinder.kt
index d3d6d8c..f2fda6b 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/QueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/QueryResultBinder.kt
@@ -16,8 +16,8 @@
 
 package androidx.room.solver.query.result
 
+import androidx.room.compiler.codegen.XPropertySpec
 import androidx.room.solver.CodeGenScope
-import com.squareup.javapoet.FieldSpec
 
 /**
  * Connects the query, db and the ResultAdapter.
@@ -34,7 +34,7 @@
     abstract fun convertAndReturn(
         roomSQLiteQueryVar: String,
         canReleaseQuery: Boolean, // false if query is provided by the user
-        dbField: FieldSpec,
+        dbProperty: XPropertySpec,
         inTransaction: Boolean,
         scope: CodeGenScope
     )
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/RxCallableQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/RxCallableQueryResultBinder.kt
index 919397e..522fc0a 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/RxCallableQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/RxCallableQueryResultBinder.kt
@@ -16,13 +16,14 @@
 
 package androidx.room.solver.query.result
 
+import androidx.room.compiler.codegen.XPropertySpec
 import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.XType
 import androidx.room.ext.AndroidTypeNames.CURSOR
 import androidx.room.ext.CallableTypeSpecBuilder
 import androidx.room.ext.L
 import androidx.room.ext.N
-import androidx.room.ext.RoomTypeNames
+import androidx.room.ext.RoomTypeNames.DB_UTIL
 import androidx.room.ext.S
 import androidx.room.ext.T
 import androidx.room.solver.CodeGenScope
@@ -42,10 +43,11 @@
     override fun convertAndReturn(
         roomSQLiteQueryVar: String,
         canReleaseQuery: Boolean,
-        dbField: FieldSpec,
+        dbProperty: XPropertySpec,
         inTransaction: Boolean,
         scope: CodeGenScope
     ) {
+        val dbField = dbProperty.toJavaPoet()
         val callable = CallableTypeSpecBuilder(typeArg.typeName) {
             fillInCallMethod(
                 roomSQLiteQueryVar = roomSQLiteQueryVar,
@@ -87,7 +89,7 @@
             "final $T $L = $T.query($N, $L, $L, $L)",
             CURSOR.toJavaPoet(),
             cursorVar,
-            RoomTypeNames.DB_UTIL,
+            DB_UTIL.toJavaPoet(),
             dbField,
             roomSQLiteQueryVar,
             if (shouldCopyCursor) "true" else "false",
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/RxQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/RxQueryResultBinder.kt
index 717d70f..a6eb925 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/RxQueryResultBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/RxQueryResultBinder.kt
@@ -16,15 +16,16 @@
 
 package androidx.room.solver.query.result
 
+import androidx.room.compiler.codegen.XPropertySpec
+import androidx.room.compiler.codegen.toJavaPoet
+import androidx.room.compiler.processing.XType
 import androidx.room.ext.CallableTypeSpecBuilder
 import androidx.room.ext.L
 import androidx.room.ext.N
 import androidx.room.ext.T
 import androidx.room.ext.arrayTypeName
-import androidx.room.compiler.processing.XType
 import androidx.room.solver.CodeGenScope
 import androidx.room.solver.RxType
-import com.squareup.javapoet.FieldSpec
 
 /**
  * Binds the result as an RxJava2 Flowable, Publisher and Observable.
@@ -38,10 +39,11 @@
     override fun convertAndReturn(
         roomSQLiteQueryVar: String,
         canReleaseQuery: Boolean,
-        dbField: FieldSpec,
+        dbProperty: XPropertySpec,
         inTransaction: Boolean,
         scope: CodeGenScope
     ) {
+        val dbField = dbProperty.toJavaPoet()
         val callableImpl = CallableTypeSpecBuilder(typeArg.typeName) {
             createRunQueryAndReturnStatements(
                 builder = this,
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/SingleEntityQueryResultAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/SingleEntityQueryResultAdapter.kt
deleted file mode 100644
index ddc0637..0000000
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/SingleEntityQueryResultAdapter.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2016 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.solver.query.result
-
-import androidx.room.ext.L
-import androidx.room.ext.T
-import androidx.room.solver.CodeGenScope
-
-/**
- * Wraps a row adapter when there is only 1 item in the result
- */
-class SingleEntityQueryResultAdapter(
-    private val rowAdapter: RowAdapter
-) : QueryResultAdapter(listOf(rowAdapter)) {
-    val type = rowAdapter.out
-    override fun convert(outVarName: String, cursorVarName: String, scope: CodeGenScope) {
-        scope.builder().apply {
-            rowAdapter.onCursorReady(cursorVarName = cursorVarName, scope = scope)
-            addStatement("final $T $L", type.typeName, outVarName)
-            beginControlFlow("if($L.moveToFirst())", cursorVarName)
-            rowAdapter.convert(outVarName, cursorVarName, scope)
-            nextControlFlow("else").apply {
-                addStatement("$L = $L", outVarName, rowAdapter.out.defaultValue())
-            }
-            endControlFlow()
-        }
-    }
-}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/SingleItemQueryResultAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/SingleItemQueryResultAdapter.kt
new file mode 100644
index 0000000..b9fdcbb
--- /dev/null
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/SingleItemQueryResultAdapter.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 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.solver.query.result
+
+import androidx.room.compiler.codegen.CodeLanguage
+import androidx.room.compiler.processing.XNullability
+import androidx.room.solver.CodeGenScope
+
+/**
+ * Wraps a row adapter when there is only 1 item in the result
+ */
+class SingleItemQueryResultAdapter(
+    private val rowAdapter: RowAdapter
+) : QueryResultAdapter(listOf(rowAdapter)) {
+    val type = rowAdapter.out
+    override fun convert(outVarName: String, cursorVarName: String, scope: CodeGenScope) {
+        scope.builder.apply {
+            rowAdapter.onCursorReady(cursorVarName = cursorVarName, scope = scope)
+            addLocalVariable(outVarName, type.asTypeName())
+            beginControlFlow("if (%L.moveToFirst())", cursorVarName).apply {
+                rowAdapter.convert(outVarName, cursorVarName, scope)
+            }
+            nextControlFlow("else").apply {
+                val defaultValue = rowAdapter.out.defaultValue()
+                if (
+                    language == CodeLanguage.KOTLIN &&
+                    type.nullability == XNullability.NONNULL &&
+                    defaultValue == "null"
+                ) {
+                    // TODO(b/249984504): Generate / output a better message.
+                    addStatement("error(%S)", "Cursor was empty, but expected a single item.")
+                } else {
+                    addStatement("%L = %L", outVarName, rowAdapter.out.defaultValue())
+                }
+            }
+            endControlFlow()
+        }
+    }
+}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/SingleNamedColumnRowAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/SingleNamedColumnRowAdapter.kt
index a4281bf..57a3a43 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/SingleNamedColumnRowAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/SingleNamedColumnRowAdapter.kt
@@ -16,8 +16,9 @@
 
 package androidx.room.solver.query.result
 
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.ext.L
-import androidx.room.ext.RoomTypeNames
+import androidx.room.ext.RoomTypeNames.CURSOR_UTIL
 import androidx.room.ext.S
 import androidx.room.ext.T
 import androidx.room.ext.capitalize
@@ -50,7 +51,7 @@
                 "final $T $L = $T.$L($L, $S)",
                 TypeName.INT,
                 indexVarName,
-                RoomTypeNames.CURSOR_UTIL,
+                CURSOR_UTIL.toJavaPoet(),
                 "getColumnIndexOrThrow",
                 cursorVarName,
                 columnName
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/TransactionWrapper.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/TransactionWrapper.kt
index 373035b..b498fa4 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/TransactionWrapper.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/TransactionWrapper.kt
@@ -16,8 +16,8 @@
 
 package androidx.room.solver.query.result
 
+import androidx.room.compiler.codegen.XCodeBlock
 import androidx.room.ext.N
-import com.squareup.javapoet.CodeBlock
 import com.squareup.javapoet.FieldSpec
 import com.squareup.javapoet.MethodSpec
 
@@ -47,19 +47,19 @@
     }
 }
 
-fun CodeBlock.Builder.transactionWrapper(dbField: FieldSpec) = object : TransactionWrapper {
+fun XCodeBlock.Builder.transactionWrapper(dbPropertyName: String) = object : TransactionWrapper {
     override fun beginTransactionWithControlFlow() {
-        addStatement("$N.beginTransaction()", dbField)
+        addStatement("%N.beginTransaction()", dbPropertyName)
         beginControlFlow("try")
     }
 
     override fun commitTransaction() {
-        addStatement("$N.setTransactionSuccessful()", dbField)
+        addStatement("%N.setTransactionSuccessful()", dbPropertyName)
     }
 
     override fun endTransactionWithControlFlow() {
         nextControlFlow("finally")
-        addStatement("$N.endTransaction()", dbField)
+        addStatement("%N.endTransaction()", dbPropertyName)
         endControlFlow()
     }
 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CallableDeleteOrUpdateMethodBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CallableDeleteOrUpdateMethodBinder.kt
index 166a74d..4c8dff9 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CallableDeleteOrUpdateMethodBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CallableDeleteOrUpdateMethodBinder.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.solver.shortcut.binder
 
+import androidx.room.compiler.codegen.XPropertySpec
 import androidx.room.compiler.processing.XType
 import androidx.room.ext.CallableTypeSpecBuilder
 import androidx.room.solver.CodeGenScope
@@ -48,7 +49,7 @@
 
     override fun convertAndReturn(
         parameters: List<ShortcutQueryParameter>,
-        adapters: Map<String, Pair<FieldSpec, TypeSpec>>,
+        adapters: Map<String, Pair<XPropertySpec, TypeSpec>>,
         dbField: FieldSpec,
         scope: CodeGenScope
     ) {
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CallableInsertMethodBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CallableInsertMethodBinder.kt
index 7aa6c6c..d0fd7ad 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CallableInsertMethodBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CallableInsertMethodBinder.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.solver.shortcut.binder
 
+import androidx.room.compiler.codegen.XPropertySpec
 import androidx.room.compiler.processing.XType
 import androidx.room.ext.CallableTypeSpecBuilder
 import androidx.room.solver.CodeGenScope
@@ -48,7 +49,7 @@
 
     override fun convertAndReturn(
         parameters: List<ShortcutQueryParameter>,
-        adapters: Map<String, Pair<FieldSpec, Any>>,
+        adapters: Map<String, Pair<XPropertySpec, Any>>,
         dbField: FieldSpec,
         scope: CodeGenScope
     ) {
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CallableUpsertMethodBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CallableUpsertMethodBinder.kt
index 0f452e07..3f35e3a 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CallableUpsertMethodBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CallableUpsertMethodBinder.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.solver.shortcut.binder
 
+import androidx.room.compiler.codegen.XPropertySpec
 import androidx.room.compiler.processing.XType
 import androidx.room.ext.CallableTypeSpecBuilder
 import androidx.room.solver.CodeGenScope
@@ -48,7 +49,7 @@
 
     override fun convertAndReturn(
         parameters: List<ShortcutQueryParameter>,
-        adapters: Map<String, Pair<FieldSpec, Any>>,
+        adapters: Map<String, Pair<XPropertySpec, Any>>,
         dbField: FieldSpec,
         scope: CodeGenScope
     ) {
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/DeleteOrUpdateMethodBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/DeleteOrUpdateMethodBinder.kt
index c576cc9..08a7d50 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/DeleteOrUpdateMethodBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/DeleteOrUpdateMethodBinder.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.solver.shortcut.binder
 
+import androidx.room.compiler.codegen.XPropertySpec
 import androidx.room.solver.CodeGenScope
 import androidx.room.solver.shortcut.result.DeleteOrUpdateMethodAdapter
 import androidx.room.vo.ShortcutQueryParameter
@@ -55,7 +56,7 @@
      */
     abstract fun convertAndReturn(
         parameters: List<ShortcutQueryParameter>,
-        adapters: Map<String, Pair<FieldSpec, TypeSpec>>,
+        adapters: Map<String, Pair<XPropertySpec, TypeSpec>>,
         dbField: FieldSpec,
         scope: CodeGenScope
     )
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InsertOrUpsertMethodBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InsertOrUpsertMethodBinder.kt
index 0fa0ae0..476a771 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InsertOrUpsertMethodBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InsertOrUpsertMethodBinder.kt
@@ -16,8 +16,9 @@
 
 package androidx.room.solver.shortcut.binder
 
-import androidx.room.solver.shortcut.result.InsertOrUpsertMethodAdapter
+import androidx.room.compiler.codegen.XPropertySpec
 import androidx.room.solver.CodeGenScope
+import androidx.room.solver.shortcut.result.InsertOrUpsertMethodAdapter
 import androidx.room.vo.ShortcutQueryParameter
 import com.squareup.javapoet.FieldSpec
 
@@ -50,7 +51,7 @@
      */
     abstract fun convertAndReturn(
         parameters: List<ShortcutQueryParameter>,
-        adapters: Map<String, Pair<FieldSpec, Any>>,
+        adapters: Map<String, Pair<XPropertySpec, Any>>,
         dbField: FieldSpec,
         scope: CodeGenScope
     )
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InstantDeleteOrUpdateMethodBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InstantDeleteOrUpdateMethodBinder.kt
index be271f5..c8de492 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InstantDeleteOrUpdateMethodBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InstantDeleteOrUpdateMethodBinder.kt
@@ -16,11 +16,11 @@
 
 package androidx.room.solver.shortcut.binder
 
+import androidx.room.compiler.codegen.XPropertySpec
 import androidx.room.ext.N
 import androidx.room.solver.CodeGenScope
 import androidx.room.solver.shortcut.result.DeleteOrUpdateMethodAdapter
 import androidx.room.vo.ShortcutQueryParameter
-import androidx.room.writer.DaoWriter
 import com.squareup.javapoet.FieldSpec
 import com.squareup.javapoet.TypeSpec
 
@@ -33,12 +33,12 @@
 
     override fun convertAndReturn(
         parameters: List<ShortcutQueryParameter>,
-        adapters: Map<String, Pair<FieldSpec, TypeSpec>>,
+        adapters: Map<String, Pair<XPropertySpec, TypeSpec>>,
         dbField: FieldSpec,
         scope: CodeGenScope
     ) {
         scope.builder().apply {
-            addStatement("$N.assertNotSuspendingTransaction()", DaoWriter.dbField)
+            addStatement("$N.assertNotSuspendingTransaction()", dbField)
         }
         adapter?.createDeleteOrUpdateMethodBody(
             parameters = parameters,
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InstantInsertMethodBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InstantInsertMethodBinder.kt
index 8e3aa51..2fe6759 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InstantInsertMethodBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InstantInsertMethodBinder.kt
@@ -16,11 +16,11 @@
 
 package androidx.room.solver.shortcut.binder
 
+import androidx.room.compiler.codegen.XPropertySpec
 import androidx.room.ext.N
 import androidx.room.solver.CodeGenScope
 import androidx.room.solver.shortcut.result.InsertOrUpsertMethodAdapter
 import androidx.room.vo.ShortcutQueryParameter
-import androidx.room.writer.DaoWriter
 import com.squareup.javapoet.FieldSpec
 
 /**
@@ -31,12 +31,12 @@
 
     override fun convertAndReturn(
         parameters: List<ShortcutQueryParameter>,
-        adapters: Map<String, Pair<FieldSpec, Any>>,
+        adapters: Map<String, Pair<XPropertySpec, Any>>,
         dbField: FieldSpec,
         scope: CodeGenScope
     ) {
         scope.builder().apply {
-            addStatement("$N.assertNotSuspendingTransaction()", DaoWriter.dbField)
+            addStatement("$N.assertNotSuspendingTransaction()", dbField)
         }
         adapter?.createMethodBody(
             parameters = parameters,
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InstantUpsertMethodBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InstantUpsertMethodBinder.kt
index bc6f811..527cf30 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InstantUpsertMethodBinder.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/InstantUpsertMethodBinder.kt
@@ -16,11 +16,11 @@
 
 package androidx.room.solver.shortcut.binder
 
+import androidx.room.compiler.codegen.XPropertySpec
 import androidx.room.ext.N
 import androidx.room.solver.CodeGenScope
 import androidx.room.solver.shortcut.result.InsertOrUpsertMethodAdapter
 import androidx.room.vo.ShortcutQueryParameter
-import androidx.room.writer.DaoWriter
 import com.squareup.javapoet.FieldSpec
 
 /**
@@ -31,12 +31,12 @@
 
     override fun convertAndReturn(
         parameters: List<ShortcutQueryParameter>,
-        adapters: Map<String, Pair<FieldSpec, Any>>,
+        adapters: Map<String, Pair<XPropertySpec, Any>>,
         dbField: FieldSpec,
         scope: CodeGenScope
     ) {
         scope.builder().apply {
-            addStatement("$N.assertNotSuspendingTransaction()", DaoWriter.dbField)
+            addStatement("$N.assertNotSuspendingTransaction()", dbField)
         }
         adapter?.createMethodBody(
             parameters = parameters,
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/result/DeleteOrUpdateMethodAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/result/DeleteOrUpdateMethodAdapter.kt
index 1531de9..0d164c9 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/result/DeleteOrUpdateMethodAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/result/DeleteOrUpdateMethodAdapter.kt
@@ -16,15 +16,16 @@
 
 package androidx.room.solver.shortcut.result
 
-import androidx.room.ext.KotlinTypeNames
-import androidx.room.ext.L
-import androidx.room.ext.N
-import androidx.room.ext.T
+import androidx.room.compiler.codegen.XPropertySpec
 import androidx.room.compiler.processing.XType
 import androidx.room.compiler.processing.isInt
 import androidx.room.compiler.processing.isKotlinUnit
 import androidx.room.compiler.processing.isVoid
 import androidx.room.compiler.processing.isVoidObject
+import androidx.room.ext.KotlinTypeNames
+import androidx.room.ext.L
+import androidx.room.ext.N
+import androidx.room.ext.T
 import androidx.room.solver.CodeGenScope
 import androidx.room.vo.ShortcutQueryParameter
 import com.squareup.javapoet.FieldSpec
@@ -53,7 +54,7 @@
 
     fun createDeleteOrUpdateMethodBody(
         parameters: List<ShortcutQueryParameter>,
-        adapters: Map<String, Pair<FieldSpec, TypeSpec>>,
+        adapters: Map<String, Pair<XPropertySpec, TypeSpec>>,
         dbField: FieldSpec,
         scope: CodeGenScope
     ) {
@@ -69,11 +70,11 @@
             addStatement("$N.beginTransaction()", dbField)
             beginControlFlow("try").apply {
                 parameters.forEach { param ->
-                    val adapter = adapters[param.name]?.first
+                    val adapter = adapters.getValue(param.name).first
                     addStatement(
-                        "$L$N.$L($L)",
+                        "$L$L.$L($L)",
                         if (resultVar == null) "" else "$resultVar +=",
-                        adapter, param.handleMethodName(), param.name
+                        adapter.name, param.handleMethodName(), param.name
                     )
                 }
                 addStatement("$N.setTransactionSuccessful()", dbField)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/result/InsertOrUpsertMethodAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/result/InsertOrUpsertMethodAdapter.kt
index 58440d8..410201c 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/result/InsertOrUpsertMethodAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/result/InsertOrUpsertMethodAdapter.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.solver.shortcut.result
 
+import androidx.room.compiler.codegen.XPropertySpec
 import androidx.room.compiler.processing.XType
 import androidx.room.compiler.processing.isArray
 import androidx.room.compiler.processing.isKotlinUnit
@@ -168,7 +169,7 @@
 
     fun createMethodBody(
         parameters: List<ShortcutQueryParameter>,
-        adapters: Map<String, Pair<FieldSpec, Any>>,
+        adapters: Map<String, Pair<XPropertySpec, Any>>,
         dbField: FieldSpec,
         scope: CodeGenScope
     ) {
@@ -191,19 +192,19 @@
 
             beginControlFlow("try").apply {
                 parameters.forEach { param ->
-                    val upsertionAdapter = adapters[param.name]?.first
+                    val upsertionAdapter = adapters.getValue(param.name).first
                     if (needsResultVar) {
                         // if it has more than 1 parameter, we would've already printed the error
                         // so we don't care about re-declaring the variable here
                         addStatement(
-                            "$T $L = $N.$L($L)",
+                            "$T $L = $L.$L($L)",
                             methodReturnType.returnTypeName, resultVar,
-                            upsertionAdapter, methodName,
+                            upsertionAdapter.name, methodName,
                             param.name
                         )
                     } else {
                         addStatement(
-                            "$N.$L($L)", upsertionAdapter, methodName,
+                            "$L.$L($L)", upsertionAdapter.name, methodName,
                             param.name
                         )
                     }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/BoxedBooleanToBoxedIntConverter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/BoxedBooleanToBoxedIntConverter.kt
index 814a482..92acbc1 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/BoxedBooleanToBoxedIntConverter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/BoxedBooleanToBoxedIntConverter.kt
@@ -16,33 +16,56 @@
 
 package androidx.room.solver.types
 
-import androidx.room.ext.L
+import androidx.room.compiler.codegen.CodeLanguage
+import androidx.room.compiler.codegen.XCodeBlock
+import androidx.room.compiler.codegen.asClassName
 import androidx.room.compiler.processing.XProcessingEnv
 import androidx.room.solver.CodeGenScope
-import com.squareup.javapoet.CodeBlock
 
 /**
  * int to boolean adapter.
  */
 object BoxedBooleanToBoxedIntConverter {
     fun create(processingEnvironment: XProcessingEnv): List<TypeConverter> {
-        val tBoolean = processingEnvironment.requireType("java.lang.Boolean").makeNullable()
-        val tInt = processingEnvironment.requireType("java.lang.Integer").makeNullable()
+        val tBoolean = processingEnvironment.requireType(
+            Boolean::class.asClassName()
+        ).makeNullable()
+        val tInt = processingEnvironment.requireType(
+            Int::class.asClassName()
+        ).makeNullable()
         return listOf(
             object : SingleStatementTypeConverter(tBoolean, tInt) {
-                override fun buildStatement(inputVarName: String, scope: CodeGenScope): CodeBlock {
-                    return CodeBlock.of(
-                        "$L == null ? null : ($L ? 1 : 0)",
-                        inputVarName, inputVarName
-                    )
+                override fun buildStatement(inputVarName: String, scope: CodeGenScope): XCodeBlock {
+                    return when (scope.language) {
+                        CodeLanguage.JAVA -> XCodeBlock.of(
+                            scope.language,
+                            "%L == null ? null : (%L ? 1 : 0)",
+                            inputVarName,
+                            inputVarName
+                        )
+                        CodeLanguage.KOTLIN -> XCodeBlock.of(
+                            scope.language,
+                            "%L?.let { if (it) 1 else 0 }",
+                            inputVarName,
+                        )
+                    }
                 }
             },
             object : SingleStatementTypeConverter(tInt, tBoolean) {
-                override fun buildStatement(inputVarName: String, scope: CodeGenScope): CodeBlock {
-                    return CodeBlock.of(
-                        "$L == null ? null : $L != 0",
-                        inputVarName, inputVarName
-                    )
+                override fun buildStatement(inputVarName: String, scope: CodeGenScope): XCodeBlock {
+                    return when (scope.language) {
+                        CodeLanguage.JAVA -> XCodeBlock.of(
+                            scope.language,
+                            "%L == null ? null : %L != 0",
+                            inputVarName,
+                            inputVarName
+                        )
+                        CodeLanguage.KOTLIN -> XCodeBlock.of(
+                            scope.language,
+                            "%L?.let { it != 0 }",
+                            inputVarName,
+                        )
+                    }
                 }
             }
         )
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/BoxedPrimitiveColumnTypeAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/BoxedPrimitiveColumnTypeAdapter.kt
index e47d917..daa8127 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/BoxedPrimitiveColumnTypeAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/BoxedPrimitiveColumnTypeAdapter.kt
@@ -16,22 +16,21 @@
 
 package androidx.room.solver.types
 
-import androidx.room.ext.L
 import androidx.room.compiler.processing.XType
 import androidx.room.solver.CodeGenScope
 
 /**
  * Adapters for all boxed primitives that has direct cursor mappings.
  */
-open class BoxedPrimitiveColumnTypeAdapter(
+class BoxedPrimitiveColumnTypeAdapter(
     boxed: XType,
     val primitiveAdapter: PrimitiveColumnTypeAdapter
 ) : ColumnTypeAdapter(boxed, primitiveAdapter.typeAffinity) {
+
     companion object {
         fun createBoxedPrimitiveAdapters(
             primitiveAdapters: List<PrimitiveColumnTypeAdapter>
         ): List<ColumnTypeAdapter> {
-
             return primitiveAdapters.map {
                 BoxedPrimitiveColumnTypeAdapter(
                     it.out.boxed().makeNullable(),
@@ -47,9 +46,9 @@
         valueVarName: String,
         scope: CodeGenScope
     ) {
-        scope.builder().apply {
-            beginControlFlow("if ($L == null)", valueVarName).apply {
-                addStatement("$L.bindNull($L)", stmtName, indexVarName)
+        scope.builder.apply {
+            beginControlFlow("if (%L == null)", valueVarName).apply {
+                addStatement("%L.bindNull(%L)", stmtName, indexVarName)
             }
             nextControlFlow("else").apply {
                 primitiveAdapter.bindToStmt(stmtName, indexVarName, valueVarName, scope)
@@ -64,9 +63,9 @@
         indexVarName: String,
         scope: CodeGenScope
     ) {
-        scope.builder().apply {
-            beginControlFlow("if ($L.isNull($L))", cursorVarName, indexVarName).apply {
-                addStatement("$L = null", outVarName)
+        scope.builder.apply {
+            beginControlFlow("if (%L.isNull(%L))", cursorVarName, indexVarName).apply {
+                addStatement("%L = null", outVarName)
             }
             nextControlFlow("else").apply {
                 primitiveAdapter.readFromCursor(outVarName, cursorVarName, indexVarName, scope)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/ByteArrayColumnTypeAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/ByteArrayColumnTypeAdapter.kt
index 9e48c76..c376526 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/ByteArrayColumnTypeAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/ByteArrayColumnTypeAdapter.kt
@@ -16,12 +16,12 @@
 
 package androidx.room.solver.types
 
+import androidx.room.compiler.codegen.XTypeName
+import androidx.room.compiler.processing.XNullability
 import androidx.room.compiler.processing.XProcessingEnv
 import androidx.room.compiler.processing.XType
-import androidx.room.ext.L
 import androidx.room.parser.SQLTypeAffinity
 import androidx.room.solver.CodeGenScope
-import com.squareup.javapoet.TypeName
 
 class ByteArrayColumnTypeAdapter private constructor(
     out: XType
@@ -35,16 +35,18 @@
         indexVarName: String,
         scope: CodeGenScope
     ) {
-        scope.builder().apply {
-            // according to docs, getBlob might throw if the value is null
-            // https://developer.android.com/reference/android/database/Cursor#getBlob(int)
-            beginControlFlow("if ($L.isNull($L))", cursorVarName, indexVarName).apply {
-                addStatement("$L = null", outVarName)
+        scope.builder.apply {
+            if (out.nullability == XNullability.NONNULL) {
+                addStatement("%L = %L.getBlob(%L)", outVarName, cursorVarName, indexVarName)
+            } else {
+                beginControlFlow("if (%L.isNull(%L))", cursorVarName, indexVarName).apply {
+                    addStatement("%L = null", outVarName)
+                }
+                nextControlFlow("else").apply {
+                    addStatement("%L = %L.getBlob(%L)", outVarName, cursorVarName, indexVarName)
+                }
+                endControlFlow()
             }
-            nextControlFlow("else").apply {
-                addStatement("$L = $L.getBlob($L)", outVarName, cursorVarName, indexVarName)
-            }
-            endControlFlow()
         }
     }
 
@@ -54,18 +56,22 @@
         valueVarName: String,
         scope: CodeGenScope
     ) {
-        scope.builder().apply {
-            beginControlFlow("if ($L == null)", valueVarName)
-                .addStatement("$L.bindNull($L)", stmtName, indexVarName)
-            nextControlFlow("else")
-                .addStatement("$L.bindBlob($L, $L)", stmtName, indexVarName, valueVarName)
-            endControlFlow()
+        scope.builder.apply {
+            if (out.nullability == XNullability.NONNULL) {
+                addStatement("%L.bindBlob(%L, %L)", stmtName, indexVarName, valueVarName)
+            } else {
+                beginControlFlow("if (%L == null)", valueVarName)
+                    .addStatement("%L.bindNull(%L)", stmtName, indexVarName)
+                nextControlFlow("else")
+                    .addStatement("%L.bindBlob(%L, %L)", stmtName, indexVarName, valueVarName)
+                endControlFlow()
+            }
         }
     }
 
     companion object {
         fun create(env: XProcessingEnv): List<ByteArrayColumnTypeAdapter> {
-            val arrayType = env.getArrayType(TypeName.BYTE)
+            val arrayType = env.getArrayType(env.requireType(XTypeName.PRIMITIVE_BYTE))
             return if (env.backend == XProcessingEnv.Backend.KSP) {
                 listOf(
                     ByteArrayColumnTypeAdapter(arrayType.makeNonNullable()),
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/ByteBufferColumnTypeAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/ByteBufferColumnTypeAdapter.kt
index c7b8a8e..063da78 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/ByteBufferColumnTypeAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/ByteBufferColumnTypeAdapter.kt
@@ -16,13 +16,13 @@
 
 package androidx.room.solver.types
 
+import androidx.room.compiler.codegen.XCodeBlock
+import androidx.room.compiler.codegen.asClassName
+import androidx.room.compiler.processing.XNullability
 import androidx.room.compiler.processing.XProcessingEnv
 import androidx.room.compiler.processing.XType
-import androidx.room.ext.L
-import androidx.room.ext.T
 import androidx.room.parser.SQLTypeAffinity
 import androidx.room.solver.CodeGenScope
-import com.squareup.javapoet.TypeName
 import java.nio.ByteBuffer
 
 class ByteBufferColumnTypeAdapter private constructor(out: XType) : ColumnTypeAdapter(
@@ -35,17 +35,25 @@
         indexVarName: String,
         scope: CodeGenScope
     ) {
-        scope.builder().apply {
-            beginControlFlow("if ($L.isNull($L))", cursorVarName, indexVarName).apply {
-                addStatement("$L = null", outVarName)
-            }
-            nextControlFlow("else").apply {
+        scope.builder.apply {
+            fun XCodeBlock.Builder.addGetBlobStatement() {
                 addStatement(
-                    "$L = $T.wrap($L.getBlob($L))",
-                    outVarName, TypeName.get(ByteBuffer::class.java), cursorVarName, indexVarName
+                    "%L = %T.wrap(%L.getBlob(%L))",
+                    outVarName,
+                    ByteBuffer::class.asClassName(),
+                    cursorVarName,
+                    indexVarName
                 )
             }
-            endControlFlow()
+            if (out.nullability == XNullability.NONNULL) {
+                addGetBlobStatement()
+            } else {
+                beginControlFlow("if (%L.isNull(%L))", cursorVarName, indexVarName)
+                    .addStatement("%L = null", outVarName)
+                nextControlFlow("else")
+                    .addGetBlobStatement()
+                endControlFlow()
+            }
         }
     }
 
@@ -55,12 +63,24 @@
         valueVarName: String,
         scope: CodeGenScope
     ) {
-        scope.builder().apply {
-            beginControlFlow("if ($L == null)", valueVarName)
-                .addStatement("$L.bindNull($L)", stmtName, indexVarName)
-            nextControlFlow("else")
-                .addStatement("$L.bindBlob($L, $L.array())", stmtName, indexVarName, valueVarName)
-            endControlFlow()
+        scope.builder.apply {
+            fun XCodeBlock.Builder.addBindBlobStatement() {
+                addStatement(
+                    "%L.bindBlob(%L, %L.array())",
+                    stmtName,
+                    indexVarName,
+                    valueVarName
+                )
+            }
+            if (out.nullability == XNullability.NONNULL) {
+                addBindBlobStatement()
+            } else {
+                beginControlFlow("if (%L == null)", valueVarName)
+                    .addStatement("%L.bindNull(%L)", stmtName, indexVarName)
+                nextControlFlow("else")
+                    .addBindBlobStatement()
+                endControlFlow()
+            }
         }
     }
 
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/ColumnTypeAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/ColumnTypeAdapter.kt
index c1fdc6d..a5c93bf 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/ColumnTypeAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/ColumnTypeAdapter.kt
@@ -16,16 +16,18 @@
 
 package androidx.room.solver.types
 
-import androidx.room.parser.SQLTypeAffinity
+import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.processing.XType
-import com.squareup.javapoet.TypeName
+import androidx.room.parser.SQLTypeAffinity
 
 /**
  * A code generator that can read a field from Cursor and write a field to a Statement
  */
-abstract class ColumnTypeAdapter(val out: XType, val typeAffinity: SQLTypeAffinity) :
-    StatementValueBinder, CursorValueReader {
-    val outTypeName: TypeName by lazy { out.typeName }
+abstract class ColumnTypeAdapter(
+    val out: XType,
+    val typeAffinity: SQLTypeAffinity
+) : StatementValueBinder, CursorValueReader {
+    val outTypeName: XTypeName by lazy { out.asTypeName() }
     override fun typeMirror() = out
     override fun affinity(): SQLTypeAffinity = typeAffinity
 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/CompositeAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/CompositeAdapter.kt
index 0f8c78d..94093c3 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/CompositeAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/CompositeAdapter.kt
@@ -16,8 +16,6 @@
 
 package androidx.room.solver.types
 
-import androidx.room.ext.L
-import androidx.room.ext.T
 import androidx.room.compiler.processing.XType
 import androidx.room.solver.CodeGenScope
 
@@ -30,8 +28,7 @@
     val columnTypeAdapter: ColumnTypeAdapter,
     val intoStatementConverter: TypeConverter?,
     val fromCursorConverter: TypeConverter?
-) :
-    ColumnTypeAdapter(out, columnTypeAdapter.typeAffinity) {
+) : ColumnTypeAdapter(out, columnTypeAdapter.typeAffinity) {
     override fun readFromCursor(
         outVarName: String,
         cursorVarName: String,
@@ -41,9 +38,9 @@
         if (fromCursorConverter == null) {
             return
         }
-        scope.builder().apply {
+        scope.builder.apply {
             val tmpCursorValue = scope.getTmpVar()
-            addStatement("final $T $L", columnTypeAdapter.outTypeName, tmpCursorValue)
+            addLocalVariable(tmpCursorValue, columnTypeAdapter.outTypeName)
             columnTypeAdapter.readFromCursor(tmpCursorValue, cursorVarName, indexVarName, scope)
             fromCursorConverter.convert(tmpCursorValue, outVarName, scope)
         }
@@ -58,7 +55,7 @@
         if (intoStatementConverter == null) {
             return
         }
-        scope.builder().apply {
+        scope.builder.apply {
             val bindVar = intoStatementConverter.convert(valueVarName, scope)
             columnTypeAdapter.bindToStmt(stmtName, indexVarName, bindVar, scope)
         }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/CustomTypeConverterWrapper.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/CustomTypeConverterWrapper.kt
index e17cad7..a62cf47 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/CustomTypeConverterWrapper.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/CustomTypeConverterWrapper.kt
@@ -17,18 +17,17 @@
 package androidx.room.solver.types
 
 import androidx.room.ProvidedTypeConverter
-import androidx.room.ext.L
-import androidx.room.ext.N
-import androidx.room.ext.T
+import androidx.room.compiler.codegen.CodeLanguage
+import androidx.room.compiler.codegen.XClassName
+import androidx.room.compiler.codegen.XCodeBlock
+import androidx.room.compiler.codegen.XFunSpec
+import androidx.room.compiler.codegen.XFunSpec.Builder.Companion.apply
+import androidx.room.compiler.codegen.XPropertySpec
 import androidx.room.ext.decapitalize
 import androidx.room.solver.CodeGenScope
 import androidx.room.vo.CustomTypeConverter
 import androidx.room.writer.DaoWriter
 import androidx.room.writer.TypeWriter
-import com.squareup.javapoet.ClassName
-import com.squareup.javapoet.CodeBlock
-import com.squareup.javapoet.FieldSpec
-import com.squareup.javapoet.MethodSpec
 import java.util.Locale
 import javax.lang.model.element.Modifier
 
@@ -38,118 +37,144 @@
 class CustomTypeConverterWrapper(
     val custom: CustomTypeConverter
 ) : SingleStatementTypeConverter(custom.from, custom.to) {
-    override fun buildStatement(inputVarName: String, scope: CodeGenScope): CodeBlock {
+    override fun buildStatement(inputVarName: String, scope: CodeGenScope): XCodeBlock {
         return if (custom.isEnclosingClassKotlinObject) {
-            CodeBlock.of(
-                "$T.INSTANCE.$L($L)",
-                custom.typeName,
-                custom.methodName, inputVarName
-            )
+            when (scope.language) {
+                CodeLanguage.JAVA -> XCodeBlock.of(
+                    scope.language,
+                    "%T.INSTANCE.%L(%L)",
+                    custom.className,
+                    custom.methodName,
+                    inputVarName
+                )
+                CodeLanguage.KOTLIN -> XCodeBlock.of(
+                    scope.language,
+                    "%T.%L(%L)",
+                    custom.className,
+                    custom.methodName,
+                    inputVarName
+                )
+            }
         } else if (custom.isStatic) {
-            CodeBlock.of(
-                "$T.$L($L)",
-                custom.typeName,
-                custom.methodName, inputVarName
+            XCodeBlock.of(
+                scope.language,
+                "%T.%L(%L)",
+                custom.className,
+                custom.methodName,
+                inputVarName
             )
         } else {
             if (custom.isProvidedConverter) {
-                CodeBlock.of(
-                    "$N().$L($L)",
+                XCodeBlock.of(
+                    scope.language,
+                    "%N().%L(%L)",
                     providedTypeConverter(scope),
-                    custom.methodName, inputVarName
+                    custom.methodName,
+                    inputVarName
                 )
             } else {
-                CodeBlock.of(
-                    "$N.$L($L)",
+                XCodeBlock.of(
+                    scope.language,
+                    "%N.%L(%L)",
                     typeConverter(scope),
-                    custom.methodName, inputVarName
+                    custom.methodName,
+                    inputVarName
                 )
             }
         }
     }
 
-    private fun providedTypeConverter(scope: CodeGenScope): MethodSpec {
-        val className = custom.typeName as ClassName
-        val baseName = className.simpleName().decapitalize(Locale.US)
-
-        val converterClassName = custom.typeName as ClassName
+    private fun providedTypeConverter(scope: CodeGenScope): XFunSpec {
+        val className = custom.className
+        val baseName = className.simpleNames.last().decapitalize(Locale.US)
+        val converterClassName = custom.className
         scope.writer.addRequiredTypeConverter(converterClassName)
-        val converterField = scope.writer.getOrCreateField(
-            object : TypeWriter.SharedFieldSpec(
-                baseName, custom.typeName
+        val converterField = scope.writer.getOrCreateProperty(
+            object : TypeWriter.SharedPropertySpec(
+                baseName, custom.className
             ) {
+                override val isMutable = true
+
                 override fun getUniqueKey(): String {
-                    return "converter_${custom.typeName}"
+                    return "converter_${custom.className}"
                 }
 
-                override fun prepare(writer: TypeWriter, builder: FieldSpec.Builder) {
-                    builder.addModifiers(Modifier.PRIVATE)
+                override fun prepare(writer: TypeWriter, builder: XPropertySpec.Builder) {
                 }
             }
         )
-
-        return scope.writer.getOrCreateMethod(object : TypeWriter.SharedMethodSpec(baseName) {
+        val funSpec = object : TypeWriter.SharedFunctionSpec(baseName) {
             override fun getUniqueKey(): String {
-                return "converterMethod_${custom.typeName}"
+                return "converterMethod_${custom.className}"
             }
 
             override fun prepare(
                 methodName: String,
                 writer: TypeWriter,
-                builder: MethodSpec.Builder
+                builder: XFunSpec.Builder
             ) {
-                builder.apply {
-                    addModifiers(Modifier.PRIVATE)
-                    addModifiers(Modifier.SYNCHRONIZED)
-                    returns(custom.typeName)
-                    addCode(buildConvertMethodBody(writer))
-                }
+                val body = buildConvertFunctionBody(builder.language)
+                builder.apply(
+                    // Apply synchronized modifier for Java
+                    javaMethodBuilder = {
+                        addModifiers(Modifier.SYNCHRONIZED)
+                        builder.addCode(body)
+                    },
+                    // Use synchronized std-lib function for Kotlin
+                    kotlinFunctionBuilder = {
+                        beginControlFlow("return synchronized")
+                        builder.addCode(body)
+                        endControlFlow()
+                    }
+                )
+                builder.returns(custom.className)
             }
 
-            private fun buildConvertMethodBody(
-                writer: TypeWriter
-            ): CodeBlock {
-                val methodScope = CodeGenScope(writer)
-                methodScope.builder().apply {
-                    beginControlFlow("if ($N == null)", converterField)
+            private fun buildConvertFunctionBody(language: CodeLanguage): XCodeBlock {
+                return XCodeBlock.builder(language).apply {
+                    beginControlFlow("if (%N == null)", converterField)
                     addStatement(
-                        "$N = $N.getTypeConverter($T.class)",
+                        "%N = %L.getTypeConverter(%L)",
                         converterField,
-                        DaoWriter.dbField,
-                        custom.typeName
+                        DaoWriter.DB_PROPERTY_NAME,
+                        XCodeBlock.ofJavaClassLiteral(language, custom.className)
                     )
                     endControlFlow()
-                    addStatement("return $N", converterField)
-                }
-                return methodScope.builder().build()
+                    when (language) {
+                        CodeLanguage.JAVA ->
+                            addStatement("return %N", converterField)
+                        CodeLanguage.KOTLIN ->
+                            addStatement("return@synchronized %N", converterField)
+                    }
+                }.build()
             }
-        })
+        }
+        return scope.writer.getOrCreateFunction(funSpec)
     }
 
-    fun typeConverter(scope: CodeGenScope): FieldSpec {
-        val baseName = (custom.typeName as ClassName).simpleName().decapitalize(Locale.US)
-        return scope.writer.getOrCreateField(
-            object : TypeWriter.SharedFieldSpec(
-                baseName, custom.typeName
-            ) {
-                override fun getUniqueKey(): String {
-                    return "converter_${custom.typeName}"
-                }
-
-                override fun prepare(writer: TypeWriter, builder: FieldSpec.Builder) {
-                    builder.addModifiers(Modifier.PRIVATE)
-                    builder.addModifiers(Modifier.FINAL)
-                    builder.initializer("new $T()", custom.typeName)
-                }
+    private fun typeConverter(scope: CodeGenScope): XPropertySpec {
+        val baseName = custom.className.simpleNames.last().decapitalize(Locale.US)
+        val propertySpec = object : TypeWriter.SharedPropertySpec(
+            baseName, custom.className
+        ) {
+            override fun getUniqueKey(): String {
+                return "converter_${custom.className}"
             }
-        )
+
+            override fun prepare(writer: TypeWriter, builder: XPropertySpec.Builder) {
+                builder.initializer(
+                    XCodeBlock.ofNewInstance(builder.language, custom.className)
+                )
+            }
+        }
+        return scope.writer.getOrCreateProperty(propertySpec)
     }
 }
 
-fun TypeWriter.addRequiredTypeConverter(className: ClassName) {
+fun TypeWriter.addRequiredTypeConverter(className: XClassName) {
     this[ProvidedTypeConverter::class] = getRequiredTypeConverters() + setOf(className)
 }
 
-fun TypeWriter.getRequiredTypeConverters(): Set<ClassName> {
-    return this.get<Set<ClassName>>(ProvidedTypeConverter::class) ?: emptySet()
+fun TypeWriter.getRequiredTypeConverters(): Set<XClassName> {
+    return this.get<Set<XClassName>>(ProvidedTypeConverter::class) ?: emptySet()
 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/EnumColumnTypeAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/EnumColumnTypeAdapter.kt
index 3820397..7d34d65 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/EnumColumnTypeAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/EnumColumnTypeAdapter.kt
@@ -16,25 +16,25 @@
 
 package androidx.room.solver.types
 
+import androidx.room.compiler.codegen.CodeLanguage
+import androidx.room.compiler.codegen.XCodeBlock
+import androidx.room.compiler.codegen.XFunSpec
+import androidx.room.compiler.codegen.asClassName
 import androidx.room.compiler.processing.XEnumTypeElement
-import androidx.room.ext.CommonTypeNames.ILLEGAL_ARG_EXCEPTION
-import androidx.room.ext.L
-import androidx.room.ext.N
-import androidx.room.ext.S
-import androidx.room.ext.T
+import androidx.room.compiler.processing.XNullability
+import androidx.room.compiler.processing.XType
 import androidx.room.parser.SQLTypeAffinity.TEXT
 import androidx.room.solver.CodeGenScope
 import androidx.room.writer.TypeWriter
-import com.squareup.javapoet.MethodSpec
-import com.squareup.javapoet.ParameterSpec
-import javax.lang.model.element.Modifier
 
 /**
  * Uses enum string representation.
  */
 class EnumColumnTypeAdapter(
-    private val enumTypeElement: XEnumTypeElement
-) : ColumnTypeAdapter(enumTypeElement.type, TEXT) {
+    private val enumTypeElement: XEnumTypeElement,
+    out: XType
+) : ColumnTypeAdapter(out, TEXT) {
+
     override fun readFromCursor(
         outVarName: String,
         cursorVarName: String,
@@ -42,11 +42,17 @@
         scope: CodeGenScope
     ) {
         val stringToEnumMethod = stringToEnumMethod(scope)
-        scope.builder()
-            .addStatement(
-                "$L = $N($L.getString($L))",
+        if (scope.language == CodeLanguage.KOTLIN && out.nullability == XNullability.NONNULL) {
+            scope.builder.addStatement(
+                "%L = checkNotNull(%N(%L.getString(%L)))",
                 outVarName, stringToEnumMethod, cursorVarName, indexVarName
             )
+        } else {
+            scope.builder.addStatement(
+                "%L = %N(%L.getString(%L))",
+                outVarName, stringToEnumMethod, cursorVarName, indexVarName
+            )
+        }
     }
 
     override fun bindToStmt(
@@ -56,92 +62,165 @@
         scope: CodeGenScope
     ) {
         val enumToStringMethod = enumToStringMethod(scope)
-        scope.builder().apply {
-            beginControlFlow("if ($L == null)", valueVarName)
-                .addStatement("$L.bindNull($L)", stmtName, indexVarName)
-            nextControlFlow("else")
-                .addStatement(
-                    "$L.bindString($L, $N($L))",
+        scope.builder.apply {
+            if (language == CodeLanguage.KOTLIN && out.nullability == XNullability.NONNULL) {
+                addStatement(
+                    "%L.bindString(%L, checkNotNull(%N(%L)))",
                     stmtName, indexVarName, enumToStringMethod, valueVarName
                 )
-            endControlFlow()
+            } else {
+                beginControlFlow("if (%L == null)", valueVarName)
+                    .addStatement("%L.bindNull(%L)", stmtName, indexVarName)
+                nextControlFlow("else")
+                    .addStatement(
+                        "%L.bindString(%L, %N(%L))",
+                        stmtName, indexVarName, enumToStringMethod, valueVarName
+                    )
+                endControlFlow()
+            }
         }
     }
 
-    private fun enumToStringMethod(scope: CodeGenScope): MethodSpec {
-        return scope.writer.getOrCreateMethod(object :
-            TypeWriter.SharedMethodSpec(out.typeElement!!.name + "_enumToString") {
+    private fun enumToStringMethod(scope: CodeGenScope): XFunSpec {
+        val funSpec = object : TypeWriter.SharedFunctionSpec(
+            out.typeElement!!.name + "_enumToString"
+        ) {
             override fun getUniqueKey(): String {
-                return "enumToString_" + out.typeName.toString()
+                return "enumToString_" + enumTypeElement.asClassName().toString()
             }
 
             override fun prepare(
                 methodName: String,
                 writer: TypeWriter,
-                builder: MethodSpec.Builder
+                builder: XFunSpec.Builder
             ) {
-                builder.apply {
-                        addModifiers(Modifier.PRIVATE)
-                        returns(String::class.java)
-                        val param = ParameterSpec.builder(
-                            out.typeName, "_value", Modifier.FINAL
-                        ).build()
-                        addParameter(param)
-                        beginControlFlow("if ($N == null)", param)
+                val body = XCodeBlock.builder(builder.language).apply {
+                    val paramName = "_value"
+                    beginControlFlow("if (%L == null)", paramName).apply {
                         addStatement("return null")
-                        nextControlFlow("switch ($N)", param)
-                        enumTypeElement.entries.map { it.name }.forEach { enumConstantName ->
-                            addStatement("case $L: return $S", enumConstantName, enumConstantName)
-                        }
-                        addStatement(
-                            "default: throw new $T($S + $N)",
-                            ILLEGAL_ARG_EXCEPTION,
-                            "Can't convert enum to string, unknown enum value: ",
-                            param
-                        )
-                        endControlFlow()
                     }
+                    endControlFlow()
+                    when (writer.codeLanguage) {
+                        // Use a switch control flow
+                        CodeLanguage.JAVA -> {
+                            beginControlFlow("switch (%L)", paramName)
+                            enumTypeElement.entries.map { it.name }.forEach { enumConstantName ->
+                                addStatement(
+                                    "case %L: return %S",
+                                    enumConstantName, enumConstantName
+                                )
+                            }
+                            addStatement(
+                                "default: throw new %T(%S + %L)",
+                                ILLEGAL_ARG_EXCEPTION,
+                                ENUM_TO_STRING_ERROR_MSG,
+                                paramName
+                            )
+                            endControlFlow()
+                        }
+                        // Use a when control flow
+                        CodeLanguage.KOTLIN -> {
+                            beginControlFlow("return when (%L)", paramName)
+                            enumTypeElement.entries.map { it.name }.forEach { enumConstantName ->
+                                addStatement("%L -> %S", enumConstantName, enumConstantName)
+                            }
+                            addStatement(
+                                "else -> throw %T(%S + %L)",
+                                ILLEGAL_ARG_EXCEPTION,
+                                ENUM_TO_STRING_ERROR_MSG,
+                                paramName
+                            )
+                            endControlFlow()
+                        }
+                    }
+                }.build()
+                builder.apply {
+                    returns(String::class.asClassName().copy(nullable = true))
+                    addParameter(
+                        out.asTypeName().copy(nullable = true),
+                        "_value"
+                    )
+                    addCode(body)
                 }
-            })
+            }
+        }
+        return scope.writer.getOrCreateFunction(funSpec)
     }
 
-    private fun stringToEnumMethod(scope: CodeGenScope): MethodSpec {
-        return scope.writer.getOrCreateMethod(object :
-            TypeWriter.SharedMethodSpec(out.typeElement!!.name + "_stringToEnum") {
+    private fun stringToEnumMethod(scope: CodeGenScope): XFunSpec {
+        val funSpec = object : TypeWriter.SharedFunctionSpec(
+            out.typeElement!!.name + "_stringToEnum"
+        ) {
             override fun getUniqueKey(): String {
-                return out.typeName.toString()
+                return "stringToEnum_" + enumTypeElement.asClassName().toString()
             }
 
             override fun prepare(
                 methodName: String,
                 writer: TypeWriter,
-                builder: MethodSpec.Builder
+                builder: XFunSpec.Builder
             ) {
-                builder.apply {
-                        addModifiers(Modifier.PRIVATE)
-                        returns(out.typeName)
-                        val param = ParameterSpec.builder(
-                            String::class.java, "_value", Modifier.FINAL
-                        ).build()
-                        addParameter(param)
-                        beginControlFlow("if ($N == null)", param)
+                val body = XCodeBlock.builder(builder.language).apply {
+                    val paramName = "_value"
+                    beginControlFlow("if (%L == null)", paramName).apply {
                         addStatement("return null")
-                        nextControlFlow("switch ($N)", param)
-                        enumTypeElement.entries.map { it.name }.forEach { enumConstantName ->
-                            addStatement(
-                                "case $S: return $T.$L",
-                                enumConstantName, out.typeName, enumConstantName
-                            )
-                        }
-                        addStatement(
-                            "default: throw new $T($S + $N)",
-                            ILLEGAL_ARG_EXCEPTION,
-                            "Can't convert value to enum, unknown value: ",
-                            param
-                        )
-                        endControlFlow()
                     }
+                    endControlFlow()
+                    when (writer.codeLanguage) {
+                        // Use a switch control flow
+                        CodeLanguage.JAVA -> {
+                            beginControlFlow("switch (%L)", paramName)
+                            enumTypeElement.entries.map { it.name }.forEach { enumConstantName ->
+                                addStatement(
+                                    "case %S: return %T.%L",
+                                    enumConstantName, out.asTypeName(), enumConstantName
+                                )
+                            }
+                            addStatement(
+                                "default: throw new %T(%S + %L)",
+                                ILLEGAL_ARG_EXCEPTION,
+                                STRING_TO_ENUM_ERROR_MSG,
+                                paramName
+                            )
+                            endControlFlow()
+                        }
+                        // Use a when control flow
+                        CodeLanguage.KOTLIN -> {
+                            beginControlFlow("return when (%L)", paramName)
+                            enumTypeElement.entries.map { it.name }.forEach { enumConstantName ->
+                                addStatement(
+                                    "%S -> %T.%L",
+                                    enumConstantName, out.asTypeName(), enumConstantName
+                                )
+                            }
+                            addStatement(
+                                "else -> throw %T(%S + %L)",
+                                ILLEGAL_ARG_EXCEPTION,
+                                STRING_TO_ENUM_ERROR_MSG,
+                                paramName
+                            )
+                            endControlFlow()
+                        }
+                    }
+                }.build()
+                builder.apply {
+                    returns(out.asTypeName().copy(nullable = true))
+                    addParameter(
+                        String::class.asClassName().copy(nullable = true),
+                        "_value"
+                    )
+                    addCode(body)
                 }
-            })
+            }
+        }
+        return scope.writer.getOrCreateFunction(funSpec)
+    }
+
+    companion object {
+        private val ILLEGAL_ARG_EXCEPTION = IllegalArgumentException::class.asClassName()
+        private const val ENUM_TO_STRING_ERROR_MSG =
+            "Can't convert enum to string, unknown enum value: "
+        private const val STRING_TO_ENUM_ERROR_MSG =
+            "Can't convert value to enum, unknown value: "
     }
 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/PrimitiveBooleanToIntConverter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/PrimitiveBooleanToIntConverter.kt
index efbb2c2..e98b077 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/PrimitiveBooleanToIntConverter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/PrimitiveBooleanToIntConverter.kt
@@ -16,32 +16,39 @@
 
 package androidx.room.solver.types
 
-import androidx.room.ext.L
+import androidx.room.compiler.codegen.CodeLanguage
+import androidx.room.compiler.codegen.XCodeBlock
+import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.processing.XProcessingEnv
 import androidx.room.solver.CodeGenScope
-import com.squareup.javapoet.CodeBlock
-import com.squareup.javapoet.TypeName
 
 /**
  * int to boolean adapter.
  */
 object PrimitiveBooleanToIntConverter {
     fun create(processingEnvironment: XProcessingEnv): List<TypeConverter> {
-        val tBoolean = processingEnvironment.requireType(TypeName.BOOLEAN)
-        val tInt = processingEnvironment.requireType(TypeName.INT)
+        val tBoolean = processingEnvironment.requireType(XTypeName.PRIMITIVE_BOOLEAN)
+        val tInt = processingEnvironment.requireType(XTypeName.PRIMITIVE_INT)
         return listOf(
             object : SingleStatementTypeConverter(tBoolean, tInt) {
-                override fun buildStatement(inputVarName: String, scope: CodeGenScope): CodeBlock {
-                    return CodeBlock.of(
-                        "$L ? 1 : 0", inputVarName
-                    )
+                override fun buildStatement(inputVarName: String, scope: CodeGenScope): XCodeBlock {
+                    return when (scope.language) {
+                        CodeLanguage.JAVA -> XCodeBlock.of(
+                            scope.language,
+                            "%L ? 1 : 0",
+                            inputVarName
+                        )
+                        CodeLanguage.KOTLIN -> XCodeBlock.of(
+                            scope.language,
+                            "if (%L) 1 else 0",
+                            inputVarName
+                        )
+                    }
                 }
             },
             object : SingleStatementTypeConverter(tInt, tBoolean) {
-                override fun buildStatement(inputVarName: String, scope: CodeGenScope): CodeBlock {
-                    return CodeBlock.of(
-                        "$L != 0", inputVarName
-                    )
+                override fun buildStatement(inputVarName: String, scope: CodeGenScope): XCodeBlock {
+                    return XCodeBlock.of(scope.language, "%L != 0", inputVarName)
                 }
             }
         )
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/SingleStatementTypeConverter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/SingleStatementTypeConverter.kt
index 9e22c51..084f28a 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/SingleStatementTypeConverter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/SingleStatementTypeConverter.kt
@@ -16,9 +16,8 @@
 
 package androidx.room.solver.types
 
+import androidx.room.compiler.codegen.XCodeBlock
 import androidx.room.compiler.processing.XType
-import androidx.room.ext.L
-import androidx.room.ext.T
 import androidx.room.solver.CodeGenScope
 import com.squareup.javapoet.CodeBlock
 
@@ -28,20 +27,20 @@
 abstract class SingleStatementTypeConverter(
     from: XType,
     to: XType
-) : TypeConverter(
-    from, to
-) {
+) : TypeConverter(from, to) {
     final override fun doConvert(inputVarName: String, outputVarName: String, scope: CodeGenScope) {
-        scope.builder().apply {
-            addStatement("$L = $L", outputVarName, buildStatement(inputVarName, scope))
+        scope.builder.apply {
+            addStatement("%L = %L", outputVarName, buildStatement(inputVarName, scope))
         }
     }
 
     final override fun doConvert(inputVarName: String, scope: CodeGenScope): String {
         val outputVarName = scope.getTmpVar()
-        scope.builder().apply {
-            addStatement(
-                "final $T $L = $L", to.typeName, outputVarName, buildStatement(inputVarName, scope)
+        scope.builder.apply {
+            addLocalVariable(
+                name = outputVarName,
+                typeName = to.asTypeName(),
+                assignExpr = buildStatement(inputVarName, scope)
             )
         }
         return outputVarName
@@ -53,5 +52,5 @@
     abstract fun buildStatement(
         inputVarName: String,
         scope: CodeGenScope
-    ): CodeBlock
+    ): XCodeBlock
 }
\ No newline at end of file
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/StringColumnTypeAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/StringColumnTypeAdapter.kt
index d2dea48..dfda989 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/StringColumnTypeAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/StringColumnTypeAdapter.kt
@@ -16,10 +16,10 @@
 
 package androidx.room.solver.types
 
+import androidx.room.compiler.processing.XNullability
 import androidx.room.compiler.processing.XProcessingEnv
 import androidx.room.compiler.processing.XType
 import androidx.room.ext.CommonTypeNames
-import androidx.room.ext.L
 import androidx.room.parser.SQLTypeAffinity.TEXT
 import androidx.room.solver.CodeGenScope
 
@@ -32,16 +32,18 @@
         indexVarName: String,
         scope: CodeGenScope
     ) {
-        scope.builder().apply {
-            // according to docs, getString might throw if the value is null
-            // https://developer.android.com/reference/android/database/Cursor#getString(int)
-            beginControlFlow("if ($L.isNull($L))", cursorVarName, indexVarName).apply {
-                addStatement("$L = null", outVarName)
+        scope.builder.apply {
+            if (out.nullability == XNullability.NONNULL) {
+                addStatement("%L = %L.getString(%L)", outVarName, cursorVarName, indexVarName)
+            } else {
+                beginControlFlow("if (%L.isNull(%L))", cursorVarName, indexVarName).apply {
+                    addStatement("%L = null", outVarName)
+                }
+                nextControlFlow("else").apply {
+                    addStatement("%L = %L.getString(%L)", outVarName, cursorVarName, indexVarName)
+                }
+                endControlFlow()
             }
-            nextControlFlow("else").apply {
-                addStatement("$L = $L.getString($L)", outVarName, cursorVarName, indexVarName)
-            }
-            endControlFlow()
         }
     }
 
@@ -51,12 +53,16 @@
         valueVarName: String,
         scope: CodeGenScope
     ) {
-        scope.builder().apply {
-            beginControlFlow("if ($L == null)", valueVarName)
-                .addStatement("$L.bindNull($L)", stmtName, indexVarName)
-            nextControlFlow("else")
-                .addStatement("$L.bindString($L, $L)", stmtName, indexVarName, valueVarName)
-            endControlFlow()
+        scope.builder.apply {
+            if (out.nullability == XNullability.NONNULL) {
+                addStatement("%L.bindString(%L, %L)", stmtName, indexVarName, valueVarName)
+            } else {
+                beginControlFlow("if (%L == null)", valueVarName)
+                    .addStatement("%L.bindNull(%L)", stmtName, indexVarName)
+                nextControlFlow("else")
+                    .addStatement("%L.bindString(%L, %L)", stmtName, indexVarName, valueVarName)
+                endControlFlow()
+            }
         }
     }
 
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/UuidColumnTypeAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/UuidColumnTypeAdapter.kt
index 71afc7a..adc3525 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/UuidColumnTypeAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/UuidColumnTypeAdapter.kt
@@ -16,10 +16,11 @@
 
 package androidx.room.solver.types
 
+import androidx.room.compiler.codegen.XCodeBlock
+import androidx.room.compiler.codegen.XMemberName.Companion.packageMember
+import androidx.room.compiler.processing.XNullability
 import androidx.room.compiler.processing.XType
-import androidx.room.ext.L
 import androidx.room.ext.RoomTypeNames
-import androidx.room.ext.T
 import androidx.room.parser.SQLTypeAffinity
 import androidx.room.solver.CodeGenScope
 
@@ -35,20 +36,25 @@
         valueVarName: String,
         scope: CodeGenScope
     ) {
-        scope.builder().apply {
-            val conversionMethodName = "convertUUIDToByte"
-            beginControlFlow("if ($L == null)", valueVarName)
-                .addStatement("$L.bindNull($L)", stmtName, indexVarName)
-            nextControlFlow("else")
-                .addStatement(
-                    "$L.bindBlob($L, $T.$L($L))",
+        scope.builder.apply {
+            fun XCodeBlock.Builder.addBindBlobStatement() {
+                addStatement(
+                    "%L.bindBlob(%L, %M(%L))",
                     stmtName,
                     indexVarName,
-                    RoomTypeNames.UUID_UTIL,
-                    conversionMethodName,
+                    RoomTypeNames.UUID_UTIL.packageMember("convertUUIDToByte"),
                     valueVarName,
                 )
-            endControlFlow()
+            }
+            if (out.nullability == XNullability.NONNULL) {
+                addBindBlobStatement()
+            } else {
+                beginControlFlow("if (%L == null)", valueVarName)
+                    .addStatement("%L.bindNull(%L)", stmtName, indexVarName)
+                nextControlFlow("else")
+                    addBindBlobStatement()
+                endControlFlow()
+            }
         }
     }
 
@@ -58,20 +64,25 @@
         indexVarName: String,
         scope: CodeGenScope
     ) {
-        scope.builder().apply {
-            val conversionMethodName = "convertByteToUUID"
-            beginControlFlow("if ($L.isNull($L))", cursorVarName, indexVarName)
-                .addStatement("$L = null", outVarName)
-            nextControlFlow("else")
-                .addStatement(
-                    "$L = $T.$L($L.getBlob($L))",
+        scope.builder.apply {
+            fun XCodeBlock.Builder.addGetBlobStatement() {
+                addStatement(
+                    "%L = %M(%L.getBlob(%L))",
                     outVarName,
-                    RoomTypeNames.UUID_UTIL,
-                    conversionMethodName,
+                    RoomTypeNames.UUID_UTIL.packageMember("convertByteToUUID"),
                     cursorVarName,
                     indexVarName
                 )
-            endControlFlow()
+            }
+            if (out.nullability == XNullability.NONNULL) {
+                addGetBlobStatement()
+            } else {
+                beginControlFlow("if (%L.isNull(%L))", cursorVarName, indexVarName)
+                    .addStatement("%L = null", outVarName)
+                nextControlFlow("else")
+                    .addGetBlobStatement()
+                endControlFlow()
+            }
         }
     }
 }
\ No newline at end of file
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/CustomTypeConverter.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/CustomTypeConverter.kt
index eaab06c..685d185 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/CustomTypeConverter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/CustomTypeConverter.kt
@@ -16,24 +16,26 @@
 
 package androidx.room.vo
 
+import androidx.room.compiler.codegen.XClassName
+import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.processing.XMethodElement
 import androidx.room.compiler.processing.XType
-import com.squareup.javapoet.TypeName
+import androidx.room.compiler.processing.XTypeElement
 
 /**
  * Generated when we parse a method annotated with TypeConverter.
  */
 data class CustomTypeConverter(
-    val enclosingClass: XType,
+    val enclosingClass: XTypeElement,
     val isEnclosingClassKotlinObject: Boolean,
     val method: XMethodElement,
     val from: XType,
     val to: XType,
     val isProvidedConverter: Boolean
 ) {
-    val typeName: TypeName by lazy { enclosingClass.typeName }
-    val fromTypeName: TypeName by lazy { from.typeName }
-    val toTypeName: TypeName by lazy { to.typeName }
+    val className: XClassName by lazy { enclosingClass.asClassName() }
+    val fromTypeName: XTypeName by lazy { from.asTypeName() }
+    val toTypeName: XTypeName by lazy { to.asTypeName() }
     val methodName by lazy { method.jvmName }
     val isStatic by lazy { method.isStatic() }
 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/Dao.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/Dao.kt
index 94f9283..eebca3b 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/Dao.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/Dao.kt
@@ -17,9 +17,9 @@
 package androidx.room.vo
 
 import androidx.room.compiler.codegen.XClassName
+import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.processing.XType
 import androidx.room.compiler.processing.XTypeElement
-import com.squareup.javapoet.TypeName
 
 data class Dao(
     val element: XTypeElement,
@@ -33,7 +33,7 @@
     val transactionMethods: List<TransactionMethod>,
     val delegatingMethods: List<KotlinBoxedPrimitiveMethodDelegate>,
     val kotlinDefaultMethodDelegates: List<KotlinDefaultMethodDelegate>,
-    val constructorParamType: TypeName?
+    val constructorParamType: XTypeName?
 ) {
     // parsed dao might have a suffix if it is used in multiple databases.
     private var suffix: String? = null
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/Pojo.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/Pojo.kt
index 25241e9..47a7e43 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/Pojo.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/Pojo.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.vo
 
+import androidx.room.compiler.codegen.XClassName
 import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.processing.XType
 import androidx.room.compiler.processing.XTypeElement
@@ -33,6 +34,7 @@
     val relations: List<Relation>,
     val constructor: Constructor? = null
 ) : HasFields {
+    val className: XClassName by lazy { element.asClassName() }
     val typeName: XTypeName by lazy { type.asTypeName() }
 
     override val fields = Fields(fields)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt
index 00a4ccb..606cfda 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt
@@ -21,7 +21,6 @@
 import androidx.room.ext.CollectionTypeNames
 import androidx.room.ext.CommonTypeNames
 import androidx.room.ext.L
-import androidx.room.ext.N
 import androidx.room.ext.T
 import androidx.room.ext.capitalize
 import androidx.room.ext.stripNonJava
@@ -39,7 +38,7 @@
 import androidx.room.solver.query.result.SingleColumnRowAdapter
 import androidx.room.verifier.DatabaseVerificationErrors
 import androidx.room.writer.QueryWriter
-import androidx.room.writer.RelationCollectorMethodWriter
+import androidx.room.writer.RelationCollectorFunctionWriter
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.CodeBlock
 import com.squareup.javapoet.ParameterizedTypeName
@@ -135,9 +134,9 @@
 
     fun writeCollectionCode(scope: CodeGenScope) {
         val method = scope.writer
-            .getOrCreateMethod(RelationCollectorMethodWriter(this))
+            .getOrCreateFunction(RelationCollectorFunctionWriter(this))
         scope.builder().apply {
-            addStatement("$N($L)", method, varName)
+            addStatement("$L($L)", method.name, varName)
         }
     }
 
@@ -269,8 +268,8 @@
                     val longSparseArrayElement = context.processingEnv
                         .requireTypeElement(CollectionTypeNames.LONG_SPARSE_ARRAY)
                     QueryParameter(
-                        name = RelationCollectorMethodWriter.PARAM_MAP_VARIABLE,
-                        sqlName = RelationCollectorMethodWriter.PARAM_MAP_VARIABLE,
+                        name = RelationCollectorFunctionWriter.PARAM_MAP_VARIABLE,
+                        sqlName = RelationCollectorFunctionWriter.PARAM_MAP_VARIABLE,
                         type = longSparseArrayElement.type,
                         queryParamAdapter = LONG_SPARSE_ARRAY_KEY_QUERY_PARAM_ADAPTER
                     )
@@ -279,8 +278,8 @@
                     val set = context.processingEnv.requireTypeElement("java.util.Set")
                     val keySet = context.processingEnv.getDeclaredType(set, keyTypeMirror)
                     QueryParameter(
-                        name = RelationCollectorMethodWriter.KEY_SET_VARIABLE,
-                        sqlName = RelationCollectorMethodWriter.KEY_SET_VARIABLE,
+                        name = RelationCollectorFunctionWriter.KEY_SET_VARIABLE,
+                        sqlName = RelationCollectorFunctionWriter.KEY_SET_VARIABLE,
                         type = keySet,
                         queryParamAdapter = context.typeAdapterStore.findQueryParameterAdapter(
                             typeMirror = keySet,
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/ShortcutEntity.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/ShortcutEntity.kt
index a87b33f..b2610f6 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/ShortcutEntity.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/ShortcutEntity.kt
@@ -16,8 +16,6 @@
 
 package androidx.room.vo
 
-import androidx.room.compiler.codegen.toJavaPoet
-
 /**
  * Represents a shortcut method parameter entity.
  */
@@ -26,7 +24,8 @@
     private val partialEntity: Pojo? // the partial entity
 ) {
     val tableName = entity.tableName
-    val entityTypeName = entity.typeName.toJavaPoet()
+    val entityClassName = entity.className
+    val entityTypeName = entity.typeName
     val primaryKey by lazy {
         if (partialEntity == null) {
             entity.primaryKey
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/AutoMigrationWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/AutoMigrationWriter.kt
index 9898cf0..0216b4c 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/AutoMigrationWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/AutoMigrationWriter.kt
@@ -22,7 +22,8 @@
 import androidx.room.compiler.codegen.XFunSpec
 import androidx.room.compiler.codegen.XFunSpec.Builder.Companion.addStatement
 import androidx.room.compiler.codegen.XTypeSpec
-import androidx.room.compiler.codegen.addOriginatingElement
+import androidx.room.compiler.codegen.XTypeSpec.Builder.Companion.addOriginatingElement
+import androidx.room.compiler.codegen.XTypeSpec.Builder.Companion.addProperty
 import androidx.room.compiler.codegen.toXClassName
 import androidx.room.compiler.processing.XTypeElement
 import androidx.room.ext.RoomTypeNames
@@ -56,8 +57,8 @@
 
             if (autoMigration.specClassName != null) {
                 builder.addProperty(
-                    typeName = RoomTypeNames.AUTO_MIGRATION_SPEC.toXClassName(),
                     name = "callback",
+                    typeName = RoomTypeNames.AUTO_MIGRATION_SPEC.toXClassName(),
                     visibility = VisibilityModifier.PRIVATE,
                     initExpr = if (!autoMigration.isSpecProvided) {
                         XCodeBlock.ofNewInstance(
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
index d5ac75a..0486273 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
@@ -17,24 +17,34 @@
 package androidx.room.writer
 
 import androidx.room.compiler.codegen.CodeLanguage
+import androidx.room.compiler.codegen.VisibilityModifier
+import androidx.room.compiler.codegen.XClassName
+import androidx.room.compiler.codegen.XCodeBlock
+import androidx.room.compiler.codegen.XCodeBlock.Builder.Companion.apply
+import androidx.room.compiler.codegen.XFunSpec
+import androidx.room.compiler.codegen.XFunSpec.Builder.Companion.apply
+import androidx.room.compiler.codegen.XPropertySpec
+import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.codegen.XTypeSpec
-import androidx.room.compiler.codegen.XTypeSpec.Builder.Companion.apply
+import androidx.room.compiler.codegen.XTypeSpec.Builder.Companion.addOriginatingElement
+import androidx.room.compiler.codegen.asClassName
 import androidx.room.compiler.codegen.toJavaPoet
-import androidx.room.compiler.processing.MethodSpecHelper
 import androidx.room.compiler.processing.XElement
 import androidx.room.compiler.processing.XMethodElement
 import androidx.room.compiler.processing.XType
-import androidx.room.compiler.processing.addOriginatingElement
 import androidx.room.compiler.processing.isVoid
-import androidx.room.ext.CommonTypeNames
 import androidx.room.ext.L
 import androidx.room.ext.N
-import androidx.room.ext.RoomTypeNames
+import androidx.room.ext.RoomTypeNames.DELETE_OR_UPDATE_ADAPTER
+import androidx.room.ext.RoomTypeNames.INSERTION_ADAPTER
+import androidx.room.ext.RoomTypeNames.ROOM_DB
+import androidx.room.ext.RoomTypeNames.ROOM_SQL_QUERY
+import androidx.room.ext.RoomTypeNames.SHARED_SQLITE_STMT
+import androidx.room.ext.RoomTypeNames.UPSERTION_ADAPTER
 import androidx.room.ext.SupportDbTypeNames
 import androidx.room.ext.T
 import androidx.room.ext.W
 import androidx.room.ext.capitalize
-import androidx.room.ext.stripNonJava
 import androidx.room.processor.OnConflictProcessor
 import androidx.room.solver.CodeGenScope
 import androidx.room.solver.KotlinDefaultMethodDelegateBinder
@@ -52,22 +62,12 @@
 import androidx.room.vo.UpdateMethod
 import androidx.room.vo.UpsertionMethod
 import androidx.room.vo.WriteQueryMethod
-import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.CodeBlock
-import com.squareup.javapoet.FieldSpec
-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 com.squareup.javapoet.WildcardTypeName
+import com.squareup.kotlinpoet.javapoet.JWildcardTypeName
 import java.util.Arrays
 import java.util.Collections
 import java.util.Locale
-import javax.lang.model.element.Modifier.FINAL
-import javax.lang.model.element.Modifier.PRIVATE
-import javax.lang.model.element.Modifier.PUBLIC
-import javax.lang.model.element.Modifier.STATIC
 
 /**
  * Creates the implementation for a class annotated with Dao.
@@ -79,28 +79,29 @@
 ) : TypeWriter(codeLanguage) {
     private val declaredDao = dao.element.type
 
+    // TODO nothing prevents this from conflicting, we should fix.
+    private val dbProperty: XPropertySpec = XPropertySpec
+        .builder(codeLanguage, DB_PROPERTY_NAME, ROOM_DB, VisibilityModifier.PRIVATE)
+        .build()
+
+    private val companionTypeBuilder = lazy {
+        XTypeSpec.companionObjectBuilder(codeLanguage)
+    }
+
     companion object {
         const val GET_LIST_OF_TYPE_CONVERTERS_METHOD = "getRequiredConverters"
 
-        // TODO nothing prevents this from conflicting, we should fix.
-        val dbField: FieldSpec = FieldSpec
-            .builder(RoomTypeNames.ROOM_DB, "__db", PRIVATE, FINAL)
-            .build()
+        const val DB_PROPERTY_NAME = "__db"
 
         private fun shortcutEntityFieldNamePart(shortcutEntity: ShortcutEntity): String {
-            return if (shortcutEntity.isPartialEntity) {
-                typeNameToFieldName(shortcutEntity.pojo.typeName.toJavaPoet()) + "As" +
-                    typeNameToFieldName(shortcutEntity.entityTypeName)
-            } else {
-                typeNameToFieldName(shortcutEntity.entityTypeName)
+            fun typeNameToFieldName(typeName: XClassName): String {
+                return typeName.simpleNames.last()
             }
-        }
-
-        private fun typeNameToFieldName(typeName: TypeName?): String {
-            return if (typeName is ClassName) {
-                typeName.simpleName()
+            return if (shortcutEntity.isPartialEntity) {
+                typeNameToFieldName(shortcutEntity.pojo.className) + "As" +
+                    typeNameToFieldName(shortcutEntity.entityClassName)
             } else {
-                typeName.toString().replace('.', '_').stripNonJava()
+                typeNameToFieldName(shortcutEntity.entityClassName)
             }
         }
     }
@@ -130,82 +131,119 @@
             addAll(createUpsertMethods())
         }
 
-        builder.apply(
-            javaTypeBuilder = {
-                addOriginatingElement(dbElement)
-                addModifiers(PUBLIC)
-                addModifiers(FINAL)
-                if (dao.element.isInterface()) {
-                    addSuperinterface(dao.typeName.toJavaPoet())
-                } else {
-                    superclass(dao.typeName.toJavaPoet())
-                }
-                addField(dbField)
-                val dbParam = ParameterSpec
-                    .builder(dao.constructorParamType ?: dbField.type, dbField.name).build()
-
-                addMethod(
-                    createConstructor(
-                        dbParam,
-                        shortcutMethods,
-                        dao.constructorParamType != null
-                    )
-                )
-
-                shortcutMethods.forEach {
-                    addMethod(it.methodImpl)
-                }
-
-                dao.queryMethods.filterIsInstance<ReadQueryMethod>().forEach { method ->
-                    addMethod(createSelectMethod(method))
-                }
-                oneOffPreparedQueries.forEach {
-                    addMethod(createPreparedQueryMethod(it))
-                }
-                dao.rawQueryMethods.forEach {
-                    addMethod(createRawQueryMethod(it))
-                }
-                dao.kotlinDefaultMethodDelegates.forEach {
-                    addMethod(createDefaultMethodDelegate(it))
-                }
-
-                dao.delegatingMethods.forEach {
-                    addMethod(createDelegatingMethod(it))
-                }
-                // keep this the last one to be generated because used custom converters will
-                // register fields with a payload which we collect in dao to report used
-                // Type Converters.
-                addMethod(createConverterListMethod())
-            },
-            kotlinTypeBuilder = {
-                TODO("Kotlin codegen not yet implemented!")
+        builder.apply {
+            addOriginatingElement(dbElement)
+            setVisibility(VisibilityModifier.PUBLIC)
+            if (dao.element.isInterface()) {
+                addSuperinterface(dao.typeName)
+            } else {
+                superclass(dao.typeName)
             }
-        )
+            addProperty(dbProperty)
+
+            addFunction(
+                createConstructor(
+                    shortcutMethods,
+                    dao.constructorParamType != null
+                )
+            )
+
+            shortcutMethods.forEach {
+                addFunction(it.functionImpl)
+            }
+
+            dao.queryMethods.filterIsInstance<ReadQueryMethod>().forEach { method ->
+                addFunction(createSelectMethod(method))
+            }
+            oneOffPreparedQueries.forEach {
+                addFunction(createPreparedQueryMethod(it))
+            }
+            dao.rawQueryMethods.forEach {
+                addFunction(createRawQueryMethod(it))
+            }
+            dao.kotlinDefaultMethodDelegates.forEach {
+                addFunction(createDefaultMethodDelegate(it))
+            }
+
+            dao.delegatingMethods.forEach {
+                addFunction(createDelegatingMethod(it))
+            }
+            // Keep this the last one to be generated because used custom converters will
+            // register fields with a payload which we collect in dao to report used
+            // Type Converters.
+            addConverterListMethod(this)
+
+            if (companionTypeBuilder.isInitialized()) {
+                addType(companionTypeBuilder.value.build())
+            }
+        }
         return builder
     }
 
-    private fun createConverterListMethod(): MethodSpec {
-        return MethodSpec.methodBuilder(GET_LIST_OF_TYPE_CONVERTERS_METHOD).apply {
-            addModifiers(STATIC, PUBLIC)
+    private fun addConverterListMethod(typeSpecBuilder: XTypeSpec.Builder) {
+        when (codeLanguage) {
+            // For Java a static method is created
+            CodeLanguage.JAVA -> typeSpecBuilder.addFunction(createConverterListMethod())
+            // For Kotlin a function in the companion object is created
+            CodeLanguage.KOTLIN -> companionTypeBuilder.value
+                    .addFunction(createConverterListMethod())
+                    .build()
+        }
+    }
+
+    private fun createConverterListMethod(): XFunSpec {
+        val body = XCodeBlock.builder(codeLanguage).apply {
+            val requiredTypeConverters = getRequiredTypeConverters()
+            if (requiredTypeConverters.isEmpty()) {
+                when (language) {
+                    CodeLanguage.JAVA ->
+                        addStatement("return %T.emptyList()", Collections::class.asClassName())
+                    CodeLanguage.KOTLIN ->
+                        addStatement("return emptyList()")
+                }
+            } else {
+                val placeholders = requiredTypeConverters.joinToString(",") { "%L" }
+                val requiredTypeConvertersLiterals = requiredTypeConverters.map {
+                    XCodeBlock.ofJavaClassLiteral(language, it)
+                }.toTypedArray()
+                when (language) {
+                    CodeLanguage.JAVA ->
+                        addStatement("return %T.asList($placeholders)",
+                            Arrays::class.asClassName(),
+                            *requiredTypeConvertersLiterals
+                        )
+                    CodeLanguage.KOTLIN ->
+                        addStatement(
+                            "return listOf($placeholders)",
+                            *requiredTypeConvertersLiterals
+                        )
+                }
+            }
+        }.build()
+        return XFunSpec.builder(
+            codeLanguage,
+            GET_LIST_OF_TYPE_CONVERTERS_METHOD,
+            VisibilityModifier.PUBLIC
+        ).apply(
+            javaMethodBuilder = {
+                addModifiers(javax.lang.model.element.Modifier.STATIC)
+            },
+            kotlinFunctionBuilder = {
+                addAnnotation(kotlin.jvm.JvmStatic::class)
+            },
+        ).apply {
             returns(
-                ParameterizedTypeName.get(
-                    CommonTypeNames.LIST,
-                    ParameterizedTypeName.get(
-                        ClassName.get(Class::class.java),
-                        WildcardTypeName.subtypeOf(Object::class.java)
+                List::class.asClassName().parametrizedBy(
+                    Class::class.asClassName().parametrizedBy(
+                        // TODO(b/249984508): Create XTypeName factory for type variable names
+                        XTypeName(
+                            java = JWildcardTypeName.subtypeOf(Object::class.java),
+                            kotlin = com.squareup.kotlinpoet.STAR
+                        )
                     )
                 )
             )
-            val requiredTypeConverters = getRequiredTypeConverters()
-            if (requiredTypeConverters.isEmpty()) {
-                addStatement("return $T.emptyList()", ClassName.get(Collections::class.java))
-            } else {
-                val placeholders = requiredTypeConverters.joinToString(",") {
-                    "$T.class"
-                }
-                val args = arrayOf(ClassName.get(Arrays::class.java)) + requiredTypeConverters
-                addStatement("return $T.asList($placeholders)", *args)
-            }
+            addCode(body)
         }.build()
     }
 
@@ -213,17 +251,14 @@
         preparedQueries: List<WriteQueryMethod>
     ): List<PreparedStmtQuery> {
         return preparedQueries.map { method ->
-            val fieldSpec = getOrCreateField(PreparedStatementField(method))
+            val fieldSpec = getOrCreateProperty(PreparedStatementProperty(method))
             val queryWriter = QueryWriter(method)
             val fieldImpl = PreparedStatementWriter(queryWriter)
-                .createAnonymous(this@DaoWriter, dbField)
+                .createAnonymous(this@DaoWriter, dbProperty.toJavaPoet())
             val methodBody =
                 createPreparedQueryMethodBody(method, fieldSpec, queryWriter)
             PreparedStmtQuery(
-                mapOf(
-                    PreparedStmtQuery.NO_PARAM_FIELD
-                        to (fieldSpec to fieldImpl)
-                ),
+                mapOf(PreparedStmtQuery.NO_PARAM_FIELD to (fieldSpec to fieldImpl)),
                 methodBody
             )
         }
@@ -231,28 +266,28 @@
 
     private fun createPreparedQueryMethodBody(
         method: WriteQueryMethod,
-        preparedStmtField: FieldSpec,
+        preparedStmtField: XPropertySpec,
         queryWriter: QueryWriter
-    ): MethodSpec {
+    ): XFunSpec {
         val scope = CodeGenScope(this)
         method.preparedQueryResultBinder.executeAndReturn(
             prepareQueryStmtBlock = {
                 val stmtName = getTmpVar("_stmt")
                 builder().apply {
                     addStatement(
-                        "final $T $L = $N.acquire()",
-                        SupportDbTypeNames.SQLITE_STMT, stmtName, preparedStmtField
+                        "final $T $L = $L.acquire()",
+                        SupportDbTypeNames.SQLITE_STMT, stmtName, preparedStmtField.name
                     )
                 }
                 queryWriter.bindArgs(stmtName, emptyList(), this)
                 stmtName
             },
             preparedStmtField = preparedStmtField.name,
-            dbField = dbField,
+            dbField = dbProperty.toJavaPoet(),
             scope = scope
         )
         return overrideWithoutAnnotations(method.element, declaredDao)
-            .addCode(scope.builder().build())
+            .addCode(scope.generate())
             .build()
     }
 
@@ -262,33 +297,27 @@
         }
     }
 
-    private fun createTransactionMethodBody(method: TransactionMethod): MethodSpec {
+    private fun createTransactionMethodBody(method: TransactionMethod): XFunSpec {
         val scope = CodeGenScope(this)
         method.methodBinder.executeAndReturn(
             returnType = method.returnType,
             parameterNames = method.parameterNames,
             daoName = dao.typeName.toJavaPoet(),
             daoImplName = dao.implTypeName.toJavaPoet(),
-            dbField = dbField,
+            dbField = dbProperty.toJavaPoet(),
             scope = scope
         )
         return overrideWithoutAnnotations(method.element, declaredDao)
-            .addCode(scope.builder().build())
+            .addCode(scope.generate())
             .build()
     }
 
     private fun createConstructor(
-        dbParam: ParameterSpec,
         shortcutMethods: List<PreparedStmtQuery>,
         callSuper: Boolean
-    ): MethodSpec {
-        return MethodSpec.constructorBuilder().apply {
-            addParameter(dbParam)
-            addModifiers(PUBLIC)
-            if (callSuper) {
-                addStatement("super($N)", dbParam)
-            }
-            addStatement("this.$N = $N", dbField, dbParam)
+    ): XFunSpec {
+        val body = XCodeBlock.builder(codeLanguage).apply {
+            addStatement("this.%N = %L", dbProperty, dbProperty.name)
             shortcutMethods.asSequence().filterNot {
                 it.fields.isEmpty()
             }.map {
@@ -297,34 +326,43 @@
                 it.first.name
             }.map {
                 it.value.first()
-            }.forEach {
-                addStatement("this.$N = $L", it.first, it.second)
+            }.forEach { (propertySpec, initExpression) ->
+                addStatement("this.%L = %L", propertySpec.name, initExpression)
             }
         }.build()
-    }
-
-    private fun createSelectMethod(method: ReadQueryMethod): MethodSpec {
-        return overrideWithoutAnnotations(method.element, declaredDao).apply {
-            addCode(createQueryMethodBody(method))
+        return XFunSpec.constructorBuilder(codeLanguage, VisibilityModifier.PUBLIC).apply {
+            addParameter(
+                typeName = dao.constructorParamType ?: ROOM_DB,
+                name = dbProperty.name
+            )
+            if (callSuper) {
+                callSuperConstructor(XCodeBlock.of(language, "%L", dbProperty.name))
+            }
+            addCode(body)
         }.build()
     }
 
-    private fun createRawQueryMethod(method: RawQueryMethod): MethodSpec {
-        return overrideWithoutAnnotations(method.element, declaredDao).apply {
+    private fun createSelectMethod(method: ReadQueryMethod): XFunSpec {
+        return overrideWithoutAnnotations(method.element, declaredDao)
+            .addCode(createQueryMethodBody(method))
+            .build()
+    }
+
+    private fun createRawQueryMethod(method: RawQueryMethod): XFunSpec {
+        val body = XCodeBlock.builder(codeLanguage).apply {
             val scope = CodeGenScope(this@DaoWriter)
             val roomSQLiteQueryVar: String
             val queryParam = method.runtimeQueryParam
             val shouldReleaseQuery: Boolean
-
             when {
                 queryParam?.isString() == true -> {
                     roomSQLiteQueryVar = scope.getTmpVar("_statement")
                     shouldReleaseQuery = true
                     addStatement(
                         "$T $L = $T.acquire($L, 0)",
-                        RoomTypeNames.ROOM_SQL_QUERY,
+                        ROOM_SQL_QUERY.toJavaPoet(),
                         roomSQLiteQueryVar,
-                        RoomTypeNames.ROOM_SQL_QUERY,
+                        ROOM_SQL_QUERY.toJavaPoet(),
                         queryParam.paramName
                     )
                 }
@@ -346,9 +384,9 @@
                     shouldReleaseQuery = false
                     addStatement(
                         "$T $L = $T.acquire($L, 0)",
-                        RoomTypeNames.ROOM_SQL_QUERY,
+                        ROOM_SQL_QUERY.toJavaPoet(),
                         roomSQLiteQueryVar,
-                        RoomTypeNames.ROOM_SQL_QUERY,
+                        ROOM_SQL_QUERY.toJavaPoet(),
                         "missing query parameter"
                     )
                 }
@@ -359,19 +397,22 @@
                 method.queryResultBinder.convertAndReturn(
                     roomSQLiteQueryVar = roomSQLiteQueryVar,
                     canReleaseQuery = shouldReleaseQuery,
-                    dbField = dbField,
+                    dbProperty = dbProperty,
                     inTransaction = method.inTransaction,
                     scope = scope
                 )
             }
-            addCode(scope.builder().build())
+            add(scope.generate())
         }.build()
+        return overrideWithoutAnnotations(method.element, declaredDao)
+            .addCode(body)
+            .build()
     }
 
-    private fun createPreparedQueryMethod(method: WriteQueryMethod): MethodSpec {
-        return overrideWithoutAnnotations(method.element, declaredDao).apply {
-            addCode(createPreparedQueryMethodBody(method))
-        }.build()
+    private fun createPreparedQueryMethod(method: WriteQueryMethod): XFunSpec {
+        return overrideWithoutAnnotations(method.element, declaredDao)
+            .addCode(createPreparedQueryMethodBody(method))
+            .build()
     }
 
     /**
@@ -385,9 +426,9 @@
                 val entities = insertionMethod.entities
 
                 val fields = entities.mapValues {
-                    val spec = getOrCreateField(InsertionMethodField(it.value, onConflict))
+                    val spec = getOrCreateProperty(InsertionMethodProperty(it.value, onConflict))
                     val impl = EntityInsertionAdapterWriter.create(it.value, onConflict)
-                        .createAnonymous(this@DaoWriter, dbField.name)
+                        .createAnonymous(this@DaoWriter, dbProperty.name)
                     spec to impl
                 }
                 val methodImpl = overrideWithoutAnnotations(
@@ -402,20 +443,19 @@
 
     private fun createInsertionMethodBody(
         method: InsertionMethod,
-        insertionAdapters: Map<String, Pair<FieldSpec, TypeSpec>>
-    ): CodeBlock {
+        insertionAdapters: Map<String, Pair<XPropertySpec, TypeSpec>>
+    ): XCodeBlock {
         if (insertionAdapters.isEmpty() || method.methodBinder == null) {
-            return CodeBlock.builder().build()
+            return XCodeBlock.builder(codeLanguage).build()
         }
         val scope = CodeGenScope(this)
-
         method.methodBinder.convertAndReturn(
             parameters = method.parameters,
             adapters = insertionAdapters,
-            dbField = dbField,
+            dbField = dbProperty.toJavaPoet(),
             scope = scope
         )
-        return scope.builder().build()
+        return scope.generate()
     }
 
     /**
@@ -424,7 +464,7 @@
     private fun createDeletionMethods(): List<PreparedStmtQuery> {
         return createShortcutMethods(dao.deletionMethods, "deletion") { _, entity ->
             EntityDeletionAdapterWriter.create(entity)
-                .createAnonymous(this@DaoWriter, dbField.name)
+                .createAnonymous(this@DaoWriter, dbProperty.name)
         }
     }
 
@@ -435,7 +475,7 @@
         return createShortcutMethods(dao.updateMethods, "update") { update, entity ->
             val onConflict = OnConflictProcessor.onConflictText(update.onConflictStrategy)
             EntityUpdateAdapterWriter.create(entity, onConflict)
-                .createAnonymous(this@DaoWriter, dbField.name)
+                .createAnonymous(this@DaoWriter, dbProperty.name)
         }
     }
 
@@ -455,8 +495,8 @@
                     ""
                 }
                 val fields = entities.mapValues {
-                    val spec = getOrCreateField(
-                        DeleteOrUpdateAdapterField(it.value, methodPrefix, onConflict)
+                    val spec = getOrCreateProperty(
+                        DeleteOrUpdateAdapterProperty(it.value, methodPrefix, onConflict)
                     )
                     val impl = implCallback(method, it.value)
                     spec to impl
@@ -471,20 +511,20 @@
 
     private fun createDeleteOrUpdateMethodBody(
         method: DeleteOrUpdateShortcutMethod,
-        adapters: Map<String, Pair<FieldSpec, TypeSpec>>
-    ): CodeBlock {
+        adapters: Map<String, Pair<XPropertySpec, TypeSpec>>
+    ): XCodeBlock {
         if (adapters.isEmpty() || method.methodBinder == null) {
-            return CodeBlock.builder().build()
+            return XCodeBlock.builder(codeLanguage).build()
         }
         val scope = CodeGenScope(this)
 
         method.methodBinder.convertAndReturn(
             parameters = method.parameters,
             adapters = adapters,
-            dbField = dbField,
+            dbField = dbProperty.toJavaPoet(),
             scope = scope
         )
-        return scope.builder().build()
+        return scope.generate()
     }
 
     /**
@@ -496,9 +536,9 @@
             .map { upsertionMethod ->
                 val entities = upsertionMethod.entities
                 val fields = entities.mapValues {
-                    val spec = getOrCreateField(UpsertionAdapterField(it.value))
+                    val spec = getOrCreateProperty(UpsertionAdapterProperty(it.value))
                     val impl = EntityUpsertionAdapterWriter.create(it.value)
-                        .createConcrete(it.value, this@DaoWriter, dbField.name)
+                        .createConcrete(it.value, this@DaoWriter, dbProperty.name)
                     spec to impl
                 }
                 val methodImpl = overrideWithoutAnnotations(
@@ -513,23 +553,23 @@
 
     private fun createUpsertionMethodBody(
         method: UpsertionMethod,
-        upsertionAdapters: Map<String, Pair<FieldSpec, CodeBlock>>
-    ): CodeBlock {
+        upsertionAdapters: Map<String, Pair<XPropertySpec, CodeBlock>>
+    ): XCodeBlock {
         if (upsertionAdapters.isEmpty() || method.methodBinder == null) {
-            return CodeBlock.builder().build()
+            return XCodeBlock.builder(codeLanguage).build()
         }
         val scope = CodeGenScope(this)
 
         method.methodBinder.convertAndReturn(
             parameters = method.parameters,
             adapters = upsertionAdapters,
-            dbField = dbField,
+            dbField = dbProperty.toJavaPoet(),
             scope = scope
         )
-        return scope.builder().build()
+        return scope.generate()
     }
 
-    private fun createPreparedQueryMethodBody(method: WriteQueryMethod): CodeBlock {
+    private fun createPreparedQueryMethodBody(method: WriteQueryMethod): XCodeBlock {
         val scope = CodeGenScope(this)
         method.preparedQueryResultBinder.executeAndReturn(
             prepareQueryStmtBlock = {
@@ -540,20 +580,20 @@
                 builder().apply {
                     addStatement(
                         "final $T $L = $N.compileStatement($L)",
-                        SupportDbTypeNames.SQLITE_STMT, stmtVar, dbField, sqlVar
+                        SupportDbTypeNames.SQLITE_STMT, stmtVar, dbProperty.toJavaPoet(), sqlVar
                     )
                 }
                 queryWriter.bindArgs(stmtVar, listSizeArgs, this)
                 stmtVar
             },
             preparedStmtField = null,
-            dbField = dbField,
+            dbField = dbProperty.toJavaPoet(),
             scope = scope
         )
-        return scope.builder().build()
+        return scope.builder.build()
     }
 
-    private fun createQueryMethodBody(method: ReadQueryMethod): CodeBlock {
+    private fun createQueryMethodBody(method: ReadQueryMethod): XCodeBlock {
         val queryWriter = QueryWriter(method)
         val scope = CodeGenScope(this)
         val sqlVar = scope.getTmpVar("_sql")
@@ -562,16 +602,18 @@
         method.queryResultBinder.convertAndReturn(
             roomSQLiteQueryVar = roomSQLiteQueryVar,
             canReleaseQuery = true,
-            dbField = dbField,
+            dbProperty = dbProperty,
             inTransaction = method.inTransaction,
             scope = scope
         )
-        return scope.builder().build()
+        return scope.generate()
     }
 
-    private fun createDefaultMethodDelegate(method: KotlinDefaultMethodDelegate): MethodSpec {
+    // TODO(b/251459654): Handle @JvmOverloads in delegating functions with Kotlin codegen.
+    private fun createDefaultMethodDelegate(method: KotlinDefaultMethodDelegate): XFunSpec {
         val scope = CodeGenScope(this)
         return overrideWithoutAnnotations(method.element, declaredDao).apply {
+            // TODO(danysantiago): Revisit this in Kotlin codegen
             KotlinDefaultMethodDelegateBinder.executeAndReturn(
                 daoName = dao.typeName.toJavaPoet(),
                 daoImplName = dao.implTypeName.toJavaPoet(),
@@ -580,47 +622,61 @@
                 parameterNames = method.element.parameters.map { it.name },
                 scope = scope
             )
-            addCode(scope.builder().build())
+            addCode(scope.generate())
         }.build()
     }
 
-    private fun createDelegatingMethod(method: KotlinBoxedPrimitiveMethodDelegate): MethodSpec {
-        return overrideWithoutAnnotations(method.element, declaredDao).apply {
-
-            val args = method.concreteMethod.parameters.map {
-                val paramTypename = it.type.typeName
-                if (paramTypename.isBoxedPrimitive()) {
-                    CodeBlock.of("$L", paramTypename, it.name.toString())
-                } else {
-                    CodeBlock.of("($T) $L", paramTypename.unbox(), it.name.toString())
+    // TODO(b/127483380): Reconsider the need of delegating method in KotlinPoet.
+    private fun createDelegatingMethod(method: KotlinBoxedPrimitiveMethodDelegate): XFunSpec {
+        val body = XCodeBlock.builder(codeLanguage).apply(
+            javaCodeBuilder = {
+                val args = method.concreteMethod.parameters.map {
+                    val paramTypename = it.type.typeName
+                    if (paramTypename.isBoxedPrimitive()) {
+                        CodeBlock.of("$L", paramTypename, it.name.toString())
+                    } else {
+                        CodeBlock.of("($T) $L", paramTypename.unbox(), it.name.toString())
+                    }
                 }
-            }
-            if (method.element.returnType.isVoid()) {
-                addStatement("$L($L)", method.element.jvmName, CodeBlock.join(args, ",$W"))
-            } else {
-                addStatement("return $L($L)", method.element.jvmName, CodeBlock.join(args, ",$W"))
-            }
-        }.build()
+                if (method.element.returnType.isVoid()) {
+                    addStatement(
+                        "$L($L)",
+                        method.element.jvmName,
+                        CodeBlock.join(args, ",$W")
+                    )
+                } else {
+                    addStatement(
+                        "return $L($L)",
+                        method.element.jvmName,
+                        CodeBlock.join(args, ",$W")
+                    )
+                }
+            },
+            kotlinCodeBuilder = { TODO("Kotlin codegen not yet implemented!") }
+        ).build()
+        return overrideWithoutAnnotations(method.element, declaredDao)
+            .addCode(body)
+            .build()
     }
 
     private fun overrideWithoutAnnotations(
         elm: XMethodElement,
         owner: XType
-    ): MethodSpec.Builder {
-        return MethodSpecHelper.overridingWithFinalParams(elm, owner)
+    ): XFunSpec.Builder {
+        return XFunSpec.overridingBuilder(codeLanguage, elm, owner)
     }
 
     /**
      * Represents a query statement prepared in Dao implementation.
      *
-     * @param fields This map holds all the member fields necessary for this query. The key is the
-     * corresponding parameter name in the defining query method. The value is a pair from the field
-     * declaration to definition.
-     * @param methodImpl The body of the query method implementation.
+     * @param fields This map holds all the member properties necessary for this query. The key is
+     * the corresponding parameter name in the defining query method. The value is a pair from the
+     * property declaration to definition.
+     * @param functionImpl The body of the query method implementation.
      */
     data class PreparedStmtQuery(
-        val fields: Map<String, Pair<FieldSpec, Any>>,
-        val methodImpl: MethodSpec
+        val fields: Map<String, Pair<XPropertySpec, Any>>,
+        val functionImpl: XFunSpec
     ) {
         companion object {
             // The key to be used in `fields` where the method requires a field that is not
@@ -629,68 +685,57 @@
         }
     }
 
-    private class InsertionMethodField(
+    private class InsertionMethodProperty(
         val shortcutEntity: ShortcutEntity,
         val onConflictText: String
-    ) : SharedFieldSpec(
+    ) : SharedPropertySpec(
         baseName = "insertionAdapterOf${shortcutEntityFieldNamePart(shortcutEntity)}",
-        type = ParameterizedTypeName.get(
-            RoomTypeNames.INSERTION_ADAPTER, shortcutEntity.pojo.typeName.toJavaPoet()
-        )
+        type = INSERTION_ADAPTER.parametrizedBy(shortcutEntity.pojo.typeName)
     ) {
         override fun getUniqueKey(): String {
-            return "${shortcutEntity.pojo.typeName.toJavaPoet()}-" +
-                "${shortcutEntity.entityTypeName}$onConflictText"
+            return "${shortcutEntity.pojo.typeName}-${shortcutEntity.entityTypeName}$onConflictText"
         }
 
-        override fun prepare(writer: TypeWriter, builder: FieldSpec.Builder) {
-            builder.addModifiers(FINAL, PRIVATE)
+        override fun prepare(writer: TypeWriter, builder: XPropertySpec.Builder) {
         }
     }
 
-    class DeleteOrUpdateAdapterField(
+    class DeleteOrUpdateAdapterProperty(
         val shortcutEntity: ShortcutEntity,
         val methodPrefix: String,
         val onConflictText: String
-    ) : SharedFieldSpec(
+    ) : SharedPropertySpec(
         baseName = "${methodPrefix}AdapterOf${shortcutEntityFieldNamePart(shortcutEntity)}",
-        type = ParameterizedTypeName.get(
-            RoomTypeNames.DELETE_OR_UPDATE_ADAPTER, shortcutEntity.pojo.typeName.toJavaPoet()
-        )
+        type = DELETE_OR_UPDATE_ADAPTER.parametrizedBy(shortcutEntity.pojo.typeName)
     ) {
-        override fun prepare(writer: TypeWriter, builder: FieldSpec.Builder) {
-            builder.addModifiers(PRIVATE, FINAL)
+        override fun prepare(writer: TypeWriter, builder: XPropertySpec.Builder) {
         }
 
         override fun getUniqueKey(): String {
-            return "${shortcutEntity.pojo.typeName.toJavaPoet()}-${shortcutEntity.entityTypeName}" +
+            return "${shortcutEntity.pojo.typeName}-${shortcutEntity.entityTypeName}" +
                 "$methodPrefix$onConflictText"
         }
     }
 
-    class UpsertionAdapterField(
+    class UpsertionAdapterProperty(
         val shortcutEntity: ShortcutEntity
-    ) : SharedFieldSpec(
+    ) : SharedPropertySpec(
         baseName = "upsertionAdapterOf${shortcutEntityFieldNamePart(shortcutEntity)}",
-        type = ParameterizedTypeName.get(
-            RoomTypeNames.UPSERTION_ADAPTER, shortcutEntity.pojo.typeName.toJavaPoet()
-        )
+        type = UPSERTION_ADAPTER.parametrizedBy(shortcutEntity.pojo.typeName)
     ) {
         override fun getUniqueKey(): String {
-            return "${shortcutEntity.pojo.typeName.toJavaPoet()}-${shortcutEntity.entityTypeName}"
+            return "${shortcutEntity.pojo.typeName}-${shortcutEntity.entityTypeName}"
         }
 
-        override fun prepare(writer: TypeWriter, builder: FieldSpec.Builder) {
-            builder.addModifiers(PRIVATE, FINAL)
+        override fun prepare(writer: TypeWriter, builder: XPropertySpec.Builder) {
         }
     }
 
-    class PreparedStatementField(val method: QueryMethod) : SharedFieldSpec(
-        "preparedStmtOf${method.element.jvmName.capitalize(Locale.US)}",
-        RoomTypeNames.SHARED_SQLITE_STMT
+    class PreparedStatementProperty(val method: QueryMethod) : SharedPropertySpec(
+        baseName = "preparedStmtOf${method.element.jvmName.capitalize(Locale.US)}",
+        type = SHARED_SQLITE_STMT
     ) {
-        override fun prepare(writer: TypeWriter, builder: FieldSpec.Builder) {
-            builder.addModifiers(PRIVATE, FINAL)
+        override fun prepare(writer: TypeWriter, builder: XPropertySpec.Builder) {
         }
 
         override fun getUniqueKey(): String {
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityCursorConverterWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityCursorConverterWriter.kt
index 891711a..941e011 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityCursorConverterWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityCursorConverterWriter.kt
@@ -16,59 +16,57 @@
 
 package androidx.room.writer
 
+import androidx.room.compiler.codegen.XCodeBlock
+import androidx.room.compiler.codegen.XFunSpec
+import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.ext.AndroidTypeNames.CURSOR
-import androidx.room.ext.L
-import androidx.room.ext.N
-import androidx.room.ext.RoomTypeNames
-import androidx.room.ext.S
-import androidx.room.ext.T
+import androidx.room.ext.RoomTypeNames.CURSOR_UTIL
 import androidx.room.ext.capitalize
 import androidx.room.ext.stripNonJava
 import androidx.room.solver.CodeGenScope
 import androidx.room.vo.Entity
 import androidx.room.vo.FieldWithIndex
-import com.squareup.javapoet.CodeBlock
-import com.squareup.javapoet.MethodSpec
-import com.squareup.javapoet.ParameterSpec
-import com.squareup.javapoet.TypeName
 import java.util.Locale
-import javax.lang.model.element.Modifier.PRIVATE
 
-class EntityCursorConverterWriter(val entity: Entity) : TypeWriter.SharedMethodSpec(
+class EntityCursorConverterWriter(val entity: Entity) : TypeWriter.SharedFunctionSpec(
     "entityCursorConverter_${entity.typeName.toJavaPoet().toString().stripNonJava()}"
 ) {
     override fun getUniqueKey(): String {
         return "generic_entity_converter_of_${entity.element.qualifiedName}"
     }
 
-    override fun prepare(methodName: String, writer: TypeWriter, builder: MethodSpec.Builder) {
+    override fun prepare(methodName: String, writer: TypeWriter, builder: XFunSpec.Builder) {
         builder.apply {
-            val cursorParam = ParameterSpec
-                .builder(CURSOR.toJavaPoet(), "cursor").build()
-            addParameter(cursorParam)
-            addModifiers(PRIVATE)
-            returns(entity.typeName.toJavaPoet())
-            addCode(buildConvertMethodBody(writer, cursorParam))
+            val cursorParamName = "cursor"
+            addParameter(CURSOR, cursorParamName)
+            returns(entity.typeName)
+            addCode(buildConvertMethodBody(writer, cursorParamName))
         }
     }
 
-    private fun buildConvertMethodBody(writer: TypeWriter, cursorParam: ParameterSpec): CodeBlock {
+    private fun buildConvertMethodBody(writer: TypeWriter, cursorParamName: String): XCodeBlock {
         val scope = CodeGenScope(writer)
         val entityVar = scope.getTmpVar("_entity")
-        scope.builder().apply {
-            scope.builder().addStatement(
-                "final $T $L",
-                entity.typeName.toJavaPoet(),
-                entityVar
+        scope.builder.apply {
+            addLocalVariable(
+                entityVar,
+                entity.typeName
             )
             val fieldsWithIndices = entity.fields.map {
                 val indexVar = scope.getTmpVar(
                     "_cursorIndexOf${it.name.stripNonJava().capitalize(Locale.US)}"
                 )
-                scope.builder().addStatement(
-                    "final $T $L = $T.getColumnIndex($N, $S)",
-                    TypeName.INT, indexVar, RoomTypeNames.CURSOR_UTIL, cursorParam, it.columnName
+                addLocalVariable(
+                    name = indexVar,
+                    typeName = XTypeName.PRIMITIVE_INT,
+                    assignExpr = XCodeBlock.of(
+                        language,
+                        "%T.getColumnIndex(%N, %S)",
+                        CURSOR_UTIL,
+                        cursorParamName,
+                        it.columnName
+                    )
                 )
                 FieldWithIndex(
                     field = it,
@@ -79,13 +77,13 @@
             FieldReadWriteWriter.readFromCursor(
                 outVar = entityVar,
                 outPojo = entity,
-                cursorVar = cursorParam.name,
+                cursorVar = cursorParamName,
                 fieldsWithIndices = fieldsWithIndices,
                 relationCollectors = emptyList(), // no relationship for entities
                 scope = scope
             )
-            addStatement("return $L", entityVar)
+            addStatement("return %L", entityVar)
         }
-        return scope.builder().build()
+        return scope.generate()
     }
 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityDeletionAdapterWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityDeletionAdapterWriter.kt
index 5766ce6..036e830 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityDeletionAdapterWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityDeletionAdapterWriter.kt
@@ -18,7 +18,7 @@
 
 import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.ext.L
-import androidx.room.ext.RoomTypeNames
+import androidx.room.ext.RoomTypeNames.DELETE_OR_UPDATE_ADAPTER
 import androidx.room.ext.S
 import androidx.room.ext.SupportDbTypeNames
 import androidx.room.solver.CodeGenScope
@@ -60,7 +60,7 @@
         return TypeSpec.anonymousClassBuilder("$L", dbParam).apply {
             superclass(
                 ParameterizedTypeName.get(
-                    RoomTypeNames.DELETE_OR_UPDATE_ADAPTER,
+                    DELETE_OR_UPDATE_ADAPTER.toJavaPoet(),
                     pojoTypeName
                 )
             )
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityInsertionAdapterWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityInsertionAdapterWriter.kt
index fc6e477..98730bd 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityInsertionAdapterWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityInsertionAdapterWriter.kt
@@ -19,7 +19,7 @@
 import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.XNullability
 import androidx.room.ext.L
-import androidx.room.ext.RoomTypeNames
+import androidx.room.ext.RoomTypeNames.INSERTION_ADAPTER
 import androidx.room.ext.S
 import androidx.room.ext.SupportDbTypeNames
 import androidx.room.solver.CodeGenScope
@@ -72,7 +72,7 @@
         return TypeSpec.anonymousClassBuilder("$L", dbParam).apply {
             superclass(
                 ParameterizedTypeName.get(
-                    RoomTypeNames.INSERTION_ADAPTER,
+                    INSERTION_ADAPTER.toJavaPoet(),
                     pojo.typeName.toJavaPoet()
                 )
             )
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityUpdateAdapterWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityUpdateAdapterWriter.kt
index e085422..8fb93f3 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityUpdateAdapterWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityUpdateAdapterWriter.kt
@@ -18,7 +18,7 @@
 
 import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.ext.L
-import androidx.room.ext.RoomTypeNames
+import androidx.room.ext.RoomTypeNames.DELETE_OR_UPDATE_ADAPTER
 import androidx.room.ext.S
 import androidx.room.ext.SupportDbTypeNames
 import androidx.room.solver.CodeGenScope
@@ -56,7 +56,7 @@
         return TypeSpec.anonymousClassBuilder("$L", dbParam).apply {
             superclass(
                 ParameterizedTypeName.get(
-                    RoomTypeNames.DELETE_OR_UPDATE_ADAPTER,
+                    DELETE_OR_UPDATE_ADAPTER.toJavaPoet(),
                     pojo.typeName.toJavaPoet()
                 )
             )
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityUpsertionAdapterWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityUpsertionAdapterWriter.kt
index ab9a46a..69972e0 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityUpsertionAdapterWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityUpsertionAdapterWriter.kt
@@ -18,7 +18,7 @@
 
 import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.ext.L
-import androidx.room.ext.RoomTypeNames
+import androidx.room.ext.RoomTypeNames.UPSERTION_ADAPTER
 import androidx.room.ext.T
 import androidx.room.vo.Pojo
 import androidx.room.vo.ShortcutEntity
@@ -45,7 +45,7 @@
         dbParam: String
     ): CodeBlock {
         val upsertionAdapter = ParameterizedTypeName.get(
-            RoomTypeNames.UPSERTION_ADAPTER, pojo.typeName.toJavaPoet()
+            UPSERTION_ADAPTER.toJavaPoet(), pojo.typeName.toJavaPoet()
         )
         val insertionHelper = EntityInsertionAdapterWriter.create(entity, "")
             .createAnonymous(typeWriter, dbParam)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/FieldReadWriteWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/FieldReadWriteWriter.kt
index bf053bb..c228992 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/FieldReadWriteWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/FieldReadWriteWriter.kt
@@ -280,7 +280,7 @@
                     // always declare, we'll set below
                     scope.builder.addLocalVariable(
                         node.varName,
-                        fieldParent.pojo.typeName
+                        fieldParent.field.typeName
                     )
                     if (fieldParent.nonNull) {
                         readNode()
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/PreparedStatementWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/PreparedStatementWriter.kt
index a37740f..919c3cc 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/PreparedStatementWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/PreparedStatementWriter.kt
@@ -15,9 +15,10 @@
  */
 package androidx.room.writer
 
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.ext.L
 import androidx.room.ext.N
-import androidx.room.ext.RoomTypeNames
+import androidx.room.ext.RoomTypeNames.SHARED_SQLITE_STMT
 import androidx.room.solver.CodeGenScope
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.FieldSpec
@@ -33,7 +34,7 @@
         val scope = CodeGenScope(typeWriter)
         @Suppress("RemoveSingleExpressionStringTemplate")
         return TypeSpec.anonymousClassBuilder("$N", dbParam).apply {
-            superclass(RoomTypeNames.SHARED_SQLITE_STMT)
+            superclass(SHARED_SQLITE_STMT.toJavaPoet())
             addMethod(
                 MethodSpec.methodBuilder("createQuery").apply {
                     addAnnotation(Override::class.java)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/QueryWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/QueryWriter.kt
index ae38d5f..b248e4d 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/QueryWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/QueryWriter.kt
@@ -16,24 +16,23 @@
 
 package androidx.room.writer
 
-import androidx.room.ext.L
-import androidx.room.ext.RoomTypeNames.ROOM_SQL_QUERY
-import androidx.room.ext.RoomTypeNames.STRING_UTIL
-import androidx.room.ext.S
-import androidx.room.ext.T
-import androidx.room.ext.typeName
+import androidx.room.compiler.codegen.XCodeBlock
+import androidx.room.compiler.codegen.XCodeBlock.Builder.Companion.addLocalVal
+import androidx.room.compiler.codegen.XMemberName.Companion.companionMember
+import androidx.room.compiler.codegen.XMemberName.Companion.packageMember
+import androidx.room.compiler.codegen.XTypeName
+import androidx.room.compiler.codegen.asClassName
+import androidx.room.ext.RoomTypeNames
 import androidx.room.parser.ParsedQuery
 import androidx.room.parser.Section
 import androidx.room.solver.CodeGenScope
 import androidx.room.vo.QueryMethod
 import androidx.room.vo.QueryParameter
-import com.squareup.javapoet.ClassName
-import com.squareup.javapoet.TypeName
 
 /**
  * Writes the SQL query and arguments for a QueryMethod.
  */
-class QueryWriter constructor(
+class QueryWriter(
     val parameters: List<QueryParameter>,
     val sectionToParamMapping: List<Pair<Section, QueryParameter?>>,
     val query: ParsedQuery
@@ -69,71 +68,96 @@
         val varargParams = parameters
             .filter { it.queryParamAdapter?.isMultiple ?: false }
         val sectionToParamMapping = sectionToParamMapping
-        val knownQueryArgsCount = sectionToParamMapping.filterNot {
-            it.second?.queryParamAdapter?.isMultiple ?: false
-        }.size
-        scope.builder().apply {
+        val knownQueryArgsCount = sectionToParamMapping
+            .filterNot { it.second?.queryParamAdapter?.isMultiple ?: false }
+            .size
+        scope.builder.apply {
             if (varargParams.isNotEmpty()) {
                 val stringBuilderVar = scope.getTmpVar("_stringBuilder")
-                addStatement(
-                    "$T $L = $T.newStringBuilder()",
-                    ClassName.get(StringBuilder::class.java), stringBuilderVar, STRING_UTIL
+                // TODO(b/127483380): Consider using `buildString { }` for Kotlin codegen
+                addLocalVal(
+                    stringBuilderVar,
+                    StringBuilder::class.asClassName(),
+                    "%T.newStringBuilder()",
+                    RoomTypeNames.STRING_UTIL
                 )
-                query.sections.forEach {
-                    @Suppress("UNUSED_VARIABLE")
-                    val exhaustive = when (it) {
-                        is Section.Text -> addStatement("$L.append($S)", stringBuilderVar, it.text)
-                        is Section.NewLine -> addStatement("$L.append($S)", stringBuilderVar, "\n")
+                query.sections.forEach { section ->
+                    when (section) {
+                        is Section.Text ->
+                            addStatement("%L.append(%S)", stringBuilderVar, section.text)
+                        is Section.NewLine ->
+                            addStatement("%L.append(%S)", stringBuilderVar, "\n")
                         is Section.BindVar -> {
                             // If it is null, will be reported as error before. We just try out
                             // best to generate as much code as possible.
-                            sectionToParamMapping.firstOrNull { mapping ->
-                                mapping.first == it
-                            }?.let { pair ->
-                                if (pair.second?.queryParamAdapter?.isMultiple ?: false) {
+                            sectionToParamMapping.firstOrNull {
+                                section == it.first
+                            }?.let { (_, param) ->
+                                if (param?.queryParamAdapter?.isMultiple == true) {
                                     val tmpCount = scope.getTmpVar("_inputSize")
-                                    listSizeVars.add(Pair(pair.second!!, tmpCount))
-                                    pair.second
-                                        ?.queryParamAdapter
-                                        ?.getArgCount(pair.second!!.name, tmpCount, scope)
+                                    listSizeVars.add(param to tmpCount)
+                                    param.queryParamAdapter.getArgCount(param.name, tmpCount, scope)
                                     addStatement(
-                                        "$T.appendPlaceholders($L, $L)",
-                                        STRING_UTIL, stringBuilderVar, tmpCount
+                                        "%M(%L, %L)",
+                                        RoomTypeNames.STRING_UTIL
+                                            .packageMember("appendPlaceholders"),
+                                        stringBuilderVar,
+                                        tmpCount
                                     )
                                 } else {
-                                    addStatement("$L.append($S)", stringBuilderVar, "?")
+                                    addStatement("%L.append(%S)", stringBuilderVar, "?")
                                 }
                             }
                         }
                     }
                 }
-
-                addStatement(
-                    "final $T $L = $L.toString()", String::class.typeName,
-                    outSqlQueryName, stringBuilderVar
+                addLocalVal(
+                    outSqlQueryName,
+                    String::class.asClassName(),
+                    "%L.toString()",
+                    stringBuilderVar
                 )
                 if (outArgsName != null) {
                     val argCount = scope.getTmpVar("_argCount")
-                    addStatement(
-                        "final $T $L = $L$L", TypeName.INT, argCount, knownQueryArgsCount,
+                    addLocalVal(
+                        argCount,
+                        XTypeName.PRIMITIVE_INT,
+                        "%L%L",
+                        knownQueryArgsCount,
                         listSizeVars.joinToString("") { " + ${it.second}" }
                     )
-                    addStatement(
-                        "final $T $L = $T.acquire($L, $L)",
-                        ROOM_SQL_QUERY, outArgsName, ROOM_SQL_QUERY, outSqlQueryName,
-                        argCount
+                    addLocalVariable(
+                        name = outArgsName,
+                        typeName = RoomTypeNames.ROOM_SQL_QUERY,
+                        assignExpr = XCodeBlock.of(
+                            language,
+                            "%M(%L, %L)",
+                            RoomTypeNames.ROOM_SQL_QUERY
+                                .companionMember("acquire", isJvmStatic = true),
+                            outSqlQueryName,
+                            argCount
+                        )
                     )
                 }
             } else {
-                addStatement(
-                    "final $T $L = $S", String::class.typeName,
-                    outSqlQueryName, query.queryWithReplacedBindParams
+                addLocalVal(
+                    outSqlQueryName,
+                    String::class.asClassName(),
+                    "%S",
+                    query.queryWithReplacedBindParams
                 )
                 if (outArgsName != null) {
-                    addStatement(
-                        "final $T $L = $T.acquire($L, $L)",
-                        ROOM_SQL_QUERY, outArgsName, ROOM_SQL_QUERY, outSqlQueryName,
-                        knownQueryArgsCount
+                    addLocalVariable(
+                        name = outArgsName,
+                        typeName = RoomTypeNames.ROOM_SQL_QUERY,
+                        assignExpr = XCodeBlock.of(
+                            language,
+                            "%M(%L, %L)",
+                            RoomTypeNames.ROOM_SQL_QUERY
+                                .companionMember("acquire", isJvmStatic = true),
+                            outSqlQueryName,
+                            knownQueryArgsCount
+                        )
                     )
                 }
             }
@@ -149,25 +173,31 @@
         if (parameters.isEmpty()) {
             return
         }
-        scope.builder().apply {
+        scope.builder.apply {
             val argIndex = scope.getTmpVar("_argIndex")
-            addStatement("$T $L = $L", TypeName.INT, argIndex, 1)
+            addLocalVariable(
+                name = argIndex,
+                typeName = XTypeName.PRIMITIVE_INT,
+                isMutable = true,
+                assignExpr = XCodeBlock.of(language, "%L", 1)
+
+            )
             // # of bindings with 1 placeholder
             var constInputs = 0
             // variable names for size of the bindings that have multiple  args
             val varInputs = arrayListOf<String>()
-            sectionToParamMapping.forEach { pair ->
+            sectionToParamMapping.forEach { (_, param) ->
                 // reset the argIndex to the correct start index
                 if (constInputs > 0 || varInputs.isNotEmpty()) {
                     addStatement(
-                        "$L = $L$L", argIndex,
-                        if (constInputs > 0) (1 + constInputs) else "1",
+                        "%L = %L%L",
+                        argIndex,
+                        if (constInputs > 0) { 1 + constInputs } else { "1" },
                         varInputs.joinToString("") { " + $it" }
                     )
                 }
-                val param = pair.second
                 param?.let {
-                    param.queryParamAdapter?.bindToStmt(param.name, outArgsName, argIndex, scope)
+                    it.queryParamAdapter?.bindToStmt(it.name, outArgsName, argIndex, scope)
                 }
                 // add these to the list so that we can use them to calculate the next count.
                 val sizeVar = listSizeVars.firstOrNull { it.first == param }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/RelationCollectorMethodWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/RelationCollectorFunctionWriter.kt
similarity index 90%
rename from room/room-compiler/src/main/kotlin/androidx/room/writer/RelationCollectorMethodWriter.kt
rename to room/room-compiler/src/main/kotlin/androidx/room/writer/RelationCollectorFunctionWriter.kt
index 3d96052..3f06b01 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/RelationCollectorMethodWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/RelationCollectorFunctionWriter.kt
@@ -16,13 +16,17 @@
 
 package androidx.room.writer
 
+import androidx.room.compiler.codegen.XFunSpec
 import androidx.room.compiler.codegen.toJavaPoet
+import androidx.room.compiler.codegen.toXTypeName
 import androidx.room.ext.AndroidTypeNames.CURSOR
 import androidx.room.ext.CollectionTypeNames
 import androidx.room.ext.CommonTypeNames
 import androidx.room.ext.L
 import androidx.room.ext.N
-import androidx.room.ext.RoomTypeNames
+import androidx.room.ext.RoomTypeNames.CURSOR_UTIL
+import androidx.room.ext.RoomTypeNames.DB_UTIL
+import androidx.room.ext.RoomTypeNames.ROOM_DB
 import androidx.room.ext.S
 import androidx.room.ext.T
 import androidx.room.ext.stripNonJava
@@ -31,20 +35,20 @@
 import androidx.room.vo.RelationCollector
 import com.squareup.javapoet.ClassName
 import com.squareup.javapoet.CodeBlock
-import com.squareup.javapoet.MethodSpec
 import com.squareup.javapoet.ParameterSpec
 import com.squareup.javapoet.ParameterizedTypeName
 import com.squareup.javapoet.TypeName
 import javax.lang.model.element.Modifier
 
 /**
- * Writes the method that fetches the relations of a POJO and assigns them into the given map.
+ * Writes the function that fetches the relations of a POJO and assigns them into the given map.
  */
-class RelationCollectorMethodWriter(private val collector: RelationCollector) :
-    TypeWriter.SharedMethodSpec(
-        "fetchRelationship${collector.relation.entity.tableName.stripNonJava()}" +
-            "As${collector.relation.pojoTypeName.toString().stripNonJava()}"
-    ) {
+class RelationCollectorFunctionWriter(
+    private val collector: RelationCollector
+) : TypeWriter.SharedFunctionSpec(
+    "fetchRelationship${collector.relation.entity.tableName.stripNonJava()}" +
+        "As${collector.relation.pojoTypeName.toString().stripNonJava()}"
+) {
     companion object {
         const val PARAM_MAP_VARIABLE = "_map"
         const val KEY_SET_VARIABLE = "__mapKeySet"
@@ -59,7 +63,7 @@
             "-${relation.createLoadAllSql()}"
     }
 
-    override fun prepare(methodName: String, writer: TypeWriter, builder: MethodSpec.Builder) {
+    override fun prepare(methodName: String, writer: TypeWriter, builder: XFunSpec.Builder) {
         val scope = CodeGenScope(writer)
         val relation = collector.relation
 
@@ -109,14 +113,14 @@
             addStatement("// check if the size is too big, if so divide")
             beginControlFlow(
                 "if($N.size() > $T.MAX_BIND_PARAMETER_CNT)",
-                param, RoomTypeNames.ROOM_DB
+                param, ROOM_DB.toJavaPoet()
             ).apply {
                 // divide it into chunks
                 val tmpMapVar = scope.getTmpVar("_tmpInnerMap")
                 addStatement(
                     "$T $L = new $T($L.MAX_BIND_PARAMETER_CNT)",
                     collector.mapTypeName, tmpMapVar,
-                    collector.mapTypeName, RoomTypeNames.ROOM_DB
+                    collector.mapTypeName, ROOM_DB.toJavaPoet()
                 )
                 val tmpIndexVar = scope.getTmpVar("_tmpIndex")
                 addStatement("$T $L = 0", TypeName.INT, tmpIndexVar)
@@ -158,7 +162,7 @@
                     addStatement("$L++", tmpIndexVar)
                     beginControlFlow(
                         "if($L == $T.MAX_BIND_PARAMETER_CNT)",
-                        tmpIndexVar, RoomTypeNames.ROOM_DB
+                        tmpIndexVar, ROOM_DB.toJavaPoet()
                     ).apply {
                         // recursively load that batch
                         addStatement("$L($L)", methodName, tmpMapVar)
@@ -170,7 +174,7 @@
                         // clear nukes the backing data hence we create a new one
                         addStatement(
                             "$L = new $T($T.MAX_BIND_PARAMETER_CNT)",
-                            tmpMapVar, collector.mapTypeName, RoomTypeNames.ROOM_DB
+                            tmpMapVar, collector.mapTypeName, ROOM_DB.toJavaPoet()
                         )
                         addStatement("$L = 0", tmpIndexVar)
                     }.endControlFlow()
@@ -194,8 +198,8 @@
                 "final $T $L = $T.query($N, $L, $L, $L)",
                 CURSOR.toJavaPoet(),
                 cursorVar,
-                RoomTypeNames.DB_UTIL,
-                DaoWriter.dbField,
+                DB_UTIL.toJavaPoet(),
+                DaoWriter.DB_PROPERTY_NAME,
                 stmtVar,
                 if (shouldCopyCursor) "true" else "false",
                 "null"
@@ -216,7 +220,7 @@
                 } else {
                     addStatement(
                         "final $T $L = $T.getColumnIndex($L, $S)",
-                        TypeName.INT, itemKeyIndexVar, RoomTypeNames.CURSOR_UTIL, cursorVar,
+                        TypeName.INT, itemKeyIndexVar, CURSOR_UTIL.toJavaPoet(), cursorVar,
                         relation.entityField.columnName
                     )
                 }
@@ -263,10 +267,8 @@
             endControlFlow()
         }
         builder.apply {
-            addModifiers(Modifier.PRIVATE)
-            addParameter(param)
-            returns(TypeName.VOID)
-            addCode(scope.builder().build())
+            addParameter(param.type.toXTypeName(), param.name)
+            addCode(scope.generate())
         }
     }
 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/SQLiteOpenHelperWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/SQLiteOpenHelperWriter.kt
index 14009da..dd62e1c 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/SQLiteOpenHelperWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/SQLiteOpenHelperWriter.kt
@@ -17,9 +17,11 @@
 package androidx.room.writer
 
 import androidx.annotation.VisibleForTesting
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.ext.L
 import androidx.room.ext.N
 import androidx.room.ext.RoomTypeNames
+import androidx.room.ext.RoomTypeNames.DB_UTIL
 import androidx.room.ext.S
 import androidx.room.ext.SupportDbTypeNames
 import androidx.room.ext.T
@@ -223,7 +225,7 @@
             addModifiers(PUBLIC)
             addAnnotation(Override::class.java)
             addParameter(SupportDbTypeNames.DB, "_db")
-            addStatement("$T.dropFtsSyncTriggers($L)", RoomTypeNames.DB_UTIL, "_db")
+            addStatement("$T.dropFtsSyncTriggers($L)", DB_UTIL.toJavaPoet(), "_db")
         }.build()
     }
 
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/TypeWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/TypeWriter.kt
index 5722888..f49c5c2 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/TypeWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/TypeWriter.kt
@@ -18,6 +18,10 @@
 
 import androidx.room.RoomProcessor
 import androidx.room.compiler.codegen.CodeLanguage
+import androidx.room.compiler.codegen.VisibilityModifier
+import androidx.room.compiler.codegen.XFunSpec
+import androidx.room.compiler.codegen.XPropertySpec
+import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.codegen.XTypeSpec
 import androidx.room.compiler.codegen.XTypeSpec.Builder.Companion.apply
 import androidx.room.compiler.processing.XProcessingEnv
@@ -33,9 +37,8 @@
  * Base class for all writers that can produce a class.
  */
 abstract class TypeWriter(val codeLanguage: CodeLanguage) {
-    // TODO(danysantiago): Migrate to XPoet
-    private val sharedFieldSpecs = mutableMapOf<String, com.squareup.javapoet.FieldSpec>()
-    private val sharedMethodSpecs = mutableMapOf<String, com.squareup.javapoet.MethodSpec>()
+    private val sharedFieldSpecs = mutableMapOf<String, XPropertySpec>()
+    private val sharedMethodSpecs = mutableMapOf<String, XFunSpec>()
     private val sharedFieldNames = mutableSetOf<String>()
     private val sharedMethodNames = mutableSetOf<String>()
 
@@ -66,13 +69,8 @@
 
     fun write(processingEnv: XProcessingEnv) {
         val builder = createTypeSpecBuilder()
-        builder.apply(
-            javaTypeBuilder = {
-                sharedFieldSpecs.values.forEach { addField(it) }
-                sharedMethodSpecs.values.forEach { addMethod(it) }
-            },
-            kotlinTypeBuilder = { }
-        )
+        sharedFieldSpecs.values.forEach { builder.addProperty(it) }
+        sharedMethodSpecs.values.forEach { builder.addFunction(it) }
         addGeneratedAnnotationIfAvailable(builder, processingEnv)
         addSuppressWarnings(builder)
         builder.build().writeTo(processingEnv.filer)
@@ -139,43 +137,51 @@
         }
     }
 
-    fun getOrCreateField(sharedField: SharedFieldSpec): com.squareup.javapoet.FieldSpec {
-        return sharedFieldSpecs.getOrPut(sharedField.getUniqueKey()) {
-            sharedField.build(this, makeUnique(sharedFieldNames, sharedField.baseName))
+    fun getOrCreateProperty(sharedProperty: SharedPropertySpec): XPropertySpec {
+        return sharedFieldSpecs.getOrPut(sharedProperty.getUniqueKey()) {
+            sharedProperty.build(this, makeUnique(sharedFieldNames, sharedProperty.baseName))
         }
     }
 
-    fun getOrCreateMethod(sharedMethod: SharedMethodSpec): com.squareup.javapoet.MethodSpec {
-        return sharedMethodSpecs.getOrPut(sharedMethod.getUniqueKey()) {
-            sharedMethod.build(this, makeUnique(sharedMethodNames, sharedMethod.baseName))
+    fun getOrCreateFunction(sharedFunction: SharedFunctionSpec): XFunSpec {
+        return sharedMethodSpecs.getOrPut(sharedFunction.getUniqueKey()) {
+            sharedFunction.build(this, makeUnique(sharedMethodNames, sharedFunction.baseName))
         }
     }
 
-    abstract class SharedFieldSpec(val baseName: String, val type: com.squareup.javapoet.TypeName) {
+    abstract class SharedPropertySpec(val baseName: String, val type: XTypeName) {
+
+        open val isMutable = false
 
         abstract fun getUniqueKey(): String
 
-        abstract fun prepare(writer: TypeWriter, builder: com.squareup.javapoet.FieldSpec.Builder)
+        abstract fun prepare(writer: TypeWriter, builder: XPropertySpec.Builder)
 
-        fun build(classWriter: TypeWriter, name: String): com.squareup.javapoet.FieldSpec {
-            val builder = com.squareup.javapoet.FieldSpec.builder(type, name)
+        fun build(classWriter: TypeWriter, name: String): XPropertySpec {
+            val builder = XPropertySpec.builder(
+                language = classWriter.codeLanguage,
+                name = name,
+                typeName = type,
+                visibility = VisibilityModifier.PRIVATE,
+                isMutable = isMutable
+            )
             prepare(classWriter, builder)
             return builder.build()
         }
     }
 
-    abstract class SharedMethodSpec(val baseName: String) {
+    abstract class SharedFunctionSpec(val baseName: String) {
 
         abstract fun getUniqueKey(): String
 
         abstract fun prepare(
             methodName: String,
             writer: TypeWriter,
-            builder: com.squareup.javapoet.MethodSpec.Builder
+            builder: XFunSpec.Builder
         )
 
-        fun build(writer: TypeWriter, name: String): com.squareup.javapoet.MethodSpec {
-            val builder = com.squareup.javapoet.MethodSpec.methodBuilder(name)
+        fun build(writer: TypeWriter, name: String): XFunSpec {
+            val builder = XFunSpec.builder(writer.codeLanguage, name, VisibilityModifier.PRIVATE)
             prepare(name, writer, builder)
             return builder.build()
         }
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/CustomConverterProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/CustomConverterProcessorTest.kt
index 7347cf1..5ef999b 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/CustomConverterProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/CustomConverterProcessorTest.kt
@@ -17,6 +17,7 @@
 package androidx.room.processor
 
 import androidx.room.TypeConverter
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.XTestInvocation
 import androidx.room.compiler.processing.util.runProcessorTest
@@ -35,13 +36,13 @@
 import com.squareup.javapoet.TypeName
 import com.squareup.javapoet.TypeSpec
 import com.squareup.javapoet.TypeVariableName
+import java.util.Date
+import javax.lang.model.element.Modifier
 import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.MatcherAssert.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
-import java.util.Date
-import javax.lang.model.element.Modifier
 
 @RunWith(JUnit4::class)
 class CustomConverterProcessorTest {
@@ -62,32 +63,32 @@
     @Test
     fun validCase() {
         singleClass(createConverter(TypeName.SHORT.box(), TypeName.CHAR.box())) { converter, _ ->
-            assertThat(converter?.fromTypeName, `is`(TypeName.SHORT.box()))
-            assertThat(converter?.toTypeName, `is`(TypeName.CHAR.box()))
+            assertThat(converter?.fromTypeName?.toJavaPoet(), `is`(TypeName.SHORT.box()))
+            assertThat(converter?.toTypeName?.toJavaPoet(), `is`(TypeName.CHAR.box()))
         }
     }
 
     @Test
     fun primitiveFrom() {
         singleClass(createConverter(TypeName.SHORT, TypeName.CHAR.box())) { converter, _ ->
-            assertThat(converter?.fromTypeName, `is`(TypeName.SHORT))
-            assertThat(converter?.toTypeName, `is`(TypeName.CHAR.box()))
+            assertThat(converter?.fromTypeName?.toJavaPoet(), `is`(TypeName.SHORT))
+            assertThat(converter?.toTypeName?.toJavaPoet(), `is`(TypeName.CHAR.box()))
         }
     }
 
     @Test
     fun primitiveTo() {
         singleClass(createConverter(TypeName.INT.box(), TypeName.DOUBLE)) { converter, _ ->
-            assertThat(converter?.fromTypeName, `is`(TypeName.INT.box()))
-            assertThat(converter?.toTypeName, `is`(TypeName.DOUBLE))
+            assertThat(converter?.fromTypeName?.toJavaPoet(), `is`(TypeName.INT.box()))
+            assertThat(converter?.toTypeName?.toJavaPoet(), `is`(TypeName.DOUBLE))
         }
     }
 
     @Test
     fun primitiveBoth() {
         singleClass(createConverter(TypeName.INT, TypeName.DOUBLE)) { converter, _ ->
-            assertThat(converter?.fromTypeName, `is`(TypeName.INT))
-            assertThat(converter?.toTypeName, `is`(TypeName.DOUBLE))
+            assertThat(converter?.fromTypeName?.toJavaPoet(), `is`(TypeName.INT))
+            assertThat(converter?.toTypeName?.toJavaPoet(), `is`(TypeName.DOUBLE))
         }
     }
 
@@ -96,8 +97,8 @@
         val string = String::class.typeName
         val date = Date::class.typeName
         singleClass(createConverter(string, date)) { converter, _ ->
-            assertThat(converter?.fromTypeName, `is`(string as TypeName))
-            assertThat(converter?.toTypeName, `is`(date as TypeName))
+            assertThat(converter?.fromTypeName?.toJavaPoet(), `is`(string as TypeName))
+            assertThat(converter?.toTypeName?.toJavaPoet(), `is`(date as TypeName))
         }
     }
 
@@ -121,8 +122,8 @@
         val list = ParameterizedTypeName.get(List::class.typeName, string)
         val map = ParameterizedTypeName.get(Map::class.typeName, string, date)
         singleClass(createConverter(list, map)) { converter, _ ->
-            assertThat(converter?.fromTypeName, `is`(list as TypeName))
-            assertThat(converter?.toTypeName, `is`(map as TypeName))
+            assertThat(converter?.fromTypeName?.toJavaPoet(), `is`(list as TypeName))
+            assertThat(converter?.toTypeName?.toJavaPoet(), `is`(map as TypeName))
         }
     }
 
@@ -184,8 +185,8 @@
                 """
             )
         ) { converter, _ ->
-            assertThat(converter?.fromTypeName, `is`(TypeName.SHORT))
-            assertThat(converter?.toTypeName, `is`(TypeName.INT))
+            assertThat(converter?.fromTypeName?.toJavaPoet(), `is`(TypeName.SHORT))
+            assertThat(converter?.toTypeName?.toJavaPoet(), `is`(TypeName.INT))
             assertThat(converter?.isStatic, `is`(true))
         }
     }
@@ -206,8 +207,8 @@
                 """
             )
         ) { converter, invocation ->
-            assertThat(converter?.fromTypeName, `is`(TypeName.SHORT))
-            assertThat(converter?.toTypeName, `is`(TypeName.INT))
+            assertThat(converter?.fromTypeName?.toJavaPoet(), `is`(TypeName.SHORT))
+            assertThat(converter?.toTypeName?.toJavaPoet(), `is`(TypeName.INT))
             assertThat(converter?.isStatic, `is`(true))
             invocation.assertCompilationResult {
                 hasErrorContaining(TYPE_CONVERTER_MUST_BE_PUBLIC)
@@ -246,7 +247,7 @@
             val converter = CustomConverterProcessor(invocation.context, element)
                 .process().firstOrNull()
             assertThat(
-                converter?.fromTypeName,
+                converter?.fromTypeName?.toJavaPoet(),
                 `is`(
                     ParameterizedTypeName.get(
                         List::class.typeName, String::class.typeName
@@ -254,7 +255,7 @@
                 )
             )
             assertThat(
-                converter?.toTypeName,
+                converter?.toTypeName?.toJavaPoet(),
                 `is`(
                     ParameterizedTypeName.get(
                         Map::class.typeName,
@@ -270,8 +271,8 @@
         singleClass(
             createConverter(TypeName.SHORT.box(), TypeName.CHAR.box(), duplicate = true)
         ) { converter, invocation ->
-            assertThat(converter?.fromTypeName, `is`(TypeName.SHORT.box()))
-            assertThat(converter?.toTypeName, `is`(TypeName.CHAR.box()))
+            assertThat(converter?.fromTypeName?.toJavaPoet(), `is`(TypeName.SHORT.box()))
+            assertThat(converter?.toTypeName?.toJavaPoet(), `is`(TypeName.CHAR.box()))
             invocation.assertCompilationResult {
                 hasErrorContaining("Multiple methods define the same conversion")
             }
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/DaoProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/DaoProcessorTest.kt
index a55ed17..52f0496 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/DaoProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/DaoProcessorTest.kt
@@ -17,12 +17,13 @@
 package androidx.room.processor
 
 import COMMON
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.isTypeElement
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.XTestInvocation
 import androidx.room.compiler.processing.util.compileFiles
 import androidx.room.compiler.processing.util.runProcessorTest
-import androidx.room.ext.RoomTypeNames
+import androidx.room.ext.RoomTypeNames.ROOM_DB
 import androidx.room.testing.context
 import androidx.room.vo.Dao
 import androidx.room.vo.ReadQueryMethod
@@ -30,12 +31,12 @@
 import com.google.common.truth.Truth
 import com.squareup.javapoet.TypeName
 import createVerifierFromEntitiesAndViews
+import java.io.File
 import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.MatcherAssert.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
-import java.io.File
 
 @RunWith(Parameterized::class)
 class DaoProcessorTest(private val enableVerification: Boolean) {
@@ -224,7 +225,7 @@
             """
         ) { dao, invocation ->
             val dbType = invocation.context.processingEnv
-                .requireType(RoomTypeNames.ROOM_DB)
+                .requireType(ROOM_DB.toJavaPoet())
             val daoProcessor =
                 DaoProcessor(invocation.context, dao.element, dbType, null)
 
@@ -272,7 +273,7 @@
             if (!dao.isTypeElement()) {
                 error("Expected DAO to be a type")
             }
-            val dbType = invocation.context.processingEnv.requireType(RoomTypeNames.ROOM_DB)
+            val dbType = invocation.context.processingEnv.requireType(ROOM_DB.toJavaPoet())
             val daoProcessor =
                 DaoProcessor(invocation.context, dao, dbType, null)
             Truth.assertThat(daoProcessor.context.logger.suppressedWarnings)
@@ -293,7 +294,7 @@
             """
         ) { dao, invocation ->
             val dbType = invocation.context.processingEnv
-                .requireType(RoomTypeNames.ROOM_DB)
+                .requireType(ROOM_DB.toJavaPoet())
             val daoProcessor =
                 DaoProcessor(invocation.context, dao.element, dbType, null)
             assertThat(
@@ -457,7 +458,7 @@
         """.trimIndent())
         runProcessorTest(sources = listOf(source)) { invocation ->
             val dao = invocation.processingEnv.requireTypeElement("MyDao")
-            val dbType = invocation.context.processingEnv.requireType(RoomTypeNames.ROOM_DB)
+            val dbType = invocation.context.processingEnv.requireType(ROOM_DB.toJavaPoet())
             DaoProcessor(
                 baseContext = invocation.context,
                 element = dao,
@@ -499,7 +500,7 @@
                 null
             }
             val dbType = invocation.context.processingEnv
-                .requireType(RoomTypeNames.ROOM_DB)
+                .requireType(ROOM_DB.toJavaPoet())
             val parser = DaoProcessor(
                 invocation.context,
                 dao, dbType, dbVerifier
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 f5ea0af..ee95322 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
@@ -18,6 +18,8 @@
 
 import COMMON
 import androidx.room.Dao
+import androidx.room.compiler.codegen.XClassName
+import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.XMethodElement
 import androidx.room.compiler.processing.XType
@@ -74,8 +76,8 @@
 
         const val DAO_SUFFIX = "}"
         val USER_TYPE_NAME: TypeName = COMMON.USER_TYPE_NAME
-        val USERNAME_TYPE_NAME: TypeName = ClassName.get("foo.bar", "Username")
-        val BOOK_TYPE_NAME: TypeName = ClassName.get("foo.bar", "Book")
+        val USERNAME_TYPE_NAME: XTypeName = XClassName.get("foo.bar", "Username")
+        val BOOK_TYPE_NAME: XTypeName = XClassName.get("foo.bar", "Book")
     }
 
     @Test
@@ -112,7 +114,7 @@
             assertThat(shortcut.entities.size, `is`(1))
             assertThat(shortcut.entities["user"]?.isPartialEntity, `is`(false))
             assertThat(
-                shortcut.entities["user"]?.pojo?.let { it.typeName.toJavaPoet() },
+                shortcut.entities["user"]?.pojo?.typeName?.toJavaPoet(),
                 `is`(USER_TYPE_NAME)
             )
         }
@@ -154,11 +156,11 @@
             }
             assertThat(shortcut.entities.size, `is`(2))
             assertThat(
-                shortcut.entities["u1"]?.pojo?.let { it.typeName.toJavaPoet() },
+                shortcut.entities["u1"]?.pojo?.typeName?.toJavaPoet(),
                 `is`(USER_TYPE_NAME)
             )
             assertThat(
-                shortcut.entities["u1"]?.pojo?.let { it.typeName.toJavaPoet() },
+                shortcut.entities["u1"]?.pojo?.typeName?.toJavaPoet(),
                 `is`(USER_TYPE_NAME)
             )
             assertThat(
@@ -201,7 +203,7 @@
                 assertThat(param.pojoType?.typeName, `is`(USER_TYPE_NAME))
                 assertThat(shortcut.entities.size, `is`(1))
                 assertThat(
-                    shortcut.entities["users"]?.pojo?.let { it.typeName.toJavaPoet() },
+                    shortcut.entities["users"]?.pojo?.typeName?.toJavaPoet(),
                     `is`(USER_TYPE_NAME)
                 )
             }
@@ -227,7 +229,7 @@
             )
             assertThat(shortcut.entities.size, `is`(1))
             assertThat(
-                shortcut.entities["users"]?.pojo?.let { it.typeName.toJavaPoet() },
+                shortcut.entities["users"]?.pojo?.typeName?.toJavaPoet(),
                 `is`(USER_TYPE_NAME)
             )
         }
@@ -255,7 +257,7 @@
             )
             assertThat(shortcut.entities.size, `is`(1))
             assertThat(
-                shortcut.entities["users"]?.pojo?.let { it.typeName.toJavaPoet() },
+                shortcut.entities["users"]?.pojo?.typeName?.toJavaPoet(),
                 `is`(USER_TYPE_NAME)
             )
         }
@@ -283,7 +285,7 @@
             )
             assertThat(shortcut.entities.size, `is`(1))
             assertThat(
-                shortcut.entities["users"]?.pojo?.let { it.typeName.toJavaPoet() },
+                shortcut.entities["users"]?.pojo?.typeName?.toJavaPoet(),
                 `is`(USER_TYPE_NAME)
             )
         }
@@ -312,7 +314,7 @@
             )
             assertThat(shortcut.entities.size, `is`(1))
             assertThat(
-                shortcut.entities["users"]?.pojo?.let { it.typeName.toJavaPoet() },
+                shortcut.entities["users"]?.pojo?.typeName?.toJavaPoet(),
                 `is`(USER_TYPE_NAME)
             )
         }
@@ -350,11 +352,11 @@
                 assertThat(shortcut.parameters.map { it.name }, `is`(listOf("u1", "b1")))
                 assertThat(shortcut.entities.size, `is`(2))
                 assertThat(
-                    shortcut.entities["u1"]?.pojo?.let { it.typeName.toJavaPoet() },
+                    shortcut.entities["u1"]?.pojo?.typeName?.toJavaPoet(),
                     `is`(USER_TYPE_NAME)
                 )
                 assertThat(
-                    shortcut.entities["b1"]?.pojo?.let { it.typeName.toJavaPoet() },
+                    shortcut.entities["b1"]?.pojo?.typeName,
                     `is`(BOOK_TYPE_NAME)
                 )
             }
@@ -447,13 +449,14 @@
             assertThat(shortcut.element.jvmName, `is`("foo"))
             assertThat(shortcut.parameters.size, `is`(1))
             val param = shortcut.parameters.first()
-            assertThat(param.type.typeName, `is`(USERNAME_TYPE_NAME))
-            assertThat(param.pojoType?.typeName, `is`(USERNAME_TYPE_NAME))
+            assertThat(param.type.asTypeName(), `is`(USERNAME_TYPE_NAME))
+            assertThat(param.pojoType?.asTypeName(), `is`(USERNAME_TYPE_NAME))
             assertThat(shortcut.entities.size, `is`(1))
             assertThat(shortcut.entities["username"]?.isPartialEntity, `is`(true))
-            assertThat(shortcut.entities["username"]?.entityTypeName, `is`(USER_TYPE_NAME))
+            assertThat(shortcut.entities["username"]?.entityTypeName?.toJavaPoet(),
+                `is`(USER_TYPE_NAME))
             assertThat(
-                shortcut.entities["username"]?.pojo?.let { it.typeName.toJavaPoet() },
+                shortcut.entities["username"]?.pojo?.typeName,
                 `is`(USERNAME_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 467a22f..9964afe 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
@@ -18,6 +18,8 @@
 
 import COMMON
 import androidx.room.Dao
+import androidx.room.compiler.codegen.XClassName
+import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.XMethodElement
 import androidx.room.compiler.processing.XType
@@ -73,8 +75,8 @@
                 """
         const val DAO_SUFFIX = "}"
         val USER_TYPE_NAME: TypeName = COMMON.USER_TYPE_NAME
-        val USERNAME_TYPE_NAME: TypeName = ClassName.get("foo.bar", "Username")
-        val BOOK_TYPE_NAME: TypeName = ClassName.get("foo.bar", "Book")
+        val USERNAME_TYPE_NAME: XTypeName = XClassName.get("foo.bar", "Username")
+        val BOOK_TYPE_NAME: XTypeName = XClassName.get("foo.bar", "Book")
     }
 
     @Test
@@ -375,7 +377,7 @@
             assertThat(insertionUpsertion.entities["u1"]?.pojo?.typeName?.toJavaPoet())
                 .isEqualTo(USER_TYPE_NAME)
 
-            assertThat(insertionUpsertion.entities["b1"]?.pojo?.typeName?.toJavaPoet())
+            assertThat(insertionUpsertion.entities["b1"]?.pojo?.typeName)
                 .isEqualTo(BOOK_TYPE_NAME)
         }
     }
@@ -582,19 +584,19 @@
 
             val param = insertionUpsertion.parameters.first()
 
-            assertThat(param.type.typeName).isEqualTo(USERNAME_TYPE_NAME)
+            assertThat(param.type.asTypeName()).isEqualTo(USERNAME_TYPE_NAME)
 
-            assertThat(param.pojoType?.typeName).isEqualTo(USERNAME_TYPE_NAME)
+            assertThat(param.pojoType?.asTypeName()).isEqualTo(USERNAME_TYPE_NAME)
 
             assertThat(insertionUpsertion.entities.size).isEqualTo(1)
 
             assertThat(insertionUpsertion.entities["username"]?.isPartialEntity)
                 .isEqualTo(true)
 
-            assertThat(insertionUpsertion.entities["username"]?.entityTypeName)
+            assertThat(insertionUpsertion.entities["username"]?.entityTypeName?.toJavaPoet())
                 .isEqualTo(USER_TYPE_NAME)
 
-            assertThat(insertionUpsertion.entities["username"]?.pojo?.typeName?.toJavaPoet())
+            assertThat(insertionUpsertion.entities["username"]?.pojo?.typeName)
                 .isEqualTo(USERNAME_TYPE_NAME)
         }
     }
@@ -660,7 +662,7 @@
             invocation.assertCompilationResult {
                 hasErrorContaining(
                     ProcessorErrors.missingRequiredColumnsInPartialEntity(
-                        partialEntityName = USERNAME_TYPE_NAME.toString(),
+                        partialEntityName = USERNAME_TYPE_NAME.toJavaPoet().toString(),
                         missingColumnNames = listOf("ageColumn")
                     )
                 )
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 34b4ff6..caedbe7 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
@@ -45,7 +45,7 @@
 import androidx.room.solver.query.result.LiveDataQueryResultBinder
 import androidx.room.solver.query.result.PojoRowAdapter
 import androidx.room.solver.query.result.SingleColumnRowAdapter
-import androidx.room.solver.query.result.SingleEntityQueryResultAdapter
+import androidx.room.solver.query.result.SingleItemQueryResultAdapter
 import androidx.room.testing.context
 import androidx.room.vo.Field
 import androidx.room.vo.QueryMethod
@@ -60,9 +60,9 @@
 import com.squareup.javapoet.TypeVariableName
 import createVerifierFromEntitiesAndViews
 import mockElementAndType
-import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.CoreMatchers.hasItem
 import org.hamcrest.CoreMatchers.instanceOf
+import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.CoreMatchers.not
 import org.hamcrest.CoreMatchers.notNullValue
 import org.hamcrest.MatcherAssert.assertThat
@@ -818,7 +818,7 @@
             )
             val adapter = parsedQuery.queryResultBinder.adapter
             assertThat(checkNotNull(adapter))
-            assertThat(adapter::class, `is`(SingleEntityQueryResultAdapter::class))
+            assertThat(adapter::class, `is`(SingleItemQueryResultAdapter::class))
             val rowAdapter = adapter.rowAdapters.single()
             assertThat(checkNotNull(rowAdapter))
             assertThat(rowAdapter::class, `is`(PojoRowAdapter::class))
@@ -1298,7 +1298,7 @@
         ) { parsedQuery, invocation ->
             val adapter = parsedQuery.queryResultBinder.adapter
             if (enableVerification) {
-                if (adapter is SingleEntityQueryResultAdapter) {
+                if (adapter is SingleItemQueryResultAdapter) {
                     handler(
                         adapter.rowAdapters.single() as? PojoRowAdapter,
                         parsedQuery,
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/solver/BasicColumnTypeAdaptersTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/solver/BasicColumnTypeAdaptersTest.kt
index 4b649bf..7ef769c 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/solver/BasicColumnTypeAdaptersTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/solver/BasicColumnTypeAdaptersTest.kt
@@ -112,7 +112,9 @@
                     affinity = null,
                     skipDefaultConverter = false
                 )!!
-            val expected = if (input.isAlwaysCheckedForNull()) {
+            val expected = if (invocation.isKsp || input.isPrimitive) {
+                bindCode
+            } else {
                 """
                 if (inp == null) {
                   st.bindNull(6);
@@ -120,8 +122,6 @@
                   $bindCode
                 }
                 """.trimIndent()
-            } else {
-                bindCode
             }
             adapter.bindToStmt("st", "6", "inp", scope)
             assertThat(scope.builder().build().toString().trim(), `is`(expected))
@@ -143,7 +143,7 @@
                 skipDefaultConverter = false
             )!!
             adapter.bindToStmt("st", "6", "inp", scope)
-            val expected = if (invocation.isKsp && !input.isAlwaysCheckedForNull()) {
+            val expected = if (invocation.isKsp) {
                 bindCode
             } else {
                 """
@@ -229,7 +229,9 @@
                 affinity = null,
                 skipDefaultConverter = false
             )!!
-            val expected = if (input.isAlwaysCheckedForNull()) {
+            val expected = if (invocation.isKsp || input.isPrimitive) {
+                cursorCode
+            } else {
                 """
                 if (crs.isNull(9)) {
                   out = null;
@@ -237,8 +239,6 @@
                   $cursorCode
                 }
                 """.trimIndent()
-            } else {
-                cursorCode
             }
             adapter.readFromCursor("out", "crs", "9", scope)
             assertThat(scope.builder().build().toString().trim(), `is`(expected))
@@ -260,7 +260,7 @@
                 skipDefaultConverter = false
             )!!
             adapter.readFromCursor("out", "crs", "9", scope)
-            val expected = if (invocation.isKsp && !input.isAlwaysCheckedForNull()) {
+            val expected = if (invocation.isKsp) {
                 cursorCode
             } else {
                 """
@@ -306,11 +306,4 @@
             generateCode(invocation, scope, nullableType)
         }
     }
-
-    /*
-     * KSP knows when a boxed primitive type is non-null but for declared types (e.g. String) we
-     * still generate code that checks for null. If we start accounting for the nullability in
-     * the generated code for declared types, this function should be removed from this test.
-     */
-    private fun TypeName.isAlwaysCheckedForNull() = !this.isPrimitive
 }
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/solver/CustomTypeConverterResolutionTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/solver/CustomTypeConverterResolutionTest.kt
index 9b09089..9d3133d 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/solver/CustomTypeConverterResolutionTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/solver/CustomTypeConverterResolutionTest.kt
@@ -26,10 +26,11 @@
 import androidx.room.Query
 import androidx.room.TypeConverter
 import androidx.room.TypeConverters
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.util.CompilationResultSubject
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.runProcessorTest
-import androidx.room.ext.RoomTypeNames
+import androidx.room.ext.RoomTypeNames.ROOM_DB
 import androidx.room.ext.S
 import androidx.room.ext.T
 import androidx.room.processor.ProcessorErrors.CANNOT_BIND_QUERY_PARAMETER_INTO_STMT
@@ -41,10 +42,10 @@
 import com.squareup.javapoet.ParameterizedTypeName
 import com.squareup.javapoet.TypeName
 import com.squareup.javapoet.TypeSpec
+import javax.lang.model.element.Modifier
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
-import javax.lang.model.element.Modifier
 
 @RunWith(JUnit4::class)
 class CustomTypeConverterResolutionTest {
@@ -318,7 +319,7 @@
     ): TypeSpec {
         return TypeSpec.classBuilder(DB).apply {
             addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC)
-            superclass(RoomTypeNames.ROOM_DB)
+            superclass(ROOM_DB.toJavaPoet())
             if (hasConverters) {
                 addAnnotation(createConvertersAnnotation(useCollection = useCollection))
             }
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
index f70f865..1713017 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/solver/TypeAdapterStoreTest.kt
@@ -20,6 +20,8 @@
 import androidx.paging.DataSource
 import androidx.paging.PagingSource
 import androidx.room.Dao
+import androidx.room.compiler.codegen.XCodeBlock
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.XProcessingEnv
 import androidx.room.compiler.processing.XRawType
 import androidx.room.compiler.processing.isTypeElement
@@ -27,15 +29,13 @@
 import androidx.room.compiler.processing.util.XTestInvocation
 import androidx.room.compiler.processing.util.runProcessorTest
 import androidx.room.ext.GuavaUtilConcurrentTypeNames
-import androidx.room.ext.L
 import androidx.room.ext.LifecyclesTypeNames
 import androidx.room.ext.PagingTypeNames
 import androidx.room.ext.ReactiveStreamsTypeNames
-import androidx.room.ext.RoomTypeNames
+import androidx.room.ext.RoomTypeNames.ROOM_DB
 import androidx.room.ext.RoomTypeNames.STRING_UTIL
 import androidx.room.ext.RxJava2TypeNames
 import androidx.room.ext.RxJava3TypeNames
-import androidx.room.ext.T
 import androidx.room.ext.implementsEqualsAndHashcode
 import androidx.room.ext.typeName
 import androidx.room.parser.SQLTypeAffinity
@@ -72,7 +72,6 @@
 import androidx.room.vo.BuiltInConverterFlags
 import androidx.room.vo.ReadQueryMethod
 import com.google.common.truth.Truth.assertThat
-import com.squareup.javapoet.CodeBlock
 import com.squareup.javapoet.TypeName
 import java.util.UUID
 import org.hamcrest.CoreMatchers.instanceOf
@@ -409,30 +408,36 @@
                 null,
                 skipDefaultConverter = false
             )
-            assertThat(adapter, notNullValue())
+            assertThat(adapter).isNotNull()
 
             val bindScope = testCodeGenScope()
             adapter!!.bindToStmt("stmt", "41", "fooVar", bindScope)
-            assertThat(
-                bindScope.builder().build().toString().trim(),
-                `is`(
-                    """
-                final java.lang.String ${tmp(0)} = androidx.room.util.StringUtil.joinIntoString(fooVar);
+            val expectedAdapterCode = if (invocation.isKsp) {
+                """
+                stmt.bindString(41, ${tmp(0)});
+                """.trimIndent()
+            } else {
+                """
                 if (${tmp(0)} == null) {
                   stmt.bindNull(41);
                 } else {
                   stmt.bindString(41, ${tmp(0)});
                 }
-                    """.trimIndent()
-                )
+                """.trimIndent()
+            }
+            assertThat(bindScope.builder().build().toString().trim()).isEqualTo(
+                """
+                |final java.lang.String ${tmp(0)} = androidx.room.util.StringUtil.joinIntoString(fooVar);
+                |$expectedAdapterCode
+                """.trimMargin()
             )
 
             val converter = store.typeConverterStore.findTypeConverter(
                 binders[0].from,
                 invocation.context.COMMON_TYPES.STRING
             )
-            assertThat(converter, notNullValue())
-            assertThat(store.typeConverterStore.reverse(converter!!), `is`(binders[1]))
+            assertThat(converter).isNotNull()
+            assertThat(store.typeConverterStore.reverse(converter!!)).isEqualTo(binders[1])
         }
     }
 
@@ -1166,7 +1171,7 @@
                 ).first()
             check(dao.isTypeElement())
             val dbType = invocation.context.processingEnv
-                .requireType(RoomTypeNames.ROOM_DB)
+                .requireType(ROOM_DB.toJavaPoet())
             val parser = DaoProcessor(
                 invocation.context,
                 dao, dbType, null,
@@ -1219,7 +1224,7 @@
                 ).first()
             check(dao.isTypeElement())
             val dbType = invocation.context.processingEnv
-                .requireType(RoomTypeNames.ROOM_DB)
+                .requireType(ROOM_DB.toJavaPoet())
             val parser = DaoProcessor(
                 invocation.context,
                 dao, dbType, null,
@@ -1271,7 +1276,7 @@
                 ).first()
             check(dao.isTypeElement())
             val dbType = invocation.context.processingEnv
-                .requireType(RoomTypeNames.ROOM_DB)
+                .requireType(ROOM_DB.toJavaPoet())
             val parser = DaoProcessor(
                 invocation.context,
                 dao, dbType, null,
@@ -1319,7 +1324,7 @@
                 ).first()
             check(dao.isTypeElement())
             val dbType = invocation.context.processingEnv
-                .requireType(RoomTypeNames.ROOM_DB)
+                .requireType(ROOM_DB.toJavaPoet())
             val parser = DaoProcessor(
                 invocation.context,
                 dao, dbType, null,
@@ -1668,9 +1673,12 @@
             listOfInts,
             invocation.context.COMMON_TYPES.STRING
         ) {
-            override fun buildStatement(inputVarName: String, scope: CodeGenScope): CodeBlock {
-                return CodeBlock.of(
-                    "$T.joinIntoString($L)", STRING_UTIL, inputVarName
+            override fun buildStatement(inputVarName: String, scope: CodeGenScope): XCodeBlock {
+                return XCodeBlock.of(
+                    scope.language,
+                    "%T.joinIntoString(%L)",
+                    STRING_UTIL.toJavaPoet(),
+                    inputVarName
                 )
             }
         }
@@ -1678,9 +1686,11 @@
         val stringToIntListConverter = object : SingleStatementTypeConverter(
             invocation.context.COMMON_TYPES.STRING, listOfInts
         ) {
-            override fun buildStatement(inputVarName: String, scope: CodeGenScope): CodeBlock {
-                return CodeBlock.of(
-                    "$T.splitToIntList($L)", STRING_UTIL,
+            override fun buildStatement(inputVarName: String, scope: CodeGenScope): XCodeBlock {
+                return XCodeBlock.of(
+                    scope.language,
+                    "%T.splitToIntList(%L)",
+                    STRING_UTIL.toJavaPoet(),
                     inputVarName
                 )
             }
@@ -1693,13 +1703,18 @@
         val tLong = env.requireType("java.lang.Long").makeNullable()
         return listOf(
             object : SingleStatementTypeConverter(tDate, tLong) {
-                override fun buildStatement(inputVarName: String, scope: CodeGenScope): CodeBlock {
-                    return CodeBlock.of("$L.time", inputVarName)
+                override fun buildStatement(inputVarName: String, scope: CodeGenScope): XCodeBlock {
+                    return XCodeBlock.of(scope.language, "%L.time", inputVarName)
                 }
             },
             object : SingleStatementTypeConverter(tLong, tDate) {
-                override fun buildStatement(inputVarName: String, scope: CodeGenScope): CodeBlock {
-                    return CodeBlock.of("new $T($L)", tDate.typeName, inputVarName)
+                override fun buildStatement(inputVarName: String, scope: CodeGenScope): XCodeBlock {
+                    return XCodeBlock.ofNewInstance(
+                        scope.language,
+                        tDate.asTypeName(),
+                        "%L",
+                        inputVarName
+                    )
                 }
             }
         )
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/solver/query/QueryWriterTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/solver/query/QueryWriterTest.kt
index c704fa4..3c09aa1 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/solver/query/QueryWriterTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/solver/query/QueryWriterTest.kt
@@ -18,6 +18,7 @@
 
 import androidx.room.Dao
 import androidx.room.Query
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.XTypeElement
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.runProcessorTest
@@ -45,7 +46,7 @@
                 abstract class MyClass {
                 """
         const val DAO_SUFFIX = "}"
-        val QUERY = ROOM_SQL_QUERY.toString()
+        val QUERY = ROOM_SQL_QUERY.toJavaPoet().toString()
     }
 
     @Test
@@ -55,7 +56,7 @@
                 @Query("SELECT id FROM users")
                 abstract java.util.List<Integer> selectAllIds();
                 """
-        ) { writer ->
+        ) { _, writer ->
             val scope = testCodeGenScope()
             writer.prepareReadAndBind("_sql", "_stmt", scope)
             assertThat(
@@ -77,22 +78,31 @@
                 @Query("SELECT id FROM users WHERE name LIKE :name")
                 abstract java.util.List<Integer> selectAllIds(String name);
                 """
-        ) { writer ->
+        ) { isKsp, writer ->
             val scope = testCodeGenScope()
             writer.prepareReadAndBind("_sql", "_stmt", scope)
+            val expectedStringBind = if (isKsp) {
+                """
+                _stmt.bindString(_argIndex, name);
+                """.trimIndent()
+            } else {
+                """
+                if (name == null) {
+                  _stmt.bindNull(_argIndex);
+                } else {
+                  _stmt.bindString(_argIndex, name);
+                }
+                """.trimIndent()
+            }
             assertThat(
                 scope.builder().build().toString().trim(),
                 `is`(
                     """
-                    final java.lang.String _sql = "SELECT id FROM users WHERE name LIKE ?";
-                    final $QUERY _stmt = $QUERY.acquire(_sql, 1);
-                    int _argIndex = 1;
-                    if (name == null) {
-                      _stmt.bindNull(_argIndex);
-                    } else {
-                      _stmt.bindString(_argIndex, name);
-                    }
-                    """.trimIndent()
+                    |final java.lang.String _sql = "SELECT id FROM users WHERE name LIKE ?";
+                    |final $QUERY _stmt = $QUERY.acquire(_sql, 1);
+                    |int _argIndex = 1;
+                    |$expectedStringBind
+                    """.trimMargin()
                 )
             )
         }
@@ -105,7 +115,7 @@
                 @Query("SELECT id FROM users WHERE id IN(:id1,:id2)")
                 abstract java.util.List<Integer> selectAllIds(int id1, int id2);
                 """
-        ) { writer ->
+        ) { _, writer ->
             val scope = testCodeGenScope()
             writer.prepareReadAndBind("_sql", "_stmt", scope)
             assertThat(
@@ -131,17 +141,17 @@
                 @Query("SELECT id FROM users WHERE id IN(:ids) AND age > :time")
                 abstract java.util.List<Integer> selectAllIds(long time, int... ids);
                 """
-        ) { writer ->
+        ) { _, writer ->
             val scope = testCodeGenScope()
             writer.prepareReadAndBind("_sql", "_stmt", scope)
             assertThat(
                 scope.builder().build().toString().trim(),
                 `is`(
                     """
-                    java.lang.StringBuilder _stringBuilder = $STRING_UTIL.newStringBuilder();
+                    final java.lang.StringBuilder _stringBuilder = ${STRING_UTIL.toJavaPoet()}.newStringBuilder();
                     _stringBuilder.append("SELECT id FROM users WHERE id IN(");
                     final int _inputSize = ids.length;
-                    $STRING_UTIL.appendPlaceholders(_stringBuilder, _inputSize);
+                    ${STRING_UTIL.toJavaPoet()}.appendPlaceholders(_stringBuilder, _inputSize);
                     _stringBuilder.append(") AND age > ");
                     _stringBuilder.append("?");
                     final java.lang.String _sql = _stringBuilder.toString();
@@ -161,10 +171,10 @@
     }
 
     val collectionOut = """
-                    java.lang.StringBuilder _stringBuilder = $STRING_UTIL.newStringBuilder();
+                    final java.lang.StringBuilder _stringBuilder = ${STRING_UTIL.toJavaPoet()}.newStringBuilder();
                     _stringBuilder.append("SELECT id FROM users WHERE id IN(");
                     final int _inputSize = ids.size();
-                    $STRING_UTIL.appendPlaceholders(_stringBuilder, _inputSize);
+                    ${STRING_UTIL.toJavaPoet()}.appendPlaceholders(_stringBuilder, _inputSize);
                     _stringBuilder.append(") AND age > ");
                     _stringBuilder.append("?");
                     final java.lang.String _sql = _stringBuilder.toString();
@@ -190,7 +200,7 @@
                 @Query("SELECT id FROM users WHERE id IN(:ids) AND age > :time")
                 abstract List<Integer> selectAllIds(long time, List<Integer> ids);
                 """
-        ) { writer ->
+        ) { _, writer ->
             val scope = testCodeGenScope()
             writer.prepareReadAndBind("_sql", "_stmt", scope)
             assertThat(scope.builder().build().toString().trim(), `is`(collectionOut))
@@ -204,7 +214,7 @@
                 @Query("SELECT id FROM users WHERE id IN(:ids) AND age > :time")
                 abstract ImmutableList<Integer> selectAllIds(long time, List<Integer> ids);
                 """
-        ) { writer ->
+        ) { _, writer ->
             val scope = testCodeGenScope()
             writer.prepareReadAndBind("_sql", "_stmt", scope)
             assertThat(scope.builder().build().toString().trim(), `is`(collectionOut))
@@ -218,7 +228,7 @@
                 @Query("SELECT id FROM users WHERE id IN(:ids) AND age > :time")
                 abstract List<Integer> selectAllIds(long time, Set<Integer> ids);
                 """
-        ) { writer ->
+        ) { _, writer ->
             val scope = testCodeGenScope()
             writer.prepareReadAndBind("_sql", "_stmt", scope)
             assertThat(scope.builder().build().toString().trim(), `is`(collectionOut))
@@ -232,7 +242,7 @@
                 @Query("SELECT id FROM users WHERE age > :age OR bage > :age")
                 abstract List<Integer> selectAllIds(int age);
                 """
-        ) { writer ->
+        ) { _, writer ->
             val scope = testCodeGenScope()
             writer.prepareReadAndBind("_sql", "_stmt", scope)
             assertThat(
@@ -258,21 +268,21 @@
                 @Query("SELECT id FROM users WHERE age > :age OR bage > :age OR fage IN(:ages)")
                 abstract List<Integer> selectAllIds(int age, int... ages);
                 """
-        ) { writer ->
+        ) { _, writer ->
             val scope = testCodeGenScope()
             writer.prepareReadAndBind("_sql", "_stmt", scope)
             assertThat(
                 scope.builder().build().toString().trim(),
                 `is`(
                     """
-                    java.lang.StringBuilder _stringBuilder = $STRING_UTIL.newStringBuilder();
+                    final java.lang.StringBuilder _stringBuilder = ${STRING_UTIL.toJavaPoet()}.newStringBuilder();
                     _stringBuilder.append("SELECT id FROM users WHERE age > ");
                     _stringBuilder.append("?");
                     _stringBuilder.append(" OR bage > ");
                     _stringBuilder.append("?");
                     _stringBuilder.append(" OR fage IN(");
                     final int _inputSize = ages.length;
-                    $STRING_UTIL.appendPlaceholders(_stringBuilder, _inputSize);
+                    ${STRING_UTIL.toJavaPoet()}.appendPlaceholders(_stringBuilder, _inputSize);
                     _stringBuilder.append(")");
                     final java.lang.String _sql = _stringBuilder.toString();
                     final int _argCount = 2 + _inputSize;
@@ -299,22 +309,22 @@
                 @Query("SELECT id FROM users WHERE age IN (:ages) OR bage > :age OR fage IN(:ages)")
                 abstract List<Integer> selectAllIds(int age, int... ages);
                 """
-        ) { writer ->
+        ) { _, writer ->
             val scope = testCodeGenScope()
             writer.prepareReadAndBind("_sql", "_stmt", scope)
             assertThat(
                 scope.builder().build().toString().trim(),
                 `is`(
                     """
-                    java.lang.StringBuilder _stringBuilder = $STRING_UTIL.newStringBuilder();
+                    final java.lang.StringBuilder _stringBuilder = ${STRING_UTIL.toJavaPoet()}.newStringBuilder();
                     _stringBuilder.append("SELECT id FROM users WHERE age IN (");
                     final int _inputSize = ages.length;
-                    $STRING_UTIL.appendPlaceholders(_stringBuilder, _inputSize);
+                    ${STRING_UTIL.toJavaPoet()}.appendPlaceholders(_stringBuilder, _inputSize);
                     _stringBuilder.append(") OR bage > ");
                     _stringBuilder.append("?");
                     _stringBuilder.append(" OR fage IN(");
                     final int _inputSize_1 = ages.length;
-                    $STRING_UTIL.appendPlaceholders(_stringBuilder, _inputSize_1);
+                    ${STRING_UTIL.toJavaPoet()}.appendPlaceholders(_stringBuilder, _inputSize_1);
                     _stringBuilder.append(")");
                     final java.lang.String _sql = _stringBuilder.toString();
                     final int _argCount = 1 + _inputSize + _inputSize_1;
@@ -339,7 +349,7 @@
 
     fun singleQueryMethod(
         vararg input: String,
-        handler: (QueryWriter) -> Unit
+        handler: (Boolean, QueryWriter) -> Unit
     ) {
         val source = Source.java(
             "foo.bar.MyClass",
@@ -365,7 +375,7 @@
                 executableElement = methods.first()
             )
             val method = parser.process()
-            handler(QueryWriter(method))
+            handler(invocation.isKsp, QueryWriter(method))
         }
     }
 }
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoWriterTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoWriterTest.kt
index 9b43506..1d77176 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoWriterTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoWriterTest.kt
@@ -18,11 +18,12 @@
 
 import COMMON
 import androidx.room.compiler.codegen.CodeLanguage
+import androidx.room.compiler.codegen.toJavaPoet
 import androidx.room.compiler.processing.XTypeElement
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.XTestInvocation
 import androidx.room.compiler.processing.util.runProcessorTest
-import androidx.room.ext.RoomTypeNames
+import androidx.room.ext.RoomTypeNames.ROOM_DB
 import androidx.room.processor.DaoProcessor
 import androidx.room.testing.context
 import createVerifierFromEntitiesAndViews
@@ -46,10 +47,11 @@
                 qName = "foo.bar.ComplexDao"
             )
         ) {
+            val backendFolder = backendFolder(it)
             it.assertCompilationResult {
                 generatedSource(
                     loadTestSource(
-                        fileName = "daoWriter/output/ComplexDao.java",
+                        fileName = "daoWriter/output/$backendFolder/ComplexDao.java",
                         qName = "foo.bar.ComplexDao_Impl"
                     )
                 )
@@ -76,10 +78,11 @@
                 qName = "foo.bar.WriterDao"
             )
         ) {
+            val backendFolder = backendFolder(it)
             it.assertCompilationResult {
                 generatedSource(
                     loadTestSource(
-                        fileName = "daoWriter/output/WriterDao.java",
+                        fileName = "daoWriter/output/$backendFolder/WriterDao.java",
                         qName = "foo.bar.WriterDao_Impl"
                     )
                 )
@@ -95,10 +98,11 @@
                 qName = "foo.bar.DeletionDao"
             )
         ) {
+            val backendFolder = backendFolder(it)
             it.assertCompilationResult {
                 generatedSource(
                     loadTestSource(
-                        fileName = "daoWriter/output/DeletionDao.java",
+                        fileName = "daoWriter/output/$backendFolder/DeletionDao.java",
                         qName = "foo.bar.DeletionDao_Impl"
                     )
                 )
@@ -114,10 +118,11 @@
                 qName = "foo.bar.UpdateDao"
             )
         ) {
+            val backendFolder = backendFolder(it)
             it.assertCompilationResult {
                 generatedSource(
                     loadTestSource(
-                        fileName = "daoWriter/output/UpdateDao.java",
+                        fileName = "daoWriter/output/$backendFolder/UpdateDao.java",
                         qName = "foo.bar.UpdateDao_Impl"
                     )
                 )
@@ -133,10 +138,11 @@
                 qName = "foo.bar.UpsertDao"
             )
         ) {
+            val backendFolder = backendFolder(it)
             it.assertCompilationResult {
                 generatedSource(
                     loadTestSource(
-                        fileName = "daoWriter/output/UpsertDao.java",
+                        fileName = "daoWriter/output/$backendFolder/UpsertDao.java",
                         qName = "foo.bar.UpsertDao_Impl"
                     )
                 )
@@ -168,7 +174,7 @@
                         androidx.room.Database::class.qualifiedName!!
                     ).filterIsInstance<XTypeElement>().firstOrNull()
                     ?: invocation.context.processingEnv
-                        .requireTypeElement(RoomTypeNames.ROOM_DB)
+                        .requireTypeElement(ROOM_DB.toJavaPoet())
                 val dbType = db.type
                 val dbVerifier = createVerifierFromEntitiesAndViews(invocation)
                 invocation.context.attachDatabaseVerifier(dbVerifier)
@@ -187,4 +193,7 @@
             handler(invocation)
         }
     }
+
+    private fun backendFolder(invocation: XTestInvocation) =
+        invocation.processingEnv.backend.name.lowercase()
 }
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 ecbec9c..4a7cc1d 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
@@ -22,7 +22,7 @@
 import androidx.room.compiler.processing.XTypeElement
 import androidx.room.compiler.processing.util.Source
 import androidx.room.compiler.processing.util.runKaptTest
-import androidx.room.ext.RoomTypeNames
+import androidx.room.ext.RoomTypeNames.ROOM_DB
 import androidx.room.processor.DaoProcessor
 import androidx.room.testing.context
 import com.google.common.truth.StringSubject
@@ -157,7 +157,7 @@
                 ).filterIsInstance<XTypeElement>()
                 .forEach { dao ->
                     val db = invocation.context.processingEnv
-                        .requireTypeElement(RoomTypeNames.ROOM_DB)
+                        .requireTypeElement(ROOM_DB.toJavaPoet())
                     val dbType = db.type
                     val parser = DaoProcessor(
                         baseContext = invocation.context,
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/writer/EntityCursorConverterWriterTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/writer/EntityCursorConverterWriterTest.kt
index b8076ef..84e5141 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/writer/EntityCursorConverterWriterTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/writer/EntityCursorConverterWriterTest.kt
@@ -34,12 +34,13 @@
         val OUT_PREFIX = """
             package foo.bar;
             import android.database.Cursor;
+            import androidx.annotation.NonNull;
             import androidx.room.util.CursorUtil;
             import java.lang.SuppressWarnings;
             import javax.annotation.processing.Generated;
             @Generated("androidx.room.RoomProcessor")
             @SuppressWarnings({"unchecked", "deprecation"})
-            public class MyContainerClass {
+            public final class MyContainerClass {
         """.trimIndent()
         const val OUT_SUFFIX = "}"
     }
@@ -47,7 +48,7 @@
     @Test
     fun generateSimple() {
         generateAndMatch(
-            """
+            input = """
                 @PrimaryKey
                 private int id;
                 String name;
@@ -55,54 +56,60 @@
                 int age;
                 public int getId() { return id; }
                 public void setId(int id) { this.id = id; }
-                """,
-            """
-                private MyEntity __entityCursorConverter_fooBarMyEntity(Cursor cursor) {
-                  final MyEntity _entity;
-                  final int _cursorIndexOfId = CursorUtil.getColumnIndex(cursor, "id");
-                  final int _cursorIndexOfName = CursorUtil.getColumnIndex(cursor, "name");
-                  final int _cursorIndexOfLastName = CursorUtil.getColumnIndex(cursor, "lastName");
-                  final int _cursorIndexOfAge = CursorUtil.getColumnIndex(cursor, "age");
-                  _entity = new MyEntity();
-                  if (_cursorIndexOfId != -1) {
-                    final int _tmpId;
-                    _tmpId = cursor.getInt(_cursorIndexOfId);
-                    _entity.setId(_tmpId);
-                  }
-                  if (_cursorIndexOfName != -1) {
-                    if (cursor.isNull(_cursorIndexOfName)) {
-                      _entity.name = null;
+                """.trimIndent(),
+            output = { isKsp ->
+                fun stringAdapterCode(out: String, indexVar: String) = if (isKsp) {
+                    """
+                    $out = cursor.getString($indexVar);
+                    """.trimIndent()
+                } else {
+                    """
+                    if (cursor.isNull($indexVar)) {
+                      $out = null;
                     } else {
-                      _entity.name = cursor.getString(_cursorIndexOfName);
+                      $out = cursor.getString($indexVar);
                     }
-                  }
-                  if (_cursorIndexOfLastName != -1) {
-                    if (cursor.isNull(_cursorIndexOfLastName)) {
-                      _entity.lastName = null;
-                    } else {
-                      _entity.lastName = cursor.getString(_cursorIndexOfLastName);
-                    }
-                  }
-                  if (_cursorIndexOfAge != -1) {
-                    _entity.age = cursor.getInt(_cursorIndexOfAge);
-                  }
-                  return _entity;
+                    """.trimIndent()
                 }
-            """.trimIndent()
+                """
+                |private MyEntity __entityCursorConverter_fooBarMyEntity(@NonNull final Cursor cursor) {
+                |  final MyEntity _entity;
+                |  final int _cursorIndexOfId = CursorUtil.getColumnIndex(cursor, "id");
+                |  final int _cursorIndexOfName = CursorUtil.getColumnIndex(cursor, "name");
+                |  final int _cursorIndexOfLastName = CursorUtil.getColumnIndex(cursor, "lastName");
+                |  final int _cursorIndexOfAge = CursorUtil.getColumnIndex(cursor, "age");
+                |  _entity = new MyEntity();
+                |  if (_cursorIndexOfId != -1) {
+                |    final int _tmpId;
+                |    _tmpId = cursor.getInt(_cursorIndexOfId);
+                |    _entity.setId(_tmpId);
+                |  }
+                |  if (_cursorIndexOfName != -1) {
+                |    ${stringAdapterCode("_entity.name", "_cursorIndexOfName")}
+                |  }
+                |  if (_cursorIndexOfLastName != -1) {
+                |    ${stringAdapterCode("_entity.lastName", "_cursorIndexOfLastName")}
+                |  }
+                |  if (_cursorIndexOfAge != -1) {
+                |    _entity.age = cursor.getInt(_cursorIndexOfAge);
+                |  }
+                |  return _entity;
+                |}
+                """.trimMargin()
+            }
         )
     }
 
     private fun generateAndMatch(
         input: String,
-        output: String,
-        attributes: Map<String, String> = mapOf()
+        output: (Boolean) -> String,
     ) {
-        generate(input, attributes) {
+        generate(input) {
             it.assertCompilationResult {
                 generatedSource(
                     Source.java(
                         qName = "foo.bar.MyContainerClass",
-                        code = listOf(OUT_PREFIX, output, OUT_SUFFIX).joinToString("\n")
+                        code = listOf(OUT_PREFIX, output(it.isKsp), OUT_SUFFIX).joinToString("\n")
                     )
                 )
             }
@@ -111,14 +118,13 @@
 
     private fun generate(
         input: String,
-        attributes: Map<String, String> = mapOf(),
         handler: (XTestInvocation) -> Unit
     ) {
-        singleEntity(input, attributes) { entity, invocation ->
+        singleEntity(input) { entity, invocation ->
             val className = XClassName.get("foo.bar", "MyContainerClass")
             val writer = object : TypeWriter(CodeLanguage.JAVA) {
                 override fun createTypeSpecBuilder(): XTypeSpec.Builder {
-                    getOrCreateMethod(EntityCursorConverterWriter(entity))
+                    getOrCreateFunction(EntityCursorConverterWriter(entity))
                     return XTypeSpec.classBuilder(codeLanguage, className)
                         .apply(
                             javaTypeBuilder = { addModifiers(Modifier.PUBLIC) },
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/writer/KotlinCodeGenTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/writer/KotlinCodeGenTest.kt
index 0e920dd..807d4f6 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/writer/KotlinCodeGenTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/writer/KotlinCodeGenTest.kt
@@ -72,6 +72,270 @@
         )
     }
 
+    @Test
+    fun pojoRowAdapter_primitives_nullable() {
+        val testName = object {}.javaClass.enclosingMethod!!.name
+        val src = Source.kotlin(
+            "MyDao.kt",
+            """
+            import androidx.room.*
+
+            @Dao
+            interface MyDao {
+              @Query("SELECT * FROM MyEntity")
+              fun getEntity(): MyEntity
+            }
+
+            @Entity
+            data class MyEntity(
+                @PrimaryKey
+                val int: Int?,
+                val short: Short?,
+                val byte: Byte?,
+                val long: Long?,
+                val char: Char?,
+                val float: Float?,
+                val double: Double?,
+            )
+            """.trimIndent()
+        )
+        runTest(
+            sources = listOf(src, databaseSrc),
+            expectedFilePath = getTestGoldenPath(testName)
+        )
+    }
+
+    @Test
+    fun pojoRowAdapter_boolean() {
+        val testName = object {}.javaClass.enclosingMethod!!.name
+        val src = Source.kotlin(
+            "MyDao.kt",
+            """
+            import androidx.room.*
+
+            @Dao
+            interface MyDao {
+              @Query("SELECT * FROM MyEntity")
+              fun getEntity(): MyEntity
+            }
+
+            @Entity
+            data class MyEntity(
+                @PrimaryKey
+                val pk: Int,
+                val boolean: Boolean,
+                val nullableBoolean: Boolean?,
+            )
+            """.trimIndent()
+        )
+        runTest(
+            sources = listOf(src, databaseSrc),
+            expectedFilePath = getTestGoldenPath(testName)
+        )
+    }
+
+    @Test
+    fun pojoRowAdapter_string() {
+        val testName = object {}.javaClass.enclosingMethod!!.name
+        val src = Source.kotlin(
+            "MyDao.kt",
+            """
+            import androidx.room.*
+
+            @Dao
+            interface MyDao {
+              @Query("SELECT * FROM MyEntity")
+              fun getEntity(): MyEntity
+            }
+
+            @Entity
+            data class MyEntity(
+                @PrimaryKey
+                val string: String,
+                val nullableString: String?,
+            )
+            """.trimIndent()
+        )
+        runTest(
+            sources = listOf(src, databaseSrc),
+            expectedFilePath = getTestGoldenPath(testName)
+        )
+    }
+
+    @Test
+    fun pojoRowAdapter_byteArray() {
+        val testName = object {}.javaClass.enclosingMethod!!.name
+        val src = Source.kotlin(
+            "MyDao.kt",
+            """
+            import androidx.room.*
+
+            @Dao
+            interface MyDao {
+              @Query("SELECT * FROM MyEntity")
+              fun getEntity(): MyEntity
+            }
+
+            @Entity
+            data class MyEntity(
+                @PrimaryKey
+                val pk: Int,
+                val byteArray: ByteArray,
+                val nullableByteArray: ByteArray?,
+            )
+            """.trimIndent()
+        )
+        runTest(
+            sources = listOf(src, databaseSrc),
+            expectedFilePath = getTestGoldenPath(testName)
+        )
+    }
+
+    @Test
+    fun pojoRowAdapter_enum() {
+        val testName = object {}.javaClass.enclosingMethod!!.name
+        val src = Source.kotlin(
+            "MyDao.kt",
+            """
+            import androidx.room.*
+
+            @Dao
+            interface MyDao {
+              @Query("SELECT * FROM MyEntity")
+              fun getEntity(): MyEntity
+            }
+
+            @Entity
+            data class MyEntity(
+                @PrimaryKey
+                val pk: Int,
+                val enum: Fruit,
+                val nullableEnum: Fruit?,
+            )
+
+            enum class Fruit {
+                APPLE,
+                BANANA
+            }
+            """.trimIndent()
+        )
+        runTest(
+            sources = listOf(src, databaseSrc),
+            expectedFilePath = getTestGoldenPath(testName)
+        )
+    }
+
+    @Test
+    fun pojoRowAdapter_uuid() {
+        val testName = object {}.javaClass.enclosingMethod!!.name
+        val src = Source.kotlin(
+            "MyDao.kt",
+            """
+            import androidx.room.*
+            import java.util.UUID
+
+            @Dao
+            interface MyDao {
+              @Query("SELECT * FROM MyEntity")
+              fun getEntity(): MyEntity
+            }
+
+            @Entity
+            data class MyEntity(
+                @PrimaryKey
+                val pk: Int,
+                val uuid: UUID,
+                val nullableUuid: UUID?,
+            )
+
+            enum class Fruit {
+                APPLE,
+                BANANA
+            }
+            """.trimIndent()
+        )
+        runTest(
+            sources = listOf(src, databaseSrc),
+            expectedFilePath = getTestGoldenPath(testName)
+        )
+    }
+
+    @Test
+    fun pojoRowAdapter_embedded() {
+        val testName = object {}.javaClass.enclosingMethod!!.name
+        val src = Source.kotlin(
+            "MyDao.kt",
+            """
+            import androidx.room.*
+            import java.util.UUID
+
+            @Dao
+            interface MyDao {
+              @Query("SELECT * FROM MyEntity")
+              fun getEntity(): MyEntity
+            }
+
+            @Entity
+            data class MyEntity(
+                @PrimaryKey
+                val pk: Int,
+                @Embedded
+                val foo: Foo,
+                @Embedded(prefix = "nullable")
+                val nullableFoo: Foo?,
+            )
+
+            data class Foo(
+                val numberData: Long,
+                val stringData: String
+            )
+            """.trimIndent()
+        )
+        runTest(
+            sources = listOf(src, databaseSrc),
+            expectedFilePath = getTestGoldenPath(testName)
+        )
+    }
+
+    @Test
+    fun pojoRowAdapter_customTypeConverter() {
+        val testName = object {}.javaClass.enclosingMethod!!.name
+        val src = Source.kotlin(
+            "MyDao.kt",
+            """
+            import androidx.room.*
+            import java.util.UUID
+
+            @Dao
+            interface MyDao {
+              @Query("SELECT * FROM MyEntity")
+              fun getEntity(): MyEntity
+            }
+
+            @Entity
+            @TypeConverters(FooConverter::class)
+            data class MyEntity(
+                @PrimaryKey
+                val pk: Int,
+                val foo: Foo,
+            )
+
+            data class Foo(val data: String)
+
+            class FooConverter {
+                @TypeConverter
+                fun fromString(data: String): Foo = Foo(data)
+                @TypeConverter
+                fun toString(foo: Foo): String = foo.data
+            }
+            """.trimIndent()
+        )
+        runTest(
+            sources = listOf(src, databaseSrc),
+            expectedFilePath = getTestGoldenPath(testName)
+        )
+    }
+
     private fun getTestGoldenPath(testName: String): String {
         return "kotlinCodeGen/$testName.kt"
     }
@@ -95,7 +359,7 @@
                 this.generatedSource(
                     loadTestSource(
                         expectedFilePath,
-                        "androidx.room.temp.PojoRowAdapter_1427165205"
+                        "MyDao_Impl"
                     )
                 )
                 this.hasNoWarnings()
diff --git a/room/room-compiler/src/test/test-data/autoMigrationWriter/output/java/AutoMigrationWithProvidedSpec.java b/room/room-compiler/src/test/test-data/autoMigrationWriter/output/java/AutoMigrationWithProvidedSpec.java
index 46162d9..291dfc9 100644
--- a/room/room-compiler/src/test/test-data/autoMigrationWriter/output/java/AutoMigrationWithProvidedSpec.java
+++ b/room/room-compiler/src/test/test-data/autoMigrationWriter/output/java/AutoMigrationWithProvidedSpec.java
@@ -10,7 +10,7 @@
 
 @Generated("androidx.room.RoomProcessor")
 @SuppressWarnings({"unchecked", "deprecation"})
-class MyDatabase_AutoMigration_1_2_Impl extends Migration {
+final class MyDatabase_AutoMigration_1_2_Impl extends Migration {
     private final AutoMigrationSpec callback;
 
     public MyDatabase_AutoMigration_1_2_Impl(@NonNull final AutoMigrationSpec callback) {
diff --git a/room/room-compiler/src/test/test-data/autoMigrationWriter/output/java/ValidAutoMigrationWithDefault.java b/room/room-compiler/src/test/test-data/autoMigrationWriter/output/java/ValidAutoMigrationWithDefault.java
index 075d606..04f2885 100644
--- a/room/room-compiler/src/test/test-data/autoMigrationWriter/output/java/ValidAutoMigrationWithDefault.java
+++ b/room/room-compiler/src/test/test-data/autoMigrationWriter/output/java/ValidAutoMigrationWithDefault.java
@@ -10,7 +10,7 @@
 
 @Generated("androidx.room.RoomProcessor")
 @SuppressWarnings({"unchecked", "deprecation"})
-class MyDatabase_AutoMigration_1_2_Impl extends Migration {
+final class MyDatabase_AutoMigration_1_2_Impl extends Migration {
     private final AutoMigrationSpec callback = new ValidAutoMigrationWithDefault();
 
     public MyDatabase_AutoMigration_1_2_Impl() {
diff --git a/room/room-compiler/src/test/test-data/autoMigrationWriter/output/java/ValidAutoMigrationWithoutDefault.java b/room/room-compiler/src/test/test-data/autoMigrationWriter/output/java/ValidAutoMigrationWithoutDefault.java
index 678c54c..e1b545d 100644
--- a/room/room-compiler/src/test/test-data/autoMigrationWriter/output/java/ValidAutoMigrationWithoutDefault.java
+++ b/room/room-compiler/src/test/test-data/autoMigrationWriter/output/java/ValidAutoMigrationWithoutDefault.java
@@ -10,7 +10,7 @@
 
 @Generated("androidx.room.RoomProcessor")
 @SuppressWarnings({"unchecked", "deprecation"})
-class MyDatabase_AutoMigration_1_2_Impl extends Migration {
+final class MyDatabase_AutoMigration_1_2_Impl extends Migration {
     private final AutoMigrationSpec callback = new ValidAutoMigrationWithoutDefault();
 
     public MyDatabase_AutoMigration_1_2_Impl() {
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/UpsertDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/UpsertDao.java
deleted file mode 100644
index b3a0a27..0000000
--- a/room/room-compiler/src/test/test-data/daoWriter/output/UpsertDao.java
+++ /dev/null
@@ -1,189 +0,0 @@
-package foo.bar;
-
-import androidx.room.EntityDeletionOrUpdateAdapter;
-import androidx.room.EntityInsertionAdapter;
-import androidx.room.EntityUpsertionAdapter;
-import androidx.room.RoomDatabase;
-import androidx.sqlite.db.SupportSQLiteStatement;
-import java.lang.Class;
-import java.lang.Override;
-import java.lang.String;
-import java.lang.SuppressWarnings;
-import java.util.Collections;
-import java.util.List;
-import javax.annotation.processing.Generated;
-
-@Generated("androidx.room.RoomProcessor")
-@SuppressWarnings({"unchecked", "deprecation"})
-public final class UpsertDao_Impl implements UpsertDao {
-    private final RoomDatabase __db;
-
-    private final EntityUpsertionAdapter<User> __upsertionAdapterOfUser;
-
-    private final EntityUpsertionAdapter<Book> __upsertionAdapterOfBook;
-
-    public UpsertDao_Impl(RoomDatabase __db) {
-        this.__db = __db;
-        this.__upsertionAdapterOfUser = new EntityUpsertionAdapter<User>(new EntityInsertionAdapter<User>(__db) {
-                    @Override
-                    public String createQuery() {
-                        return "INSERT INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
-                    }
-
-                    @Override
-                    public void bind(SupportSQLiteStatement stmt, User value) {
-                        stmt.bindLong(1, value.uid);
-                        if (value.name == null) {
-                            stmt.bindNull(2);
-                        } else {
-                            stmt.bindString(2, value.name);
-                        }
-                        if (value.getLastName() == null) {
-                            stmt.bindNull(3);
-                        } else {
-                            stmt.bindString(3, value.getLastName());
-                        }
-                        stmt.bindLong(4, value.age);
-                    }
-                }, new EntityDeletionOrUpdateAdapter<User>(__db) {
-                    @Override
-                    public String createQuery() {
-                        return "UPDATE `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
-                    }
-
-                    @Override
-                    public void bind(SupportSQLiteStatement stmt, User value) {
-                        stmt.bindLong(1, value.uid);
-                        if (value.name == null) {
-                            stmt.bindNull(2);
-                        } else {
-                            stmt.bindString(2, value.name);
-                        }
-                        if (value.getLastName() == null) {
-                            stmt.bindNull(3);
-                        } else {
-                            stmt.bindString(3, value.getLastName());
-                        }
-                        stmt.bindLong(4, value.age);
-                        stmt.bindLong(5, value.uid);
-                    }
-        });
-        this.__upsertionAdapterOfBook = new EntityUpsertionAdapter<Book>(new EntityInsertionAdapter<Book>(__db) {
-                    @Override
-                    public String createQuery() {
-                        return "INSERT INTO `Book` (`bookId`,`uid`) VALUES (?,?)";
-                    }
-
-                    @Override
-                    public void bind(SupportSQLiteStatement stmt, Book value) {
-                        stmt.bindLong(1, value.bookId);
-                        stmt.bindLong(2, value.uid);
-                    }
-                }, new EntityDeletionOrUpdateAdapter<Book>(__db) {
-                    @Override
-                    public String createQuery() {
-                        return "UPDATE `Book` SET `bookId` = ?,`uid` = ? WHERE `bookId` = ?";
-                    }
-
-                    @Override
-                    public void bind(SupportSQLiteStatement stmt, Book value) {
-                        stmt.bindLong(1, value.bookId);
-                        stmt.bindLong(2, value.uid);
-                        stmt.bindLong(3, value.bookId);
-                    }
-        });
-    }
-
-    @Override
-    public void upsertUser(final User user) {
-        __db.assertNotSuspendingTransaction();
-        __db.beginTransaction();
-        try {
-            __upsertionAdapterOfUser.upsert(user);
-            __db.setTransactionSuccessful();
-        } finally {
-            __db.endTransaction();
-        }
-    }
-
-    @Override
-    public void upsertUsers(final User user1, final List<User> others) {
-        __db.assertNotSuspendingTransaction();
-        __db.beginTransaction();
-        try {
-            __upsertionAdapterOfUser.upsert(user1);
-            __upsertionAdapterOfUser.upsert(others);
-            __db.setTransactionSuccessful();
-        } finally {
-            __db.endTransaction();
-        }
-    }
-
-    @Override
-    public void upsertUsers(final User[] users) {
-        __db.assertNotSuspendingTransaction();
-        __db.beginTransaction();
-        try {
-            __upsertionAdapterOfUser.upsert(users);
-            __db.setTransactionSuccessful();
-        } finally {
-            __db.endTransaction();
-        }
-    }
-
-    @Override
-    public void upsertTwoUsers(final User userOne, final User userTwo) {
-        __db.assertNotSuspendingTransaction();
-        __db.beginTransaction();
-        try {
-            __upsertionAdapterOfUser.upsert(userOne);
-            __upsertionAdapterOfUser.upsert(userTwo);
-            __db.setTransactionSuccessful();
-        } finally {
-            __db.endTransaction();
-        }
-    }
-
-    @Override
-    public void upsertUserAndBook(final User user, final Book book) {
-        __db.assertNotSuspendingTransaction();
-        __db.beginTransaction();
-        try {
-            __upsertionAdapterOfUser.upsert(user);
-            __upsertionAdapterOfBook.upsert(book);
-            __db.setTransactionSuccessful();
-        } finally {
-            __db.endTransaction();
-        }
-    }
-
-    @Override
-    public long upsertAndReturnId(final User user) {
-        __db.assertNotSuspendingTransaction();
-        __db.beginTransaction();
-        try {
-            long _result = __upsertionAdapterOfUser.upsertAndReturnId(user);
-            __db.setTransactionSuccessful();
-            return _result;
-        } finally {
-            __db.endTransaction();
-        }
-    }
-
-    @Override
-    public long[] upsertAndReturnIdsArray(final User[] users) {
-        __db.assertNotSuspendingTransaction();
-        __db.beginTransaction();
-        try {
-            long[] _result = __upsertionAdapterOfUser.upsertAndReturnIdsArray(users);
-            __db.setTransactionSuccessful();
-            return _result;
-        } finally {
-            __db.endTransaction();
-        }
-    }
-
-    public static List<Class<?>> getRequiredConverters() {
-        return Collections.emptyList();
-    }
-}
\ No newline at end of file
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/ComplexDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/ComplexDao.java
similarity index 97%
rename from room/room-compiler/src/test/test-data/daoWriter/output/ComplexDao.java
rename to room/room-compiler/src/test/test-data/daoWriter/output/javac/ComplexDao.java
index c9f0981c7..4d6feda 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/ComplexDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/ComplexDao.java
@@ -2,6 +2,7 @@
 
 import android.database.Cursor;
 import android.os.CancellationSignal;
+import androidx.annotation.NonNull;
 import androidx.lifecycle.LiveData;
 import androidx.room.RoomDatabase;
 import androidx.room.RoomSQLiteQuery;
@@ -29,7 +30,7 @@
 public final class ComplexDao_Impl extends ComplexDao {
     private final RoomDatabase __db;
 
-    public ComplexDao_Impl(ComplexDatabase __db) {
+    public ComplexDao_Impl(final ComplexDatabase __db) {
         super(__db);
         this.__db = __db;
     }
@@ -90,7 +91,7 @@
             final int _cursorIndexOfLastName = CursorUtil.getColumnIndexOrThrow(_cursor, "lastName");
             final int _cursorIndexOfAge = CursorUtil.getColumnIndexOrThrow(_cursor, "ageColumn");
             final User _result;
-            if(_cursor.moveToFirst()) {
+            if (_cursor.moveToFirst()) {
                 _result = new User();
                 _result.uid = _cursor.getInt(_cursorIndexOfUid);
                 if (_cursor.isNull(_cursorIndexOfName)) {
@@ -140,7 +141,7 @@
             final int _cursorIndexOfLastName = CursorUtil.getColumnIndexOrThrow(_cursor, "lastName");
             final int _cursorIndexOfAge = CursorUtil.getColumnIndexOrThrow(_cursor, "ageColumn");
             final User _result;
-            if(_cursor.moveToFirst()) {
+            if (_cursor.moveToFirst()) {
                 _result = new User();
                 _result.uid = _cursor.getInt(_cursorIndexOfUid);
                 if (_cursor.isNull(_cursorIndexOfName)) {
@@ -168,7 +169,7 @@
 
     @Override
     public List<User> loadAllByIds(final int... ids) {
-        StringBuilder _stringBuilder = StringUtil.newStringBuilder();
+        final StringBuilder _stringBuilder = StringUtil.newStringBuilder();
         _stringBuilder.append("SELECT * FROM user where uid IN (");
         final int _inputSize = ids.length;
         StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
@@ -225,7 +226,7 @@
         final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
         try {
             final int _result;
-            if(_cursor.moveToFirst()) {
+            if (_cursor.moveToFirst()) {
                 _result = _cursor.getInt(0);
             } else {
                 _result = 0;
@@ -239,7 +240,7 @@
 
     @Override
     public int[] getAllAges(final int... ids) {
-        StringBuilder _stringBuilder = StringUtil.newStringBuilder();
+        final StringBuilder _stringBuilder = StringUtil.newStringBuilder();
         _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
         final int _inputSize = ids.length;
         StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
@@ -272,7 +273,7 @@
 
     @Override
     public List<Integer> getAllAgesAsList(final List<Integer> ids) {
-        StringBuilder _stringBuilder = StringUtil.newStringBuilder();
+        final StringBuilder _stringBuilder = StringUtil.newStringBuilder();
         _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
         final int _inputSize = ids.size();
         StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
@@ -312,7 +313,7 @@
     @Override
     public List<Integer> getAllAgesAsList(final List<Integer> ids1, final int[] ids2,
             final int... ids3) {
-        StringBuilder _stringBuilder = StringUtil.newStringBuilder();
+        final StringBuilder _stringBuilder = StringUtil.newStringBuilder();
         _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
         final int _inputSize = ids1.size();
         StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
@@ -381,7 +382,7 @@
                     final int _cursorIndexOfLastName = CursorUtil.getColumnIndexOrThrow(_cursor, "lastName");
                     final int _cursorIndexOfAge = CursorUtil.getColumnIndexOrThrow(_cursor, "ageColumn");
                     final User _result;
-                    if(_cursor.moveToFirst()) {
+                    if (_cursor.moveToFirst()) {
                         _result = new User();
                         _result.uid = _cursor.getInt(_cursorIndexOfUid);
                         if (_cursor.isNull(_cursorIndexOfName)) {
@@ -415,7 +416,7 @@
 
     @Override
     public LiveData<List<User>> loadUsersByIdsLive(final int... ids) {
-        StringBuilder _stringBuilder = StringUtil.newStringBuilder();
+        final StringBuilder _stringBuilder = StringUtil.newStringBuilder();
         _stringBuilder.append("SELECT * FROM user where uid IN (");
         final int _inputSize = ids.length;
         StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
@@ -641,7 +642,7 @@
         final Cursor _cursor = DBUtil.query(__db, _internalQuery, false, null);
         try {
             final User _result;
-            if(_cursor.moveToFirst()) {
+            if (_cursor.moveToFirst()) {
                 _result = __entityCursorConverter_fooBarUser(_cursor);
             } else {
                 _result = null;
@@ -652,11 +653,12 @@
         }
     }
 
+    @NonNull
     public static List<Class<?>> getRequiredConverters() {
         return Collections.emptyList();
     }
 
-    private User __entityCursorConverter_fooBarUser(Cursor cursor) {
+    private User __entityCursorConverter_fooBarUser(@NonNull final Cursor cursor) {
         final User _entity;
         final int _cursorIndexOfUid = CursorUtil.getColumnIndex(cursor, "uid");
         final int _cursorIndexOfName = CursorUtil.getColumnIndex(cursor, "name");
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/DeletionDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/DeletionDao.java
similarity index 98%
rename from room/room-compiler/src/test/test-data/daoWriter/output/DeletionDao.java
rename to room/room-compiler/src/test/test-data/daoWriter/output/javac/DeletionDao.java
index 04979c9..bd7ff8d 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/DeletionDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/DeletionDao.java
@@ -1,5 +1,6 @@
 package foo.bar;
 
+import androidx.annotation.NonNull;
 import androidx.room.EntityDeletionOrUpdateAdapter;
 import androidx.room.RoomDatabase;
 import androidx.room.SharedSQLiteStatement;
@@ -36,7 +37,7 @@
 
   private final SharedSQLiteStatement __preparedStmtOfDeleteEverything;
 
-  public DeletionDao_Impl(RoomDatabase __db) {
+  public DeletionDao_Impl(@NonNull final RoomDatabase __db) {
     this.__db = __db;
     this.__deletionAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
       @Override
@@ -368,7 +369,7 @@
   @Override
   public int deleteByUidList(final int... uid) {
     __db.assertNotSuspendingTransaction();
-    StringBuilder _stringBuilder = StringUtil.newStringBuilder();
+    final StringBuilder _stringBuilder = StringUtil.newStringBuilder();
     _stringBuilder.append("DELETE FROM user where uid IN(");
     final int _inputSize = uid.length;
     StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
@@ -390,6 +391,7 @@
     }
   }
 
+  @NonNull
   public static List<Class<?>> getRequiredConverters() {
     return Collections.emptyList();
   }
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/UpdateDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/UpdateDao.java
similarity index 98%
rename from room/room-compiler/src/test/test-data/daoWriter/output/UpdateDao.java
rename to room/room-compiler/src/test/test-data/daoWriter/output/javac/UpdateDao.java
index 889b4fb..c019e37 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/UpdateDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/UpdateDao.java
@@ -1,5 +1,6 @@
 package foo.bar;
 
+import androidx.annotation.NonNull;
 import androidx.room.EntityDeletionOrUpdateAdapter;
 import androidx.room.RoomDatabase;
 import androidx.room.SharedSQLiteStatement;
@@ -36,7 +37,7 @@
 
   private final SharedSQLiteStatement __preparedStmtOfAgeUserAll;
 
-  public UpdateDao_Impl(RoomDatabase __db) {
+  public UpdateDao_Impl(@NonNull final RoomDatabase __db) {
     this.__db = __db;
     this.__updateAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
       @Override
@@ -432,6 +433,7 @@
     });
   }
 
+  @NonNull
   public static List<Class<?>> getRequiredConverters() {
     return Collections.emptyList();
   }
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/UpsertDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/UpsertDao.java
new file mode 100644
index 0000000..ea988e8
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/UpsertDao.java
@@ -0,0 +1,191 @@
+package foo.bar;
+
+import androidx.annotation.NonNull;
+import androidx.room.EntityDeletionOrUpdateAdapter;
+import androidx.room.EntityInsertionAdapter;
+import androidx.room.EntityUpsertionAdapter;
+import androidx.room.RoomDatabase;
+import androidx.sqlite.db.SupportSQLiteStatement;
+import java.lang.Class;
+import java.lang.Override;
+import java.lang.String;
+import java.lang.SuppressWarnings;
+import java.util.Collections;
+import java.util.List;
+import javax.annotation.processing.Generated;
+
+@Generated("androidx.room.RoomProcessor")
+@SuppressWarnings({"unchecked", "deprecation"})
+public final class UpsertDao_Impl implements UpsertDao {
+    private final RoomDatabase __db;
+
+    private final EntityUpsertionAdapter<User> __upsertionAdapterOfUser;
+
+    private final EntityUpsertionAdapter<Book> __upsertionAdapterOfBook;
+
+    public UpsertDao_Impl(@NonNull final RoomDatabase __db) {
+        this.__db = __db;
+        this.__upsertionAdapterOfUser = new EntityUpsertionAdapter<User>(new EntityInsertionAdapter<User>(__db) {
+            @Override
+            public String createQuery() {
+                return "INSERT INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
+            }
+
+            @Override
+            public void bind(SupportSQLiteStatement stmt, User value) {
+                stmt.bindLong(1, value.uid);
+                if (value.name == null) {
+                    stmt.bindNull(2);
+                } else {
+                    stmt.bindString(2, value.name);
+                }
+                if (value.getLastName() == null) {
+                    stmt.bindNull(3);
+                } else {
+                    stmt.bindString(3, value.getLastName());
+                }
+                stmt.bindLong(4, value.age);
+            }
+        }, new EntityDeletionOrUpdateAdapter<User>(__db) {
+            @Override
+            public String createQuery() {
+                return "UPDATE `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
+            }
+
+            @Override
+            public void bind(SupportSQLiteStatement stmt, User value) {
+                stmt.bindLong(1, value.uid);
+                if (value.name == null) {
+                    stmt.bindNull(2);
+                } else {
+                    stmt.bindString(2, value.name);
+                }
+                if (value.getLastName() == null) {
+                    stmt.bindNull(3);
+                } else {
+                    stmt.bindString(3, value.getLastName());
+                }
+                stmt.bindLong(4, value.age);
+                stmt.bindLong(5, value.uid);
+            }
+        });
+        this.__upsertionAdapterOfBook = new EntityUpsertionAdapter<Book>(new EntityInsertionAdapter<Book>(__db) {
+            @Override
+            public String createQuery() {
+                return "INSERT INTO `Book` (`bookId`,`uid`) VALUES (?,?)";
+            }
+
+            @Override
+            public void bind(SupportSQLiteStatement stmt, Book value) {
+                stmt.bindLong(1, value.bookId);
+                stmt.bindLong(2, value.uid);
+            }
+        }, new EntityDeletionOrUpdateAdapter<Book>(__db) {
+            @Override
+            public String createQuery() {
+                return "UPDATE `Book` SET `bookId` = ?,`uid` = ? WHERE `bookId` = ?";
+            }
+
+            @Override
+            public void bind(SupportSQLiteStatement stmt, Book value) {
+                stmt.bindLong(1, value.bookId);
+                stmt.bindLong(2, value.uid);
+                stmt.bindLong(3, value.bookId);
+            }
+        });
+    }
+
+    @Override
+    public void upsertUser(final User user) {
+        __db.assertNotSuspendingTransaction();
+        __db.beginTransaction();
+        try {
+            __upsertionAdapterOfUser.upsert(user);
+            __db.setTransactionSuccessful();
+        } finally {
+            __db.endTransaction();
+        }
+    }
+
+    @Override
+    public void upsertUsers(final User user1, final List<User> others) {
+        __db.assertNotSuspendingTransaction();
+        __db.beginTransaction();
+        try {
+            __upsertionAdapterOfUser.upsert(user1);
+            __upsertionAdapterOfUser.upsert(others);
+            __db.setTransactionSuccessful();
+        } finally {
+            __db.endTransaction();
+        }
+    }
+
+    @Override
+    public void upsertUsers(final User[] users) {
+        __db.assertNotSuspendingTransaction();
+        __db.beginTransaction();
+        try {
+            __upsertionAdapterOfUser.upsert(users);
+            __db.setTransactionSuccessful();
+        } finally {
+            __db.endTransaction();
+        }
+    }
+
+    @Override
+    public void upsertTwoUsers(final User userOne, final User userTwo) {
+        __db.assertNotSuspendingTransaction();
+        __db.beginTransaction();
+        try {
+            __upsertionAdapterOfUser.upsert(userOne);
+            __upsertionAdapterOfUser.upsert(userTwo);
+            __db.setTransactionSuccessful();
+        } finally {
+            __db.endTransaction();
+        }
+    }
+
+    @Override
+    public void upsertUserAndBook(final User user, final Book book) {
+        __db.assertNotSuspendingTransaction();
+        __db.beginTransaction();
+        try {
+            __upsertionAdapterOfUser.upsert(user);
+            __upsertionAdapterOfBook.upsert(book);
+            __db.setTransactionSuccessful();
+        } finally {
+            __db.endTransaction();
+        }
+    }
+
+    @Override
+    public long upsertAndReturnId(final User user) {
+        __db.assertNotSuspendingTransaction();
+        __db.beginTransaction();
+        try {
+            long _result = __upsertionAdapterOfUser.upsertAndReturnId(user);
+            __db.setTransactionSuccessful();
+            return _result;
+        } finally {
+            __db.endTransaction();
+        }
+    }
+
+    @Override
+    public long[] upsertAndReturnIdsArray(final User[] users) {
+        __db.assertNotSuspendingTransaction();
+        __db.beginTransaction();
+        try {
+            long[] _result = __upsertionAdapterOfUser.upsertAndReturnIdsArray(users);
+            __db.setTransactionSuccessful();
+            return _result;
+        } finally {
+            __db.endTransaction();
+        }
+    }
+
+    @NonNull
+    public static List<Class<?>> getRequiredConverters() {
+        return Collections.emptyList();
+    }
+}
\ No newline at end of file
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/WriterDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/WriterDao.java
similarity index 97%
rename from room/room-compiler/src/test/test-data/daoWriter/output/WriterDao.java
rename to room/room-compiler/src/test/test-data/daoWriter/output/javac/WriterDao.java
index deaabb5..67e893e 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/WriterDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/WriterDao.java
@@ -1,5 +1,6 @@
 package foo.bar;
 
+import androidx.annotation.NonNull;
 import androidx.room.EntityInsertionAdapter;
 import androidx.room.RoomDatabase;
 import androidx.sqlite.db.SupportSQLiteStatement;
@@ -24,7 +25,7 @@
 
     private final EntityInsertionAdapter<Book> __insertionAdapterOfBook;
 
-    public WriterDao_Impl(RoomDatabase __db) {
+    public WriterDao_Impl(@NonNull final RoomDatabase __db) {
         this.__db = __db;
         this.__insertionAdapterOfUser = new EntityInsertionAdapter<User>(__db) {
             @Override
@@ -169,6 +170,7 @@
         }
     }
 
+    @NonNull
     public static List<Class<?>> getRequiredConverters() {
         return Collections.emptyList();
     }
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/ComplexDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/ComplexDao.java
similarity index 80%
copy from room/room-compiler/src/test/test-data/daoWriter/output/ComplexDao.java
copy to room/room-compiler/src/test/test-data/daoWriter/output/ksp/ComplexDao.java
index c9f0981c7..d985724 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/ComplexDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/ComplexDao.java
@@ -2,6 +2,7 @@
 
 import android.database.Cursor;
 import android.os.CancellationSignal;
+import androidx.annotation.NonNull;
 import androidx.lifecycle.LiveData;
 import androidx.room.RoomDatabase;
 import androidx.room.RoomSQLiteQuery;
@@ -29,7 +30,7 @@
 public final class ComplexDao_Impl extends ComplexDao {
     private final RoomDatabase __db;
 
-    public ComplexDao_Impl(ComplexDatabase __db) {
+    public ComplexDao_Impl(final ComplexDatabase __db) {
         super(__db);
         this.__db = __db;
     }
@@ -61,11 +62,7 @@
             while(_cursor.moveToNext()) {
                 final ComplexDao.FullName _item;
                 _item = new ComplexDao.FullName();
-                if (_cursor.isNull(_cursorIndexOfFullName)) {
-                    _item.fullName = null;
-                } else {
-                    _item.fullName = _cursor.getString(_cursorIndexOfFullName);
-                }
+                _item.fullName = _cursor.getString(_cursorIndexOfFullName);
                 _item.id = _cursor.getInt(_cursorIndexOfId);
                 _result.add(_item);
             }
@@ -90,20 +87,12 @@
             final int _cursorIndexOfLastName = CursorUtil.getColumnIndexOrThrow(_cursor, "lastName");
             final int _cursorIndexOfAge = CursorUtil.getColumnIndexOrThrow(_cursor, "ageColumn");
             final User _result;
-            if(_cursor.moveToFirst()) {
+            if (_cursor.moveToFirst()) {
                 _result = new User();
                 _result.uid = _cursor.getInt(_cursorIndexOfUid);
-                if (_cursor.isNull(_cursorIndexOfName)) {
-                    _result.name = null;
-                } else {
-                    _result.name = _cursor.getString(_cursorIndexOfName);
-                }
+                _result.name = _cursor.getString(_cursorIndexOfName);
                 final String _tmpLastName;
-                if (_cursor.isNull(_cursorIndexOfLastName)) {
-                    _tmpLastName = null;
-                } else {
-                    _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
-                }
+                _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
                 _result.setLastName(_tmpLastName);
                 _result.age = _cursor.getInt(_cursorIndexOfAge);
             } else {
@@ -121,17 +110,9 @@
         final String _sql = "SELECT * FROM user where name LIKE ? AND lastName LIKE ?";
         final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 2);
         int _argIndex = 1;
-        if (name == null) {
-            _statement.bindNull(_argIndex);
-        } else {
-            _statement.bindString(_argIndex, name);
-        }
+        _statement.bindString(_argIndex, name);
         _argIndex = 2;
-        if (lastName == null) {
-            _statement.bindNull(_argIndex);
-        } else {
-            _statement.bindString(_argIndex, lastName);
-        }
+        _statement.bindString(_argIndex, lastName);
         __db.assertNotSuspendingTransaction();
         final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
         try {
@@ -140,20 +121,12 @@
             final int _cursorIndexOfLastName = CursorUtil.getColumnIndexOrThrow(_cursor, "lastName");
             final int _cursorIndexOfAge = CursorUtil.getColumnIndexOrThrow(_cursor, "ageColumn");
             final User _result;
-            if(_cursor.moveToFirst()) {
+            if (_cursor.moveToFirst()) {
                 _result = new User();
                 _result.uid = _cursor.getInt(_cursorIndexOfUid);
-                if (_cursor.isNull(_cursorIndexOfName)) {
-                    _result.name = null;
-                } else {
-                    _result.name = _cursor.getString(_cursorIndexOfName);
-                }
+                _result.name = _cursor.getString(_cursorIndexOfName);
                 final String _tmpLastName;
-                if (_cursor.isNull(_cursorIndexOfLastName)) {
-                    _tmpLastName = null;
-                } else {
-                    _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
-                }
+                _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
                 _result.setLastName(_tmpLastName);
                 _result.age = _cursor.getInt(_cursorIndexOfAge);
             } else {
@@ -168,7 +141,7 @@
 
     @Override
     public List<User> loadAllByIds(final int... ids) {
-        StringBuilder _stringBuilder = StringUtil.newStringBuilder();
+        final StringBuilder _stringBuilder = StringUtil.newStringBuilder();
         _stringBuilder.append("SELECT * FROM user where uid IN (");
         final int _inputSize = ids.length;
         StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
@@ -193,17 +166,9 @@
                 final User _item_1;
                 _item_1 = new User();
                 _item_1.uid = _cursor.getInt(_cursorIndexOfUid);
-                if (_cursor.isNull(_cursorIndexOfName)) {
-                    _item_1.name = null;
-                } else {
-                    _item_1.name = _cursor.getString(_cursorIndexOfName);
-                }
+                _item_1.name = _cursor.getString(_cursorIndexOfName);
                 final String _tmpLastName;
-                if (_cursor.isNull(_cursorIndexOfLastName)) {
-                    _tmpLastName = null;
-                } else {
-                    _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
-                }
+                _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
                 _item_1.setLastName(_tmpLastName);
                 _item_1.age = _cursor.getInt(_cursorIndexOfAge);
                 _result.add(_item_1);
@@ -225,7 +190,7 @@
         final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
         try {
             final int _result;
-            if(_cursor.moveToFirst()) {
+            if (_cursor.moveToFirst()) {
                 _result = _cursor.getInt(0);
             } else {
                 _result = 0;
@@ -239,7 +204,7 @@
 
     @Override
     public int[] getAllAges(final int... ids) {
-        StringBuilder _stringBuilder = StringUtil.newStringBuilder();
+        final StringBuilder _stringBuilder = StringUtil.newStringBuilder();
         _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
         final int _inputSize = ids.length;
         StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
@@ -272,7 +237,7 @@
 
     @Override
     public List<Integer> getAllAgesAsList(final List<Integer> ids) {
-        StringBuilder _stringBuilder = StringUtil.newStringBuilder();
+        final StringBuilder _stringBuilder = StringUtil.newStringBuilder();
         _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
         final int _inputSize = ids.size();
         StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
@@ -312,7 +277,7 @@
     @Override
     public List<Integer> getAllAgesAsList(final List<Integer> ids1, final int[] ids2,
             final int... ids3) {
-        StringBuilder _stringBuilder = StringUtil.newStringBuilder();
+        final StringBuilder _stringBuilder = StringUtil.newStringBuilder();
         _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
         final int _inputSize = ids1.size();
         StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
@@ -381,20 +346,12 @@
                     final int _cursorIndexOfLastName = CursorUtil.getColumnIndexOrThrow(_cursor, "lastName");
                     final int _cursorIndexOfAge = CursorUtil.getColumnIndexOrThrow(_cursor, "ageColumn");
                     final User _result;
-                    if(_cursor.moveToFirst()) {
+                    if (_cursor.moveToFirst()) {
                         _result = new User();
                         _result.uid = _cursor.getInt(_cursorIndexOfUid);
-                        if (_cursor.isNull(_cursorIndexOfName)) {
-                            _result.name = null;
-                        } else {
-                            _result.name = _cursor.getString(_cursorIndexOfName);
-                        }
+                        _result.name = _cursor.getString(_cursorIndexOfName);
                         final String _tmpLastName;
-                        if (_cursor.isNull(_cursorIndexOfLastName)) {
-                            _tmpLastName = null;
-                        } else {
-                            _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
-                        }
+                        _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
                         _result.setLastName(_tmpLastName);
                         _result.age = _cursor.getInt(_cursorIndexOfAge);
                     } else {
@@ -415,7 +372,7 @@
 
     @Override
     public LiveData<List<User>> loadUsersByIdsLive(final int... ids) {
-        StringBuilder _stringBuilder = StringUtil.newStringBuilder();
+        final StringBuilder _stringBuilder = StringUtil.newStringBuilder();
         _stringBuilder.append("SELECT * FROM user where uid IN (");
         final int _inputSize = ids.length;
         StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
@@ -442,17 +399,9 @@
                         final User _item_1;
                         _item_1 = new User();
                         _item_1.uid = _cursor.getInt(_cursorIndexOfUid);
-                        if (_cursor.isNull(_cursorIndexOfName)) {
-                            _item_1.name = null;
-                        } else {
-                            _item_1.name = _cursor.getString(_cursorIndexOfName);
-                        }
+                        _item_1.name = _cursor.getString(_cursorIndexOfName);
                         final String _tmpLastName;
-                        if (_cursor.isNull(_cursorIndexOfLastName)) {
-                            _tmpLastName = null;
-                        } else {
-                            _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
-                        }
+                        _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
                         _item_1.setLastName(_tmpLastName);
                         _item_1.age = _cursor.getInt(_cursorIndexOfAge);
                         _result.add(_item_1);
@@ -487,20 +436,12 @@
                 final int _tmpId;
                 _tmpId = _cursor.getInt(_cursorIndexOfId);
                 final String _tmpName;
-                if (_cursor.isNull(_cursorIndexOfName)) {
-                    _tmpName = null;
-                } else {
-                    _tmpName = _cursor.getString(_cursorIndexOfName);
-                }
+                _tmpName = _cursor.getString(_cursorIndexOfName);
                 final Info _tmpInfo;
                 if (!(_cursor.isNull(_cursorIndexOfSerial) && _cursor.isNull(_cursorIndexOfCode))) {
                     _tmpInfo = new Info();
                     _tmpInfo.serial = _cursor.getInt(_cursorIndexOfSerial);
-                    if (_cursor.isNull(_cursorIndexOfCode)) {
-                        _tmpInfo.code = null;
-                    } else {
-                        _tmpInfo.code = _cursor.getString(_cursorIndexOfCode);
-                    }
+                    _tmpInfo.code = _cursor.getString(_cursorIndexOfCode);
                 } else {
                     _tmpInfo = null;
                 }
@@ -531,20 +472,12 @@
                 final int _tmpId;
                 _tmpId = _cursor.getInt(_cursorIndexOfId);
                 final String _tmpName;
-                if (_cursor.isNull(_cursorIndexOfName)) {
-                    _tmpName = null;
-                } else {
-                    _tmpName = _cursor.getString(_cursorIndexOfName);
-                }
+                _tmpName = _cursor.getString(_cursorIndexOfName);
                 final Info _tmpInfo;
                 if (!(_cursor.isNull(_cursorIndexOfSerial) && _cursor.isNull(_cursorIndexOfCode))) {
                     _tmpInfo = new Info();
                     _tmpInfo.serial = _cursor.getInt(_cursorIndexOfSerial);
-                    if (_cursor.isNull(_cursorIndexOfCode)) {
-                        _tmpInfo.code = null;
-                    } else {
-                        _tmpInfo.code = _cursor.getString(_cursorIndexOfCode);
-                    }
+                    _tmpInfo.code = _cursor.getString(_cursorIndexOfCode);
                 } else {
                     _tmpInfo = null;
                 }
@@ -578,20 +511,12 @@
                         final int _tmpId;
                         _tmpId = _cursor.getInt(_cursorIndexOfId);
                         final String _tmpName;
-                        if (_cursor.isNull(_cursorIndexOfName)) {
-                            _tmpName = null;
-                        } else {
-                            _tmpName = _cursor.getString(_cursorIndexOfName);
-                        }
+                        _tmpName = _cursor.getString(_cursorIndexOfName);
                         final Info _tmpInfo;
                         if (!(_cursor.isNull(_cursorIndexOfSerial) && _cursor.isNull(_cursorIndexOfCode))) {
                             _tmpInfo = new Info();
                             _tmpInfo.serial = _cursor.getInt(_cursorIndexOfSerial);
-                            if (_cursor.isNull(_cursorIndexOfCode)) {
-                                _tmpInfo.code = null;
-                            } else {
-                                _tmpInfo.code = _cursor.getString(_cursorIndexOfCode);
-                            }
+                            _tmpInfo.code = _cursor.getString(_cursorIndexOfCode);
                         } else {
                             _tmpInfo = null;
                         }
@@ -620,11 +545,7 @@
                 final UserSummary _item;
                 _item = new UserSummary();
                 _item.uid = _cursor.getInt(_cursorIndexOfUid);
-                if (_cursor.isNull(_cursorIndexOfName)) {
-                    _item.name = null;
-                } else {
-                    _item.name = _cursor.getString(_cursorIndexOfName);
-                }
+                _item.name = _cursor.getString(_cursorIndexOfName);
                 _result.add(_item);
             }
             return _result;
@@ -641,7 +562,7 @@
         final Cursor _cursor = DBUtil.query(__db, _internalQuery, false, null);
         try {
             final User _result;
-            if(_cursor.moveToFirst()) {
+            if (_cursor.moveToFirst()) {
                 _result = __entityCursorConverter_fooBarUser(_cursor);
             } else {
                 _result = null;
@@ -652,11 +573,12 @@
         }
     }
 
+    @NonNull
     public static List<Class<?>> getRequiredConverters() {
         return Collections.emptyList();
     }
 
-    private User __entityCursorConverter_fooBarUser(Cursor cursor) {
+    private User __entityCursorConverter_fooBarUser(@NonNull final Cursor cursor) {
         final User _entity;
         final int _cursorIndexOfUid = CursorUtil.getColumnIndex(cursor, "uid");
         final int _cursorIndexOfName = CursorUtil.getColumnIndex(cursor, "name");
@@ -667,19 +589,11 @@
             _entity.uid = cursor.getInt(_cursorIndexOfUid);
         }
         if (_cursorIndexOfName != -1) {
-            if (cursor.isNull(_cursorIndexOfName)) {
-                _entity.name = null;
-            } else {
-                _entity.name = cursor.getString(_cursorIndexOfName);
-            }
+            _entity.name = cursor.getString(_cursorIndexOfName);
         }
         if (_cursorIndexOfLastName != -1) {
             final String _tmpLastName;
-            if (cursor.isNull(_cursorIndexOfLastName)) {
-                _tmpLastName = null;
-            } else {
-                _tmpLastName = cursor.getString(_cursorIndexOfLastName);
-            }
+            _tmpLastName = cursor.getString(_cursorIndexOfLastName);
             _entity.setLastName(_tmpLastName);
         }
         if (_cursorIndexOfAge != -1) {
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/DeletionDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/DeletionDao.java
similarity index 96%
copy from room/room-compiler/src/test/test-data/daoWriter/output/DeletionDao.java
copy to room/room-compiler/src/test/test-data/daoWriter/output/ksp/DeletionDao.java
index 04979c9..10d5a27 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/DeletionDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/DeletionDao.java
@@ -1,5 +1,6 @@
 package foo.bar;
 
+import androidx.annotation.NonNull;
 import androidx.room.EntityDeletionOrUpdateAdapter;
 import androidx.room.RoomDatabase;
 import androidx.room.SharedSQLiteStatement;
@@ -36,7 +37,7 @@
 
   private final SharedSQLiteStatement __preparedStmtOfDeleteEverything;
 
-  public DeletionDao_Impl(RoomDatabase __db) {
+  public DeletionDao_Impl(@NonNull final RoomDatabase __db) {
     this.__db = __db;
     this.__deletionAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
       @Override
@@ -57,16 +58,8 @@
 
       @Override
       public void bind(SupportSQLiteStatement stmt, MultiPKeyEntity value) {
-        if (value.name == null) {
-          stmt.bindNull(1);
-        } else {
-          stmt.bindString(1, value.name);
-        }
-        if (value.lastName == null) {
-          stmt.bindNull(2);
-        } else {
-          stmt.bindString(2, value.lastName);
-        }
+        stmt.bindString(1, value.name);
+        stmt.bindString(2, value.lastName);
       }
     };
     this.__deletionAdapterOfBook = new EntityDeletionOrUpdateAdapter<Book>(__db) {
@@ -368,7 +361,7 @@
   @Override
   public int deleteByUidList(final int... uid) {
     __db.assertNotSuspendingTransaction();
-    StringBuilder _stringBuilder = StringUtil.newStringBuilder();
+    final StringBuilder _stringBuilder = StringUtil.newStringBuilder();
     _stringBuilder.append("DELETE FROM user where uid IN(");
     final int _inputSize = uid.length;
     StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
@@ -390,6 +383,7 @@
     }
   }
 
+  @NonNull
   public static List<Class<?>> getRequiredConverters() {
     return Collections.emptyList();
   }
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/UpdateDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpdateDao.java
similarity index 89%
copy from room/room-compiler/src/test/test-data/daoWriter/output/UpdateDao.java
copy to room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpdateDao.java
index 889b4fb..eeb7d0f 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/UpdateDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpdateDao.java
@@ -1,5 +1,6 @@
 package foo.bar;
 
+import androidx.annotation.NonNull;
 import androidx.room.EntityDeletionOrUpdateAdapter;
 import androidx.room.RoomDatabase;
 import androidx.room.SharedSQLiteStatement;
@@ -36,7 +37,7 @@
 
   private final SharedSQLiteStatement __preparedStmtOfAgeUserAll;
 
-  public UpdateDao_Impl(RoomDatabase __db) {
+  public UpdateDao_Impl(@NonNull final RoomDatabase __db) {
     this.__db = __db;
     this.__updateAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
       @Override
@@ -47,16 +48,8 @@
       @Override
       public void bind(SupportSQLiteStatement stmt, User value) {
         stmt.bindLong(1, value.uid);
-        if (value.name == null) {
-          stmt.bindNull(2);
-        } else {
-          stmt.bindString(2, value.name);
-        }
-        if (value.getLastName() == null) {
-          stmt.bindNull(3);
-        } else {
-          stmt.bindString(3, value.getLastName());
-        }
+        stmt.bindString(2, value.name);
+        stmt.bindString(3, value.getLastName());
         stmt.bindLong(4, value.age);
         stmt.bindLong(5, value.uid);
       }
@@ -70,16 +63,8 @@
       @Override
       public void bind(SupportSQLiteStatement stmt, User value) {
         stmt.bindLong(1, value.uid);
-        if (value.name == null) {
-          stmt.bindNull(2);
-        } else {
-          stmt.bindString(2, value.name);
-        }
-        if (value.getLastName() == null) {
-          stmt.bindNull(3);
-        } else {
-          stmt.bindString(3, value.getLastName());
-        }
+        stmt.bindString(2, value.name);
+        stmt.bindString(3, value.getLastName());
         stmt.bindLong(4, value.age);
         stmt.bindLong(5, value.uid);
       }
@@ -92,26 +77,10 @@
 
       @Override
       public void bind(SupportSQLiteStatement stmt, MultiPKeyEntity value) {
-        if (value.name == null) {
-          stmt.bindNull(1);
-        } else {
-          stmt.bindString(1, value.name);
-        }
-        if (value.lastName == null) {
-          stmt.bindNull(2);
-        } else {
-          stmt.bindString(2, value.lastName);
-        }
-        if (value.name == null) {
-          stmt.bindNull(3);
-        } else {
-          stmt.bindString(3, value.name);
-        }
-        if (value.lastName == null) {
-          stmt.bindNull(4);
-        } else {
-          stmt.bindString(4, value.lastName);
-        }
+        stmt.bindString(1, value.name);
+        stmt.bindString(2, value.lastName);
+        stmt.bindString(3, value.name);
+        stmt.bindString(4, value.lastName);
       }
     };
     this.__updateAdapterOfBook = new EntityDeletionOrUpdateAdapter<Book>(__db) {
@@ -346,11 +315,7 @@
     __db.assertNotSuspendingTransaction();
     final SupportSQLiteStatement _stmt = __preparedStmtOfAgeUserByUid.acquire();
     int _argIndex = 1;
-    if (uid == null) {
-      _stmt.bindNull(_argIndex);
-    } else {
-      _stmt.bindString(_argIndex, uid);
-    }
+    _stmt.bindString(_argIndex, uid);
     __db.beginTransaction();
     try {
       _stmt.executeUpdateDelete();
@@ -432,6 +397,7 @@
     });
   }
 
+  @NonNull
   public static List<Class<?>> getRequiredConverters() {
     return Collections.emptyList();
   }
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpsertDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpsertDao.java
new file mode 100644
index 0000000..b43b9b33
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpsertDao.java
@@ -0,0 +1,175 @@
+package foo.bar;
+
+import androidx.annotation.NonNull;
+import androidx.room.EntityDeletionOrUpdateAdapter;
+import androidx.room.EntityInsertionAdapter;
+import androidx.room.EntityUpsertionAdapter;
+import androidx.room.RoomDatabase;
+import androidx.sqlite.db.SupportSQLiteStatement;
+import java.lang.Class;
+import java.lang.Override;
+import java.lang.String;
+import java.lang.SuppressWarnings;
+import java.util.Collections;
+import java.util.List;
+import javax.annotation.processing.Generated;
+
+@Generated("androidx.room.RoomProcessor")
+@SuppressWarnings({"unchecked", "deprecation"})
+public final class UpsertDao_Impl implements UpsertDao {
+    private final RoomDatabase __db;
+
+    private final EntityUpsertionAdapter<User> __upsertionAdapterOfUser;
+
+    private final EntityUpsertionAdapter<Book> __upsertionAdapterOfBook;
+
+    public UpsertDao_Impl(@NonNull final RoomDatabase __db) {
+        this.__db = __db;
+        this.__upsertionAdapterOfUser = new EntityUpsertionAdapter<User>(new EntityInsertionAdapter<User>(__db) {
+            @Override
+            public String createQuery() {
+                return "INSERT INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
+            }
+
+            @Override
+            public void bind(SupportSQLiteStatement stmt, User value) {
+                stmt.bindLong(1, value.uid);
+                stmt.bindString(2, value.name);
+                stmt.bindString(3, value.getLastName());
+                stmt.bindLong(4, value.age);
+            }
+        }, new EntityDeletionOrUpdateAdapter<User>(__db) {
+            @Override
+            public String createQuery() {
+                return "UPDATE `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
+            }
+
+            @Override
+            public void bind(SupportSQLiteStatement stmt, User value) {
+                stmt.bindLong(1, value.uid);
+                stmt.bindString(2, value.name);
+                stmt.bindString(3, value.getLastName());
+                stmt.bindLong(4, value.age);
+                stmt.bindLong(5, value.uid);
+            }
+        });
+        this.__upsertionAdapterOfBook = new EntityUpsertionAdapter<Book>(new EntityInsertionAdapter<Book>(__db) {
+            @Override
+            public String createQuery() {
+                return "INSERT INTO `Book` (`bookId`,`uid`) VALUES (?,?)";
+            }
+
+            @Override
+            public void bind(SupportSQLiteStatement stmt, Book value) {
+                stmt.bindLong(1, value.bookId);
+                stmt.bindLong(2, value.uid);
+            }
+        }, new EntityDeletionOrUpdateAdapter<Book>(__db) {
+            @Override
+            public String createQuery() {
+                return "UPDATE `Book` SET `bookId` = ?,`uid` = ? WHERE `bookId` = ?";
+            }
+
+            @Override
+            public void bind(SupportSQLiteStatement stmt, Book value) {
+                stmt.bindLong(1, value.bookId);
+                stmt.bindLong(2, value.uid);
+                stmt.bindLong(3, value.bookId);
+            }
+        });
+    }
+
+    @Override
+    public void upsertUser(final User user) {
+        __db.assertNotSuspendingTransaction();
+        __db.beginTransaction();
+        try {
+            __upsertionAdapterOfUser.upsert(user);
+            __db.setTransactionSuccessful();
+        } finally {
+            __db.endTransaction();
+        }
+    }
+
+    @Override
+    public void upsertUsers(final User user1, final List<User> others) {
+        __db.assertNotSuspendingTransaction();
+        __db.beginTransaction();
+        try {
+            __upsertionAdapterOfUser.upsert(user1);
+            __upsertionAdapterOfUser.upsert(others);
+            __db.setTransactionSuccessful();
+        } finally {
+            __db.endTransaction();
+        }
+    }
+
+    @Override
+    public void upsertUsers(final User[] users) {
+        __db.assertNotSuspendingTransaction();
+        __db.beginTransaction();
+        try {
+            __upsertionAdapterOfUser.upsert(users);
+            __db.setTransactionSuccessful();
+        } finally {
+            __db.endTransaction();
+        }
+    }
+
+    @Override
+    public void upsertTwoUsers(final User userOne, final User userTwo) {
+        __db.assertNotSuspendingTransaction();
+        __db.beginTransaction();
+        try {
+            __upsertionAdapterOfUser.upsert(userOne);
+            __upsertionAdapterOfUser.upsert(userTwo);
+            __db.setTransactionSuccessful();
+        } finally {
+            __db.endTransaction();
+        }
+    }
+
+    @Override
+    public void upsertUserAndBook(final User user, final Book book) {
+        __db.assertNotSuspendingTransaction();
+        __db.beginTransaction();
+        try {
+            __upsertionAdapterOfUser.upsert(user);
+            __upsertionAdapterOfBook.upsert(book);
+            __db.setTransactionSuccessful();
+        } finally {
+            __db.endTransaction();
+        }
+    }
+
+    @Override
+    public long upsertAndReturnId(final User user) {
+        __db.assertNotSuspendingTransaction();
+        __db.beginTransaction();
+        try {
+            long _result = __upsertionAdapterOfUser.upsertAndReturnId(user);
+            __db.setTransactionSuccessful();
+            return _result;
+        } finally {
+            __db.endTransaction();
+        }
+    }
+
+    @Override
+    public long[] upsertAndReturnIdsArray(final User[] users) {
+        __db.assertNotSuspendingTransaction();
+        __db.beginTransaction();
+        try {
+            long[] _result = __upsertionAdapterOfUser.upsertAndReturnIdsArray(users);
+            __db.setTransactionSuccessful();
+            return _result;
+        } finally {
+            __db.endTransaction();
+        }
+    }
+
+    @NonNull
+    public static List<Class<?>> getRequiredConverters() {
+        return Collections.emptyList();
+    }
+}
\ No newline at end of file
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/WriterDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/WriterDao.java
similarity index 80%
copy from room/room-compiler/src/test/test-data/daoWriter/output/WriterDao.java
copy to room/room-compiler/src/test/test-data/daoWriter/output/ksp/WriterDao.java
index deaabb5..82b9457 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/WriterDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/WriterDao.java
@@ -1,5 +1,6 @@
 package foo.bar;
 
+import androidx.annotation.NonNull;
 import androidx.room.EntityInsertionAdapter;
 import androidx.room.RoomDatabase;
 import androidx.sqlite.db.SupportSQLiteStatement;
@@ -24,7 +25,7 @@
 
     private final EntityInsertionAdapter<Book> __insertionAdapterOfBook;
 
-    public WriterDao_Impl(RoomDatabase __db) {
+    public WriterDao_Impl(@NonNull final RoomDatabase __db) {
         this.__db = __db;
         this.__insertionAdapterOfUser = new EntityInsertionAdapter<User>(__db) {
             @Override
@@ -35,16 +36,8 @@
             @Override
             public void bind(SupportSQLiteStatement stmt, User value) {
                 stmt.bindLong(1, value.uid);
-                if (value.name == null) {
-                    stmt.bindNull(2);
-                } else {
-                    stmt.bindString(2, value.name);
-                }
-                if (value.getLastName() == null) {
-                    stmt.bindNull(3);
-                } else {
-                    stmt.bindString(3, value.getLastName());
-                }
+                stmt.bindString(2, value.name);
+                stmt.bindString(3, value.getLastName());
                 stmt.bindLong(4, value.age);
             }
         };
@@ -57,16 +50,8 @@
             @Override
             public void bind(SupportSQLiteStatement stmt, User value) {
                 stmt.bindLong(1, value.uid);
-                if (value.name == null) {
-                    stmt.bindNull(2);
-                } else {
-                    stmt.bindString(2, value.name);
-                }
-                if (value.getLastName() == null) {
-                    stmt.bindNull(3);
-                } else {
-                    stmt.bindString(3, value.getLastName());
-                }
+                stmt.bindString(2, value.name);
+                stmt.bindString(3, value.getLastName());
                 stmt.bindLong(4, value.age);
             }
         };
@@ -79,16 +64,8 @@
             @Override
             public void bind(SupportSQLiteStatement stmt, User value) {
                 stmt.bindLong(1, value.uid);
-                if (value.name == null) {
-                    stmt.bindNull(2);
-                } else {
-                    stmt.bindString(2, value.name);
-                }
-                if (value.getLastName() == null) {
-                    stmt.bindNull(3);
-                } else {
-                    stmt.bindString(3, value.getLastName());
-                }
+                stmt.bindString(2, value.name);
+                stmt.bindString(3, value.getLastName());
                 stmt.bindLong(4, value.age);
             }
         };
@@ -169,6 +146,7 @@
         }
     }
 
+    @NonNull
     public static List<Class<?>> getRequiredConverters() {
         return Collections.emptyList();
     }
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
new file mode 100644
index 0000000..88f1b4b
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_boolean.kt
@@ -0,0 +1,65 @@
+import android.database.Cursor
+import androidx.room.RoomDatabase
+import androidx.room.RoomSQLiteQuery
+import androidx.room.RoomSQLiteQuery.Companion.acquire
+import androidx.room.util.getColumnIndexOrThrow
+import androidx.room.util.query
+import java.lang.Class
+import javax.`annotation`.processing.Generated
+import kotlin.Boolean
+import kotlin.Int
+import kotlin.String
+import kotlin.Suppress
+import kotlin.collections.List
+import kotlin.jvm.JvmStatic
+
+@Generated(value = ["androidx.room.RoomProcessor"])
+@Suppress(names = ["unchecked", "deprecation"])
+public class MyDao_Impl : MyDao {
+    private val __db: RoomDatabase
+
+    public constructor(__db: RoomDatabase) {
+        this.__db = __db
+    }
+
+    public override fun getEntity(): MyEntity {
+        val _sql: String = "SELECT * FROM MyEntity"
+        val _statement: RoomSQLiteQuery = acquire(_sql, 0)
+        __db.assertNotSuspendingTransaction()
+        val _cursor: Cursor = query(__db, _statement, false, null)
+        try {
+            val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
+            val _cursorIndexOfBoolean: Int = getColumnIndexOrThrow(_cursor, "boolean")
+            val _cursorIndexOfNullableBoolean: Int = getColumnIndexOrThrow(_cursor, "nullableBoolean")
+            val _result: MyEntity
+            if (_cursor.moveToFirst()) {
+                val _tmpPk: Int
+                _tmpPk = _cursor.getInt(_cursorIndexOfPk)
+                val _tmpBoolean: Boolean
+                val _tmp: Int
+                _tmp = _cursor.getInt(_cursorIndexOfBoolean)
+                _tmpBoolean = _tmp != 0
+                val _tmpNullableBoolean: Boolean?
+                val _tmp_1: Int?
+                if (_cursor.isNull(_cursorIndexOfNullableBoolean)) {
+                    _tmp_1 = null
+                } else {
+                    _tmp_1 = _cursor.getInt(_cursorIndexOfNullableBoolean)
+                }
+                _tmpNullableBoolean = _tmp_1?.let { it != 0 }
+                _result = MyEntity(_tmpPk,_tmpBoolean,_tmpNullableBoolean)
+            } else {
+                error("Cursor was empty, but expected a single item.")
+            }
+            return _result
+        } finally {
+            _cursor.close()
+            _statement.release()
+        }
+    }
+
+    public companion object {
+        @JvmStatic
+        public fun getRequiredConverters(): List<Class<*>> = emptyList()
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..c08a1dd
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_byteArray.kt
@@ -0,0 +1,61 @@
+import android.database.Cursor
+import androidx.room.RoomDatabase
+import androidx.room.RoomSQLiteQuery
+import androidx.room.RoomSQLiteQuery.Companion.acquire
+import androidx.room.util.getColumnIndexOrThrow
+import androidx.room.util.query
+import java.lang.Class
+import javax.`annotation`.processing.Generated
+import kotlin.ByteArray
+import kotlin.Int
+import kotlin.String
+import kotlin.Suppress
+import kotlin.collections.List
+import kotlin.jvm.JvmStatic
+
+@Generated(value = ["androidx.room.RoomProcessor"])
+@Suppress(names = ["unchecked", "deprecation"])
+public class MyDao_Impl : MyDao {
+    private val __db: RoomDatabase
+
+    public constructor(__db: RoomDatabase) {
+        this.__db = __db
+    }
+
+    public override fun getEntity(): MyEntity {
+        val _sql: String = "SELECT * FROM MyEntity"
+        val _statement: RoomSQLiteQuery = acquire(_sql, 0)
+        __db.assertNotSuspendingTransaction()
+        val _cursor: Cursor = query(__db, _statement, false, null)
+        try {
+            val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
+            val _cursorIndexOfByteArray: Int = getColumnIndexOrThrow(_cursor, "byteArray")
+            val _cursorIndexOfNullableByteArray: Int = getColumnIndexOrThrow(_cursor, "nullableByteArray")
+            val _result: MyEntity
+            if (_cursor.moveToFirst()) {
+                val _tmpPk: Int
+                _tmpPk = _cursor.getInt(_cursorIndexOfPk)
+                val _tmpByteArray: ByteArray
+                _tmpByteArray = _cursor.getBlob(_cursorIndexOfByteArray)
+                val _tmpNullableByteArray: ByteArray?
+                if (_cursor.isNull(_cursorIndexOfNullableByteArray)) {
+                    _tmpNullableByteArray = null
+                } else {
+                    _tmpNullableByteArray = _cursor.getBlob(_cursorIndexOfNullableByteArray)
+                }
+                _result = MyEntity(_tmpPk,_tmpByteArray,_tmpNullableByteArray)
+            } else {
+                error("Cursor was empty, but expected a single item.")
+            }
+            return _result
+        } finally {
+            _cursor.close()
+            _statement.release()
+        }
+    }
+
+    public companion object {
+        @JvmStatic
+        public fun getRequiredConverters(): List<Class<*>> = emptyList()
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..9ed16ca
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter.kt
@@ -0,0 +1,57 @@
+import android.database.Cursor
+import androidx.room.RoomDatabase
+import androidx.room.RoomSQLiteQuery
+import androidx.room.RoomSQLiteQuery.Companion.acquire
+import androidx.room.util.getColumnIndexOrThrow
+import androidx.room.util.query
+import java.lang.Class
+import javax.`annotation`.processing.Generated
+import kotlin.Int
+import kotlin.String
+import kotlin.Suppress
+import kotlin.collections.List
+import kotlin.jvm.JvmStatic
+
+@Generated(value = ["androidx.room.RoomProcessor"])
+@Suppress(names = ["unchecked", "deprecation"])
+public class MyDao_Impl : MyDao {
+    private val __db: RoomDatabase
+
+    private val __fooConverter: FooConverter = FooConverter()
+
+    public constructor(__db: RoomDatabase) {
+        this.__db = __db
+    }
+
+    public override fun getEntity(): MyEntity {
+        val _sql: String = "SELECT * FROM MyEntity"
+        val _statement: RoomSQLiteQuery = acquire(_sql, 0)
+        __db.assertNotSuspendingTransaction()
+        val _cursor: Cursor = query(__db, _statement, false, null)
+        try {
+            val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
+            val _cursorIndexOfFoo: Int = getColumnIndexOrThrow(_cursor, "foo")
+            val _result: MyEntity
+            if (_cursor.moveToFirst()) {
+                val _tmpPk: Int
+                _tmpPk = _cursor.getInt(_cursorIndexOfPk)
+                val _tmpFoo: Foo
+                val _tmp: String
+                _tmp = _cursor.getString(_cursorIndexOfFoo)
+                _tmpFoo = __fooConverter.fromString(_tmp)
+                _result = MyEntity(_tmpPk,_tmpFoo)
+            } else {
+                error("Cursor was empty, but expected a single item.")
+            }
+            return _result
+        } finally {
+            _cursor.close()
+            _statement.release()
+        }
+    }
+
+    public companion object {
+        @JvmStatic
+        public fun getRequiredConverters(): List<Class<*>> = emptyList()
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..33d097e
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_embedded.kt
@@ -0,0 +1,72 @@
+import android.database.Cursor
+import androidx.room.RoomDatabase
+import androidx.room.RoomSQLiteQuery
+import androidx.room.RoomSQLiteQuery.Companion.acquire
+import androidx.room.util.getColumnIndexOrThrow
+import androidx.room.util.query
+import java.lang.Class
+import javax.`annotation`.processing.Generated
+import kotlin.Int
+import kotlin.Long
+import kotlin.String
+import kotlin.Suppress
+import kotlin.collections.List
+import kotlin.jvm.JvmStatic
+
+@Generated(value = ["androidx.room.RoomProcessor"])
+@Suppress(names = ["unchecked", "deprecation"])
+public class MyDao_Impl : MyDao {
+    private val __db: RoomDatabase
+
+    public constructor(__db: RoomDatabase) {
+        this.__db = __db
+    }
+
+    public override fun getEntity(): MyEntity {
+        val _sql: String = "SELECT * FROM MyEntity"
+        val _statement: RoomSQLiteQuery = acquire(_sql, 0)
+        __db.assertNotSuspendingTransaction()
+        val _cursor: Cursor = query(__db, _statement, false, null)
+        try {
+            val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
+            val _cursorIndexOfNumberData: Int = getColumnIndexOrThrow(_cursor, "numberData")
+            val _cursorIndexOfStringData: Int = getColumnIndexOrThrow(_cursor, "stringData")
+            val _cursorIndexOfNumberData_1: Int = getColumnIndexOrThrow(_cursor, "nullablenumberData")
+            val _cursorIndexOfStringData_1: Int = getColumnIndexOrThrow(_cursor, "nullablestringData")
+            val _result: MyEntity
+            if (_cursor.moveToFirst()) {
+                val _tmpPk: Int
+                _tmpPk = _cursor.getInt(_cursorIndexOfPk)
+                val _tmpFoo: Foo
+                val _tmpNumberData: Long
+                _tmpNumberData = _cursor.getLong(_cursorIndexOfNumberData)
+                val _tmpStringData: String
+                _tmpStringData = _cursor.getString(_cursorIndexOfStringData)
+                _tmpFoo = Foo(_tmpNumberData,_tmpStringData)
+                val _tmpNullableFoo: Foo?
+                if (!(_cursor.isNull(_cursorIndexOfNumberData_1) &&
+                        _cursor.isNull(_cursorIndexOfStringData_1))) {
+                    val _tmpNumberData_1: Long
+                    _tmpNumberData_1 = _cursor.getLong(_cursorIndexOfNumberData_1)
+                    val _tmpStringData_1: String
+                    _tmpStringData_1 = _cursor.getString(_cursorIndexOfStringData_1)
+                    _tmpNullableFoo = Foo(_tmpNumberData_1,_tmpStringData_1)
+                } else {
+                    _tmpNullableFoo = null
+                }
+                _result = MyEntity(_tmpPk,_tmpFoo,_tmpNullableFoo)
+            } else {
+                error("Cursor was empty, but expected a single item.")
+            }
+            return _result
+        } finally {
+            _cursor.close()
+            _statement.release()
+        }
+    }
+
+    public companion object {
+        @JvmStatic
+        public fun getRequiredConverters(): List<Class<*>> = emptyList()
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..a170782
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_enum.kt
@@ -0,0 +1,69 @@
+import android.database.Cursor
+import androidx.room.RoomDatabase
+import androidx.room.RoomSQLiteQuery
+import androidx.room.RoomSQLiteQuery.Companion.acquire
+import androidx.room.util.getColumnIndexOrThrow
+import androidx.room.util.query
+import java.lang.Class
+import java.lang.IllegalArgumentException
+import javax.`annotation`.processing.Generated
+import kotlin.Int
+import kotlin.String
+import kotlin.Suppress
+import kotlin.collections.List
+import kotlin.jvm.JvmStatic
+
+@Generated(value = ["androidx.room.RoomProcessor"])
+@Suppress(names = ["unchecked", "deprecation"])
+public class MyDao_Impl : MyDao {
+    private val __db: RoomDatabase
+
+    public constructor(__db: RoomDatabase) {
+        this.__db = __db
+    }
+
+    public override fun getEntity(): MyEntity {
+        val _sql: String = "SELECT * FROM MyEntity"
+        val _statement: RoomSQLiteQuery = acquire(_sql, 0)
+        __db.assertNotSuspendingTransaction()
+        val _cursor: Cursor = query(__db, _statement, false, null)
+        try {
+            val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
+            val _cursorIndexOfEnum: Int = getColumnIndexOrThrow(_cursor, "enum")
+            val _cursorIndexOfNullableEnum: Int = getColumnIndexOrThrow(_cursor, "nullableEnum")
+            val _result: MyEntity
+            if (_cursor.moveToFirst()) {
+                val _tmpPk: Int
+                _tmpPk = _cursor.getInt(_cursorIndexOfPk)
+                val _tmpEnum: Fruit
+                _tmpEnum = checkNotNull(__Fruit_stringToEnum(_cursor.getString(_cursorIndexOfEnum)))
+                val _tmpNullableEnum: Fruit?
+                _tmpNullableEnum = __Fruit_stringToEnum(_cursor.getString(_cursorIndexOfNullableEnum))
+                _result = MyEntity(_tmpPk,_tmpEnum,_tmpNullableEnum)
+            } else {
+                error("Cursor was empty, but expected a single item.")
+            }
+            return _result
+        } finally {
+            _cursor.close()
+            _statement.release()
+        }
+    }
+
+    private fun __Fruit_stringToEnum(_value: String?): Fruit? {
+        if (_value == null) {
+            return null
+        }
+        return when (_value) {
+            "APPLE" -> Fruit.APPLE
+            "BANANA" -> Fruit.BANANA
+            else -> throw IllegalArgumentException("Can't convert value to enum, unknown value: " +
+                _value)
+        }
+    }
+
+    public companion object {
+        @JvmStatic
+        public fun getRequiredConverters(): List<Class<*>> = emptyList()
+    }
+}
\ No newline at end of file
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 367cf89..2b8c698 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
@@ -1,7 +1,10 @@
-package androidx.room.temp
-
-import MyEntity
 import android.database.Cursor
+import androidx.room.RoomDatabase
+import androidx.room.RoomSQLiteQuery
+import androidx.room.RoomSQLiteQuery.Companion.acquire
+import androidx.room.util.getColumnIndexOrThrow
+import androidx.room.util.query
+import java.lang.Class
 import javax.`annotation`.processing.Generated
 import kotlin.Byte
 import kotlin.Char
@@ -10,37 +13,62 @@
 import kotlin.Int
 import kotlin.Long
 import kotlin.Short
+import kotlin.String
 import kotlin.Suppress
+import kotlin.collections.List
+import kotlin.jvm.JvmStatic
 
 @Generated(value = ["androidx.room.RoomProcessor"])
 @Suppress(names = ["unchecked", "deprecation"])
-public class PojoRowAdapter_1427165205 {
-    public fun readFromCursor(
-        _cursor: Cursor,
-        _cursorIndexOfInt: Int,
-        _cursorIndexOfShort: Int,
-        _cursorIndexOfByte: Int,
-        _cursorIndexOfLong: Int,
-        _cursorIndexOfChar: Int,
-        _cursorIndexOfFloat: Int,
-        _cursorIndexOfDouble: Int,
-    ): MyEntity {
-        val _result: MyEntity
-        val _tmpInt: Int
-        _tmpInt = _cursor.getInt(_cursorIndexOfInt)
-        val _tmpShort: Short
-        _tmpShort = _cursor.getShort(_cursorIndexOfShort)
-        val _tmpByte: Byte
-        _tmpByte = _cursor.getShort(_cursorIndexOfByte).toByte()
-        val _tmpLong: Long
-        _tmpLong = _cursor.getLong(_cursorIndexOfLong)
-        val _tmpChar: Char
-        _tmpChar = _cursor.getInt(_cursorIndexOfChar).toChar()
-        val _tmpFloat: Float
-        _tmpFloat = _cursor.getFloat(_cursorIndexOfFloat)
-        val _tmpDouble: Double
-        _tmpDouble = _cursor.getDouble(_cursorIndexOfDouble)
-        _result = MyEntity(_tmpInt,_tmpShort,_tmpByte,_tmpLong,_tmpChar,_tmpFloat,_tmpDouble)
-        return _result
+public class MyDao_Impl : MyDao {
+    private val __db: RoomDatabase
+
+    public constructor(__db: RoomDatabase) {
+        this.__db = __db
+    }
+
+    public override fun getEntity(): MyEntity {
+        val _sql: String = "SELECT * FROM MyEntity"
+        val _statement: RoomSQLiteQuery = acquire(_sql, 0)
+        __db.assertNotSuspendingTransaction()
+        val _cursor: Cursor = query(__db, _statement, false, null)
+        try {
+            val _cursorIndexOfInt: Int = getColumnIndexOrThrow(_cursor, "int")
+            val _cursorIndexOfShort: Int = getColumnIndexOrThrow(_cursor, "short")
+            val _cursorIndexOfByte: Int = getColumnIndexOrThrow(_cursor, "byte")
+            val _cursorIndexOfLong: Int = getColumnIndexOrThrow(_cursor, "long")
+            val _cursorIndexOfChar: Int = getColumnIndexOrThrow(_cursor, "char")
+            val _cursorIndexOfFloat: Int = getColumnIndexOrThrow(_cursor, "float")
+            val _cursorIndexOfDouble: Int = getColumnIndexOrThrow(_cursor, "double")
+            val _result: MyEntity
+            if (_cursor.moveToFirst()) {
+                val _tmpInt: Int
+                _tmpInt = _cursor.getInt(_cursorIndexOfInt)
+                val _tmpShort: Short
+                _tmpShort = _cursor.getShort(_cursorIndexOfShort)
+                val _tmpByte: Byte
+                _tmpByte = _cursor.getShort(_cursorIndexOfByte).toByte()
+                val _tmpLong: Long
+                _tmpLong = _cursor.getLong(_cursorIndexOfLong)
+                val _tmpChar: Char
+                _tmpChar = _cursor.getInt(_cursorIndexOfChar).toChar()
+                val _tmpFloat: Float
+                _tmpFloat = _cursor.getFloat(_cursorIndexOfFloat)
+                val _tmpDouble: Double
+                _tmpDouble = _cursor.getDouble(_cursorIndexOfDouble)
+                _result = MyEntity(_tmpInt,_tmpShort,_tmpByte,_tmpLong,_tmpChar,_tmpFloat,_tmpDouble)
+            } else {
+                error("Cursor was empty, but expected a single item.")
+            }
+            return _result
+        } finally {
+            _cursor.close()
+            _statement.release()
+        }
+    }
+
+    public companion object {
+        @JvmStatic
+        public fun getRequiredConverters(): List<Class<*>> = emptyList()
     }
 }
\ No newline at end of file
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
new file mode 100644
index 0000000..8d61578
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_primitives_nullable.kt
@@ -0,0 +1,102 @@
+import android.database.Cursor
+import androidx.room.RoomDatabase
+import androidx.room.RoomSQLiteQuery
+import androidx.room.RoomSQLiteQuery.Companion.acquire
+import androidx.room.util.getColumnIndexOrThrow
+import androidx.room.util.query
+import java.lang.Class
+import javax.`annotation`.processing.Generated
+import kotlin.Byte
+import kotlin.Char
+import kotlin.Double
+import kotlin.Float
+import kotlin.Int
+import kotlin.Long
+import kotlin.Short
+import kotlin.String
+import kotlin.Suppress
+import kotlin.collections.List
+import kotlin.jvm.JvmStatic
+
+@Generated(value = ["androidx.room.RoomProcessor"])
+@Suppress(names = ["unchecked", "deprecation"])
+public class MyDao_Impl : MyDao {
+    private val __db: RoomDatabase
+
+    public constructor(__db: RoomDatabase) {
+        this.__db = __db
+    }
+
+    public override fun getEntity(): MyEntity {
+        val _sql: String = "SELECT * FROM MyEntity"
+        val _statement: RoomSQLiteQuery = acquire(_sql, 0)
+        __db.assertNotSuspendingTransaction()
+        val _cursor: Cursor = query(__db, _statement, false, null)
+        try {
+            val _cursorIndexOfInt: Int = getColumnIndexOrThrow(_cursor, "int")
+            val _cursorIndexOfShort: Int = getColumnIndexOrThrow(_cursor, "short")
+            val _cursorIndexOfByte: Int = getColumnIndexOrThrow(_cursor, "byte")
+            val _cursorIndexOfLong: Int = getColumnIndexOrThrow(_cursor, "long")
+            val _cursorIndexOfChar: Int = getColumnIndexOrThrow(_cursor, "char")
+            val _cursorIndexOfFloat: Int = getColumnIndexOrThrow(_cursor, "float")
+            val _cursorIndexOfDouble: Int = getColumnIndexOrThrow(_cursor, "double")
+            val _result: MyEntity
+            if (_cursor.moveToFirst()) {
+                val _tmpInt: Int?
+                if (_cursor.isNull(_cursorIndexOfInt)) {
+                    _tmpInt = null
+                } else {
+                    _tmpInt = _cursor.getInt(_cursorIndexOfInt)
+                }
+                val _tmpShort: Short?
+                if (_cursor.isNull(_cursorIndexOfShort)) {
+                    _tmpShort = null
+                } else {
+                    _tmpShort = _cursor.getShort(_cursorIndexOfShort)
+                }
+                val _tmpByte: Byte?
+                if (_cursor.isNull(_cursorIndexOfByte)) {
+                    _tmpByte = null
+                } else {
+                    _tmpByte = _cursor.getShort(_cursorIndexOfByte).toByte()
+                }
+                val _tmpLong: Long?
+                if (_cursor.isNull(_cursorIndexOfLong)) {
+                    _tmpLong = null
+                } else {
+                    _tmpLong = _cursor.getLong(_cursorIndexOfLong)
+                }
+                val _tmpChar: Char?
+                if (_cursor.isNull(_cursorIndexOfChar)) {
+                    _tmpChar = null
+                } else {
+                    _tmpChar = _cursor.getInt(_cursorIndexOfChar).toChar()
+                }
+                val _tmpFloat: Float?
+                if (_cursor.isNull(_cursorIndexOfFloat)) {
+                    _tmpFloat = null
+                } else {
+                    _tmpFloat = _cursor.getFloat(_cursorIndexOfFloat)
+                }
+                val _tmpDouble: Double?
+                if (_cursor.isNull(_cursorIndexOfDouble)) {
+                    _tmpDouble = null
+                } else {
+                    _tmpDouble = _cursor.getDouble(_cursorIndexOfDouble)
+                }
+                _result = MyEntity(_tmpInt,_tmpShort,_tmpByte,_tmpLong,_tmpChar,_tmpFloat,_tmpDouble)
+            } else {
+                error("Cursor was empty, but expected a single item.")
+            }
+            return _result
+        } finally {
+            _cursor.close()
+            _statement.release()
+        }
+    }
+
+    public companion object {
+        @JvmStatic
+        public fun getRequiredConverters(): List<Class<*>> = emptyList()
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..5f278a5
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_string.kt
@@ -0,0 +1,57 @@
+import android.database.Cursor
+import androidx.room.RoomDatabase
+import androidx.room.RoomSQLiteQuery
+import androidx.room.RoomSQLiteQuery.Companion.acquire
+import androidx.room.util.getColumnIndexOrThrow
+import androidx.room.util.query
+import java.lang.Class
+import javax.`annotation`.processing.Generated
+import kotlin.Int
+import kotlin.String
+import kotlin.Suppress
+import kotlin.collections.List
+import kotlin.jvm.JvmStatic
+
+@Generated(value = ["androidx.room.RoomProcessor"])
+@Suppress(names = ["unchecked", "deprecation"])
+public class MyDao_Impl : MyDao {
+    private val __db: RoomDatabase
+
+    public constructor(__db: RoomDatabase) {
+        this.__db = __db
+    }
+
+    public override fun getEntity(): MyEntity {
+        val _sql: String = "SELECT * FROM MyEntity"
+        val _statement: RoomSQLiteQuery = acquire(_sql, 0)
+        __db.assertNotSuspendingTransaction()
+        val _cursor: Cursor = query(__db, _statement, false, null)
+        try {
+            val _cursorIndexOfString: Int = getColumnIndexOrThrow(_cursor, "string")
+            val _cursorIndexOfNullableString: Int = getColumnIndexOrThrow(_cursor, "nullableString")
+            val _result: MyEntity
+            if (_cursor.moveToFirst()) {
+                val _tmpString: String
+                _tmpString = _cursor.getString(_cursorIndexOfString)
+                val _tmpNullableString: String?
+                if (_cursor.isNull(_cursorIndexOfNullableString)) {
+                    _tmpNullableString = null
+                } else {
+                    _tmpNullableString = _cursor.getString(_cursorIndexOfNullableString)
+                }
+                _result = MyEntity(_tmpString,_tmpNullableString)
+            } else {
+                error("Cursor was empty, but expected a single item.")
+            }
+            return _result
+        } finally {
+            _cursor.close()
+            _statement.release()
+        }
+    }
+
+    public companion object {
+        @JvmStatic
+        public fun getRequiredConverters(): List<Class<*>> = emptyList()
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..afaa0e1
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_uuid.kt
@@ -0,0 +1,62 @@
+import android.database.Cursor
+import androidx.room.RoomDatabase
+import androidx.room.RoomSQLiteQuery
+import androidx.room.RoomSQLiteQuery.Companion.acquire
+import androidx.room.util.convertByteToUUID
+import androidx.room.util.getColumnIndexOrThrow
+import androidx.room.util.query
+import java.lang.Class
+import java.util.UUID
+import javax.`annotation`.processing.Generated
+import kotlin.Int
+import kotlin.String
+import kotlin.Suppress
+import kotlin.collections.List
+import kotlin.jvm.JvmStatic
+
+@Generated(value = ["androidx.room.RoomProcessor"])
+@Suppress(names = ["unchecked", "deprecation"])
+public class MyDao_Impl : MyDao {
+    private val __db: RoomDatabase
+
+    public constructor(__db: RoomDatabase) {
+        this.__db = __db
+    }
+
+    public override fun getEntity(): MyEntity {
+        val _sql: String = "SELECT * FROM MyEntity"
+        val _statement: RoomSQLiteQuery = acquire(_sql, 0)
+        __db.assertNotSuspendingTransaction()
+        val _cursor: Cursor = query(__db, _statement, false, null)
+        try {
+            val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
+            val _cursorIndexOfUuid: Int = getColumnIndexOrThrow(_cursor, "uuid")
+            val _cursorIndexOfNullableUuid: Int = getColumnIndexOrThrow(_cursor, "nullableUuid")
+            val _result: MyEntity
+            if (_cursor.moveToFirst()) {
+                val _tmpPk: Int
+                _tmpPk = _cursor.getInt(_cursorIndexOfPk)
+                val _tmpUuid: UUID
+                _tmpUuid = convertByteToUUID(_cursor.getBlob(_cursorIndexOfUuid))
+                val _tmpNullableUuid: UUID?
+                if (_cursor.isNull(_cursorIndexOfNullableUuid)) {
+                    _tmpNullableUuid = null
+                } else {
+                    _tmpNullableUuid = convertByteToUUID(_cursor.getBlob(_cursorIndexOfNullableUuid))
+                }
+                _result = MyEntity(_tmpPk,_tmpUuid,_tmpNullableUuid)
+            } else {
+                error("Cursor was empty, but expected a single item.")
+            }
+            return _result
+        } finally {
+            _cursor.close()
+            _statement.release()
+        }
+    }
+
+    public companion object {
+        @JvmStatic
+        public fun getRequiredConverters(): List<Class<*>> = emptyList()
+    }
+}
\ No newline at end of file
diff --git a/room/room-ktx/build.gradle b/room/room-ktx/build.gradle
index 1b1eec4..d824294 100644
--- a/room/room-ktx/build.gradle
+++ b/room/room-ktx/build.gradle
@@ -29,7 +29,7 @@
     api(libs.kotlinStdlib)
     api(libs.kotlinCoroutinesAndroid)
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.truth)
     testImplementation("androidx.lifecycle:lifecycle-livedata-core:2.0.0")
     testImplementation(libs.testRunner)
diff --git a/room/room-ktx/src/main/java/androidx/room/migration/Migration.kt b/room/room-ktx/src/main/java/androidx/room/migration/MigrationExt.kt
similarity index 94%
rename from room/room-ktx/src/main/java/androidx/room/migration/Migration.kt
rename to room/room-ktx/src/main/java/androidx/room/migration/MigrationExt.kt
index f9488c1..a697681 100644
--- a/room/room-ktx/src/main/java/androidx/room/migration/Migration.kt
+++ b/room/room-ktx/src/main/java/androidx/room/migration/MigrationExt.kt
@@ -1,3 +1,4 @@
+
 /*
  * Copyright 2021 The Android Open Source Project
  *
@@ -13,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+@file:JvmName("MigrationKt")
 
 package androidx.room.migration
 
@@ -47,5 +49,5 @@
     endVersion: Int,
     val migrateCallback: (SupportSQLiteDatabase) -> Unit
 ) : Migration(startVersion, endVersion) {
-    override fun migrate(database: SupportSQLiteDatabase) = migrateCallback(database)
+    override fun migrate(db: SupportSQLiteDatabase) = migrateCallback(db)
 }
diff --git a/room/room-migration/build.gradle b/room/room-migration/build.gradle
index d9f13b9..6fa47eaf 100644
--- a/room/room-migration/build.gradle
+++ b/room/room-migration/build.gradle
@@ -34,7 +34,7 @@
     implementation(libs.gson)
     testImplementation(libs.junit)
     testImplementation(libs.intellijAnnotations)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
 }
 
 androidx {
diff --git a/room/room-runtime/api/current.txt b/room/room-runtime/api/current.txt
index f72bcc8..33f9ae3 100644
--- a/room/room-runtime/api/current.txt
+++ b/room/room-runtime/api/current.txt
@@ -148,12 +148,12 @@
 package androidx.room.migration {
 
   public interface AutoMigrationSpec {
-    method public default void onPostMigrate(androidx.sqlite.db.SupportSQLiteDatabase);
+    method public default void onPostMigrate(androidx.sqlite.db.SupportSQLiteDatabase db);
   }
 
   public abstract class Migration {
-    ctor public Migration(int, int);
-    method public abstract void migrate(androidx.sqlite.db.SupportSQLiteDatabase);
+    ctor public Migration(int startVersion, int endVersion);
+    method public abstract void migrate(androidx.sqlite.db.SupportSQLiteDatabase db);
     field public final int endVersion;
     field public final int startVersion;
   }
diff --git a/room/room-runtime/api/public_plus_experimental_current.txt b/room/room-runtime/api/public_plus_experimental_current.txt
index b9bfe81..68990e9 100644
--- a/room/room-runtime/api/public_plus_experimental_current.txt
+++ b/room/room-runtime/api/public_plus_experimental_current.txt
@@ -158,12 +158,12 @@
 package androidx.room.migration {
 
   public interface AutoMigrationSpec {
-    method public default void onPostMigrate(androidx.sqlite.db.SupportSQLiteDatabase);
+    method public default void onPostMigrate(androidx.sqlite.db.SupportSQLiteDatabase db);
   }
 
   public abstract class Migration {
-    ctor public Migration(int, int);
-    method public abstract void migrate(androidx.sqlite.db.SupportSQLiteDatabase);
+    ctor public Migration(int startVersion, int endVersion);
+    method public abstract void migrate(androidx.sqlite.db.SupportSQLiteDatabase db);
     field public final int endVersion;
     field public final int startVersion;
   }
diff --git a/room/room-runtime/api/restricted_current.txt b/room/room-runtime/api/restricted_current.txt
index 44454ed..b577a4a 100644
--- a/room/room-runtime/api/restricted_current.txt
+++ b/room/room-runtime/api/restricted_current.txt
@@ -276,12 +276,12 @@
 package androidx.room.migration {
 
   public interface AutoMigrationSpec {
-    method public default void onPostMigrate(androidx.sqlite.db.SupportSQLiteDatabase);
+    method public default void onPostMigrate(androidx.sqlite.db.SupportSQLiteDatabase db);
   }
 
   public abstract class Migration {
-    ctor public Migration(int, int);
-    method public abstract void migrate(androidx.sqlite.db.SupportSQLiteDatabase);
+    ctor public Migration(int startVersion, int endVersion);
+    method public abstract void migrate(androidx.sqlite.db.SupportSQLiteDatabase db);
     field public final int endVersion;
     field public final int startVersion;
   }
diff --git a/room/room-runtime/build.gradle b/room/room-runtime/build.gradle
index 8eb962e..c9153f0 100644
--- a/room/room-runtime/build.gradle
+++ b/room/room-runtime/build.gradle
@@ -48,7 +48,7 @@
 
     testImplementation("androidx.arch.core:core-testing:2.0.1")
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.mockitoKotlin4)
     testImplementation("androidx.lifecycle:lifecycle-livedata-core:2.0.0")
     testImplementation(libs.kotlinStdlib)
diff --git a/room/room-runtime/src/main/java/androidx/room/migration/AutoMigrationSpec.java b/room/room-runtime/src/main/java/androidx/room/migration/AutoMigrationSpec.kt
similarity index 77%
rename from room/room-runtime/src/main/java/androidx/room/migration/AutoMigrationSpec.java
rename to room/room-runtime/src/main/java/androidx/room/migration/AutoMigrationSpec.kt
index 1eea189..7b163c0 100644
--- a/room/room-runtime/src/main/java/androidx/room/migration/AutoMigrationSpec.java
+++ b/room/room-runtime/src/main/java/androidx/room/migration/AutoMigrationSpec.kt
@@ -13,26 +13,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package androidx.room.migration
 
-package androidx.room.migration;
-import androidx.annotation.NonNull;
-import androidx.room.AutoMigration;
-import androidx.sqlite.db.SupportSQLiteDatabase;
+import androidx.sqlite.db.SupportSQLiteDatabase
 
 /**
  * Interface for defining an automatic migration specification for Room databases.
- * <p>
+ *
  * The methods defined in this interface will be called on a background thread from the executor
  * set in Room's builder. It is important to note that the methods are all in a transaction when
  * it is called.
  *
- * @see AutoMigration
+ * For details, see [androidx.room.AutoMigration]
  */
-public interface AutoMigrationSpec {
-
+interface AutoMigrationSpec {
     /**
      * Invoked after the migration is completed.
      * @param db The SQLite database.
      */
-    default void onPostMigrate(@NonNull SupportSQLiteDatabase db) {}
-}
+    fun onPostMigrate(db: SupportSQLiteDatabase) {}
+}
\ No newline at end of file
diff --git a/room/room-runtime/src/main/java/androidx/room/migration/Migration.java b/room/room-runtime/src/main/java/androidx/room/migration/Migration.java
deleted file mode 100644
index 4aa7a7e..0000000
--- a/room/room-runtime/src/main/java/androidx/room/migration/Migration.java
+++ /dev/null
@@ -1,63 +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.room.migration;
-
-import androidx.annotation.NonNull;
-import androidx.sqlite.db.SupportSQLiteDatabase;
-
-/**
- * Base class for a database migration.
- * <p>
- * Each migration can move between 2 versions that are defined by {@link #startVersion} and
- * {@link #endVersion}.
- * <p>
- * A migration can handle more than 1 version (e.g. if you have a faster path to choose when
- * going version 3 to 5 without going to version 4). If Room opens a database at version
- * 3 and latest version is &gt;= 5, Room will use the migration object that can migrate from
- * 3 to 5 instead of 3 to 4 and 4 to 5.
- * <p>
- * If there are not enough migrations provided to move from the current version to the latest
- * version, Room will clear the database and recreate so even if you have no changes between 2
- * versions, you should still provide a Migration object to the builder.
- */
-public abstract class Migration {
-    public final int startVersion;
-    public final int endVersion;
-
-    /**
-     * Creates a new migration between {@code startVersion} and {@code endVersion}.
-     *
-     * @param startVersion The start version of the database.
-     * @param endVersion The end version of the database after this migration is applied.
-     */
-    public Migration(int startVersion, int endVersion) {
-        this.startVersion = startVersion;
-        this.endVersion = endVersion;
-    }
-
-    /**
-     * Should run the necessary migrations.
-     * <p>
-     * This class cannot access any generated Dao in this method.
-     * <p>
-     * This method is already called inside a transaction and that transaction might actually be a
-     * composite transaction of all necessary {@code Migration}s.
-     *
-     * @param database The database instance
-     */
-    public abstract void migrate(@NonNull SupportSQLiteDatabase database);
-}
diff --git a/room/room-runtime/src/main/java/androidx/room/migration/Migration.kt b/room/room-runtime/src/main/java/androidx/room/migration/Migration.kt
new file mode 100644
index 0000000..6250702
--- /dev/null
+++ b/room/room-runtime/src/main/java/androidx/room/migration/Migration.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.room.migration
+
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+/**
+ * Base class for a database migration.
+ *
+ * Creates a new migration between [startVersion] and [endVersion].
+ *
+ * Each migration can move between 2 versions that are defined by [startVersion] and
+ * [endVersion].
+ *
+ * A migration can handle more than 1 version (e.g. if you have a faster path to choose when
+ * going version 3 to 5 without going to version 4). If Room opens a database at version
+ * 3 and latest version is 5, Room will use the migration object that can migrate from
+ * 3 to 5 instead of 3 to 4 and 4 to 5.
+ *
+ * If there are not enough migrations provided to move from the current version to the latest
+ * version, Room will clear the database and recreate so even if you have no changes between 2
+ * versions, you should still provide a Migration object to the builder.
+ */
+abstract class Migration(
+    @JvmField
+    val startVersion: Int,
+    @JvmField
+    val endVersion: Int
+) {
+    /**
+     * Should run the necessary migrations.
+     *
+     * The Migration class cannot access any generated Dao in this method.
+     *
+     * This method is already called inside a transaction and that transaction might actually be a
+     * composite transaction of all necessary `Migration`s.
+     *
+     * @param db The database instance
+     */
+    abstract fun migrate(db: SupportSQLiteDatabase)
+}
\ No newline at end of file
diff --git a/room/room-runtime/src/test/java/androidx/room/BuilderTest.kt b/room/room-runtime/src/test/java/androidx/room/BuilderTest.kt
index cd2f9f1..45b1274 100644
--- a/room/room-runtime/src/test/java/androidx/room/BuilderTest.kt
+++ b/room/room-runtime/src/test/java/androidx/room/BuilderTest.kt
@@ -453,6 +453,6 @@
     }
 
     internal class EmptyMigration(start: Int, end: Int) : Migration(start, end) {
-        override fun migrate(database: SupportSQLiteDatabase) {}
+        override fun migrate(db: SupportSQLiteDatabase) {}
     }
 }
diff --git a/room/room-rxjava2/build.gradle b/room/room-rxjava2/build.gradle
index a3653c8..ed6314b 100644
--- a/room/room-rxjava2/build.gradle
+++ b/room/room-rxjava2/build.gradle
@@ -32,7 +32,7 @@
 
     testImplementation(libs.truth)
     testImplementation(libs.kotlinTest)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.mockitoKotlin4)
     testImplementation("androidx.arch.core:core-testing:2.0.1")
     testImplementation("androidx.lifecycle:lifecycle-livedata:2.0.0") // for mocking invalidation tracker
diff --git a/room/room-rxjava3/build.gradle b/room/room-rxjava3/build.gradle
index 32f1a35..48b85ff 100644
--- a/room/room-rxjava3/build.gradle
+++ b/room/room-rxjava3/build.gradle
@@ -33,7 +33,7 @@
 
     testImplementation(libs.truth)
     testImplementation(libs.kotlinTest)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.mockitoKotlin4)
     testImplementation("androidx.arch.core:core-testing:2.0.1")
     testImplementation("androidx.lifecycle:lifecycle-livedata:2.0.0") // for mocking invalidation tracker
diff --git a/security/security-app-authenticator/build.gradle b/security/security-app-authenticator/build.gradle
index 21f835c..730607e 100644
--- a/security/security-app-authenticator/build.gradle
+++ b/security/security-app-authenticator/build.gradle
@@ -32,7 +32,7 @@
     testImplementation(libs.testCore)
     testImplementation(libs.testRunner)
     testImplementation(libs.testRules)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.robolectric)
 
     androidTestImplementation(libs.junit)
diff --git a/security/security-app-authenticator/src/test/java/androidx/security/app/authenticator/AppAuthenticatorTest.java b/security/security-app-authenticator/src/test/java/androidx/security/app/authenticator/AppAuthenticatorTest.java
index c95b3c8..cd3ad3e 100644
--- a/security/security-app-authenticator/src/test/java/androidx/security/app/authenticator/AppAuthenticatorTest.java
+++ b/security/security-app-authenticator/src/test/java/androidx/security/app/authenticator/AppAuthenticatorTest.java
@@ -53,6 +53,7 @@
     @Mock
     private AppSignatureVerifier mMockAppSignatureVerifier;
 
+    @SuppressWarnings("deprecation") // b/251210952
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
diff --git a/security/security-app-authenticator/src/test/java/androidx/security/app/authenticator/AppSignatureVerifierTest.java b/security/security-app-authenticator/src/test/java/androidx/security/app/authenticator/AppSignatureVerifierTest.java
index 31a1e90..3219ae9 100644
--- a/security/security-app-authenticator/src/test/java/androidx/security/app/authenticator/AppSignatureVerifierTest.java
+++ b/security/security-app-authenticator/src/test/java/androidx/security/app/authenticator/AppSignatureVerifierTest.java
@@ -76,6 +76,7 @@
 
     private AppSignatureVerifierTestBuilder mBuilder;
 
+    @SuppressWarnings("deprecation") // b/251210952
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
diff --git a/settings.gradle b/settings.gradle
index cde0211..3e517c5 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -691,12 +691,12 @@
 includeProject(":lifecycle:lifecycle-common-java8", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE])
 includeProject(":lifecycle:lifecycle-compiler", [BuildType.MAIN, BuildType.FLAN])
 includeProject(":lifecycle:lifecycle-extensions", [BuildType.MAIN, BuildType.FLAN])
-includeProject(":lifecycle:lifecycle-livedata", [BuildType.MAIN, BuildType.FLAN])
+includeProject(":lifecycle:lifecycle-livedata", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE])
 includeProject(":lifecycle:lifecycle-livedata-core", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE])
-includeProject(":lifecycle:lifecycle-livedata-core-ktx", [BuildType.MAIN, BuildType.FLAN])
-includeProject(":lifecycle:lifecycle-livedata-core-ktx-lint", [BuildType.MAIN, BuildType.FLAN])
+includeProject(":lifecycle:lifecycle-livedata-core-ktx", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE])
+includeProject(":lifecycle:lifecycle-livedata-core-ktx-lint", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE])
 includeProject(":lifecycle:lifecycle-livedata-core-truth", [BuildType.MAIN, BuildType.FLAN])
-includeProject(":lifecycle:lifecycle-livedata-ktx", [BuildType.MAIN, BuildType.FLAN])
+includeProject(":lifecycle:lifecycle-livedata-ktx", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE])
 includeProject(":lifecycle:lifecycle-process", [BuildType.MAIN, BuildType.FLAN])
 includeProject(":lifecycle:lifecycle-reactivestreams", [BuildType.MAIN, BuildType.FLAN])
 includeProject(":lifecycle:lifecycle-reactivestreams-ktx", [BuildType.MAIN, BuildType.FLAN])
@@ -712,7 +712,7 @@
 includeProject(":lifecycle:lifecycle-viewmodel-compose", [BuildType.COMPOSE])
 includeProject(":lifecycle:lifecycle-viewmodel-compose:lifecycle-viewmodel-compose-samples", "lifecycle/lifecycle-viewmodel-compose/samples", [BuildType.COMPOSE])
 includeProject(":lifecycle:lifecycle-viewmodel-compose:integration-tests:lifecycle-viewmodel-demos", [BuildType.COMPOSE])
-includeProject(":lifecycle:lifecycle-viewmodel-ktx", [BuildType.MAIN, BuildType.FLAN, BuildType.COMPOSE])
+includeProject(":lifecycle:lifecycle-viewmodel-ktx", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE])
 includeProject(":lifecycle:lifecycle-viewmodel-savedstate", [BuildType.MAIN, BuildType.FLAN, BuildType.WEAR, BuildType.COMPOSE])
 includeProject(":lint-checks")
 includeProject(":lint-checks:integration-tests")
@@ -930,6 +930,7 @@
 includeProject(":window:integration-tests:configuration-change-tests", [BuildType.MAIN])
 includeProject(":window:sidecar:sidecar", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.CAMERA])
 includeProject(":window:window-java", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.CAMERA])
+includeProject(":window:window-core", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.CAMERA])
 includeProject(":window:window-rxjava2", [BuildType.MAIN])
 includeProject(":window:window-rxjava3", [BuildType.MAIN])
 includeProject(":window:window-samples", [BuildType.MAIN, BuildType.COMPOSE, BuildType.FLAN, BuildType.CAMERA])
diff --git a/sqlite/sqlite-ktx/build.gradle b/sqlite/sqlite-ktx/build.gradle
index f4e7351..32c89ad 100644
--- a/sqlite/sqlite-ktx/build.gradle
+++ b/sqlite/sqlite-ktx/build.gradle
@@ -27,7 +27,7 @@
     api(libs.kotlinStdlib)
 
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
 }
 
 androidx {
diff --git a/sqlite/sqlite/build.gradle b/sqlite/sqlite/build.gradle
index 8576750..47256e3 100644
--- a/sqlite/sqlite/build.gradle
+++ b/sqlite/sqlite/build.gradle
@@ -26,7 +26,7 @@
     api("androidx.annotation:annotation:1.0.0")
     implementation(libs.kotlinStdlib)
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.kotlinTest)
     testImplementation(libs.truth)
 }
diff --git a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/BaseTest.java b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/BaseTest.java
index 0f21e88..1648409 100644
--- a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/BaseTest.java
+++ b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/BaseTest.java
@@ -48,6 +48,7 @@
         mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
         mDevice.wakeUp();
         mDevice.pressHome();
+        mDevice.setOrientationNatural();
     }
 
     protected void launchTestActivity(@NonNull Class<? extends Activity> activity) {
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 ce4c857..30ee3ee 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
@@ -22,7 +22,9 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import android.app.UiAutomation;
 import android.graphics.Point;
+import android.os.SystemClock;
 import android.view.KeyEvent;
 import android.widget.TextView;
 
@@ -55,11 +57,8 @@
 public class UiDeviceTest extends BaseTest {
 
     private static final long TIMEOUT_MS = 5_000;
-
     private static final int GESTURE_MARGIN = 50;
-
     private static final String PACKAGE_NAME = "androidx.test.uiautomator.testapp";
-
     // Defined in 'AndroidManifest.xml'.
     private static final String APP_NAME = "UiAutomator Test App";
 
@@ -352,40 +351,43 @@
         assertEquals(PACKAGE_NAME, mDevice.getCurrentPackageName());
     }
 
+    @Test
+    public void testSetOrientationLeft() throws Exception {
+        launchTestActivity(KeycodeTestActivity.class);
+
+        assertTrue(mDevice.isNaturalOrientation());
+        assertEquals(UiAutomation.ROTATION_FREEZE_0, mDevice.getDisplayRotation());
+
+        mDevice.setOrientationLeft();
+        // Make the device wait for 1 sec for the rotation animation to finish.
+        SystemClock.sleep(1_000);
+        assertFalse(mDevice.isNaturalOrientation());
+        assertEquals(UiAutomation.ROTATION_FREEZE_90, mDevice.getDisplayRotation());
+
+        mDevice.setOrientationNatural();
+        SystemClock.sleep(1_000);
+        assertTrue(mDevice.isNaturalOrientation());
+    }
+
+    @Test
+    public void testSetOrientationRight() throws Exception {
+        launchTestActivity(KeycodeTestActivity.class);
+
+        assertTrue(mDevice.isNaturalOrientation());
+        assertEquals(UiAutomation.ROTATION_FREEZE_0, mDevice.getDisplayRotation());
+
+        mDevice.setOrientationRight();
+        SystemClock.sleep(1_000);
+        assertFalse(mDevice.isNaturalOrientation());
+        assertEquals(UiAutomation.ROTATION_FREEZE_270, mDevice.getDisplayRotation());
+
+        mDevice.setOrientationNatural();
+        SystemClock.sleep(1_000);
+        assertTrue(mDevice.isNaturalOrientation());
+    }
+
     /* TODO(b/235841020): Implement these tests, and the tests for exceptions of each tested method.
 
-    public void testRegisterWatcher() {}
-
-    public void testRemoveWatcher() {}
-
-    public void testRunWatchers() {}
-
-    public void testResetWatcherTriggers() {}
-
-    public void testHasWatcherTriggered() {}
-
-    public void testHasAnyWatcherTriggered() {}
-
-    public void testIsNaturalOrientation() {}
-
-    public void testGetDisplayRotation() {}
-
-    public void testFreezeRotation() {}
-
-    public void testUnfreezeRotation() {}
-
-    public void testSetOrientationLeft() {}
-
-    public void testSetOrientationRight() {}
-
-    public void testSetOrientationNatural() {}
-
-    public void testWakeUp() {}
-
-    public void testIsScreenOn() {}
-
-    public void testSleep() {}
-
     public void testDumpWindowHierarchy_withString() {}
 
     public void testDumpWindowHierarchy_withFile() {} // already added
diff --git a/test/uiautomator/uiautomator/src/androidTest/java/androidx/test/uiautomator/UiDeviceTest.java b/test/uiautomator/uiautomator/src/androidTest/java/androidx/test/uiautomator/UiDeviceTest.java
index 03248eb..9c49fce 100644
--- a/test/uiautomator/uiautomator/src/androidTest/java/androidx/test/uiautomator/UiDeviceTest.java
+++ b/test/uiautomator/uiautomator/src/androidTest/java/androidx/test/uiautomator/UiDeviceTest.java
@@ -17,6 +17,7 @@
 package androidx.test.uiautomator;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -28,13 +29,16 @@
 
 import android.app.Instrumentation;
 import android.app.UiAutomation;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.os.Build;
+import android.provider.Settings;
 import android.util.DisplayMetrics;
 import android.view.WindowManager;
 
+import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.SdkSuppress;
 import androidx.test.platform.app.InstrumentationRegistry;
 
@@ -49,6 +53,8 @@
 
 public class UiDeviceTest {
 
+    private static final String WATCHER_NAME = "test_watcher";
+
     @Rule
     public TemporaryFolder mTmpDir = new TemporaryFolder();
 
@@ -107,6 +113,78 @@
     }
 
     @Test
+    public void testRegisterAndRunUiWatcher_conditionMet() {
+        // The watcher will return true when its watching condition is met.
+        UiWatcher watcher = () -> true;
+        mDevice.registerWatcher(WATCHER_NAME, watcher);
+
+        assertFalse(mDevice.hasWatcherTriggered(WATCHER_NAME));
+        assertFalse(mDevice.hasAnyWatcherTriggered());
+        mDevice.runWatchers();
+        assertTrue(mDevice.hasWatcherTriggered(WATCHER_NAME));
+        assertTrue(mDevice.hasAnyWatcherTriggered());
+    }
+
+    @Test
+    public void testRegisterAndRunUiWatcher_conditionNotMet() {
+        UiWatcher watcher = () -> false;
+        mDevice.registerWatcher(WATCHER_NAME, watcher);
+
+        assertFalse(mDevice.hasWatcherTriggered(WATCHER_NAME));
+        assertFalse(mDevice.hasAnyWatcherTriggered());
+        mDevice.runWatchers();
+        assertFalse(mDevice.hasWatcherTriggered(WATCHER_NAME));
+        assertFalse(mDevice.hasAnyWatcherTriggered());
+    }
+
+    @Test
+    public void testResetUiWatcher() {
+        UiWatcher watcher = () -> true;
+        mDevice.registerWatcher(WATCHER_NAME, watcher);
+        mDevice.runWatchers();
+
+        assertTrue(mDevice.hasWatcherTriggered(WATCHER_NAME));
+        assertTrue(mDevice.hasAnyWatcherTriggered());
+        mDevice.resetWatcherTriggers();
+        assertFalse(mDevice.hasWatcherTriggered(WATCHER_NAME));
+        assertFalse(mDevice.hasAnyWatcherTriggered());
+    }
+
+    @Test
+    public void testRemoveUiWatcher() {
+        UiWatcher watcher = () -> true;
+        mDevice.registerWatcher(WATCHER_NAME, watcher);
+        mDevice.removeWatcher(WATCHER_NAME);
+        mDevice.runWatchers();
+
+        assertFalse(mDevice.hasWatcherTriggered(WATCHER_NAME));
+        assertFalse(mDevice.hasAnyWatcherTriggered());
+    }
+
+    @Test
+    public void testFreezeAndUnfreezeRotation() throws Exception {
+        ContentResolver resolver = ApplicationProvider.getApplicationContext().getContentResolver();
+
+        mDevice.freezeRotation();
+        // The value of `ACCELEROMETER_ROTATION` will be 0 if the accelerometer is NOT used for
+        // detecting rotation, and 1 otherwise.
+        assertEquals(0,
+                Settings.System.getInt(resolver, Settings.System.ACCELEROMETER_ROTATION, 0));
+
+        mDevice.unfreezeRotation();
+        assertEquals(1,
+                Settings.System.getInt(resolver, Settings.System.ACCELEROMETER_ROTATION, 0));
+    }
+
+    @Test
+    public void testWakeUpAndShutDownScreen() throws Exception {
+        mDevice.wakeUp();
+        assertTrue(mDevice.isScreenOn());
+        mDevice.sleep();
+        assertFalse(mDevice.isScreenOn());
+    }
+
+    @Test
     @SdkSuppress(maxSdkVersion = Build.VERSION_CODES.M)
     public void testGetUiAutomation_withoutFlags() {
         mDevice.getUiAutomation();
diff --git a/testutils/testutils-navigation/build.gradle b/testutils/testutils-navigation/build.gradle
index a76c992..b4e0204 100644
--- a/testutils/testutils-navigation/build.gradle
+++ b/testutils/testutils-navigation/build.gradle
@@ -29,7 +29,7 @@
     testImplementation(projectOrArtifact(":navigation:navigation-testing"))
     testImplementation("androidx.arch.core:core-testing:2.1.0")
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.truth)
     testImplementation(libs.kotlinCoroutinesTest)
 
diff --git a/testutils/testutils-runtime/src/main/java/androidx/testutils/RecreatedActivity.kt b/testutils/testutils-runtime/src/main/java/androidx/testutils/RecreatedActivity.kt
index d8eb3bc..8ddecfe 100644
--- a/testutils/testutils-runtime/src/main/java/androidx/testutils/RecreatedActivity.kt
+++ b/testutils/testutils-runtime/src/main/java/androidx/testutils/RecreatedActivity.kt
@@ -45,6 +45,7 @@
     override fun onDestroy() {
         super.onDestroy()
         destroyedLatch?.countDown()
+        activity = null
     }
 
     companion object {
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 dff94a5..1f8f66b 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-alpha05"
+#define VERSION "1.0.0-alpha06"
 
 namespace tracing_perfetto {
     void RegisterWithPerfetto() {
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 ec8ff95..3e52432 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-alpha05" // TODO: get using reflection
+        const val libraryVersion = "1.0.0-alpha06" // TODO: get using reflection
     }
 
     @Test
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 ee0e1b0..1d969f4 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
@@ -23,12 +23,12 @@
 
     // TODO(224510255): load from a file produced at build time
     object Metadata {
-        const val version = "1.0.0-alpha05"
+        const val version = "1.0.0-alpha06"
         val checksums = mapOf(
-            "arm64-v8a" to "86fbcded1a071253e6b1ec8ac820b3f5f8c47a727beb9eb10f73b6ac0fbdfa7d",
-            "armeabi-v7a" to "0ec22f0516b0c46a6edd2b7e3f1bbae25e28874780ab2d881a188c9f56e11f5a",
-            "x86" to "f360e949c9b6659318ca010fda67bf35608f596d20430724941e444e25ba7097",
-            "x86_64" to "219cc54c2fda8f777b71809910c1c0fce4aeb8e0ccd3dc8861fb7afa1dc5f9aa",
+            "arm64-v8a" to "c8a60a491ef1381c2c95e6281401251dbc9c128a402506bb93681d11cb01e2f7",
+            "armeabi-v7a" to "1bdd59a655c574d561087389863d110b4193458f6baf7d539847d37516caf477",
+            "x86" to "889c130b1a028cabf288af1a8f94bc430edf06bb740854ac43073b3db7e59927",
+            "x86_64" to "2757be9d9b44b59b2c69c0afad68b0e6871e11a53f453bb777911585e0e84086",
         )
     }
 
diff --git a/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/PlaceholderTest.kt b/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/PlaceholderTest.kt
index cd637cf..8284aad 100644
--- a/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/PlaceholderTest.kt
+++ b/wear/compose/compose-material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/PlaceholderTest.kt
@@ -18,6 +18,7 @@
 
 import android.os.Build
 import androidx.annotation.RequiresApi
+import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
@@ -28,6 +29,7 @@
 import androidx.compose.ui.test.onNodeWithTag
 import org.junit.Rule
 import org.junit.Test
+import com.google.common.truth.Truth.assertThat
 
 class PlaceholderTest {
     @get:Rule
@@ -36,48 +38,249 @@
     @RequiresApi(Build.VERSION_CODES.O)
     @OptIn(ExperimentalWearMaterialApi::class)
     @Test
-    fun placeholder_is_correct_color() {
-        var expectedPlaceholderColor = Color.Transparent
-        // var expectedBackgroundColor = Color.Transparent
+    fun placeholder_initially_show_content() {
+        var contentReady = true
+        lateinit var placeholderState: PlaceholderState
+        rule.setContentWithTheme {
+            placeholderState = rememberPlaceholderState {
+                contentReady
+            }
+            Chip(
+                modifier = Modifier
+                    .testTag("test-item")
+                    .fillMaxWidth(),
+                content = {},
+                onClick = {},
+                colors = ChipDefaults.secondaryChipColors(),
+                border = ChipDefaults.chipBorder()
+            )
+        }
+
+        // For testing we need to manually manage the frame clock for the placeholder animation
+        placeholderState.initializeTestFrameMillis(PlaceholderStage.ShowContent)
+
+        // Advance placeholder clock without changing the content ready and confirm still in
+        // ShowPlaceholder
+        placeholderState.advanceToNextPlaceholderAnimationLoopAndCheckStage(
+            PlaceholderStage.ShowContent
+        )
+
+        contentReady = false
+
+        // Check that the state does not go to ShowPlaceholder
+        placeholderState.advanceToNextPlaceholderAnimationLoopAndCheckStage(
+            PlaceholderStage.ShowContent
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @OptIn(ExperimentalWearMaterialApi::class)
+    @Test
+    fun placeholder_initially_show_placeholder_transitions_correctly() {
         var contentReady = false
         lateinit var placeholderState: PlaceholderState
         rule.setContentWithTheme {
             placeholderState = rememberPlaceholderState {
                 contentReady
             }
-            expectedPlaceholderColor = MaterialTheme.colors.onSurface.copy(alpha = 0.1f)
-                .compositeOver(MaterialTheme.colors.surface)
-            // expectedBackgroundColor = MaterialTheme.colors.primary
             Chip(
                 modifier = Modifier
                     .testTag("test-item")
-                    .placeholder(placeholderState = placeholderState),
+                    .fillMaxWidth(),
+                content = {},
+                onClick = {},
+                colors = ChipDefaults.secondaryChipColors(),
+                border = ChipDefaults.chipBorder()
+            )
+        }
+
+        // For testing we need to manually manage the frame clock for the placeholder animation
+        placeholderState.initializeTestFrameMillis()
+
+        // Advance placeholder clock without changing the content ready and confirm still in
+        // ShowPlaceholder
+        placeholderState.advanceFrameMillisAndCheckState(
+            PLACEHOLDER_GAP_BETWEEN_ANIMATION_LOOPS_MS,
+            PlaceholderStage.ShowPlaceholder)
+
+        // Change contentReady and confirm that state is still ShowPlaceholder
+        contentReady = true
+        placeholderState.advanceFrameMillisAndCheckState(
+            0L,
+            PlaceholderStage.ShowPlaceholder
+        )
+
+        // Advance the clock by one cycle and check we have moved to WipeOff
+        placeholderState.advanceFrameMillisAndCheckState(
+            PLACEHOLDER_GAP_BETWEEN_ANIMATION_LOOPS_MS,
+            PlaceholderStage.WipeOff
+        )
+
+        // Advance the clock by one cycle and check we have moved to ShowContent
+        placeholderState.advanceFrameMillisAndCheckState(
+            PLACEHOLDER_GAP_BETWEEN_ANIMATION_LOOPS_MS,
+            PlaceholderStage.ShowContent
+        )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun default_placeholder_is_correct_color() {
+        placeholder_is_correct_color(null)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @Test
+    fun custom_placeholder_is_correct_color() {
+        placeholder_is_correct_color(Color.Blue)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @OptIn(ExperimentalWearMaterialApi::class)
+    fun placeholder_is_correct_color(placeholderColor: Color?) {
+        var expectedPlaceholderColor = Color.Transparent
+        var expectedBackgroundColor = Color.Transparent
+        var contentReady = false
+        lateinit var placeholderState: PlaceholderState
+        rule.setContentWithTheme {
+            placeholderState = rememberPlaceholderState {
+                contentReady
+            }
+            expectedPlaceholderColor =
+                placeholderColor
+                    ?: MaterialTheme.colors.onSurface.copy(alpha = 0.1f)
+                        .compositeOver(MaterialTheme.colors.surface)
+            expectedBackgroundColor = MaterialTheme.colors.primary
+            Chip(
+                modifier = Modifier
+                    .testTag("test-item")
+                    .then(
+                        if (placeholderColor != null)
+                            Modifier.placeholder(
+                                placeholderState = placeholderState,
+                                color = placeholderColor
+                            )
+                        else Modifier.placeholder(placeholderState = placeholderState)
+                    ),
                 content = {},
                 onClick = {},
                 colors = ChipDefaults.primaryChipColors(),
                 border = ChipDefaults.chipBorder()
             )
-            LaunchedEffect(placeholderState) {
-                placeholderState.startPlaceholderAnimation()
-            }
         }
 
+        // For testing we need to manually manage the frame clock for the placeholder animation
+        placeholderState.initializeTestFrameMillis()
+
         rule.onNodeWithTag("test-item")
             .captureToImage()
             .assertContainsColor(
                 expectedPlaceholderColor
             )
 
-//        contentReady = true
-//        // Ideally advance the clock in order to let the wipe off take effect.
-//        // However this doesn't work with withInfiniteAnimationFrameMillis as it is not delivered
-//        // any frame timings so we will need to look for a different way to test
-//        rule.mainClock.advanceTimeBy(3500)
-//        rule.onNodeWithTag("test-item")
-//            .captureToImage()
-//            .assertContainsColor(
-//                expectedBackgroundColor
-//            )
+        contentReady = true
+
+        // Advance the clock to the next placeholder animation loop and check for wipe-off mode
+        placeholderState
+            .advanceToNextPlaceholderAnimationLoopAndCheckStage(PlaceholderStage.WipeOff)
+
+        // Advance the clock to the next placeholder animation loop and check for show content mode
+        placeholderState
+            .advanceToNextPlaceholderAnimationLoopAndCheckStage(PlaceholderStage.ShowContent)
+
+        rule.onNodeWithTag("test-item")
+            .captureToImage()
+            .assertContainsColor(
+                expectedBackgroundColor
+            )
+    }
+
+    @RequiresApi(Build.VERSION_CODES.O)
+    @OptIn(ExperimentalWearMaterialApi::class)
+    @Test
+    fun placeholder_shimmer_visible_during_showplaceholder_only() {
+        var expectedBackgroundColor = Color.Transparent
+        var contentReady = false
+        lateinit var placeholderState: PlaceholderState
+        var expectedShimmerColor = Color.Transparent
+        rule.setContentWithTheme {
+            placeholderState = rememberPlaceholderState {
+                contentReady
+            }
+            expectedBackgroundColor = MaterialTheme.colors.surface
+            expectedShimmerColor = MaterialTheme.colors.onSurface.copy(0.13f)
+                .compositeOver(expectedBackgroundColor)
+            Chip(
+                modifier = Modifier
+                    .testTag("test-item")
+                    .fillMaxWidth()
+                    .placeholderShimmer(placeholderState = placeholderState),
+                content = {},
+                onClick = {},
+                colors = ChipDefaults.secondaryChipColors(),
+                border = ChipDefaults.chipBorder()
+            )
+        }
+
+        placeholderState.initializeTestFrameMillis()
+
+        // Check the background color is correct
+        rule.onNodeWithTag("test-item")
+            .captureToImage()
+            .assertContainsColor(
+                expectedBackgroundColor, 80f
+            )
+        // Check that there is no shimmer color
+        rule.onNodeWithTag("test-item")
+            .captureToImage()
+            .assertDoesNotContainColor(
+                expectedShimmerColor
+            )
+
+        // Move the start of the next placeholder animation loop and them advance the clock 200
+        // milliseconds (PLACEHOLDER_PROGRESSION_DURATION_MS / 4) to show the shimmer.
+        //
+        // We choose (PLACEHOLDER_PROGRESSION_DURATION_MS / 4) as this should put the center of the
+        // shimmer gradiant at the top left (0,0) of the screen and as we have placed the component
+        // at the top of the screen this should ensure we have some shimmer gradiant cast over the
+        // component regardless of the screen size/shape. So should work for round, square or
+        // rectangular screens.
+        placeholderState.moveToStartOfNextAnimationLoop()
+        placeholderState.advanceFrameMillisAndCheckState(
+            PLACEHOLDER_PROGRESSION_DURATION_MS / 4,
+            PlaceholderStage.ShowPlaceholder
+        )
+
+        // The placeholder shimmer effect is faint and largely transparent gradiant, so we are
+        // looking for a very small amount to be visible.
+        rule.onNodeWithTag("test-item")
+            .captureToImage()
+            .assertContainsColor(
+                expectedShimmerColor, 1f
+            )
+
+        // Prepare to start to wipe off and show contents.
+        contentReady = true
+
+        placeholderState
+            .advanceToNextPlaceholderAnimationLoopAndCheckStage(PlaceholderStage.WipeOff)
+
+        // Check that the shimmer is no longer visible
+        rule.onNodeWithTag("test-item")
+            .captureToImage()
+            .assertDoesNotContainColor(
+                expectedShimmerColor
+            )
+
+        placeholderState
+            .advanceToNextPlaceholderAnimationLoopAndCheckStage(PlaceholderStage.ShowContent)
+
+        // Check that the shimmer is no longer visible
+        rule.onNodeWithTag("test-item")
+            .captureToImage()
+            .assertDoesNotContainColor(
+                expectedShimmerColor
+            )
     }
 
     @RequiresApi(Build.VERSION_CODES.O)
@@ -85,7 +288,7 @@
     @Test
     fun placeholder_background_is_correct_color() {
         var expectedPlaceholderBackgroundColor = Color.Transparent
-        // var expectedBackgroundColor = Color.Transparent
+        var expectedBackgroundColor = Color.Transparent
         var contentReady = false
         lateinit var placeholderState: PlaceholderState
         rule.setContentWithTheme {
@@ -93,7 +296,7 @@
                 contentReady
             }
             expectedPlaceholderBackgroundColor = MaterialTheme.colors.surface
-            // expectedBackgroundColor = MaterialTheme.colors.primary
+            expectedBackgroundColor = MaterialTheme.colors.primary
             Chip(
                 modifier = Modifier
                     .testTag("test-item"),
@@ -110,21 +313,70 @@
             }
         }
 
+        placeholderState.initializeTestFrameMillis()
+
         rule.onNodeWithTag("test-item")
             .captureToImage()
             .assertContainsColor(
                 expectedPlaceholderBackgroundColor
             )
 
-//        contentReady = true
-//        // Ideally advance the clock in order to let the wipe off take effect.
-//        // However this doesn't work with withInfiniteAnimationFrameMillis as it is not delivered
-//        // any frame timings so we will need to look for a different way to test
-//        rule.mainClock.advanceTimeBy(3500)
-//        rule.onNodeWithTag("test-item")
-//            .captureToImage()
-//            .assertContainsColor(
-//                expectedBackgroundColor
-//            )
+        contentReady = true
+
+        // Advance the clock to the next placeholder animation loop to move into wipe-off mode
+        placeholderState
+            .advanceToNextPlaceholderAnimationLoopAndCheckStage(PlaceholderStage.WipeOff)
+
+        // Advance the clock to the next placeholder animation loop to move into show content mode
+        placeholderState
+            .advanceToNextPlaceholderAnimationLoopAndCheckStage(PlaceholderStage.ShowContent)
+
+        rule.onNodeWithTag("test-item")
+            .captureToImage()
+            .assertContainsColor(
+                expectedBackgroundColor
+            )
+    }
+
+    @OptIn(ExperimentalWearMaterialApi::class)
+    private fun PlaceholderState.advanceFrameMillisAndCheckState(
+        timeToAdd: Long,
+        expectedStage: PlaceholderStage
+    ) {
+        frameMillis.value += timeToAdd
+        rule.waitForIdle()
+        assertThat(placeholderStage).isEqualTo(expectedStage)
+    }
+
+    @OptIn(ExperimentalWearMaterialApi::class)
+    private fun PlaceholderState.advanceToNextPlaceholderAnimationLoopAndCheckStage(
+        expectedStage: PlaceholderStage
+    ) {
+        frameMillis.value += PLACEHOLDER_GAP_BETWEEN_ANIMATION_LOOPS_MS
+        rule.waitForIdle()
+        assertThat(placeholderStage).isEqualTo(expectedStage)
+    }
+
+    @OptIn(ExperimentalWearMaterialApi::class)
+    private fun PlaceholderState.initializeTestFrameMillis(
+        initialPlaceholderStage: PlaceholderStage = PlaceholderStage.ShowPlaceholder
+    ): Long {
+        val currentTime = rule.mainClock.currentTime
+        frameMillis.value = currentTime
+        rule.waitForIdle()
+        assertThat(placeholderStage).isEqualTo(initialPlaceholderStage)
+        return currentTime
+    }
+
+    @OptIn(ExperimentalWearMaterialApi::class)
+    private fun PlaceholderState.moveToStartOfNextAnimationLoop(
+        expectedPlaceholderStage: PlaceholderStage = PlaceholderStage.ShowPlaceholder
+    ) {
+        val animationLoopStart =
+            (frameMillis.value.div(PLACEHOLDER_GAP_BETWEEN_ANIMATION_LOOPS_MS) + 1) *
+            PLACEHOLDER_GAP_BETWEEN_ANIMATION_LOOPS_MS
+        frameMillis.value = animationLoopStart
+        rule.waitForIdle()
+        assertThat(placeholderStage).isEqualTo(expectedPlaceholderStage)
     }
 }
\ No newline at end of file
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Picker.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Picker.kt
index 89be03e..daf68e7 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Picker.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Picker.kt
@@ -140,16 +140,16 @@
                 }
             }.then(
                 if (!readOnly && gradientRatio > 0.0f) {
-                    Modifier
-                        .drawWithContent {
-                            drawContent()
-                            drawGradient(gradientColor, gradientRatio)
-                        }
-                        // b/223386180 - add padding when drawing rectangles to
-                        // prevent jitter on screen.
-                        .padding(vertical = 1.dp)
-                        .align(Alignment.Center)
-                } else if (readOnly) {
+                        Modifier
+                            .drawWithContent {
+                                drawContent()
+                                drawGradient(gradientColor, gradientRatio)
+                            }
+                            // b/223386180 - add padding when drawing rectangles to
+                            // prevent jitter on screen.
+                            .padding(vertical = 1.dp)
+                            .align(Alignment.Center)
+                    } else if (readOnly) {
                     Modifier
                         .drawWithContent {
                             drawContent()
@@ -185,7 +185,8 @@
             verticalArrangement = Arrangement.spacedBy(
                 space = separation
             ),
-            flingBehavior = flingBehavior
+            flingBehavior = flingBehavior,
+            autoCentering = AutoCenteringParams(itemIndex = 0)
         )
         if (readOnly && readOnlyLabel != null) {
             readOnlyLabel()
diff --git a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Placeholder.kt b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Placeholder.kt
index b50b9db..b3d563a 100644
--- a/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Placeholder.kt
+++ b/wear/compose/compose-material/src/commonMain/kotlin/androidx/wear/compose/material/Placeholder.kt
@@ -704,7 +704,7 @@
     }
 }
 
-private const val PLACEHOLDER_PROGRESSION_DURATION_MS = 800L
-private const val PLACEHOLDER_DELAY_BETWEEN_PROGRESSIONS_MS = 800L
-private const val PLACEHOLDER_GAP_BETWEEN_ANIMATION_LOOPS_MS =
+internal const val PLACEHOLDER_PROGRESSION_DURATION_MS = 800L
+internal const val PLACEHOLDER_DELAY_BETWEEN_PROGRESSIONS_MS = 800L
+internal const val PLACEHOLDER_GAP_BETWEEN_ANIMATION_LOOPS_MS =
     PLACEHOLDER_PROGRESSION_DURATION_MS + PLACEHOLDER_DELAY_BETWEEN_PROGRESSIONS_MS
\ No newline at end of file
diff --git a/wear/tiles/tiles-material/build.gradle b/wear/tiles/tiles-material/build.gradle
index a7156b4c..55ad39a 100644
--- a/wear/tiles/tiles-material/build.gradle
+++ b/wear/tiles/tiles-material/build.gradle
@@ -41,7 +41,7 @@
     androidTestImplementation(libs.protobuf)
 
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.robolectric)
     testImplementation(libs.testExtJunit)
     testImplementation(libs.testExtTruth)
diff --git a/wear/tiles/tiles-renderer/build.gradle b/wear/tiles/tiles-renderer/build.gradle
index bd54c58..33e7c27 100644
--- a/wear/tiles/tiles-renderer/build.gradle
+++ b/wear/tiles/tiles-renderer/build.gradle
@@ -62,8 +62,8 @@
     testImplementation(libs.testRules)
     testImplementation(libs.kotlinCoroutinesTest)
     testImplementation(libs.robolectric)
-    testImplementation(libs.mockitoCore)
-    testImplementation(libs.mockitoKotlin)
+    testImplementation(libs.mockitoCore4)
+    testImplementation(libs.mockitoKotlin4)
     testImplementation(libs.truth)
 }
 
diff --git a/wear/tiles/tiles-renderer/src/test/java/androidx/wear/tiles/checkers/TimelineCheckerTest.kt b/wear/tiles/tiles-renderer/src/test/java/androidx/wear/tiles/checkers/TimelineCheckerTest.kt
index 7389c34..2886608 100644
--- a/wear/tiles/tiles-renderer/src/test/java/androidx/wear/tiles/checkers/TimelineCheckerTest.kt
+++ b/wear/tiles/tiles-renderer/src/test/java/androidx/wear/tiles/checkers/TimelineCheckerTest.kt
@@ -20,13 +20,13 @@
 import androidx.wear.tiles.TilesTestRunner
 import androidx.wear.tiles.TimelineBuilders
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.any
-import com.nhaarman.mockitokotlin2.argumentCaptor
-import com.nhaarman.mockitokotlin2.doReturn
-import com.nhaarman.mockitokotlin2.doThrow
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.times
-import com.nhaarman.mockitokotlin2.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.doThrow
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
 import org.junit.Test
 import org.junit.runner.RunWith
 
diff --git a/wear/tiles/tiles/build.gradle b/wear/tiles/tiles/build.gradle
index f316baf..b7df1ba 100644
--- a/wear/tiles/tiles/build.gradle
+++ b/wear/tiles/tiles/build.gradle
@@ -38,7 +38,7 @@
     testImplementation(libs.testRules)
     testImplementation("androidx.concurrent:concurrent-futures:1.1.0")
     testImplementation(libs.robolectric)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
 }
 
 android {
diff --git a/wear/watchface/watchface-client/api/current.txt b/wear/watchface/watchface-client/api/current.txt
index b596ff3..40bcc03 100644
--- a/wear/watchface/watchface-client/api/current.txt
+++ b/wear/watchface/watchface-client/api/current.txt
@@ -163,7 +163,7 @@
     field public static final int TAP_TYPE_UP = 2; // 0x2
   }
 
-  public static interface InteractiveWatchFaceClient.ClientDisconnectListener {
+  @kotlin.jvm.JvmDefaultWithCompatibility public static interface InteractiveWatchFaceClient.ClientDisconnectListener {
     method @Deprecated public default void onClientDisconnected();
     method public default void onClientDisconnected(int disconnectReason);
   }
diff --git a/wear/watchface/watchface-client/api/public_plus_experimental_current.txt b/wear/watchface/watchface-client/api/public_plus_experimental_current.txt
index 9e0648e..5d787a5 100644
--- a/wear/watchface/watchface-client/api/public_plus_experimental_current.txt
+++ b/wear/watchface/watchface-client/api/public_plus_experimental_current.txt
@@ -169,7 +169,7 @@
     field public static final int TAP_TYPE_UP = 2; // 0x2
   }
 
-  public static interface InteractiveWatchFaceClient.ClientDisconnectListener {
+  @kotlin.jvm.JvmDefaultWithCompatibility public static interface InteractiveWatchFaceClient.ClientDisconnectListener {
     method @Deprecated public default void onClientDisconnected();
     method public default void onClientDisconnected(int disconnectReason);
   }
diff --git a/wear/watchface/watchface-client/api/restricted_current.txt b/wear/watchface/watchface-client/api/restricted_current.txt
index b596ff3..40bcc03 100644
--- a/wear/watchface/watchface-client/api/restricted_current.txt
+++ b/wear/watchface/watchface-client/api/restricted_current.txt
@@ -163,7 +163,7 @@
     field public static final int TAP_TYPE_UP = 2; // 0x2
   }
 
-  public static interface InteractiveWatchFaceClient.ClientDisconnectListener {
+  @kotlin.jvm.JvmDefaultWithCompatibility public static interface InteractiveWatchFaceClient.ClientDisconnectListener {
     method @Deprecated public default void onClientDisconnected();
     method public default void onClientDisconnected(int disconnectReason);
   }
diff --git a/wear/watchface/watchface-client/build.gradle b/wear/watchface/watchface-client/build.gradle
index a5c8e08..48e14cb 100644
--- a/wear/watchface/watchface-client/build.gradle
+++ b/wear/watchface/watchface-client/build.gradle
@@ -41,8 +41,8 @@
     androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(libs.truth)
 
-    testImplementation(libs.mockitoCore)
-    testImplementation(libs.mockitoKotlin)
+    testImplementation(libs.mockitoCore4)
+    testImplementation(libs.mockitoKotlin4)
     testImplementation(libs.robolectric)
     testImplementation(libs.testCore)
     testImplementation(libs.testExtJunit)
@@ -62,6 +62,9 @@
     // Use Robolectric 4.+
     testOptions.unitTests.includeAndroidResources = true
     namespace "androidx.wear.watchface.client"
+    kotlinOptions {
+        freeCompilerArgs += ["-Xjvm-default=all"]
+    }
 }
 
 androidx {
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 30d5c48..c30e3fa 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
@@ -2463,6 +2463,7 @@
         attachBaseContext(testContext)
     }
 
+    @Suppress("deprecation")
     private val complicationsStyleSetting =
         UserStyleSetting.ComplicationSlotsUserStyleSetting(
             UserStyleSetting.Id(COMPLICATIONS_STYLE_SETTING),
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 08e68ed..74fc74f 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
@@ -247,6 +247,7 @@
      * Callback that observes when the client disconnects. Use [addClientDisconnectListener] to
      * register a ClientDisconnectListener.
      */
+    @JvmDefaultWithCompatibility
     public interface ClientDisconnectListener {
         /**
          * The client disconnected, typically due to the server side crashing. Note this is not
diff --git a/wear/watchface/watchface-client/src/test/java/androidx/wear/watchface/client/InteractiveWatchFaceClientTest.kt b/wear/watchface/watchface-client/src/test/java/androidx/wear/watchface/client/InteractiveWatchFaceClientTest.kt
index 8a468f0..931ae1f 100644
--- a/wear/watchface/watchface-client/src/test/java/androidx/wear/watchface/client/InteractiveWatchFaceClientTest.kt
+++ b/wear/watchface/watchface-client/src/test/java/androidx/wear/watchface/client/InteractiveWatchFaceClientTest.kt
@@ -18,10 +18,10 @@
 
 import android.os.IBinder
 import androidx.wear.watchface.control.IInteractiveWatchFace
-import com.nhaarman.mockitokotlin2.any
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.times
-import com.nhaarman.mockitokotlin2.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.`when`
diff --git a/wear/watchface/watchface-client/src/test/java/androidx/wear/watchface/client/JavaCompatTest.java b/wear/watchface/watchface-client/src/test/java/androidx/wear/watchface/client/JavaCompatTest.java
new file mode 100644
index 0000000..1270b7b
--- /dev/null
+++ b/wear/watchface/watchface-client/src/test/java/androidx/wear/watchface/client/JavaCompatTest.java
@@ -0,0 +1,27 @@
+/*
+ * 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.wear.watchface.client;
+
+/** Tests that Java interfaces implementing kotlin interfaces with defaults compile. */
+public class JavaCompatTest {
+    class ClientDisconnectListenerImpl implements
+            InteractiveWatchFaceClient.ClientDisconnectListener {
+        @Override
+        @SuppressWarnings("deprecation")
+        public void onClientDisconnected() {}
+    }
+}
diff --git a/wear/watchface/watchface-client/src/test/java/androidx/wear/watchface/client/WatchFaceMetadataClientTest.kt b/wear/watchface/watchface-client/src/test/java/androidx/wear/watchface/client/WatchFaceMetadataClientTest.kt
index bd75cf4..43ff4bc 100644
--- a/wear/watchface/watchface-client/src/test/java/androidx/wear/watchface/client/WatchFaceMetadataClientTest.kt
+++ b/wear/watchface/watchface-client/src/test/java/androidx/wear/watchface/client/WatchFaceMetadataClientTest.kt
@@ -28,10 +28,10 @@
 import androidx.wear.watchface.control.IWatchFaceControlService
 import androidx.wear.watchface.data.ComplicationSlotMetadataWireFormat
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.any
+import org.mockito.kotlin.any
 import org.junit.Test
 import org.junit.runner.RunWith
-import com.nhaarman.mockitokotlin2.mock
+import org.mockito.kotlin.mock
 import org.mockito.Mockito.`when`
 
 @RunWith(ClientTestRunner::class)
diff --git a/wear/watchface/watchface-complications-data-source/api/current.txt b/wear/watchface/watchface-complications-data-source/api/current.txt
index d57c497..0aaf430 100644
--- a/wear/watchface/watchface-complications-data-source/api/current.txt
+++ b/wear/watchface/watchface-complications-data-source/api/current.txt
@@ -27,7 +27,7 @@
   public static final class ComplicationDataSourceService.Companion {
   }
 
-  public static interface ComplicationDataSourceService.ComplicationRequestListener {
+  @kotlin.jvm.JvmDefaultWithCompatibility public static interface ComplicationDataSourceService.ComplicationRequestListener {
     method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public void onComplicationData(androidx.wear.watchface.complications.data.ComplicationData? complicationData) throws android.os.RemoteException;
     method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public default void onComplicationDataTimeline(androidx.wear.watchface.complications.datasource.ComplicationDataTimeline? complicationDataTimeline) throws android.os.RemoteException;
   }
diff --git a/wear/watchface/watchface-complications-data-source/api/public_plus_experimental_current.txt b/wear/watchface/watchface-complications-data-source/api/public_plus_experimental_current.txt
index d57c497..0aaf430 100644
--- a/wear/watchface/watchface-complications-data-source/api/public_plus_experimental_current.txt
+++ b/wear/watchface/watchface-complications-data-source/api/public_plus_experimental_current.txt
@@ -27,7 +27,7 @@
   public static final class ComplicationDataSourceService.Companion {
   }
 
-  public static interface ComplicationDataSourceService.ComplicationRequestListener {
+  @kotlin.jvm.JvmDefaultWithCompatibility public static interface ComplicationDataSourceService.ComplicationRequestListener {
     method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public void onComplicationData(androidx.wear.watchface.complications.data.ComplicationData? complicationData) throws android.os.RemoteException;
     method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public default void onComplicationDataTimeline(androidx.wear.watchface.complications.datasource.ComplicationDataTimeline? complicationDataTimeline) throws android.os.RemoteException;
   }
diff --git a/wear/watchface/watchface-complications-data-source/api/restricted_current.txt b/wear/watchface/watchface-complications-data-source/api/restricted_current.txt
index d57c497..0aaf430 100644
--- a/wear/watchface/watchface-complications-data-source/api/restricted_current.txt
+++ b/wear/watchface/watchface-complications-data-source/api/restricted_current.txt
@@ -27,7 +27,7 @@
   public static final class ComplicationDataSourceService.Companion {
   }
 
-  public static interface ComplicationDataSourceService.ComplicationRequestListener {
+  @kotlin.jvm.JvmDefaultWithCompatibility public static interface ComplicationDataSourceService.ComplicationRequestListener {
     method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public void onComplicationData(androidx.wear.watchface.complications.data.ComplicationData? complicationData) throws android.os.RemoteException;
     method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public default void onComplicationDataTimeline(androidx.wear.watchface.complications.datasource.ComplicationDataTimeline? complicationDataTimeline) throws android.os.RemoteException;
   }
diff --git a/wear/watchface/watchface-complications-data-source/build.gradle b/wear/watchface/watchface-complications-data-source/build.gradle
index 31a1651..a60100a 100644
--- a/wear/watchface/watchface-complications-data-source/build.gradle
+++ b/wear/watchface/watchface-complications-data-source/build.gradle
@@ -33,7 +33,7 @@
     testImplementation(libs.testRunner)
     testImplementation(libs.testRules)
     testImplementation(libs.robolectric)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.truth)
     testImplementation(libs.junit)
 }
@@ -49,6 +49,9 @@
     // Use Robolectric 4.+
     testOptions.unitTests.includeAndroidResources = true
     namespace "androidx.wear.watchface.complications.datasource"
+    kotlinOptions {
+        freeCompilerArgs += ["-Xjvm-default=all"]
+    }
 }
 
 androidx {
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 fd936bf..ecc3af3 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
@@ -271,6 +271,7 @@
      * Callback for [onComplicationRequest] where only one of [onComplicationData] or
      * [onComplicationDataTimeline] should be called.
      */
+    @JvmDefaultWithCompatibility
     public interface ComplicationRequestListener {
         /**
          * Sends the [ComplicationData] to the system. If null is passed then any previous
diff --git a/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceServiceTest.java b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceServiceTest.java
index 0857efc..cc84470 100644
--- a/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceServiceTest.java
+++ b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataSourceServiceTest.java
@@ -251,6 +251,7 @@
                 }
             };
 
+    @SuppressWarnings("deprecation") // b/251211092
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataTimelineTest.java b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataTimelineTest.java
index 96dec62..14afc68 100644
--- a/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataTimelineTest.java
+++ b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/ComplicationDataTimelineTest.java
@@ -138,8 +138,9 @@
                         + "tapAction=null, validTimeRange=TimeRange("
                         + "startDateTimeMillis=-1000000000-01-01T00:00:00Z, "
                         + "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), "
-                        + "dataSource=null), timelineEntries=[TimelineEntry(validity=TimeInterval("
-                        + "start=1970-01-02T03:46:40Z, end=1970-01-03T07:33:20Z), "
+                        + "dataSource=null, persistencePolicy=0, displayPolicy=0), "
+                        + "timelineEntries=[TimelineEntry(validity"
+                        + "=TimeInterval(start=1970-01-02T03:46:40Z, end=1970-01-03T07:33:20Z), "
                         + "complicationData=ShortTextComplicationData(text=ComplicationText{"
                         + "mSurroundingText=Updated, mTimeDependentText=null}, title=null, "
                         + "monochromaticImage=null, smallImage=null, contentDescription="
@@ -147,7 +148,7 @@
                         + "tapActionLostDueToSerialization=false, tapAction=null, validTimeRange="
                         + "TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, "
                         + "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), "
-                        + "dataSource=null))])"
+                        + "dataSource=null, persistencePolicy=0, displayPolicy=0))])"
         );
     }
 
diff --git a/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/JavaCompatTest.java b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/JavaCompatTest.java
new file mode 100644
index 0000000..156d273
--- /dev/null
+++ b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/JavaCompatTest.java
@@ -0,0 +1,34 @@
+/*
+ * 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.wear.watchface.complications.datasource;
+
+import android.os.RemoteException;
+
+import androidx.annotation.Nullable;
+import androidx.wear.watchface.complications.data.ComplicationData;
+
+/** Tests that Java interfaces implementing kotlin interfaces with defaults compile. */
+public class JavaCompatTest {
+    class ComplicationRequestListenerImpl implements
+            ComplicationDataSourceService.ComplicationRequestListener {
+
+        @Override
+        public void onComplicationData(
+                @Nullable ComplicationData complicationData) throws RemoteException {
+        }
+    }
+}
diff --git a/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/TimelineEntryTest.java b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/TimelineEntryTest.java
index dd0931c..94a6ed9 100644
--- a/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/TimelineEntryTest.java
+++ b/wear/watchface/watchface-complications-data-source/src/test/java/androidx/wear/watchface/complications/datasource/TimelineEntryTest.java
@@ -88,7 +88,8 @@
                         + "placeholder=null, "
                         + "tapActionLostDueToSerialization=false, tapAction=null, "
                         + "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00"
-                        + ":00Z, endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z)))"
+                        + ":00Z, endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), "
+                        + "persistencePolicy=0, displayPolicy=0))"
         );
     }
 }
diff --git a/wear/watchface/watchface-complications-data/api/current.txt b/wear/watchface/watchface-complications-data/api/current.txt
index 890e4a6..d385ad9 100644
--- a/wear/watchface/watchface-complications-data/api/current.txt
+++ b/wear/watchface/watchface-complications-data/api/current.txt
@@ -3,7 +3,9 @@
 
   public abstract sealed class ComplicationData {
     method public final android.content.ComponentName? getDataSource();
+    method public final int getDisplayPolicy();
     method public java.time.Instant getNextChangeInstant(java.time.Instant afterInstant);
+    method public final int getPersistencePolicy();
     method public final android.app.PendingIntent? getTapAction();
     method public final androidx.wear.watchface.complications.data.ComplicationType getType();
     method public final androidx.wear.watchface.complications.data.TimeRange getValidTimeRange();
@@ -11,12 +13,26 @@
     method public final boolean isTapActionLostDueToSerialization();
     method public final void setTapActionLostDueToSerialization(boolean);
     property public final android.content.ComponentName? dataSource;
+    property public final int displayPolicy;
+    property public final int persistencePolicy;
     property public final android.app.PendingIntent? tapAction;
     property public final boolean tapActionLostDueToSerialization;
     property public final androidx.wear.watchface.complications.data.ComplicationType type;
     property public final androidx.wear.watchface.complications.data.TimeRange validTimeRange;
   }
 
+  public final class ComplicationDisplayPolicies {
+    field public static final int ALWAYS_DISPLAY = 0; // 0x0
+    field public static final int DO_NOT_SHOW_WHEN_DEVICE_LOCKED = 1; // 0x1
+    field public static final androidx.wear.watchface.complications.data.ComplicationDisplayPolicies INSTANCE;
+  }
+
+  public final class ComplicationPersistencePolicies {
+    field public static final int CACHING_ALLOWED = 0; // 0x0
+    field public static final int DO_NOT_PERSIST = 1; // 0x1
+    field public static final androidx.wear.watchface.complications.data.ComplicationPersistencePolicies INSTANCE;
+  }
+
   public interface ComplicationText {
     method public java.time.Instant getNextChangeTime(java.time.Instant afterInstant);
     method public CharSequence getTextAt(android.content.res.Resources resources, java.time.Instant instant);
@@ -85,8 +101,10 @@
   public static final class LongTextComplicationData.Builder {
     ctor public LongTextComplicationData.Builder(androidx.wear.watchface.complications.data.ComplicationText text, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData build();
-    method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? icon);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -118,7 +136,9 @@
   public static final class MonochromaticImageComplicationData.Builder {
     ctor public MonochromaticImageComplicationData.Builder(androidx.wear.watchface.complications.data.MonochromaticImage monochromaticImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData build();
-    method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
@@ -148,8 +168,10 @@
   public static final class NoPermissionComplicationData.Builder {
     ctor public NoPermissionComplicationData.Builder();
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData build();
-    method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -172,7 +194,9 @@
   public static final class PhotoImageComplicationData.Builder {
     ctor public PhotoImageComplicationData.Builder(android.graphics.drawable.Icon photoImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.PhotoImageComplicationData build();
-    method public androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
@@ -213,8 +237,10 @@
   public static final class RangedValueComplicationData.Builder {
     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 setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
@@ -240,8 +266,10 @@
   public static final class ShortTextComplicationData.Builder {
     ctor public ShortTextComplicationData.Builder(androidx.wear.watchface.complications.data.ComplicationText text, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData build();
-    method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -275,7 +303,9 @@
   public static final class SmallImageComplicationData.Builder {
     ctor public SmallImageComplicationData.Builder(androidx.wear.watchface.complications.data.SmallImage smallImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.SmallImageComplicationData build();
-    method public androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
diff --git a/wear/watchface/watchface-complications-data/api/public_plus_experimental_current.txt b/wear/watchface/watchface-complications-data/api/public_plus_experimental_current.txt
index fb8258ea..2b7bcf9 100644
--- a/wear/watchface/watchface-complications-data/api/public_plus_experimental_current.txt
+++ b/wear/watchface/watchface-complications-data/api/public_plus_experimental_current.txt
@@ -11,7 +11,9 @@
 
   public abstract sealed class ComplicationData {
     method public final android.content.ComponentName? getDataSource();
+    method public final int getDisplayPolicy();
     method public java.time.Instant getNextChangeInstant(java.time.Instant afterInstant);
+    method public final int getPersistencePolicy();
     method public final android.app.PendingIntent? getTapAction();
     method public final androidx.wear.watchface.complications.data.ComplicationType getType();
     method public final androidx.wear.watchface.complications.data.TimeRange getValidTimeRange();
@@ -19,15 +21,29 @@
     method public final boolean isTapActionLostDueToSerialization();
     method public final void setTapActionLostDueToSerialization(boolean);
     property public final android.content.ComponentName? dataSource;
+    property public final int displayPolicy;
+    property public final int persistencePolicy;
     property public final android.app.PendingIntent? tapAction;
     property public final boolean tapActionLostDueToSerialization;
     property public final androidx.wear.watchface.complications.data.ComplicationType type;
     property public final androidx.wear.watchface.complications.data.TimeRange validTimeRange;
   }
 
+  public final class ComplicationDisplayPolicies {
+    field public static final int ALWAYS_DISPLAY = 0; // 0x0
+    field public static final int DO_NOT_SHOW_WHEN_DEVICE_LOCKED = 1; // 0x1
+    field public static final androidx.wear.watchface.complications.data.ComplicationDisplayPolicies INSTANCE;
+  }
+
   @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 ComplicationExperimental {
   }
 
+  public final class ComplicationPersistencePolicies {
+    field public static final int CACHING_ALLOWED = 0; // 0x0
+    field public static final int DO_NOT_PERSIST = 1; // 0x1
+    field public static final androidx.wear.watchface.complications.data.ComplicationPersistencePolicies INSTANCE;
+  }
+
   public interface ComplicationText {
     method public java.time.Instant getNextChangeTime(java.time.Instant afterInstant);
     method public CharSequence getTextAt(android.content.res.Resources resources, java.time.Instant instant);
@@ -100,8 +116,10 @@
   public static final class DiscreteRangedValueComplicationData.Builder {
     ctor public DiscreteRangedValueComplicationData.Builder(int value, int min, int max, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.DiscreteRangedValueComplicationData build();
-    method public androidx.wear.watchface.complications.data.DiscreteRangedValueComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.DiscreteRangedValueComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.DiscreteRangedValueComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.DiscreteRangedValueComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.DiscreteRangedValueComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
@@ -139,8 +157,10 @@
     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 @androidx.wear.watchface.complications.data.ComplicationExperimental public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setColorRamp(androidx.wear.watchface.complications.data.ColorRamp? colorRamp);
-    method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.GoalProgressComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
@@ -165,7 +185,9 @@
   public static final class ListComplicationData.Builder {
     ctor public ListComplicationData.Builder(java.util.List<? extends androidx.wear.watchface.complications.data.ComplicationData> complicationList, androidx.wear.watchface.complications.data.ListComplicationData.StyleHint styleHint, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.ListComplicationData build();
-    method public androidx.wear.watchface.complications.data.ListComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.ListComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.ListComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
@@ -194,8 +216,10 @@
   public static final class LongTextComplicationData.Builder {
     ctor public LongTextComplicationData.Builder(androidx.wear.watchface.complications.data.ComplicationText text, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData build();
-    method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? icon);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -227,7 +251,9 @@
   public static final class MonochromaticImageComplicationData.Builder {
     ctor public MonochromaticImageComplicationData.Builder(androidx.wear.watchface.complications.data.MonochromaticImage monochromaticImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData build();
-    method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
@@ -257,8 +283,10 @@
   public static final class NoPermissionComplicationData.Builder {
     ctor public NoPermissionComplicationData.Builder();
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData build();
-    method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -281,7 +309,9 @@
   public static final class PhotoImageComplicationData.Builder {
     ctor public PhotoImageComplicationData.Builder(android.graphics.drawable.Icon photoImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.PhotoImageComplicationData build();
-    method public androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
@@ -314,7 +344,9 @@
     ctor public ProtoLayoutComplicationData.Builder(byte[] ambientLayout, byte[] interactiveLayout, byte[] layoutResources, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     ctor public ProtoLayoutComplicationData.Builder(androidx.wear.tiles.LayoutElementBuilders.Layout ambientLayout, androidx.wear.tiles.LayoutElementBuilders.Layout interactiveLayout, androidx.wear.tiles.ResourceBuilders.Resources resources, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.ProtoLayoutComplicationData build();
-    method public androidx.wear.watchface.complications.data.ProtoLayoutComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.ProtoLayoutComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.ProtoLayoutComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
@@ -346,8 +378,10 @@
     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 @androidx.wear.watchface.complications.data.ComplicationExperimental public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setColorRamp(androidx.wear.watchface.complications.data.ColorRamp? colorRamp);
-    method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
@@ -373,8 +407,10 @@
   public static final class ShortTextComplicationData.Builder {
     ctor public ShortTextComplicationData.Builder(androidx.wear.watchface.complications.data.ComplicationText text, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData build();
-    method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -408,7 +444,9 @@
   public static final class SmallImageComplicationData.Builder {
     ctor public SmallImageComplicationData.Builder(androidx.wear.watchface.complications.data.SmallImage smallImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.SmallImageComplicationData build();
-    method public androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
@@ -515,8 +553,10 @@
   public static final class WeightedElementsComplicationData.Builder {
     ctor public WeightedElementsComplicationData.Builder(java.util.List<androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Element> elements, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData build();
-    method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.WeightedElementsComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
diff --git a/wear/watchface/watchface-complications-data/api/restricted_current.txt b/wear/watchface/watchface-complications-data/api/restricted_current.txt
index 2cbf36c..efb6b9a 100644
--- a/wear/watchface/watchface-complications-data/api/restricted_current.txt
+++ b/wear/watchface/watchface-complications-data/api/restricted_current.txt
@@ -3,7 +3,9 @@
 
   public abstract sealed class ComplicationData {
     method public final android.content.ComponentName? getDataSource();
+    method public final int getDisplayPolicy();
     method public java.time.Instant getNextChangeInstant(java.time.Instant afterInstant);
+    method public final int getPersistencePolicy();
     method public final android.app.PendingIntent? getTapAction();
     method public final androidx.wear.watchface.complications.data.ComplicationType getType();
     method public final androidx.wear.watchface.complications.data.TimeRange getValidTimeRange();
@@ -11,12 +13,26 @@
     method public final boolean isTapActionLostDueToSerialization();
     method public final void setTapActionLostDueToSerialization(boolean);
     property public final android.content.ComponentName? dataSource;
+    property public final int displayPolicy;
+    property public final int persistencePolicy;
     property public final android.app.PendingIntent? tapAction;
     property public final boolean tapActionLostDueToSerialization;
     property public final androidx.wear.watchface.complications.data.ComplicationType type;
     property public final androidx.wear.watchface.complications.data.TimeRange validTimeRange;
   }
 
+  public final class ComplicationDisplayPolicies {
+    field public static final int ALWAYS_DISPLAY = 0; // 0x0
+    field public static final int DO_NOT_SHOW_WHEN_DEVICE_LOCKED = 1; // 0x1
+    field public static final androidx.wear.watchface.complications.data.ComplicationDisplayPolicies INSTANCE;
+  }
+
+  public final class ComplicationPersistencePolicies {
+    field public static final int CACHING_ALLOWED = 0; // 0x0
+    field public static final int DO_NOT_PERSIST = 1; // 0x1
+    field public static final androidx.wear.watchface.complications.data.ComplicationPersistencePolicies INSTANCE;
+  }
+
   public interface ComplicationText {
     method public java.time.Instant getNextChangeTime(java.time.Instant afterInstant);
     method public CharSequence getTextAt(android.content.res.Resources resources, java.time.Instant instant);
@@ -85,8 +101,10 @@
   public static final class LongTextComplicationData.Builder {
     ctor public LongTextComplicationData.Builder(androidx.wear.watchface.complications.data.ComplicationText text, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData build();
-    method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? icon);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.LongTextComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -118,7 +136,9 @@
   public static final class MonochromaticImageComplicationData.Builder {
     ctor public MonochromaticImageComplicationData.Builder(androidx.wear.watchface.complications.data.MonochromaticImage monochromaticImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData build();
-    method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.MonochromaticImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
@@ -148,8 +168,10 @@
   public static final class NoPermissionComplicationData.Builder {
     ctor public NoPermissionComplicationData.Builder();
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData build();
-    method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
     method public androidx.wear.watchface.complications.data.NoPermissionComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -172,7 +194,9 @@
   public static final class PhotoImageComplicationData.Builder {
     ctor public PhotoImageComplicationData.Builder(android.graphics.drawable.Icon photoImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.PhotoImageComplicationData build();
-    method public androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.PhotoImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
@@ -214,8 +238,10 @@
   public static final class RangedValueComplicationData.Builder {
     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 setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.RangedValueComplicationData.Builder setText(androidx.wear.watchface.complications.data.ComplicationText? text);
@@ -241,8 +267,10 @@
   public static final class ShortTextComplicationData.Builder {
     ctor public ShortTextComplicationData.Builder(androidx.wear.watchface.complications.data.ComplicationText text, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData build();
-    method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setMonochromaticImage(androidx.wear.watchface.complications.data.MonochromaticImage? monochromaticImage);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setSmallImage(androidx.wear.watchface.complications.data.SmallImage? smallImage);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.ShortTextComplicationData.Builder setTitle(androidx.wear.watchface.complications.data.ComplicationText? title);
@@ -276,7 +304,9 @@
   public static final class SmallImageComplicationData.Builder {
     ctor public SmallImageComplicationData.Builder(androidx.wear.watchface.complications.data.SmallImage smallImage, androidx.wear.watchface.complications.data.ComplicationText contentDescription);
     method public androidx.wear.watchface.complications.data.SmallImageComplicationData build();
-    method public androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDataSource(android.content.ComponentName? dataSource);
+    method public final T setDisplayPolicy(int displayPolicy);
+    method public final T setPersistencePolicy(int persistencePolicy);
     method public androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setTapAction(android.app.PendingIntent? tapAction);
     method public androidx.wear.watchface.complications.data.SmallImageComplicationData.Builder setValidTimeRange(androidx.wear.watchface.complications.data.TimeRange? validTimeRange);
   }
diff --git a/wear/watchface/watchface-complications-data/build.gradle b/wear/watchface/watchface-complications-data/build.gradle
index 52572f0..c574d08 100644
--- a/wear/watchface/watchface-complications-data/build.gradle
+++ b/wear/watchface/watchface-complications-data/build.gradle
@@ -38,7 +38,7 @@
     testImplementation(libs.testRunner)
     testImplementation(libs.testRules)
     testImplementation(libs.robolectric)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.truth)
     testImplementation(libs.junit)
 
diff --git a/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.java b/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.java
index e584635..92d08bde 100644
--- a/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.java
+++ b/wear/watchface/watchface-complications-data/src/main/java/android/support/wearable/complications/ComplicationData.java
@@ -34,6 +34,10 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
+import androidx.wear.watchface.complications.data.ComplicationDisplayPolicies;
+import androidx.wear.watchface.complications.data.ComplicationDisplayPolicy;
+import androidx.wear.watchface.complications.data.ComplicationPersistencePolicies;
+import androidx.wear.watchface.complications.data.ComplicationPersistencePolicy;
 
 import java.io.IOException;
 import java.io.InvalidObjectException;
@@ -295,6 +299,7 @@
     private static final String FIELD_COLOR_RAMP = "COLOR_RAMP";
     private static final String FIELD_COLOR_RAMP_INTERPOLATED = "COLOR_RAMP_INTERPOLATED";
     private static final String FIELD_DATA_SOURCE = "FIELD_DATA_SOURCE";
+    private static final String FIELD_DISPLAY_POLICY = "DISPLAY_POLICY";
     private static final String FIELD_ELEMENT_COLORS = "ELEMENT_COLORS";
     private static final String FIELD_ELEMENT_WEIGHTS = "ELEMENT_WEIGHTS";
     private static final String FIELD_END_TIME = "END_TIME";
@@ -312,6 +317,7 @@
     private static final String FIELD_LONG_TEXT = "LONG_TEXT";
     private static final String FIELD_MAX_VALUE = "MAX_VALUE";
     private static final String FIELD_MIN_VALUE = "MIN_VALUE";
+    private static final String FIELD_PERSISTENCE_POLICY = "PERSISTENCE_POLICY";
     private static final String FIELD_PLACEHOLDER_FIELDS = "PLACEHOLDER_FIELDS";
     private static final String FIELD_PLACEHOLDER_TYPE = "PLACEHOLDER_TYPE";
     private static final String FIELD_PROTO_LAYOUT_AMBIENT = "FIELD_PROTO_LAYOUT_AMBIENT";
@@ -375,7 +381,9 @@
                     FIELD_IMAGE_STYLE,
                     FIELD_TAP_ACTION,
                     FIELD_CONTENT_DESCRIPTION,
-                    FIELD_DATA_SOURCE
+                    FIELD_DATA_SOURCE,
+                    FIELD_PERSISTENCE_POLICY,
+                    FIELD_DISPLAY_POLICY
             }, // SHORT_TEXT
             {
                     FIELD_LONG_TITLE,
@@ -386,7 +394,9 @@
                     FIELD_IMAGE_STYLE,
                     FIELD_TAP_ACTION,
                     FIELD_CONTENT_DESCRIPTION,
-                    FIELD_DATA_SOURCE
+                    FIELD_DATA_SOURCE,
+                    FIELD_PERSISTENCE_POLICY,
+                    FIELD_DISPLAY_POLICY
             }, // LONG_TEXT
             {
                     FIELD_SHORT_TEXT,
@@ -400,22 +410,32 @@
                     FIELD_CONTENT_DESCRIPTION,
                     FIELD_DATA_SOURCE,
                     FIELD_COLOR_RAMP,
-                    FIELD_COLOR_RAMP_INTERPOLATED
+                    FIELD_COLOR_RAMP_INTERPOLATED,
+                    FIELD_PERSISTENCE_POLICY,
+                    FIELD_DISPLAY_POLICY
             }, // RANGED_VALUE
             {
                     FIELD_TAP_ACTION,
                     FIELD_ICON_BURN_IN_PROTECTION,
                     FIELD_CONTENT_DESCRIPTION,
-                    FIELD_DATA_SOURCE
+                    FIELD_DATA_SOURCE,
+                    FIELD_PERSISTENCE_POLICY,
+                    FIELD_DISPLAY_POLICY
             }, // ICON
             {
                     FIELD_TAP_ACTION,
                     FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
                     FIELD_CONTENT_DESCRIPTION,
-                    FIELD_DATA_SOURCE
+                    FIELD_DATA_SOURCE,
+                    FIELD_PERSISTENCE_POLICY,
+                    FIELD_DISPLAY_POLICY
             }, // SMALL_IMAGE
             {
-                    FIELD_TAP_ACTION, FIELD_CONTENT_DESCRIPTION, FIELD_DATA_SOURCE
+                    FIELD_TAP_ACTION,
+                    FIELD_CONTENT_DESCRIPTION,
+                    FIELD_DATA_SOURCE,
+                    FIELD_PERSISTENCE_POLICY,
+                    FIELD_DISPLAY_POLICY
             }, // LARGE_IMAGE
             {
                     FIELD_SHORT_TEXT,
@@ -426,7 +446,9 @@
                     FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
                     FIELD_IMAGE_STYLE,
                     FIELD_CONTENT_DESCRIPTION,
-                    FIELD_DATA_SOURCE
+                    FIELD_DATA_SOURCE,
+                    FIELD_PERSISTENCE_POLICY,
+                    FIELD_DISPLAY_POLICY
             }, // TYPE_NO_PERMISSION
             {  // TYPE_NO_DATA
                     FIELD_CONTENT_DESCRIPTION,
@@ -446,16 +468,24 @@
                     FIELD_SMALL_IMAGE_BURN_IN_PROTECTION,
                     FIELD_TAP_ACTION,
                     FIELD_VALUE,
-                    FIELD_DATA_SOURCE
+                    FIELD_DATA_SOURCE,
+                    FIELD_PERSISTENCE_POLICY,
+                    FIELD_DISPLAY_POLICY
             },
             {
-                    FIELD_TAP_ACTION, FIELD_CONTENT_DESCRIPTION, FIELD_DATA_SOURCE
+                    FIELD_TAP_ACTION,
+                    FIELD_CONTENT_DESCRIPTION,
+                    FIELD_DATA_SOURCE,
+                    FIELD_PERSISTENCE_POLICY,
+                    FIELD_DISPLAY_POLICY
             }, // TYPE_PROTO_LAYOUT
             {
                     FIELD_TAP_ACTION,
                     FIELD_LIST_STYLE_HINT,
                     FIELD_CONTENT_DESCRIPTION,
-                    FIELD_DATA_SOURCE
+                    FIELD_DATA_SOURCE,
+                    FIELD_PERSISTENCE_POLICY,
+                    FIELD_DISPLAY_POLICY
             }, // TYPE_LIST
             {
                     FIELD_SHORT_TEXT,
@@ -469,7 +499,9 @@
                     FIELD_CONTENT_DESCRIPTION,
                     FIELD_DATA_SOURCE,
                     FIELD_COLOR_RAMP,
-                    FIELD_COLOR_RAMP_INTERPOLATED
+                    FIELD_COLOR_RAMP_INTERPOLATED,
+                    FIELD_PERSISTENCE_POLICY,
+                    FIELD_DISPLAY_POLICY
             }, // TYPE_GOAL_PROGRESS
             {
                     FIELD_SHORT_TEXT,
@@ -482,6 +514,8 @@
                     FIELD_TAP_ACTION,
                     FIELD_CONTENT_DESCRIPTION,
                     FIELD_DATA_SOURCE,
+                    FIELD_PERSISTENCE_POLICY,
+                    FIELD_DISPLAY_POLICY
             }, // DISCRETE_RANGED_VALUE
             {
                     FIELD_SHORT_TEXT,
@@ -494,6 +528,8 @@
                     FIELD_TAP_ACTION,
                     FIELD_CONTENT_DESCRIPTION,
                     FIELD_DATA_SOURCE,
+                    FIELD_PERSISTENCE_POLICY,
+                    FIELD_DISPLAY_POLICY
             }  // TYPE_WEIGHTED_ELEMENTS
     };
 
@@ -536,7 +572,7 @@
 
     @RequiresApi(api = Build.VERSION_CODES.P)
     private static class SerializedForm implements Serializable {
-        private static final int VERSION_NUMBER = 16;
+        private static final int VERSION_NUMBER = 17;
 
         @NonNull
         ComplicationData mComplicationData;
@@ -553,6 +589,8 @@
             oos.writeInt(VERSION_NUMBER);
             int type = mComplicationData.getType();
             oos.writeInt(type);
+            oos.writeInt(mComplicationData.getPersistencePolicy());
+            oos.writeInt(mComplicationData.getDisplayPolicy());
 
             if (isFieldValidForType(FIELD_LONG_TEXT, type)) {
                 oos.writeObject(mComplicationData.getLongText());
@@ -755,6 +793,8 @@
             }
             int type = ois.readInt();
             Bundle fields = new Bundle();
+            fields.putInt(FIELD_PERSISTENCE_POLICY, ois.readInt());
+            fields.putInt(FIELD_DISPLAY_POLICY, ois.readInt());
 
             if (isFieldValidForType(FIELD_LONG_TEXT, type)) {
                 putIfNotNull(fields, FIELD_LONG_TEXT, (ComplicationText) ois.readObject());
@@ -1759,6 +1799,21 @@
         return mFields.getByteArray(FIELD_PROTO_LAYOUT_RESOURCES);
     }
 
+    /** Return's the complication's [ComplicationCachePolicy]. */
+    @ComplicationPersistencePolicy
+    public int getPersistencePolicy() {
+        checkFieldValidForTypeWithoutThrowingException(FIELD_PERSISTENCE_POLICY, mType);
+        return mFields.getInt(
+                FIELD_PERSISTENCE_POLICY, ComplicationPersistencePolicies.CACHING_ALLOWED);
+    }
+
+    /** Return's the complication's [ComplicationDisplayPolicy]. */
+    @ComplicationDisplayPolicy
+    public int getDisplayPolicy() {
+        checkFieldValidForTypeWithoutThrowingException(FIELD_DISPLAY_POLICY, mType);
+        return mFields.getInt(FIELD_DISPLAY_POLICY, ComplicationDisplayPolicies.ALWAYS_DISPLAY);
+    }
+
     /**
      * Returns the start time for this complication data (i.e. the first time at which it should
      * be considered active and displayed), this may be 0. See also {@link #isActiveAt(long)}.
@@ -1881,6 +1936,20 @@
             }
         }
 
+        /** Sets the complication's [ComplicationCachePolicy]. */
+        @NonNull
+        public Builder setPersistencePolicy(@ComplicationPersistencePolicy int cachePolicy) {
+            mFields.putInt(FIELD_PERSISTENCE_POLICY, cachePolicy);
+            return this;
+        }
+
+        /** Sets the complication's [ComplicationDisplayPolicy]. */
+        @NonNull
+        public Builder setDisplayPolicy(@ComplicationDisplayPolicy int displayPolicy) {
+            mFields.putInt(FIELD_DISPLAY_POLICY, displayPolicy);
+            return this;
+        }
+
         /**
          * Sets the start time for this complication data. This is optional for any type.
          *
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 b5559b0..d66bdf2 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
@@ -21,6 +21,7 @@
 import android.graphics.drawable.Icon
 import android.os.Build
 import androidx.annotation.ColorInt
+import androidx.annotation.IntDef
 import androidx.annotation.FloatRange
 import androidx.annotation.RestrictTo
 import androidx.wear.tiles.LayoutElementBuilders
@@ -35,6 +36,51 @@
     android.support.wearable.complications.ComplicationData.Builder
 
 /**
+ * The policies that control complication persistence.
+ */
+public object ComplicationPersistencePolicies {
+    /** The default policy is that persistence/caching is allowed. */
+    public const val CACHING_ALLOWED: Int = 0
+
+    /**
+     * Instructs the system to not persist the complication past a reboot. This is useful when
+     * freshness is important.
+     */
+    public const val DO_NOT_PERSIST: Int = 1
+}
+
+/** @hide */
+@IntDef(
+    flag = true, // This is a flag to allow for future expansion.
+    value = [
+        ComplicationPersistencePolicies.CACHING_ALLOWED,
+        ComplicationPersistencePolicies.DO_NOT_PERSIST
+    ]
+)
+public annotation class ComplicationPersistencePolicy
+
+/**
+ * The policies that control when complications should be displayed.
+ */
+public object ComplicationDisplayPolicies {
+    /** The default policy is that the complication should always be shown. */
+    public const val ALWAYS_DISPLAY: Int = 0
+
+    /** Instructs the system not to display the complication while the device is locked. */
+    public const val DO_NOT_SHOW_WHEN_DEVICE_LOCKED: Int = 1
+}
+
+/** @hide */
+@IntDef(
+    flag = true, // This is a flag to allow for future expansion.
+    value = [
+        ComplicationDisplayPolicies.ALWAYS_DISPLAY,
+        ComplicationDisplayPolicies.DO_NOT_SHOW_WHEN_DEVICE_LOCKED
+    ]
+)
+public annotation class ComplicationDisplayPolicy
+
+/**
  * Base type for all different types of [ComplicationData] types.
  *
  * Please note to aid unit testing of ComplicationDataSourceServices, [equals], [hashCode] and
@@ -51,13 +97,19 @@
  * @property dataSource The [ComponentName] of the
  * [androidx.wear.watchface.complications.datasource.ComplicationDataSourceService] that provided
  * the ComplicationData. This may be `null` when run on old systems.
+ * @property persistencePolicy The [ComplicationPersistencePolicy] for this complication. This
+ * requires the watchface to be built with a compatible library to work.
+ * @property displayPolicy The [ComplicationDisplayPolicy] for this complication. This requires the
+ * watchface to be built with a compatible library to work.
  */
 public sealed class ComplicationData constructor(
     public val type: ComplicationType,
     public val tapAction: PendingIntent?,
     internal var cachedWireComplicationData: WireComplicationData?,
     public val validTimeRange: TimeRange = TimeRange.ALWAYS,
-    public val dataSource: ComponentName?
+    public val dataSource: ComponentName?,
+    @ComplicationPersistencePolicy public val persistencePolicy: Int,
+    @ComplicationDisplayPolicy public val displayPolicy: Int
 ) {
     /**
      * [tapAction] which is a [PendingIntent] unfortunately can't be serialized. This property is
@@ -85,6 +137,8 @@
             WireComplicationDataBuilder(it)
         } ?: WireComplicationDataBuilder(type.toWireComplicationType()).apply {
             setDataSource(dataSource)
+            setPersistencePolicy(persistencePolicy)
+            setDisplayPolicy(displayPolicy)
         }
 
     internal open fun fillWireComplicationDataBuilder(builder: WireComplicationDataBuilder) {
@@ -107,6 +161,59 @@
      * @param afterInstant The reference [Instant], after which changes will be reported.
      */
     public open fun getNextChangeInstant(afterInstant: Instant): Instant = Instant.MAX
+
+    /**
+     * Builder for properties in common for most Complication Types.
+     * @hide
+     */
+    public abstract class BaseBuilder<T : BaseBuilder<T, ReturnT>, ReturnT> {
+        internal var cachedWireComplicationData: WireComplicationData? = null
+        internal var dataSource: ComponentName? = null
+        internal var persistencePolicy = ComplicationPersistencePolicies.CACHING_ALLOWED
+        internal var displayPolicy = ComplicationDisplayPolicies.ALWAYS_DISPLAY
+
+        /**
+         * Sets the [ComponentName] of the ComplicationDataSourceService that provided this
+         * ComplicationData, if any.
+         *
+         * Note a ComplicationDataSourceService does not need to call this because the system will
+         * set this value on its behalf.
+         */
+        @Suppress("UNCHECKED_CAST", "SetterReturnsThis")
+        public fun setDataSource(dataSource: ComponentName?): T {
+            this.dataSource = dataSource
+            return this as T
+        }
+
+        @Suppress("UNCHECKED_CAST", "SetterReturnsThis")
+        internal fun setCachedWireComplicationData(
+            cachedWireComplicationData: WireComplicationData?
+        ): T {
+            this.cachedWireComplicationData = cachedWireComplicationData
+            return this as T
+        }
+
+        /**
+         * Sets the complication's [ComplicationPersistencePolicy].
+         */
+        @Suppress("UNCHECKED_CAST", "SetterReturnsThis")
+        public fun setPersistencePolicy(@ComplicationPersistencePolicy persistencePolicy: Int): T {
+            this.persistencePolicy = persistencePolicy
+            return this as T
+        }
+
+        /**
+         * Sets the complication's [ComplicationDisplayPolicy].
+         */
+        @Suppress("UNCHECKED_CAST", "SetterReturnsThis")
+        public fun setDisplayPolicy(@ComplicationDisplayPolicy displayPolicy: Int): T {
+            this.displayPolicy = displayPolicy
+            return this as T
+        }
+
+        /** Builds the ComplicationData */
+        abstract fun build(): ReturnT
+    }
 }
 
 /**
@@ -134,7 +241,10 @@
     TYPE,
     placeholder?.tapAction,
     cachedWireComplicationData,
-    dataSource = null
+    dataSource = null,
+    persistencePolicy =
+        placeholder?.persistencePolicy ?: ComplicationPersistencePolicies.CACHING_ALLOWED,
+    displayPolicy = placeholder?.displayPolicy ?: ComplicationDisplayPolicies.ALWAYS_DISPLAY
 ) {
 
     /** Constructs a NoDataComplicationData without a [placeholder]. */
@@ -192,6 +302,8 @@
         if (tapActionLostDueToSerialization != other.tapActionLostDueToSerialization) return false
         if (tapAction != other.tapAction) return false
         if (validTimeRange != other.validTimeRange) return false
+        if (persistencePolicy != other.persistencePolicy) return false
+        if (displayPolicy != other.displayPolicy) return false
 
         return true
     }
@@ -201,6 +313,8 @@
         result = 31 * result + tapActionLostDueToSerialization.hashCode()
         result = 31 * result + (tapAction?.hashCode() ?: 0)
         result = 31 * result + validTimeRange.hashCode()
+        result = 31 * result + persistencePolicy.hashCode()
+        result = 31 * result + displayPolicy.hashCode()
         return result
     }
 
@@ -208,7 +322,8 @@
         return "NoDataComplicationData(" +
             "placeholder=$placeholder, " +
             "tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
-            "tapAction=$tapAction, validTimeRange=$validTimeRange)"
+            "tapAction=$tapAction, validTimeRange=$validTimeRange, " +
+            "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy)"
     }
 
     /** @hide */
@@ -228,7 +343,9 @@
     TYPE,
     tapAction = null,
     cachedWireComplicationData = null,
-    dataSource = null
+    dataSource = null,
+    persistencePolicy = ComplicationPersistencePolicies.CACHING_ALLOWED,
+    displayPolicy = ComplicationDisplayPolicies.ALWAYS_DISPLAY
 ) {
     /** @hide */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@@ -266,7 +383,9 @@
     TYPE,
     tapAction = null,
     cachedWireComplicationData = null,
-    dataSource = null
+    dataSource = null,
+    persistencePolicy = ComplicationPersistencePolicies.CACHING_ALLOWED,
+    displayPolicy = ComplicationDisplayPolicies.ALWAYS_DISPLAY
 ) {
     /** @hide */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@@ -343,13 +462,17 @@
     tapAction: PendingIntent?,
     validTimeRange: TimeRange?,
     cachedWireComplicationData: WireComplicationData?,
-    dataSource: ComponentName?
+    dataSource: ComponentName?,
+    @ComplicationPersistencePolicy persistencePolicy: Int,
+    @ComplicationDisplayPolicy displayPolicy: Int
 ) : ComplicationData(
     TYPE,
     tapAction = tapAction,
     cachedWireComplicationData = cachedWireComplicationData,
     validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
-    dataSource = dataSource
+    dataSource = dataSource,
+    persistencePolicy = persistencePolicy,
+    displayPolicy = displayPolicy
 ) {
     /**
      * Builder for [ShortTextComplicationData].
@@ -362,14 +485,12 @@
     public class Builder(
         private val text: ComplicationText,
         private var contentDescription: ComplicationText
-    ) {
+    ) : BaseBuilder<Builder, ShortTextComplicationData>() {
         private var tapAction: PendingIntent? = null
         private var validTimeRange: TimeRange? = null
         private var title: ComplicationText? = null
         private var monochromaticImage: MonochromaticImage? = null
         private var smallImage: SmallImage? = null
-        private var cachedWireComplicationData: WireComplicationData? = null
-        private var dataSource: ComponentName? = null
 
         /** Sets optional pending intent to be invoked when the complication is tapped. */
         public fun setTapAction(tapAction: PendingIntent?): Builder = apply {
@@ -397,25 +518,8 @@
             this.smallImage = smallImage
         }
 
-        /**
-         * Sets the [ComponentName] of the ComplicationDataSourceService that provided this
-         * ComplicationData, if any.
-         *
-         * Note a ComplicationDataSourceService does not need to call this because the system will
-         * set this value on its behalf.
-         */
-        public fun setDataSource(dataSource: ComponentName?): Builder = apply {
-            this.dataSource = dataSource
-        }
-
-        internal fun setCachedWireComplicationData(
-            cachedWireComplicationData: WireComplicationData?
-        ): Builder = apply {
-            this.cachedWireComplicationData = cachedWireComplicationData
-        }
-
         /** Builds the [ShortTextComplicationData]. */
-        public fun build(): ShortTextComplicationData =
+        public override fun build(): ShortTextComplicationData =
             ShortTextComplicationData(
                 text,
                 title,
@@ -425,7 +529,9 @@
                 tapAction,
                 validTimeRange,
                 cachedWireComplicationData,
-                dataSource
+                dataSource,
+                persistencePolicy,
+                displayPolicy
             )
     }
 
@@ -471,6 +577,8 @@
         if (tapAction != other.tapAction) return false
         if (validTimeRange != other.validTimeRange) return false
         if (dataSource != other.dataSource) return false
+        if (persistencePolicy != other.persistencePolicy) return false
+        if (displayPolicy != other.displayPolicy) return false
 
         return true
     }
@@ -485,6 +593,8 @@
         result = 31 * result + (tapAction?.hashCode() ?: 0)
         result = 31 * result + validTimeRange.hashCode()
         result = 31 * result + dataSource.hashCode()
+        result = 31 * result + persistencePolicy.hashCode()
+        result = 31 * result + displayPolicy.hashCode()
         return result
     }
 
@@ -493,7 +603,8 @@
             "monochromaticImage=$monochromaticImage, smallImage=$smallImage, " +
             "contentDescription=$contentDescription, " +
             "tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
-            "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource)"
+            "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
+            "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy)"
     }
 
     override fun hasPlaceholderFields() = text.isPlaceholder() || title?.isPlaceholder() == true ||
@@ -564,13 +675,18 @@
     tapAction: PendingIntent?,
     validTimeRange: TimeRange?,
     cachedWireComplicationData: WireComplicationData?,
-    dataSource: ComponentName?
+    dataSource: ComponentName?,
+    @ComplicationPersistencePolicy persistencePolicy: Int,
+    @ComplicationDisplayPolicy displayPolicy: Int
 ) : ComplicationData(
     TYPE,
     tapAction = tapAction,
     cachedWireComplicationData = cachedWireComplicationData,
     validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
-    dataSource = dataSource
+    dataSource = dataSource,
+    persistencePolicy = persistencePolicy,
+    displayPolicy = displayPolicy
+
 ) {
     /**
      * Builder for [LongTextComplicationData].
@@ -584,14 +700,12 @@
     public class Builder(
         private val text: ComplicationText,
         private var contentDescription: ComplicationText
-    ) {
+    ) : BaseBuilder<Builder, LongTextComplicationData>() {
         private var tapAction: PendingIntent? = null
         private var validTimeRange: TimeRange? = null
         private var title: ComplicationText? = null
         private var monochromaticImage: MonochromaticImage? = null
         private var smallImage: SmallImage? = null
-        private var cachedWireComplicationData: WireComplicationData? = null
-        private var dataSource: ComponentName? = null
 
         /** Sets optional pending intent to be invoked when the complication is tapped. */
         public fun setTapAction(tapAction: PendingIntent?): Builder = apply {
@@ -619,25 +733,8 @@
             this.smallImage = smallImage
         }
 
-        /**
-         * Sets the [ComponentName] of the ComplicationDataSourceService that provided this
-         * ComplicationData, if any.
-         *
-         * Note a ComplicationDataSourceService does not need to call this because the system will
-         * set this value on its behalf.
-         */
-        public fun setDataSource(dataSource: ComponentName?): Builder = apply {
-            this.dataSource = dataSource
-        }
-
-        internal fun setCachedWireComplicationData(
-            cachedWireComplicationData: WireComplicationData?
-        ): Builder = apply {
-            this.cachedWireComplicationData = cachedWireComplicationData
-        }
-
         /** Builds the [LongTextComplicationData]. */
-        public fun build(): LongTextComplicationData =
+        public override fun build(): LongTextComplicationData =
             LongTextComplicationData(
                 text,
                 title,
@@ -647,7 +744,9 @@
                 tapAction,
                 validTimeRange,
                 cachedWireComplicationData,
-                dataSource
+                dataSource,
+                persistencePolicy,
+                displayPolicy
             )
     }
 
@@ -693,6 +792,8 @@
         if (tapAction != other.tapAction) return false
         if (validTimeRange != other.validTimeRange) return false
         if (dataSource != other.dataSource) return false
+        if (persistencePolicy != other.persistencePolicy) return false
+        if (displayPolicy != other.displayPolicy) return false
 
         return true
     }
@@ -707,6 +808,8 @@
         result = 31 * result + (tapAction?.hashCode() ?: 0)
         result = 31 * result + validTimeRange.hashCode()
         result = 31 * result + dataSource.hashCode()
+        result = 31 * result + persistencePolicy.hashCode()
+        result = 31 * result + displayPolicy.hashCode()
         return result
     }
 
@@ -715,7 +818,8 @@
             "monochromaticImage=$monochromaticImage, smallImage=$smallImage, " +
             "contentDescription=$contentDescription), " +
             "tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
-            "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource)"
+            "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
+            "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy)"
     }
 
     override fun hasPlaceholderFields() = text.isPlaceholder() || title?.isPlaceholder() == true ||
@@ -855,13 +959,17 @@
     validTimeRange: TimeRange?,
     cachedWireComplicationData: WireComplicationData?,
     dataSource: ComponentName?,
-    colorRamp: ColorRamp?
+    colorRamp: ColorRamp?,
+    @ComplicationPersistencePolicy persistencePolicy: Int,
+    @ComplicationDisplayPolicy displayPolicy: Int
 ) : ComplicationData(
     TYPE,
     tapAction = tapAction,
     cachedWireComplicationData = cachedWireComplicationData,
     validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
-    dataSource = dataSource
+    dataSource = dataSource,
+    persistencePolicy = persistencePolicy,
+    displayPolicy = displayPolicy
 ) {
     /** Optional hint to render the value with the specified [ColorRamp]. */
     @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
@@ -887,15 +995,13 @@
         private val min: Float,
         private val max: Float,
         private var contentDescription: ComplicationText
-    ) {
+    ) : BaseBuilder<Builder, RangedValueComplicationData>() {
         private var tapAction: PendingIntent? = null
         private var validTimeRange: TimeRange? = null
         private var monochromaticImage: MonochromaticImage? = null
         private var smallImage: SmallImage? = null
         private var title: ComplicationText? = null
         private var text: ComplicationText? = null
-        private var cachedWireComplicationData: WireComplicationData? = null
-        private var dataSource: ComponentName? = null
         @OptIn(ComplicationExperimental::class)
         private var colorRamp: ColorRamp? = null
 
@@ -937,17 +1043,6 @@
         }
 
         /**
-         * Sets the [ComponentName] of the ComplicationDataSourceService that provided this
-         * ComplicationData, if any.
-         *
-         * Note a ComplicationDataSourceService does not need to call this because the system will
-         * set this value on its behalf.
-         */
-        public fun setDataSource(dataSource: ComponentName?): Builder = apply {
-            this.dataSource = dataSource
-        }
-
-        /**
          * Sets an optional hint which suggests the renderer draws the complication using a
          * [ColorRamp].
          */
@@ -956,15 +1051,9 @@
             this.colorRamp = colorRamp
         }
 
-        internal fun setCachedWireComplicationData(
-            cachedWireComplicationData: WireComplicationData?
-        ): Builder = apply {
-            this.cachedWireComplicationData = cachedWireComplicationData
-        }
-
         /** Builds the [RangedValueComplicationData]. */
         @OptIn(ComplicationExperimental::class)
-        public fun build(): RangedValueComplicationData {
+        public override fun build(): RangedValueComplicationData {
             require(
                 monochromaticImage != null || smallImage != null || text != null || title != null
             ) {
@@ -983,7 +1072,9 @@
                 validTimeRange,
                 cachedWireComplicationData,
                 dataSource,
-                colorRamp
+                colorRamp,
+                persistencePolicy,
+                displayPolicy
             )
         }
     }
@@ -1043,6 +1134,8 @@
         if (validTimeRange != other.validTimeRange) return false
         if (dataSource != other.dataSource) return false
         if (colorRamp != other.colorRamp) return false
+        if (persistencePolicy != other.persistencePolicy) return false
+        if (displayPolicy != other.displayPolicy) return false
 
         return true
     }
@@ -1062,6 +1155,8 @@
         result = 31 * result + validTimeRange.hashCode()
         result = 31 * result + dataSource.hashCode()
         result = 31 * result + colorRamp.hashCode()
+        result = 31 * result + persistencePolicy.hashCode()
+        result = 31 * result + displayPolicy.hashCode()
         return result
     }
 
@@ -1077,7 +1172,8 @@
             "text=$text, contentDescription=$contentDescription), " +
             "tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
             "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
-            "colorRamp=$colorRamp)"
+            "colorRamp=$colorRamp, persistencePolicy=$persistencePolicy, " +
+            "displayPolicy=$displayPolicy)"
     }
 
     override fun hasPlaceholderFields() = value == PLACEHOLDER || text?.isPlaceholder() == true ||
@@ -1176,13 +1272,17 @@
     validTimeRange: TimeRange?,
     cachedWireComplicationData: WireComplicationData?,
     dataSource: ComponentName?,
-    colorRamp: ColorRamp?
+    colorRamp: ColorRamp?,
+    @ComplicationPersistencePolicy persistencePolicy: Int,
+    @ComplicationDisplayPolicy displayPolicy: Int
 ) : ComplicationData(
     TYPE,
     tapAction = tapAction,
     cachedWireComplicationData = cachedWireComplicationData,
     validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
-    dataSource = dataSource
+    dataSource = dataSource,
+    persistencePolicy = persistencePolicy,
+    displayPolicy = displayPolicy
 ) {
     /** Optional hint to render the value with the specified [ColorRamp]. */
     @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
@@ -1205,15 +1305,13 @@
         private val value: Float,
         private val targetValue: Float,
         private var contentDescription: ComplicationText
-    ) {
+    ) : BaseBuilder<Builder, GoalProgressComplicationData>() {
         private var tapAction: PendingIntent? = null
         private var validTimeRange: TimeRange? = null
         private var monochromaticImage: MonochromaticImage? = null
         private var smallImage: SmallImage? = null
         private var title: ComplicationText? = null
         private var text: ComplicationText? = null
-        private var cachedWireComplicationData: WireComplicationData? = null
-        private var dataSource: ComponentName? = null
         @OptIn(ComplicationExperimental::class)
         private var colorRamp: ColorRamp? = null
 
@@ -1255,17 +1353,6 @@
         }
 
         /**
-         * Sets the [ComponentName] of the ComplicationDataSourceService that provided this
-         * ComplicationData, if any.
-         *
-         * Note a ComplicationDataSourceService does not need to call this because the system will
-         * set this value on its behalf.
-         */
-        public fun setDataSource(dataSource: ComponentName?): Builder = apply {
-            this.dataSource = dataSource
-        }
-
-        /**
          * Sets an optional hint which suggests the renderer draws the complication using a
          * [ColorRamp].
          */
@@ -1274,15 +1361,9 @@
             this.colorRamp = colorRamp
         }
 
-        internal fun setCachedWireComplicationData(
-            cachedWireComplicationData: WireComplicationData?
-        ): Builder = apply {
-            this.cachedWireComplicationData = cachedWireComplicationData
-        }
-
         /** Builds the [GoalProgressComplicationData]. */
         @OptIn(ComplicationExperimental::class)
-        public fun build(): GoalProgressComplicationData {
+        public override fun build(): GoalProgressComplicationData {
             require(
                 monochromaticImage != null || smallImage != null || text != null || title != null
             ) {
@@ -1300,7 +1381,9 @@
                 validTimeRange,
                 cachedWireComplicationData,
                 dataSource,
-                colorRamp
+                colorRamp,
+                persistencePolicy,
+                displayPolicy
             )
         }
     }
@@ -1358,6 +1441,8 @@
         if (validTimeRange != other.validTimeRange) return false
         if (dataSource != other.dataSource) return false
         if (colorRamp != other.colorRamp) return false
+        if (persistencePolicy != other.persistencePolicy) return false
+        if (displayPolicy != other.displayPolicy) return false
 
         return true
     }
@@ -1376,6 +1461,8 @@
         result = 31 * result + validTimeRange.hashCode()
         result = 31 * result + dataSource.hashCode()
         result = 31 * result + colorRamp.hashCode()
+        result = 31 * result + persistencePolicy.hashCode()
+        result = 31 * result + displayPolicy.hashCode()
         return result
     }
 
@@ -1391,7 +1478,8 @@
             "text=$text, contentDescription=$contentDescription), " +
             "tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
             "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
-            "colorRamp=$colorRamp)"
+            "colorRamp=$colorRamp, persistencePolicy=$persistencePolicy, " +
+            "displayPolicy=$displayPolicy)"
     }
 
     override fun hasPlaceholderFields() = value == PLACEHOLDER || text?.isPlaceholder() == true ||
@@ -1486,13 +1574,17 @@
     tapAction: PendingIntent?,
     validTimeRange: TimeRange?,
     cachedWireComplicationData: WireComplicationData?,
-    dataSource: ComponentName?
+    dataSource: ComponentName?,
+    @ComplicationPersistencePolicy persistencePolicy: Int,
+    @ComplicationDisplayPolicy displayPolicy: Int
 ) : ComplicationData(
     TYPE,
     tapAction = tapAction,
     cachedWireComplicationData = cachedWireComplicationData,
     validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
-    dataSource = dataSource
+    dataSource = dataSource,
+    persistencePolicy = persistencePolicy,
+    displayPolicy = displayPolicy
 ) {
     /**
      * Describes a single value within a [WeightedElementsComplicationData].
@@ -1551,15 +1643,13 @@
     public class Builder(
         private val elements: List<Element>,
         private var contentDescription: ComplicationText
-    ) {
+    ) : BaseBuilder<Builder, WeightedElementsComplicationData>() {
         private var tapAction: PendingIntent? = null
         private var validTimeRange: TimeRange? = null
         private var monochromaticImage: MonochromaticImage? = null
         private var smallImage: SmallImage? = null
         private var title: ComplicationText? = null
         private var text: ComplicationText? = null
-        private var cachedWireComplicationData: WireComplicationData? = null
-        private var dataSource: ComponentName? = null
 
         /** Sets optional pending intent to be invoked when the complication is tapped. */
         public fun setTapAction(tapAction: PendingIntent?): Builder = apply {
@@ -1592,26 +1682,9 @@
             this.text = text
         }
 
-        /**
-         * Sets the [ComponentName] of the ComplicationDataSourceService that provided this
-         * ComplicationData, if any.
-         *
-         * Note a ComplicationDataSourceService does not need to call this because the system will
-         * set this value on its behalf.
-         */
-        public fun setDataSource(dataSource: ComponentName?): Builder = apply {
-            this.dataSource = dataSource
-        }
-
-        internal fun setCachedWireComplicationData(
-            cachedWireComplicationData: WireComplicationData?
-        ): Builder = apply {
-            this.cachedWireComplicationData = cachedWireComplicationData
-        }
-
         /** Builds the [GoalProgressComplicationData]. */
         @OptIn(ComplicationExperimental::class)
-        public fun build(): WeightedElementsComplicationData {
+        public override fun build(): WeightedElementsComplicationData {
             require(
                 monochromaticImage != null || smallImage != null || text != null || title != null
             ) {
@@ -1627,7 +1700,9 @@
                 tapAction,
                 validTimeRange,
                 cachedWireComplicationData,
-                dataSource
+                dataSource,
+                persistencePolicy,
+                displayPolicy
             )
         }
     }
@@ -1683,7 +1758,8 @@
             "monochromaticImage=$monochromaticImage, smallImage=$smallImage, title=$title, " +
             "text=$text, contentDescription=$contentDescription), " +
             "tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
-            "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource)"
+            "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
+            "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy)"
     }
 
     override fun hasPlaceholderFields() = elements == PLACEHOLDER ||
@@ -1702,6 +1778,8 @@
         if (title != other.title) return false
         if (text != other.text) return false
         if (contentDescription != other.contentDescription) return false
+        if (persistencePolicy != other.persistencePolicy) return false
+        if (displayPolicy != other.displayPolicy) return false
 
         return true
     }
@@ -1713,6 +1791,10 @@
         result = 31 * result + (title?.hashCode() ?: 0)
         result = 31 * result + (text?.hashCode() ?: 0)
         result = 31 * result + (contentDescription?.hashCode() ?: 0)
+        result = 31 * result + persistencePolicy.hashCode()
+        result = 31 * result + persistencePolicy.hashCode()
+        result = 31 * result + displayPolicy.hashCode()
+
         return result
     }
 
@@ -1799,12 +1881,16 @@
     validTimeRange: TimeRange?,
     cachedWireComplicationData: WireComplicationData?,
     dataSource: ComponentName?,
+    @ComplicationPersistencePolicy persistencePolicy: Int,
+    @ComplicationDisplayPolicy displayPolicy: Int
 ) : ComplicationData(
     TYPE,
     tapAction = tapAction,
     cachedWireComplicationData = cachedWireComplicationData,
     validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
-    dataSource = dataSource
+    dataSource = dataSource,
+    persistencePolicy = persistencePolicy,
+    displayPolicy = displayPolicy
 ) {
     /**
      * Builder for [DiscreteRangedValueComplicationData].
@@ -1824,15 +1910,13 @@
         private val min: Int,
         private val max: Int,
         private var contentDescription: ComplicationText
-    ) {
+    ) : BaseBuilder<Builder, DiscreteRangedValueComplicationData>() {
         private var tapAction: PendingIntent? = null
         private var validTimeRange: TimeRange? = null
         private var monochromaticImage: MonochromaticImage? = null
         private var smallImage: SmallImage? = null
         private var title: ComplicationText? = null
         private var text: ComplicationText? = null
-        private var cachedWireComplicationData: WireComplicationData? = null
-        private var dataSource: ComponentName? = null
 
         init {
             require(max != Int.MAX_VALUE) {
@@ -1871,26 +1955,9 @@
             this.text = text
         }
 
-        /**
-         * Sets the [ComponentName] of the ComplicationDataSourceService that provided this
-         * ComplicationData, if any.
-         *
-         * Note a ComplicationDataSourceService does not need to call this because the system will
-         * set this value on its behalf.
-         */
-        public fun setDataSource(dataSource: ComponentName?): Builder = apply {
-            this.dataSource = dataSource
-        }
-
-        internal fun setCachedWireComplicationData(
-            cachedWireComplicationData: WireComplicationData?
-        ): Builder = apply {
-            this.cachedWireComplicationData = cachedWireComplicationData
-        }
-
         /** Builds the [DiscreteRangedValueComplicationData]. */
         @OptIn(ComplicationExperimental::class)
-        public fun build(): DiscreteRangedValueComplicationData {
+        public override fun build(): DiscreteRangedValueComplicationData {
             require(
                 monochromaticImage != null || smallImage != null || text != null || title != null
             ) {
@@ -1908,7 +1975,9 @@
                 tapAction,
                 validTimeRange,
                 cachedWireComplicationData,
-                dataSource
+                dataSource,
+                persistencePolicy,
+                displayPolicy
             )
         }
     }
@@ -1962,6 +2031,8 @@
         if (tapAction != other.tapAction) return false
         if (validTimeRange != other.validTimeRange) return false
         if (dataSource != other.dataSource) return false
+        if (persistencePolicy != other.persistencePolicy) return false
+        if (displayPolicy != other.displayPolicy) return false
 
         return true
     }
@@ -1980,6 +2051,10 @@
         result = 31 * result + (tapAction?.hashCode() ?: 0)
         result = 31 * result + validTimeRange.hashCode()
         result = 31 * result + dataSource.hashCode()
+        result = 31 * result + persistencePolicy.hashCode()
+        result = 31 * result + persistencePolicy.hashCode()
+        result = 31 * result + displayPolicy.hashCode()
+
         return result
     }
 
@@ -1993,7 +2068,8 @@
             "monochromaticImage=$monochromaticImage, smallImage=$smallImage, title=$title, " +
             "text=$text, contentDescription=$contentDescription), " +
             "tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
-            "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource)"
+            "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
+            "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy)"
     }
 
     override fun hasPlaceholderFields() = value == PLACEHOLDER || text?.isPlaceholder() == true ||
@@ -2050,13 +2126,17 @@
     tapAction: PendingIntent?,
     validTimeRange: TimeRange?,
     cachedWireComplicationData: WireComplicationData?,
-    dataSource: ComponentName?
+    dataSource: ComponentName?,
+    @ComplicationPersistencePolicy persistencePolicy: Int,
+    @ComplicationDisplayPolicy displayPolicy: Int
 ) : ComplicationData(
     TYPE,
     tapAction = tapAction,
     cachedWireComplicationData = cachedWireComplicationData,
     validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
-    dataSource = dataSource
+    dataSource = dataSource,
+    persistencePolicy = persistencePolicy,
+    displayPolicy = displayPolicy
 ) {
     /**
      * Builder for [MonochromaticImageComplicationData].
@@ -2069,11 +2149,9 @@
     public class Builder(
         private val monochromaticImage: MonochromaticImage,
         private val contentDescription: ComplicationText
-    ) {
+    ) : BaseBuilder<Builder, MonochromaticImageComplicationData>() {
         private var tapAction: PendingIntent? = null
         private var validTimeRange: TimeRange? = null
-        private var cachedWireComplicationData: WireComplicationData? = null
-        private var dataSource: ComponentName? = null
 
         /** Sets optional pending intent to be invoked when the complication is tapped. */
         public fun setTapAction(tapAction: PendingIntent?): Builder = apply {
@@ -2086,32 +2164,17 @@
             this.validTimeRange = validTimeRange
         }
 
-        internal fun setCachedWireComplicationData(
-            cachedWireComplicationData: WireComplicationData?
-        ): Builder = apply {
-            this.cachedWireComplicationData = cachedWireComplicationData
-        }
-
-        /**
-         * Sets the [ComponentName] of the ComplicationDataSourceService that provided this
-         * ComplicationData, if any.
-         *
-         * Note a ComplicationDataSourceService does not need to call this because the system will
-         * set this value on its behalf.
-         */
-        public fun setDataSource(dataSource: ComponentName?): Builder = apply {
-            this.dataSource = dataSource
-        }
-
         /** Builds the [MonochromaticImageComplicationData]. */
-        public fun build(): MonochromaticImageComplicationData =
+        public override fun build(): MonochromaticImageComplicationData =
             MonochromaticImageComplicationData(
                 monochromaticImage,
                 contentDescription,
                 tapAction,
                 validTimeRange,
                 cachedWireComplicationData,
-                dataSource
+                dataSource,
+                persistencePolicy,
+                displayPolicy
             )
     }
 
@@ -2151,6 +2214,8 @@
         if (tapAction != other.tapAction) return false
         if (validTimeRange != other.validTimeRange) return false
         if (dataSource != other.dataSource) return false
+        if (persistencePolicy != other.persistencePolicy) return false
+        if (displayPolicy != other.displayPolicy) return false
 
         return true
     }
@@ -2162,6 +2227,8 @@
         result = 31 * result + (tapAction?.hashCode() ?: 0)
         result = 31 * result + validTimeRange.hashCode()
         result = 31 * result + dataSource.hashCode()
+        result = 31 * result + persistencePolicy.hashCode()
+        result = 31 * result + displayPolicy.hashCode()
         return result
     }
 
@@ -2171,7 +2238,8 @@
         return "MonochromaticImageComplicationData(monochromaticImage=$monochromaticImage, " +
             "contentDescription=$contentDescription), " +
             "tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
-            "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource)"
+            "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
+            "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy)"
     }
 
     /** @hide */
@@ -2202,13 +2270,17 @@
     tapAction: PendingIntent?,
     validTimeRange: TimeRange?,
     cachedWireComplicationData: WireComplicationData?,
-    dataSource: ComponentName?
+    dataSource: ComponentName?,
+    @ComplicationPersistencePolicy persistencePolicy: Int,
+    @ComplicationDisplayPolicy displayPolicy: Int
 ) : ComplicationData(
     TYPE,
     tapAction = tapAction,
     cachedWireComplicationData = cachedWireComplicationData,
     validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
-    dataSource = dataSource
+    dataSource = dataSource,
+    persistencePolicy = persistencePolicy,
+    displayPolicy = displayPolicy
 ) {
     /**
      * Builder for [SmallImageComplicationData].
@@ -2221,11 +2293,9 @@
     public class Builder(
         private val smallImage: SmallImage,
         private val contentDescription: ComplicationText
-    ) {
+    ) : BaseBuilder<Builder, SmallImageComplicationData>() {
         private var tapAction: PendingIntent? = null
         private var validTimeRange: TimeRange? = null
-        private var cachedWireComplicationData: WireComplicationData? = null
-        private var dataSource: ComponentName? = null
 
         /** Sets optional pending intent to be invoked when the complication is tapped. */
         public fun setTapAction(tapAction: PendingIntent?): Builder = apply {
@@ -2238,32 +2308,17 @@
             this.validTimeRange = validTimeRange
         }
 
-        internal fun setCachedWireComplicationData(
-            cachedWireComplicationData: WireComplicationData?
-        ): Builder = apply {
-            this.cachedWireComplicationData = cachedWireComplicationData
-        }
-
-        /**
-         * Sets the [ComponentName] of the ComplicationDataSourceService that provided this
-         * ComplicationData, if any.
-         *
-         * Note a ComplicationDataSourceService does not need to call this because the system will
-         * set this value on its behalf.
-         */
-        public fun setDataSource(dataSource: ComponentName?): Builder = apply {
-            this.dataSource = dataSource
-        }
-
         /** Builds the [MonochromaticImageComplicationData]. */
-        public fun build(): SmallImageComplicationData =
+        public override fun build(): SmallImageComplicationData =
             SmallImageComplicationData(
                 smallImage,
                 contentDescription,
                 tapAction,
                 validTimeRange,
                 cachedWireComplicationData,
-                dataSource
+                dataSource,
+                persistencePolicy,
+                displayPolicy
             )
     }
 
@@ -2303,6 +2358,8 @@
         if (tapAction != other.tapAction) return false
         if (validTimeRange != other.validTimeRange) return false
         if (dataSource != other.dataSource) return false
+        if (persistencePolicy != other.persistencePolicy) return false
+        if (displayPolicy != other.displayPolicy) return false
 
         return true
     }
@@ -2314,6 +2371,8 @@
         result = 31 * result + (tapAction?.hashCode() ?: 0)
         result = 31 * result + validTimeRange.hashCode()
         result = 31 * result + dataSource.hashCode()
+        result = 31 * result + persistencePolicy.hashCode()
+        result = 31 * result + displayPolicy.hashCode()
         return result
     }
 
@@ -2321,7 +2380,8 @@
         return "SmallImageComplicationData(smallImage=$smallImage, " +
             "contentDescription=$contentDescription), " +
             "tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
-            "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource)"
+            "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
+            "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy)"
     }
 
     override fun hasPlaceholderFields() = smallImage.isPlaceholder()
@@ -2359,13 +2419,17 @@
     tapAction: PendingIntent?,
     validTimeRange: TimeRange?,
     cachedWireComplicationData: WireComplicationData?,
-    dataSource: ComponentName?
+    dataSource: ComponentName?,
+    @ComplicationPersistencePolicy persistencePolicy: Int,
+    @ComplicationDisplayPolicy displayPolicy: Int
 ) : ComplicationData(
     TYPE,
     tapAction = tapAction,
     cachedWireComplicationData = cachedWireComplicationData,
     validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
-    dataSource = dataSource
+    dataSource = dataSource,
+    persistencePolicy = persistencePolicy,
+    displayPolicy = displayPolicy
 ) {
     /**
      * Builder for [PhotoImageComplicationData].
@@ -2378,11 +2442,9 @@
     public class Builder(
         private val photoImage: Icon,
         private val contentDescription: ComplicationText
-    ) {
+    ) : BaseBuilder<Builder, PhotoImageComplicationData>() {
         private var tapAction: PendingIntent? = null
         private var validTimeRange: TimeRange? = null
-        private var cachedWireComplicationData: WireComplicationData? = null
-        private var dataSource: ComponentName? = null
 
         /** Sets optional pending intent to be invoked when the complication is tapped. */
         @SuppressWarnings("MissingGetterMatchingBuilder") // See http://b/174052810
@@ -2396,32 +2458,17 @@
             this.validTimeRange = validTimeRange
         }
 
-        /**
-         * Sets the [ComponentName] of the ComplicationDataSourceService that provided this
-         * ComplicationData, if any.
-         *
-         * Note a ComplicationDataSourceService does not need to call this because the system will
-         * set this value on its behalf.
-         */
-        public fun setDataSource(dataSource: ComponentName?): Builder = apply {
-            this.dataSource = dataSource
-        }
-
-        internal fun setCachedWireComplicationData(
-            cachedWireComplicationData: WireComplicationData?
-        ): Builder = apply {
-            this.cachedWireComplicationData = cachedWireComplicationData
-        }
-
         /** Builds the [PhotoImageComplicationData]. */
-        public fun build(): PhotoImageComplicationData =
+        public override fun build(): PhotoImageComplicationData =
             PhotoImageComplicationData(
                 photoImage,
                 contentDescription,
                 tapAction,
                 validTimeRange,
                 cachedWireComplicationData,
-                dataSource
+                dataSource,
+                persistencePolicy,
+                displayPolicy
             )
     }
 
@@ -2467,6 +2514,8 @@
         if (tapAction != other.tapAction) return false
         if (validTimeRange != other.validTimeRange) return false
         if (dataSource != other.dataSource) return false
+        if (persistencePolicy != other.persistencePolicy) return false
+        if (displayPolicy != other.displayPolicy) return false
 
         return true
     }
@@ -2478,6 +2527,8 @@
             result = 31 * result + tapActionLostDueToSerialization.hashCode()
             result = 31 * result + (tapAction?.hashCode() ?: 0)
             result = 31 * result + dataSource.hashCode()
+            result = 31 * result + persistencePolicy.hashCode()
+            result = 31 * result + displayPolicy.hashCode()
             result
         } else {
             var result = IconHelperBeforeP.hashCode(photoImage)
@@ -2486,6 +2537,8 @@
             result = 31 * result + (tapAction?.hashCode() ?: 0)
             result = 31 * result + validTimeRange.hashCode()
             result = 31 * result + dataSource.hashCode()
+            result = 31 * result + persistencePolicy.hashCode()
+            result = 31 * result + displayPolicy.hashCode()
             result
         }
     }
@@ -2494,7 +2547,8 @@
         return "PhotoImageComplicationData(photoImage=$photoImage, " +
             "contentDescription=$contentDescription), " +
             "tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
-            "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource)"
+            "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
+            "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy)"
     }
 
     override fun hasPlaceholderFields() = photoImage.isPlaceholder()
@@ -2545,14 +2599,17 @@
     tapAction: PendingIntent?,
     validTimeRange: TimeRange?,
     cachedWireComplicationData: WireComplicationData?,
-    dataSource: ComponentName?
-) :
-    ComplicationData(
+    dataSource: ComponentName?,
+    @ComplicationPersistencePolicy persistencePolicy: Int,
+    @ComplicationDisplayPolicy displayPolicy: Int
+) : ComplicationData(
         TYPE,
         tapAction,
         cachedWireComplicationData,
         validTimeRange ?: TimeRange.ALWAYS,
-        dataSource = dataSource
+        dataSource = dataSource,
+        persistencePolicy = persistencePolicy,
+        displayPolicy = displayPolicy
     ) {
 
     /** The [LayoutElementBuilders.Layout] to be displayed when the device is ambient. */
@@ -2589,7 +2646,7 @@
         private val interactiveLayout: ByteArray,
         private val layoutResources: ByteArray,
         private val contentDescription: ComplicationText
-    ) {
+    ) : BaseBuilder<Builder, ProtoLayoutComplicationData>() {
         /**
          * @param ambientLayout The [LayoutElementBuilders.Layout] to be displayed when the device
          * is ambient
@@ -2613,8 +2670,6 @@
 
         private var tapAction: PendingIntent? = null
         private var validTimeRange: TimeRange? = null
-        private var cachedWireComplicationData: WireComplicationData? = null
-        private var dataSource: ComponentName? = null
 
         /** Sets optional pending intent to be invoked when the complication is tapped. */
         public fun setTapAction(tapAction: PendingIntent?): Builder = apply {
@@ -2627,23 +2682,8 @@
             this.validTimeRange = validTimeRange
         }
 
-        /**
-         * Sets the [ComponentName] of the ComplicationDataSourceService that provided this
-         * ComplicationData, if any.
-         *
-         * Note a ComplicationDataSourceService does not need to call this because the system will
-         * set this value on its behalf.
-         */
-        public fun setDataSource(dataSource: ComponentName?): Builder = apply {
-            this.dataSource = dataSource
-        }
-
-        internal fun setCachedWireComplicationData(
-            cachedWireComplicationData: WireComplicationData?
-        ): Builder = apply { this.cachedWireComplicationData = cachedWireComplicationData }
-
         /** Builds the [ProtoLayoutComplicationData]. */
-        public fun build(): ProtoLayoutComplicationData =
+        public override fun build(): ProtoLayoutComplicationData =
             ProtoLayoutComplicationData(
                 ambientLayout,
                 interactiveLayout,
@@ -2652,7 +2692,9 @@
                 tapAction,
                 validTimeRange,
                 cachedWireComplicationData,
-                dataSource
+                dataSource,
+                persistencePolicy,
+                displayPolicy
             )
     }
 
@@ -2698,6 +2740,8 @@
         if (tapAction != other.tapAction) return false
         if (validTimeRange != other.validTimeRange) return false
         if (dataSource != other.dataSource) return false
+        if (persistencePolicy != other.persistencePolicy) return false
+        if (displayPolicy != other.displayPolicy) return false
 
         return true
     }
@@ -2711,6 +2755,9 @@
         result = 31 * result + (tapAction?.hashCode() ?: 0)
         result = 31 * result + validTimeRange.hashCode()
         result = 31 * result + dataSource.hashCode()
+        result = 31 * result + persistencePolicy.hashCode()
+        result = 31 * result + persistencePolicy.hashCode()
+        result = 31 * result + displayPolicy.hashCode()
         return result
     }
 
@@ -2720,7 +2767,8 @@
             "resourcesWireFormat=$layoutResourcesWireFormat, " +
             "contentDescription=$contentDescription, " +
             "tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
-            "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource)"
+            "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
+            "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy)"
     }
 
     /** @hide */
@@ -2754,14 +2802,18 @@
     validTimeRange: TimeRange?,
     cachedWireComplicationData: WireComplicationData?,
     dataSource: ComponentName?,
-    public val styleHint: StyleHint
+    public val styleHint: StyleHint,
+    @ComplicationPersistencePolicy persistencePolicy: Int,
+    @ComplicationDisplayPolicy displayPolicy: Int
 ) : ComplicationData(
         TYPE,
         tapAction = tapAction,
         cachedWireComplicationData = cachedWireComplicationData,
         validTimeRange = validTimeRange ?: TimeRange.ALWAYS,
-        dataSource = dataSource
-    ) {
+        dataSource = dataSource,
+        persistencePolicy = persistencePolicy,
+        displayPolicy = displayPolicy
+) {
 
     init {
         require(complicationList.size <= MAX_ITEMS) {
@@ -2817,11 +2869,9 @@
         private val complicationList: List<ComplicationData>,
         private val styleHint: StyleHint,
         private val contentDescription: ComplicationText
-    ) {
+    ) : BaseBuilder<Builder, ListComplicationData>() {
         private var tapAction: PendingIntent? = null
         private var validTimeRange: TimeRange? = null
-        private var cachedWireComplicationData: WireComplicationData? = null
-        private var dataSource: ComponentName? = null
 
         /** Sets optional pending intent to be invoked when the complication is tapped. */
         @SuppressWarnings("MissingGetterMatchingBuilder") // See http://b/174052810
@@ -2835,23 +2885,8 @@
             this.validTimeRange = validTimeRange
         }
 
-        /**
-         * Sets the [ComponentName] of the ComplicationDataSourceService that provided this
-         * ComplicationData, if any.
-         *
-         * Note a ComplicationDataSourceService does not need to call this because the system will
-         * set this value on its behalf.
-         */
-        public fun setDataSource(dataSource: ComponentName?): Builder = apply {
-            this.dataSource = dataSource
-        }
-
-        internal fun setCachedWireComplicationData(
-            cachedWireComplicationData: WireComplicationData?
-        ): Builder = apply { this.cachedWireComplicationData = cachedWireComplicationData }
-
         /** Builds the [ListComplicationData]. */
-        public fun build(): ListComplicationData =
+        public override fun build(): ListComplicationData =
             ListComplicationData(
                 complicationList,
                 contentDescription,
@@ -2859,7 +2894,9 @@
                 validTimeRange,
                 cachedWireComplicationData,
                 dataSource,
-                styleHint
+                styleHint,
+                persistencePolicy,
+                displayPolicy
             )
     }
 
@@ -2906,6 +2943,8 @@
         if (validTimeRange != other.validTimeRange) return false
         if (dataSource != other.dataSource) return false
         if (styleHint != other.styleHint) return false
+        if (persistencePolicy != other.persistencePolicy) return false
+        if (displayPolicy != other.displayPolicy) return false
 
         return true
     }
@@ -2918,6 +2957,8 @@
         result = 31 * result + validTimeRange.hashCode()
         result = 31 * result + dataSource.hashCode()
         result = 31 * result + styleHint.hashCode()
+        result = 31 * result + persistencePolicy.hashCode()
+        result = 31 * result + displayPolicy.hashCode()
         return result
     }
 
@@ -2926,7 +2967,8 @@
             "contentDescription=$contentDescription, " +
             "tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
             "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
-            "styleHint=$styleHint)"
+            "styleHint=$styleHint, persistencePolicy=$persistencePolicy, " +
+            "displayPolicy=$displayPolicy)"
     }
 
     /** @hide */
@@ -2973,23 +3015,25 @@
     public val monochromaticImage: MonochromaticImage?,
     public val smallImage: SmallImage?,
     cachedWireComplicationData: WireComplicationData?,
-    dataSource: ComponentName?
+    dataSource: ComponentName?,
+    @ComplicationPersistencePolicy persistencePolicy: Int,
+    @ComplicationDisplayPolicy displayPolicy: Int
 ) : ComplicationData(
     TYPE,
     tapAction = null,
     cachedWireComplicationData = cachedWireComplicationData,
-    dataSource = dataSource
+    dataSource = dataSource,
+    persistencePolicy = persistencePolicy,
+    displayPolicy = displayPolicy
 ) {
     /**
      * Builder for [NoPermissionComplicationData].
      */
-    public class Builder {
+    public class Builder : BaseBuilder<Builder, NoPermissionComplicationData>() {
         private var text: ComplicationText? = null
         private var title: ComplicationText? = null
         private var monochromaticImage: MonochromaticImage? = null
         private var smallImage: SmallImage? = null
-        private var cachedWireComplicationData: WireComplicationData? = null
-        private var dataSource: ComponentName? = null
 
         /** Sets optional text associated with the complication data. */
         public fun setText(text: ComplicationText?): Builder = apply {
@@ -3011,32 +3055,17 @@
             this.smallImage = smallImage
         }
 
-        /**
-         * Sets the [ComponentName] of the ComplicationDataSourceService that provided this
-         * ComplicationData, if any.
-         *
-         * Note a ComplicationDataSourceService does not need to call this because the system will
-         * set this value on its behalf.
-         */
-        public fun setDataSource(dataSource: ComponentName?): Builder = apply {
-            this.dataSource = dataSource
-        }
-
-        internal fun setCachedWireComplicationData(
-            cachedWireComplicationData: WireComplicationData?
-        ): Builder = apply {
-            this.cachedWireComplicationData = cachedWireComplicationData
-        }
-
         /** Builds the [NoPermissionComplicationData]. */
-        public fun build(): NoPermissionComplicationData =
+        public override fun build(): NoPermissionComplicationData =
             NoPermissionComplicationData(
                 text,
                 title,
                 monochromaticImage,
                 smallImage,
                 cachedWireComplicationData,
-                dataSource
+                dataSource,
+                persistencePolicy,
+                displayPolicy
             )
     }
 
@@ -3068,6 +3097,8 @@
         if (tapAction != other.tapAction) return false
         if (validTimeRange != other.validTimeRange) return false
         if (dataSource != other.dataSource) return false
+        if (persistencePolicy != other.persistencePolicy) return false
+        if (displayPolicy != other.displayPolicy) return false
 
         return true
     }
@@ -3081,6 +3112,8 @@
         result = 31 * result + (tapAction?.hashCode() ?: 0)
         result = 31 * result + validTimeRange.hashCode()
         result = 31 * result + dataSource.hashCode()
+        result = 31 * result + persistencePolicy.hashCode()
+        result = 31 * result + displayPolicy.hashCode()
         return result
     }
 
@@ -3088,7 +3121,8 @@
         return "NoPermissionComplicationData(text=$text, title=$title, " +
             "monochromaticImage=$monochromaticImage, smallImage=$smallImage, " +
             "tapActionLostDueToSerialization=$tapActionLostDueToSerialization, " +
-            "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource)"
+            "tapAction=$tapAction, validTimeRange=$validTimeRange, dataSource=$dataSource, " +
+            "persistencePolicy=$persistencePolicy, displayPolicy=$displayPolicy)"
     }
 
     override fun getNextChangeInstant(afterInstant: Instant): Instant {
@@ -3110,174 +3144,202 @@
 }
 
 @OptIn(ComplicationExperimental::class)
-internal fun WireComplicationData.toPlaceholderComplicationData(): ComplicationData? = when (type) {
-    NoDataComplicationData.TYPE.toWireComplicationType() -> null
+internal fun WireComplicationData.toPlaceholderComplicationData(): ComplicationData? {
+    // Make sure we use the correct dataSource, persistencePolicy & displayPolicy.
+    val dataSourceCopy = dataSource
+    val persistencePolicyCopy = persistencePolicy
+    val displayPolicyCopy = displayPolicy
+    return when (type) {
+        NoDataComplicationData.TYPE.toWireComplicationType() -> null
 
-    ShortTextComplicationData.TYPE.toWireComplicationType() -> {
-        ShortTextComplicationData.Builder(
-            shortText!!.toApiComplicationTextPlaceholderAware(),
-            contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
-        ).apply {
-            setTapAction(tapAction)
-            setValidTimeRange(parseTimeRange())
-            setMonochromaticImage(parseIconPlaceholderAware())
-            setSmallImage(parseSmallImagePlaceholderAware())
-            setTitle(shortTitle?.toApiComplicationTextPlaceholderAware())
-            setDataSource(dataSource)
-        }.build()
-    }
-
-    LongTextComplicationData.TYPE.toWireComplicationType() -> {
-        LongTextComplicationData.Builder(
-            longText!!.toApiComplicationTextPlaceholderAware(),
-            contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
-        ).apply {
-            setTapAction(tapAction)
-            setValidTimeRange(parseTimeRange())
-            setMonochromaticImage(parseIconPlaceholderAware())
-            setSmallImage(parseSmallImagePlaceholderAware())
-            setTitle(longTitle?.toApiComplicationTextPlaceholderAware())
-            setDataSource(dataSource)
-        }.build()
-    }
-
-    RangedValueComplicationData.TYPE.toWireComplicationType() ->
-        RangedValueComplicationData.Builder(
-            value = rangedValue,
-            min = rangedMinValue,
-            max = rangedMaxValue,
-            contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
-        ).apply {
-            setTapAction(tapAction)
-            setValidTimeRange(parseTimeRange())
-            setMonochromaticImage(parseIconPlaceholderAware())
-            setSmallImage(parseSmallImagePlaceholderAware())
-            setTitle(shortTitle?.toApiComplicationTextPlaceholderAware())
-            setText(shortText?.toApiComplicationTextPlaceholderAware())
-            setDataSource(dataSource)
-            colorRamp?.let {
-                setColorRamp(ColorRamp(it, isColorRampInterpolated!!))
-            }
-        }.build()
-
-    MonochromaticImageComplicationData.TYPE.toWireComplicationType() ->
-        MonochromaticImageComplicationData(
-            parseIconPlaceholderAware()!!,
-            contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY,
-            tapAction,
-            parseTimeRange(),
-            this,
-            dataSource
-        )
-
-    SmallImageComplicationData.TYPE.toWireComplicationType() ->
-        SmallImageComplicationData(
-            parseSmallImagePlaceholderAware()!!,
-            contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY,
-            tapAction,
-            parseTimeRange(),
-            this,
-            dataSource
-        )
-
-    PhotoImageComplicationData.TYPE.toWireComplicationType() ->
-        PhotoImageComplicationData(
-            parseLargeImagePlaceholderAware()!!,
-            contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY,
-            tapAction,
-            parseTimeRange(),
-            this,
-            dataSource
-        )
-
-    // TODO(b/230102159): We need to build support for placeholder ProtoLayoutComplicationData.
-    ProtoLayoutComplicationData.TYPE.toWireComplicationType() ->
-        ProtoLayoutComplicationData.Builder(
-            ambientLayout!!,
-            interactiveLayout!!,
-            layoutResources!!,
-            contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
-        )
-            .apply {
+        ShortTextComplicationData.TYPE.toWireComplicationType() -> {
+            ShortTextComplicationData.Builder(
+                shortText!!.toApiComplicationTextPlaceholderAware(),
+                contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
+            ).apply {
                 setTapAction(tapAction)
                 setValidTimeRange(parseTimeRange())
-                setDataSource(dataSource)
-            }
-            .build()
+                setMonochromaticImage(parseIconPlaceholderAware())
+                setSmallImage(parseSmallImagePlaceholderAware())
+                setTitle(shortTitle?.toApiComplicationTextPlaceholderAware())
+                setDataSource(dataSourceCopy)
+                setPersistencePolicy(persistencePolicyCopy)
+                setDisplayPolicy(displayPolicyCopy)
+            }.build()
+        }
 
-    ListComplicationData.TYPE.toWireComplicationType() ->
-        ListComplicationData.Builder(
-            listEntries!!.map { it.toApiComplicationData() },
-            ListComplicationData.StyleHint.fromWireFormat(listStyleHint),
-            contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
-        )
-            .apply {
+        LongTextComplicationData.TYPE.toWireComplicationType() -> {
+            LongTextComplicationData.Builder(
+                longText!!.toApiComplicationTextPlaceholderAware(),
+                contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
+            ).apply {
                 setTapAction(tapAction)
                 setValidTimeRange(parseTimeRange())
-                setDataSource(dataSource)
-            }
-            .build()
+                setMonochromaticImage(parseIconPlaceholderAware())
+                setSmallImage(parseSmallImagePlaceholderAware())
+                setTitle(longTitle?.toApiComplicationTextPlaceholderAware())
+                setDataSource(dataSourceCopy)
+                setPersistencePolicy(persistencePolicyCopy)
+                setDisplayPolicy(displayPolicyCopy)
+            }.build()
+        }
 
-    GoalProgressComplicationData.TYPE.toWireComplicationType() ->
-        GoalProgressComplicationData.Builder(
-            value = rangedValue,
-            targetValue = targetValue,
-            contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
-        ).apply {
-            setTapAction(tapAction)
-            setValidTimeRange(parseTimeRange())
-            setMonochromaticImage(parseIconPlaceholderAware())
-            setSmallImage(parseSmallImagePlaceholderAware())
-            setTitle(shortTitle?.toApiComplicationTextPlaceholderAware())
-            setText(shortText?.toApiComplicationTextPlaceholderAware())
-            setDataSource(dataSource)
-            colorRamp?.let {
-                setColorRamp(ColorRamp(it, isColorRampInterpolated!!))
-            }
-        }.build()
-
-    DiscreteRangedValueComplicationData.TYPE.toWireComplicationType() ->
-        DiscreteRangedValueComplicationData.Builder(
-            value = discreteRangedValue,
-            min = discreteRangedMinValue,
-            max = discreteRangedMaxValue,
-            contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
-        ).apply {
-            setTapAction(tapAction)
-            setValidTimeRange(parseTimeRange())
-            setMonochromaticImage(parseIconPlaceholderAware())
-            setSmallImage(parseSmallImagePlaceholderAware())
-            setTitle(shortTitle?.toApiComplicationTextPlaceholderAware())
-            setText(shortText?.toApiComplicationTextPlaceholderAware())
-            setDataSource(dataSource)
-        }.build()
-
-    WeightedElementsComplicationData.TYPE.toWireComplicationType() ->
-        WeightedElementsComplicationData.Builder(
-            elements = if (elementWeights!!.isEmpty()) {
-                WeightedElementsComplicationData.PLACEHOLDER
-            } else {
-                val elementWeights = this.elementWeights!!
-                val elementColors = this.elementColors!!
-                require(elementWeights.size == elementColors.size) {
-                    "elementWeights and elementColors must have the same size"
+        RangedValueComplicationData.TYPE.toWireComplicationType() ->
+            RangedValueComplicationData.Builder(
+                value = rangedValue,
+                min = rangedMinValue,
+                max = rangedMaxValue,
+                contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
+            ).apply {
+                setTapAction(tapAction)
+                setValidTimeRange(parseTimeRange())
+                setMonochromaticImage(parseIconPlaceholderAware())
+                setSmallImage(parseSmallImagePlaceholderAware())
+                setTitle(shortTitle?.toApiComplicationTextPlaceholderAware())
+                setText(shortText?.toApiComplicationTextPlaceholderAware())
+                setDataSource(dataSourceCopy)
+                colorRamp?.let {
+                    setColorRamp(ColorRamp(it, isColorRampInterpolated!!))
                 }
-                elementWeights.mapIndexed { index, weight ->
-                    WeightedElementsComplicationData.Element(weight, elementColors[index])
-                }.toList()
-            },
-            contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
-        ).apply {
-            setTapAction(tapAction)
-            setValidTimeRange(parseTimeRange())
-            setMonochromaticImage(parseIconPlaceholderAware())
-            setSmallImage(parseSmallImagePlaceholderAware())
-            setTitle(shortTitle?.toApiComplicationTextPlaceholderAware())
-            setText(shortText?.toApiComplicationTextPlaceholderAware())
-            setDataSource(dataSource)
-        }.build()
+                setPersistencePolicy(persistencePolicyCopy)
+                setDisplayPolicy(displayPolicyCopy)
+            }.build()
 
-    else -> null
+        MonochromaticImageComplicationData.TYPE.toWireComplicationType() ->
+            MonochromaticImageComplicationData(
+                parseIconPlaceholderAware()!!,
+                contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY,
+                tapAction,
+                parseTimeRange(),
+                this,
+                dataSourceCopy,
+                persistencePolicyCopy,
+                displayPolicyCopy
+            )
+
+        SmallImageComplicationData.TYPE.toWireComplicationType() ->
+            SmallImageComplicationData(
+                parseSmallImagePlaceholderAware()!!,
+                contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY,
+                tapAction,
+                parseTimeRange(),
+                this,
+                dataSourceCopy,
+                persistencePolicyCopy,
+                displayPolicyCopy
+            )
+
+        PhotoImageComplicationData.TYPE.toWireComplicationType() ->
+            PhotoImageComplicationData(
+                parseLargeImagePlaceholderAware()!!,
+                contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY,
+                tapAction,
+                parseTimeRange(),
+                this,
+                dataSourceCopy,
+                persistencePolicyCopy,
+                displayPolicyCopy
+            )
+
+        // TODO(b/230102159): We need to build support for placeholder ProtoLayoutComplicationData.
+        ProtoLayoutComplicationData.TYPE.toWireComplicationType() ->
+            ProtoLayoutComplicationData.Builder(
+                ambientLayout!!,
+                interactiveLayout!!,
+                layoutResources!!,
+                contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
+            )
+                .apply {
+                    setTapAction(tapAction)
+                    setValidTimeRange(parseTimeRange())
+                    setDataSource(dataSourceCopy)
+                    setPersistencePolicy(persistencePolicyCopy)
+                    setDisplayPolicy(displayPolicyCopy)
+                }
+                .build()
+
+        ListComplicationData.TYPE.toWireComplicationType() ->
+            ListComplicationData.Builder(
+                listEntries!!.map { it.toApiComplicationData() },
+                ListComplicationData.StyleHint.fromWireFormat(listStyleHint),
+                contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
+            )
+                .apply {
+                    setTapAction(tapAction)
+                    setValidTimeRange(parseTimeRange())
+                    setDataSource(dataSourceCopy)
+                    setPersistencePolicy(persistencePolicyCopy)
+                    setDisplayPolicy(displayPolicyCopy)
+                }
+                .build()
+
+        GoalProgressComplicationData.TYPE.toWireComplicationType() ->
+            GoalProgressComplicationData.Builder(
+                value = rangedValue,
+                targetValue = targetValue,
+                contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
+            ).apply {
+                setTapAction(tapAction)
+                setValidTimeRange(parseTimeRange())
+                setMonochromaticImage(parseIconPlaceholderAware())
+                setSmallImage(parseSmallImagePlaceholderAware())
+                setTitle(shortTitle?.toApiComplicationTextPlaceholderAware())
+                setText(shortText?.toApiComplicationTextPlaceholderAware())
+                setDataSource(dataSourceCopy)
+                colorRamp?.let {
+                    setColorRamp(ColorRamp(it, isColorRampInterpolated!!))
+                }
+                setPersistencePolicy(persistencePolicyCopy)
+                setDisplayPolicy(displayPolicyCopy)
+            }.build()
+
+        DiscreteRangedValueComplicationData.TYPE.toWireComplicationType() ->
+            DiscreteRangedValueComplicationData.Builder(
+                value = discreteRangedValue,
+                min = discreteRangedMinValue,
+                max = discreteRangedMaxValue,
+                contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
+            ).apply {
+                setTapAction(tapAction)
+                setValidTimeRange(parseTimeRange())
+                setMonochromaticImage(parseIconPlaceholderAware())
+                setSmallImage(parseSmallImagePlaceholderAware())
+                setTitle(shortTitle?.toApiComplicationTextPlaceholderAware())
+                setText(shortText?.toApiComplicationTextPlaceholderAware())
+                setDataSource(dataSourceCopy)
+                setPersistencePolicy(persistencePolicyCopy)
+                setDisplayPolicy(displayPolicyCopy)
+            }.build()
+
+        WeightedElementsComplicationData.TYPE.toWireComplicationType() ->
+            WeightedElementsComplicationData.Builder(
+                elements = if (elementWeights!!.isEmpty()) {
+                    WeightedElementsComplicationData.PLACEHOLDER
+                } else {
+                    val elementWeights = this.elementWeights!!
+                    val elementColors = this.elementColors!!
+                    require(elementWeights.size == elementColors.size) {
+                        "elementWeights and elementColors must have the same size"
+                    }
+                    elementWeights.mapIndexed { index, weight ->
+                        WeightedElementsComplicationData.Element(weight, elementColors[index])
+                    }.toList()
+                },
+                contentDescription?.toApiComplicationText() ?: ComplicationText.EMPTY
+            ).apply {
+                setTapAction(tapAction)
+                setValidTimeRange(parseTimeRange())
+                setMonochromaticImage(parseIconPlaceholderAware())
+                setSmallImage(parseSmallImagePlaceholderAware())
+                setTitle(shortTitle?.toApiComplicationTextPlaceholderAware())
+                setText(shortText?.toApiComplicationTextPlaceholderAware())
+                setDataSource(dataSourceCopy)
+                setPersistencePolicy(persistencePolicyCopy)
+                setDisplayPolicy(displayPolicyCopy)
+            }.build()
+
+        else -> null
+    }
 }
 
 /**
@@ -3286,6 +3348,10 @@
 @OptIn(ComplicationExperimental::class)
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public fun WireComplicationData.toApiComplicationData(): ComplicationData {
+    // Make sure we use the correct dataSource, persistencePolicy & displayPolicy.
+    val dataSourceCopy = dataSource
+    val persistencePolicyCopy = persistencePolicy
+    val displayPolicyCopy = displayPolicy
     val wireComplicationData = this
     return when (type) {
         NoDataComplicationData.TYPE.toWireComplicationType() -> {
@@ -3310,7 +3376,9 @@
                 setMonochromaticImage(parseIcon())
                 setSmallImage(parseSmallImage())
                 setCachedWireComplicationData(wireComplicationData)
-                setDataSource(dataSource)
+                setDataSource(dataSourceCopy)
+                setPersistencePolicy(persistencePolicyCopy)
+                setDisplayPolicy(displayPolicyCopy)
             }.build()
 
         LongTextComplicationData.TYPE.toWireComplicationType() ->
@@ -3324,7 +3392,9 @@
                 setMonochromaticImage(parseIcon())
                 setSmallImage(parseSmallImage())
                 setCachedWireComplicationData(wireComplicationData)
-                setDataSource(dataSource)
+                setDataSource(dataSourceCopy)
+                setPersistencePolicy(persistencePolicyCopy)
+                setDisplayPolicy(displayPolicyCopy)
             }.build()
 
         RangedValueComplicationData.TYPE.toWireComplicationType() ->
@@ -3341,10 +3411,12 @@
                 setTitle(shortTitle?.toApiComplicationText())
                 setText(shortText?.toApiComplicationText())
                 setCachedWireComplicationData(wireComplicationData)
-                setDataSource(dataSource)
                 colorRamp?.let {
                     setColorRamp(ColorRamp(it, isColorRampInterpolated!!))
                 }
+                setDataSource(dataSourceCopy)
+                setPersistencePolicy(persistencePolicyCopy)
+                setDisplayPolicy(displayPolicyCopy)
             }.build()
 
         MonochromaticImageComplicationData.TYPE.toWireComplicationType() ->
@@ -3355,7 +3427,9 @@
                 setTapAction(tapAction)
                 setValidTimeRange(parseTimeRange())
                 setCachedWireComplicationData(wireComplicationData)
-                setDataSource(dataSource)
+                setDataSource(dataSourceCopy)
+                setPersistencePolicy(persistencePolicyCopy)
+                setDisplayPolicy(displayPolicyCopy)
             }.build()
 
         SmallImageComplicationData.TYPE.toWireComplicationType() ->
@@ -3366,7 +3440,9 @@
                 setTapAction(tapAction)
                 setValidTimeRange(parseTimeRange())
                 setCachedWireComplicationData(wireComplicationData)
-                setDataSource(dataSource)
+                setDataSource(dataSourceCopy)
+                setPersistencePolicy(persistencePolicyCopy)
+                setDisplayPolicy(displayPolicyCopy)
             }.build()
 
         PhotoImageComplicationData.TYPE.toWireComplicationType() ->
@@ -3377,7 +3453,9 @@
                 setTapAction(tapAction)
                 setValidTimeRange(parseTimeRange())
                 setCachedWireComplicationData(wireComplicationData)
-                setDataSource(dataSource)
+                setDataSource(dataSourceCopy)
+                setPersistencePolicy(persistencePolicyCopy)
+                setDisplayPolicy(displayPolicyCopy)
             }.build()
 
         ProtoLayoutComplicationData.TYPE.toWireComplicationType() ->
@@ -3391,7 +3469,9 @@
                     setTapAction(tapAction)
                     setValidTimeRange(parseTimeRange())
                     setCachedWireComplicationData(wireComplicationData)
-                    setDataSource(dataSource)
+                    setDataSource(dataSourceCopy)
+                    setPersistencePolicy(persistencePolicyCopy)
+                    setDisplayPolicy(displayPolicyCopy)
                 }
                 .build()
 
@@ -3405,7 +3485,9 @@
                     setTapAction(tapAction)
                     setValidTimeRange(parseTimeRange())
                     setCachedWireComplicationData(wireComplicationData)
-                    setDataSource(dataSource)
+                    setDataSource(dataSourceCopy)
+                    setPersistencePolicy(persistencePolicyCopy)
+                    setDisplayPolicy(displayPolicyCopy)
                 }
                 .build()
 
@@ -3416,7 +3498,9 @@
                 setTitle(shortTitle?.toApiComplicationText())
                 setText(shortText?.toApiComplicationText())
                 setCachedWireComplicationData(wireComplicationData)
-                setDataSource(dataSource)
+                setDataSource(dataSourceCopy)
+                setPersistencePolicy(persistencePolicyCopy)
+                setDisplayPolicy(displayPolicyCopy)
             }.build()
 
         GoalProgressComplicationData.TYPE.toWireComplicationType() ->
@@ -3433,10 +3517,12 @@
                 setTitle(shortTitle?.toApiComplicationText())
                 setText(shortText?.toApiComplicationText())
                 setCachedWireComplicationData(wireComplicationData)
-                setDataSource(dataSource)
                 colorRamp?.let {
                     setColorRamp(ColorRamp(it, isColorRampInterpolated!!))
                 }
+                setDataSource(dataSourceCopy)
+                setPersistencePolicy(persistencePolicyCopy)
+                setDisplayPolicy(displayPolicyCopy)
             }.build()
 
         DiscreteRangedValueComplicationData.TYPE.toWireComplicationType() ->
@@ -3454,7 +3540,9 @@
                 setTitle(shortTitle?.toApiComplicationText())
                 setText(shortText?.toApiComplicationText())
                 setCachedWireComplicationData(wireComplicationData)
-                setDataSource(dataSource)
+                setDataSource(dataSourceCopy)
+                setPersistencePolicy(persistencePolicyCopy)
+                setDisplayPolicy(displayPolicyCopy)
             }.build()
 
         WeightedElementsComplicationData.TYPE.toWireComplicationType() -> {
@@ -3476,7 +3564,9 @@
                 setTitle(shortTitle?.toApiComplicationText())
                 setText(shortText?.toApiComplicationText())
                 setCachedWireComplicationData(wireComplicationData)
-                setDataSource(dataSource)
+                setDataSource(dataSourceCopy)
+                setPersistencePolicy(persistencePolicyCopy)
+                setDisplayPolicy(displayPolicyCopy)
             }.build()
         }
 
diff --git a/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/DataTest.kt b/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/DataTest.kt
index 73c4f3b..199c818 100644
--- a/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/DataTest.kt
+++ b/wear/watchface/watchface-complications-data/src/test/java/androidx/wear/watchface/complications/data/DataTest.kt
@@ -71,6 +71,8 @@
             .hasSameSerializationAs(
                 WireComplicationDataBuilder(WireComplicationData.TYPE_NO_DATA)
                     .setPlaceholder(null)
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -82,7 +84,7 @@
             "NoDataComplicationData(placeholder=null, " +
                 "tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=TimeRange(" +
                 "startDateTimeMillis=-1000000000-01-01T00:00:00Z, endDateTimeMillis=" +
-                "+1000000000-12-31T23:59:59.999999999Z))"
+                "+1000000000-12-31T23:59:59.999999999Z), persistencePolicy=0, displayPolicy=0)"
         )
     }
 
@@ -125,6 +127,8 @@
         )
             .setTitle("title".complicationText)
             .setDataSource(dataSourceA)
+            .setPersistencePolicy(ComplicationPersistencePolicies.DO_NOT_PERSIST)
+            .setDisplayPolicy(ComplicationDisplayPolicies.DO_NOT_SHOW_WHEN_DEVICE_LOCKED)
             .build()
         ParcelableSubject.assertThat(data.asWireComplicationData())
             .hasSameSerializationAs(
@@ -133,6 +137,8 @@
                     .setShortTitle(WireComplicationText.plainText("title"))
                     .setContentDescription(WireComplicationText.plainText("content description"))
                     .setDataSource(dataSourceA)
+                    .setPersistencePolicy(ComplicationPersistencePolicies.DO_NOT_PERSIST)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.DO_NOT_SHOW_WHEN_DEVICE_LOCKED)
                     .build()
             )
         testRoundTripConversions(data)
@@ -150,6 +156,8 @@
         )
             .setTitle("title".complicationText)
             .setDataSource(dataSourceA)
+            .setPersistencePolicy(ComplicationPersistencePolicies.DO_NOT_PERSIST)
+            .setDisplayPolicy(ComplicationDisplayPolicies.DO_NOT_SHOW_WHEN_DEVICE_LOCKED)
             .build()
         val data3 = ShortTextComplicationData.Builder(
             "text3".complicationText,
@@ -157,6 +165,8 @@
         )
             .setTitle("title3".complicationText)
             .setDataSource(dataSourceB)
+            .setPersistencePolicy(ComplicationPersistencePolicies.DO_NOT_PERSIST)
+            .setDisplayPolicy(ComplicationDisplayPolicies.DO_NOT_SHOW_WHEN_DEVICE_LOCKED)
             .build()
 
         assertThat(data).isEqualTo(data2)
@@ -171,7 +181,7 @@
                 "mTimeDependentText=null}, tapActionLostDueToSerialization=false, tapAction=null," +
                 " validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                 "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), dataSource=" +
-                "ComponentInfo{com.pkg_a/com.a})"
+                "ComponentInfo{com.pkg_a/com.a}, persistencePolicy=1, displayPolicy=1)"
         )
     }
 
@@ -197,6 +207,8 @@
                     .setSmallImageStyle(WireComplicationData.IMAGE_STYLE_PHOTO)
                     .setContentDescription(WireComplicationText.plainText("content description"))
                     .setDataSource(dataSourceA)
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -244,7 +256,7 @@
                 "mTimeDependentText=null}, tapActionLostDueToSerialization=false, tapAction=null," +
                 " validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                 "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
-                "dataSource=ComponentInfo{com.pkg_a/com.a})"
+                "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, displayPolicy=0)"
         )
     }
 
@@ -264,6 +276,8 @@
                     .setLongTitle(WireComplicationText.plainText("title"))
                     .setContentDescription(WireComplicationText.plainText("content description"))
                     .setDataSource(dataSourceA)
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -302,7 +316,8 @@
                 "mTimeDependentText=null}), tapActionLostDueToSerialization=false, " +
                 "tapAction=null, validTimeRange=TimeRange(startDateTimeMillis=" +
                 "-1000000000-01-01T00:00:00Z, endDateTimeMillis=" +
-                "+1000000000-12-31T23:59:59.999999999Z), dataSource=ComponentInfo{com.pkg_a/com.a})"
+                "+1000000000-12-31T23:59:59.999999999Z), " +
+                "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, displayPolicy=0)"
         )
     }
 
@@ -328,6 +343,8 @@
                     .setSmallImageStyle(WireComplicationData.IMAGE_STYLE_PHOTO)
                     .setContentDescription(WireComplicationText.plainText("content description"))
                     .setDataSource(dataSourceA)
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -375,7 +392,8 @@
                 "mTimeDependentText=null}), tapActionLostDueToSerialization=false, " +
                 "tapAction=null, validTimeRange=TimeRange(startDateTimeMillis=" +
                 "-1000000000-01-01T00:00:00Z, endDateTimeMillis=" +
-                "+1000000000-12-31T23:59:59.999999999Z), dataSource=ComponentInfo{com.pkg_a/com.a})"
+                "+1000000000-12-31T23:59:59.999999999Z), " +
+                "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, displayPolicy=0)"
         )
     }
 
@@ -398,6 +416,8 @@
                     .setShortTitle(WireComplicationText.plainText("battery"))
                     .setContentDescription(WireComplicationText.plainText("content description"))
                     .setDataSource(dataSourceA)
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -439,7 +459,8 @@
                 "tapAction=null, validTimeRange=TimeRange(" +
                 "startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                 "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), dataSource=" +
-                "ComponentInfo{com.pkg_a/com.a}, colorRamp=null)"
+                "ComponentInfo{com.pkg_a/com.a}, colorRamp=null, persistencePolicy=0, " +
+                "displayPolicy=0)"
         )
     }
 
@@ -468,6 +489,8 @@
                     .setShortTitle(WireComplicationText.plainText("battery"))
                     .setContentDescription(WireComplicationText.plainText("content description"))
                     .setDataSource(dataSourceA)
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -517,7 +540,8 @@
                 "tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=" +
                 "TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                 "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
-                "dataSource=ComponentInfo{com.pkg_a/com.a}, colorRamp=null)"
+                "dataSource=ComponentInfo{com.pkg_a/com.a}, colorRamp=null, persistencePolicy=0, " +
+                "displayPolicy=0)"
         )
     }
 
@@ -542,6 +566,8 @@
                     .setDataSource(dataSourceA)
                     .setColorRamp(intArrayOf(Color.RED, Color.GREEN, Color.BLUE))
                     .setColorRampIsSmoothShaded(true)
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -585,7 +611,7 @@
                 "-1000000000-01-01T00:00:00Z, endDateTimeMillis=" +
                 "+1000000000-12-31T23:59:59.999999999Z), dataSource=" +
                 "ComponentInfo{com.pkg_a/com.a}, colorRamp=ColorRamp(colors=[-65536, -16711936, " +
-                "-16776961], interpolated=true))"
+                "-16776961], interpolated=true), persistencePolicy=0, displayPolicy=0)"
         )
     }
 
@@ -616,6 +642,8 @@
                     .setDataSource(dataSourceA)
                     .setColorRamp(intArrayOf(Color.RED, Color.GREEN, Color.BLUE))
                     .setColorRampIsSmoothShaded(true)
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -667,7 +695,8 @@
                 "TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                 "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                 "dataSource=ComponentInfo{com.pkg_a/com.a}, " +
-                "colorRamp=ColorRamp(colors=[-65536, -16711936, -16776961], interpolated=true))"
+                "colorRamp=ColorRamp(colors=[-65536, -16711936, -16776961], interpolated=true), " +
+                "persistencePolicy=0, displayPolicy=0)"
         )
     }
 
@@ -690,6 +719,8 @@
                     .setShortTitle(WireComplicationText.plainText("battery"))
                     .setContentDescription(WireComplicationText.plainText("content description"))
                     .setDataSource(dataSourceA)
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -730,7 +761,8 @@
                 "mTimeDependentText=null}), tapActionLostDueToSerialization=false, " +
                 "tapAction=null, validTimeRange=TimeRange(startDateTimeMillis=" +
                 "-1000000000-01-01T00:00:00Z, endDateTimeMillis=" +
-                "+1000000000-12-31T23:59:59.999999999Z), dataSource=ComponentInfo{com.pkg_a/com.a})"
+                "+1000000000-12-31T23:59:59.999999999Z), " +
+                "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, displayPolicy=0)"
         )
     }
 
@@ -759,6 +791,8 @@
                     .setShortTitle(WireComplicationText.plainText("battery"))
                     .setContentDescription(WireComplicationText.plainText("content description"))
                     .setDataSource(dataSourceA)
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -808,7 +842,8 @@
                 "mTimeDependentText=null}), tapActionLostDueToSerialization=false, " +
                 "tapAction=null, validTimeRange=TimeRange(startDateTimeMillis=" +
                 "-1000000000-01-01T00:00:00Z, endDateTimeMillis=" +
-                "+1000000000-12-31T23:59:59.999999999Z), dataSource=ComponentInfo{com.pkg_a/com.a})"
+                "+1000000000-12-31T23:59:59.999999999Z), " +
+                "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, displayPolicy=0)"
         )
     }
 
@@ -834,6 +869,8 @@
                     .setDataSource(dataSourceA)
                     .setColorRamp(intArrayOf(Color.RED, Color.GREEN, Color.BLUE))
                     .setColorRampIsSmoothShaded(true)
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -878,7 +915,7 @@
                 "-1000000000-01-01T00:00:00Z, endDateTimeMillis=" +
                 "+1000000000-12-31T23:59:59.999999999Z), dataSource=" +
                 "ComponentInfo{com.pkg_a/com.a}, colorRamp=ColorRamp(colors=[-65536, -16711936, " +
-                "-16776961], interpolated=true))"
+                "-16776961], interpolated=true), persistencePolicy=0, displayPolicy=0)"
         )
     }
 
@@ -904,6 +941,8 @@
                     .setShortTitle(WireComplicationText.plainText("calories"))
                     .setContentDescription(WireComplicationText.plainText("content description"))
                     .setDataSource(dataSourceA)
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -957,7 +996,8 @@
                 "mTimeDependentText=null}), tapActionLostDueToSerialization=false, " +
                 "tapAction=null, validTimeRange=TimeRange(" +
                 "startDateTimeMillis=-1000000000-01-01T00:00:00Z, endDateTimeMillis=" +
-                "+1000000000-12-31T23:59:59.999999999Z), dataSource=ComponentInfo{com.pkg_a/com.a})"
+                "+1000000000-12-31T23:59:59.999999999Z), " +
+                "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, displayPolicy=0)"
         )
     }
 
@@ -989,6 +1029,8 @@
                     .setSmallImageStyle(WireComplicationData.IMAGE_STYLE_PHOTO)
                     .setContentDescription(WireComplicationText.plainText("content description"))
                     .setDataSource(dataSourceA)
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -1051,7 +1093,8 @@
                 "mTimeDependentText=null}), tapActionLostDueToSerialization=false, " +
                 "tapAction=null, validTimeRange=TimeRange(startDateTimeMillis=" +
                 "-1000000000-01-01T00:00:00Z, endDateTimeMillis=" +
-                "+1000000000-12-31T23:59:59.999999999Z), dataSource=ComponentInfo{com.pkg_a/com.a})"
+                "+1000000000-12-31T23:59:59.999999999Z), " +
+                "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, displayPolicy=0)"
         )
     }
 
@@ -1067,6 +1110,8 @@
                     .setIcon(icon)
                     .setContentDescription(WireComplicationText.plainText("content description"))
                     .setDataSource(dataSourceA)
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -1094,7 +1139,8 @@
                 "ComplicationText{mSurroundingText=content description, mTimeDependentText=null})" +
                 ", tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=" +
                 "TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, endDateTimeMillis=" +
-                "+1000000000-12-31T23:59:59.999999999Z), dataSource=ComponentInfo{com.pkg_a/com.a})"
+                "+1000000000-12-31T23:59:59.999999999Z), " +
+                "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, displayPolicy=0)"
         )
     }
 
@@ -1112,6 +1158,8 @@
                     .setSmallImageStyle(WireComplicationData.IMAGE_STYLE_PHOTO)
                     .setContentDescription(WireComplicationText.plainText("content description"))
                     .setDataSource(dataSourceA)
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -1142,7 +1190,8 @@
                 "mSurroundingText=content description, mTimeDependentText=null}), " +
                 "tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=" +
                 "TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, endDateTimeMillis=" +
-                "+1000000000-12-31T23:59:59.999999999Z), dataSource=ComponentInfo{com.pkg_a/com.a})"
+                "+1000000000-12-31T23:59:59.999999999Z), " +
+                "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, displayPolicy=0)"
         )
     }
 
@@ -1162,6 +1211,8 @@
                     .setSmallImageStyle(WireComplicationData.IMAGE_STYLE_PHOTO)
                     .setContentDescription(WireComplicationText.plainText("content description"))
                     .setDataSource(dataSourceA)
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -1189,6 +1240,8 @@
                     .setLargeImage(photoImage)
                     .setContentDescription(WireComplicationText.plainText("content description"))
                     .setDataSource(dataSourceA)
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -1217,7 +1270,8 @@
                 "ComplicationText{mSurroundingText=content description, mTimeDependentText=null})" +
                 ", tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=" +
                 "TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, endDateTimeMillis=" +
-                "+1000000000-12-31T23:59:59.999999999Z), dataSource=ComponentInfo{com.pkg_a/com.a})"
+                "+1000000000-12-31T23:59:59.999999999Z), " +
+                "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, displayPolicy=0)"
         )
     }
 
@@ -1232,6 +1286,8 @@
                 WireComplicationDataBuilder(WireComplicationData.TYPE_NO_PERMISSION)
                     .setShortText(WireComplicationText.plainText("needs location"))
                     .setDataSource(dataSourceA)
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -1258,7 +1314,8 @@
                 " mTimeDependentText=null}, title=null, monochromaticImage=null, smallImage=null," +
                 " tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=" +
                 "TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, endDateTimeMillis=" +
-                "+1000000000-12-31T23:59:59.999999999Z), dataSource=ComponentInfo{com.pkg_a/com.a})"
+                "+1000000000-12-31T23:59:59.999999999Z), " +
+                "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, displayPolicy=0)"
         )
     }
 
@@ -1278,6 +1335,8 @@
                     .setSmallImage(smallImageIcon)
                     .setSmallImageStyle(WireComplicationData.IMAGE_STYLE_PHOTO)
                     .setDataSource(dataSourceA)
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -1309,7 +1368,7 @@
                 "ambientImage=null), tapActionLostDueToSerialization=false, tapAction=null, " +
                 "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                 "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
-                "dataSource=ComponentInfo{com.pkg_a/com.a})"
+                "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, displayPolicy=0)"
         )
     }
 
@@ -1341,17 +1400,27 @@
                         listOf(
                             WireComplicationDataBuilder(WireComplicationData.TYPE_SHORT_TEXT)
                                 .setShortText(WireComplicationText.plainText("text"))
+                                .setPersistencePolicy(
+                                    ComplicationPersistencePolicies.CACHING_ALLOWED
+                                )
+                                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                                 .build(),
                             WireComplicationDataBuilder(WireComplicationData.TYPE_RANGED_VALUE)
                                 .setRangedValue(95f)
                                 .setRangedMinValue(0f)
                                 .setRangedMaxValue(100f)
                                 .setShortText(WireComplicationText.plainText("battery"))
+                                .setPersistencePolicy(
+                                    ComplicationPersistencePolicies.CACHING_ALLOWED
+                                )
+                                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                                 .build()
                         )
                     )
                     .setListStyleHint(ListComplicationData.StyleHint.RowOfColumns.ordinal)
                     .setDataSource(dataSourceA)
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -1415,20 +1484,23 @@
                 "contentDescription=ComplicationText{mSurroundingText=, mTimeDependentText=null}," +
                 " tapActionLostDueToSerialization=false, tapAction=null, " +
                 "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
-                "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), dataSource=null), " +
-                "RangedValueComplicationData(value=95.0, min=0.0, max=100.0, " +
-                "monochromaticImage=null, smallImage=null, title=null, text=ComplicationText{" +
-                "mSurroundingText=battery, mTimeDependentText=null}, contentDescription=" +
-                "ComplicationText{mSurroundingText=, mTimeDependentText=null}), " +
-                "tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=TimeRange(" +
+                "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), dataSource=null, " +
+                "persistencePolicy=0, displayPolicy=0), RangedValueComplicationData(value=95.0, " +
+                "min=0.0, max=100.0, monochromaticImage=null, smallImage=null, title=null, " +
+                "text=ComplicationText{mSurroundingText=battery, mTimeDependentText=null}, " +
+                "contentDescription=ComplicationText{mSurroundingText=, " +
+                "mTimeDependentText=null}), tapActionLostDueToSerialization=false, " +
+                "tapAction=null, validTimeRange=TimeRange(" +
                 "startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                 "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), dataSource=null, " +
-                "colorRamp=null)], contentDescription=ComplicationText{mSurroundingText=, " +
-                "mTimeDependentText=null}, tapActionLostDueToSerialization=false, tapAction=null," +
+                "colorRamp=null, persistencePolicy=0, displayPolicy=0)], " +
+                "contentDescription=ComplicationText{mSurroundingText=, mTimeDependentText=null}," +
+                " tapActionLostDueToSerialization=false, tapAction=null," +
                 " validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z," +
                 " endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), dataSource=" +
                 "ComponentInfo{com.pkg_a/com.a}, " +
-                "styleHint=ListComplicationLayoutStyleHint(wireType=1))"
+                "styleHint=ListComplicationLayoutStyleHint(wireType=1), persistencePolicy=0, " +
+                "displayPolicy=0)"
         )
     }
 
@@ -1456,8 +1528,12 @@
                                 WireComplicationText.plainText("content description")
                             )
                             .setDataSource(dataSourceA)
+                            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                             .build()
                     )
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -1498,10 +1574,11 @@
                 "tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=" +
                 "TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                 "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
-                "dataSource=ComponentInfo{com.pkg_a/com.a}), " +
-                "tapActionLostDueToSerialization=false, tapAction=null," +
+                "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
+                "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null," +
                 " validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
-                "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z))"
+                "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), persistencePolicy=0, " +
+                "displayPolicy=0)"
         )
     }
 
@@ -1523,8 +1600,12 @@
                                 WireComplicationText.plainText("content description")
                             )
                             .setDataSource(dataSourceA)
+                            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                             .build()
                     )
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -1557,10 +1638,11 @@
                 "tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=TimeRange(" +
                 "startDateTimeMillis=-1000000000-01-01T00:00:00Z, endDateTimeMillis=" +
                 "+1000000000-12-31T23:59:59.999999999Z), " +
-                "dataSource=ComponentInfo{com.pkg_a/com.a}), " +
-                "tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=TimeRange(" +
-                "startDateTimeMillis=-1000000000-01-01T00:00:00Z, endDateTimeMillis=" +
-                "+1000000000-12-31T23:59:59.999999999Z))"
+                "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
+                "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
+                "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
+                "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), persistencePolicy=0, " +
+                "displayPolicy=0)"
         )
     }
 
@@ -1591,8 +1673,12 @@
                                 WireComplicationText.plainText("content description")
                             )
                             .setDataSource(dataSourceA)
+                            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                             .build()
                     )
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -1635,11 +1721,12 @@
                 "content description, mTimeDependentText=null}), " +
                 "tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=TimeRange(" +
                 "startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
-                "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z)," +
-                " dataSource=ComponentInfo{com.pkg_a/com.a}, " +
-                "colorRamp=null), tapActionLostDueToSerialization=false, tapAction=null, " +
+                "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
+                "dataSource=ComponentInfo{com.pkg_a/com.a}, colorRamp=null, persistencePolicy=0, " +
+                "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
                 "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
-                "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z))"
+                "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), persistencePolicy=0, " +
+                "displayPolicy=0)"
         )
     }
 
@@ -1671,8 +1758,12 @@
                             .setDataSource(dataSourceA)
                             .setColorRamp(intArrayOf(Color.RED, Color.GREEN, Color.BLUE))
                             .setColorRampIsSmoothShaded(false)
+                            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                             .build()
                     )
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -1716,10 +1807,12 @@
                 "startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                 "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                 "dataSource=ComponentInfo{com.pkg_a/com.a}, " +
-                "colorRamp=ColorRamp(colors=[-65536, -16711936, -16776961], interpolated=false))," +
-                " tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=" +
-                "TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
-                "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z))"
+                "colorRamp=ColorRamp(colors=[-65536, -16711936, -16776961], interpolated=false)," +
+                " persistencePolicy=0, displayPolicy=0), tapActionLostDueToSerialization=false, " +
+                "tapAction=null, validTimeRange=TimeRange(" +
+                "startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
+                "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), persistencePolicy=0, " +
+                "displayPolicy=0)"
         )
     }
 
@@ -1750,8 +1843,12 @@
                                 WireComplicationText.plainText("content description")
                             )
                             .setDataSource(dataSourceA)
+                            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                             .build()
                     )
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -1794,11 +1891,12 @@
                 "content description, mTimeDependentText=null}), " +
                 "tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=TimeRange(" +
                 "startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
-                "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z)," +
-                " dataSource=ComponentInfo{com.pkg_a/com.a}), " +
-                "tapActionLostDueToSerialization=false, tapAction=null, " +
+                "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
+                "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
+                "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
                 "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
-                "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z))"
+                "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), persistencePolicy=0, " +
+                "displayPolicy=0)"
         )
     }
 
@@ -1830,8 +1928,12 @@
                                 WireComplicationText.plainText("content description")
                             )
                             .setDataSource(dataSourceA)
+                            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                             .build()
                     )
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -1879,10 +1981,11 @@
                 "tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=TimeRange(" +
                 "startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                 "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
-                "dataSource=ComponentInfo{com.pkg_a/com.a}), " +
-                "tapActionLostDueToSerialization=false, tapAction=null, " +
+                "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
+                "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
                 "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
-                "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z))"
+                "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), persistencePolicy=0, " +
+                "displayPolicy=0)"
         )
     }
 
@@ -1916,8 +2019,12 @@
                             .setDataSource(dataSourceA)
                             .setColorRamp(intArrayOf(Color.RED, Color.GREEN, Color.BLUE))
                             .setColorRampIsSmoothShaded(true)
+                            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                             .build()
                     )
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -1963,10 +2070,12 @@
                 "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
                 "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), " +
                 "dataSource=ComponentInfo{com.pkg_a/com.a}, " +
-                "colorRamp=ColorRamp(colors=[-65536, -16711936, -16776961], interpolated=true)), " +
-                "tapActionLostDueToSerialization=false, tapAction=null, " +
-                "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
-                "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z))"
+                "colorRamp=ColorRamp(colors=[-65536, -16711936, -16776961], interpolated=true), " +
+                "persistencePolicy=0, displayPolicy=0), tapActionLostDueToSerialization=false, " +
+                "tapAction=null, validTimeRange=TimeRange(" +
+                "startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
+                "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), persistencePolicy=0, " +
+                "displayPolicy=0)"
         )
     }
 
@@ -1988,8 +2097,12 @@
                                 WireComplicationText.plainText("content description")
                             )
                             .setDataSource(dataSourceA)
+                            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                             .build()
                     )
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -2023,11 +2136,12 @@
                 "mSurroundingText=content description, mTimeDependentText=null}), " +
                 "tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=TimeRange(" +
                 "startDateTimeMillis=-1000000000-01-01T00:00:00Z, endDateTimeMillis=" +
-                "+1000000000-12-31T23:59:59.999999999Z)," +
-                " dataSource=ComponentInfo{com.pkg_a/com.a}), " +
-                "tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=TimeRange(" +
-                "startDateTimeMillis=-1000000000-01-01T00:00:00Z, endDateTimeMillis=" +
-                "+1000000000-12-31T23:59:59.999999999Z))"
+                "+1000000000-12-31T23:59:59.999999999Z), " +
+                "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
+                "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
+                "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
+                "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), persistencePolicy=0, " +
+                "displayPolicy=0)"
         )
     }
 
@@ -2050,8 +2164,12 @@
                                 WireComplicationText.plainText("content description")
                             )
                             .setDataSource(dataSourceA)
+                            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                             .build()
                     )
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -2085,11 +2203,12 @@
                 "content description, mTimeDependentText=null}), tapActionLostDueToSerialization=" +
                 "false, tapAction=null, validTimeRange=TimeRange(startDateTimeMillis=" +
                 "-1000000000-01-01T00:00:00Z, endDateTimeMillis=" +
-                "+1000000000-12-31T23:59:59.999999999Z)," +
-                " dataSource=ComponentInfo{com.pkg_a/com.a}), " +
-                "tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=TimeRange(" +
-                "startDateTimeMillis=-1000000000-01-01T00:00:00Z, endDateTimeMillis=" +
-                "+1000000000-12-31T23:59:59.999999999Z))"
+                "+1000000000-12-31T23:59:59.999999999Z), " +
+                "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
+                "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
+                "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
+                "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), persistencePolicy=0, " +
+                "displayPolicy=0)"
         )
     }
 
@@ -2111,8 +2230,12 @@
                                 WireComplicationText.plainText("content description")
                             )
                             .setDataSource(dataSourceA)
+                            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                             .build()
                     )
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
         testRoundTripConversions(data)
@@ -2144,11 +2267,12 @@
                 "mSurroundingText=content description, mTimeDependentText=null}), " +
                 "tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=TimeRange(" +
                 "startDateTimeMillis=-1000000000-01-01T00:00:00Z, endDateTimeMillis=" +
-                "+1000000000-12-31T23:59:59.999999999Z)," +
-                " dataSource=ComponentInfo{com.pkg_a/com.a}), " +
-                "tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=TimeRange(" +
-                "startDateTimeMillis=-1000000000-01-01T00:00:00Z, endDateTimeMillis=" +
-                "+1000000000-12-31T23:59:59.999999999Z))"
+                "+1000000000-12-31T23:59:59.999999999Z), " +
+                "dataSource=ComponentInfo{com.pkg_a/com.a}, persistencePolicy=0, " +
+                "displayPolicy=0), tapActionLostDueToSerialization=false, tapAction=null, " +
+                "validTimeRange=TimeRange(startDateTimeMillis=-1000000000-01-01T00:00:00Z, " +
+                "endDateTimeMillis=+1000000000-12-31T23:59:59.999999999Z), persistencePolicy=0, " +
+                "displayPolicy=0)"
         )
     }
 
@@ -2179,7 +2303,10 @@
     public fun noDataComplicationData() {
         assertRoundtrip(
             WireComplicationDataBuilder(WireComplicationData.TYPE_NO_DATA)
-                .setPlaceholder(null).build(),
+                .setPlaceholder(null)
+                .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
+                .build(),
             ComplicationType.NO_DATA
         )
     }
@@ -2207,6 +2334,8 @@
                 .setShortText(WireComplicationText.plainText("text"))
                 .setShortTitle(WireComplicationText.plainText("title"))
                 .setContentDescription(WireComplicationText.plainText("content description"))
+                .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                 .build(),
             ComplicationType.SHORT_TEXT
         )
@@ -2219,6 +2348,8 @@
                 .setLongText(WireComplicationText.plainText("text"))
                 .setLongTitle(WireComplicationText.plainText("title"))
                 .setContentDescription(WireComplicationText.plainText("content description"))
+                .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                 .build(),
             ComplicationType.LONG_TEXT
         )
@@ -2234,6 +2365,8 @@
                 .setRangedMaxValue(100f)
                 .setShortTitle(WireComplicationText.plainText("battery"))
                 .setContentDescription(WireComplicationText.plainText("content description"))
+                .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                 .build(),
             ComplicationType.RANGED_VALUE
         )
@@ -2249,6 +2382,8 @@
                 .setRangedMaxValue(100f)
                 .setShortTitle(WireComplicationText.plainText("battery"))
                 .setContentDescription(WireComplicationText.plainText("content description"))
+                .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                 .build(),
             ComplicationType.RANGED_VALUE
         )
@@ -2265,6 +2400,8 @@
                 .setContentDescription(WireComplicationText.plainText("content description"))
                 .setColorRamp(intArrayOf(Color.RED, Color.GREEN, Color.BLUE))
                 .setColorRampIsSmoothShaded(false)
+                .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                 .build(),
             ComplicationType.GOAL_PROGRESS
         )
@@ -2280,6 +2417,8 @@
                 .setDiscreteRangedMaxValue(100)
                 .setShortTitle(WireComplicationText.plainText("battery"))
                 .setContentDescription(WireComplicationText.plainText("content description"))
+                .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                 .build(),
             ComplicationType.DISCRETE_RANGED_VALUE
         )
@@ -2294,6 +2433,8 @@
                 .setElementColors(intArrayOf(Color.RED, Color.GREEN, Color.BLUE))
                 .setShortTitle(WireComplicationText.plainText("calories"))
                 .setContentDescription(WireComplicationText.plainText("content description"))
+                .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                 .build(),
             ComplicationType.WEIGHTED_ELEMENTS
         )
@@ -2306,6 +2447,8 @@
             WireComplicationDataBuilder(WireComplicationData.TYPE_ICON)
                 .setIcon(icon)
                 .setContentDescription(WireComplicationText.plainText("content description"))
+                .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                 .build(),
             ComplicationType.MONOCHROMATIC_IMAGE
         )
@@ -2319,6 +2462,8 @@
                 .setSmallImage(icon)
                 .setSmallImageStyle(WireComplicationData.IMAGE_STYLE_PHOTO)
                 .setContentDescription(WireComplicationText.plainText("content description"))
+                .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                 .build(),
             ComplicationType.SMALL_IMAGE
         )
@@ -2331,6 +2476,8 @@
             WireComplicationDataBuilder(WireComplicationData.TYPE_LARGE_IMAGE)
                 .setLargeImage(icon)
                 .setContentDescription(WireComplicationText.plainText("content description"))
+                .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                 .build(),
             ComplicationType.PHOTO_IMAGE
         )
@@ -2341,6 +2488,8 @@
         assertRoundtrip(
             WireComplicationDataBuilder(WireComplicationData.TYPE_NO_PERMISSION)
                 .setShortText(WireComplicationText.plainText("needs location"))
+                .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                 .build(),
             ComplicationType.NO_PERMISSION
         )
@@ -2358,6 +2507,8 @@
                 .setAmbientLayout(ambientLayout)
                 .setInteractiveLayout(interactiveLayout)
                 .setLayoutResources(resources)
+                .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                 .build(),
             ComplicationType.PROTO_LAYOUT
         )
@@ -2383,6 +2534,8 @@
                 .setListStyleHint(
                     ListComplicationData.StyleHint.RowOfColumns.ordinal
                 )
+                .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                 .build(),
             ComplicationType.LIST
         )
@@ -2401,8 +2554,12 @@
                         .setShortText(WireComplicationText.plainText("text"))
                         .setShortTitle(WireComplicationText.plainText("title"))
                         .setIcon(icon)
+                        .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                        .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                         .build()
                 )
+                .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                 .build(),
             ComplicationType.NO_DATA
         )
@@ -2421,8 +2578,12 @@
                         .setLongText(WireComplicationText.plainText("text"))
                         .setLongTitle(WireComplicationText.plainText("title"))
                         .setIcon(icon)
+                        .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                        .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                         .build()
                 )
+                .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                 .build(),
             ComplicationType.NO_DATA
         )
@@ -2444,8 +2605,12 @@
                         .setRangedMaxValue(100f)
                         .setShortTitle(WireComplicationText.plainText("battery"))
                         .setIcon(icon)
+                        .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                        .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                         .build()
                 )
+                .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                 .build(),
             ComplicationType.NO_DATA
         )
@@ -2467,8 +2632,12 @@
                         .setRangedMaxValue(100f)
                         .setShortTitle(WireComplicationText.plainText("battery"))
                         .setIcon(icon)
+                        .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                        .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                         .build()
                 )
+                .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                 .build(),
             ComplicationType.NO_DATA
         )
@@ -2491,8 +2660,12 @@
                         .setColorRamp(intArrayOf(Color.RED, Color.GREEN, Color.BLUE))
                         .setColorRampIsSmoothShaded(true)
                         .setIcon(icon)
+                        .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                        .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                         .build()
                 )
+                .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                 .build(),
             ComplicationType.NO_DATA
         )
@@ -2514,8 +2687,12 @@
                         .setDiscreteRangedMaxValue(100)
                         .setShortTitle(WireComplicationText.plainText("battery"))
                         .setIcon(icon)
+                        .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                        .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                         .build()
                 )
+                .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                 .build(),
             ComplicationType.NO_DATA
         )
@@ -2534,8 +2711,13 @@
                         .setContentDescription(
                             WireComplicationText.plainText("content description")
                         )
+                        .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                        .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                         .build()
-                ).build(),
+                )
+                .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
+                .build(),
             ComplicationType.NO_DATA
         )
     }
@@ -2552,8 +2734,12 @@
                         .setContentDescription(
                             WireComplicationText.plainText("content description")
                         )
+                        .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                        .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                         .build()
                 )
+                .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                 .build(),
             ComplicationType.NO_DATA
         )
@@ -2570,8 +2756,12 @@
                         .setContentDescription(
                             WireComplicationText.plainText("content description")
                         )
+                        .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                        .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                         .build()
                 )
+                .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                 .build(),
             ComplicationType.NO_DATA
         )
@@ -2589,8 +2779,12 @@
                         .setAmbientLayout(ambientLayout)
                         .setInteractiveLayout(interactiveLayout)
                         .setLayoutResources(resources)
+                        .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                        .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                         .build()
                 )
+                .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                 .build(),
             ComplicationType.NO_DATA
         )
@@ -2608,6 +2802,8 @@
             .setRangedMinValue(0f)
             .setRangedMaxValue(100f)
             .setShortTitle(WireComplicationText.plainText("battery"))
+            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
             .build()
 
         assertRoundtrip(
@@ -2618,8 +2814,12 @@
                         .setListStyleHint(
                             ListComplicationData.StyleHint.RowOfColumns.ordinal
                         )
+                        .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                        .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                         .build(),
                 )
+                .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                 .build(),
             ComplicationType.NO_DATA
         )
@@ -3003,6 +3203,8 @@
                     .setShortText(WireComplicationText.plainText("text"))
                     .setStartDateTimeMillis(testStartInstant.toEpochMilli())
                     .setEndDateTimeMillis(testEndDateInstant.toEpochMilli())
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
     }
@@ -3018,6 +3220,8 @@
                     .setLongText(WireComplicationText.plainText("text"))
                     .setStartDateTimeMillis(testStartInstant.toEpochMilli())
                     .setEndDateTimeMillis(testEndDateInstant.toEpochMilli())
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
     }
@@ -3041,6 +3245,8 @@
                     .setShortText(WireComplicationText.plainText("battery"))
                     .setStartDateTimeMillis(testStartInstant.toEpochMilli())
                     .setEndDateTimeMillis(testEndDateInstant.toEpochMilli())
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
     }
@@ -3067,6 +3273,8 @@
                     .setColorRampIsSmoothShaded(true)
                     .setStartDateTimeMillis(testStartInstant.toEpochMilli())
                     .setEndDateTimeMillis(testEndDateInstant.toEpochMilli())
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
     }
@@ -3090,6 +3298,8 @@
                     .setShortText(WireComplicationText.plainText("battery"))
                     .setStartDateTimeMillis(testStartInstant.toEpochMilli())
                     .setEndDateTimeMillis(testEndDateInstant.toEpochMilli())
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
     }
@@ -3117,6 +3327,8 @@
                     .setContentDescription(WireComplicationText.plainText("content description"))
                     .setStartDateTimeMillis(testStartInstant.toEpochMilli())
                     .setEndDateTimeMillis(testEndDateInstant.toEpochMilli())
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
     }
@@ -3134,6 +3346,8 @@
                     .setIcon(icon)
                     .setStartDateTimeMillis(testStartInstant.toEpochMilli())
                     .setEndDateTimeMillis(testEndDateInstant.toEpochMilli())
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
     }
@@ -3152,6 +3366,8 @@
                     .setSmallImageStyle(WireComplicationData.IMAGE_STYLE_PHOTO)
                     .setStartDateTimeMillis(testStartInstant.toEpochMilli())
                     .setEndDateTimeMillis(testEndDateInstant.toEpochMilli())
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
     }
@@ -3168,6 +3384,8 @@
                     .setLargeImage(photoImage)
                     .setStartDateTimeMillis(testStartInstant.toEpochMilli())
                     .setEndDateTimeMillis(testEndDateInstant.toEpochMilli())
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
     }
@@ -3196,6 +3414,8 @@
                     .setLayoutResources(resources)
                     .setStartDateTimeMillis(testStartInstant.toEpochMilli())
                     .setEndDateTimeMillis(testEndDateInstant.toEpochMilli())
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
     }
@@ -3227,6 +3447,8 @@
 
         val wireShortText = WireComplicationDataBuilder(WireComplicationData.TYPE_SHORT_TEXT)
             .setShortText(WireComplicationText.plainText("text"))
+            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
             .build()
 
         val wireRangedValue = WireComplicationDataBuilder(WireComplicationData.TYPE_RANGED_VALUE)
@@ -3234,6 +3456,8 @@
             .setRangedMinValue(0f)
             .setRangedMaxValue(100f)
             .setShortText(WireComplicationText.plainText("battery"))
+            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
             .build()
 
         ParcelableSubject.assertThat(data.asWireComplicationData())
@@ -3245,6 +3469,8 @@
                     )
                     .setStartDateTimeMillis(testStartInstant.toEpochMilli())
                     .setEndDateTimeMillis(testEndDateInstant.toEpochMilli())
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
     }
@@ -3261,8 +3487,12 @@
                     .setPlaceholder(
                         WireComplicationDataBuilder(WireComplicationData.TYPE_SHORT_TEXT)
                             .setShortText(WireComplicationText.plainText("text"))
+                            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                             .build()
                     )
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
     }
@@ -3279,8 +3509,12 @@
                     .setPlaceholder(
                         WireComplicationDataBuilder(WireComplicationData.TYPE_LONG_TEXT)
                             .setLongText(WireComplicationText.plainText("text"))
+                            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                             .build()
                     )
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
     }
@@ -3307,8 +3541,12 @@
                             .setRangedMinValue(0f)
                             .setRangedMaxValue(100f)
                             .setShortText(WireComplicationText.plainText("battery"))
+                            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                             .build()
                     )
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
     }
@@ -3338,8 +3576,12 @@
                             )
                             .setColorRamp(intArrayOf(Color.RED, Color.GREEN, Color.BLUE))
                             .setColorRampIsSmoothShaded(false)
+                            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                             .build()
                     )
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
     }
@@ -3366,8 +3608,12 @@
                             .setDiscreteRangedMinValue(0)
                             .setDiscreteRangedMaxValue(100)
                             .setShortText(WireComplicationText.plainText("battery"))
+                            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                             .build()
                     )
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
     }
@@ -3398,8 +3644,12 @@
                             .setContentDescription(
                                 WireComplicationText.plainText("content description")
                             )
+                            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                             .build()
                     )
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
     }
@@ -3417,8 +3667,12 @@
                     .setPlaceholder(
                         WireComplicationDataBuilder(WireComplicationData.TYPE_ICON)
                             .setIcon(icon)
+                            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                             .build()
                     )
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
     }
@@ -3437,8 +3691,12 @@
                         WireComplicationDataBuilder(WireComplicationData.TYPE_SMALL_IMAGE)
                             .setSmallImage(icon)
                             .setSmallImageStyle(WireComplicationData.IMAGE_STYLE_PHOTO)
+                            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                             .build()
                     )
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
     }
@@ -3455,8 +3713,12 @@
                     .setPlaceholder(
                         WireComplicationDataBuilder(WireComplicationData.TYPE_LARGE_IMAGE)
                             .setLargeImage(icon)
+                            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                             .build()
                     )
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
     }
@@ -3484,8 +3746,12 @@
                             .setAmbientLayout(ambientLayout)
                             .setInteractiveLayout(interactiveLayout)
                             .setLayoutResources(resources)
+                            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                             .build()
                     )
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
     }
@@ -3517,6 +3783,8 @@
 
         val wireShortText = WireComplicationDataBuilder(WireComplicationData.TYPE_SHORT_TEXT)
             .setShortText(WireComplicationText.plainText("text"))
+            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
             .build()
 
         val wireRangedValue = WireComplicationDataBuilder(WireComplicationData.TYPE_RANGED_VALUE)
@@ -3524,6 +3792,8 @@
             .setRangedMinValue(0f)
             .setRangedMaxValue(100f)
             .setShortText(WireComplicationText.plainText("battery"))
+            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
             .build()
 
         ParcelableSubject.assertThat(data.asWireComplicationData())
@@ -3535,8 +3805,12 @@
                             .setListStyleHint(
                                 ListComplicationData.StyleHint.RowOfColumns.ordinal
                             )
+                            .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                            .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                             .build()
                     )
+                    .setPersistencePolicy(ComplicationPersistencePolicies.CACHING_ALLOWED)
+                    .setDisplayPolicy(ComplicationDisplayPolicies.ALWAYS_DISPLAY)
                     .build()
             )
     }
@@ -3573,7 +3847,8 @@
                 "mTimeDependentText=null}, monochromaticImage=null, smallImage=null, " +
                 "contentDescription=ComplicationText{mSurroundingText=REDACTED, " +
                 "mTimeDependentText=null}, tapActionLostDueToSerialization=false, tapAction=null," +
-                " validTimeRange=TimeRange(REDACTED), dataSource=null)"
+                " validTimeRange=TimeRange(REDACTED), dataSource=null, persistencePolicy=0, " +
+                "displayPolicy=0)"
         )
         assertThat(data.asWireComplicationData().toString()).isEqualTo(
             "ComplicationData{mType=3, mFields=REDACTED}"
@@ -3595,7 +3870,7 @@
             "monochromaticImage=null, smallImage=null, contentDescription=ComplicationText" +
             "{mSurroundingText=REDACTED, mTimeDependentText=null}), " +
             "tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=TimeRange" +
-            "(REDACTED), dataSource=null)"
+            "(REDACTED), dataSource=null, persistencePolicy=0, displayPolicy=0)"
         )
         assertThat(data.asWireComplicationData().toString()).isEqualTo(
             "ComplicationData{mType=4, mFields=REDACTED}"
@@ -3620,7 +3895,8 @@
             "text=ComplicationText{mSurroundingText=REDACTED, mTimeDependentText=null}, " +
             "contentDescription=ComplicationText{mSurroundingText=REDACTED, " +
             "mTimeDependentText=null}), tapActionLostDueToSerialization=false, " +
-            "tapAction=null, validTimeRange=TimeRange(REDACTED), dataSource=null, colorRamp=null)"
+            "tapAction=null, validTimeRange=TimeRange(REDACTED), dataSource=null, " +
+            "colorRamp=null, persistencePolicy=0, displayPolicy=0)"
         )
         assertThat(data.asWireComplicationData().toString()).isEqualTo(
             "ComplicationData{mType=5, mFields=REDACTED}"
@@ -3645,7 +3921,8 @@
                 "contentDescription=ComplicationText{mSurroundingText=REDACTED, " +
                 "mTimeDependentText=null}), tapActionLostDueToSerialization=false, " +
                 "tapAction=null, validTimeRange=TimeRange(REDACTED), dataSource=null, " +
-                "colorRamp=ColorRamp(colors=[-65536, -16711936, -16776961], interpolated=true))"
+                "colorRamp=ColorRamp(colors=[-65536, -16711936, -16776961], interpolated=true), " +
+                "persistencePolicy=0, displayPolicy=0)"
         )
         assertThat(data.asWireComplicationData().toString()).isEqualTo(
             "ComplicationData{mType=13, mFields=REDACTED}"
@@ -3672,7 +3949,8 @@
             "mSurroundingText=REDACTED, mTimeDependentText=null}, contentDescription=" +
             "ComplicationText{mSurroundingText=REDACTED, mTimeDependentText=null}), " +
             "tapActionLostDueToSerialization=false, tapAction=null, " +
-            "validTimeRange=TimeRange(REDACTED), dataSource=null)"
+            "validTimeRange=TimeRange(REDACTED), dataSource=null, persistencePolicy=0, " +
+            "displayPolicy=0)"
         )
         assertThat(data.asWireComplicationData().toString()).isEqualTo(
             "ComplicationData{mType=14, mFields=REDACTED}"
@@ -3694,8 +3972,9 @@
                 "title=null, monochromaticImage=null, smallImage=null, contentDescription=" +
                 "ComplicationText{mSurroundingText=REDACTED, mTimeDependentText=null}), " +
                 "tapActionLostDueToSerialization=false, tapAction=null, validTimeRange=" +
-                "TimeRange(REDACTED), dataSource=null), tapActionLostDueToSerialization=false, " +
-                "tapAction=null, validTimeRange=TimeRange(REDACTED))"
+                "TimeRange(REDACTED), dataSource=null, persistencePolicy=0, displayPolicy=0), " +
+                "tapActionLostDueToSerialization=false, tapAction=null, " +
+                "validTimeRange=TimeRange(REDACTED), persistencePolicy=0, displayPolicy=0)"
         )
         assertThat(data.asWireComplicationData().toString()).isEqualTo(
             "ComplicationData{mType=10, mFields=REDACTED}"
diff --git a/wear/watchface/watchface-complications-rendering/build.gradle b/wear/watchface/watchface-complications-rendering/build.gradle
index e80cb79..1f3f0cc 100644
--- a/wear/watchface/watchface-complications-rendering/build.gradle
+++ b/wear/watchface/watchface-complications-rendering/build.gradle
@@ -39,7 +39,7 @@
     testImplementation(libs.testRunner)
     testImplementation(libs.testRules)
     testImplementation(libs.robolectric)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.truth)
     testImplementation(libs.junit)
     testImplementation(project(":wear:watchface:watchface"))
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 7ca4cc4..a019e7b 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
@@ -100,6 +100,7 @@
     @Mock
     Drawable.Callback mMockDrawableCallback;
 
+    @SuppressWarnings("deprecation") // b/251211092
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
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 654f80f..72795cd 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
@@ -30,7 +30,7 @@
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -107,6 +107,7 @@
     private OnInvalidateListener mMockInvalidateListener;
     private final Resources mResurces = ApplicationProvider.getApplicationContext().getResources();
 
+    @SuppressWarnings("deprecation") // b/251211092
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -334,7 +335,7 @@
         // AND complication is drawn again
         mComplicationRenderer.draw(mMockCanvas, REFERENCE_TIME, false, false, false, false);
         // THEN nothing is drawn on canvas
-        verifyZeroInteractions(mMockCanvas);
+        verifyNoMoreInteractions(mMockCanvas);
     }
 
     @Test
@@ -351,7 +352,7 @@
         mComplicationRenderer.draw(mMockCanvas, REFERENCE_TIME, false, false, false, false);
 
         // THEN nothing is drawn on canvas
-        verifyZeroInteractions(mMockCanvas);
+        verifyNoMoreInteractions(mMockCanvas);
     }
 
     @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 ca40577..ac2c452 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
@@ -52,6 +52,7 @@
 
     @Mock Canvas mMockCanvas;
 
+    @SuppressWarnings("deprecation") // b/251211092
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/wear/watchface/watchface-complications/build.gradle b/wear/watchface/watchface-complications/build.gradle
index ca48143..6d6f514 100644
--- a/wear/watchface/watchface-complications/build.gradle
+++ b/wear/watchface/watchface-complications/build.gradle
@@ -34,7 +34,7 @@
     testImplementation(libs.testRunner)
     testImplementation(libs.testRules)
     testImplementation(libs.robolectric)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.truth)
     testImplementation(libs.junit)
     testImplementation(libs.kotlinReflect)
diff --git a/wear/watchface/watchface-style/api/current.txt b/wear/watchface/watchface-style/api/current.txt
index 356d6a4..f79c03d 100644
--- a/wear/watchface/watchface-style/api/current.txt
+++ b/wear/watchface/watchface-style/api/current.txt
@@ -130,7 +130,7 @@
 
   public static final class UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay {
     ctor public UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(int complicationSlotId, optional Boolean? enabled, optional androidx.wear.watchface.complications.ComplicationSlotBounds? complicationSlotBounds, optional Integer? accessibilityTraversalIndex, optional Integer? nameResourceId, optional Integer? screenReaderNameResourceId);
-    ctor @Deprecated public UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(int complicationSlotId, optional boolean enabled, optional androidx.wear.watchface.complications.ComplicationSlotBounds? complicationSlotBounds, optional int accessibilityTraversalIndex);
+    ctor @Deprecated public UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(int complicationSlotId, optional Boolean? enabled, optional androidx.wear.watchface.complications.ComplicationSlotBounds? complicationSlotBounds, optional Integer? accessibilityTraversalIndex);
     method public Integer? getAccessibilityTraversalIndex();
     method public androidx.wear.watchface.complications.ComplicationSlotBounds? getComplicationSlotBounds();
     method public int getComplicationSlotId();
diff --git a/wear/watchface/watchface-style/api/public_plus_experimental_current.txt b/wear/watchface/watchface-style/api/public_plus_experimental_current.txt
index 356d6a4..f79c03d 100644
--- a/wear/watchface/watchface-style/api/public_plus_experimental_current.txt
+++ b/wear/watchface/watchface-style/api/public_plus_experimental_current.txt
@@ -130,7 +130,7 @@
 
   public static final class UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay {
     ctor public UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(int complicationSlotId, optional Boolean? enabled, optional androidx.wear.watchface.complications.ComplicationSlotBounds? complicationSlotBounds, optional Integer? accessibilityTraversalIndex, optional Integer? nameResourceId, optional Integer? screenReaderNameResourceId);
-    ctor @Deprecated public UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(int complicationSlotId, optional boolean enabled, optional androidx.wear.watchface.complications.ComplicationSlotBounds? complicationSlotBounds, optional int accessibilityTraversalIndex);
+    ctor @Deprecated public UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(int complicationSlotId, optional Boolean? enabled, optional androidx.wear.watchface.complications.ComplicationSlotBounds? complicationSlotBounds, optional Integer? accessibilityTraversalIndex);
     method public Integer? getAccessibilityTraversalIndex();
     method public androidx.wear.watchface.complications.ComplicationSlotBounds? getComplicationSlotBounds();
     method public int getComplicationSlotId();
diff --git a/wear/watchface/watchface-style/api/restricted_current.txt b/wear/watchface/watchface-style/api/restricted_current.txt
index 356d6a4..f79c03d 100644
--- a/wear/watchface/watchface-style/api/restricted_current.txt
+++ b/wear/watchface/watchface-style/api/restricted_current.txt
@@ -130,7 +130,7 @@
 
   public static final class UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay {
     ctor public UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(int complicationSlotId, optional Boolean? enabled, optional androidx.wear.watchface.complications.ComplicationSlotBounds? complicationSlotBounds, optional Integer? accessibilityTraversalIndex, optional Integer? nameResourceId, optional Integer? screenReaderNameResourceId);
-    ctor @Deprecated public UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(int complicationSlotId, optional boolean enabled, optional androidx.wear.watchface.complications.ComplicationSlotBounds? complicationSlotBounds, optional int accessibilityTraversalIndex);
+    ctor @Deprecated public UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(int complicationSlotId, optional Boolean? enabled, optional androidx.wear.watchface.complications.ComplicationSlotBounds? complicationSlotBounds, optional Integer? accessibilityTraversalIndex);
     method public Integer? getAccessibilityTraversalIndex();
     method public androidx.wear.watchface.complications.ComplicationSlotBounds? getComplicationSlotBounds();
     method public int getComplicationSlotId();
diff --git a/wear/watchface/watchface-style/build.gradle b/wear/watchface/watchface-style/build.gradle
index 36c56da..9d0ba2c 100644
--- a/wear/watchface/watchface-style/build.gradle
+++ b/wear/watchface/watchface-style/build.gradle
@@ -34,7 +34,7 @@
 
     testImplementation(libs.testCore)
     testImplementation(libs.testRules)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.robolectric)
     testImplementation(libs.truth)
     testImplementation(libs.kotlinTest)
diff --git a/wear/watchface/watchface-style/src/androidTest/java/androidx/wear/watchface/style/IconWireSizeAndDimensionsTest.kt b/wear/watchface/watchface-style/src/androidTest/java/androidx/wear/watchface/style/IconWireSizeAndDimensionsTest.kt
index 282d5c9..07829db 100644
--- a/wear/watchface/watchface-style/src/androidTest/java/androidx/wear/watchface/style/IconWireSizeAndDimensionsTest.kt
+++ b/wear/watchface/watchface-style/src/androidTest/java/androidx/wear/watchface/style/IconWireSizeAndDimensionsTest.kt
@@ -188,6 +188,7 @@
     }
 
     @Test
+    @Suppress("Deprecation")
     public fun estimateWireSizeInBytes_ComplicationSlotsUserStyleSetting() {
         val leftComplicationID = 101
         val rightComplicationID = 102
diff --git a/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt b/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt
index fe51f1a..86fad78 100644
--- a/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt
+++ b/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt
@@ -931,11 +931,10 @@
              * optional parameters nameResourceId and screenReaderNameResourceId
              * [ComplicationSlotOverlay(Int, Boolean?, ComplicationSlotBounds?, Int?, Int?, Int?]
              */
-
             @Deprecated(
                 message = "This constructor is deprecated in favour of the one that specifies " +
                     "optional parameters nameResourceId and screenReaderNameResourceId",
-                level = DeprecationLevel.HIDDEN)
+                level = DeprecationLevel.WARNING)
             public constructor(
                 complicationSlotId: Int,
                 @Suppress("AutoBoxing")
diff --git a/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/StyleParcelableTest.kt b/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/StyleParcelableTest.kt
index 7dc6cb4..cd39c27 100644
--- a/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/StyleParcelableTest.kt
+++ b/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/StyleParcelableTest.kt
@@ -543,6 +543,7 @@
     }
 
     @Test
+    @Suppress("Deprecation")
     public fun parcelAndUnparcelComplicationsUserStyleSetting() {
         val leftComplicationID = 101
         val rightComplicationID = 102
diff --git a/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/UserStyleSettingTest.kt b/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/UserStyleSettingTest.kt
index 718ffa9..db8c668 100644
--- a/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/UserStyleSettingTest.kt
+++ b/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/UserStyleSettingTest.kt
@@ -254,6 +254,7 @@
     }
 
     @Test
+    @Suppress("Deprecation")
     public fun noDuplicatedComplicationSlotOptions() {
         val leftComplicationSlot =
             UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay(1)
diff --git a/wear/watchface/watchface/api/current.txt b/wear/watchface/watchface/api/current.txt
index 3c2f5aa..d19bba9 100644
--- a/wear/watchface/watchface/api/current.txt
+++ b/wear/watchface/watchface/api/current.txt
@@ -5,7 +5,7 @@
     ctor public BackgroundComplicationTapFilter();
   }
 
-  public interface CanvasComplication {
+  @kotlin.jvm.JvmDefaultWithCompatibility 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();
     method public void loadData(androidx.wear.watchface.complications.data.ComplicationData complicationData, boolean loadDrawablesAsynchronous);
@@ -111,7 +111,7 @@
   public final class ComplicationSlotsManagerKt {
   }
 
-  public interface ComplicationTapFilter {
+  @kotlin.jvm.JvmDefaultWithCompatibility public interface ComplicationTapFilter {
     method public default boolean hitTest(androidx.wear.watchface.ComplicationSlot complicationSlot, android.graphics.Rect screenBounds, @Px int x, @Px int y, boolean includeMargins);
     method @Deprecated public default boolean hitTest(androidx.wear.watchface.ComplicationSlot complicationSlot, android.graphics.Rect screenBounds, @Px int x, @Px int y);
   }
diff --git a/wear/watchface/watchface/api/public_plus_experimental_current.txt b/wear/watchface/watchface/api/public_plus_experimental_current.txt
index a42fa47..7763240 100644
--- a/wear/watchface/watchface/api/public_plus_experimental_current.txt
+++ b/wear/watchface/watchface/api/public_plus_experimental_current.txt
@@ -16,7 +16,7 @@
     property public final float totalAngle;
   }
 
-  public interface CanvasComplication {
+  @kotlin.jvm.JvmDefaultWithCompatibility 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 @androidx.wear.watchface.complications.data.ComplicationExperimental public default void drawHighlight(android.graphics.Canvas canvas, android.graphics.Rect bounds, int boundsType, java.time.ZonedDateTime zonedDateTime, @ColorInt int color, androidx.wear.watchface.BoundingArc? boundingArc);
     method public androidx.wear.watchface.complications.data.ComplicationData getData();
@@ -127,7 +127,7 @@
   public final class ComplicationSlotsManagerKt {
   }
 
-  public interface ComplicationTapFilter {
+  @kotlin.jvm.JvmDefaultWithCompatibility public interface ComplicationTapFilter {
     method public default boolean hitTest(androidx.wear.watchface.ComplicationSlot complicationSlot, android.graphics.Rect screenBounds, @Px int x, @Px int y, boolean includeMargins);
     method @Deprecated public default boolean hitTest(androidx.wear.watchface.ComplicationSlot complicationSlot, android.graphics.Rect screenBounds, @Px int x, @Px int y);
   }
diff --git a/wear/watchface/watchface/api/restricted_current.txt b/wear/watchface/watchface/api/restricted_current.txt
index 3c2f5aa..d19bba9 100644
--- a/wear/watchface/watchface/api/restricted_current.txt
+++ b/wear/watchface/watchface/api/restricted_current.txt
@@ -5,7 +5,7 @@
     ctor public BackgroundComplicationTapFilter();
   }
 
-  public interface CanvasComplication {
+  @kotlin.jvm.JvmDefaultWithCompatibility 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();
     method public void loadData(androidx.wear.watchface.complications.data.ComplicationData complicationData, boolean loadDrawablesAsynchronous);
@@ -111,7 +111,7 @@
   public final class ComplicationSlotsManagerKt {
   }
 
-  public interface ComplicationTapFilter {
+  @kotlin.jvm.JvmDefaultWithCompatibility public interface ComplicationTapFilter {
     method public default boolean hitTest(androidx.wear.watchface.ComplicationSlot complicationSlot, android.graphics.Rect screenBounds, @Px int x, @Px int y, boolean includeMargins);
     method @Deprecated public default boolean hitTest(androidx.wear.watchface.ComplicationSlot complicationSlot, android.graphics.Rect screenBounds, @Px int x, @Px int y);
   }
diff --git a/wear/watchface/watchface/build.gradle b/wear/watchface/watchface/build.gradle
index f7bcec2..d59f829 100644
--- a/wear/watchface/watchface/build.gradle
+++ b/wear/watchface/watchface/build.gradle
@@ -50,8 +50,8 @@
     testImplementation(libs.testExtJunit)
     testImplementation(libs.testCore)
     testImplementation(libs.testRules)
-    testImplementation(libs.mockitoCore)
-    testImplementation(libs.mockitoKotlin)
+    testImplementation(libs.mockitoCore4)
+    testImplementation(libs.mockitoKotlin4)
     testImplementation(libs.robolectric)
     testImplementation(libs.truth)
     testImplementation(libs.kotlinTest)
@@ -70,6 +70,9 @@
     // Use Robolectric 4.+
     testOptions.unitTests.includeAndroidResources = true
     namespace "androidx.wear.watchface"
+    kotlinOptions {
+        freeCompilerArgs += ["-Xjvm-default=all"]
+    }
 }
 
 androidx {
diff --git a/wear/watchface/watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasAnalogWatchFaceService.kt b/wear/watchface/watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasAnalogWatchFaceService.kt
index 6e93c5c..e22627f 100644
--- a/wear/watchface/watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasAnalogWatchFaceService.kt
+++ b/wear/watchface/watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleCanvasAnalogWatchFaceService.kt
@@ -183,6 +183,7 @@
 
     // These are style overrides applied on top of the complicationSlots passed into
     // complicationSlotsManager below.
+    @Suppress("Deprecation")
     private val complicationsStyleSetting by lazy {
         ComplicationSlotsUserStyleSetting(
             UserStyleSetting.Id(COMPLICATIONS_STYLE_SETTING),
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 a75123a..7db3ef4 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
@@ -41,10 +41,6 @@
 import androidx.test.filters.MediumTest
 import androidx.test.screenshot.AndroidXScreenshotTestRule
 import androidx.test.screenshot.assertAgainstGolden
-import androidx.wear.watchface.complications.SystemDataSources
-import androidx.wear.watchface.complications.data.ComplicationText
-import androidx.wear.watchface.complications.data.PlainComplicationText
-import androidx.wear.watchface.complications.data.ShortTextComplicationData
 import androidx.wear.watchface.ComplicationSlotsManager
 import androidx.wear.watchface.DrawMode
 import androidx.wear.watchface.MutableWatchState
@@ -55,10 +51,14 @@
 import androidx.wear.watchface.WatchFace
 import androidx.wear.watchface.WatchFaceService
 import androidx.wear.watchface.WatchState
+import androidx.wear.watchface.complications.SystemDataSources
+import androidx.wear.watchface.complications.data.ComplicationText
 import androidx.wear.watchface.complications.data.LongTextComplicationData
 import androidx.wear.watchface.complications.data.MonochromaticImage
 import androidx.wear.watchface.complications.data.NoDataComplicationData
+import androidx.wear.watchface.complications.data.PlainComplicationText
 import androidx.wear.watchface.complications.data.RangedValueComplicationData
+import androidx.wear.watchface.complications.data.ShortTextComplicationData
 import androidx.wear.watchface.complications.data.SmallImage
 import androidx.wear.watchface.complications.data.SmallImageType
 import androidx.wear.watchface.control.IInteractiveWatchFace
@@ -79,7 +79,14 @@
 import androidx.wear.watchface.style.WatchFaceLayer
 import androidx.wear.watchface.style.data.UserStyleWireFormat
 import com.google.common.truth.Truth.assertThat
+import java.time.Instant
+import java.time.ZoneId
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.android.asCoroutineDispatcher
+import kotlinx.coroutines.launch
 import kotlinx.coroutines.runBlocking
 import org.junit.After
 import org.junit.Assert.fail
@@ -91,13 +98,6 @@
 import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.MockitoAnnotations
-import java.time.Instant
-import java.time.ZoneId
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.android.asCoroutineDispatcher
-import kotlinx.coroutines.launch
 
 private const val BITMAP_WIDTH = 400
 private const val BITMAP_HEIGHT = 400
@@ -270,6 +270,7 @@
     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)
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 8919290..6368e9d 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
@@ -169,6 +169,7 @@
     private var initLatch = CountDownLatch(1)
     private lateinit var interactiveWatchFaceInstance: IInteractiveWatchFace
 
+    @Suppress("DEPRECATION") // b/251211092
     @Before
     public fun setUp() {
         Assume.assumeTrue("This test suite assumes API 29", Build.VERSION.SDK_INT >= 29)
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/BroadcastsObserver.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/BroadcastsObserver.kt
index 30b857f..baf683c 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/BroadcastsObserver.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/BroadcastsObserver.kt
@@ -16,7 +16,9 @@
 
 package androidx.wear.watchface
 
+import android.app.KeyguardManager
 import android.content.ContentResolver
+import android.content.Context
 import android.content.Intent
 import android.provider.Settings
 import androidx.annotation.RestrictTo
@@ -104,6 +106,12 @@
     }
 
     override fun onActionScreenOff() {
+        val keyguardManager =
+            watchFaceHostApi.getContext().getSystemService(Context.KEYGUARD_SERVICE)
+                as KeyguardManager
+
+        (watchState.isLocked as MutableStateFlow).value = keyguardManager.isDeviceLocked
+
         // Before SysUI has connected, we use ActionScreenOn/ActionScreenOff as a trigger to query
         // AMBIENT_ENABLED_PATH in order to determine if the device os ambient or not.
         if (sysUiHasSentWatchUiState) {
@@ -124,6 +132,8 @@
     }
 
     override fun onActionScreenOn() {
+        (watchState.isLocked as MutableStateFlow).value = false
+
         // Before SysUI has connected, we use ActionScreenOn/ActionScreenOff as a trigger to query
         // AMBIENT_ENABLED_PATH in order to determine if the device os ambient or not.
         if (sysUiHasSentWatchUiState) {
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 18c41fe..d557218 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
@@ -35,6 +35,7 @@
 import androidx.wear.watchface.complications.data.EmptyComplicationData
 import androidx.wear.watchface.complications.data.NoDataComplicationData
 import androidx.wear.watchface.RenderParameters.HighlightedElement
+import androidx.wear.watchface.complications.data.ComplicationDisplayPolicies
 import androidx.wear.watchface.complications.data.ComplicationExperimental
 import androidx.wear.watchface.complications.data.toApiComplicationData
 import androidx.wear.watchface.data.BoundingArcWireFormat
@@ -59,6 +60,7 @@
  * [CanvasComplicationFactory.create]. If state needs to be shared with the [Renderer] that should
  * be set up inside [onRendererCreated].
  */
+@JvmDefaultWithCompatibility
 public interface CanvasComplication {
 
     /** Interface for observing when a [CanvasComplication] needs the screen to be redrawn. */
@@ -156,6 +158,7 @@
 }
 
 /** Interface for determining whether a tap hits a complication. */
+@JvmDefaultWithCompatibility
 public interface ComplicationTapFilter {
     /**
      * Performs a hit test, returning `true` if the supplied coordinates in pixels are within the
@@ -481,6 +484,8 @@
 
         internal val unitSquare = RectF(0f, 0f, 1f, 1f)
 
+        internal val screenLockedFallback = NoDataComplicationData()
+
         /**
          * Constructs a [Builder] for a complication with bounds type
          * [ComplicationSlotBoundsType.ROUND_RECT]. This is the most common type of complication. These
@@ -1025,6 +1030,15 @@
             }
         }
 
+        // If the screen is locked and our policy is to not display it when locked then select
+        // screenLockedFallback instead.
+        if ((best.displayPolicy and
+                ComplicationDisplayPolicies.DO_NOT_SHOW_WHEN_DEVICE_LOCKED) != 0 &&
+            complicationSlotsManager.watchState.isLocked.value
+        ) {
+            best = screenLockedFallback // This is NoDataComplicationData.
+        }
+
         if (forceUpdate || complicationData.value != best) {
             renderer.loadData(best, loadDrawablesAsynchronous)
             (complicationData as MutableStateFlow).value = best
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 7df9bc3..3916ab2 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
@@ -16,6 +16,7 @@
 
 package androidx.wear.watchface
 
+import android.app.KeyguardManager
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
@@ -54,9 +55,11 @@
 import androidx.annotation.WorkerThread
 import androidx.versionedparcelable.ParcelUtils
 import androidx.wear.watchface.complications.SystemDataSources.DataSourceId
+import androidx.wear.watchface.complications.data.ComplicationPersistencePolicies
 import androidx.wear.watchface.complications.data.ComplicationData
 import androidx.wear.watchface.complications.data.ComplicationExperimental
 import androidx.wear.watchface.complications.data.ComplicationType
+import androidx.wear.watchface.complications.data.NoDataComplicationData
 import androidx.wear.watchface.complications.data.toApiComplicationData
 import androidx.wear.watchface.complications.data.toWireTypes
 import androidx.wear.watchface.control.HeadlessWatchFaceImpl
@@ -334,6 +337,7 @@
         /** The maximum reasonable wire size for an Icon in a [UserStyleSchema] in pixels. */
         @Px
         internal const val MAX_REASONABLE_SCHEMA_ICON_WIDTH = 400
+
         @Px
         internal const val MAX_REASONABLE_SCHEMA_ICON_HEIGHT = 400
 
@@ -806,7 +810,13 @@
             for (slot in complicationSlotsManager.complicationSlots) {
                 objectOutputStream.writeInt(slot.key)
                 objectOutputStream.writeObject(
-                    slot.value.complicationData.value.asWireComplicationData()
+                    if ((slot.value.complicationData.value.persistencePolicy and
+                            ComplicationPersistencePolicies.DO_NOT_PERSIST) != 0
+                    ) {
+                        NoDataComplicationData().asWireComplicationData()
+                    } else {
+                        slot.value.complicationData.value.asWireComplicationData()
+                    }
                 )
             }
             objectOutputStream.close()
@@ -1167,6 +1177,8 @@
             // assume we're not in ambient mode which should be correct most of the time.
             isAmbient.value = false
             isHeadless = headless
+            isLocked.value =
+                (getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager).isDeviceLocked
         }
 
         /**
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchState.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchState.kt
index c4c1133..4068678 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchState.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchState.kt
@@ -79,6 +79,38 @@
     public val isHeadless: Boolean,
     public val watchFaceInstanceId: StateFlow<String>
 ) {
+    /** Whether the device is locked or not. */
+    internal var isLocked: StateFlow<Boolean> = MutableStateFlow(false)
+
+    internal constructor(
+        interruptionFilter: StateFlow<Int?>,
+        isAmbient: StateFlow<Boolean?>,
+        isBatteryLowAndNotCharging: StateFlow<Boolean?>,
+        isVisible: StateFlow<Boolean?>,
+        hasLowBitAmbient: Boolean,
+        hasBurnInProtection: Boolean,
+        analogPreviewReferenceTimeMillis: Long,
+        digitalPreviewReferenceTimeMillis: Long,
+        @Px chinHeight: Int,
+        isHeadless: Boolean,
+        watchFaceInstanceId: StateFlow<String>,
+        isLocked: StateFlow<Boolean>
+    ) : this(
+        interruptionFilter,
+        isAmbient,
+        isBatteryLowAndNotCharging,
+        isVisible,
+        hasLowBitAmbient,
+        hasBurnInProtection,
+        analogPreviewReferenceTimeMillis,
+        digitalPreviewReferenceTimeMillis,
+        chinHeight,
+        isHeadless,
+        watchFaceInstanceId
+    ) {
+        this.isLocked = isLocked
+    }
+
     @Deprecated("WatchState constructors without watchFaceInstanceId are deprecated")
     constructor(
         interruptionFilter: StateFlow<Int?>,
@@ -102,7 +134,8 @@
         digitalPreviewReferenceTimeMillis,
         chinHeight,
         isHeadless,
-        watchFaceInstanceId = MutableStateFlow(DEFAULT_INSTANCE_ID)
+        watchFaceInstanceId = MutableStateFlow(DEFAULT_INSTANCE_ID),
+        MutableStateFlow(false)
     )
 
     @UiThread
@@ -126,7 +159,7 @@
 
 /** @hide */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class MutableWatchState {
+public class MutableWatchState() {
     public val interruptionFilter: MutableStateFlow<Int> = MutableStateFlow(
         NotificationManager.INTERRUPTION_FILTER_UNKNOWN
     )
@@ -138,6 +171,7 @@
     public var analogPreviewReferenceTimeMillis: Long = 0
     public var digitalPreviewReferenceTimeMillis: Long = 0
     public val watchFaceInstanceId: MutableStateFlow<String> = MutableStateFlow(DEFAULT_INSTANCE_ID)
+    public val isLocked: MutableStateFlow<Boolean> = MutableStateFlow(false)
 
     @Px
     public var chinHeight: Int = 0
@@ -158,6 +192,7 @@
         digitalPreviewReferenceTimeMillis = digitalPreviewReferenceTimeMillis,
         chinHeight = chinHeight,
         isHeadless = isHeadless,
-        watchFaceInstanceId = watchFaceInstanceId
+        watchFaceInstanceId = watchFaceInstanceId,
+        isLocked = isLocked
     )
 }
diff --git a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/AsyncWatchFaceInitTest.kt b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/AsyncWatchFaceInitTest.kt
index d3ef0be7..d727951 100644
--- a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/AsyncWatchFaceInitTest.kt
+++ b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/AsyncWatchFaceInitTest.kt
@@ -35,7 +35,7 @@
 import androidx.wear.watchface.style.CurrentUserStyleRepository
 import androidx.wear.watchface.style.UserStyleSchema
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.mock
+import org.mockito.kotlin.mock
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.Deferred
 import kotlinx.coroutines.launch
diff --git a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/JavaCompatTest.java b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/JavaCompatTest.java
new file mode 100644
index 0000000..32c6b89
--- /dev/null
+++ b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/JavaCompatTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.wear.watchface;
+
+import android.graphics.Canvas;
+import android.graphics.Rect;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Px;
+import androidx.wear.watchface.complications.data.ComplicationData;
+
+import java.time.ZonedDateTime;
+
+/** Tests that Java interfaces implementing kotlin interfaces with defaults compile. */
+public class JavaCompatTest {
+    class ComplicationTapFilterImpl implements ComplicationTapFilter {
+        @SuppressWarnings("deprecation")
+        public boolean hitTest(@NonNull ComplicationSlot complicationSlot,
+                @NonNull Rect screenBounds, @Px int x, @Px int y) {
+            return true;
+        }
+    }
+
+    class CanvasComplicationImpl implements CanvasComplication {
+        @Override
+        public void onRendererCreated(@NonNull Renderer renderer) {
+            CanvasComplication.super.onRendererCreated(renderer);
+        }
+
+        @Override
+        public void render(@NonNull Canvas canvas, @NonNull Rect bounds,
+                @NonNull ZonedDateTime zonedDateTime, @NonNull RenderParameters renderParameters,
+                int slotId) {
+        }
+
+        @Override
+        public void drawHighlight(@NonNull Canvas canvas, @NonNull Rect bounds, int boundsType,
+                @NonNull ZonedDateTime zonedDateTime, int color) {
+        }
+
+        @NonNull
+        @Override
+        public ComplicationData getData() {
+            return null;
+        }
+
+        @Override
+        public void loadData(@NonNull ComplicationData complicationData,
+                boolean loadDrawablesAsynchronous) {
+
+        }
+    }
+}
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 024b835..417efe4 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
@@ -54,6 +54,8 @@
 import androidx.wear.watchface.complications.ComplicationSlotBounds
 import androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy
 import androidx.wear.watchface.complications.SystemDataSources
+import androidx.wear.watchface.complications.data.ComplicationPersistencePolicies
+import androidx.wear.watchface.complications.data.ComplicationDisplayPolicies
 import androidx.wear.watchface.complications.data.ComplicationExperimental
 import androidx.wear.watchface.complications.data.ComplicationType
 import androidx.wear.watchface.complications.data.CountUpTimeReference
@@ -90,9 +92,9 @@
 import androidx.wear.watchface.style.WatchFaceLayer
 import androidx.wear.watchface.style.data.UserStyleWireFormat
 import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.eq
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verifyNoMoreInteractions
 import java.io.StringWriter
 import java.nio.ByteBuffer
 import java.time.Instant
@@ -2882,6 +2884,128 @@
     }
 
     @Test
+    public fun complicationCachePolicy() {
+        val complicationCache = HashMap<String, ByteArray>()
+        val instanceParams = WallpaperInteractiveWatchFaceInstanceParams(
+            INTERACTIVE_INSTANCE_ID,
+            DeviceConfig(false, false, 0, 0),
+            WatchUiState(false, 0),
+            UserStyle(emptyMap()).toWireFormat(),
+            null
+        )
+        initWallpaperInteractiveWatchFaceInstance(
+            WatchFaceType.ANALOG,
+            listOf(leftComplication, rightComplication),
+            UserStyleSchema(emptyList()),
+            instanceParams,
+            complicationCache = complicationCache
+        )
+
+        // Set some ComplicationData. The TapAction can't be serialized.
+        interactiveWatchFaceInstance.updateComplicationData(
+            listOf(
+                IdAndComplicationDataWireFormat(
+                    LEFT_COMPLICATION_ID,
+                    ComplicationData.Builder(ComplicationData.TYPE_LONG_TEXT)
+                        .setLongText(ComplicationText.plainText("TYPE_LONG_TEXT"))
+                        .setTapAction(
+                            PendingIntent.getActivity(
+                                context, 0, Intent("LongText"),
+                                PendingIntent.FLAG_IMMUTABLE
+                            )
+                        )
+                        .setPersistencePolicy(ComplicationPersistencePolicies.DO_NOT_PERSIST)
+                        .build()
+                ),
+                IdAndComplicationDataWireFormat(
+                    RIGHT_COMPLICATION_ID,
+                    ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
+                        .setShortText(ComplicationText.plainText("TYPE_SHORT_TEXT")).build()
+                )
+            )
+        )
+
+        // Complication cache writes are deferred for 1s to try and batch up multiple updates.
+        runPostedTasksFor(1000)
+        assertThat(complicationCache.size).isEqualTo(1)
+        assertThat(complicationCache).containsKey(INTERACTIVE_INSTANCE_ID)
+
+        engineWrapper.onDestroy()
+        InteractiveInstanceManager.releaseInstance(INTERACTIVE_INSTANCE_ID)
+
+        val service2 = TestWatchFaceService(
+            WatchFaceType.ANALOG,
+            listOf(leftComplication, rightComplication),
+            { _, currentUserStyleRepository, watchState ->
+                TestRenderer(
+                    surfaceHolder,
+                    currentUserStyleRepository,
+                    watchState,
+                    INTERACTIVE_UPDATE_RATE_MS
+                )
+            },
+            UserStyleSchema(emptyList()),
+            watchState,
+            handler,
+            null,
+            true,
+            null,
+            choreographer,
+            complicationCache = complicationCache
+        )
+
+        lateinit var instance2: IInteractiveWatchFace
+        InteractiveInstanceManager
+            .getExistingInstanceOrSetPendingWallpaperInteractiveWatchFaceInstance(
+                InteractiveInstanceManager.PendingWallpaperInteractiveWatchFaceInstance(
+                    instanceParams,
+                    object : IPendingInteractiveWatchFace.Stub() {
+                        override fun getApiVersion() =
+                            IPendingInteractiveWatchFace.API_VERSION
+
+                        override fun onInteractiveWatchFaceCreated(
+                            iInteractiveWatchFace: IInteractiveWatchFace
+                        ) {
+                            instance2 = iInteractiveWatchFace
+                        }
+
+                        override fun onInteractiveWatchFaceCrashed(exception: CrashInfoParcel?) {
+                            fail("WatchFace crashed: $exception")
+                        }
+                    }
+                )
+            )
+
+        val engineWrapper2 = service2.onCreateEngine() as WatchFaceService.EngineWrapper
+        engineWrapper2.onCreate(surfaceHolder)
+        engineWrapper2.onSurfaceChanged(surfaceHolder, 0, 100, 100)
+
+        // [WatchFaceService.createWatchFace] Will have run by now because we're using an immediate
+        // coroutine dispatcher.
+        runBlocking {
+            val watchFaceImpl2 = engineWrapper2.deferredWatchFaceImpl.await()
+
+            // Check only the right ComplicationData was cached.
+            val leftComplicationData =
+                watchFaceImpl2.complicationSlotsManager[
+                    LEFT_COMPLICATION_ID
+                ]!!.complicationData.value.asWireComplicationData()
+            val rightComplicationData =
+                watchFaceImpl2.complicationSlotsManager[
+                    RIGHT_COMPLICATION_ID
+                ]!!.complicationData.value.asWireComplicationData()
+
+            assertThat(leftComplicationData.type).isEqualTo(ComplicationData.TYPE_NO_DATA)
+            assertThat(rightComplicationData.type).isEqualTo(ComplicationData.TYPE_SHORT_TEXT)
+            assertThat(rightComplicationData.shortText?.getTextAt(context.resources, 0))
+                .isEqualTo("TYPE_SHORT_TEXT")
+            assertThat(rightComplicationData.tapActionLostDueToSerialization).isFalse()
+        }
+
+        instance2.release()
+    }
+
+    @Test
     public fun complicationCache_timeline() {
         val complicationCache = HashMap<String, ByteArray>()
         val instanceParams = WallpaperInteractiveWatchFaceInstanceParams(
@@ -5116,6 +5240,7 @@
     }
 
     @Test
+    @Suppress("Deprecation")
     public fun applyComplicationSlotsStyleCategoryOption() {
         initWallpaperInteractiveWatchFaceInstance(
             WatchFaceType.ANALOG,
@@ -6020,6 +6145,58 @@
         engineWrapper.onDestroy()
     }
 
+    @Test
+    public fun doNotDisplayComplicationWhenScreenLocked() {
+        initWallpaperInteractiveWatchFaceInstance(
+            WatchFaceType.ANALOG,
+            listOf(leftComplication),
+            UserStyleSchema(emptyList()),
+            WallpaperInteractiveWatchFaceInstanceParams(
+                INTERACTIVE_INSTANCE_ID,
+                DeviceConfig(
+                    false,
+                    false,
+                    0,
+                    0
+                ),
+                WatchUiState(false, 0),
+                UserStyle(emptyMap()).toWireFormat(),
+                null
+            )
+        )
+
+        engineWrapper.setComplicationDataList(
+            listOf(
+                IdAndComplicationDataWireFormat(
+                    LEFT_COMPLICATION_ID,
+                    ShortTextComplicationData.Builder(
+                        TimeDifferenceComplicationText.Builder(
+                            TimeDifferenceStyle.STOPWATCH,
+                            CountUpTimeReference(Instant.parse("2022-10-30T10:15:30.001Z"))
+                        ).setMinimumTimeUnit(TimeUnit.MINUTES).build(),
+                        androidx.wear.watchface.complications.data.ComplicationText.EMPTY
+                    )
+                        .setDisplayPolicy(
+                            ComplicationDisplayPolicies.DO_NOT_SHOW_WHEN_DEVICE_LOCKED
+                        )
+                        .build()
+                        .asWireComplicationData()
+                )
+            )
+        )
+
+        watchState.isLocked.value = true
+        // Note selectComplicationDataForInstant is called before rendering.
+        complicationSlotsManager.selectComplicationDataForInstant(Instant.EPOCH)
+        assertThat(complicationSlotsManager[LEFT_COMPLICATION_ID]!!.complicationData.value)
+            .isInstanceOf(NoDataComplicationData::class.java)
+
+        watchState.isLocked.value = false
+        complicationSlotsManager.selectComplicationDataForInstant(Instant.EPOCH)
+        assertThat(complicationSlotsManager[LEFT_COMPLICATION_ID]!!.complicationData.value)
+            .isInstanceOf(ShortTextComplicationData::class.java)
+    }
+
     private fun getLeftShortTextComplicationDataText(): CharSequence {
         val complication = complicationSlotsManager[
             LEFT_COMPLICATION_ID
diff --git a/wear/wear-input/build.gradle b/wear/wear-input/build.gradle
index d55712d..3338f9b 100644
--- a/wear/wear-input/build.gradle
+++ b/wear/wear-input/build.gradle
@@ -31,7 +31,7 @@
     testImplementation(libs.testRunner)
     testImplementation(libs.testRules)
     testImplementation(libs.robolectric)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
     testImplementation(project(":wear:wear-input-testing"))
 
     compileOnly(fileTree(dir: "../wear_stubs", include: ["com.google.android.wearable-stubs.jar"]))
diff --git a/wear/wear-phone-interactions/build.gradle b/wear/wear-phone-interactions/build.gradle
index 8bfb4bc..70b1fa7 100644
--- a/wear/wear-phone-interactions/build.gradle
+++ b/wear/wear-phone-interactions/build.gradle
@@ -34,8 +34,7 @@
     testImplementation(libs.testRunner)
     testImplementation(libs.testRules)
     testImplementation(libs.robolectric)
-    testImplementation(libs.mockitoCore)
-    testImplementation(libs.mockitoKotlin)
+    testImplementation(libs.mockitoCore4)
     testImplementation(libs.truth)
 }
 
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 093ca7c..4afca04 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
@@ -47,6 +47,7 @@
     var mockContentProvider: ContentProvider? = null
     private var contentResolver: ContentResolver? = null
 
+    @Suppress("DEPRECATION") // b/251211092
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
diff --git a/wear/wear-remote-interactions/build.gradle b/wear/wear-remote-interactions/build.gradle
index 6b92f7e..d7fb72e 100644
--- a/wear/wear-remote-interactions/build.gradle
+++ b/wear/wear-remote-interactions/build.gradle
@@ -41,8 +41,8 @@
     testImplementation(libs.testRules)
     testImplementation(libs.testRunner)
     testImplementation(libs.robolectric)
-    testImplementation(libs.mockitoCore)
-    testImplementation(libs.mockitoKotlin)
+    testImplementation(libs.mockitoCore4)
+    testImplementation(libs.mockitoKotlin4)
 
     implementation("androidx.annotation:annotation:1.2.0")
     implementation(libs.playServicesBasement)
diff --git a/wear/wear-remote-interactions/src/test/java/androidx/wear/remote/interactions/RemoteActivityHelperTest.kt b/wear/wear-remote-interactions/src/test/java/androidx/wear/remote/interactions/RemoteActivityHelperTest.kt
index 944bf9a..b5c09d4 100644
--- a/wear/wear-remote-interactions/src/test/java/androidx/wear/remote/interactions/RemoteActivityHelperTest.kt
+++ b/wear/wear-remote-interactions/src/test/java/androidx/wear/remote/interactions/RemoteActivityHelperTest.kt
@@ -36,7 +36,7 @@
 import com.google.android.gms.tasks.Tasks
 import com.google.android.gms.wearable.Node
 import com.google.android.gms.wearable.NodeClient
-import com.nhaarman.mockitokotlin2.mock
+import org.mockito.kotlin.mock
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertThrows
 import org.junit.Assert.assertTrue
diff --git a/wear/wear/build.gradle b/wear/wear/build.gradle
index 401ee9b..f055c6f 100644
--- a/wear/wear/build.gradle
+++ b/wear/wear/build.gradle
@@ -31,8 +31,7 @@
     testImplementation(libs.robolectric)
     testImplementation(libs.testExtJunit)
     testImplementation(libs.testRules)
-    testImplementation(libs.mockitoCore)
-    testImplementation(libs.mockitoKotlin)
+    testImplementation(libs.mockitoCore4)
 
     implementation("androidx.core:core-ktx:1.6.0")
 
diff --git a/wear/wear/src/test/java/androidx/wear/ambient/AmbientDelegateTest.java b/wear/wear/src/test/java/androidx/wear/ambient/AmbientDelegateTest.java
index 1ca8721..ec8ade8 100644
--- a/wear/wear/src/test/java/androidx/wear/ambient/AmbientDelegateTest.java
+++ b/wear/wear/src/test/java/androidx/wear/ambient/AmbientDelegateTest.java
@@ -18,7 +18,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import androidx.fragment.app.FragmentActivity;
@@ -66,7 +66,7 @@
     public void testNullActivity() {
         mAmbientDelegateUnderTest = new AmbientDelegate(null,
                 mMockWearableControllerProvider, mMockAmbientCallback);
-        verifyZeroInteractions(mMockWearableControllerProvider);
+        verifyNoMoreInteractions(mMockWearableControllerProvider);
 
         assertFalse(mAmbientDelegateUnderTest.isAmbient());
 
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 cf0c654..f3a455a 100644
--- a/wear/wear/src/test/java/androidx/wear/utils/WearTypeHelperTest.java
+++ b/wear/wear/src/test/java/androidx/wear/utils/WearTypeHelperTest.java
@@ -38,6 +38,7 @@
     private ShadowPackageManager mShadowPackageManager = null;
     private Context mContext;
 
+    @SuppressWarnings("deprecation") // b/251211092
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
diff --git a/webkit/integration-tests/testapp/src/main/AndroidManifest.xml b/webkit/integration-tests/testapp/src/main/AndroidManifest.xml
index de2c95e..74e90b23 100644
--- a/webkit/integration-tests/testapp/src/main/AndroidManifest.xml
+++ b/webkit/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -106,6 +106,9 @@
             android:name=".JsJavaInteractionActivity"
             android:exported="true" />
         <activity
+            android:name=".WebMessageCompatActivity"
+            android:exported="true" />
+        <activity
             android:name=".WebMessageListenerActivity"
             android:exported="true" />
         <activity
@@ -125,5 +128,7 @@
         <activity
             android:name=".RequestedWithHeaderActivity"
             android:exported="true" />
+        <activity android:name=".CookieManagerActivity"
+            android:exported="true" />
     </application>
 </manifest>
diff --git a/webkit/integration-tests/testapp/src/main/assets/www/web_message_compat.html b/webkit/integration-tests/testapp/src/main/assets/www/web_message_compat.html
new file mode 100644
index 0000000..2f15318
--- /dev/null
+++ b/webkit/integration-tests/testapp/src/main/assets/www/web_message_compat.html
@@ -0,0 +1,63 @@
+<html>
+<!-- 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.
+-->
+<head>
+  <script type="text/javascript">
+    let timestamp = 0;
+    let messagePort;
+
+    function onNativeMessage(event) {
+      let i = 0;
+      const data = event.data;
+      if (typeof data === 'string') {
+        i = parseInt(event.data);
+        messagePort.postMessage(i.toString());
+      } else if (data instanceof ArrayBuffer) {
+        i = new DataView(event.data).getInt32(0, false);
+        const buffer = new ArrayBuffer(4);
+        new DataView(buffer).setInt32(0, i, false);
+        messagePort.postMessage(buffer, [buffer]);
+      } else {
+        // Invalid type
+        console.error(`Invalid type: ${typeof data}`);
+      }
+      if (i % 100 === 0) {
+        const counterInfo = `${i} messages sent.`;
+        document.getElementById("result").innerHTML = counterInfo;
+      }
+    }
+
+    window.onmessage = (e) => {
+      if (e.ports[0]) {
+        messagePort = e.ports[0];
+        messagePort.onmessage = onNativeMessage;
+        document.getElementById("port").style = "";
+      } else {
+        onNativeMessage(e);
+      }
+    }
+  </script>
+</head>
+<body>
+  <h1>Web content (within WebView)</h1>
+  <div>
+    <p id="port" style="display: none;">MessagePort set</p>
+  </div>
+  <div>
+    <span>Message from app: </span>
+    <span id="result" style="color:red">Not received.</span>
+  </div>
+</body>
+</html>
diff --git a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/CookieManagerActivity.java b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/CookieManagerActivity.java
new file mode 100644
index 0000000..dc503b0
--- /dev/null
+++ b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/CookieManagerActivity.java
@@ -0,0 +1,87 @@
+/*
+ * 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 com.example.androidx.webkit;
+
+import android.os.Bundle;
+import android.webkit.CookieManager;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.webkit.CookieManagerCompat;
+import androidx.webkit.WebViewFeature;
+
+import java.util.List;
+
+/**
+ * An {@link android.app.Activity} to demonstrate {@link CookieManagerCompat#getCookieInfo}.
+ */
+public class CookieManagerActivity extends AppCompatActivity {
+    private static final String MAIN_PAGE_URL = "https://developer.android"
+            + ".com/reference/androidx/webkit/package-summary";
+    private static final String COOKIE_URL = "https://developer.android.com";
+    private static final String COOKIE_NAME = "signin=";
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_get_cookie_info);
+        setTitle(R.string.cookie_manager_activity_title);
+        WebkitHelpers.appendWebViewVersionToTitle(this);
+
+        if (!WebViewFeature.isFeatureSupported(WebViewFeature.GET_COOKIE_INFO)) {
+            WebkitHelpers.showMessageInActivity(CookieManagerActivity.this,
+                    R.string.cookie_manager_get_cookie_info_not_supported);
+            return;
+        }
+
+        WebView webView = findViewById(R.id.webView);
+        TextView oldApiText = findViewById(R.id.textViewTop);
+        TextView newApiText = findViewById(R.id.textViewBottom);
+
+        WebViewClient client = new WebViewClient() {
+            @Override
+            public void onPageFinished(WebView view, String url) {
+                super.onPageFinished(view, url);
+                String oldCookie = CookieManager.getInstance().getCookie(COOKIE_URL);
+                if (oldCookie != null) {
+                    String[] oldCookies = oldCookie.split("; ");
+                    for (String cookie : oldCookies) {
+                        if (cookie.startsWith(COOKIE_NAME)) {
+                            oldApiText.append(cookie);
+                            break;
+                        }
+                    }
+                }
+
+                List<String> cookies = CookieManagerCompat.getCookieInfo(
+                        CookieManager.getInstance(), COOKIE_URL);
+                for (String cookie : cookies) {
+                    if (cookie.startsWith(COOKIE_NAME)) {
+                        newApiText.append(cookie);
+                        break;
+                    }
+                }
+            }
+        };
+
+        webView.setWebViewClient(client);
+        webView.loadUrl(MAIN_PAGE_URL);
+    }
+}
diff --git a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/JsJavaInteractionActivity.java b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/JsJavaInteractionActivity.java
index c1fb39e..e7841a5 100644
--- a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/JsJavaInteractionActivity.java
+++ b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/JsJavaInteractionActivity.java
@@ -50,6 +50,9 @@
                 new MenuListView.MenuItem(
                         getResources().getString(R.string.document_start_javascript_activity_title),
                         new Intent(activityContext, DocumentStartJavaScriptActivity.class)),
+                new MenuListView.MenuItem(
+                        getResources().getString(R.string.web_message_compat_activity_title),
+                        new Intent(activityContext, WebMessageCompatActivity.class)),
         };
         listView.setItems(menuItems);
     }
diff --git a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/MainActivity.java b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/MainActivity.java
index f79d558..b5525525 100644
--- a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/MainActivity.java
+++ b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/MainActivity.java
@@ -81,7 +81,9 @@
                 new MenuListView.MenuItem(
                         getResources().getString(R.string.requested_with_activity_title),
                         new Intent(activityContext, RequestedWithHeaderActivity.class)),
-
+                new MenuListView.MenuItem(
+                        getResources().getString(R.string.cookie_manager_activity_title),
+                        new Intent(activityContext, CookieManagerActivity.class)),
         };
         listView.setItems(menuItems);
     }
diff --git a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/WebMessageCompatActivity.java b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/WebMessageCompatActivity.java
new file mode 100644
index 0000000..9d15a52
--- /dev/null
+++ b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/WebMessageCompatActivity.java
@@ -0,0 +1,192 @@
+/*
+ * 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 com.example.androidx.webkit;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.style.AbsoluteSizeSpan;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.webkit.WebMessageCompat;
+import androidx.webkit.WebMessagePortCompat;
+import androidx.webkit.WebViewCompat;
+import androidx.webkit.WebViewFeature;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.ByteStreams;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+/**
+ * An {@link Activity} to exercise WebMessageCompat related functionality.
+ */
+public class WebMessageCompatActivity extends AppCompatActivity {
+    private static final String TYPE_STRING = "String";
+    private static final String TYPE_ARRAY_BUFFER = "ArrayBuffer";
+    private static final String[] MESSAGE_TYPES = {TYPE_STRING, TYPE_ARRAY_BUFFER};
+    private static final boolean PAYLOAD_FEATURE_ENABLED =
+            WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_GET_MESSAGE_PAYLOAD);
+
+    private WebView mWebView;
+    private TextView mTextView, mPerfTextView;
+    private CheckBox mCheckBox;
+    private WebMessagePortCompat mPort;
+    private Spinner mSpinner;
+    private int mMessageCount = 0;
+    private int mExpectedCount = 0;
+    private long mTimeStamp;
+
+    static CharSequence createNativeTitle() {
+        final String title = "Native View\n";
+        SpannableString ss = new SpannableString(title);
+        ss.setSpan(new AbsoluteSizeSpan(55, true), 0, title.length() - 1,
+                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        return ss;
+    }
+
+    @SuppressLint("SetJavascriptEnabled")
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_web_message_compat);
+        setTitle(R.string.web_message_compat_activity_title);
+        WebkitHelpers.appendWebViewVersionToTitle(this);
+        if (!WebViewFeature.isFeatureSupported(WebViewFeature.POST_WEB_MESSAGE)) {
+            WebkitHelpers.showMessageInActivity(WebMessageCompatActivity.this,
+                    R.string.webkit_api_not_available);
+            return;
+        }
+
+        mWebView = findViewById(R.id.webview);
+        mWebView.getSettings().setJavaScriptEnabled(true);
+        mWebView.setWebViewClient(new MyWebViewClient());
+        mTextView = findViewById(R.id.textview);
+        mPerfTextView = findViewById(R.id.textview_perf);
+        mCheckBox = findViewById(R.id.checkbox_window_message);
+        mSpinner = findViewById(R.id.message_type_spinner);
+        final ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
+                android.R.layout.simple_spinner_dropdown_item, MESSAGE_TYPES);
+        mSpinner.setAdapter(adapter);
+        // If GET_PAYLOAD feature is not supported, disable the type selection spinner.
+        mSpinner.setEnabled(PAYLOAD_FEATURE_ENABLED);
+
+        try (InputStream is = getAssets().open("www/web_message_compat.html")) {
+            String webContent = new String(ByteStreams.toByteArray(is), Charsets.UTF_8);
+            mWebView.loadDataWithBaseURL("https://example.com", webContent, "text/html", null,
+                    null);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        findViewById(R.id.button_send).setOnClickListener(view -> {
+            mExpectedCount = mMessageCount + 5000;
+            mTimeStamp = System.currentTimeMillis();
+            mSpinner.setEnabled(false);
+            sendMessage();
+        });
+    }
+
+    private void sendMessage() {
+        if (mMessageCount >= mExpectedCount) {
+            return;
+        }
+        final String selectedType = (String) mSpinner.getSelectedItem();
+        final WebMessageCompat message;
+        switch (selectedType) {
+            case TYPE_STRING:
+                message = new WebMessageCompat(String.valueOf(mMessageCount + 1));
+                break;
+            case TYPE_ARRAY_BUFFER:
+                byte[] bytes =
+                        ByteBuffer.allocate(Integer.BYTES).putInt(mMessageCount + 1).array();
+                message = new WebMessageCompat(bytes);
+                break;
+            default:
+                // Should never happen.
+                throw new RuntimeException("Invalid type.");
+        }
+        if (mCheckBox.isChecked()) {
+            WebViewCompat.postWebMessage(mWebView, message, Uri.EMPTY);
+        } else {
+            mPort.postMessage(message);
+        }
+    }
+
+    private void refreshNativeText() {
+        mTextView.setText(TextUtils.concat(createNativeTitle(), String.valueOf(mMessageCount),
+                " messages received"));
+    }
+
+    private void refreshPerfText() {
+        mPerfTextView.setText("Average time over 5000 messages: "
+                + (System.currentTimeMillis() - mTimeStamp) / 5000.0 + " ms");
+    }
+
+    private void setupMessagePort() {
+        WebMessagePortCompat[] ports = WebViewCompat.createWebMessageChannel(mWebView);
+        WebViewCompat.postWebMessage(mWebView,
+                new WebMessageCompat("setup", new WebMessagePortCompat[]{ports[0]}), Uri.EMPTY);
+        mPort = ports[1];
+        mPort.setWebMessageCallback(new WebMessagePortCompat.WebMessageCallbackCompat() {
+            @Override
+            public void onMessage(@NonNull WebMessagePortCompat port,
+                    @Nullable WebMessageCompat message) {
+                switch (message.getType()) {
+                    case WebMessageCompat.TYPE_STRING:
+                        mMessageCount = Integer.parseInt(message.getData());
+                        break;
+                    case WebMessageCompat.TYPE_ARRAY_BUFFER:
+                        mMessageCount = ByteBuffer.wrap(message.getArrayBuffer()).getInt();
+                        break;
+                    default:
+                        // Should never happen
+                        throw new RuntimeException("Invalid type: " + message.getType());
+                }
+                if (mMessageCount % 100 == 0) {
+                    refreshNativeText();
+                }
+                if (mMessageCount == mExpectedCount) {
+                    refreshPerfText();
+                    mSpinner.setEnabled(PAYLOAD_FEATURE_ENABLED);
+                }
+                sendMessage();
+            }
+        });
+    }
+
+    private class MyWebViewClient extends WebViewClient {
+        @Override
+        public void onPageFinished(WebView view, String url) {
+            super.onPageFinished(view, url);
+            setupMessagePort();
+            view.setWebViewClient(null);
+        }
+    }
+}
diff --git a/webkit/integration-tests/testapp/src/main/res/layout/activity_get_cookie_info.xml b/webkit/integration-tests/testapp/src/main/res/layout/activity_get_cookie_info.xml
new file mode 100644
index 0000000..864271c
--- /dev/null
+++ b/webkit/integration-tests/testapp/src/main/res/layout/activity_get_cookie_info.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    tools:context=".CookieManagerActivity" >
+
+    <WebView
+        android:id="@+id/webView"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="0.8"/>
+
+    <View
+        android:id="@+id/divider"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="?android:attr/listDivider" />
+
+    <TextView
+        android:id="@+id/textViewTop"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:padding="5dp"
+        android:text="@string/cookie_manager_old_api_text"
+        android:textAppearance="@style/TextAppearance.AppCompat.Medium"
+        android:textColor="@color/colorPrimaryText" />
+
+    <TextView
+        android:id="@+id/textViewBottom"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:padding="5dp"
+        android:text="@string/cookie_manager_new_api_text"
+        android:textAppearance="@style/TextAppearance.AppCompat.Medium"
+        android:textColor="@color/colorPrimaryText" />
+</LinearLayout>
\ No newline at end of file
diff --git a/webkit/integration-tests/testapp/src/main/res/layout/activity_web_message_compat.xml b/webkit/integration-tests/testapp/src/main/res/layout/activity_web_message_compat.xml
new file mode 100644
index 0000000..1959619
--- /dev/null
+++ b/webkit/integration-tests/testapp/src/main/res/layout/activity_web_message_compat.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2022 The Android Open Source Project
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+          http://www.apache.org/licenses/LICENSE-2.0
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/activity_web_message_compat"
+    android:orientation="vertical"
+    android:weightSum="4"
+    android:background="#FF0"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <WebView
+        android:id="@+id/webview"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="2"/>
+
+    <TextView
+        android:id="@+id/textview"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"/>
+
+    <TextView
+        android:id="@+id/textview_perf"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"/>
+
+    <CheckBox
+        android:id="@+id/checkbox_window_message"
+        android:text="Use WebViewCompat#PostWebMessage"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <Spinner
+        android:id="@+id/message_type_spinner"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+    <Button
+        android:id="@+id/button_send"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal"
+        android:text="Send 5000 messages" />
+
+</LinearLayout>
diff --git a/webkit/integration-tests/testapp/src/main/res/values/colors.xml b/webkit/integration-tests/testapp/src/main/res/values/colors.xml
index d65070a..4eab5cd 100644
--- a/webkit/integration-tests/testapp/src/main/res/values/colors.xml
+++ b/webkit/integration-tests/testapp/src/main/res/values/colors.xml
@@ -18,6 +18,7 @@
 <resources>
     <color name="colorPrimary">#3F51B5</color>
     <color name="colorPrimaryDark">#303F9F</color>
+    <color name="colorPrimaryText">#1F1F1F</color>
     <color name="colorAccent">#FF4081</color>
     <color name="colorBackground">#546072</color>
     <color name="colorTextBox">#BDC1C2</color>
diff --git a/webkit/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml b/webkit/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
index dbeecb4..92d9e9c 100644
--- a/webkit/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
+++ b/webkit/integration-tests/testapp/src/main/res/values/donottranslate-strings.xml
@@ -69,6 +69,7 @@
     <string name="renderer_unresponsive_description">WebView renderer has become unresponsive.</string>
     <string name="fullscreen_activity_title">Fullscreen web contents Demo</string>
     <string name="js_java_interaction_activity_title">JavaScript - Java communication</string>
+    <string name="web_message_compat_activity_title">WebMessageCompat Demo</string>
     <string name="web_message_listener_activity_title">WebMessageListener Demo</string>
     <string name="web_message_listener_malicious_website_activity_title">Malicious Website Demo</string>
     <string name="document_start_javascript_activity_title">DocumentStartJavaScript Demo</string>
@@ -83,6 +84,10 @@
     <string name="variations_header_activity_title">Variations Header</string>
     <string name="variations_header_message">Variations header is: "%1$s"</string>
     <string name="variations_header_unavailable">Variations Header not available</string>
+    <string name="cookie_manager_activity_title">Cookie Manager</string>
+    <string name="cookie_manager_old_api_text">Old cookie API: </string>
+    <string name="cookie_manager_new_api_text">New cookie API: </string>
+    <string name="cookie_manager_get_cookie_info_not_supported">CookieManagerCompat#getCookieInfo not supported.</string>
 
     <!-- Proxy Override -->
     <string name="proxy_override_activity_title">Proxy Override</string>
diff --git a/webkit/webkit/api/current.txt b/webkit/webkit/api/current.txt
index 88cad05..5637712 100644
--- a/webkit/webkit/api/current.txt
+++ b/webkit/webkit/api/current.txt
@@ -1,6 +1,10 @@
 // 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 abstract class JavaScriptReplyProxy {
     method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(String);
   }
@@ -227,6 +231,7 @@
     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";
diff --git a/webkit/webkit/api/public_plus_experimental_current.txt b/webkit/webkit/api/public_plus_experimental_current.txt
index 88cad05..5637712 100644
--- a/webkit/webkit/api/public_plus_experimental_current.txt
+++ b/webkit/webkit/api/public_plus_experimental_current.txt
@@ -1,6 +1,10 @@
 // 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 abstract class JavaScriptReplyProxy {
     method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(String);
   }
@@ -227,6 +231,7 @@
     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";
diff --git a/webkit/webkit/api/restricted_current.txt b/webkit/webkit/api/restricted_current.txt
index 88cad05..5637712 100644
--- a/webkit/webkit/api/restricted_current.txt
+++ b/webkit/webkit/api/restricted_current.txt
@@ -1,6 +1,10 @@
 // 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 abstract class JavaScriptReplyProxy {
     method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(String);
   }
@@ -227,6 +231,7 @@
     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";
diff --git a/webkit/webkit/src/androidTest/java/androidx/webkit/CookieManagerCompatTest.java b/webkit/webkit/src/androidTest/java/androidx/webkit/CookieManagerCompatTest.java
index 5f68f6c..ebd0934 100644
--- a/webkit/webkit/src/androidTest/java/androidx/webkit/CookieManagerCompatTest.java
+++ b/webkit/webkit/src/androidTest/java/androidx/webkit/CookieManagerCompatTest.java
@@ -17,7 +17,6 @@
 package androidx.webkit;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
 
 import android.os.Build;
 import android.webkit.CookieManager;
diff --git a/webkit/webkit/src/androidTest/java/androidx/webkit/WebViewWebMessageCompatTest.java b/webkit/webkit/src/androidTest/java/androidx/webkit/WebViewWebMessageCompatTest.java
new file mode 100644
index 0000000..752546d
--- /dev/null
+++ b/webkit/webkit/src/androidTest/java/androidx/webkit/WebViewWebMessageCompatTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.webkit;
+
+import android.net.Uri;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SdkSuppress;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Random;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * Test {@link WebMessagePortCompat#postMessage} and {@link
+ * WebMessagePortCompat#setWebMessageCallback} basic usages.
+ *
+ * Test in Chromium tree PostMessageTest.java for these APIs are more comprehensive.
+ */
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.LOLLIPOP)
+public class WebViewWebMessageCompatTest {
+    private static final String BASE_URI = "http://www.example.com";
+    private static final String ECHO_MESSAGE = "<!DOCTYPE html><html><body>"
+            + "    <script>"
+            + "        var port;"
+            + "        function echo(e) {"
+            + "            port.postMessage(e.data);"
+            + "            port.postMessage(e.data, [e.data]);"
+            + "        }"
+            + "        window.onmessage = (e) => {"
+            + "            if (e.ports[0]) { "
+            + "                port = e.ports[0]; "
+            + "                port.onmessage = (event) => echo(event);"
+            + "                return; "
+            + "            }"
+            + "            echo(e);"
+            + "        }"
+            + "    </script>"
+            + "</body></html>";
+
+    private WebViewOnUiThread mWebViewOnUiThread;
+    private final TestWebMessageListener mListener = new TestWebMessageListener();
+    private final byte[] mBytes = new byte[1000];
+
+    private static class TestWebMessageListener extends
+            WebMessagePortCompat.WebMessageCallbackCompat {
+        private final BlockingQueue<WebMessageCompat> mQueue = new LinkedBlockingQueue<>();
+
+        @Override
+        public void onMessage(@NonNull WebMessagePortCompat port,
+                @Nullable WebMessageCompat message) {
+            mQueue.add(message);
+        }
+
+        public WebMessageCompat waitForOnPostMessage() throws Exception {
+            return WebkitUtils.waitForNextQueueElement(mQueue);
+        }
+    }
+
+    @Before
+    public void setUp() {
+        mWebViewOnUiThread = new WebViewOnUiThread();
+        mWebViewOnUiThread.getSettings().setJavaScriptEnabled(true);
+
+        final Random random = new Random(42);
+        random.nextBytes(mBytes);
+    }
+
+    @After
+    public void tearDown() {
+        if (mWebViewOnUiThread != null) {
+            mWebViewOnUiThread.cleanUp();
+        }
+    }
+
+    private WebMessagePortCompat preparePort() {
+        final WebMessagePortCompat[] port = mWebViewOnUiThread.createWebMessageChannelCompat();
+        mWebViewOnUiThread.postWebMessageCompat(new WebMessageCompat("setup",
+                new WebMessagePortCompat[]{port[1]}), Uri.EMPTY);
+        port[0].setWebMessageCallback(new Handler(Looper.getMainLooper()), mListener);
+        return port[0];
+    }
+
+    private void assertArrayBufferMessage() throws Exception {
+        final WebMessageCompat message1 = mListener.waitForOnPostMessage();
+        Assert.assertEquals(WebMessageCompat.TYPE_ARRAY_BUFFER, message1.getType());
+        Assert.assertArrayEquals(mBytes, message1.getArrayBuffer());
+
+        final WebMessageCompat message2 = mListener.waitForOnPostMessage();
+        Assert.assertEquals(WebMessageCompat.TYPE_ARRAY_BUFFER, message2.getType());
+        Assert.assertArrayEquals(mBytes, message2.getArrayBuffer());
+    }
+
+    @Test
+    public void testArrayBufferOverPort() throws Exception {
+        WebkitUtils.checkFeature(WebViewFeature.POST_WEB_MESSAGE);
+        WebkitUtils.checkFeature(WebViewFeature.WEB_MESSAGE_GET_MESSAGE_PAYLOAD);
+        loadHtmlSync(ECHO_MESSAGE);
+        final WebMessagePortCompat port = preparePort();
+        port.postMessage(new WebMessageCompat(mBytes));
+        assertArrayBufferMessage();
+    }
+
+    @Test
+    public void testArrayBufferToMainFrame() throws Exception {
+        WebkitUtils.checkFeature(WebViewFeature.POST_WEB_MESSAGE);
+        WebkitUtils.checkFeature(WebViewFeature.WEB_MESSAGE_GET_MESSAGE_PAYLOAD);
+        loadHtmlSync(ECHO_MESSAGE);
+        preparePort();
+        mWebViewOnUiThread.postWebMessageCompat(new WebMessageCompat(mBytes), Uri.EMPTY);
+        assertArrayBufferMessage();
+    }
+
+    private void loadHtmlSync(String html) {
+        mWebViewOnUiThread.loadDataWithBaseURLAndWaitForCompletion(
+                BASE_URI, html, "text/html", null, null);
+    }
+}
diff --git a/webkit/webkit/src/main/java/androidx/webkit/CookieManagerCompat.java b/webkit/webkit/src/main/java/androidx/webkit/CookieManagerCompat.java
index d3ca97a..3aaad88 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/CookieManagerCompat.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/CookieManagerCompat.java
@@ -20,7 +20,6 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresFeature;
-import androidx.annotation.RestrictTo;
 import androidx.webkit.internal.ApiFeature;
 import androidx.webkit.internal.CookieManagerAdapter;
 import androidx.webkit.internal.WebViewFeatureInternal;
@@ -30,9 +29,7 @@
 
 /**
  * Compatibility version of {@link android.webkit.CookieManager}
- * @hide
  */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public class CookieManagerCompat {
     private CookieManagerCompat() {}
 
@@ -44,9 +41,7 @@
      *
      * @param url the URL for which the API retrieves all available cookies.
      * @return the cookies as a list of strings.
-     * @hide
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     @RequiresFeature(name = WebViewFeature.GET_COOKIE_INFO,
             enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
     public static @NonNull List<String> getCookieInfo(@NonNull CookieManager cookieManager,
diff --git a/webkit/webkit/src/main/java/androidx/webkit/WebMessageCompat.java b/webkit/webkit/src/main/java/androidx/webkit/WebMessageCompat.java
index e1b1193..c80f246 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/WebMessageCompat.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/WebMessageCompat.java
@@ -16,7 +16,15 @@
 
 package androidx.webkit;
 
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresFeature;
+import androidx.annotation.RestrictTo;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 
 /**
  * The Java representation of the HTML5 PostMessage event. See
@@ -25,32 +33,102 @@
  */
 public class WebMessageCompat {
 
-    private String mData;
-    private WebMessagePortCompat[] mPorts;
+    /**
+     * Indicates the payload of WebMessageCompat is String.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static final int TYPE_STRING = 0;
+    /**
+     * Indicates the payload of WebMessageCompat is JavaScript ArrayBuffer.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static final int TYPE_ARRAY_BUFFER = 1;
+    private final @Nullable WebMessagePortCompat[] mPorts;
+    private final @Nullable String mString;
+    private final @Nullable byte[] mArrayBuffer;
+    private final @Type int mType;
 
     /**
-     * Creates a WebMessage.
-     * @param data  the data of the message.
+     * Creates a WebMessage with String payload.
+     *
+     * @param data the string of the message.
      */
     public WebMessageCompat(@Nullable String data) {
-        mData = data;
+        this(data, null);
     }
 
     /**
-     * Creates a WebMessage.
-     * @param data  the data of the message.
-     * @param ports  the ports that are sent with the message.
+     * Creates a WebMessage with String payload.
+     *
+     * @param data  the string data of the message.
+     * @param ports the ports that are sent with the message.
      */
     public WebMessageCompat(@Nullable String data, @Nullable WebMessagePortCompat[] ports) {
-        mData = data;
+        mString = data;
+        mArrayBuffer = null;
         mPorts = ports;
+        mType = TYPE_STRING;
     }
 
     /**
-     * Returns the data of the message.
+     * Creates a WebMessage with JavaScript ArrayBuffer payload.
+     *
+     * @param arrayBuffer the array buffer data of the message.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @RequiresFeature(name = WebViewFeature.WEB_MESSAGE_GET_MESSAGE_PAYLOAD,
+            enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
+    public WebMessageCompat(@NonNull byte[] arrayBuffer) {
+        this(arrayBuffer, null);
+    }
+
+    /**
+     * Creates a WebMessage with JavaScript ArrayBuffer payload.
+     *
+     * @param arrayBuffer the array buffer data of the message.
+     * @param ports       the ports that are sent with the message.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @RequiresFeature(name = WebViewFeature.WEB_MESSAGE_GET_MESSAGE_PAYLOAD,
+            enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
+    public WebMessageCompat(@NonNull byte[] arrayBuffer,
+            @Nullable WebMessagePortCompat[] ports) {
+        Objects.requireNonNull(arrayBuffer);
+        mArrayBuffer = arrayBuffer;
+        mString = null;
+        mPorts = ports;
+        mType = TYPE_ARRAY_BUFFER;
+    }
+
+    /**
+     * Returns the payload type of the message.
+     * @return the payload type of WebMessageCompat.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public @Type int getType() {
+        return mType;
+    }
+
+    /**
+     * Returns the ArrayBuffer data of message. A ArrayBuffer or Transferable ArrayBuffer can be
+     * received from JavaScript.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public @Nullable byte[] getArrayBuffer() {
+        return mArrayBuffer;
+    }
+
+    /**
+     * Returns the String data of the message.
      */
     public @Nullable String getData() {
-        return mData;
+        return mString;
     }
 
     /**
@@ -61,4 +139,10 @@
     public WebMessagePortCompat[] getPorts() {
         return mPorts;
     }
+
+    /** @hide */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @IntDef({TYPE_STRING, TYPE_ARRAY_BUFFER})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
 }
diff --git a/webkit/webkit/src/main/java/androidx/webkit/WebViewCompat.java b/webkit/webkit/src/main/java/androidx/webkit/WebViewCompat.java
index 1970130..0b42210 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/WebViewCompat.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/WebViewCompat.java
@@ -39,6 +39,7 @@
 import androidx.webkit.internal.ApiHelperForOMR1;
 import androidx.webkit.internal.ApiHelperForP;
 import androidx.webkit.internal.ApiHelperForQ;
+import androidx.webkit.internal.WebMessageAdapter;
 import androidx.webkit.internal.WebMessagePortImpl;
 import androidx.webkit.internal.WebViewFeatureInternal;
 import androidx.webkit.internal.WebViewGlueCommunicator;
@@ -483,10 +484,12 @@
         }
 
         final ApiFeature.M feature = WebViewFeatureInternal.POST_WEB_MESSAGE;
-        if (feature.isSupportedByFramework()) {
+        // Only String type is supported by framework.
+        if (feature.isSupportedByFramework() && message.getType() == WebMessageCompat.TYPE_STRING) {
             ApiHelperForM.postWebMessage(webview,
                     WebMessagePortImpl.compatToFrameworkMessage(message), targetOrigin);
-        } else if (feature.isSupportedByWebView()) {
+        } else if (feature.isSupportedByWebView()
+                && WebMessageAdapter.isMessagePayloadTypeSupportedByWebView(message.getType())) {
             getProvider(webview).postWebMessage(message, targetOrigin);
         } else {
             throw WebViewFeatureInternal.getUnsupportedOperationException();
diff --git a/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java b/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
index 5e1059d..b089191 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
@@ -80,6 +80,7 @@
             SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL,
             WEB_MESSAGE_PORT_POST_MESSAGE,
             WEB_MESSAGE_PORT_CLOSE,
+            WEB_MESSAGE_GET_MESSAGE_PAYLOAD,
             WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK,
             CREATE_WEB_MESSAGE_CHANNEL,
             POST_WEB_MESSAGE,
@@ -349,6 +350,16 @@
     /**
      * Feature for {@link #isFeatureSupported(String)}.
      * This feature covers
+     * {@link WebMessagePortCompat#postMessage(WebMessageCompat)} with ArrayBuffer type, and
+     * {@link WebViewCompat#postWebMessage(WebView, WebMessageCompat, Uri)} with ArrayBuffer type.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static final String WEB_MESSAGE_GET_MESSAGE_PAYLOAD = "WEB_MESSAGE_GET_MESSAGE_PAYLOAD";
+
+    /**
+     * Feature for {@link #isFeatureSupported(String)}.
+     * This feature covers
      * {@link androidx.webkit.WebMessagePortCompat#setWebMessageCallback(
      * WebMessagePortCompat.WebMessageCallbackCompat)}, and
      * {@link androidx.webkit.WebMessagePortCompat#setWebMessageCallback(Handler,
@@ -509,9 +520,7 @@
      * Feature for {@link #isFeatureSupported(String)}.
      * This feature covers
      * {@link CookieManagerCompat#getCookieInfo(CookieManager, String)}.
-     * @hide
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     public static final String GET_COOKIE_INFO = "GET_COOKIE_INFO";
 
     /**
diff --git a/webkit/webkit/src/main/java/androidx/webkit/internal/WebMessageAdapter.java b/webkit/webkit/src/main/java/androidx/webkit/internal/WebMessageAdapter.java
index a65f15a..e1222fa 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/internal/WebMessageAdapter.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/internal/WebMessageAdapter.java
@@ -16,12 +16,20 @@
 
 package androidx.webkit.internal;
 
+import static org.chromium.support_lib_boundary.WebMessagePayloadBoundaryInterface.WebMessagePayloadType;
+
+import android.os.Build;
+
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 import androidx.webkit.WebMessageCompat;
 import androidx.webkit.WebMessagePortCompat;
 
 import org.chromium.support_lib_boundary.WebMessageBoundaryInterface;
+import org.chromium.support_lib_boundary.WebMessagePayloadBoundaryInterface;
+import org.chromium.support_lib_boundary.util.BoundaryInterfaceReflectionUtil;
+import org.chromium.support_lib_boundary.util.Features;
 
 import java.lang.reflect.InvocationHandler;
 
@@ -33,21 +41,29 @@
 public class WebMessageAdapter implements WebMessageBoundaryInterface {
     private WebMessageCompat mWebMessageCompat;
 
+    private static final String[] sFeatures = {Features.WEB_MESSAGE_GET_MESSAGE_PAYLOAD};
+
     public WebMessageAdapter(@NonNull WebMessageCompat webMessage) {
         this.mWebMessageCompat = webMessage;
     }
 
-    @SuppressWarnings("deprecation")
+    /**
+     * @deprecated  Keep backwards competibility with old version of WebView. This method is
+     * equivalent to {@link WebMessagePayloadBoundaryInterface#getAsString()}.
+     */
+    @Deprecated
     @Override
     @Nullable
     public String getData() {
         return mWebMessageCompat.getData();
     }
 
-    @SuppressWarnings("MissingOverride")
+    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
+    @Override
     @Nullable
     public InvocationHandler getMessagePayload() {
-        throw new UnsupportedOperationException("This method is not yet supported");
+        return BoundaryInterfaceReflectionUtil.createInvocationHandlerFor(
+                new WebMessagePayloadAdapter(mWebMessageCompat));
     }
 
     @Override
@@ -67,7 +83,17 @@
     @NonNull
     public String[] getSupportedFeatures() {
         // getData() and getPorts() are not covered by feature flags.
-        return new String[0];
+        return sFeatures;
+    }
+
+    /**
+     * Utility method to check if the WebMessageCompat payload type is supported by WebView.
+     */
+    public static boolean isMessagePayloadTypeSupportedByWebView(
+            @WebMessageCompat.Type final int type) {
+        return type == WebMessageCompat.TYPE_STRING
+                || (type == WebMessageCompat.TYPE_ARRAY_BUFFER
+                && WebViewFeatureInternal.WEB_MESSAGE_GET_MESSAGE_PAYLOAD.isSupportedByWebView());
     }
 
     // ====================================================================================
@@ -77,16 +103,31 @@
     /**
      * Utility method used to convert PostMessages from the Chromium side to
      * {@link WebMessageCompat} objects - a class apps recognize.
+     * Return null when the WebMessageCompat payload type is not supported by AndroidX now.
      */
-    @NonNull
-    // Suppress deprecation warning for usage of WebMessageBoundaryInterface's getData() method,
-    // TODO([email protected]): remove this once changes corresponding to https://crrev.com/c/3607795
-    // are done in webkit.
     @SuppressWarnings("deprecation")
+    @Nullable
     public static WebMessageCompat webMessageCompatFromBoundaryInterface(
             @NonNull WebMessageBoundaryInterface boundaryInterface) {
-        return new WebMessageCompat(boundaryInterface.getData(),
-                toWebMessagePortCompats(boundaryInterface.getPorts()));
+        final WebMessagePortCompat[] ports = toWebMessagePortCompats(
+                boundaryInterface.getPorts());
+        if (WebViewFeatureInternal.WEB_MESSAGE_GET_MESSAGE_PAYLOAD.isSupportedByWebView()) {
+            WebMessagePayloadBoundaryInterface payloadInterface =
+                    BoundaryInterfaceReflectionUtil.castToSuppLibClass(
+                            WebMessagePayloadBoundaryInterface.class,
+                            boundaryInterface.getMessagePayload());
+            final @WebMessagePayloadType int type = payloadInterface.getType();
+            switch (type) {
+                case WebMessagePayloadType.TYPE_STRING:
+                    return new WebMessageCompat(payloadInterface.getAsString(), ports);
+                case WebMessagePayloadType.TYPE_ARRAY_BUFFER:
+                    return new WebMessageCompat(payloadInterface.getAsArrayBuffer(), ports);
+            }
+            // Unsupported message type.
+            return null;
+        }
+        // MessagePayload not supported by WebView, fallback.
+        return new WebMessageCompat(boundaryInterface.getData(), ports);
     }
 
     @NonNull
diff --git a/webkit/webkit/src/main/java/androidx/webkit/internal/WebMessageCallbackAdapter.java b/webkit/webkit/src/main/java/androidx/webkit/internal/WebMessageCallbackAdapter.java
index f68ba94..cc490cb 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/internal/WebMessageCallbackAdapter.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/internal/WebMessageCallbackAdapter.java
@@ -17,6 +17,7 @@
 package androidx.webkit.internal;
 
 import androidx.annotation.NonNull;
+import androidx.webkit.WebMessageCompat;
 import androidx.webkit.WebMessagePortCompat.WebMessageCallbackCompat;
 
 import org.chromium.support_lib_boundary.WebMessageBoundaryInterface;
@@ -38,10 +39,13 @@
 
     @Override
     public void onMessage(@NonNull InvocationHandler port, @NonNull InvocationHandler message) {
-        mImpl.onMessage(new WebMessagePortImpl(port),
+        final WebMessageCompat messageCompat =
                 WebMessageAdapter.webMessageCompatFromBoundaryInterface(
-                        BoundaryInterfaceReflectionUtil.castToSuppLibClass(
-                                WebMessageBoundaryInterface.class, message)));
+                BoundaryInterfaceReflectionUtil.castToSuppLibClass(
+                        WebMessageBoundaryInterface.class, message));
+        if (messageCompat != null) {
+            mImpl.onMessage(new WebMessagePortImpl(port), messageCompat);
+        }
     }
 
     // This method declares which APIs the support library side supports - so that the Chromium-side
diff --git a/webkit/webkit/src/main/java/androidx/webkit/internal/WebMessageListenerAdapter.java b/webkit/webkit/src/main/java/androidx/webkit/internal/WebMessageListenerAdapter.java
index 84825e8..1fe7990 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/internal/WebMessageListenerAdapter.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/internal/WebMessageListenerAdapter.java
@@ -46,13 +46,15 @@
     public void onPostMessage(@NonNull WebView view,
             @NonNull /* WebMessage */ InvocationHandler message, @NonNull Uri sourceOrigin,
             boolean isMainFrame, @NonNull /* JavaScriptReplyProxy */ InvocationHandler replyProxy) {
-        WebMessageCompat webMessage = WebMessageAdapter.webMessageCompatFromBoundaryInterface(
+        final WebMessageCompat webMessage = WebMessageAdapter.webMessageCompatFromBoundaryInterface(
                 BoundaryInterfaceReflectionUtil.castToSuppLibClass(
                         WebMessageBoundaryInterface.class, message));
-        JavaScriptReplyProxy jsReplyProxy =
-                JavaScriptReplyProxyImpl.forInvocationHandler(replyProxy);
-        mWebMessageListener.onPostMessage(
-                view, webMessage, sourceOrigin, isMainFrame, jsReplyProxy);
+        if (webMessage != null) {
+            JavaScriptReplyProxy jsReplyProxy =
+                    JavaScriptReplyProxyImpl.forInvocationHandler(replyProxy);
+            mWebMessageListener.onPostMessage(
+                    view, webMessage, sourceOrigin, isMainFrame, jsReplyProxy);
+        }
     }
 
     /**
diff --git a/webkit/webkit/src/main/java/androidx/webkit/internal/WebMessagePayloadAdapter.java b/webkit/webkit/src/main/java/androidx/webkit/internal/WebMessagePayloadAdapter.java
new file mode 100644
index 0000000..fc194d6
--- /dev/null
+++ b/webkit/webkit/src/main/java/androidx/webkit/internal/WebMessagePayloadAdapter.java
@@ -0,0 +1,67 @@
+/*
+ * 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.webkit.internal;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.webkit.WebMessageCompat;
+
+import org.chromium.support_lib_boundary.WebMessagePayloadBoundaryInterface;
+
+/**
+ * Adapter between {@link WebMessageCompat} and {@link WebMessagePayloadBoundaryInterface}.
+ * This class is used to pass **payload** of a WebMessageCompat to Chromium.
+ */
+public class WebMessagePayloadAdapter implements WebMessagePayloadBoundaryInterface {
+    private final @NonNull WebMessageCompat mMessageCompat;
+
+    public WebMessagePayloadAdapter(@NonNull WebMessageCompat webMessageCompat) {
+        mMessageCompat = webMessageCompat;
+    }
+
+    @Override
+    @NonNull
+    public String[] getSupportedFeatures() {
+        // getType, getAsString and getAsArrayBuffer are covered by
+        // WEB_MESSAGE_GET_MESSAGE_PAYLOAD.
+        return new String[0];
+    }
+
+    @Override
+    public int getType() {
+        switch (mMessageCompat.getType()) {
+            case WebMessageCompat.TYPE_STRING:
+                return WebMessagePayloadType.TYPE_STRING;
+            case WebMessageCompat.TYPE_ARRAY_BUFFER:
+                return WebMessagePayloadType.TYPE_ARRAY_BUFFER;
+        }
+        // Should never happen.
+        throw WebViewFeatureInternal.getUnsupportedOperationException();
+    }
+
+    @Nullable
+    @Override
+    public String getAsString() {
+        return mMessageCompat.getData();
+    }
+
+    @NonNull
+    @Override
+    public byte[] getAsArrayBuffer() {
+        return mMessageCompat.getArrayBuffer();
+    }
+}
diff --git a/webkit/webkit/src/main/java/androidx/webkit/internal/WebMessagePortImpl.java b/webkit/webkit/src/main/java/androidx/webkit/internal/WebMessagePortImpl.java
index 551bf4c..c74e9aa 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/internal/WebMessagePortImpl.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/internal/WebMessagePortImpl.java
@@ -72,9 +72,11 @@
     @Override
     public void postMessage(@NonNull WebMessageCompat message) {
         final ApiFeature.M feature = WebViewFeatureInternal.WEB_MESSAGE_PORT_POST_MESSAGE;
-        if (feature.isSupportedByFramework()) {
+        // Only String type is supported by framework.
+        if (feature.isSupportedByFramework() && message.getType() == WebMessageCompat.TYPE_STRING) {
             ApiHelperForM.postMessage(getFrameworksImpl(), compatToFrameworkMessage(message));
-        } else if (feature.isSupportedByWebView()) {
+        } else if (feature.isSupportedByWebView()
+                && WebMessageAdapter.isMessagePayloadTypeSupportedByWebView(message.getType())) {
             getBoundaryInterface().postMessage(
                     BoundaryInterfaceReflectionUtil.createInvocationHandlerFor(
                             new WebMessageAdapter(message)));
@@ -98,12 +100,14 @@
     @Override
     public void setWebMessageCallback(@NonNull final WebMessageCallbackCompat callback) {
         final ApiFeature.M feature = WebViewFeatureInternal.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK;
-        if (feature.isSupportedByFramework()) {
-            ApiHelperForM.setWebMessageCallback(getFrameworksImpl(), callback);
-        } else if (feature.isSupportedByWebView()) {
+        if (feature.isSupportedByWebView()) {
+            // We prefer use WebView impl, since the impl in framework does not support
+            // WebMessageCompat types other than String.
             getBoundaryInterface().setWebMessageCallback(
                     BoundaryInterfaceReflectionUtil.createInvocationHandlerFor(
                             new WebMessageCallbackAdapter(callback)));
+        } else if (feature.isSupportedByFramework()) {
+            ApiHelperForM.setWebMessageCallback(getFrameworksImpl(), callback);
         } else {
             throw WebViewFeatureInternal.getUnsupportedOperationException();
         }
@@ -113,12 +117,14 @@
     public void setWebMessageCallback(@Nullable Handler handler,
             @NonNull final WebMessageCallbackCompat callback) {
         final ApiFeature.M feature = WebViewFeatureInternal.CREATE_WEB_MESSAGE_CHANNEL;
-        if (feature.isSupportedByFramework()) {
-            ApiHelperForM.setWebMessageCallback(getFrameworksImpl(), callback, handler);
-        } else if (feature.isSupportedByWebView()) {
+        if (feature.isSupportedByWebView()) {
+            // We prefer use WebView impl, since the impl in framework does not support
+            // WebMessageCompat types other than String.
             getBoundaryInterface().setWebMessageCallback(
                     BoundaryInterfaceReflectionUtil.createInvocationHandlerFor(
                             new WebMessageCallbackAdapter(callback)), handler);
+        } else if (feature.isSupportedByFramework()) {
+            ApiHelperForM.setWebMessageCallback(getFrameworksImpl(), callback, handler);
         } else {
             throw WebViewFeatureInternal.getUnsupportedOperationException();
         }
diff --git a/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java b/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
index f949681..75767b6 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
@@ -305,6 +305,15 @@
 
     /**
      * This feature covers
+     * {@link WebMessagePortCompat#postMessage(WebMessageCompat)} with ArrayBuffer type, and
+     * {@link WebViewCompat#postWebMessage(WebView, WebMessageCompat, Uri)} with ArrayBuffer type.
+     */
+    public static final ApiFeature.NoFramework WEB_MESSAGE_GET_MESSAGE_PAYLOAD =
+            new ApiFeature.NoFramework(WebViewFeature.WEB_MESSAGE_GET_MESSAGE_PAYLOAD,
+                    Features.WEB_MESSAGE_GET_MESSAGE_PAYLOAD);
+
+    /**
+     * This feature covers
      * {@link WebMessagePortCompat#setWebMessageCallback(
      *WebMessagePortCompat.WebMessageCallbackCompat)}, and
      * {@link WebMessagePortCompat#setWebMessageCallback(Handler,
diff --git a/window/window-core/api/current.txt b/window/window-core/api/current.txt
new file mode 100644
index 0000000..199e08d
--- /dev/null
+++ b/window/window-core/api/current.txt
@@ -0,0 +1,40 @@
+// Signature format: 4.0
+package androidx.window.core.layout {
+
+  public final class WindowHeightSizeClass {
+    method public static androidx.window.core.layout.WindowHeightSizeClass compute(float dpHeight);
+    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 {
+    method public androidx.window.core.layout.WindowHeightSizeClass compute(float dpHeight);
+  }
+
+  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/public_plus_experimental_current.txt b/window/window-core/api/public_plus_experimental_current.txt
new file mode 100644
index 0000000..199e08d
--- /dev/null
+++ b/window/window-core/api/public_plus_experimental_current.txt
@@ -0,0 +1,40 @@
+// Signature format: 4.0
+package androidx.window.core.layout {
+
+  public final class WindowHeightSizeClass {
+    method public static androidx.window.core.layout.WindowHeightSizeClass compute(float dpHeight);
+    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 {
+    method public androidx.window.core.layout.WindowHeightSizeClass compute(float dpHeight);
+  }
+
+  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-current.txt b/window/window-core/api/res-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/window/window-core/api/res-current.txt
diff --git a/window/window-core/api/restricted_current.txt b/window/window-core/api/restricted_current.txt
new file mode 100644
index 0000000..199e08d
--- /dev/null
+++ b/window/window-core/api/restricted_current.txt
@@ -0,0 +1,40 @@
+// Signature format: 4.0
+package androidx.window.core.layout {
+
+  public final class WindowHeightSizeClass {
+    method public static androidx.window.core.layout.WindowHeightSizeClass compute(float dpHeight);
+    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 {
+    method public androidx.window.core.layout.WindowHeightSizeClass compute(float dpHeight);
+  }
+
+  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/build.gradle b/window/window-core/build.gradle
new file mode 100644
index 0000000..e4cb437
--- /dev/null
+++ b/window/window-core/build.gradle
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.LibraryType
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+    api(libs.kotlinStdlib)
+    // Add dependencies here
+
+    testImplementation(libs.kotlinTest)
+    testImplementation(libs.kotlinTestAnnotationsCommon)
+    testImplementation(libs.kotlinCoroutinesCore)
+}
+
+android {
+    namespace "androidx.window.core"
+}
+
+androidx {
+    name = "androidx.window:window-core"
+    type = LibraryType.PUBLISHED_LIBRARY
+    mavenGroup = LibraryGroups.WINDOW
+    inceptionYear = "2022"
+    description = "WindowManager Core Library."
+}
diff --git a/window/window-core/src/main/java/androidx/window/core/layout/WindowHeightSizeClass.kt b/window/window-core/src/main/java/androidx/window/core/layout/WindowHeightSizeClass.kt
new file mode 100644
index 0000000..bc94c99
--- /dev/null
+++ b/window/window-core/src/main/java/androidx/window/core/layout/WindowHeightSizeClass.kt
@@ -0,0 +1,95 @@
+/*
+ * 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.window.core.layout
+
+import androidx.window.core.layout.WindowHeightSizeClass.Companion.COMPACT
+import androidx.window.core.layout.WindowHeightSizeClass.Companion.EXPANDED
+import androidx.window.core.layout.WindowHeightSizeClass.Companion.MEDIUM
+
+/**
+ * A class to represent the width size buckets for a viewport. The possible values are [COMPACT],
+ * [MEDIUM], and [EXPANDED]. [WindowHeightSizeClass] should not be used as a proxy for the device
+ * type. It is possible to have resizeable windows in different device types.
+ * The viewport might change from a [COMPACT] all the way to an [EXPANDED] size class.
+ */
+class WindowHeightSizeClass private constructor(
+    private val rawValue: Int
+) {
+
+    override fun toString(): String {
+        val name = when (this) {
+            COMPACT -> "COMPACT"
+            MEDIUM -> "MEDIUM"
+            EXPANDED -> "EXPANDED"
+            else -> "UNKNOWN"
+        }
+        return "WindowHeightSizeClass: $name"
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as WindowHeightSizeClass
+
+        if (rawValue != other.rawValue) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        return rawValue
+    }
+
+    companion object {
+        /**
+         * A bucket to represent a compact height, typical for a phone that is in landscape.
+         */
+        @JvmField
+        val COMPACT: WindowHeightSizeClass = WindowHeightSizeClass(0)
+
+        /**
+         * A bucket to represent a medium height, typical for a phone in portrait or a tablet.
+         */
+        @JvmField
+        val MEDIUM: WindowHeightSizeClass = WindowHeightSizeClass(1)
+
+        /**
+         * A bucket to represent an expanded height window, typical for a large tablet or a
+         * desktop form-factor.
+         */
+        @JvmField
+        val EXPANDED: WindowHeightSizeClass = WindowHeightSizeClass(2)
+
+        /**
+         * Returns a recommended [WindowHeightSizeClass] for the height of a window given the
+         * height in DP.
+         * @param dpHeight the height of the window in DP
+         * @return A recommended size class for the height
+         * @throws IllegalArgumentException if the height is negative
+         */
+        @JvmStatic
+        fun compute(dpHeight: Float): WindowHeightSizeClass {
+            require(dpHeight > 0) { "Width must be positive, received $dpHeight" }
+            return when {
+                dpHeight < 480 -> COMPACT
+                dpHeight < 900 -> MEDIUM
+                else -> EXPANDED
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/window/window-core/src/main/java/androidx/window/core/layout/WindowSizeClass.kt b/window/window-core/src/main/java/androidx/window/core/layout/WindowSizeClass.kt
new file mode 100644
index 0000000..c4ad955
--- /dev/null
+++ b/window/window-core/src/main/java/androidx/window/core/layout/WindowSizeClass.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.window.core.layout
+
+/**
+ * [WindowSizeClass] provides breakpoints for a viewport. Designers should design around the
+ * different combinations of width and height buckets. Developers should use the different buckets
+ * to specify the layouts. Ideally apps will work well in each bucket and by extension work well
+ * across multiple devices. If two devices are in similar buckets they should behave similarly.
+ *
+ * This class is meant to be a common definition that can be shared across different device types.
+ * Application developers can use WindowSizeClass to have standard window buckets and design the UI
+ * around those buckets. Library developers can use these buckets to create different UI with
+ * respect to each bucket. This will help with consistency across multiple device types.
+ *
+ * A library developer use-case can be creating some navigation UI library. For a size
+ * class with the [WindowWidthSizeClass.EXPANDED] width it might be more reasonable to have a side
+ * navigation. For a [WindowWidthSizeClass.COMPACT] width, a bottom navigation might be a better
+ * fit.
+ *
+ * An application use-case can be applied for apps that use a list-detail pattern. The app can use
+ * the [WindowWidthSizeClass.MEDIUM] to determine if there is enough space to show the list and the
+ * detail side by side. If all apps follow this guidance then it will present a very consistent user
+ * experience.
+ *
+ * @see WindowWidthSizeClass
+ * @see WindowHeightSizeClass
+ */
+class WindowSizeClass private constructor(
+    val windowWidthSizeClass: WindowWidthSizeClass,
+    val windowHeightSizeClass: WindowHeightSizeClass
+) {
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as WindowSizeClass
+
+        if (windowWidthSizeClass != other.windowWidthSizeClass) return false
+        if (windowHeightSizeClass != other.windowHeightSizeClass) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = windowWidthSizeClass.hashCode()
+        result = 31 * result + windowHeightSizeClass.hashCode()
+        return result
+    }
+
+    override fun toString(): String {
+        return "SizeClass { widthSizeClass: $windowWidthSizeClass," +
+            " heightSizeClass: $windowHeightSizeClass }"
+    }
+
+    companion object {
+        /**
+         * Computes the [WindowSizeClass] for the given width and height in DP.
+         * @param dpWidth width of a window in DP.
+         * @param dpHeight height of a window in DP.
+         * @return [WindowSizeClass] that is recommended for the given dimensions.
+         * @throws IllegalArgumentException if [dpWidth] or [dpHeight] is
+         * negative.
+         */
+        @JvmStatic
+        fun compute(dpWidth: Float, dpHeight: Float): WindowSizeClass {
+            val windowWidthSizeClass = WindowWidthSizeClass.compute(dpWidth)
+            val windowHeightSizeClass = WindowHeightSizeClass.compute(dpHeight)
+            return WindowSizeClass(windowWidthSizeClass, windowHeightSizeClass)
+        }
+    }
+}
\ No newline at end of file
diff --git a/window/window-core/src/main/java/androidx/window/core/layout/WindowWidthSizeClass.kt b/window/window-core/src/main/java/androidx/window/core/layout/WindowWidthSizeClass.kt
new file mode 100644
index 0000000..f95b12b
--- /dev/null
+++ b/window/window-core/src/main/java/androidx/window/core/layout/WindowWidthSizeClass.kt
@@ -0,0 +1,90 @@
+/*
+ * 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.window.core.layout
+
+/**
+ * A class to represent the width size buckets for a viewport. The possible values are [COMPACT],
+ * [MEDIUM], and [EXPANDED]. [WindowWidthSizeClass] should not be used as a proxy for the device
+ * type. It is possible to have resizeable windows in different device types.
+ * The viewport might change from a [COMPACT] all the way to an [EXPANDED] size class.
+ */
+class WindowWidthSizeClass private constructor(
+    private val rawValue: Int
+) {
+    override fun toString(): String {
+        val name = when (this) {
+            COMPACT -> "COMPACT"
+            MEDIUM -> "MEDIUM"
+            EXPANDED -> "EXPANDED"
+            else -> "UNKNOWN"
+        }
+        return "WindowWidthSizeClass: $name"
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as WindowWidthSizeClass
+
+        if (rawValue != other.rawValue) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        return rawValue
+    }
+
+    companion object {
+        /**
+         * A bucket to represent a compact width window, typical for a phone in portrait.
+         */
+        @JvmField
+        val COMPACT: WindowWidthSizeClass = WindowWidthSizeClass(0)
+
+        /**
+         * A bucket to represent a medium width window, typical for a phone in landscape or
+         * a tablet.
+         */
+        @JvmField
+        val MEDIUM: WindowWidthSizeClass = WindowWidthSizeClass(1)
+
+        /**
+         * A bucket to represent an expanded width window, typical for a large tablet or desktop
+         * form-factor.
+         */
+        @JvmField
+        val EXPANDED: WindowWidthSizeClass = WindowWidthSizeClass(2)
+
+        /**
+         * Returns a recommended [WindowWidthSizeClass] for the width of a window given the width
+         * in DP.
+         * @param dpWidth the width of the window in DP
+         * @return A recommended size class for the width
+         * @throws IllegalArgumentException if the width is negative
+         */
+        internal fun compute(dpWidth: Float): WindowWidthSizeClass {
+            require(dpWidth > 0) { "Width must be positive, received $dpWidth" }
+            return when {
+                dpWidth < 600 -> COMPACT
+                dpWidth < 840 -> MEDIUM
+                else -> EXPANDED
+            }
+        }
+    }
+}
diff --git a/window/window-core/src/test/java/androidx/window/core/layout/WindowHeightSizeClassTest.kt b/window/window-core/src/test/java/androidx/window/core/layout/WindowHeightSizeClassTest.kt
new file mode 100644
index 0000000..cf667a8
--- /dev/null
+++ b/window/window-core/src/test/java/androidx/window/core/layout/WindowHeightSizeClassTest.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.window.core.layout
+
+import androidx.window.core.layout.WindowHeightSizeClass.Companion.COMPACT
+import androidx.window.core.layout.WindowHeightSizeClass.Companion.EXPANDED
+import androidx.window.core.layout.WindowHeightSizeClass.Companion.MEDIUM
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class WindowHeightSizeClassTest {
+
+    @Test
+    fun testComputeForDifferentBuckets() {
+        val expected = listOf(COMPACT, MEDIUM, EXPANDED)
+        val actual = listOf(479f, 899f, 900f)
+            .map { value -> WindowHeightSizeClass.compute(value) }
+        assertEquals(expected, actual)
+    }
+
+    @Test
+    fun testToStringContainsName() {
+        val expected = listOf("COMPACT", "MEDIUM", "EXPANDED")
+            .map { value -> "WindowHeightSizeClass: $value" }
+        val actual = listOf(479f, 899f, 900f)
+            .map { value -> WindowHeightSizeClass.compute(value).toString() }
+        assertEquals(expected, actual)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun testInvalidSizeBucket() {
+        WindowHeightSizeClass.compute(-1f)
+    }
+}
\ No newline at end of file
diff --git a/window/window-core/src/test/java/androidx/window/core/layout/WindowSizeClassTest.kt b/window/window-core/src/test/java/androidx/window/core/layout/WindowSizeClassTest.kt
new file mode 100644
index 0000000..139901b
--- /dev/null
+++ b/window/window-core/src/test/java/androidx/window/core/layout/WindowSizeClassTest.kt
@@ -0,0 +1,66 @@
+/*
+ * 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.window.core.layout
+
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+public class WindowSizeClassTest {
+
+    @Test
+    public fun testWidthSizeClass_construction() {
+        val expected = listOf(
+            WindowWidthSizeClass.COMPACT,
+            WindowWidthSizeClass.MEDIUM,
+            WindowWidthSizeClass.EXPANDED
+        )
+
+        val actual = listOf(100f, 700f, 900f).map { width ->
+            WindowSizeClass.compute(dpWidth = width, dpHeight = 100f)
+        }.map { sizeClass ->
+            sizeClass.windowWidthSizeClass
+        }
+
+        assertEquals(expected, actual)
+    }
+
+    @Test
+    public fun testHeightSizeClass_construction() {
+        val expected = listOf(
+            WindowHeightSizeClass.COMPACT,
+            WindowHeightSizeClass.MEDIUM,
+            WindowHeightSizeClass.EXPANDED
+        )
+
+        val actual = listOf(100f, 500f, 900f).map { height ->
+            WindowSizeClass.compute(dpHeight = height, dpWidth = 100f)
+        }.map { sizeClass ->
+            sizeClass.windowHeightSizeClass
+        }
+
+        assertEquals(expected, actual)
+    }
+
+    @Test
+    public fun testEqualsImpliesHashCode() {
+        val first = WindowSizeClass.compute(100f, 500f)
+        val second = WindowSizeClass.compute(100f, 500f)
+
+        assertEquals(first, second)
+        assertEquals(first.hashCode(), second.hashCode())
+    }
+}
diff --git a/window/window-core/src/test/java/androidx/window/core/layout/WindowWidthSizeClassTest.kt b/window/window-core/src/test/java/androidx/window/core/layout/WindowWidthSizeClassTest.kt
new file mode 100644
index 0000000..1050bef
--- /dev/null
+++ b/window/window-core/src/test/java/androidx/window/core/layout/WindowWidthSizeClassTest.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.window.core.layout
+
+import androidx.window.core.layout.WindowWidthSizeClass.Companion.COMPACT
+import androidx.window.core.layout.WindowWidthSizeClass.Companion.EXPANDED
+import androidx.window.core.layout.WindowWidthSizeClass.Companion.MEDIUM
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class WindowWidthSizeClassTest {
+
+    @Test
+    fun testComputeForDifferentBuckets() {
+        val expected = listOf(COMPACT, MEDIUM, EXPANDED)
+        val actual = listOf(599f, 839f, 840f)
+            .map { value -> WindowWidthSizeClass.compute(value) }
+        assertEquals(expected, actual)
+    }
+
+    @Test
+    fun testToStringContainsName() {
+        val expected = listOf("COMPACT", "MEDIUM", "EXPANDED")
+            .map { value -> "WindowWidthSizeClass: $value" }
+        val actual = listOf(599f, 839f, 840f)
+            .map { value -> WindowWidthSizeClass.compute(value).toString() }
+        assertEquals(expected, actual)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun testInvalidSizeBucket() {
+        WindowWidthSizeClass.compute(-1f)
+    }
+}
\ No newline at end of file
diff --git a/window/window-java/build.gradle b/window/window-java/build.gradle
index eb27a1f..5390e0c 100644
--- a/window/window-java/build.gradle
+++ b/window/window-java/build.gradle
@@ -33,8 +33,8 @@
     testImplementation(libs.testCore)
     testImplementation(libs.testRunner)
     testImplementation(libs.junit)
-    testImplementation(libs.mockitoCore)
-    testImplementation(libs.mockitoKotlin)
+    testImplementation(libs.mockitoCore4)
+    testImplementation(libs.mockitoKotlin4)
     testImplementation(libs.kotlinCoroutinesTest)
 
     androidTestImplementation(libs.testExtJunit)
diff --git a/window/window-java/src/test/java/androidx/window/java/layout/WindowInfoTrackerCallbackAdapterTest.kt b/window/window-java/src/test/java/androidx/window/java/layout/WindowInfoTrackerCallbackAdapterTest.kt
index da19ea4..639e073 100644
--- a/window/window-java/src/test/java/androidx/window/java/layout/WindowInfoTrackerCallbackAdapterTest.kt
+++ b/window/window-java/src/test/java/androidx/window/java/layout/WindowInfoTrackerCallbackAdapterTest.kt
@@ -19,11 +19,11 @@
 import androidx.core.util.Consumer
 import androidx.window.layout.WindowInfoTracker
 import androidx.window.layout.WindowLayoutInfo
-import com.nhaarman.mockitokotlin2.any
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.verify
-import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
-import com.nhaarman.mockitokotlin2.whenever
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyNoMoreInteractions
+import org.mockito.kotlin.whenever
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.runBlocking
 import org.junit.Test
diff --git a/window/window/build.gradle b/window/window/build.gradle
index 7eb8cff..f045acf 100644
--- a/window/window/build.gradle
+++ b/window/window/build.gradle
@@ -57,8 +57,8 @@
     testImplementation(libs.junit)
     testImplementation(libs.truth)
     testImplementation(libs.robolectric)
-    testImplementation(libs.mockitoCore)
-    testImplementation(libs.mockitoKotlin)
+    testImplementation(libs.mockitoCore4)
+    testImplementation(libs.mockitoKotlin4)
     testImplementation(libs.kotlinCoroutinesTest)
     testImplementation(compileOnly(project(":window:sidecar:sidecar")))
     testImplementation(compileOnly("androidx.window.extensions:extensions:1.1.0-alpha02"))
diff --git a/window/window/proguard-rules.pro b/window/window/proguard-rules.pro
index 47996d9..609e1cc1 100644
--- a/window/window/proguard-rules.pro
+++ b/window/window/proguard-rules.pro
@@ -15,11 +15,11 @@
 # A rule that will keep classes that implement SidecarInterface$SidecarCallback if Sidecar seems
 # be used. See b/157286362 and b/165268619 for details.
 # TODO(b/208543178) investigate how to pass header jar to R8 so we don't need this rule
--if class androidx.window.layout.SidecarCompat {
-  public *** setExtensionCallback(androidx.window.layout.ExtensionInterfaceCompat$ExtensionCallbackInterface);
+-if class androidx.window.layout.adapter.sidecar.SidecarCompat {
+  public *** setExtensionCallback(androidx.window.layout.adapter.sidecar.ExtensionInterfaceCompat$ExtensionCallbackInterface);
 }
--keep class androidx.window.layout.SidecarCompat$TranslatingCallback,
- androidx.window.layout.DistinctElementSidecarCallback {
+-keep class androidx.window.layout.adapter.sidecar.SidecarCompat$TranslatingCallback,
+ androidx.window.layout.adapter.sidecar.DistinctElementSidecarCallback {
   public *** onDeviceStateChanged(androidx.window.sidecar.SidecarDeviceState);
   public *** onWindowLayoutChanged(android.os.IBinder, androidx.window.sidecar.SidecarWindowLayoutInfo);
 }
\ No newline at end of file
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 86bb0db..16dbdf6 100644
--- a/window/window/src/androidTest/java/androidx/window/layout/WindowInfoTrackerImplTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/layout/WindowInfoTrackerImplTest.kt
@@ -21,6 +21,7 @@
 import androidx.test.ext.junit.rules.ActivityScenarioRule
 import androidx.window.TestActivity
 import androidx.window.TestConsumer
+import androidx.window.layout.adapter.WindowBackend
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.test.TestScope
diff --git a/window/window/src/androidTest/java/androidx/window/layout/ExtensionWindowLayoutInfoBackendTest.kt b/window/window/src/androidTest/java/androidx/window/layout/adapter/extensions/ExtensionWindowLayoutInfoBackendTest.kt
similarity index 97%
rename from window/window/src/androidTest/java/androidx/window/layout/ExtensionWindowLayoutInfoBackendTest.kt
rename to window/window/src/androidTest/java/androidx/window/layout/adapter/extensions/ExtensionWindowLayoutInfoBackendTest.kt
index a5f8cbd..e7ca275 100644
--- a/window/window/src/androidTest/java/androidx/window/layout/ExtensionWindowLayoutInfoBackendTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/layout/adapter/extensions/ExtensionWindowLayoutInfoBackendTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.window.layout
+package androidx.window.layout.adapter.extensions
 
 import android.annotation.SuppressLint
 import android.app.Activity
@@ -28,7 +28,7 @@
 import androidx.window.extensions.layout.FoldingFeature.STATE_FLAT
 import androidx.window.extensions.layout.FoldingFeature.TYPE_HINGE
 import androidx.window.extensions.layout.WindowLayoutComponent
-import androidx.window.layout.ExtensionsWindowLayoutInfoAdapter.translate
+import androidx.window.layout.adapter.extensions.ExtensionsWindowLayoutInfoAdapter.translate
 import com.nhaarman.mockitokotlin2.any
 import com.nhaarman.mockitokotlin2.argumentCaptor
 import com.nhaarman.mockitokotlin2.eq
@@ -44,6 +44,8 @@
 import androidx.window.extensions.layout.FoldingFeature as OEMFoldingFeature
 import androidx.window.extensions.layout.WindowLayoutInfo as OEMWindowLayoutInfo
 import java.util.function.Consumer as JavaConsumer
+import androidx.window.layout.WindowLayoutInfo
+import androidx.window.layout.WindowMetricsCalculatorCompat
 import com.nhaarman.mockitokotlin2.times
 
 class ExtensionWindowLayoutInfoBackendTest {
diff --git a/window/window/src/androidTest/java/androidx/window/layout/ExtensionsWindowLayoutInfoAdapterTest.kt b/window/window/src/androidTest/java/androidx/window/layout/adapter/extensions/ExtensionsWindowLayoutInfoAdapterTest.kt
similarity index 96%
rename from window/window/src/androidTest/java/androidx/window/layout/ExtensionsWindowLayoutInfoAdapterTest.kt
rename to window/window/src/androidTest/java/androidx/window/layout/adapter/extensions/ExtensionsWindowLayoutInfoAdapterTest.kt
index 81c9235..f1d8f53 100644
--- a/window/window/src/androidTest/java/androidx/window/layout/ExtensionsWindowLayoutInfoAdapterTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/layout/adapter/extensions/ExtensionsWindowLayoutInfoAdapterTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.window.layout
+package androidx.window.layout.adapter.extensions
 
 import android.graphics.Rect
 import androidx.test.ext.junit.rules.ActivityScenarioRule
@@ -34,6 +34,8 @@
 import org.junit.Test
 import androidx.window.extensions.layout.FoldingFeature as OEMFoldingFeature
 import androidx.window.extensions.layout.WindowLayoutInfo as OEMWindowLayoutInfo
+import androidx.window.layout.HardwareFoldingFeature
+import androidx.window.layout.WindowLayoutInfo
 
 class ExtensionsWindowLayoutInfoAdapterTest {
 
diff --git a/window/window/src/androidTest/java/androidx/window/layout/SidecarAdapterTest.kt b/window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarAdapterTest.kt
similarity index 97%
rename from window/window/src/androidTest/java/androidx/window/layout/SidecarAdapterTest.kt
rename to window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarAdapterTest.kt
index 5d42f22..d9c3924 100644
--- a/window/window/src/androidTest/java/androidx/window/layout/SidecarAdapterTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarAdapterTest.kt
@@ -16,12 +16,14 @@
 // Sidecar is deprecated but consuming code must be maintained for compatibility reasons
 @file:Suppress("DEPRECATION")
 
-package androidx.window.layout
+package androidx.window.layout.adapter.sidecar
 
 import android.graphics.Rect
 import androidx.window.core.Bounds
 import androidx.window.layout.FoldingFeature.State.Companion.FLAT
+import androidx.window.layout.HardwareFoldingFeature
 import androidx.window.layout.HardwareFoldingFeature.Type.Companion.FOLD
+import androidx.window.layout.WindowLayoutInfo
 import androidx.window.sidecar.SidecarDeviceState
 import androidx.window.sidecar.SidecarDisplayFeature
 import androidx.window.sidecar.SidecarWindowLayoutInfo
diff --git a/window/window/src/androidTest/java/androidx/window/layout/SidecarCompatDeviceTest.kt b/window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarCompatDeviceTest.kt
similarity index 96%
rename from window/window/src/androidTest/java/androidx/window/layout/SidecarCompatDeviceTest.kt
rename to window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarCompatDeviceTest.kt
index 4079498..783c535 100644
--- a/window/window/src/androidTest/java/androidx/window/layout/SidecarCompatDeviceTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarCompatDeviceTest.kt
@@ -16,7 +16,7 @@
 // Sidecar is deprecated but consuming code must be maintained for compatibility reasons
 @file:Suppress("DEPRECATION")
 
-package androidx.window.layout
+package androidx.window.layout.adapter.sidecar
 
 import android.content.pm.ActivityInfo
 import androidx.test.core.app.ActivityScenario
@@ -27,10 +27,12 @@
 import androidx.window.WindowTestBase
 import androidx.window.core.VerificationMode.QUIET
 import androidx.window.core.Version
-import androidx.window.layout.ExtensionInterfaceCompat.ExtensionCallbackInterface
+import androidx.window.layout.HardwareFoldingFeature
+import androidx.window.layout.adapter.sidecar.ExtensionInterfaceCompat.ExtensionCallbackInterface
 import androidx.window.layout.HardwareFoldingFeature.Type
 import androidx.window.layout.HardwareFoldingFeature.Type.Companion.FOLD
 import androidx.window.layout.HardwareFoldingFeature.Type.Companion.HINGE
+import androidx.window.layout.WindowLayoutInfo
 import androidx.window.sidecar.SidecarDisplayFeature
 import androidx.window.sidecar.SidecarWindowLayoutInfo
 import com.nhaarman.mockitokotlin2.any
diff --git a/window/window/src/androidTest/java/androidx/window/layout/SidecarCompatTest.kt b/window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarCompatTest.kt
similarity index 97%
rename from window/window/src/androidTest/java/androidx/window/layout/SidecarCompatTest.kt
rename to window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarCompatTest.kt
index b43f083..0772127 100644
--- a/window/window/src/androidTest/java/androidx/window/layout/SidecarCompatTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarCompatTest.kt
@@ -16,7 +16,7 @@
 // Sidecar is deprecated but consuming code must be maintained for compatibility reasons
 @file:Suppress("DEPRECATION")
 
-package androidx.window.layout
+package androidx.window.layout.adapter.sidecar
 
 import android.app.Activity
 import android.content.Context
@@ -30,12 +30,15 @@
 import androidx.window.TestWindow
 import androidx.window.WindowTestBase
 import androidx.window.core.Bounds
-import androidx.window.layout.ExtensionInterfaceCompat.ExtensionCallbackInterface
+import androidx.window.layout.FoldingFeature
+import androidx.window.layout.adapter.sidecar.ExtensionInterfaceCompat.ExtensionCallbackInterface
 import androidx.window.layout.FoldingFeature.State.Companion.FLAT
 import androidx.window.layout.FoldingFeature.State.Companion.HALF_OPENED
+import androidx.window.layout.HardwareFoldingFeature
 import androidx.window.layout.HardwareFoldingFeature.Type.Companion.FOLD
 import androidx.window.layout.TestFoldingFeatureUtil.invalidFoldBounds
 import androidx.window.layout.TestFoldingFeatureUtil.validFoldBound
+import androidx.window.layout.WindowLayoutInfo
 import androidx.window.sidecar.SidecarDeviceState
 import androidx.window.sidecar.SidecarDisplayFeature
 import androidx.window.sidecar.SidecarInterface
@@ -380,11 +383,13 @@
     fun testOnWindowLayoutInfoChanged_emitNewValueWhenResubscribe() {
         val layoutInfo = SidecarWindowLayoutInfo()
         val expectedLayoutInfo = WindowLayoutInfo(listOf())
-        val expectedLayoutInfo2 = WindowLayoutInfo(listOf(HardwareFoldingFeature(
+        val expectedLayoutInfo2 = WindowLayoutInfo(listOf(
+            HardwareFoldingFeature(
             Bounds(validFoldBound(WINDOW_BOUNDS)),
             FOLD,
             HALF_OPENED
-        )))
+        )
+        ))
         val listener = mock<ExtensionCallbackInterface>()
         sidecarCompat.setExtensionCallback(listener)
         whenever(sidecarCompat.sidecar!!.getWindowLayoutInfo(any()))
@@ -395,12 +400,14 @@
         sidecarCompat.onWindowLayoutChangeListenerRemoved(activity)
         // change the value for new subscriber
         whenever(sidecarCompat.sidecar!!.getWindowLayoutInfo(any()))
-            .thenReturn(newWindowLayoutInfo(listOf(
+            .thenReturn(
+                newWindowLayoutInfo(listOf(
                 newDisplayFeature(
                     validFoldBound(WINDOW_BOUNDS),
                     SidecarDisplayFeature.TYPE_FOLD
                 )
-            )))
+            ))
+            )
         // resubscribe
         sidecarCompat.onWindowLayoutChangeListenerAdded(activity)
         verify(listener).onWindowLayoutChanged(activity, expectedLayoutInfo2)
diff --git a/window/window/src/androidTest/java/androidx/window/layout/SidecarWindowBackendIntegrationTest.kt b/window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarWindowBackendIntegrationTest.kt
similarity index 96%
rename from window/window/src/androidTest/java/androidx/window/layout/SidecarWindowBackendIntegrationTest.kt
rename to window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarWindowBackendIntegrationTest.kt
index 2834b25..bde27e9 100644
--- a/window/window/src/androidTest/java/androidx/window/layout/SidecarWindowBackendIntegrationTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarWindowBackendIntegrationTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.window.layout
+package androidx.window.layout.adapter.sidecar
 
 import android.content.Context
 import android.content.pm.ActivityInfo
@@ -32,7 +32,11 @@
 import androidx.window.WindowTestBase
 import androidx.window.core.Version
 import androidx.window.extensions.layout.FoldingFeature as ExtensionFoldingFeature
-import androidx.window.layout.ExtensionInterfaceCompat.ExtensionCallbackInterface
+import androidx.window.layout.DisplayFeature
+import androidx.window.layout.FoldingFeature
+import androidx.window.layout.WindowLayoutInfo
+import androidx.window.layout.WindowMetricsCalculator
+import androidx.window.layout.adapter.sidecar.ExtensionInterfaceCompat.ExtensionCallbackInterface
 import com.nhaarman.mockitokotlin2.any
 import com.nhaarman.mockitokotlin2.argThat
 import com.nhaarman.mockitokotlin2.atLeastOnce
diff --git a/window/window/src/androidTest/java/androidx/window/layout/SidecarWindowBackendTest.kt b/window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarWindowBackendTest.kt
similarity index 97%
rename from window/window/src/androidTest/java/androidx/window/layout/SidecarWindowBackendTest.kt
rename to window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarWindowBackendTest.kt
index f851c8a..4cd3c48 100644
--- a/window/window/src/androidTest/java/androidx/window/layout/SidecarWindowBackendTest.kt
+++ b/window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/SidecarWindowBackendTest.kt
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package androidx.window.layout
+package androidx.window.layout.adapter.sidecar
 
 import android.app.Activity
 import android.content.Context
@@ -24,8 +24,11 @@
 import androidx.window.TestConsumer
 import androidx.window.WindowTestBase
 import androidx.window.core.Bounds
+import androidx.window.layout.DisplayFeature
 import androidx.window.layout.FoldingFeature.State.Companion.FLAT
+import androidx.window.layout.HardwareFoldingFeature
 import androidx.window.layout.HardwareFoldingFeature.Type.Companion.HINGE
+import androidx.window.layout.WindowLayoutInfo
 import com.google.common.util.concurrent.MoreExecutors
 import com.nhaarman.mockitokotlin2.eq
 import com.nhaarman.mockitokotlin2.mock
diff --git a/window/window/src/androidTest/java/androidx/window/layout/TranslatorTestInterface.kt b/window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/TranslatorTestInterface.kt
similarity index 95%
rename from window/window/src/androidTest/java/androidx/window/layout/TranslatorTestInterface.kt
rename to window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/TranslatorTestInterface.kt
index 672ebbf..c8dac55 100644
--- a/window/window/src/androidTest/java/androidx/window/layout/TranslatorTestInterface.kt
+++ b/window/window/src/androidTest/java/androidx/window/layout/adapter/sidecar/TranslatorTestInterface.kt
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package androidx.window.layout
+package androidx.window.layout.adapter.sidecar
 
 /**
  * An interface to ensure the same tests are run for translating from value objects in
diff --git a/window/window/src/main/java/androidx/window/layout/DisplayFeature.kt b/window/window/src/main/java/androidx/window/layout/DisplayFeature.kt
index 814b057..d58b650 100644
--- a/window/window/src/main/java/androidx/window/layout/DisplayFeature.kt
+++ b/window/window/src/main/java/androidx/window/layout/DisplayFeature.kt
@@ -28,10 +28,10 @@
  *
  * @see FoldingFeature Represents a screen fold that intersects the application window.
  */
-public interface DisplayFeature {
+interface DisplayFeature {
     /**
      * The bounding rectangle of the feature within the application window
      * in the window coordinate space.
      */
-    public val bounds: Rect
+    val bounds: Rect
 }
diff --git a/window/window/src/main/java/androidx/window/layout/FoldingFeature.kt b/window/window/src/main/java/androidx/window/layout/FoldingFeature.kt
index 52cb1f1..9cbcc71 100644
--- a/window/window/src/main/java/androidx/window/layout/FoldingFeature.kt
+++ b/window/window/src/main/java/androidx/window/layout/FoldingFeature.kt
@@ -20,18 +20,18 @@
  * or a hinge between two physical display panels.
  *
  */
-public interface FoldingFeature : DisplayFeature {
+interface FoldingFeature : DisplayFeature {
 
     /**
      * Represents how the hinge might occlude content.
      */
-    public class OcclusionType private constructor(private val description: String) {
+    class OcclusionType private constructor(private val description: String) {
 
         override fun toString(): String {
             return description
         }
 
-        public companion object {
+        companion object {
             /**
              * The [FoldingFeature] does not occlude the content in any way. One example is a flat
              * continuous fold where content can stretch across the fold. Another example is a hinge
@@ -39,7 +39,7 @@
              * across both displays, but fully visible.
              */
             @JvmField
-            public val NONE: OcclusionType = OcclusionType("NONE")
+            val NONE: OcclusionType = OcclusionType("NONE")
 
             /**
              * The [FoldingFeature] occludes all content. One example is a hinge that is considered
@@ -49,45 +49,45 @@
              * and text.
              */
             @JvmField
-            public val FULL: OcclusionType = OcclusionType("FULL")
+            val FULL: OcclusionType = OcclusionType("FULL")
         }
     }
 
     /**
      * Represents the axis for which the [FoldingFeature] runs parallel to.
      */
-    public class Orientation private constructor(private val description: String) {
+    class Orientation private constructor(private val description: String) {
 
         override fun toString(): String {
             return description
         }
 
-        public companion object {
+        companion object {
 
             /**
              * The height of the [FoldingFeature] is greater than or equal to the width.
              */
             @JvmField
-            public val VERTICAL: Orientation = Orientation("VERTICAL")
+            val VERTICAL: Orientation = Orientation("VERTICAL")
 
             /**
              * The width of the [FoldingFeature] is greater than the height.
              */
             @JvmField
-            public val HORIZONTAL: Orientation = Orientation("HORIZONTAL")
+            val HORIZONTAL: Orientation = Orientation("HORIZONTAL")
         }
     }
 
     /**
      * Represents the [State] of the [FoldingFeature].
      */
-    public class State private constructor(private val description: String) {
+    class State private constructor(private val description: String) {
 
         override fun toString(): String {
             return description
         }
 
-        public companion object {
+        companion object {
             /**
              * The foldable device is completely open, the screen space that is presented to the
              * user is flat. See the
@@ -95,7 +95,7 @@
              * section in the official documentation for visual samples and references.
              */
             @JvmField
-            public val FLAT: State = State("FLAT")
+            val FLAT: State = State("FLAT")
 
             /**
              * The foldable device's hinge is in an intermediate position between opened and closed
@@ -105,7 +105,7 @@
              * section in the official documentation for visual samples and references.
              */
             @JvmField
-            public val HALF_OPENED: State = State("HALF_OPENED")
+            val HALF_OPENED: State = State("HALF_OPENED")
         }
     }
 
@@ -128,7 +128,7 @@
      * @return `true` if the feature splits the display into two areas, `false`
      * otherwise.
      */
-    public val isSeparating: Boolean
+    val isSeparating: Boolean
 
     /**
      * Calculates the occlusion mode to determine if a [FoldingFeature] occludes a part of
@@ -149,16 +149,16 @@
      * @return [FoldingFeature.OcclusionType.NONE] if the [FoldingFeature] has empty
      * bounds.
      */
-    public val occlusionType: OcclusionType
+    val occlusionType: OcclusionType
 
     /**
      * Returns [FoldingFeature.Orientation.HORIZONTAL] if the width is greater than the
      * height, [FoldingFeature.Orientation.VERTICAL] otherwise.
      */
-    public val orientation: Orientation
+    val orientation: Orientation
 
     /**
      * Returns the [FoldingFeature.State] for the [FoldingFeature]
      */
-    public val state: FoldingFeature.State
+    val state: State
 }
diff --git a/window/window/src/main/java/androidx/window/layout/WindowInfoTracker.kt b/window/window/src/main/java/androidx/window/layout/WindowInfoTracker.kt
index b58f640..3ae9ec8 100644
--- a/window/window/src/main/java/androidx/window/layout/WindowInfoTracker.kt
+++ b/window/window/src/main/java/androidx/window/layout/WindowInfoTracker.kt
@@ -22,13 +22,16 @@
 import androidx.annotation.RestrictTo
 import androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP
 import androidx.window.core.ConsumerAdapter
+import androidx.window.layout.adapter.WindowBackend
+import androidx.window.layout.adapter.extensions.ExtensionWindowLayoutInfoBackend
+import androidx.window.layout.adapter.sidecar.SidecarWindowBackend
 import kotlinx.coroutines.flow.Flow
 
 /**
  * An interface to provide all the relevant info about a [android.view.Window].
  * @see WindowInfoTracker.getOrCreate to get an instance.
  */
-public interface WindowInfoTracker {
+interface WindowInfoTracker {
 
     /**
      * A [Flow] of [WindowLayoutInfo] that contains all the available features. A [WindowLayoutInfo]
@@ -47,9 +50,9 @@
      * @see WindowLayoutInfo
      * @see DisplayFeature
      */
-    public fun windowLayoutInfo(activity: Activity): Flow<WindowLayoutInfo>
+    fun windowLayoutInfo(activity: Activity): Flow<WindowLayoutInfo>
 
-    public companion object {
+    companion object {
 
         private val DEBUG = false
         private val TAG = WindowInfoTracker::class.simpleName
@@ -83,7 +86,7 @@
          */
         @JvmName("getOrCreate")
         @JvmStatic
-        public fun getOrCreate(context: Context): WindowInfoTracker {
+        fun getOrCreate(context: Context): WindowInfoTracker {
             val backend = extensionBackend ?: SidecarWindowBackend.getInstance(context)
             val repo = WindowInfoTrackerImpl(WindowMetricsCalculatorCompat, backend)
             return decorator.decorate(repo)
@@ -91,25 +94,25 @@
 
         @JvmStatic
         @RestrictTo(LIBRARY_GROUP)
-        public fun overrideDecorator(overridingDecorator: WindowInfoTrackerDecorator) {
+        fun overrideDecorator(overridingDecorator: WindowInfoTrackerDecorator) {
             decorator = overridingDecorator
         }
 
         @JvmStatic
         @RestrictTo(LIBRARY_GROUP)
-        public fun reset() {
+        fun reset() {
             decorator = EmptyDecorator
         }
     }
 }
 
 @RestrictTo(LIBRARY_GROUP)
-public interface WindowInfoTrackerDecorator {
+interface WindowInfoTrackerDecorator {
     /**
      * Returns an instance of [WindowInfoTracker] associated to the [Activity]
      */
     @RestrictTo(LIBRARY_GROUP)
-    public fun decorate(tracker: WindowInfoTracker): WindowInfoTracker
+    fun decorate(tracker: WindowInfoTracker): WindowInfoTracker
 }
 
 private object EmptyDecorator : WindowInfoTrackerDecorator {
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 3582ac5..16efaad 100644
--- a/window/window/src/main/java/androidx/window/layout/WindowInfoTrackerImpl.kt
+++ b/window/window/src/main/java/androidx/window/layout/WindowInfoTrackerImpl.kt
@@ -19,6 +19,7 @@
 import android.app.Activity
 import android.content.Context
 import androidx.core.util.Consumer
+import androidx.window.layout.adapter.WindowBackend
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.callbackFlow
diff --git a/window/window/src/main/java/androidx/window/layout/WindowLayoutInfo.kt b/window/window/src/main/java/androidx/window/layout/WindowLayoutInfo.kt
index 9e4bcd6..81e6e46 100644
--- a/window/window/src/main/java/androidx/window/layout/WindowLayoutInfo.kt
+++ b/window/window/src/main/java/androidx/window/layout/WindowLayoutInfo.kt
@@ -16,7 +16,7 @@
 package androidx.window.layout
 
 import androidx.annotation.RestrictTo
-import androidx.annotation.RestrictTo.Scope.TESTS
+import androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP
 
 /**
  * Contains the list of [DisplayFeature]-s located within the window. For example, a hinge or
@@ -27,20 +27,12 @@
  * positions and sizes can change if the window is moved or resized on screen.
  * @see WindowInfoTracker.windowLayoutInfo
  */
-public class WindowLayoutInfo {
-
+class WindowLayoutInfo @RestrictTo(LIBRARY_GROUP) constructor(
     /**
      * [displayFeatures] all the [DisplayFeature] within the window.
      */
-    public val displayFeatures: List<DisplayFeature>
-
-    /**
-     * @suppress
-     */
-    @RestrictTo(TESTS)
-    public constructor(displayFeatures: List<DisplayFeature>) {
-        this.displayFeatures = displayFeatures
-    }
+    val displayFeatures: List<DisplayFeature>
+) {
 
     override fun toString(): String {
         return displayFeatures.joinToString(
diff --git a/window/window/src/main/java/androidx/window/layout/WindowMetrics.kt b/window/window/src/main/java/androidx/window/layout/WindowMetrics.kt
index a410d62e..bbf6d69 100644
--- a/window/window/src/main/java/androidx/window/layout/WindowMetrics.kt
+++ b/window/window/src/main/java/androidx/window/layout/WindowMetrics.kt
@@ -33,15 +33,17 @@
  *
  * @see WindowMetricsCalculator
  */
-public class WindowMetrics internal constructor
-    (private val _bounds: Bounds, private val _windowInsetsCompat: WindowInsetsCompat) {
+class WindowMetrics internal constructor(
+    private val _bounds: Bounds,
+    private val _windowInsetsCompat: WindowInsetsCompat
+) {
 
     /**
      * An internal constructor for [WindowMetrics]
      * @suppress
      */
     @RestrictTo(TESTS)
-    public constructor(
+    constructor(
         bounds: Rect,
         insets: WindowInsetsCompat = WindowInsetsCompat.Builder().build()
         ) : this(Bounds(bounds), insets)
@@ -56,7 +58,7 @@
      *
      * @return window bounds in pixels.
      */
-    public val bounds: Rect
+    val bounds: Rect
         get() = _bounds.toRect()
 
     override fun toString(): String {
diff --git a/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculator.kt b/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculator.kt
index 3da6ff5..0de1501 100644
--- a/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculator.kt
+++ b/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculator.kt
@@ -89,14 +89,14 @@
         @ExperimentalWindowApi
         @JvmStatic
         @RestrictTo(RestrictTo.Scope.TESTS)
-        public fun overrideDecorator(overridingDecorator: WindowMetricsCalculatorDecorator) {
+        fun overrideDecorator(overridingDecorator: WindowMetricsCalculatorDecorator) {
             decorator = overridingDecorator::decorate
         }
 
         @ExperimentalWindowApi
         @JvmStatic
         @RestrictTo(RestrictTo.Scope.TESTS)
-        public fun reset() {
+        fun reset() {
             decorator = { it }
         }
     }
@@ -104,12 +104,12 @@
 
 @ExperimentalWindowApi
 @RestrictTo(RestrictTo.Scope.TESTS)
-public interface WindowMetricsCalculatorDecorator {
+interface WindowMetricsCalculatorDecorator {
 
     @ExperimentalWindowApi
     /**
      * Returns an instance of [WindowMetricsCalculator]
      */
     @RestrictTo(RestrictTo.Scope.TESTS)
-    public fun decorate(calculator: WindowMetricsCalculator): WindowMetricsCalculator
+    fun decorate(calculator: WindowMetricsCalculator): WindowMetricsCalculator
 }
\ No newline at end of file
diff --git a/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculatorCompat.kt b/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculatorCompat.kt
index 87089dd..4fa733a 100644
--- a/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculatorCompat.kt
+++ b/window/window/src/main/java/androidx/window/layout/WindowMetricsCalculatorCompat.kt
@@ -30,15 +30,15 @@
 import androidx.annotation.VisibleForTesting
 import androidx.core.view.WindowInsetsCompat
 import androidx.window.core.Bounds
-import androidx.window.layout.ActivityCompatHelperApi24.isInMultiWindowMode
-import androidx.window.layout.ActivityCompatHelperApi30.currentWindowBounds
-import androidx.window.layout.ActivityCompatHelperApi30.currentWindowInsets
-import androidx.window.layout.ActivityCompatHelperApi30.maximumWindowBounds
-import androidx.window.layout.DisplayCompatHelperApi17.getRealSize
-import androidx.window.layout.DisplayCompatHelperApi28.safeInsetBottom
-import androidx.window.layout.DisplayCompatHelperApi28.safeInsetLeft
-import androidx.window.layout.DisplayCompatHelperApi28.safeInsetRight
-import androidx.window.layout.DisplayCompatHelperApi28.safeInsetTop
+import androidx.window.layout.util.ActivityCompatHelperApi24.isInMultiWindowMode
+import androidx.window.layout.util.ActivityCompatHelperApi30.currentWindowBounds
+import androidx.window.layout.util.ActivityCompatHelperApi30.currentWindowInsets
+import androidx.window.layout.util.ActivityCompatHelperApi30.maximumWindowBounds
+import androidx.window.layout.util.DisplayCompatHelperApi17.getRealSize
+import androidx.window.layout.util.DisplayCompatHelperApi28.safeInsetBottom
+import androidx.window.layout.util.DisplayCompatHelperApi28.safeInsetLeft
+import androidx.window.layout.util.DisplayCompatHelperApi28.safeInsetRight
+import androidx.window.layout.util.DisplayCompatHelperApi28.safeInsetTop
 import java.lang.reflect.InvocationTargetException
 
 /**
diff --git a/window/window/src/main/java/androidx/window/layout/WindowBackend.kt b/window/window/src/main/java/androidx/window/layout/adapter/WindowBackend.kt
similarity index 85%
rename from window/window/src/main/java/androidx/window/layout/WindowBackend.kt
rename to window/window/src/main/java/androidx/window/layout/adapter/WindowBackend.kt
index 3ad8374..d799f4a 100644
--- a/window/window/src/main/java/androidx/window/layout/WindowBackend.kt
+++ b/window/window/src/main/java/androidx/window/layout/adapter/WindowBackend.kt
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package androidx.window.layout
+package androidx.window.layout.adapter
 
 import android.app.Activity
 import androidx.core.util.Consumer
+import androidx.window.layout.WindowLayoutInfo
 import java.util.concurrent.Executor
 
 /**
- * Backing interface for [WindowInfoTracker] instances that serve as the default
- * information supplier.
+ * Backing interface for [androidx.window.layout.WindowInfoTracker] instances that serve as the
+ * default information supplier.
  */
 internal interface WindowBackend {
     /**
diff --git a/window/window/src/main/java/androidx/window/layout/ExtensionWindowLayoutInfoBackend.kt b/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionWindowLayoutInfoBackend.kt
similarity index 95%
rename from window/window/src/main/java/androidx/window/layout/ExtensionWindowLayoutInfoBackend.kt
rename to window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionWindowLayoutInfoBackend.kt
index d8ff7b3..beebba0 100644
--- a/window/window/src/main/java/androidx/window/layout/ExtensionWindowLayoutInfoBackend.kt
+++ b/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionWindowLayoutInfoBackend.kt
@@ -14,18 +14,20 @@
  * limitations under the License.
  */
 
-package androidx.window.layout
+package androidx.window.layout.adapter.extensions
 
+import androidx.window.extensions.layout.WindowLayoutInfo as OEMWindowLayoutInfo
 import android.app.Activity
 import androidx.annotation.GuardedBy
 import androidx.core.util.Consumer
 import androidx.window.core.ConsumerAdapter
 import androidx.window.extensions.layout.WindowLayoutComponent
-import androidx.window.layout.ExtensionsWindowLayoutInfoAdapter.translate
+import androidx.window.layout.WindowLayoutInfo
+import androidx.window.layout.adapter.WindowBackend
+import androidx.window.layout.adapter.extensions.ExtensionsWindowLayoutInfoAdapter.translate
 import java.util.concurrent.Executor
 import java.util.concurrent.locks.ReentrantLock
 import kotlin.concurrent.withLock
-import androidx.window.extensions.layout.WindowLayoutInfo as OEMWindowLayoutInfo
 
 /**
  * A wrapper around [WindowLayoutComponent] that ensures
diff --git a/window/window/src/main/java/androidx/window/layout/ExtensionsWindowLayoutInfoAdapter.kt b/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionsWindowLayoutInfoAdapter.kt
similarity index 94%
rename from window/window/src/main/java/androidx/window/layout/ExtensionsWindowLayoutInfoAdapter.kt
rename to window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionsWindowLayoutInfoAdapter.kt
index d9464dd..2ba93ca 100644
--- a/window/window/src/main/java/androidx/window/layout/ExtensionsWindowLayoutInfoAdapter.kt
+++ b/window/window/src/main/java/androidx/window/layout/adapter/extensions/ExtensionsWindowLayoutInfoAdapter.kt
@@ -14,17 +14,20 @@
  * limitations under the License.
  */
 
-package androidx.window.layout
+package androidx.window.layout.adapter.extensions
 
-import android.app.Activity
-import androidx.window.core.Bounds
-import androidx.window.layout.FoldingFeature.State.Companion.FLAT
-import androidx.window.layout.FoldingFeature.State.Companion.HALF_OPENED
-import androidx.window.layout.HardwareFoldingFeature.Type.Companion.FOLD
-import androidx.window.layout.HardwareFoldingFeature.Type.Companion.HINGE
-import androidx.window.layout.WindowMetricsCalculatorCompat.computeCurrentWindowMetrics
 import androidx.window.extensions.layout.FoldingFeature as OEMFoldingFeature
 import androidx.window.extensions.layout.WindowLayoutInfo as OEMWindowLayoutInfo
+import android.app.Activity
+import androidx.window.core.Bounds
+import androidx.window.layout.FoldingFeature
+import androidx.window.layout.FoldingFeature.State.Companion.FLAT
+import androidx.window.layout.FoldingFeature.State.Companion.HALF_OPENED
+import androidx.window.layout.HardwareFoldingFeature
+import androidx.window.layout.HardwareFoldingFeature.Type.Companion.FOLD
+import androidx.window.layout.HardwareFoldingFeature.Type.Companion.HINGE
+import androidx.window.layout.WindowLayoutInfo
+import androidx.window.layout.WindowMetricsCalculatorCompat.computeCurrentWindowMetrics
 
 internal object ExtensionsWindowLayoutInfoAdapter {
 
diff --git a/window/window/src/main/java/androidx/window/layout/DistinctElementSidecarCallback.java b/window/window/src/main/java/androidx/window/layout/adapter/sidecar/DistinctElementSidecarCallback.java
similarity index 83%
rename from window/window/src/main/java/androidx/window/layout/DistinctElementSidecarCallback.java
rename to window/window/src/main/java/androidx/window/layout/adapter/sidecar/DistinctElementSidecarCallback.java
index 9f72e79..83c851b 100644
--- a/window/window/src/main/java/androidx/window/layout/DistinctElementSidecarCallback.java
+++ b/window/window/src/main/java/androidx/window/layout/adapter/sidecar/DistinctElementSidecarCallback.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.window.layout;
+package androidx.window.layout.adapter.sidecar;
 
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
@@ -36,11 +36,18 @@
  * duplicates. This class uses [SidecarAdapter] to compute equality since the methods
  * [Object.equals] and [Object.hashCode] may not have been overridden.
  *
+ * This is a Java class because in Kotlin we would not be able to ignore [null] values.
+ *
+ * Sidecar is currently deprecated and we do not plan on developing it further. Sidecar is still
+ * supported for basic [FoldingFeature]. The deprecation lint warning is suppressed since it is
+ * picking up the Sidecar deprecations.
+ *
  * NOTE: If you change the name of this class, you must update the proguard file.
  * @hide
  */
+@SuppressWarnings("deprecation") // Sidecar is deprecated but we still support it.
 @RestrictTo(LIBRARY_GROUP)
-class DistinctElementSidecarCallback implements SidecarCallback {
+public class DistinctElementSidecarCallback implements SidecarCallback {
     private final Object mLock = new Object();
 
     @GuardedBy("mLock")
@@ -58,7 +65,7 @@
     }
 
     @VisibleForTesting
-    DistinctElementSidecarCallback(@NonNull SidecarCallback callback) {
+    public DistinctElementSidecarCallback(@NonNull SidecarCallback callback) {
         mAdapter = new SidecarAdapter();
         mCallback = callback;
     }
diff --git a/window/window/src/main/java/androidx/window/layout/ExtensionInterfaceCompat.kt b/window/window/src/main/java/androidx/window/layout/adapter/sidecar/ExtensionInterfaceCompat.kt
similarity index 95%
rename from window/window/src/main/java/androidx/window/layout/ExtensionInterfaceCompat.kt
rename to window/window/src/main/java/androidx/window/layout/adapter/sidecar/ExtensionInterfaceCompat.kt
index 577d9d1..acb3ed3 100644
--- a/window/window/src/main/java/androidx/window/layout/ExtensionInterfaceCompat.kt
+++ b/window/window/src/main/java/androidx/window/layout/adapter/sidecar/ExtensionInterfaceCompat.kt
@@ -13,9 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package androidx.window.layout
+package androidx.window.layout.adapter.sidecar
 
 import android.app.Activity
+import androidx.window.layout.WindowLayoutInfo
 
 /**
  * Base interface for different extension versions that serves as an API compatibility wrapper.
diff --git a/window/window/src/main/java/androidx/window/layout/SidecarAdapter.kt b/window/window/src/main/java/androidx/window/layout/adapter/sidecar/SidecarAdapter.kt
similarity index 97%
rename from window/window/src/main/java/androidx/window/layout/SidecarAdapter.kt
rename to window/window/src/main/java/androidx/window/layout/adapter/sidecar/SidecarAdapter.kt
index 4d7eefe2..37b02fb 100644
--- a/window/window/src/main/java/androidx/window/layout/SidecarAdapter.kt
+++ b/window/window/src/main/java/androidx/window/layout/adapter/sidecar/SidecarAdapter.kt
@@ -15,7 +15,7 @@
  */
 @file:Suppress("DEPRECATION")
 
-package androidx.window.layout
+package androidx.window.layout.adapter.sidecar
 
 import android.annotation.SuppressLint
 import androidx.annotation.VisibleForTesting
@@ -23,9 +23,13 @@
 import androidx.window.core.SpecificationComputer.Companion.startSpecification
 import androidx.window.core.VerificationMode
 import androidx.window.core.VerificationMode.QUIET
+import androidx.window.layout.DisplayFeature
+import androidx.window.layout.FoldingFeature
+import androidx.window.layout.HardwareFoldingFeature
 import androidx.window.layout.HardwareFoldingFeature.Type.Companion.FOLD
 import androidx.window.layout.HardwareFoldingFeature.Type.Companion.HINGE
-import androidx.window.layout.SidecarWindowBackend.Companion.DEBUG
+import androidx.window.layout.WindowLayoutInfo
+import androidx.window.layout.adapter.sidecar.SidecarWindowBackend.Companion.DEBUG
 import androidx.window.sidecar.SidecarDeviceState
 import androidx.window.sidecar.SidecarDisplayFeature
 import androidx.window.sidecar.SidecarDisplayFeature.TYPE_FOLD
@@ -321,4 +325,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/window/window/src/main/java/androidx/window/layout/SidecarCompat.kt b/window/window/src/main/java/androidx/window/layout/adapter/sidecar/SidecarCompat.kt
similarity index 98%
rename from window/window/src/main/java/androidx/window/layout/SidecarCompat.kt
rename to window/window/src/main/java/androidx/window/layout/adapter/sidecar/SidecarCompat.kt
index 8c22494..233d710 100644
--- a/window/window/src/main/java/androidx/window/layout/SidecarCompat.kt
+++ b/window/window/src/main/java/androidx/window/layout/adapter/sidecar/SidecarCompat.kt
@@ -16,7 +16,7 @@
 // Sidecar is deprecated but we still need to support it.
 @file:Suppress("DEPRECATION")
 
-package androidx.window.layout
+package androidx.window.layout.adapter.sidecar
 
 import android.annotation.SuppressLint
 import android.app.Activity
@@ -32,8 +32,9 @@
 import androidx.core.util.Consumer
 import androidx.window.core.Version
 import androidx.window.core.Version.Companion.parse
-import androidx.window.layout.ExtensionInterfaceCompat.ExtensionCallbackInterface
-import androidx.window.layout.SidecarWindowBackend.Companion.DEBUG
+import androidx.window.layout.WindowLayoutInfo
+import androidx.window.layout.adapter.sidecar.ExtensionInterfaceCompat.ExtensionCallbackInterface
+import androidx.window.layout.adapter.sidecar.SidecarWindowBackend.Companion.DEBUG
 import androidx.window.sidecar.SidecarDeviceState
 import androidx.window.sidecar.SidecarDisplayFeature
 import androidx.window.sidecar.SidecarInterface
@@ -47,7 +48,7 @@
 
 /** Extension interface compatibility wrapper for v0.1 sidecar.  */
 internal class SidecarCompat @VisibleForTesting constructor(
-    @VisibleForTesting
+    @get:VisibleForTesting
     val sidecar: SidecarInterface?,
     private val sidecarAdapter: SidecarAdapter
 ) : ExtensionInterfaceCompat {
diff --git a/window/window/src/main/java/androidx/window/layout/SidecarWindowBackend.kt b/window/window/src/main/java/androidx/window/layout/adapter/sidecar/SidecarWindowBackend.kt
similarity index 97%
rename from window/window/src/main/java/androidx/window/layout/SidecarWindowBackend.kt
rename to window/window/src/main/java/androidx/window/layout/adapter/sidecar/SidecarWindowBackend.kt
index 745e030..fa9f015 100644
--- a/window/window/src/main/java/androidx/window/layout/SidecarWindowBackend.kt
+++ b/window/window/src/main/java/androidx/window/layout/adapter/sidecar/SidecarWindowBackend.kt
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package androidx.window.layout
+package androidx.window.layout.adapter.sidecar
 
 import android.annotation.SuppressLint
 import android.app.Activity
@@ -23,7 +23,9 @@
 import androidx.annotation.VisibleForTesting
 import androidx.core.util.Consumer
 import androidx.window.core.Version
-import androidx.window.layout.ExtensionInterfaceCompat.ExtensionCallbackInterface
+import androidx.window.layout.WindowLayoutInfo
+import androidx.window.layout.adapter.WindowBackend
+import androidx.window.layout.adapter.sidecar.ExtensionInterfaceCompat.ExtensionCallbackInterface
 import java.util.concurrent.CopyOnWriteArrayList
 import java.util.concurrent.Executor
 import java.util.concurrent.locks.ReentrantLock
diff --git a/window/window/src/main/java/androidx/window/layout/ActivityCompatHelper.kt b/window/window/src/main/java/androidx/window/layout/util/ActivityCompatHelper.kt
similarity index 94%
rename from window/window/src/main/java/androidx/window/layout/ActivityCompatHelper.kt
rename to window/window/src/main/java/androidx/window/layout/util/ActivityCompatHelper.kt
index ea940df..aafd1f9 100644
--- a/window/window/src/main/java/androidx/window/layout/ActivityCompatHelper.kt
+++ b/window/window/src/main/java/androidx/window/layout/util/ActivityCompatHelper.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.window.layout
+package androidx.window.layout.util
 
 import android.app.Activity
 import android.graphics.Rect
@@ -45,7 +45,7 @@
      * @DoNotInline required for implementation-specific class method to prevent it from being
      * inlined.
      *
-     * @see WindowMetrics.getWindowInsets
+     * @see androidx.window.layout.WindowMetrics.getWindowInsets
      */
     @DoNotInline
     fun currentWindowInsets(activity: Activity): WindowInsetsCompat {
diff --git a/window/window/src/main/java/androidx/window/layout/DisplayCompatHelper.kt b/window/window/src/main/java/androidx/window/layout/util/DisplayCompatHelper.kt
similarity index 97%
rename from window/window/src/main/java/androidx/window/layout/DisplayCompatHelper.kt
rename to window/window/src/main/java/androidx/window/layout/util/DisplayCompatHelper.kt
index 5c9fba8..8532337 100644
--- a/window/window/src/main/java/androidx/window/layout/DisplayCompatHelper.kt
+++ b/window/window/src/main/java/androidx/window/layout/util/DisplayCompatHelper.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.window.layout
+package androidx.window.layout.util
 
 import android.graphics.Point
 import android.os.Build
diff --git a/window/window/src/test/java/androidx/window/core/ConsumerAdapterTest.kt b/window/window/src/test/java/androidx/window/core/ConsumerAdapterTest.kt
index 1cf2db2..57709d2 100644
--- a/window/window/src/test/java/androidx/window/core/ConsumerAdapterTest.kt
+++ b/window/window/src/test/java/androidx/window/core/ConsumerAdapterTest.kt
@@ -18,7 +18,7 @@
 
 import android.annotation.SuppressLint
 import android.app.Activity
-import com.nhaarman.mockitokotlin2.mock
+import org.mockito.kotlin.mock
 import java.util.function.Consumer
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
diff --git a/window/window/src/test/java/androidx/window/core/SpecificationComputerTest.kt b/window/window/src/test/java/androidx/window/core/SpecificationComputerTest.kt
index 376a7dc..a527f04 100644
--- a/window/window/src/test/java/androidx/window/core/SpecificationComputerTest.kt
+++ b/window/window/src/test/java/androidx/window/core/SpecificationComputerTest.kt
@@ -20,9 +20,9 @@
 import androidx.window.core.VerificationMode.LOG
 import androidx.window.core.VerificationMode.QUIET
 import androidx.window.core.VerificationMode.STRICT
-import com.nhaarman.mockitokotlin2.eq
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.verify
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotNull
 import org.junit.Assert.assertNull
diff --git a/window/window/src/test/java/androidx/window/embedding/ActivityStackTest.kt b/window/window/src/test/java/androidx/window/embedding/ActivityStackTest.kt
index 491d35e..2ef71b1 100644
--- a/window/window/src/test/java/androidx/window/embedding/ActivityStackTest.kt
+++ b/window/window/src/test/java/androidx/window/embedding/ActivityStackTest.kt
@@ -18,7 +18,7 @@
 
 import android.app.Activity
 import androidx.window.core.ExperimentalWindowApi
-import com.nhaarman.mockitokotlin2.mock
+import org.mockito.kotlin.mock
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
 import org.junit.Test
diff --git a/window/window/src/test/java/androidx/window/embedding/EmbeddingCompatTest.kt b/window/window/src/test/java/androidx/window/embedding/EmbeddingCompatTest.kt
new file mode 100644
index 0000000..b1f6a6b
--- /dev/null
+++ b/window/window/src/test/java/androidx/window/embedding/EmbeddingCompatTest.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.window.embedding
+
+import android.app.Activity
+import androidx.window.core.ConsumerAdapter
+import androidx.window.core.ExperimentalWindowApi
+import androidx.window.core.PredicateAdapter
+import androidx.window.extensions.embedding.ActivityEmbeddingComponent
+import org.junit.Test
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+
+@ExperimentalWindowApi
+class EmbeddingCompatTest {
+
+    private val component = mock<ActivityEmbeddingComponent>()
+    private val embeddingCompat = EmbeddingCompat(component, EMBEDDING_ADAPTER, CONSUMER_ADAPTER)
+
+    @Test
+    fun setSplitInfoCallback_callsActualMethod() {
+        val callback = object : EmbeddingInterfaceCompat.EmbeddingCallbackInterface {
+            override fun onSplitInfoChanged(splitInfo: List<SplitInfo>) {
+            }
+        }
+        embeddingCompat.setEmbeddingCallback(callback)
+
+        verify(component).setSplitInfoCallback(any())
+    }
+
+    @Test
+    fun setSplitRules_delegatesToActivityEmbeddingComponent() {
+        embeddingCompat.setSplitRules(emptySet())
+
+        verify(component).setEmbeddingRules(any())
+    }
+
+    @Test
+    fun isActivityEmbedded_delegatesToComponent() {
+        val activity = mock<Activity>()
+
+        embeddingCompat.isActivityEmbedded(activity)
+
+        verify(component).isActivityEmbedded(activity)
+    }
+
+    companion object {
+        private val LOADER = EmbeddingCompatTest::class.java.classLoader!!
+        private val PREDICATE_ADAPTER = PredicateAdapter(LOADER)
+        private val CONSUMER_ADAPTER = ConsumerAdapter(LOADER)
+        private val EMBEDDING_ADAPTER = EmbeddingAdapter(PREDICATE_ADAPTER)
+    }
+}
\ No newline at end of file
diff --git a/window/window/src/test/java/androidx/window/embedding/SplitInfoTest.kt b/window/window/src/test/java/androidx/window/embedding/SplitInfoTest.kt
index e7833f8..e642d25 100644
--- a/window/window/src/test/java/androidx/window/embedding/SplitInfoTest.kt
+++ b/window/window/src/test/java/androidx/window/embedding/SplitInfoTest.kt
@@ -18,7 +18,7 @@
 
 import android.app.Activity
 import androidx.window.core.ExperimentalWindowApi
-import com.nhaarman.mockitokotlin2.mock
+import org.mockito.kotlin.mock
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
 import org.junit.Test
diff --git a/window/window/src/test/java/androidx/window/layout/DistinctElementSidecarCallbackTest.java b/window/window/src/test/java/androidx/window/layout/adapter/sidecar/DistinctElementSidecarCallbackTest.java
similarity index 87%
rename from window/window/src/test/java/androidx/window/layout/DistinctElementSidecarCallbackTest.java
rename to window/window/src/test/java/androidx/window/layout/adapter/sidecar/DistinctElementSidecarCallbackTest.java
index 17d8297..c9d9a9a 100644
--- a/window/window/src/test/java/androidx/window/layout/DistinctElementSidecarCallbackTest.java
+++ b/window/window/src/test/java/androidx/window/layout/adapter/sidecar/DistinctElementSidecarCallbackTest.java
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package androidx.window.layout;
+package androidx.window.layout.adapter.sidecar;
 
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import androidx.window.sidecar.SidecarInterface;
 
@@ -33,6 +33,6 @@
         DistinctElementSidecarCallback callback = new DistinctElementSidecarCallback(mockCallback);
 
         callback.onDeviceStateChanged(null);
-        verifyZeroInteractions(mockCallback);
+        verifyNoMoreInteractions(mockCallback);
     }
 }
diff --git a/window/window/src/test/java/androidx/window/layout/SidecarCompatUnitTest.kt b/window/window/src/test/java/androidx/window/layout/adapter/sidecar/SidecarCompatUnitTest.kt
similarity index 96%
rename from window/window/src/test/java/androidx/window/layout/SidecarCompatUnitTest.kt
rename to window/window/src/test/java/androidx/window/layout/adapter/sidecar/SidecarCompatUnitTest.kt
index b41f3de..6af845c 100644
--- a/window/window/src/test/java/androidx/window/layout/SidecarCompatUnitTest.kt
+++ b/window/window/src/test/java/androidx/window/layout/adapter/sidecar/SidecarCompatUnitTest.kt
@@ -16,7 +16,7 @@
 // Sidecar is deprecated but consuming code must be maintained for compatibility reasons
 @file:Suppress("DEPRECATION")
 
-package androidx.window.layout
+package androidx.window.layout.adapter.sidecar
 
 import android.app.Activity
 import android.graphics.Rect
@@ -25,22 +25,23 @@
 import android.view.Window
 import android.view.WindowManager
 import androidx.window.TestWindow
-import androidx.window.layout.ExtensionInterfaceCompat.ExtensionCallbackInterface
+import androidx.window.layout.adapter.sidecar.ExtensionInterfaceCompat.ExtensionCallbackInterface
 import androidx.window.layout.TestFoldingFeatureUtil.invalidFoldBounds
 import androidx.window.layout.TestFoldingFeatureUtil.validFoldBound
+import androidx.window.layout.WindowLayoutInfo
 import androidx.window.sidecar.SidecarDeviceState
 import androidx.window.sidecar.SidecarDisplayFeature
 import androidx.window.sidecar.SidecarInterface
 import androidx.window.sidecar.SidecarInterface.SidecarCallback
 import androidx.window.sidecar.SidecarWindowLayoutInfo
-import com.nhaarman.mockitokotlin2.any
-import com.nhaarman.mockitokotlin2.doAnswer
-import com.nhaarman.mockitokotlin2.doReturn
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.spy
-import com.nhaarman.mockitokotlin2.times
-import com.nhaarman.mockitokotlin2.verify
-import com.nhaarman.mockitokotlin2.whenever
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
 import org.junit.Assert.assertEquals
 import org.junit.Before
 import org.junit.Test
diff --git a/window/window/src/test/java/androidx/window/layout/SidecarWindowBackendUnitTest.kt b/window/window/src/test/java/androidx/window/layout/adapter/sidecar/SidecarWindowBackendUnitTest.kt
similarity index 94%
rename from window/window/src/test/java/androidx/window/layout/SidecarWindowBackendUnitTest.kt
rename to window/window/src/test/java/androidx/window/layout/adapter/sidecar/SidecarWindowBackendUnitTest.kt
index 6a5660c..a5d698f 100644
--- a/window/window/src/test/java/androidx/window/layout/SidecarWindowBackendUnitTest.kt
+++ b/window/window/src/test/java/androidx/window/layout/adapter/sidecar/SidecarWindowBackendUnitTest.kt
@@ -13,17 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package androidx.window.layout
+package androidx.window.layout.adapter.sidecar
 
 import android.app.Activity
 import android.content.Context
 import androidx.core.util.Consumer
-import androidx.window.layout.ExtensionInterfaceCompat.ExtensionCallbackInterface
-import com.nhaarman.mockitokotlin2.any
-import com.nhaarman.mockitokotlin2.eq
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.times
-import com.nhaarman.mockitokotlin2.verify
+import androidx.window.layout.WindowLayoutInfo
+import androidx.window.layout.adapter.sidecar.ExtensionInterfaceCompat.ExtensionCallbackInterface
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotNull
 import org.junit.Assert.assertTrue
diff --git a/window/window/src/testUtil/java/androidx/window/layout/EmptyExtensionCallbackInterface.kt b/window/window/src/testUtil/java/androidx/window/layout/adapter/sidecar/EmptyExtensionCallbackInterface.kt
similarity index 82%
rename from window/window/src/testUtil/java/androidx/window/layout/EmptyExtensionCallbackInterface.kt
rename to window/window/src/testUtil/java/androidx/window/layout/adapter/sidecar/EmptyExtensionCallbackInterface.kt
index 4718f77..4bb595f 100644
--- a/window/window/src/testUtil/java/androidx/window/layout/EmptyExtensionCallbackInterface.kt
+++ b/window/window/src/testUtil/java/androidx/window/layout/adapter/sidecar/EmptyExtensionCallbackInterface.kt
@@ -13,10 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package androidx.window.layout
+package androidx.window.layout.adapter.sidecar
 
 import android.app.Activity
-import androidx.window.layout.ExtensionInterfaceCompat.ExtensionCallbackInterface
+import androidx.window.layout.WindowLayoutInfo
+import androidx.window.layout.adapter.sidecar.ExtensionInterfaceCompat.ExtensionCallbackInterface
 
 /**
  * An empty implementation of [ExtensionCallbackInterface] with no-op methods.
diff --git a/window/window/src/testUtil/java/androidx/window/layout/SwitchOnUnregisterExtensionInterfaceCompat.kt b/window/window/src/testUtil/java/androidx/window/layout/adapter/sidecar/SwitchOnUnregisterExtensionInterfaceCompat.kt
similarity index 90%
rename from window/window/src/testUtil/java/androidx/window/layout/SwitchOnUnregisterExtensionInterfaceCompat.kt
rename to window/window/src/testUtil/java/androidx/window/layout/adapter/sidecar/SwitchOnUnregisterExtensionInterfaceCompat.kt
index 1dc98f9..1af0445 100644
--- a/window/window/src/testUtil/java/androidx/window/layout/SwitchOnUnregisterExtensionInterfaceCompat.kt
+++ b/window/window/src/testUtil/java/androidx/window/layout/adapter/sidecar/SwitchOnUnregisterExtensionInterfaceCompat.kt
@@ -13,17 +13,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package androidx.window.layout
+package androidx.window.layout.adapter.sidecar
 
 import android.app.Activity
 import android.graphics.Rect
 import androidx.annotation.GuardedBy
 import androidx.window.core.Bounds
-import androidx.window.layout.ExtensionInterfaceCompat.ExtensionCallbackInterface
+import androidx.window.layout.FoldingFeature
+import androidx.window.layout.adapter.sidecar.ExtensionInterfaceCompat.ExtensionCallbackInterface
 import androidx.window.layout.FoldingFeature.State
 import androidx.window.layout.FoldingFeature.State.Companion.FLAT
 import androidx.window.layout.FoldingFeature.State.Companion.HALF_OPENED
+import androidx.window.layout.HardwareFoldingFeature
 import androidx.window.layout.HardwareFoldingFeature.Type.Companion.HINGE
+import androidx.window.layout.WindowLayoutInfo
 import java.util.concurrent.locks.Lock
 import java.util.concurrent.locks.ReentrantLock
 import kotlin.concurrent.withLock
diff --git a/work/work-gcm/build.gradle b/work/work-gcm/build.gradle
index 332a4c4..79b7b9f 100644
--- a/work/work-gcm/build.gradle
+++ b/work/work-gcm/build.gradle
@@ -32,9 +32,9 @@
 dependencies {
     api project(":work:work-runtime")
     implementation(libs.gcmNetworkManager)
-    annotationProcessor(projectOrArtifact(":room:room-compiler"))
-    implementation(projectOrArtifact(":room:room-runtime"))
-    androidTestImplementation(projectOrArtifact(":room:room-testing"))
+    annotationProcessor("androidx.room:room-compiler:2.5.0-beta01")
+    implementation("androidx.room:room-runtime:2.5.0-beta01")
+    androidTestImplementation("androidx.room:room-testing:2.5.0-beta01")
 
     androidTestImplementation(project(":work:work-runtime-ktx"))
     androidTestImplementation(libs.testExtJunit)
diff --git a/work/work-inspection/build.gradle b/work/work-inspection/build.gradle
index 6c66eb3..8de2604 100644
--- a/work/work-inspection/build.gradle
+++ b/work/work-inspection/build.gradle
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
+
 import androidx.build.LibraryType
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
     id("AndroidXPlugin")
@@ -30,7 +30,7 @@
     compileOnly("androidx.inspection:inspection:1.0.0")
     compileOnly("androidx.lifecycle:lifecycle-runtime:2.2.0")
     compileOnly(project(":work:work-runtime"))
-    compileOnly(projectOrArtifact(":room:room-runtime"))
+    compileOnly("androidx.room:room-runtime:2.5.0-beta01")
     androidTestImplementation(project(":inspection:inspection-testing"))
     androidTestImplementation(project(":work:work-runtime"))
     androidTestImplementation(project(":work:work-runtime-ktx"))
diff --git a/work/work-multiprocess/build.gradle b/work/work-multiprocess/build.gradle
index fb72873..f960c4e 100644
--- a/work/work-multiprocess/build.gradle
+++ b/work/work-multiprocess/build.gradle
@@ -35,7 +35,7 @@
     api(libs.kotlinCoroutinesAndroid)
     api(libs.guavaListenableFuture)
     implementation("androidx.core:core:1.1.0")
-    implementation(projectOrArtifact(":room:room-runtime"))
+    implementation("androidx.room:room-runtime:2.5.0-beta01")
     androidTestImplementation(libs.kotlinStdlib)
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testCore)
diff --git a/work/work-runtime-ktx/build.gradle b/work/work-runtime-ktx/build.gradle
index e276de3..a202b23 100644
--- a/work/work-runtime-ktx/build.gradle
+++ b/work/work-runtime-ktx/build.gradle
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
+
 import androidx.build.Publish
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
     id("AndroidXPlugin")
@@ -35,7 +35,7 @@
     androidTestImplementation(libs.espressoCore)
     androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has its own MockMaker
     androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy) // DexMaker has its own MockMaker
-    androidTestImplementation(projectOrArtifact(":room:room-testing"))
+    androidTestImplementation("androidx.room:room-testing:2.5.0-beta01")
     testImplementation(libs.junit)
 }
 
diff --git a/work/work-runtime/build.gradle b/work/work-runtime/build.gradle
index 66fb8c2..ca651ca 100644
--- a/work/work-runtime/build.gradle
+++ b/work/work-runtime/build.gradle
@@ -57,10 +57,10 @@
 
 dependencies {
     implementation("androidx.core:core:1.6.0")
-    ksp(projectOrArtifact(":room:room-compiler"))
-    implementation(projectOrArtifact(":room:room-runtime"))
-    androidTestImplementation(projectOrArtifact(":room:room-testing"))
-    implementation(projectOrArtifact(":sqlite:sqlite-framework"))
+    ksp("androidx.room:room-compiler:2.5.0-beta01")
+    implementation("androidx.room:room-runtime:2.5.0-beta01")
+    androidTestImplementation("androidx.room:room-testing:2.5.0-beta01")
+    implementation("androidx.sqlite:sqlite-framework:2.3.0-beta01")
     api("androidx.annotation:annotation-experimental:1.0.0")
     api(libs.guavaListenableFuture)
     api("androidx.lifecycle:lifecycle-livedata:2.1.0")
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/WorkDatabaseMigrations.kt b/work/work-runtime/src/main/java/androidx/work/impl/WorkDatabaseMigrations.kt
index 096e9498..e0f73d2 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/WorkDatabaseMigrations.kt
+++ b/work/work-runtime/src/main/java/androidx/work/impl/WorkDatabaseMigrations.kt
@@ -126,11 +126,11 @@
  * Adds implicit work tags for all work (a tag with the worker class name).
  */
 object Migration_1_2 : Migration(VERSION_1, VERSION_2) {
-    override fun migrate(database: SupportSQLiteDatabase) {
-        database.execSQL(CREATE_SYSTEM_ID_INFO)
-        database.execSQL(MIGRATE_ALARM_INFO_TO_SYSTEM_ID_INFO)
-        database.execSQL(REMOVE_ALARM_INFO)
-        database.execSQL(
+    override fun migrate(db: SupportSQLiteDatabase) {
+        db.execSQL(CREATE_SYSTEM_ID_INFO)
+        db.execSQL(MIGRATE_ALARM_INFO_TO_SYSTEM_ID_INFO)
+        db.execSQL(REMOVE_ALARM_INFO)
+        db.execSQL(
             """
                 INSERT OR IGNORE INTO worktag(tag, work_spec_id)
                 SELECT worker_class_name AS tag, id AS work_spec_id FROM workspec
@@ -144,9 +144,9 @@
  * `SCHEDULE_NOT_REQUESTED_AT`.
  */
 object Migration_3_4 : Migration(VERSION_3, VERSION_4) {
-    override fun migrate(database: SupportSQLiteDatabase) {
+    override fun migrate(db: SupportSQLiteDatabase) {
         if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) {
-            database.execSQL(PERIODIC_WORK_SET_SCHEDULE_REQUESTED_AT)
+            db.execSQL(PERIODIC_WORK_SET_SCHEDULE_REQUESTED_AT)
         }
     }
 }
@@ -155,9 +155,9 @@
  * Adds the `ContentUri` delays to the WorkSpec table.
  */
 object Migration_4_5 : Migration(VERSION_4, VERSION_5) {
-    override fun migrate(database: SupportSQLiteDatabase) {
-        database.execSQL(WORKSPEC_ADD_TRIGGER_UPDATE_DELAY)
-        database.execSQL(WORKSPEC_ADD_TRIGGER_MAX_CONTENT_DELAY)
+    override fun migrate(db: SupportSQLiteDatabase) {
+        db.execSQL(WORKSPEC_ADD_TRIGGER_UPDATE_DELAY)
+        db.execSQL(WORKSPEC_ADD_TRIGGER_MAX_CONTENT_DELAY)
     }
 }
 
@@ -165,8 +165,8 @@
  * Adds [androidx.work.impl.model.WorkProgress].
  */
 object Migration_6_7 : Migration(VERSION_6, VERSION_7) {
-    override fun migrate(database: SupportSQLiteDatabase) {
-        database.execSQL(CREATE_WORK_PROGRESS)
+    override fun migrate(db: SupportSQLiteDatabase) {
+        db.execSQL(CREATE_WORK_PROGRESS)
     }
 }
 
@@ -174,8 +174,8 @@
  * Adds an index on period_start_time in [WorkSpec].
  */
 object Migration_7_8 : Migration(VERSION_7, VERSION_8) {
-    override fun migrate(database: SupportSQLiteDatabase) {
-        database.execSQL(CREATE_INDEX_PERIOD_START_TIME)
+    override fun migrate(db: SupportSQLiteDatabase) {
+        db.execSQL(CREATE_INDEX_PERIOD_START_TIME)
     }
 }
 
@@ -183,8 +183,8 @@
  * Adds a notification_provider to the [WorkSpec].
  */
 object Migration_8_9 : Migration(VERSION_8, VERSION_9) {
-    override fun migrate(database: SupportSQLiteDatabase) {
-        database.execSQL(CREATE_RUN_IN_FOREGROUND)
+    override fun migrate(db: SupportSQLiteDatabase) {
+        db.execSQL(CREATE_RUN_IN_FOREGROUND)
     }
 }
 
@@ -192,15 +192,15 @@
  * Adds a notification_provider to the [WorkSpec].
  */
 object Migration_11_12 : Migration(VERSION_11, VERSION_12) {
-    override fun migrate(database: SupportSQLiteDatabase) {
-        database.execSQL(CREATE_OUT_OF_QUOTA_POLICY)
+    override fun migrate(db: SupportSQLiteDatabase) {
+        db.execSQL(CREATE_OUT_OF_QUOTA_POLICY)
     }
 }
 
 object Migration_12_13 : Migration(VERSION_12, VERSION_13) {
-    override fun migrate(database: SupportSQLiteDatabase) {
-        database.execSQL(SET_DEFAULT_NETWORK_TYPE)
-        database.execSQL(SET_DEFAULT_CONTENT_URI_TRIGGERS)
+    override fun migrate(db: SupportSQLiteDatabase) {
+        db.execSQL(SET_DEFAULT_NETWORK_TYPE)
+        db.execSQL(SET_DEFAULT_CONTENT_URI_TRIGGERS)
     }
 }
 
@@ -226,9 +226,9 @@
  */
 class RescheduleMigration(val mContext: Context, startVersion: Int, endVersion: Int) :
     Migration(startVersion, endVersion) {
-    override fun migrate(database: SupportSQLiteDatabase) {
+    override fun migrate(db: SupportSQLiteDatabase) {
         if (endVersion >= VERSION_10) {
-            database.execSQL(
+            db.execSQL(
                 PreferenceUtils.INSERT_PREFERENCE,
                 arrayOf(PreferenceUtils.KEY_RESCHEDULE_NEEDED, 1)
             )
@@ -251,29 +251,29 @@
  * Adds the [androidx.work.impl.model.Preference] table.
  */
 internal class WorkMigration9To10(private val context: Context) : Migration(VERSION_9, VERSION_10) {
-    override fun migrate(database: SupportSQLiteDatabase) {
-        database.execSQL(PreferenceUtils.CREATE_PREFERENCE)
-        PreferenceUtils.migrateLegacyPreferences(context, database)
-        migrateLegacyIdGenerator(context, database)
+    override fun migrate(db: SupportSQLiteDatabase) {
+        db.execSQL(PreferenceUtils.CREATE_PREFERENCE)
+        PreferenceUtils.migrateLegacyPreferences(context, db)
+        migrateLegacyIdGenerator(context, db)
     }
 }
 
 object Migration_15_16 : Migration(VERSION_15, VERSION_16) {
-    override fun migrate(database: SupportSQLiteDatabase) {
+    override fun migrate(db: SupportSQLiteDatabase) {
         // b/239543214: unclear how data got corrupted,
         // but foreign key check on SystemIdInfo fails,
         // meaning SystemIdInfo has work_spec_id that doesn't exist in WorkSpec table.
-        database.execSQL(
+        db.execSQL(
             "DELETE FROM SystemIdInfo WHERE work_spec_id IN " +
                 "(SELECT work_spec_id FROM SystemIdInfo " +
                 "LEFT JOIN WorkSpec ON work_spec_id = id WHERE WorkSpec.id IS NULL)"
         )
 
-        database.execSQL(
+        db.execSQL(
             "ALTER TABLE `WorkSpec` ADD COLUMN `generation` " +
                 "INTEGER NOT NULL DEFAULT 0"
         )
-        database.execSQL(
+        db.execSQL(
             """CREATE TABLE IF NOT EXISTS `_new_SystemIdInfo` (
             `work_spec_id` TEXT NOT NULL, 
             `generation` INTEGER NOT NULL DEFAULT 0, 
@@ -283,11 +283,11 @@
                 ON UPDATE CASCADE ON DELETE CASCADE )
                """.trimIndent()
         )
-        database.execSQL(
+        db.execSQL(
             "INSERT INTO `_new_SystemIdInfo` (`work_spec_id`,`system_id`) " +
                 "SELECT `work_spec_id`,`system_id` FROM `SystemIdInfo`"
         )
-        database.execSQL("DROP TABLE `SystemIdInfo`")
-        database.execSQL("ALTER TABLE `_new_SystemIdInfo` RENAME TO `SystemIdInfo`")
+        db.execSQL("DROP TABLE `SystemIdInfo`")
+        db.execSQL("ALTER TABLE `_new_SystemIdInfo` RENAME TO `SystemIdInfo`")
     }
 }
diff --git a/work/work-rxjava2/build.gradle b/work/work-rxjava2/build.gradle
index 575800f..4e66b7a 100644
--- a/work/work-rxjava2/build.gradle
+++ b/work/work-rxjava2/build.gradle
@@ -28,7 +28,7 @@
     testImplementation(libs.truth)
     testImplementation(libs.junit)
     testImplementation(libs.kotlinStdlib)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
 }
 
 androidx {
diff --git a/work/work-rxjava3/build.gradle b/work/work-rxjava3/build.gradle
index 4894866..2996856 100644
--- a/work/work-rxjava3/build.gradle
+++ b/work/work-rxjava3/build.gradle
@@ -28,7 +28,7 @@
     testImplementation(libs.truth)
     testImplementation(libs.junit)
     testImplementation(libs.kotlinStdlib)
-    testImplementation(libs.mockitoCore)
+    testImplementation(libs.mockitoCore4)
 }
 
 androidx {
diff --git a/work/work-testing/build.gradle b/work/work-testing/build.gradle
index d30aefa..32d8428 100644
--- a/work/work-testing/build.gradle
+++ b/work/work-testing/build.gradle
@@ -25,7 +25,7 @@
 dependencies {
     api(project(":work:work-runtime-ktx"))
     implementation("androidx.lifecycle:lifecycle-livedata-core:2.1.0")
-    implementation(projectOrArtifact(":room:room-runtime"))
+    implementation("androidx.room:room-runtime:2.5.0-beta01")
 
     androidTestImplementation("androidx.arch.core:core-testing:2.1.0")
     androidTestImplementation(libs.testExtJunit)