Merge "[FPS Range Settings] Improve the power and latency by setting the fps range before creating capture session" into androidx-main
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java
index 93530a91..5778397 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextHelper.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
+import android.graphics.Paint;
 import android.graphics.PorterDuff;
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
@@ -40,8 +41,11 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
+import androidx.annotation.UiThread;
 import androidx.appcompat.R;
+import androidx.collection.LruCache;
 import androidx.core.content.res.ResourcesCompat;
+import androidx.core.util.Pair;
 import androidx.core.util.TypedValueCompat;
 import androidx.core.view.ViewCompat;
 import androidx.core.view.inputmethod.EditorInfoCompat;
@@ -86,6 +90,14 @@
         mAutoSizeTextHelper = new AppCompatTextViewAutoSizeHelper(mView);
     }
 
+    @RequiresApi(26)
+    @UiThread
+    @NonNull
+    static Typeface createVariationInstance(@NonNull Typeface baseTypeface,
+            @NonNull String fontVariationSettings) {
+        return Api26Impl.createVariationInstance(baseTypeface, fontVariationSettings);
+    }
+
     @SuppressLint("NewApi")
     void loadFromAttributes(@Nullable AttributeSet attrs, int defStyleAttr) {
         final Context context = mView.getContext();
@@ -772,6 +784,17 @@
 
     @RequiresApi(26)
     static class Api26Impl {
+        /**
+         * Cache for variation instances created based on an existing Typeface
+         */
+        private static final LruCache<Pair<Typeface, String>, Typeface> sVariationsCache =
+                new LruCache<>(30);
+
+        /**
+         * Used to create variation instances; initialized lazily
+         */
+        private static @Nullable Paint sPaint;
+
         private Api26Impl() {
             // This class is not instantiable.
         }
@@ -788,6 +811,54 @@
             return textView.setFontVariationSettings(fontVariationSettings);
         }
 
+        /**
+         * Create a new Typeface based on {@code baseTypeFace} with the specified variation
+         * settings.  Uses a cache to avoid memory scaling with the number of AppCompatTextViews.
+         *
+         * @param baseTypeface the original typeface, preferably without variations applied
+         *                     (used both to create the new instance, and as a cache key).
+         *                     Note: this method will correctly handle instances with variations
+         *                     applied, as we have no way of detecting that.  However, cache hit
+         *                     rates may be decreased.
+         * @param fontVariationSettings the new font variation settings.
+         *                              This is used as a cache key without sorting, to avoid
+         *                              additional per-TextView allocations to parse and sort the
+         *                              variation settings.  App developers should strive to provide
+         *                              the settings in the same order every time within their app,
+         *                              in order to get the best cache performance.
+         * @return the new instance, or {@code null} if
+         *         {@link Paint#setFontVariationSettings(String)} would return null for this
+         *         Typeface and font variation settings string.
+         */
+        @Nullable
+        @UiThread
+        static Typeface createVariationInstance(@Nullable Typeface baseTypeface,
+                @Nullable String fontVariationSettings) {
+            Pair<Typeface, String> cacheKey = new Pair<>(baseTypeface, fontVariationSettings);
+
+            Typeface result = sVariationsCache.get(cacheKey);
+            if (result != null) {
+                return result;
+            }
+            Paint paint = sPaint != null ? sPaint : (sPaint = new Paint());
+
+            // Work around b/353609778
+            if (Objects.equals(paint.getFontVariationSettings(), fontVariationSettings)) {
+                paint.setFontVariationSettings(null);
+            }
+
+            // Use Paint to create a new Typeface based on an existing one
+            paint.setTypeface(baseTypeface);
+            boolean effective = paint.setFontVariationSettings(fontVariationSettings);
+            if (effective) {
+                result = paint.getTypeface();
+                sVariationsCache.put(cacheKey, result);
+                return result;
+            } else {
+                return null;
+            }
+        }
+
         static int getAutoSizeStepGranularity(TextView textView) {
             return textView.getAutoSizeStepGranularity();
         }
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextView.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextView.java
index 0b7c10b..42f25b5 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextView.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextView.java
@@ -96,6 +96,20 @@
 
     private boolean mIsSetTypefaceProcessing = false;
 
+    /**
+     * Equivalent to Typeface.mOriginalTypeface.
+     * Used to correctly emulate the behavior of getTypeface(), because we need to call setTypeface
+     * directly in order to implement caching of variation instances of typefaces.
+     */
+    private Typeface mOriginalTypeface;
+
+    /**
+     * The currently applied font variation settings.
+     * Used to make getFontVariationSettings somewhat more accurate with Typeface instance caching,
+     * as we don't call super.setFontVariationSettings.
+     */
+    private String mFontVariationSettings;
+
     @Nullable
     private SuperCaller mSuperCaller = null;
 
@@ -160,7 +174,6 @@
     /**
      * This should be accessed via
      * {@link androidx.core.view.ViewCompat#setBackgroundTintList(android.view.View, ColorStateList)}
-     *
      */
     @RestrictTo(LIBRARY_GROUP_PREFIX)
     @Override
@@ -173,7 +186,6 @@
     /**
      * This should be accessed via
      * {@link androidx.core.view.ViewCompat#getBackgroundTintList(android.view.View)}
-     *
      */
     @RestrictTo(LIBRARY_GROUP_PREFIX)
     @Override
@@ -186,7 +198,6 @@
     /**
      * This should be accessed via
      * {@link androidx.core.view.ViewCompat#setBackgroundTintMode(android.view.View, PorterDuff.Mode)}
-     *
      */
     @RestrictTo(LIBRARY_GROUP_PREFIX)
     @Override
@@ -199,7 +210,6 @@
     /**
      * This should be accessed via
      * {@link androidx.core.view.ViewCompat#getBackgroundTintMode(android.view.View)}
-     *
      */
     @RestrictTo(LIBRARY_GROUP_PREFIX)
     @Override
@@ -217,6 +227,38 @@
         }
     }
 
+    /**
+     * Set font variation settings.
+     * See {@link TextView#setFontVariationSettings(String)} for details.
+     * <p>
+     * <em>Note:</em> Due to performance optimizations,
+     * {@code getPaint().getFontVariationSettings()} will be less reliable than if not using
+     * AppCompatTextView.  You should prefer {@link #getFontVariationSettings()}, which will be more
+     * accurate. However, neither approach will work correctly if using Typeface objects with
+     * embedded font variation settings.
+     */
+    @RequiresApi(26)
+    @Override
+    public boolean setFontVariationSettings(@Nullable String fontVariationSettings) {
+        Typeface variationTypefaceInstance = AppCompatTextHelper.Api26Impl.createVariationInstance(
+                mOriginalTypeface, fontVariationSettings);
+        if (variationTypefaceInstance != null) {
+            // Call superclass method directly to bypass overwriting mOriginalTypeface
+            super.setTypeface(variationTypefaceInstance);
+            mFontVariationSettings = fontVariationSettings;
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    @Nullable
+    @RequiresApi(26)
+    @Override
+    public String getFontVariationSettings() {
+        return mFontVariationSettings;
+    }
+
     @Override
     public void setFilters(@SuppressWarnings("ArrayReturn") @NonNull InputFilter[] filters) {
         super.setFilters(getEmojiTextViewHelper().getFilters(filters));
@@ -755,6 +797,20 @@
     }
 
     @Override
+    public void setTypeface(@Nullable Typeface tf) {
+        mOriginalTypeface = tf;
+        super.setTypeface(tf);
+    }
+
+    @Override
+    @Nullable
+    // Code inspection reveals that the superclass method can return null.
+    @SuppressWarnings("InvalidNullabilityOverride")
+    public Typeface getTypeface() {
+        return mOriginalTypeface;
+    }
+
+    @Override
     public void setTypeface(@Nullable Typeface tf, int style) {
         if (mIsSetTypefaceProcessing) {
             // b/151782655
diff --git a/buildSrc/jetpad-integration/src/main/java/androidx/build/jetpad/LibraryBuildInfoFile.java b/buildSrc/jetpad-integration/src/main/java/androidx/build/jetpad/LibraryBuildInfoFile.java
index bf7a859..9781b2e 100644
--- a/buildSrc/jetpad-integration/src/main/java/androidx/build/jetpad/LibraryBuildInfoFile.java
+++ b/buildSrc/jetpad-integration/src/main/java/androidx/build/jetpad/LibraryBuildInfoFile.java
@@ -45,7 +45,6 @@
     public String kotlinVersion;
     public String path;
     public String sha;
-    public String groupZipPath;
     public String projectZipPath;
     public Boolean groupIdRequiresSameVersion;
     public ArrayList<Dependency> dependencies;
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/Release.kt b/buildSrc/private/src/main/kotlin/androidx/build/Release.kt
index 5a347ab..791d72d 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/Release.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/Release.kt
@@ -18,7 +18,6 @@
 import androidx.build.uptodatedness.cacheEvenIfNoOutputs
 import java.io.File
 import java.io.FileNotFoundException
-import java.util.Locale
 import org.gradle.api.Action
 import org.gradle.api.DefaultTask
 import org.gradle.api.GradleException
@@ -121,14 +120,11 @@
 object Release {
     @Suppress("MemberVisibilityCanBePrivate")
     const val PROJECT_ARCHIVE_ZIP_TASK_NAME = "createProjectZip"
-    const val DIFF_TASK_PREFIX = "createDiffArchive"
-    const val FULL_ARCHIVE_TASK_NAME = "createArchive"
-    const val ALL_ARCHIVES_TASK_NAME = "createAllArchives"
+    private const val FULL_ARCHIVE_TASK_NAME = "createArchive"
+    private const val ALL_ARCHIVES_TASK_NAME = "createAllArchives"
     const val DEFAULT_PUBLISH_CONFIG = "release"
-    const val GROUP_ZIPS_FOLDER = "per-group-zips"
     const val PROJECT_ZIPS_FOLDER = "per-project-zips"
-    const val GROUP_ZIP_PREFIX = "gmaven"
-    const val GLOBAL_ZIP_PREFIX = "top-of-tree-m2repository"
+    private const val GLOBAL_ZIP_PREFIX = "top-of-tree-m2repository"
 
     // lazily created config action params so that we don't keep re-creating them
     private var configActionParams: GMavenZipTask.ConfigAction.Params? = null
@@ -158,12 +154,6 @@
             )
             return
         }
-
-        val mavenGroup =
-            androidXExtension.mavenGroup?.group
-                ?: throw IllegalArgumentException(
-                    "Cannot register a project to release if it does not have a mavenGroup set up"
-                )
         if (!androidXExtension.isVersionSet()) {
             throw IllegalArgumentException(
                 "Cannot register a project to release if it does not have a mavenVersion set up"
@@ -172,12 +162,7 @@
         val version = project.version
 
         val projectZipTask = getProjectZipTask(project)
-        val zipTasks =
-            listOf(
-                projectZipTask,
-                getGroupReleaseZipTask(project, mavenGroup),
-                getGlobalFullZipTask(project)
-            )
+        val zipTasks = listOf(projectZipTask, getGlobalFullZipTask(project))
 
         val artifacts = androidXExtension.publishedArtifacts
         val publishTask = project.tasks.named("publish")
@@ -295,28 +280,6 @@
         )
     }
 
-    /** Creates and returns the zip task that includes artifacts only in the given maven group. */
-    private fun getGroupReleaseZipTask(
-        project: Project,
-        group: String
-    ): TaskProvider<GMavenZipTask> {
-        return project.rootProject.maybeRegister(
-            name = "${DIFF_TASK_PREFIX}For${groupToTaskNameSuffix(group)}",
-            onConfigure = { task: GMavenZipTask ->
-                GMavenZipTask.ConfigAction(
-                        getParams(
-                            project = project,
-                            distDir = File(project.getDistributionDirectory(), GROUP_ZIPS_FOLDER),
-                            fileNamePrefix = GROUP_ZIP_PREFIX,
-                            group = group
-                        )
-                    )
-                    .execute(task)
-            },
-            onRegister = { taskProvider -> project.addToAnchorTask(taskProvider) }
-        )
-    }
-
     private fun getProjectZipTask(project: Project): TaskProvider<GMavenZipTask> {
         val taskProvider =
             project.tasks.register(PROJECT_ARCHIVE_ZIP_TASK_NAME, GMavenZipTask::class.java) {
@@ -403,7 +366,7 @@
         }
     }
 
-    fun verifyFiles() {
+    private fun verifyFiles() {
         val missingFiles = mutableListOf<String>()
         val emptyDirs = mutableListOf<String>()
         filesToVerify.forEach { file ->
@@ -478,15 +441,6 @@
         return declaredTargets.toList()
     }
 
-/** Converts the maven group into a readable task name. */
-private fun groupToTaskNameSuffix(group: String): String {
-    return group.split('.').joinToString("") {
-        it.replaceFirstChar { char ->
-            if (char.isLowerCase()) char.titlecase(Locale.getDefault()) else char.toString()
-        }
-    }
-}
-
 private fun Project.projectZipPrefix(): String {
     return "${project.group}-${project.name}"
 }
@@ -509,17 +463,3 @@
         getZipName(projectZipPrefix(), "") +
         "-${project.version}.zip"
 }
-
-fun Project.getGroupZipPath(): String {
-    return Release.GROUP_ZIPS_FOLDER +
-        "/" +
-        getZipName(Release.GROUP_ZIP_PREFIX, project.group.toString()) +
-        ".zip"
-}
-
-fun Project.getGlobalZipFile(): File {
-    return File(
-        project.getDistributionDirectory(),
-        getZipName(Release.GLOBAL_ZIP_PREFIX, "") + ".zip"
-    )
-}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/buildInfo/CreateLibraryBuildInfoFileTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/buildInfo/CreateLibraryBuildInfoFileTask.kt
index efe9eee..07f4db2 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/buildInfo/CreateLibraryBuildInfoFileTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/buildInfo/CreateLibraryBuildInfoFileTask.kt
@@ -21,7 +21,6 @@
 import androidx.build.LibraryGroup
 import androidx.build.docs.CheckTipOfTreeDocsTask.Companion.requiresDocs
 import androidx.build.getBuildInfoDirectory
-import androidx.build.getGroupZipPath
 import androidx.build.getProjectZipPath
 import androidx.build.getSupportRootFolder
 import androidx.build.gitclient.getHeadShaProvider
@@ -92,8 +91,6 @@
 
     @get:Input abstract val groupIdRequiresSameVersion: Property<Boolean>
 
-    @get:Input abstract val groupZipPath: Property<String>
-
     @get:Input abstract val projectZipPath: Property<String>
 
     @get:[Input Optional]
@@ -139,7 +136,6 @@
         libraryBuildInfoFile.path = projectDir.get()
         libraryBuildInfoFile.sha = commit.get()
         libraryBuildInfoFile.groupIdRequiresSameVersion = groupIdRequiresSameVersion.get()
-        libraryBuildInfoFile.groupZipPath = groupZipPath.get()
         libraryBuildInfoFile.projectZipPath = projectZipPath.get()
         libraryBuildInfoFile.kotlinVersion = kotlinVersion.orNull
         libraryBuildInfoFile.checks = ArrayList()
@@ -196,7 +192,6 @@
                 )
                 task.commit.set(shaProvider)
                 task.groupIdRequiresSameVersion.set(mavenGroup?.requireSameVersion ?: false)
-                task.groupZipPath.set(project.getGroupZipPath())
                 task.projectZipPath.set(project.getProjectZipPath())
 
                 // Note:
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/FocusMeteringControl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/FocusMeteringControl.java
index 6129f59..03633e9 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/FocusMeteringControl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/FocusMeteringControl.java
@@ -458,6 +458,8 @@
      */
     @ExecutedBy("mExecutor")
     void triggerAePrecapture(@Nullable Completer<Void> completer) {
+        Logger.d(TAG, "triggerAePrecapture");
+
         if (!mIsActive) {
             if (completer != null) {
                 completer.setException(
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/ScreenFlashView.java b/camera/camera-view/src/main/java/androidx/camera/view/ScreenFlashView.java
index a42bd1b..529e630 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/ScreenFlashView.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/ScreenFlashView.java
@@ -19,6 +19,8 @@
 import static androidx.camera.core.ImageCapture.FLASH_MODE_SCREEN;
 import static androidx.camera.core.impl.utils.Threads.checkMainThread;
 
+import android.animation.Animator;
+import android.animation.ValueAnimator;
 import android.app.Activity;
 import android.content.Context;
 import android.graphics.Color;
@@ -29,6 +31,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
 import androidx.annotation.UiThread;
 import androidx.camera.core.ImageCapture;
 import androidx.camera.core.ImageCapture.ScreenFlash;
@@ -58,8 +61,8 @@
  * {@link PreviewView} does not encompass the full screen, users may want to use this view
  * separately so that whole screen can be encompassed during screen flash operation.
  *
+ * @see #getScreenFlash
  * @see ImageCapture#FLASH_MODE_SCREEN
- * @see PreviewView#getScreenFlash
  */
 public final class ScreenFlashView extends View {
     private static final String TAG = "ScreenFlashView";
@@ -67,6 +70,9 @@
     private Window mScreenFlashWindow;
     private ImageCapture.ScreenFlash mScreenFlash;
 
+    /** The timeout in seconds for the visibility animation at {@link ScreenFlash#apply}. */
+    private static final long ANIMATION_DURATION_MILLIS = 1000;
+
     @UiThread
     public ScreenFlashView(@NonNull Context context) {
         this(context, null);
@@ -80,7 +86,7 @@
     @UiThread
     public ScreenFlashView(@NonNull Context context, @Nullable AttributeSet attrs,
             int defStyleAttr) {
-        this(context, attrs,  defStyleAttr, 0);
+        this(context, attrs, defStyleAttr, 0);
     }
 
     @UiThread
@@ -134,7 +140,7 @@
             return;
         }
         mCameraController.setScreenFlashUiInfo(new ScreenFlashUiInfo(
-                        ScreenFlashUiInfo.ProviderType.SCREEN_FLASH_VIEW, control));
+                ScreenFlashUiInfo.ProviderType.SCREEN_FLASH_VIEW, control));
     }
 
     /**
@@ -170,45 +176,122 @@
         if (mScreenFlashWindow != window) {
             mScreenFlash = window == null ? null : new ScreenFlash() {
                 private float mPreviousBrightness;
+                private ValueAnimator mAnimator;
 
                 @Override
                 public void apply(long expirationTimeMillis,
                         @NonNull ImageCapture.ScreenFlashListener screenFlashListener) {
                     Logger.d(TAG, "ScreenFlash#apply");
 
-                    setAlpha(1f);
+                    mPreviousBrightness = getBrightness();
+                    setBrightness(1.0f);
 
-                    // Maximize screen brightness
-                    WindowManager.LayoutParams layoutParam = mScreenFlashWindow.getAttributes();
-                    mPreviousBrightness = layoutParam.screenBrightness;
-                    layoutParam.screenBrightness = 1F;
-                    mScreenFlashWindow.setAttributes(layoutParam);
-
-                    screenFlashListener.onCompleted();
+                    if (mAnimator != null) {
+                        mAnimator.cancel();
+                    }
+                    mAnimator = animateToFullOpacity(screenFlashListener::onCompleted);
                 }
 
                 @Override
                 public void clear() {
                     Logger.d(TAG, "ScreenFlash#clearScreenFlashUi");
 
+                    if (mAnimator != null) {
+                        mAnimator.cancel();
+                        mAnimator = null;
+                    }
+
                     setAlpha(0f);
 
                     // Restore screen brightness
-                    WindowManager.LayoutParams layoutParam = mScreenFlashWindow.getAttributes();
-                    layoutParam.screenBrightness = mPreviousBrightness;
-                    mScreenFlashWindow.setAttributes(layoutParam);
+                    setBrightness(mPreviousBrightness);
                 }
             };
         }
     }
 
+    private ValueAnimator animateToFullOpacity(@Nullable Runnable onAnimationEnd) {
+        Logger.d(TAG, "animateToFullOpacity");
+
+        ValueAnimator animator = ValueAnimator.ofFloat(0F, 1F);
+
+        // TODO: b/355168952 - Allow users to overwrite the animation duration.
+        animator.setDuration(getVisibilityRampUpAnimationDurationMillis());
+
+        animator.addUpdateListener(animation -> {
+            Logger.d(TAG, "animateToFullOpacity: value = " + (float) animation.getAnimatedValue());
+            setAlpha((float) animation.getAnimatedValue());
+        });
+
+        animator.addListener(new Animator.AnimatorListener() {
+            @Override
+            public void onAnimationStart(@NonNull Animator animation) {
+
+            }
+
+            @Override
+            public void onAnimationEnd(@NonNull Animator animation) {
+                Logger.d(TAG, "ScreenFlash#apply: onAnimationEnd");
+                if (onAnimationEnd != null) {
+                    onAnimationEnd.run();
+                }
+            }
+
+            @Override
+            public void onAnimationCancel(@NonNull Animator animation) {
+
+            }
+
+            @Override
+            public void onAnimationRepeat(@NonNull Animator animation) {
+
+            }
+        });
+
+        animator.start();
+
+        return animator;
+    }
+
+    private float getBrightness() {
+        if (mScreenFlashWindow == null) {
+            Logger.e(TAG, "setBrightness: mScreenFlashWindow is null!");
+            return Float.NaN;
+        }
+
+        WindowManager.LayoutParams layoutParam = mScreenFlashWindow.getAttributes();
+        return layoutParam.screenBrightness;
+    }
+
+    private void setBrightness(float value) {
+        if (mScreenFlashWindow == null) {
+            Logger.e(TAG, "setBrightness: mScreenFlashWindow is null!");
+            return;
+        }
+
+        if (Float.isNaN(value)) {
+            Logger.e(TAG, "setBrightness: value is NaN!");
+            return;
+        }
+
+        WindowManager.LayoutParams layoutParam = mScreenFlashWindow.getAttributes();
+        layoutParam.screenBrightness = value;
+        mScreenFlashWindow.setAttributes(layoutParam);
+        Logger.d(TAG, "Brightness set to " + layoutParam.screenBrightness);
+    }
+
     /**
      * Returns an {@link ScreenFlash} implementation based on the {@link Window} instance
      * set via {@link #setScreenFlashWindow(Window)}.
      *
      * <p> When {@link ScreenFlash#apply(long, ImageCapture.ScreenFlashListener)} is invoked,
-     * this view becomes fully visible and screen brightness is maximized using the provided
-     * {@code Window}. The default color of the overlay view is {@link Color#WHITE}. To change
+     * this view becomes fully visible gradually with an animation and screen brightness is
+     * maximized using the provided {@code Window}. Since brightness change of the display happens
+     * asynchronously and may take some time to be completed, the animation to ramp up visibility
+     * may require a duration of sufficient delay (decided internally) before
+     * {@link ImageCapture.ScreenFlashListener#onCompleted()} is invoked.
+     *
+     * <p> The default color of the overlay view is {@link Color#WHITE}. To change
      * the color, use {@link #setBackgroundColor(int)}.
      *
      * <p> When {@link ScreenFlash#clear()} is invoked, the view
@@ -219,11 +302,23 @@
      * Window} is set or none set at all, a null value will be returned by this method.
      *
      * @return A simple {@link ScreenFlash} implementation, or null value if a non-null
-     *         {@code Window} instance hasn't been set.
+     * {@code Window} instance hasn't been set.
      */
     @UiThread
     @Nullable
     public ScreenFlash getScreenFlash() {
         return mScreenFlash;
     }
+
+    /**
+     * Returns the duration of the visibility ramp-up animation.
+     *
+     * <p> This is currently used in {@link ScreenFlash#apply}.
+     *
+     * @see #getScreenFlash()
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public long getVisibilityRampUpAnimationDurationMillis() {
+        return ANIMATION_DURATION_MILLIS;
+    }
 }
diff --git a/camera/camera-view/src/test/java/androidx/camera/view/ScreenFlashViewTest.kt b/camera/camera-view/src/test/java/androidx/camera/view/ScreenFlashViewTest.kt
index 981552b..510c632 100644
--- a/camera/camera-view/src/test/java/androidx/camera/view/ScreenFlashViewTest.kt
+++ b/camera/camera-view/src/test/java/androidx/camera/view/ScreenFlashViewTest.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.os.Build
+import android.os.Looper.getMainLooper
 import android.view.Window
 import androidx.camera.core.CameraSelector
 import androidx.camera.core.ImageCapture
@@ -26,12 +27,14 @@
 import androidx.test.core.app.ApplicationProvider
 import com.google.common.truth.Truth.assertThat
 import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.runBlocking
 import org.junit.Assert
 import org.junit.Assume
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.robolectric.RobolectricTestRunner
+import org.robolectric.Shadows.shadowOf
 import org.robolectric.annotation.Config
 import org.robolectric.annotation.internal.DoNotInstrument
 import org.robolectric.shadows.ShadowWindow
@@ -98,13 +101,29 @@
     }
 
     @Test
-    fun isFullyVisible_whenScreenFlashApplyInvoked() {
+    fun isNotVisibleImmediately_whenScreenFlashApplyInvoked() {
         val screenFlash = getScreenFlashAfterSettingWindow(true)
         screenFlash!!.apply(
             System.currentTimeMillis() +
                 TimeUnit.SECONDS.toMillis(ImageCapture.SCREEN_FLASH_UI_APPLY_TIMEOUT_SECONDS),
             noOpListener,
         )
+        assertThat(screenFlashView.alpha).isEqualTo(0f)
+    }
+
+    @Test
+    fun isFullyVisibleAfterAnimationDuration_whenScreenFlashApplyInvoked() = runBlocking {
+        val screenFlash = getScreenFlashAfterSettingWindow(true)
+        screenFlash!!.apply(
+            System.currentTimeMillis() +
+                TimeUnit.SECONDS.toMillis(ImageCapture.SCREEN_FLASH_UI_APPLY_TIMEOUT_SECONDS),
+            noOpListener,
+        )
+        shadowOf(getMainLooper())
+            .idleFor(
+                screenFlashView.visibilityRampUpAnimationDurationMillis + 1,
+                TimeUnit.MILLISECONDS
+            )
         assertThat(screenFlashView.alpha).isEqualTo(1f)
     }
 
diff --git a/compose/animation/animation-core/build.gradle b/compose/animation/animation-core/build.gradle
index 595aef5..8eac369 100644
--- a/compose/animation/animation-core/build.gradle
+++ b/compose/animation/animation-core/build.gradle
@@ -46,7 +46,7 @@
                 implementation(project(":compose:ui:ui-unit"))
                 implementation(project(":compose:ui:ui-graphics"))
                 implementation(project(":compose:ui:ui-util"))
-                implementation(project(":collection:collection"))
+                implementation("androidx.collection:collection:1.4.2")
                 implementation(libs.kotlinStdlib)
                 api(libs.kotlinCoroutinesCore)
             }
diff --git a/compose/animation/animation-graphics/build.gradle b/compose/animation/animation-graphics/build.gradle
index 16f1350..399503d 100644
--- a/compose/animation/animation-graphics/build.gradle
+++ b/compose/animation/animation-graphics/build.gradle
@@ -49,7 +49,7 @@
                 api(project(":compose:ui:ui-geometry"))
 
                 implementation(project(":compose:ui:ui-util"))
-                implementation(project(":collection:collection"))
+                implementation("androidx.collection:collection:1.4.2")
             }
         }
         androidMain.dependencies {
diff --git a/compose/animation/animation/build.gradle b/compose/animation/animation/build.gradle
index f968628..a2950c4 100644
--- a/compose/animation/animation/build.gradle
+++ b/compose/animation/animation/build.gradle
@@ -52,7 +52,7 @@
                 implementation(project(":compose:ui:ui-util"))
                 implementation(project(":compose:ui:ui-graphics"))
 
-                implementation(project(":collection:collection"))
+                implementation("androidx.collection:collection:1.4.2")
             }
         }
 
diff --git a/compose/foundation/foundation-layout/build.gradle b/compose/foundation/foundation-layout/build.gradle
index 0fefb0e..703c2f2 100644
--- a/compose/foundation/foundation-layout/build.gradle
+++ b/compose/foundation/foundation-layout/build.gradle
@@ -44,8 +44,8 @@
                 api(project(":compose:ui:ui"))
                 implementation(project(":compose:runtime:runtime"))
                 implementation(project(":compose:ui:ui-util"))
-                implementation(project(":collection:collection"))
                 implementation(project(":compose:ui:ui-unit"))
+                implementation("androidx.collection:collection:1.4.2")
             }
         }
 
diff --git a/compose/foundation/foundation/api/current.txt b/compose/foundation/foundation/api/current.txt
index 79430fc..3c64ee1 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -93,8 +93,10 @@
     method public static androidx.compose.foundation.CombinedClickableNode CombinedClickableNode(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, String? onLongClickLabel, kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleClick, androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, androidx.compose.foundation.IndicationNodeFactory? indicationNodeFactory, boolean enabled, String? onClickLabel, androidx.compose.ui.semantics.Role? role);
     method public static androidx.compose.ui.Modifier clickable(androidx.compose.ui.Modifier, androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
     method public static androidx.compose.ui.Modifier clickable(androidx.compose.ui.Modifier, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
-    method public static androidx.compose.ui.Modifier combinedClickable(androidx.compose.ui.Modifier, androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, optional String? onLongClickLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleClick, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
-    method public static androidx.compose.ui.Modifier combinedClickable(androidx.compose.ui.Modifier, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, optional String? onLongClickLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleClick, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
+    method public static androidx.compose.ui.Modifier combinedClickable(androidx.compose.ui.Modifier, androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, optional String? onLongClickLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleClick, optional boolean hapticFeedbackEnabled, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
+    method @Deprecated public static androidx.compose.ui.Modifier combinedClickable(androidx.compose.ui.Modifier, androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, optional String? onLongClickLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleClick, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
+    method public static androidx.compose.ui.Modifier combinedClickable(androidx.compose.ui.Modifier, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, optional String? onLongClickLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleClick, optional boolean hapticFeedbackEnabled, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
+    method @Deprecated public static androidx.compose.ui.Modifier combinedClickable(androidx.compose.ui.Modifier, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, optional String? onLongClickLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleClick, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
   }
 
   public final class ClipScrollableContainerKt {
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index 3254ecd..a3d6ffc 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -93,8 +93,10 @@
     method public static androidx.compose.foundation.CombinedClickableNode CombinedClickableNode(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, String? onLongClickLabel, kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleClick, androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, androidx.compose.foundation.IndicationNodeFactory? indicationNodeFactory, boolean enabled, String? onClickLabel, androidx.compose.ui.semantics.Role? role);
     method public static androidx.compose.ui.Modifier clickable(androidx.compose.ui.Modifier, androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
     method public static androidx.compose.ui.Modifier clickable(androidx.compose.ui.Modifier, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
-    method public static androidx.compose.ui.Modifier combinedClickable(androidx.compose.ui.Modifier, androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, optional String? onLongClickLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleClick, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
-    method public static androidx.compose.ui.Modifier combinedClickable(androidx.compose.ui.Modifier, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, optional String? onLongClickLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleClick, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
+    method public static androidx.compose.ui.Modifier combinedClickable(androidx.compose.ui.Modifier, androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, optional String? onLongClickLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleClick, optional boolean hapticFeedbackEnabled, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
+    method @Deprecated public static androidx.compose.ui.Modifier combinedClickable(androidx.compose.ui.Modifier, androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, androidx.compose.foundation.Indication? indication, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, optional String? onLongClickLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleClick, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
+    method public static androidx.compose.ui.Modifier combinedClickable(androidx.compose.ui.Modifier, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, optional String? onLongClickLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleClick, optional boolean hapticFeedbackEnabled, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
+    method @Deprecated public static androidx.compose.ui.Modifier combinedClickable(androidx.compose.ui.Modifier, optional boolean enabled, optional String? onClickLabel, optional androidx.compose.ui.semantics.Role? role, optional String? onLongClickLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onDoubleClick, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
   }
 
   public final class ClipScrollableContainerKt {
diff --git a/compose/foundation/foundation/build.gradle b/compose/foundation/foundation/build.gradle
index 196e79c..ed11b00 100644
--- a/compose/foundation/foundation/build.gradle
+++ b/compose/foundation/foundation/build.gradle
@@ -41,7 +41,7 @@
         commonMain {
             dependencies {
                 implementation(libs.kotlinStdlib)
-                api(project(":collection:collection"))
+                api("androidx.collection:collection:1.4.2")
                 api(project(":compose:animation:animation"))
                 api(project(":compose:runtime:runtime"))
                 api(project(":compose:ui:ui"))
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/CombinedClickableParameterizedKeyInputTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/CombinedClickableParameterizedKeyInputTest.kt
index d5f2f0c..65b960f 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/CombinedClickableParameterizedKeyInputTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/CombinedClickableParameterizedKeyInputTest.kt
@@ -23,6 +23,7 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.text.BasicText
+import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.ReusableContent
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -32,10 +33,13 @@
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.focus.focusTarget
+import androidx.compose.ui.hapticfeedback.HapticFeedback
+import androidx.compose.ui.hapticfeedback.HapticFeedbackType
 import androidx.compose.ui.input.InputMode.Companion.Keyboard
 import androidx.compose.ui.input.InputModeManager
 import androidx.compose.ui.input.key.Key
 import androidx.compose.ui.input.key.onKeyEvent
+import androidx.compose.ui.platform.LocalHapticFeedback
 import androidx.compose.ui.platform.LocalInputModeManager
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.ExperimentalTestApi
@@ -45,6 +49,7 @@
 import androidx.compose.ui.test.performKeyInput
 import androidx.compose.ui.test.pressKey
 import androidx.compose.ui.unit.dp
+import androidx.test.filters.LargeTest
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineScope
@@ -232,6 +237,57 @@
         }
     }
 
+    @OptIn(ExperimentalTestApi::class)
+    @Test
+    @LargeTest
+    fun longClickWithKey_doesNotTriggerHapticFeedback() {
+        var clickCounter = 0
+        var longClickCounter = 0
+        val focusRequester = FocusRequester()
+        lateinit var inputModeManager: InputModeManager
+        val performedHaptics = mutableListOf<HapticFeedbackType>()
+
+        val hapticFeedback: HapticFeedback =
+            object : HapticFeedback {
+                override fun performHapticFeedback(hapticFeedbackType: HapticFeedbackType) {
+                    performedHaptics += hapticFeedbackType
+                }
+            }
+        rule.setContent {
+            inputModeManager = LocalInputModeManager.current
+            CompositionLocalProvider(LocalHapticFeedback provides hapticFeedback) {
+                BasicText(
+                    "ClickableText",
+                    modifier =
+                        Modifier.testTag("myClickable")
+                            .focusRequester(focusRequester)
+                            .combinedClickable(
+                                onLongClick = { ++longClickCounter },
+                                onClick = { ++clickCounter },
+                                hapticFeedbackEnabled = true
+                            )
+                )
+            }
+        }
+        rule.runOnIdle {
+            inputModeManager.requestInputMode(Keyboard)
+            focusRequester.requestFocus()
+        }
+
+        rule.onNodeWithTag("myClickable").performKeyInput {
+            assertThat(inputModeManager.inputMode).isEqualTo(Keyboard)
+            // The press duration is 100ms longer than the minimum required for a long press.
+            val durationMillis: Long = viewConfiguration.longPressTimeoutMillis + 100
+            pressKey(key, durationMillis)
+        }
+
+        rule.runOnIdle {
+            assertThat(longClickCounter).isEqualTo(1)
+            assertThat(clickCounter).isEqualTo(0)
+            assertThat(performedHaptics).isEmpty()
+        }
+    }
+
     @Test
     @OptIn(ExperimentalTestApi::class)
     fun longClickWithKey_notInvokedIfFocusIsLostWhilePressed() {
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/CombinedClickableTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/CombinedClickableTest.kt
index 44c3864..165c8c5 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/CombinedClickableTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/CombinedClickableTest.kt
@@ -50,6 +50,8 @@
 import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.focus.onFocusEvent
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.hapticfeedback.HapticFeedback
+import androidx.compose.ui.hapticfeedback.HapticFeedbackType
 import androidx.compose.ui.input.InputMode
 import androidx.compose.ui.input.InputMode.Companion.Keyboard
 import androidx.compose.ui.input.InputMode.Companion.Touch
@@ -57,6 +59,7 @@
 import androidx.compose.ui.input.key.Key
 import androidx.compose.ui.platform.InspectableValue
 import androidx.compose.ui.platform.LocalFocusManager
+import androidx.compose.ui.platform.LocalHapticFeedback
 import androidx.compose.ui.platform.LocalInputModeManager
 import androidx.compose.ui.platform.isDebugInspectorInfoEnabled
 import androidx.compose.ui.platform.testTag
@@ -318,6 +321,107 @@
     }
 
     @Test
+    @LargeTest
+    fun longClick_hapticFeedbackEnabled() {
+        var counter = 0
+        val onClick: () -> Unit = { ++counter }
+        val performedHaptics = mutableListOf<HapticFeedbackType>()
+
+        val hapticFeedback: HapticFeedback =
+            object : HapticFeedback {
+                override fun performHapticFeedback(hapticFeedbackType: HapticFeedbackType) {
+                    performedHaptics += hapticFeedbackType
+                }
+            }
+
+        rule.setContent {
+            CompositionLocalProvider(LocalHapticFeedback provides hapticFeedback) {
+                Box {
+                    BasicText(
+                        "ClickableText",
+                        modifier =
+                            Modifier.testTag("myClickable").combinedClickable(
+                                onLongClick = onClick,
+                                hapticFeedbackEnabled = true
+                            ) {}
+                    )
+                }
+            }
+        }
+
+        rule.onNodeWithTag("myClickable").performTouchInput { down(center) }
+
+        // Advance a small amount of time
+        rule.mainClock.advanceTimeBy(100)
+
+        rule.onNodeWithTag("myClickable").performTouchInput { up() }
+
+        // Releasing the press before the long click timeout shouldn't trigger haptic feedback
+        rule.runOnIdle { assertThat(counter).isEqualTo(0) }
+        rule.runOnIdle { assertThat(performedHaptics).isEmpty() }
+
+        rule.onNodeWithTag("myClickable").performTouchInput { down(center) }
+
+        // Advance past the long press timeout
+        rule.mainClock.advanceTimeBy(1000)
+
+        // Long press haptic feedback should be invoked
+        rule.runOnIdle { assertThat(counter).isEqualTo(1) }
+        rule.runOnIdle {
+            assertThat(performedHaptics).containsExactly(HapticFeedbackType.LongPress)
+        }
+    }
+
+    @Test
+    @LargeTest
+    fun longClick_hapticFeedbackDisabled() {
+        var counter = 0
+        val onClick: () -> Unit = { ++counter }
+        val performedHaptics = mutableListOf<HapticFeedbackType>()
+
+        val hapticFeedback: HapticFeedback =
+            object : HapticFeedback {
+                override fun performHapticFeedback(hapticFeedbackType: HapticFeedbackType) {
+                    performedHaptics += hapticFeedbackType
+                }
+            }
+
+        rule.setContent {
+            CompositionLocalProvider(LocalHapticFeedback provides hapticFeedback) {
+                Box {
+                    BasicText(
+                        "ClickableText",
+                        modifier =
+                            Modifier.testTag("myClickable").combinedClickable(
+                                onLongClick = onClick,
+                                hapticFeedbackEnabled = false
+                            ) {}
+                    )
+                }
+            }
+        }
+
+        rule.onNodeWithTag("myClickable").performTouchInput { down(center) }
+
+        // Advance a small amount of time
+        rule.mainClock.advanceTimeBy(100)
+
+        rule.onNodeWithTag("myClickable").performTouchInput { up() }
+
+        rule.runOnIdle { assertThat(counter).isEqualTo(0) }
+        rule.runOnIdle { assertThat(performedHaptics).isEmpty() }
+
+        rule.onNodeWithTag("myClickable").performTouchInput { down(center) }
+
+        // Advance past the long press timeout
+        rule.mainClock.advanceTimeBy(1000)
+
+        // Long press should be invoked, without any haptics
+        rule.runOnIdle { assertThat(counter).isEqualTo(1) }
+        rule.runOnIdle { assertThat(performedHaptics).isEmpty() }
+    }
+
+    @Test
     @OptIn(ExperimentalTestApi::class)
     fun longClickWithEnterKeyThenDPadCenter_triggersListenerTwice() {
         var clickCounter = 0
@@ -1808,7 +1912,8 @@
                     "onClick",
                     "onDoubleClick",
                     "onLongClick",
-                    "onLongClickLabel"
+                    "onLongClickLabel",
+                    "hapticFeedbackEnabled"
                 )
         }
     }
@@ -1836,7 +1941,8 @@
                     "onLongClick",
                     "onLongClickLabel",
                     "indicationNodeFactory",
-                    "interactionSource"
+                    "interactionSource",
+                    "hapticFeedbackEnabled"
                 )
         }
     }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
index 5a2dde7..ee11b6e 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
@@ -29,6 +29,8 @@
 import androidx.compose.ui.composed
 import androidx.compose.ui.focus.Focusability
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.hapticfeedback.HapticFeedback
+import androidx.compose.ui.hapticfeedback.HapticFeedbackType
 import androidx.compose.ui.input.key.KeyEvent
 import androidx.compose.ui.input.key.KeyInputModifierNode
 import androidx.compose.ui.input.key.key
@@ -48,6 +50,7 @@
 import androidx.compose.ui.node.invalidateSemantics
 import androidx.compose.ui.node.traverseAncestors
 import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.platform.LocalHapticFeedback
 import androidx.compose.ui.platform.LocalViewConfiguration
 import androidx.compose.ui.platform.debugInspectorInfo
 import androidx.compose.ui.semantics.Role
@@ -228,6 +231,7 @@
  * @param onLongClickLabel semantic / accessibility label for the [onLongClick] action
  * @param onLongClick will be called when user long presses on the element
  * @param onDoubleClick will be called when user double clicks on the element
+ * @param hapticFeedbackEnabled whether to use the default [HapticFeedback] behavior
  * @param onClick will be called when user clicks on the element
  */
 fun Modifier.combinedClickable(
@@ -237,6 +241,56 @@
     onLongClickLabel: String? = null,
     onLongClick: (() -> Unit)? = null,
     onDoubleClick: (() -> Unit)? = null,
+    hapticFeedbackEnabled: Boolean = true,
+    onClick: () -> Unit
+) =
+    composed(
+        inspectorInfo =
+            debugInspectorInfo {
+                name = "combinedClickable"
+                properties["enabled"] = enabled
+                properties["onClickLabel"] = onClickLabel
+                properties["role"] = role
+                properties["onClick"] = onClick
+                properties["onDoubleClick"] = onDoubleClick
+                properties["onLongClick"] = onLongClick
+                properties["onLongClickLabel"] = onLongClickLabel
+                properties["hapticFeedbackEnabled"] = hapticFeedbackEnabled
+            }
+    ) {
+        val localIndication = LocalIndication.current
+        val interactionSource =
+            if (localIndication is IndicationNodeFactory) {
+                // We can fast path here as it will be created inside clickable lazily
+                null
+            } else {
+                // We need an interaction source to pass between the indication modifier and
+                // clickable, so
+                // by creating here we avoid another composed down the line
+                remember { MutableInteractionSource() }
+            }
+        Modifier.combinedClickable(
+            enabled = enabled,
+            onClickLabel = onClickLabel,
+            onLongClickLabel = onLongClickLabel,
+            onLongClick = onLongClick,
+            onDoubleClick = onDoubleClick,
+            onClick = onClick,
+            role = role,
+            indication = localIndication,
+            interactionSource = interactionSource,
+            hapticFeedbackEnabled = hapticFeedbackEnabled
+        )
+    }
+
+@Deprecated(message = "Maintained for binary compatibility", level = DeprecationLevel.HIDDEN)
+fun Modifier.combinedClickable(
+    enabled: Boolean = true,
+    onClickLabel: String? = null,
+    role: Role? = null,
+    onLongClickLabel: String? = null,
+    onLongClick: (() -> Unit)? = null,
+    onDoubleClick: (() -> Unit)? = null,
     onClick: () -> Unit
 ) =
     composed(
@@ -272,7 +326,8 @@
             onClick = onClick,
             role = role,
             indication = localIndication,
-            interactionSource = interactionSource
+            interactionSource = interactionSource,
+            hapticFeedbackEnabled = true
         )
     }
 
@@ -322,6 +377,7 @@
  * @param onLongClickLabel semantic / accessibility label for the [onLongClick] action
  * @param onLongClick will be called when user long presses on the element
  * @param onDoubleClick will be called when user double clicks on the element
+ * @param hapticFeedbackEnabled whether to use the default [HapticFeedback] behavior
  * @param onClick will be called when user clicks on the element
  */
 fun Modifier.combinedClickable(
@@ -333,6 +389,37 @@
     onLongClickLabel: String? = null,
     onLongClick: (() -> Unit)? = null,
     onDoubleClick: (() -> Unit)? = null,
+    hapticFeedbackEnabled: Boolean = true,
+    onClick: () -> Unit
+) =
+    clickableWithIndicationIfNeeded(
+        interactionSource = interactionSource,
+        indication = indication
+    ) { intSource, indicationNodeFactory ->
+        CombinedClickableElement(
+            interactionSource = intSource,
+            indicationNodeFactory = indicationNodeFactory,
+            enabled = enabled,
+            onClickLabel = onClickLabel,
+            role = role,
+            onClick = onClick,
+            onLongClickLabel = onLongClickLabel,
+            onLongClick = onLongClick,
+            onDoubleClick = onDoubleClick,
+            hapticFeedbackEnabled = hapticFeedbackEnabled
+        )
+    }
+
+@Deprecated(message = "Maintained for binary compatibility", level = DeprecationLevel.HIDDEN)
+fun Modifier.combinedClickable(
+    interactionSource: MutableInteractionSource?,
+    indication: Indication?,
+    enabled: Boolean = true,
+    onClickLabel: String? = null,
+    role: Role? = null,
+    onLongClickLabel: String? = null,
+    onLongClick: (() -> Unit)? = null,
+    onDoubleClick: (() -> Unit)? = null,
     onClick: () -> Unit
 ) =
     clickableWithIndicationIfNeeded(
@@ -348,7 +435,8 @@
             onClick = onClick,
             onLongClickLabel = onLongClickLabel,
             onLongClick = onLongClick,
-            onDoubleClick = onDoubleClick
+            onDoubleClick = onDoubleClick,
+            hapticFeedbackEnabled = true
         )
     }
 
@@ -481,7 +569,8 @@
     private val onClick: () -> Unit,
     private val onLongClickLabel: String?,
     private val onLongClick: (() -> Unit)?,
-    private val onDoubleClick: (() -> Unit)?
+    private val onDoubleClick: (() -> Unit)?,
+    private val hapticFeedbackEnabled: Boolean,
 ) : ModifierNodeElement<CombinedClickableNodeImpl>() {
     override fun create() =
         CombinedClickableNodeImpl(
@@ -489,6 +578,7 @@
             onLongClickLabel,
             onLongClick,
             onDoubleClick,
+            hapticFeedbackEnabled,
             interactionSource,
             indicationNodeFactory,
             enabled,
@@ -497,6 +587,7 @@
         )
 
     override fun update(node: CombinedClickableNodeImpl) {
+        node.hapticFeedbackEnabled = hapticFeedbackEnabled
         node.update(
             onClick,
             onLongClickLabel,
@@ -521,6 +612,7 @@
         properties["onDoubleClick"] = onDoubleClick
         properties["onLongClick"] = onLongClick
         properties["onLongClickLabel"] = onLongClickLabel
+        properties["hapticFeedbackEnabled"] = hapticFeedbackEnabled
     }
 
     override fun equals(other: Any?): Boolean {
@@ -539,6 +631,7 @@
         if (onLongClickLabel != other.onLongClickLabel) return false
         if (onLongClick !== other.onLongClick) return false
         if (onDoubleClick !== other.onDoubleClick) return false
+        if (hapticFeedbackEnabled != other.hapticFeedbackEnabled) return false
 
         return true
     }
@@ -553,6 +646,7 @@
         result = 31 * result + (onLongClickLabel?.hashCode() ?: 0)
         result = 31 * result + (onLongClick?.hashCode() ?: 0)
         result = 31 * result + (onDoubleClick?.hashCode() ?: 0)
+        result = 31 * result + hapticFeedbackEnabled.hashCode()
         return result
     }
 }
@@ -645,6 +739,7 @@
         onLongClickLabel,
         onLongClick,
         onDoubleClick,
+        hapticFeedbackEnabled = true,
         interactionSource,
         indicationNodeFactory,
         enabled,
@@ -696,6 +791,7 @@
     private var onLongClickLabel: String?,
     private var onLongClick: (() -> Unit)?,
     private var onDoubleClick: (() -> Unit)?,
+    var hapticFeedbackEnabled: Boolean,
     interactionSource: MutableInteractionSource?,
     indicationNodeFactory: IndicationNodeFactory?,
     enabled: Boolean,
@@ -722,7 +818,13 @@
                 } else null,
             onLongPress =
                 if (enabled && onLongClick != null) {
-                    { onLongClick?.invoke() }
+                    {
+                        onLongClick?.invoke()
+                        if (hapticFeedbackEnabled) {
+                            currentValueOf(LocalHapticFeedback)
+                                .performHapticFeedback(HapticFeedbackType.LongPress)
+                        }
+                    }
                 } else null,
             onPress = { offset ->
                 if (enabled) {
diff --git a/compose/material/material-ripple/build.gradle b/compose/material/material-ripple/build.gradle
index 417619c..51fe754 100644
--- a/compose/material/material-ripple/build.gradle
+++ b/compose/material/material-ripple/build.gradle
@@ -44,7 +44,7 @@
                 api(project(":compose:foundation:foundation"))
                 api(project(":compose:runtime:runtime"))
 
-                implementation(project(":collection:collection"))
+                implementation("androidx.collection:collection:1.4.2")
                 implementation(project(":compose:animation:animation"))
                 implementation(project(":compose:ui:ui-util"))
             }
diff --git a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/NavigationRailBenchmark.kt b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/NavigationRailBenchmark.kt
index 86a552e..f971352 100644
--- a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/NavigationRailBenchmark.kt
+++ b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/NavigationRailBenchmark.kt
@@ -21,16 +21,20 @@
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.MaterialExpressiveTheme
 import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.ModalExpandedNavigationRail
+import androidx.compose.material3.ModalExpandedNavigationRailState
 import androidx.compose.material3.NavigationRail
 import androidx.compose.material3.NavigationRailItem
 import androidx.compose.material3.WideNavigationRail
 import androidx.compose.material3.WideNavigationRailItem
+import androidx.compose.material3.rememberModalExpandedNavigationRailState
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.MutableIntState
 import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.testutils.LayeredComposeTestCase
 import androidx.compose.testutils.ToggleableTestCase
 import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
@@ -40,6 +44,8 @@
 import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -52,6 +58,7 @@
     private val testCaseFactory = { NavigationRailTestCase() }
     private val collapsedWideRailTestCaseFactory = { NavigationRailTestCase(true) }
     private val expandedWideRailTestCaseFactory = { NavigationRailTestCase(true, true) }
+    private val modalExpandedRailTestCaseFactory = { ModalExpandedRailTestCase() }
 
     @Test
     fun firstPixel() {
@@ -113,6 +120,19 @@
             assertOneRecomposition = false,
         )
     }
+
+    @Test
+    fun modalExpandedNavigationRail_firstPixel() {
+        benchmarkRule.benchmarkToFirstPixel(modalExpandedRailTestCaseFactory)
+    }
+
+    @Test
+    fun modalExpandedNavigationRail_stateChange() {
+        benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
+            modalExpandedRailTestCaseFactory,
+            assertOneRecomposition = false,
+        )
+    }
 }
 
 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
@@ -181,3 +201,48 @@
         }
     }
 }
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+internal class ModalExpandedRailTestCase() : LayeredComposeTestCase(), ToggleableTestCase {
+    private lateinit var state: ModalExpandedNavigationRailState
+    private lateinit var scope: CoroutineScope
+
+    @Composable
+    override fun MeasuredContent() {
+        state = rememberModalExpandedNavigationRailState()
+        scope = rememberCoroutineScope()
+
+        ModalExpandedNavigationRail(
+            onDismissRequest = {},
+            railState = state,
+        ) {
+            WideNavigationRailItem(
+                selected = true,
+                onClick = {},
+                icon = { Spacer(Modifier.size(24.dp)) },
+                railExpanded = true,
+                label = { Spacer(Modifier.size(24.dp)) }
+            )
+            WideNavigationRailItem(
+                selected = false,
+                onClick = {},
+                icon = { Spacer(Modifier.size(24.dp)) },
+                railExpanded = true,
+                label = { Spacer(Modifier.size(24.dp)) }
+            )
+        }
+    }
+
+    @Composable
+    override fun ContentWrappers(content: @Composable () -> Unit) {
+        MaterialExpressiveTheme { content() }
+    }
+
+    override fun toggleState() {
+        if (state.isOpen) {
+            scope.launch { state.close() }
+        } else {
+            scope.launch { state.open() }
+        }
+    }
+}
diff --git a/compose/material3/material3/api/current.txt b/compose/material3/material3/api/current.txt
index 07af007..0544251c 100644
--- a/compose/material3/material3/api/current.txt
+++ b/compose/material3/material3/api/current.txt
@@ -244,21 +244,15 @@
   }
 
   public final class ButtonShapes {
-    ctor public ButtonShapes(androidx.compose.ui.graphics.Shape shape, androidx.compose.ui.graphics.Shape pressedShape, androidx.compose.ui.graphics.Shape hoveredShape, androidx.compose.ui.graphics.Shape focusedShape, androidx.compose.ui.graphics.Shape checkedShape);
+    ctor public ButtonShapes(androidx.compose.ui.graphics.Shape shape, androidx.compose.ui.graphics.Shape pressedShape, androidx.compose.ui.graphics.Shape checkedShape);
     method public androidx.compose.ui.graphics.Shape component1();
     method public androidx.compose.ui.graphics.Shape component2();
     method public androidx.compose.ui.graphics.Shape component3();
-    method public androidx.compose.ui.graphics.Shape component4();
-    method public androidx.compose.ui.graphics.Shape component5();
-    method public androidx.compose.material3.ButtonShapes copy(androidx.compose.ui.graphics.Shape shape, androidx.compose.ui.graphics.Shape pressedShape, androidx.compose.ui.graphics.Shape hoveredShape, androidx.compose.ui.graphics.Shape focusedShape, androidx.compose.ui.graphics.Shape checkedShape);
+    method public androidx.compose.material3.ButtonShapes copy(androidx.compose.ui.graphics.Shape shape, androidx.compose.ui.graphics.Shape pressedShape, androidx.compose.ui.graphics.Shape checkedShape);
     method public androidx.compose.ui.graphics.Shape getCheckedShape();
-    method public androidx.compose.ui.graphics.Shape getFocusedShape();
-    method public androidx.compose.ui.graphics.Shape getHoveredShape();
     method public androidx.compose.ui.graphics.Shape getPressedShape();
     method public androidx.compose.ui.graphics.Shape getShape();
     property public final androidx.compose.ui.graphics.Shape checkedShape;
-    property public final androidx.compose.ui.graphics.Shape focusedShape;
-    property public final androidx.compose.ui.graphics.Shape hoveredShape;
     property public final androidx.compose.ui.graphics.Shape pressedShape;
     property public final androidx.compose.ui.graphics.Shape shape;
   }
@@ -2552,18 +2546,12 @@
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getCheckedShape();
     method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getElevatedCheckedShape();
-    method public androidx.compose.ui.graphics.Shape getElevatedFocusedShape();
-    method public androidx.compose.ui.graphics.Shape getElevatedHoveredShape();
     method public androidx.compose.ui.graphics.Shape getElevatedPressedShape();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getElevatedShape();
-    method public androidx.compose.ui.graphics.Shape getFocusedShape();
-    method public androidx.compose.ui.graphics.Shape getHoveredShape();
     method public float getIconSize();
     method public float getIconSpacing();
     method public float getMinHeight();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getOutlinedCheckedShape();
-    method public androidx.compose.ui.graphics.Shape getOutlinedFocusedShape();
-    method public androidx.compose.ui.graphics.Shape getOutlinedHoveredShape();
     method public androidx.compose.ui.graphics.Shape getOutlinedPressedShape();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getOutlinedShape();
     method public androidx.compose.ui.graphics.Shape getPressedShape();
@@ -2571,13 +2559,11 @@
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getSquareShape();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getTonalCheckedShape();
-    method public androidx.compose.ui.graphics.Shape getTonalFocusedShape();
-    method public androidx.compose.ui.graphics.Shape getTonalHoveredShape();
     method public androidx.compose.ui.graphics.Shape getTonalPressedShape();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getTonalShape();
     method @androidx.compose.runtime.Composable public androidx.compose.material3.ToggleButtonColors outlinedToggleButtonColors();
     method @androidx.compose.runtime.Composable public androidx.compose.material3.ToggleButtonColors outlinedToggleButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor, optional long checkedContainerColor, optional long checkedContentColor);
-    method public androidx.compose.material3.ButtonShapes shapes(androidx.compose.ui.graphics.Shape shape, androidx.compose.ui.graphics.Shape pressedShape, androidx.compose.ui.graphics.Shape hoverShape, androidx.compose.ui.graphics.Shape focusShape, androidx.compose.ui.graphics.Shape checkedShape);
+    method public androidx.compose.material3.ButtonShapes shapes(androidx.compose.ui.graphics.Shape shape, androidx.compose.ui.graphics.Shape pressedShape, androidx.compose.ui.graphics.Shape checkedShape);
     method @androidx.compose.runtime.Composable public androidx.compose.material3.ToggleButtonColors toggleButtonColors();
     method @androidx.compose.runtime.Composable public androidx.compose.material3.ToggleButtonColors toggleButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor, optional long checkedContainerColor, optional long checkedContentColor);
     method @androidx.compose.runtime.Composable public androidx.compose.material3.ToggleButtonColors tonalToggleButtonColors();
@@ -2588,15 +2574,9 @@
     property public final float MinHeight;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape checkedShape;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape elevatedCheckedShape;
-    property public final androidx.compose.ui.graphics.Shape elevatedFocusedShape;
-    property public final androidx.compose.ui.graphics.Shape elevatedHoveredShape;
     property public final androidx.compose.ui.graphics.Shape elevatedPressedShape;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape elevatedShape;
-    property public final androidx.compose.ui.graphics.Shape focusedShape;
-    property public final androidx.compose.ui.graphics.Shape hoveredShape;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape outlinedCheckedShape;
-    property public final androidx.compose.ui.graphics.Shape outlinedFocusedShape;
-    property public final androidx.compose.ui.graphics.Shape outlinedHoveredShape;
     property public final androidx.compose.ui.graphics.Shape outlinedPressedShape;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape outlinedShape;
     property public final androidx.compose.ui.graphics.Shape pressedShape;
@@ -2604,8 +2584,6 @@
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape squareShape;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape tonalCheckedShape;
-    property public final androidx.compose.ui.graphics.Shape tonalFocusedShape;
-    property public final androidx.compose.ui.graphics.Shape tonalHoveredShape;
     property public final androidx.compose.ui.graphics.Shape tonalPressedShape;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape tonalShape;
     field public static final androidx.compose.material3.ToggleButtonDefaults INSTANCE;
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index 07af007..0544251c 100644
--- a/compose/material3/material3/api/restricted_current.txt
+++ b/compose/material3/material3/api/restricted_current.txt
@@ -244,21 +244,15 @@
   }
 
   public final class ButtonShapes {
-    ctor public ButtonShapes(androidx.compose.ui.graphics.Shape shape, androidx.compose.ui.graphics.Shape pressedShape, androidx.compose.ui.graphics.Shape hoveredShape, androidx.compose.ui.graphics.Shape focusedShape, androidx.compose.ui.graphics.Shape checkedShape);
+    ctor public ButtonShapes(androidx.compose.ui.graphics.Shape shape, androidx.compose.ui.graphics.Shape pressedShape, androidx.compose.ui.graphics.Shape checkedShape);
     method public androidx.compose.ui.graphics.Shape component1();
     method public androidx.compose.ui.graphics.Shape component2();
     method public androidx.compose.ui.graphics.Shape component3();
-    method public androidx.compose.ui.graphics.Shape component4();
-    method public androidx.compose.ui.graphics.Shape component5();
-    method public androidx.compose.material3.ButtonShapes copy(androidx.compose.ui.graphics.Shape shape, androidx.compose.ui.graphics.Shape pressedShape, androidx.compose.ui.graphics.Shape hoveredShape, androidx.compose.ui.graphics.Shape focusedShape, androidx.compose.ui.graphics.Shape checkedShape);
+    method public androidx.compose.material3.ButtonShapes copy(androidx.compose.ui.graphics.Shape shape, androidx.compose.ui.graphics.Shape pressedShape, androidx.compose.ui.graphics.Shape checkedShape);
     method public androidx.compose.ui.graphics.Shape getCheckedShape();
-    method public androidx.compose.ui.graphics.Shape getFocusedShape();
-    method public androidx.compose.ui.graphics.Shape getHoveredShape();
     method public androidx.compose.ui.graphics.Shape getPressedShape();
     method public androidx.compose.ui.graphics.Shape getShape();
     property public final androidx.compose.ui.graphics.Shape checkedShape;
-    property public final androidx.compose.ui.graphics.Shape focusedShape;
-    property public final androidx.compose.ui.graphics.Shape hoveredShape;
     property public final androidx.compose.ui.graphics.Shape pressedShape;
     property public final androidx.compose.ui.graphics.Shape shape;
   }
@@ -2552,18 +2546,12 @@
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getCheckedShape();
     method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getElevatedCheckedShape();
-    method public androidx.compose.ui.graphics.Shape getElevatedFocusedShape();
-    method public androidx.compose.ui.graphics.Shape getElevatedHoveredShape();
     method public androidx.compose.ui.graphics.Shape getElevatedPressedShape();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getElevatedShape();
-    method public androidx.compose.ui.graphics.Shape getFocusedShape();
-    method public androidx.compose.ui.graphics.Shape getHoveredShape();
     method public float getIconSize();
     method public float getIconSpacing();
     method public float getMinHeight();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getOutlinedCheckedShape();
-    method public androidx.compose.ui.graphics.Shape getOutlinedFocusedShape();
-    method public androidx.compose.ui.graphics.Shape getOutlinedHoveredShape();
     method public androidx.compose.ui.graphics.Shape getOutlinedPressedShape();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getOutlinedShape();
     method public androidx.compose.ui.graphics.Shape getPressedShape();
@@ -2571,13 +2559,11 @@
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getSquareShape();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getTonalCheckedShape();
-    method public androidx.compose.ui.graphics.Shape getTonalFocusedShape();
-    method public androidx.compose.ui.graphics.Shape getTonalHoveredShape();
     method public androidx.compose.ui.graphics.Shape getTonalPressedShape();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getTonalShape();
     method @androidx.compose.runtime.Composable public androidx.compose.material3.ToggleButtonColors outlinedToggleButtonColors();
     method @androidx.compose.runtime.Composable public androidx.compose.material3.ToggleButtonColors outlinedToggleButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor, optional long checkedContainerColor, optional long checkedContentColor);
-    method public androidx.compose.material3.ButtonShapes shapes(androidx.compose.ui.graphics.Shape shape, androidx.compose.ui.graphics.Shape pressedShape, androidx.compose.ui.graphics.Shape hoverShape, androidx.compose.ui.graphics.Shape focusShape, androidx.compose.ui.graphics.Shape checkedShape);
+    method public androidx.compose.material3.ButtonShapes shapes(androidx.compose.ui.graphics.Shape shape, androidx.compose.ui.graphics.Shape pressedShape, androidx.compose.ui.graphics.Shape checkedShape);
     method @androidx.compose.runtime.Composable public androidx.compose.material3.ToggleButtonColors toggleButtonColors();
     method @androidx.compose.runtime.Composable public androidx.compose.material3.ToggleButtonColors toggleButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor, optional long checkedContainerColor, optional long checkedContentColor);
     method @androidx.compose.runtime.Composable public androidx.compose.material3.ToggleButtonColors tonalToggleButtonColors();
@@ -2588,15 +2574,9 @@
     property public final float MinHeight;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape checkedShape;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape elevatedCheckedShape;
-    property public final androidx.compose.ui.graphics.Shape elevatedFocusedShape;
-    property public final androidx.compose.ui.graphics.Shape elevatedHoveredShape;
     property public final androidx.compose.ui.graphics.Shape elevatedPressedShape;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape elevatedShape;
-    property public final androidx.compose.ui.graphics.Shape focusedShape;
-    property public final androidx.compose.ui.graphics.Shape hoveredShape;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape outlinedCheckedShape;
-    property public final androidx.compose.ui.graphics.Shape outlinedFocusedShape;
-    property public final androidx.compose.ui.graphics.Shape outlinedHoveredShape;
     property public final androidx.compose.ui.graphics.Shape outlinedPressedShape;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape outlinedShape;
     property public final androidx.compose.ui.graphics.Shape pressedShape;
@@ -2604,8 +2584,6 @@
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape shape;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape squareShape;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape tonalCheckedShape;
-    property public final androidx.compose.ui.graphics.Shape tonalFocusedShape;
-    property public final androidx.compose.ui.graphics.Shape tonalHoveredShape;
     property public final androidx.compose.ui.graphics.Shape tonalPressedShape;
     property @androidx.compose.runtime.Composable public final androidx.compose.ui.graphics.Shape tonalShape;
     field public static final androidx.compose.material3.ToggleButtonDefaults INSTANCE;
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ToggleButtonSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ToggleButtonSamples.kt
index 8460484..b215df5 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ToggleButtonSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ToggleButtonSamples.kt
@@ -57,8 +57,6 @@
         ButtonShapes(
             shape = ToggleButtonDefaults.roundShape,
             pressedShape = ToggleButtonDefaults.pressedShape,
-            hoveredShape = ToggleButtonDefaults.hoveredShape,
-            focusedShape = ToggleButtonDefaults.focusedShape,
             checkedShape = ToggleButtonDefaults.squareShape
         )
     ToggleButton(checked = checked, onCheckedChange = { checked = it }, shapes = shapes) {
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ToggleButton.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ToggleButton.kt
index ed0c3b7..8ce3633 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ToggleButton.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ToggleButton.kt
@@ -21,11 +21,9 @@
 import androidx.compose.animation.core.SpringSpec
 import androidx.compose.animation.core.spring
 import androidx.compose.foundation.BorderStroke
-import androidx.compose.foundation.interaction.FocusInteraction
-import androidx.compose.foundation.interaction.HoverInteraction
 import androidx.compose.foundation.interaction.Interaction
 import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.interaction.PressInteraction
+import androidx.compose.foundation.interaction.collectIsPressedAsState
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Row
@@ -45,7 +43,6 @@
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
@@ -63,15 +60,14 @@
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
 import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.launch
 
 /**
  * TODO link to mio page when available.
  *
  * Toggle button is a toggleable button that switches between primary and tonal colors depending on
- * [checked]'s value. It also morphs between the five shapes provided in [shapes] depending on the
- * state of the interaction with the toggle button as long as the five shapes provided our
+ * [checked]'s value. It also morphs between the three shapes provided in [shapes] depending on the
+ * state of the interaction with the toggle button as long as the three shapes provided our
  * [CornerBasedShape]s. If a shape in [shapes] isn't a [CornerBasedShape], then toggle button will
  * toggle between the [ButtonShapes] according to user interaction.
  *
@@ -120,8 +116,6 @@
         ToggleButtonDefaults.shapes(
             ToggleButtonDefaults.shape,
             ToggleButtonDefaults.pressedShape,
-            ToggleButtonDefaults.hoveredShape,
-            ToggleButtonDefaults.focusedShape,
             ToggleButtonDefaults.checkedShape
         ),
     colors: ToggleButtonColors = ToggleButtonDefaults.toggleButtonColors(),
@@ -136,66 +130,31 @@
     val isCornerBasedShape =
         shapes.shape is CornerBasedShape &&
             shapes.checkedShape is CornerBasedShape &&
-            shapes.pressedShape is CornerBasedShape &&
-            shapes.hoveredShape is CornerBasedShape &&
-            shapes.focusedShape is CornerBasedShape
+            shapes.pressedShape is CornerBasedShape
 
-    var pressed by remember { mutableStateOf(false) }
-    var hovered by remember { mutableStateOf(false) }
-    var focused by remember { mutableStateOf(false) }
+    val pressed by interactionSource.collectIsPressedAsState()
 
     val state: AnimatedShapeState? =
         if (isCornerBasedShape) {
             val defaultShape = shapes.shape as CornerBasedShape
             val pressedShape = shapes.pressedShape as CornerBasedShape
-            val hoveredShape = shapes.hoveredShape as CornerBasedShape
-            val focusedShape = shapes.focusedShape as CornerBasedShape
             val checkedShape = shapes.checkedShape as CornerBasedShape
             remember {
                 AnimatedShapeState(
                     startShape = if (checked) checkedShape else defaultShape,
                     defaultShape = defaultShape,
                     pressedShape = pressedShape,
-                    hoveredShape = hoveredShape,
-                    focusedShape = focusedShape,
                     checkedShape = checkedShape,
                     spring(),
                 )
             }
         } else null
 
-    LaunchedEffect(interactionSource) {
-        interactionSource.interactions.collectLatest { interaction ->
-            when (interaction) {
-                is PressInteraction.Press -> {
-                    pressed = true
-                }
-                is HoverInteraction.Enter -> {
-                    hovered = true
-                }
-                is FocusInteraction.Focus -> {
-                    focused = true
-                }
-                is PressInteraction.Release,
-                is PressInteraction.Cancel -> {
-                    pressed = false
-                }
-                is HoverInteraction.Exit -> {
-                    hovered = false
-                }
-                is FocusInteraction.Unfocus -> {
-                    focused = false
-                }
-            }
-        }
-    }
-
     val containerColor = colors.containerColor(enabled, checked)
     val contentColor = colors.contentColor(enabled, checked)
     val shadowElevation = elevation?.shadowElevation(enabled, interactionSource)?.value ?: 0.dp
 
-    val buttonShape =
-        shapeByInteraction(isCornerBasedShape, state, shapes, pressed, hovered, focused, checked)
+    val buttonShape = shapeByInteraction(isCornerBasedShape, state, shapes, pressed, checked)
 
     Surface(
         checked = checked,
@@ -234,8 +193,8 @@
  * TODO link to mio page when available.
  *
  * Toggle button is a toggleable button that switches between primary and tonal colors depending on
- * [checked]'s value. It also morphs between the five shapes provided in [shapes] depending on the
- * state of the interaction with the toggle button as long as the five shapes provided our
+ * [checked]'s value. It also morphs between the three shapes provided in [shapes] depending on the
+ * state of the interaction with the toggle button as long as the three shapes provided our
  * [CornerBasedShape]s. If a shape in [shapes] isn't a [CornerBasedShape], then toggle button will
  * toggle between the [ButtonShapes] according to user interaction.
  *
@@ -282,8 +241,6 @@
         ToggleButtonDefaults.shapes(
             ToggleButtonDefaults.elevatedShape,
             ToggleButtonDefaults.elevatedPressedShape,
-            ToggleButtonDefaults.elevatedHoveredShape,
-            ToggleButtonDefaults.elevatedFocusedShape,
             ToggleButtonDefaults.elevatedCheckedShape
         ),
     colors: ToggleButtonColors = ToggleButtonDefaults.elevatedToggleButtonColors(),
@@ -311,8 +268,8 @@
  * TODO link to mio page when available.
  *
  * Toggle button is a toggleable button that switches between primary and tonal colors depending on
- * [checked]'s value. It also morphs between the five shapes provided in [shapes] depending on the
- * state of the interaction with the toggle button as long as the five shapes provided our
+ * [checked]'s value. It also morphs between the three shapes provided in [shapes] depending on the
+ * state of the interaction with the toggle button as long as the three shapes provided our
  * [CornerBasedShape]s. If a shape in [shapes] isn't a [CornerBasedShape], then toggle button will
  * toggle between the [ButtonShapes] according to user interaction.
  *
@@ -362,8 +319,6 @@
         ToggleButtonDefaults.shapes(
             ToggleButtonDefaults.tonalShape,
             ToggleButtonDefaults.tonalPressedShape,
-            ToggleButtonDefaults.tonalHoveredShape,
-            ToggleButtonDefaults.tonalFocusedShape,
             ToggleButtonDefaults.tonalCheckedShape
         ),
     colors: ToggleButtonColors = ToggleButtonDefaults.tonalToggleButtonColors(),
@@ -391,8 +346,8 @@
  * TODO link to mio page when available.
  *
  * Toggle button is a toggleable button that switches between primary and tonal colors depending on
- * [checked]'s value. It also morphs between the five shapes provided in [shapes] depending on the
- * state of the interaction with the toggle button as long as the five shapes provided our
+ * [checked]'s value. It also morphs between the three shapes provided in [shapes] depending on the
+ * state of the interaction with the toggle button as long as the three shapes provided our
  * [CornerBasedShape]s. If a shape in [shapes] isn't a [CornerBasedShape], then toggle button will
  * toggle between the [ButtonShapes] according to user interaction.
  *
@@ -440,8 +395,6 @@
         ToggleButtonDefaults.shapes(
             ToggleButtonDefaults.outlinedShape,
             ToggleButtonDefaults.outlinedPressedShape,
-            ToggleButtonDefaults.outlinedHoveredShape,
-            ToggleButtonDefaults.outlinedFocusedShape,
             ToggleButtonDefaults.outlinedCheckedShape
         ),
     colors: ToggleButtonColors = ToggleButtonDefaults.outlinedToggleButtonColors(),
@@ -506,17 +459,10 @@
      *
      * @param shape the unchecked shape for [ButtonShapes]
      * @param pressedShape the unchecked shape for [ButtonShapes]
-     * @param hoverShape the unchecked shape for [ButtonShapes]
-     * @param focusShape the unchecked shape for [ButtonShapes]
      * @param checkedShape the unchecked shape for [ButtonShapes]
      */
-    fun shapes(
-        shape: Shape,
-        pressedShape: Shape,
-        hoverShape: Shape,
-        focusShape: Shape,
-        checkedShape: Shape
-    ): ButtonShapes = ButtonShapes(shape, pressedShape, hoverShape, focusShape, checkedShape)
+    fun shapes(shape: Shape, pressedShape: Shape, checkedShape: Shape): ButtonShapes =
+        ButtonShapes(shape, pressedShape, checkedShape)
 
     /** A round shape that can be used for all [ToggleButton]s and its variants */
     val roundShape: Shape
@@ -554,30 +500,6 @@
     /** The default pressed shape for [OutlinedToggleButton] */
     val outlinedPressedShape: Shape = RoundedCornerShape(6.dp)
 
-    /** The default hovered shape for [ToggleButton] */
-    val hoveredShape: Shape = RoundedCornerShape(6.dp)
-
-    /** The default hovered shape for [ElevatedToggleButton] */
-    val elevatedHoveredShape: Shape = RoundedCornerShape(6.dp)
-
-    /** The default hovered shape for [TonalToggleButton] */
-    val tonalHoveredShape: Shape = RoundedCornerShape(6.dp)
-
-    /** The default hovered shape for [OutlinedToggleButton] */
-    val outlinedHoveredShape: Shape = RoundedCornerShape(6.dp)
-
-    /** The default focused shape for [ToggleButton] */
-    val focusedShape: Shape = RoundedCornerShape(6.dp)
-
-    /** The default focused shape for [ElevatedToggleButton] */
-    val elevatedFocusedShape: Shape = RoundedCornerShape(6.dp)
-
-    /** The default focused shape for [TonalToggleButton] */
-    val tonalFocusedShape: Shape = RoundedCornerShape(6.dp)
-
-    /** The default focused shape for [OutlinedToggleButton] */
-    val outlinedFocusedShape: Shape = RoundedCornerShape(6.dp)
-
     // TODO: Change this to the new ButtonSmallTokens.SelectedShape when available
     /** The default checked shape for [ToggleButton] */
     val checkedShape: Shape
@@ -939,17 +861,9 @@
  *
  * @property shape is the unchecked shape.
  * @property pressedShape is the pressed shape.
- * @property hoveredShape is the hovered shape.
- * @property focusedShape is the focused shape.
  * @property checkedShape is the checked shape.
  */
-data class ButtonShapes(
-    val shape: Shape,
-    val pressedShape: Shape,
-    val hoveredShape: Shape,
-    val focusedShape: Shape,
-    val checkedShape: Shape
-)
+data class ButtonShapes(val shape: Shape, val pressedShape: Shape, val checkedShape: Shape)
 
 @Composable
 private fun shapeByInteraction(
@@ -957,19 +871,13 @@
     state: AnimatedShapeState?,
     shapes: ButtonShapes,
     pressed: Boolean,
-    hovered: Boolean,
-    focused: Boolean,
     checked: Boolean
 ): Shape {
     return if (isCornerBasedShape) {
         if (state != null) {
-            LaunchedEffect(pressed, hovered, focused, checked) {
+            LaunchedEffect(pressed, checked) {
                 if (pressed) {
                     state.animateToPressed()
-                } else if (hovered) {
-                    state.animateToHovered()
-                } else if (focused) {
-                    state.animateToFocused()
                 } else if (checked) {
                     state.animateToChecked()
                 } else {
@@ -982,10 +890,6 @@
         }
     } else if (pressed) {
         shapes.pressedShape
-    } else if (hovered) {
-        shapes.hoveredShape
-    } else if (focused) {
-        shapes.focusedShape
     } else if (checked) {
         shapes.checkedShape
     } else {
@@ -997,6 +901,7 @@
 private fun rememberAnimatedShape(state: AnimatedShapeState): Shape {
     val density = LocalDensity.current
     state.density = density
+
     return remember(density) {
         object : Shape {
 
@@ -1028,8 +933,6 @@
     val startShape: CornerBasedShape,
     val defaultShape: CornerBasedShape,
     val pressedShape: CornerBasedShape,
-    val hoveredShape: CornerBasedShape,
-    val focusedShape: CornerBasedShape,
     val checkedShape: CornerBasedShape,
     val spec: SpringSpec<Float>,
 ) {
@@ -1063,10 +966,6 @@
 
     suspend fun animateToDefault() = animateToShape(defaultShape)
 
-    suspend fun animateToHovered() = animateToShape(hoveredShape)
-
-    suspend fun animateToFocused() = animateToShape(focusedShape)
-
     private suspend fun animateToShape(shape: CornerBasedShape) = coroutineScope {
         launch { topStart?.animateTo(shape.topStart.toPx(size, density), spec) }
         launch { topEnd?.animateTo(shape.topEnd.toPx(size, density), spec) }
diff --git a/compose/runtime/runtime/build.gradle b/compose/runtime/runtime/build.gradle
index a82f5ac..fc61115 100644
--- a/compose/runtime/runtime/build.gradle
+++ b/compose/runtime/runtime/build.gradle
@@ -43,7 +43,7 @@
             dependencies {
                 implementation(libs.kotlinStdlibCommon)
                 implementation(libs.kotlinCoroutinesCore)
-                implementation(project(":collection:collection"))
+                implementation("androidx.collection:collection:1.4.2")
             }
         }
 
diff --git a/core/core/src/androidTest/java/androidx/core/view/WindowInsetsControllerCompatActivityTest.kt b/core/core/src/androidTest/java/androidx/core/view/WindowInsetsControllerCompatActivityTest.kt
index 2f07d423..c58bad7 100644
--- a/core/core/src/androidTest/java/androidx/core/view/WindowInsetsControllerCompatActivityTest.kt
+++ b/core/core/src/androidTest/java/androidx/core/view/WindowInsetsControllerCompatActivityTest.kt
@@ -120,37 +120,6 @@
 
     /** IME visibility is only reliable on API 23+, where we have access to the root WindowInsets */
     @SdkSuppress(minSdkVersion = 23)
-    @Test
-    public fun do_not_show_IME_if_TextView_in_dialog_not_focused() {
-        val dialog =
-            scenario.withActivity {
-                object : Dialog(this) {
-                        override fun onAttachedToWindow() {
-                            super.onAttachedToWindow()
-                            WindowCompat.setDecorFitsSystemWindows(window!!, false)
-                        }
-                    }
-                    .apply { setContentView(R.layout.insets_compat_activity) }
-            }
-
-        val editText = dialog.findViewById<TextView>(R.id.edittext)
-
-        // We hide the edit text to ensure it won't be automatically focused
-        scenario.onActivity {
-            dialog.show()
-            editText.visibility = View.GONE
-            assertThat(editText.isFocused, `is`(false))
-        }
-
-        val type = WindowInsetsCompat.Type.ime()
-        scenario.onActivity {
-            WindowCompat.getInsetsController(dialog.window!!, editText).show(type)
-        }
-        container.assertInsetsVisibility(type, false)
-    }
-
-    /** IME visibility is only reliable on API 23+, where we have access to the root WindowInsets */
-    @SdkSuppress(minSdkVersion = 23)
     @Ignore("b/294556594")
     @Test
     fun show_IME_fromEditText_in_dialog() {
diff --git a/core/core/src/main/java/androidx/core/graphics/TypefaceCompat.java b/core/core/src/main/java/androidx/core/graphics/TypefaceCompat.java
index b165f5c..fcf65ad 100644
--- a/core/core/src/main/java/androidx/core/graphics/TypefaceCompat.java
+++ b/core/core/src/main/java/androidx/core/graphics/TypefaceCompat.java
@@ -78,7 +78,8 @@
     }
 
     /**
-     * Cache for Typeface objects dynamically loaded from assets.
+     * Cache for Typeface objects dynamically loaded from assets,
+     * keyed by {@link #createResourceUid(Resources, int, String, int, int)}
      */
     private static final LruCache<String, Typeface> sTypefaceCache = new LruCache<>(16);
 
diff --git a/core/core/src/main/java/androidx/core/provider/FontRequestWorker.java b/core/core/src/main/java/androidx/core/provider/FontRequestWorker.java
index 761c25b..509de8d 100644
--- a/core/core/src/main/java/androidx/core/provider/FontRequestWorker.java
+++ b/core/core/src/main/java/androidx/core/provider/FontRequestWorker.java
@@ -58,6 +58,9 @@
 
     private FontRequestWorker() {}
 
+    /**
+     * Keyed by {@link #createCacheId(List, int)}
+     */
     static final LruCache<String, Typeface> sTypefaceCache = new LruCache<>(16);
 
     private static final ExecutorService DEFAULT_EXECUTOR_SERVICE = RequestExecutor
diff --git a/core/core/src/main/java/androidx/core/view/WindowCompat.java b/core/core/src/main/java/androidx/core/view/WindowCompat.java
index 679f4cf..3c16649 100644
--- a/core/core/src/main/java/androidx/core/view/WindowCompat.java
+++ b/core/core/src/main/java/androidx/core/view/WindowCompat.java
@@ -164,9 +164,13 @@
 
         static void setDecorFitsSystemWindows(@NonNull Window window,
                 final boolean decorFitsSystemWindows) {
+            final int stableFlag = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
+
             final View decorView = window.getDecorView();
             final int sysUiVis = decorView.getSystemUiVisibility();
-            decorView.setSystemUiVisibility(sysUiVis | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+            decorView.setSystemUiVisibility(decorFitsSystemWindows
+                    ? sysUiVis & ~stableFlag
+                    : sysUiVis | stableFlag);
             window.setDecorFitsSystemWindows(decorFitsSystemWindows);
         }
     }
diff --git a/core/core/src/main/java/androidx/core/view/WindowInsetsControllerCompat.java b/core/core/src/main/java/androidx/core/view/WindowInsetsControllerCompat.java
index f7400fa..e74ec94 100644
--- a/core/core/src/main/java/androidx/core/view/WindowInsetsControllerCompat.java
+++ b/core/core/src/main/java/androidx/core/view/WindowInsetsControllerCompat.java
@@ -393,6 +393,8 @@
     }
 
     private static class Impl {
+        static final int KEY_BEHAVIOR = 356039078;
+
         Impl() {
             //private
         }
@@ -412,7 +414,7 @@
         }
 
         int getSystemBarsBehavior() {
-            return 0;
+            return BEHAVIOR_DEFAULT;
         }
 
         public boolean isAppearanceLightStatusBars() {
@@ -533,6 +535,7 @@
 
         @Override
         void setSystemBarsBehavior(int behavior) {
+            mWindow.getDecorView().setTag(KEY_BEHAVIOR, behavior);
             switch (behavior) {
                 case BEHAVIOR_DEFAULT:
                     unsetSystemUiFlag(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
@@ -551,7 +554,8 @@
 
         @Override
         int getSystemBarsBehavior() {
-            return 0;
+            final Object behaviorTag = mWindow.getDecorView().getTag(KEY_BEHAVIOR);
+            return behaviorTag != null ? (int) behaviorTag : BEHAVIOR_DEFAULT;
         }
 
         @Override
@@ -777,7 +781,28 @@
          */
         @Override
         void setSystemBarsBehavior(@Behavior int behavior) {
-            mInsetsController.setSystemBarsBehavior(behavior);
+            if (mWindow != null) {
+                // Use the legacy way to control the behavior as a workaround because API 30 has a
+                // bug that the behavior might be cleared unexpectedly after setting a LayoutParam
+                // to a window.
+                mWindow.getDecorView().setTag(KEY_BEHAVIOR, behavior);
+                switch (behavior) {
+                    case BEHAVIOR_DEFAULT:
+                        unsetSystemUiFlag(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
+                        setSystemUiFlag(View.SYSTEM_UI_FLAG_IMMERSIVE);
+                        break;
+                    case BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE:
+                        unsetSystemUiFlag(View.SYSTEM_UI_FLAG_IMMERSIVE);
+                        setSystemUiFlag(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
+                        break;
+                    case BEHAVIOR_SHOW_BARS_BY_TOUCH:
+                        unsetSystemUiFlag(View.SYSTEM_UI_FLAG_IMMERSIVE
+                                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
+                        break;
+                }
+            } else {
+                mInsetsController.setSystemBarsBehavior(behavior);
+            }
         }
 
         /**
@@ -790,7 +815,12 @@
         @Override
         @Behavior
         int getSystemBarsBehavior() {
-            return mInsetsController.getSystemBarsBehavior();
+            if (mWindow != null) {
+                final Object behaviorTag = mWindow.getDecorView().getTag(KEY_BEHAVIOR);
+                return behaviorTag != null ? (int) behaviorTag : BEHAVIOR_DEFAULT;
+            } else {
+                return mInsetsController.getSystemBarsBehavior();
+            }
         }
 
         @Override
@@ -839,8 +869,48 @@
         }
     }
 
+    @RequiresApi(31)
+    private static class Impl31 extends Impl30 {
+
+        Impl31(@NonNull Window window,
+                @NonNull WindowInsetsControllerCompat compatController,
+                @NonNull SoftwareKeyboardControllerCompat softwareKeyboardControllerCompat) {
+            super(window, compatController, softwareKeyboardControllerCompat);
+        }
+
+        Impl31(@NonNull WindowInsetsController insetsController,
+                @NonNull WindowInsetsControllerCompat compatController,
+                @NonNull SoftwareKeyboardControllerCompat softwareKeyboardControllerCompat) {
+            super(insetsController, compatController, softwareKeyboardControllerCompat);
+        }
+
+        /**
+         * Controls the behavior of system bars.
+         *
+         * @param behavior Determines how the bars behave when being hidden by the application.
+         * @see #getSystemBarsBehavior
+         */
+        @Override
+        void setSystemBarsBehavior(@Behavior int behavior) {
+            mInsetsController.setSystemBarsBehavior(behavior);
+        }
+
+        /**
+         * Retrieves the requested behavior of system bars.
+         *
+         * @return the system bar behavior controlled by this window.
+         * @see #setSystemBarsBehavior(int)
+         */
+        @SuppressLint("WrongConstant")
+        @Override
+        @Behavior
+        int getSystemBarsBehavior() {
+            return mInsetsController.getSystemBarsBehavior();
+        }
+    }
+
     @RequiresApi(35)
-    private static class Impl35 extends Impl30 {
+    private static class Impl35 extends Impl31 {
 
         Impl35(@NonNull Window window,
                 @NonNull WindowInsetsControllerCompat compatController,
diff --git a/datastore/datastore-core/build.gradle b/datastore/datastore-core/build.gradle
index 0a0a304..d7eaded0 100644
--- a/datastore/datastore-core/build.gradle
+++ b/datastore/datastore-core/build.gradle
@@ -80,7 +80,7 @@
             dependencies {
                 api(libs.kotlinStdlib)
                 api(libs.kotlinCoroutinesCore)
-                api(project(":annotation:annotation"))
+                api("androidx.annotation:annotation:1.8.1")
             }
         }
 
diff --git a/datastore/datastore/build.gradle b/datastore/datastore/build.gradle
index c59b19f..05776a1 100644
--- a/datastore/datastore/build.gradle
+++ b/datastore/datastore/build.gradle
@@ -47,7 +47,7 @@
             dependencies {
                 api(libs.kotlinStdlib)
                 api(libs.kotlinCoroutinesCore)
-                api(project(":annotation:annotation"))
+                api("androidx.annotation:annotation:1.8.1")
                 api(project(":datastore:datastore-core"))
                 api(project(":datastore:datastore-core-okio"))
             }
diff --git a/hilt/hilt-compiler/build.gradle b/hilt/hilt-compiler/build.gradle
index 7980e5f..3213852 100644
--- a/hilt/hilt-compiler/build.gradle
+++ b/hilt/hilt-compiler/build.gradle
@@ -39,7 +39,7 @@
     implementation(libs.kspApi)
 
     testImplementation(project(":hilt:hilt-common"))
-    testImplementation(project(":annotation:annotation"))
+    testImplementation("androidx.annotation:annotation:1.8.1")
     testImplementation(libs.junit)
     testImplementation(libs.truth)
     testImplementation(project(":room:room-compiler-processing-testing"))
diff --git a/paging/integration-tests/testapp/build.gradle b/paging/integration-tests/testapp/build.gradle
index cbd8d34..a1918e9 100644
--- a/paging/integration-tests/testapp/build.gradle
+++ b/paging/integration-tests/testapp/build.gradle
@@ -41,12 +41,10 @@
 
     // Only needed to ensure version of annotation:annotation matches in impl
     // and androidTestImpl, for both AOSP and playground builds.
-    implementation(project(":annotation:annotation"))
-    implementation(project(":annotation:annotation-experimental"))
+    implementation("androidx.annotation:annotation:1.8.1")
+    implementation("androidx.annotation:annotation-experimental:1.4.1")
 
     // Align dependencies in debugRuntimeClasspath and debugAndroidTestRuntimeClasspath.
-    androidTestImplementation(project(":annotation:annotation"))
-    androidTestImplementation(project(":annotation:annotation-experimental"))
     androidTestImplementation(libs.kotlinTest)
     androidTestImplementation(libs.testCore)
     androidTestImplementation(libs.testExtJunit)
diff --git a/remotecallback/remotecallback/build.gradle b/remotecallback/remotecallback/build.gradle
index e5ff2a7..c1da69a 100644
--- a/remotecallback/remotecallback/build.gradle
+++ b/remotecallback/remotecallback/build.gradle
@@ -30,14 +30,14 @@
 
 dependencies {
     api("androidx.annotation:annotation:1.8.1")
-    implementation(project(":collection:collection"))
+    implementation("androidx.collection:collection:1.4.2")
 
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testCore)
     androidTestImplementation(libs.testRunner)
     androidTestImplementation(libs.mockitoCore, excludes.bytebuddy)
     androidTestImplementation(libs.dexmakerMockito, excludes.bytebuddy)
-    androidTestAnnotationProcessor (project(":remotecallback:remotecallback-processor"))
+    androidTestAnnotationProcessor(project(":remotecallback:remotecallback-processor"))
 }
 
 android {
diff --git a/resourceinspection/resourceinspection-processor/build.gradle b/resourceinspection/resourceinspection-processor/build.gradle
index 720cf10e..5251358 100644
--- a/resourceinspection/resourceinspection-processor/build.gradle
+++ b/resourceinspection/resourceinspection-processor/build.gradle
@@ -46,7 +46,7 @@
     testImplementation(libs.truth)
 
     testRuntimeOnly(project(":resourceinspection:resourceinspection-annotation"))
-    testRuntimeOnly(project(":annotation:annotation"))
+    testRuntimeOnly("androidx.annotation:annotation:1.8.1")
     testRuntimeOnly(SdkHelperKt.getSdkDependency(project))
     testRuntimeOnly(libs.intellijAnnotations)
     testRuntimeOnly(libs.jsr250)
diff --git a/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/JvmOnlyDatabaseDeclarationTest.kt b/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/JvmOnlyDatabaseDeclarationTest.kt
new file mode 100644
index 0000000..d716001
--- /dev/null
+++ b/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/JvmOnlyDatabaseDeclarationTest.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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.integration.multiplatformtestapp.test
+
+import androidx.kruth.assertThat
+import androidx.room.Database
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import androidx.room.Room
+import androidx.room.RoomDatabase
+import androidx.room.useReaderConnection
+import androidx.sqlite.driver.bundled.BundledSQLiteDriver
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+
+/**
+ * This test validates that a [Database] declared in the Jvm source set does not required the usage
+ * of [androidx.room.ConstructedBy] nor [androidx.room.RoomDatabaseConstructor].
+ */
+class JvmOnlyDatabaseDeclarationTest {
+
+    @Test
+    fun buildJvmOnlyRoomDatabase() = runTest {
+        val database =
+            Room.inMemoryDatabaseBuilder<TestDatabase>().setDriver(BundledSQLiteDriver()).build()
+        val dbVersion =
+            database.useReaderConnection { connection ->
+                connection.usePrepared("PRAGMA user_version") {
+                    it.step()
+                    it.getLong(0)
+                }
+            }
+        assertThat(dbVersion).isEqualTo(1)
+    }
+
+    @Database(entities = [TestEntity::class], version = 1, exportSchema = false)
+    abstract class TestDatabase : RoomDatabase()
+
+    @Entity data class TestEntity(@PrimaryKey val id: Long)
+}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/ext/xpoet_ext.kt b/room/room-compiler/src/main/kotlin/androidx/room/ext/xpoet_ext.kt
index f57ed83..7c0e658 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/ext/xpoet_ext.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/ext/xpoet_ext.kt
@@ -222,20 +222,39 @@
 }
 
 object RoomRxJava2TypeNames {
-    val RX_ROOM = XClassName.get(ROOM_PACKAGE, "RxRoom")
-    val RX_ROOM_CREATE_FLOWABLE = "createFlowable"
-    val RX_ROOM_CREATE_OBSERVABLE = "createObservable"
-    val RX_EMPTY_RESULT_SET_EXCEPTION = XClassName.get(ROOM_PACKAGE, "EmptyResultSetException")
+    val RX2_ROOM = XClassName.get(ROOM_PACKAGE, "RxRoom")
+    val RX2_EMPTY_RESULT_SET_EXCEPTION = XClassName.get(ROOM_PACKAGE, "EmptyResultSetException")
+}
+
+object RoomRxJava2MemberNames {
+    val RX_ROOM_CREATE_FLOWABLE =
+        RoomRxJava2TypeNames.RX2_ROOM.companionMember("createFlowable", isJvmStatic = true)
+    val RX_ROOM_CREATE_OBSERVABLE =
+        RoomRxJava2TypeNames.RX2_ROOM.companionMember("createObservable", isJvmStatic = true)
+    val RX_ROOM_CREATE_SINGLE =
+        RoomRxJava2TypeNames.RX2_ROOM.companionMember("createSingle", isJvmStatic = true)
+    val RX_ROOM_CREATE_MAYBE =
+        RoomRxJava2TypeNames.RX2_ROOM.companionMember("createMaybe", isJvmStatic = true)
+    val RX_ROOM_CREATE_COMPLETABLE =
+        RoomRxJava2TypeNames.RX2_ROOM.companionMember("createCompletable", isJvmStatic = true)
 }
 
 object RoomRxJava3TypeNames {
-    val RX_ROOM = XClassName.get("$ROOM_PACKAGE.rxjava3", "RxRoom")
-    val RX_ROOM_CREATE_FLOWABLE = "createFlowable"
-    val RX_ROOM_CREATE_OBSERVABLE = "createObservable"
-    val RX_EMPTY_RESULT_SET_EXCEPTION =
+    val RX3_ROOM = XClassName.get("$ROOM_PACKAGE.rxjava3", "RxRoom")
+    val RX3_ROOM_MARKER = XClassName.get("$ROOM_PACKAGE.rxjava3", "Rx3RoomArtifactMarker")
+    val RX3_EMPTY_RESULT_SET_EXCEPTION =
         XClassName.get("$ROOM_PACKAGE.rxjava3", "EmptyResultSetException")
 }
 
+object RoomRxJava3MemberNames {
+    val RX_ROOM_CREATE_FLOWABLE = RoomRxJava3TypeNames.RX3_ROOM.packageMember("createFlowable")
+    val RX_ROOM_CREATE_OBSERVABLE = RoomRxJava3TypeNames.RX3_ROOM.packageMember("createObservable")
+    val RX_ROOM_CREATE_SINGLE = RoomRxJava3TypeNames.RX3_ROOM.packageMember("createSingle")
+    val RX_ROOM_CREATE_MAYBE = RoomRxJava3TypeNames.RX3_ROOM.packageMember("createMaybe")
+    val RX_ROOM_CREATE_COMPLETABLE =
+        RoomRxJava3TypeNames.RX3_ROOM.packageMember("createCompletable")
+}
+
 object RoomPagingTypeNames {
     val LIMIT_OFFSET_PAGING_SOURCE =
         XClassName.get("$ROOM_PACKAGE.paging", "LimitOffsetPagingSource")
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/Context.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/Context.kt
index 8d375ac..3909425 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/Context.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/Context.kt
@@ -323,4 +323,10 @@
             targetPlatforms.contains(XProcessingEnv.Platform.JVM) &&
             this.processingEnv.findType("android.content.Context") != null
     }
+
+    /** Check if the target platform is JVM. */
+    fun isJvmOnlyTarget(): Boolean {
+        val targetPlatforms = this.processingEnv.targetPlatforms
+        return targetPlatforms.size == 1 && targetPlatforms.contains(XProcessingEnv.Platform.JVM)
+    }
 }
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 7f1f632..b49a6c2 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
@@ -546,8 +546,11 @@
     private fun processConstructorObject(element: XTypeElement): XTypeElement? {
         val annotation = element.getAnnotation(androidx.room.ConstructedBy::class)
         if (annotation == null) {
+            // If no @ConstructedBy is present then validate target is JVM (including Android)
+            // since reflection is available in those platforms and a database constructor is not
+            // needed.
             context.checker.check(
-                predicate = context.isAndroidOnlyTarget(),
+                predicate = context.isJvmOnlyTarget(),
                 element = element,
                 errorMsg = ProcessorErrors.MISSING_CONSTRUCTED_BY_ANNOTATION
             )
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/RxTypes.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/RxTypes.kt
index 762028e..b6b1fd6 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/RxTypes.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/RxTypes.kt
@@ -17,7 +17,10 @@
 package androidx.room.solver
 
 import androidx.room.compiler.codegen.XClassName
+import androidx.room.compiler.codegen.XMemberName
+import androidx.room.ext.RoomRxJava2MemberNames
 import androidx.room.ext.RoomRxJava2TypeNames
+import androidx.room.ext.RoomRxJava3MemberNames
 import androidx.room.ext.RoomRxJava3TypeNames
 import androidx.room.ext.RxJava2TypeNames
 import androidx.room.ext.RxJava3TypeNames
@@ -26,54 +29,80 @@
 internal enum class RxType(
     val version: RxVersion,
     val className: XClassName,
-    val factoryMethodName: String? = null,
+    val factoryMethodName: XMemberName,
     val canBeNull: Boolean = false
 ) {
     // RxJava2 types
     RX2_FLOWABLE(
         version = RxVersion.TWO,
         className = RxJava2TypeNames.FLOWABLE,
-        factoryMethodName = RoomRxJava2TypeNames.RX_ROOM_CREATE_FLOWABLE
+        factoryMethodName = RoomRxJava2MemberNames.RX_ROOM_CREATE_FLOWABLE
     ),
     RX2_OBSERVABLE(
         version = RxVersion.TWO,
         className = RxJava2TypeNames.OBSERVABLE,
-        factoryMethodName = RoomRxJava2TypeNames.RX_ROOM_CREATE_OBSERVABLE
+        factoryMethodName = RoomRxJava2MemberNames.RX_ROOM_CREATE_OBSERVABLE
     ),
-    RX2_SINGLE(version = RxVersion.TWO, className = RxJava2TypeNames.SINGLE),
-    RX2_MAYBE(version = RxVersion.TWO, className = RxJava2TypeNames.MAYBE, canBeNull = true),
-    RX2_COMPLETABLE(version = RxVersion.TWO, className = RxJava2TypeNames.COMPLETABLE),
+    RX2_SINGLE(
+        version = RxVersion.TWO,
+        className = RxJava2TypeNames.SINGLE,
+        factoryMethodName = RoomRxJava2MemberNames.RX_ROOM_CREATE_SINGLE
+    ),
+    RX2_MAYBE(
+        version = RxVersion.TWO,
+        className = RxJava2TypeNames.MAYBE,
+        factoryMethodName = RoomRxJava2MemberNames.RX_ROOM_CREATE_MAYBE,
+        canBeNull = true
+    ),
+    RX2_COMPLETABLE(
+        version = RxVersion.TWO,
+        className = RxJava2TypeNames.COMPLETABLE,
+        factoryMethodName = RoomRxJava2MemberNames.RX_ROOM_CREATE_COMPLETABLE
+    ),
     // RxJava3 types
     RX3_FLOWABLE(
         version = RxVersion.THREE,
         className = RxJava3TypeNames.FLOWABLE,
-        factoryMethodName = RoomRxJava3TypeNames.RX_ROOM_CREATE_FLOWABLE
+        factoryMethodName = RoomRxJava3MemberNames.RX_ROOM_CREATE_FLOWABLE
     ),
     RX3_OBSERVABLE(
         version = RxVersion.THREE,
         className = RxJava3TypeNames.OBSERVABLE,
-        factoryMethodName = RoomRxJava3TypeNames.RX_ROOM_CREATE_OBSERVABLE
+        factoryMethodName = RoomRxJava3MemberNames.RX_ROOM_CREATE_OBSERVABLE
     ),
-    RX3_SINGLE(version = RxVersion.THREE, className = RxJava3TypeNames.SINGLE),
-    RX3_MAYBE(version = RxVersion.THREE, className = RxJava3TypeNames.MAYBE, canBeNull = true),
-    RX3_COMPLETABLE(version = RxVersion.THREE, className = RxJava3TypeNames.COMPLETABLE);
+    RX3_SINGLE(
+        version = RxVersion.THREE,
+        className = RxJava3TypeNames.SINGLE,
+        factoryMethodName = RoomRxJava3MemberNames.RX_ROOM_CREATE_SINGLE
+    ),
+    RX3_MAYBE(
+        version = RxVersion.THREE,
+        className = RxJava3TypeNames.MAYBE,
+        factoryMethodName = RoomRxJava3MemberNames.RX_ROOM_CREATE_MAYBE,
+        canBeNull = true
+    ),
+    RX3_COMPLETABLE(
+        version = RxVersion.THREE,
+        className = RxJava3TypeNames.COMPLETABLE,
+        factoryMethodName = RoomRxJava3MemberNames.RX_ROOM_CREATE_COMPLETABLE
+    );
 
     fun isSingle() = this == RX2_SINGLE || this == RX3_SINGLE
 }
 
 internal enum class RxVersion(
-    val rxRoomClassName: XClassName,
+    val rxMarkerClassName: XClassName,
     val emptyResultExceptionClassName: XClassName,
     val missingArtifactMessage: String
 ) {
     TWO(
-        rxRoomClassName = RoomRxJava2TypeNames.RX_ROOM,
-        emptyResultExceptionClassName = RoomRxJava2TypeNames.RX_EMPTY_RESULT_SET_EXCEPTION,
+        rxMarkerClassName = RoomRxJava2TypeNames.RX2_ROOM,
+        emptyResultExceptionClassName = RoomRxJava2TypeNames.RX2_EMPTY_RESULT_SET_EXCEPTION,
         missingArtifactMessage = ProcessorErrors.MISSING_ROOM_RXJAVA2_ARTIFACT
     ),
     THREE(
-        rxRoomClassName = RoomRxJava3TypeNames.RX_ROOM,
-        emptyResultExceptionClassName = RoomRxJava3TypeNames.RX_EMPTY_RESULT_SET_EXCEPTION,
+        rxMarkerClassName = RoomRxJava3TypeNames.RX3_ROOM_MARKER,
+        emptyResultExceptionClassName = RoomRxJava3TypeNames.RX3_EMPTY_RESULT_SET_EXCEPTION,
         missingArtifactMessage = ProcessorErrors.MISSING_ROOM_RXJAVA3_ARTIFACT
     )
 }
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 4523c50..abe96e5 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
@@ -54,9 +54,9 @@
 import androidx.room.solver.binderprovider.ListenableFuturePagingSourceQueryResultBinderProvider
 import androidx.room.solver.binderprovider.LiveDataQueryResultBinderProvider
 import androidx.room.solver.binderprovider.PagingSourceQueryResultBinderProvider
-import androidx.room.solver.binderprovider.RxCallableQueryResultBinderProvider
 import androidx.room.solver.binderprovider.RxJava2PagingSourceQueryResultBinderProvider
 import androidx.room.solver.binderprovider.RxJava3PagingSourceQueryResultBinderProvider
+import androidx.room.solver.binderprovider.RxLambdaQueryResultBinderProvider
 import androidx.room.solver.binderprovider.RxQueryResultBinderProvider
 import androidx.room.solver.prepared.binder.PreparedQueryResultBinder
 import androidx.room.solver.prepared.binderprovider.GuavaListenableFuturePreparedQueryResultBinderProvider
@@ -208,7 +208,7 @@
             add(LiveDataQueryResultBinderProvider(context))
             add(GuavaListenableFutureQueryResultBinderProvider(context))
             addAll(RxQueryResultBinderProvider.getAll(context))
-            addAll(RxCallableQueryResultBinderProvider.getAll(context))
+            addAll(RxLambdaQueryResultBinderProvider.getAll(context))
             add(DataSourceQueryResultBinderProvider(context))
             add(DataSourceFactoryQueryResultBinderProvider(context))
             add(RxJava2PagingSourceQueryResultBinderProvider(context))
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/RxCallableQueryResultBinderProvider.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/RxLambdaQueryResultBinderProvider.kt
similarity index 89%
rename from room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/RxCallableQueryResultBinderProvider.kt
rename to room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/RxLambdaQueryResultBinderProvider.kt
index 129241d..701cf87 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/RxCallableQueryResultBinderProvider.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/RxLambdaQueryResultBinderProvider.kt
@@ -24,9 +24,9 @@
 import androidx.room.solver.RxType
 import androidx.room.solver.TypeAdapterExtras
 import androidx.room.solver.query.result.QueryResultBinder
-import androidx.room.solver.query.result.RxCallableQueryResultBinder
+import androidx.room.solver.query.result.RxLambdaQueryResultBinder
 
-class RxCallableQueryResultBinderProvider
+class RxLambdaQueryResultBinderProvider
 private constructor(val context: Context, private val rxType: RxType) : QueryResultBinderProvider {
     override fun provide(
         declared: XType,
@@ -40,7 +40,7 @@
         )
         val typeArg = extractTypeArg(declared)
         val adapter = context.typeAdapterStore.findQueryResultAdapter(typeArg, query, extras)
-        return RxCallableQueryResultBinder(rxType, typeArg, adapter)
+        return RxLambdaQueryResultBinder(rxType, typeArg, adapter)
     }
 
     override fun matches(declared: XType): Boolean =
@@ -62,10 +62,10 @@
     companion object {
         fun getAll(context: Context) =
             listOf(RxType.RX2_SINGLE, RxType.RX2_MAYBE, RxType.RX3_SINGLE, RxType.RX3_MAYBE).map {
-                RxCallableQueryResultBinderProvider(context, it)
+                RxLambdaQueryResultBinderProvider(context, it)
                     .requireArtifact(
                         context = context,
-                        requiredType = it.version.rxRoomClassName,
+                        requiredType = it.version.rxMarkerClassName,
                         missingArtifactErrorMsg = it.version.missingArtifactMessage
                     )
             }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/RxQueryResultBinderProvider.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/RxQueryResultBinderProvider.kt
index a592faaf..aad54a6 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/RxQueryResultBinderProvider.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/binderprovider/RxQueryResultBinderProvider.kt
@@ -25,6 +25,7 @@
 import androidx.room.solver.query.result.QueryResultBinder
 import androidx.room.solver.query.result.RxQueryResultBinder
 
+/** Generic result binder for Rx classes that are reactive. */
 class RxQueryResultBinderProvider
 private constructor(context: Context, private val rxType: RxType) :
     ObservableQueryResultBinderProvider(context) {
@@ -69,7 +70,7 @@
                     RxQueryResultBinderProvider(context, it)
                         .requireArtifact(
                             context = context,
-                            requiredType = it.version.rxRoomClassName,
+                            requiredType = it.version.rxMarkerClassName,
                             missingArtifactErrorMsg = it.version.missingArtifactMessage
                         )
                 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/prepared/binder/CallablePreparedQueryResultBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/prepared/binder/CallablePreparedQueryResultBinder.kt
deleted file mode 100644
index 622bb16..0000000
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/prepared/binder/CallablePreparedQueryResultBinder.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.room.solver.prepared.binder
-
-import androidx.room.compiler.codegen.XCodeBlock
-import androidx.room.compiler.codegen.XPropertySpec
-import androidx.room.compiler.codegen.XTypeSpec
-import androidx.room.compiler.processing.XType
-import androidx.room.ext.CallableTypeSpecBuilder
-import androidx.room.solver.CodeGenScope
-import androidx.room.solver.prepared.result.PreparedQueryResultAdapter
-
-/**
- * Binder for deferred queries.
- *
- * This binder will create a Callable implementation that delegates to the
- * [PreparedQueryResultAdapter]. Usage of the Callable impl is then delegate to the [addStmntBlock]
- * function.
- */
-class CallablePreparedQueryResultBinder
-private constructor(
-    val returnType: XType,
-    val addStmntBlock:
-        XCodeBlock.Builder.(callableImpl: XTypeSpec, dbProperty: XPropertySpec) -> Unit,
-    adapter: PreparedQueryResultAdapter?
-) : PreparedQueryResultBinder(adapter) {
-
-    companion object {
-        fun createPreparedBinder(
-            returnType: XType,
-            adapter: PreparedQueryResultAdapter?,
-            addCodeBlock:
-                XCodeBlock.Builder.(callableImpl: XTypeSpec, dbProperty: XPropertySpec) -> Unit
-        ) = CallablePreparedQueryResultBinder(returnType, addCodeBlock, adapter)
-    }
-
-    override fun executeAndReturn(
-        prepareQueryStmtBlock: CodeGenScope.() -> String,
-        preparedStmtProperty: XPropertySpec?,
-        dbProperty: XPropertySpec,
-        scope: CodeGenScope
-    ) {
-        val binderScope = scope.fork()
-        val callableImpl =
-            CallableTypeSpecBuilder(scope.language, returnType.asTypeName()) {
-                    adapter?.executeAndReturn(
-                        binderScope.prepareQueryStmtBlock(),
-                        preparedStmtProperty,
-                        dbProperty,
-                        binderScope
-                    )
-                    addCode(binderScope.generate())
-                }
-                .build()
-
-        scope.builder.apply { addStmntBlock(callableImpl, dbProperty) }
-    }
-}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/prepared/binderprovider/RxPreparedQueryResultBinderProvider.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/prepared/binderprovider/RxPreparedQueryResultBinderProvider.kt
index 5b5a248..d7153fc 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/prepared/binderprovider/RxPreparedQueryResultBinderProvider.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/prepared/binderprovider/RxPreparedQueryResultBinderProvider.kt
@@ -18,10 +18,11 @@
 
 import androidx.room.compiler.processing.XRawType
 import androidx.room.compiler.processing.XType
+import androidx.room.ext.KotlinTypeNames
 import androidx.room.parser.ParsedQuery
 import androidx.room.processor.Context
 import androidx.room.solver.RxType
-import androidx.room.solver.prepared.binder.CallablePreparedQueryResultBinder.Companion.createPreparedBinder
+import androidx.room.solver.prepared.binder.LambdaPreparedQueryResultBinder
 import androidx.room.solver.prepared.binder.PreparedQueryResultBinder
 
 open class RxPreparedQueryResultBinderProvider
@@ -29,7 +30,8 @@
     PreparedQueryResultBinderProvider {
 
     private val hasRxJavaArtifact by lazy {
-        context.processingEnv.findTypeElement(rxType.version.rxRoomClassName.canonicalName) != null
+        context.processingEnv.findTypeElement(rxType.version.rxMarkerClassName.canonicalName) !=
+            null
     }
 
     override fun matches(declared: XType): Boolean =
@@ -44,12 +46,11 @@
             context.logger.e(rxType.version.missingArtifactMessage)
         }
         val typeArg = extractTypeArg(declared)
-        return createPreparedBinder(
+        return LambdaPreparedQueryResultBinder(
             returnType = typeArg,
+            functionName = rxType.factoryMethodName,
             adapter = context.typeAdapterStore.findPreparedQueryResultAdapter(typeArg, query)
-        ) { callableImpl, _ ->
-            addStatement("return %T.fromCallable(%L)", rxType.className, callableImpl)
-        }
+        )
     }
 
     open fun extractTypeArg(declared: XType): XType = declared.typeArguments.first()
@@ -82,16 +83,17 @@
     }
 
     /**
-     * Since Completable is not a generic, the supported return type should be Void (nullable). Like
-     * this, the generated Callable.call method will return Void.
+     * Since Completable has no type argument, the supported return type is Unit (non-nullable)
+     * since the 'createCompletable" factory method take a Kotlin lambda.
      */
-    override fun extractTypeArg(declared: XType): XType = context.COMMON_TYPES.VOID.makeNullable()
+    override fun extractTypeArg(declared: XType): XType =
+        context.processingEnv.requireType(KotlinTypeNames.UNIT)
 }
 
 private class RxSingleOrMaybePreparedQueryResultBinderProvider(context: Context, rxType: RxType) :
     RxPreparedQueryResultBinderProvider(context, rxType) {
 
-    /** Since Maybe can have null values, the Callable returned must allow for null values. */
+    /** Since Maybe can have null values, the lambda returned must allow for null values. */
     override fun extractTypeArg(declared: XType): XType =
         declared.typeArguments.first().makeNullable()
 }
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/RxLambdaQueryResultBinder.kt
similarity index 68%
rename from room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/RxCallableQueryResultBinder.kt
rename to room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/RxLambdaQueryResultBinder.kt
index a16c91f..10973f3 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/RxLambdaQueryResultBinder.kt
@@ -19,20 +19,25 @@
 import androidx.room.compiler.codegen.CodeLanguage
 import androidx.room.compiler.codegen.VisibilityModifier
 import androidx.room.compiler.codegen.XCodeBlock
+import androidx.room.compiler.codegen.XCodeBlock.Builder.Companion.addLocalVal
 import androidx.room.compiler.codegen.XFunSpec
 import androidx.room.compiler.codegen.XFunSpec.Builder.Companion.addStatement
 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 androidx.room.compiler.processing.XType
 import androidx.room.ext.AndroidTypeNames.CURSOR
 import androidx.room.ext.CallableTypeSpecBuilder
+import androidx.room.ext.InvokeWithLambdaParameter
+import androidx.room.ext.LambdaSpec
 import androidx.room.ext.RoomMemberNames.DB_UTIL_QUERY
+import androidx.room.ext.SQLiteDriverTypeNames
 import androidx.room.solver.CodeGenScope
 import androidx.room.solver.RxType
 
-/** Generic Result binder for Rx classes that accept a callable. */
-internal class RxCallableQueryResultBinder(
+/** Generic result binder for Rx classes that are not reactive. */
+internal class RxLambdaQueryResultBinder(
     private val rxType: RxType,
     val typeArg: XType,
     adapter: QueryResultAdapter?
@@ -68,7 +73,7 @@
                 .build()
         scope.builder.apply {
             if (rxType.isSingle()) {
-                addStatement("return %T.createSingle(%L)", rxType.version.rxRoomClassName, callable)
+                addStatement("return %M(%L)", rxType.factoryMethodName, callable)
             } else {
                 addStatement("return %T.fromCallable(%L)", rxType.className, callable)
             }
@@ -160,4 +165,57 @@
                 .build()
         )
     }
+
+    override fun isMigratedToDriver() = adapter?.isMigratedToDriver() == true
+
+    override fun convertAndReturn(
+        sqlQueryVar: String,
+        dbProperty: XPropertySpec,
+        bindStatement: CodeGenScope.(String) -> Unit,
+        returnTypeName: XTypeName,
+        inTransaction: Boolean,
+        scope: CodeGenScope
+    ) {
+        val connectionVar = scope.getTmpVar("_connection")
+        val performBlock =
+            InvokeWithLambdaParameter(
+                scope = scope,
+                functionName = rxType.factoryMethodName,
+                argFormat = listOf("%N", "%L", "%L"),
+                args = listOf(dbProperty, /* isReadOnly= */ true, inTransaction),
+                lambdaSpec =
+                    object :
+                        LambdaSpec(
+                            parameterTypeName = SQLiteDriverTypeNames.CONNECTION,
+                            parameterName = connectionVar,
+                            returnTypeName = typeArg.asTypeName(),
+                            javaLambdaSyntaxAvailable = scope.javaLambdaSyntaxAvailable
+                        ) {
+                        override fun XCodeBlock.Builder.body(scope: CodeGenScope) {
+                            val returnPrefix =
+                                when (language) {
+                                    CodeLanguage.JAVA -> "return "
+                                    CodeLanguage.KOTLIN -> ""
+                                }
+                            val statementVar = scope.getTmpVar("_stmt")
+                            addLocalVal(
+                                statementVar,
+                                SQLiteDriverTypeNames.STATEMENT,
+                                "%L.prepare(%L)",
+                                connectionVar,
+                                sqlQueryVar
+                            )
+                            beginControlFlow("try")
+                            bindStatement(scope, statementVar)
+                            val outVar = scope.getTmpVar("_result")
+                            adapter?.convert(outVar, statementVar, scope)
+                            addStatement("$returnPrefix%L", outVar)
+                            nextControlFlow("finally")
+                            addStatement("%L.close()", statementVar)
+                            endControlFlow()
+                        }
+                    }
+            )
+        scope.builder.add("return %L", performBlock)
+    }
 }
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 838c502..bc8877e 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,12 +16,18 @@
 
 package androidx.room.solver.query.result
 
+import androidx.room.compiler.codegen.CodeLanguage
 import androidx.room.compiler.codegen.XCodeBlock
+import androidx.room.compiler.codegen.XCodeBlock.Builder.Companion.addLocalVal
 import androidx.room.compiler.codegen.XPropertySpec
+import androidx.room.compiler.codegen.XTypeName
 import androidx.room.compiler.processing.XType
 import androidx.room.ext.ArrayLiteral
 import androidx.room.ext.CallableTypeSpecBuilder
 import androidx.room.ext.CommonTypeNames
+import androidx.room.ext.InvokeWithLambdaParameter
+import androidx.room.ext.LambdaSpec
+import androidx.room.ext.SQLiteDriverTypeNames
 import androidx.room.solver.CodeGenScope
 import androidx.room.solver.RxType
 
@@ -69,9 +75,8 @@
                     *queryTableNames.toTypedArray()
                 )
             addStatement(
-                "return %T.%N(%N, %L, %L, %L)",
-                rxType.version.rxRoomClassName,
-                rxType.factoryMethodName!!,
+                "return %M(%N, %L, %L, %L)",
+                rxType.factoryMethodName,
                 dbProperty,
                 if (inTransaction) "true" else "false",
                 arrayOfTableNamesLiteral,
@@ -79,4 +84,66 @@
             )
         }
     }
+
+    override fun isMigratedToDriver() = adapter?.isMigratedToDriver() ?: false
+
+    override fun convertAndReturn(
+        sqlQueryVar: String,
+        dbProperty: XPropertySpec,
+        bindStatement: CodeGenScope.(String) -> Unit,
+        returnTypeName: XTypeName,
+        inTransaction: Boolean,
+        scope: CodeGenScope
+    ) {
+        val connectionVar = scope.getTmpVar("_connection")
+        val performBlock =
+            InvokeWithLambdaParameter(
+                scope = scope,
+                functionName = rxType.factoryMethodName,
+                argFormat = listOf("%N", "%L", "%L"),
+                args =
+                    listOf(
+                        dbProperty,
+                        inTransaction,
+                        ArrayLiteral(
+                            scope.language,
+                            CommonTypeNames.STRING,
+                            *queryTableNames.toTypedArray()
+                        )
+                    ),
+                lambdaSpec =
+                    object :
+                        LambdaSpec(
+                            parameterTypeName = SQLiteDriverTypeNames.CONNECTION,
+                            parameterName = connectionVar,
+                            returnTypeName = typeArg.asTypeName(),
+                            javaLambdaSyntaxAvailable = scope.javaLambdaSyntaxAvailable
+                        ) {
+                        override fun XCodeBlock.Builder.body(scope: CodeGenScope) {
+                            val returnPrefix =
+                                when (language) {
+                                    CodeLanguage.JAVA -> "return "
+                                    CodeLanguage.KOTLIN -> ""
+                                }
+                            val statementVar = scope.getTmpVar("_stmt")
+                            addLocalVal(
+                                statementVar,
+                                SQLiteDriverTypeNames.STATEMENT,
+                                "%L.prepare(%L)",
+                                connectionVar,
+                                sqlQueryVar
+                            )
+                            beginControlFlow("try")
+                            bindStatement(scope, statementVar)
+                            val outVar = scope.getTmpVar("_result")
+                            adapter?.convert(outVar, statementVar, scope)
+                            addStatement("$returnPrefix%L", outVar)
+                            nextControlFlow("finally")
+                            addStatement("%L.close()", statementVar)
+                            endControlFlow()
+                        }
+                    }
+            )
+        scope.builder.add("return %L", performBlock)
+    }
 }
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
deleted file mode 100644
index 58f383f..0000000
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CallableDeleteOrUpdateMethodBinder.kt
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.room.solver.shortcut.binder
-
-import androidx.room.compiler.codegen.XCodeBlock
-import androidx.room.compiler.codegen.XPropertySpec
-import androidx.room.compiler.codegen.XTypeSpec
-import androidx.room.compiler.processing.XType
-import androidx.room.ext.CallableTypeSpecBuilder
-import androidx.room.solver.CodeGenScope
-import androidx.room.solver.shortcut.result.DeleteOrUpdateMethodAdapter
-import androidx.room.vo.ShortcutQueryParameter
-
-/**
- * Binder for deferred delete and update methods.
- *
- * This binder will create a Callable implementation that delegates to the
- * [DeleteOrUpdateMethodAdapter]. Usage of the Callable impl is then delegate to the [addStmntBlock]
- * function.
- */
-class CallableDeleteOrUpdateMethodBinder
-private constructor(
-    val typeArg: XType,
-    val addStmntBlock: XCodeBlock.Builder.(callableImpl: XTypeSpec, dbField: XPropertySpec) -> Unit,
-    adapter: DeleteOrUpdateMethodAdapter?
-) : DeleteOrUpdateMethodBinder(adapter) {
-
-    companion object {
-        fun createDeleteOrUpdateBinder(
-            typeArg: XType,
-            adapter: DeleteOrUpdateMethodAdapter?,
-            addCodeBlock:
-                XCodeBlock.Builder.(callableImpl: XTypeSpec, dbField: XPropertySpec) -> Unit
-        ) = CallableDeleteOrUpdateMethodBinder(typeArg, addCodeBlock, adapter)
-    }
-
-    override fun convertAndReturn(
-        parameters: List<ShortcutQueryParameter>,
-        adapters: Map<String, Pair<XPropertySpec, XTypeSpec>>,
-        dbProperty: XPropertySpec,
-        scope: CodeGenScope
-    ) {
-        convertAndReturnCompat(parameters, adapters, dbProperty, scope)
-    }
-
-    override fun convertAndReturnCompat(
-        parameters: List<ShortcutQueryParameter>,
-        adapters: Map<String, Pair<XPropertySpec, XTypeSpec>>,
-        dbProperty: XPropertySpec,
-        scope: CodeGenScope
-    ) {
-        val adapterScope = scope.fork()
-        val callableImpl =
-            CallableTypeSpecBuilder(scope.language, typeArg.asTypeName()) {
-                    adapter?.generateMethodBodyCompat(
-                        parameters = parameters,
-                        adapters = adapters,
-                        dbProperty = dbProperty,
-                        scope = adapterScope
-                    )
-                    addCode(adapterScope.generate())
-                }
-                .build()
-
-        scope.builder.apply { addStmntBlock(callableImpl, dbProperty) }
-    }
-}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CallableInsertOrUpsertMethodBinder.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CallableInsertOrUpsertMethodBinder.kt
deleted file mode 100644
index 7dee268..0000000
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binder/CallableInsertOrUpsertMethodBinder.kt
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.room.solver.shortcut.binder
-
-import androidx.room.compiler.codegen.XCodeBlock
-import androidx.room.compiler.codegen.XPropertySpec
-import androidx.room.compiler.codegen.XTypeSpec
-import androidx.room.compiler.processing.XType
-import androidx.room.ext.CallableTypeSpecBuilder
-import androidx.room.solver.CodeGenScope
-import androidx.room.solver.shortcut.result.InsertOrUpsertMethodAdapter
-import androidx.room.vo.ShortcutQueryParameter
-
-/**
- * Binder for deferred insert methods.
- *
- * This binder will create a Callable implementation that delegates to the
- * [InsertOrUpsertMethodAdapter]. Usage of the Callable impl is then delegate to the [addStmntBlock]
- * function.
- */
-class CallableInsertOrUpsertMethodBinder(
-    val typeArg: XType,
-    val addStmntBlock: XCodeBlock.Builder.(callableImpl: XTypeSpec, dbField: XPropertySpec) -> Unit,
-    adapter: InsertOrUpsertMethodAdapter?
-) : InsertOrUpsertMethodBinder(adapter) {
-
-    companion object {
-        fun createInsertOrUpsertBinder(
-            typeArg: XType,
-            adapter: InsertOrUpsertMethodAdapter?,
-            addCodeBlock:
-                XCodeBlock.Builder.(callableImpl: XTypeSpec, dbField: XPropertySpec) -> Unit
-        ) = CallableInsertOrUpsertMethodBinder(typeArg, addCodeBlock, adapter)
-    }
-
-    override fun convertAndReturn(
-        parameters: List<ShortcutQueryParameter>,
-        adapters: Map<String, Pair<XPropertySpec, Any>>,
-        dbProperty: XPropertySpec,
-        scope: CodeGenScope
-    ) {
-        convertAndReturnCompat(parameters, adapters, dbProperty, scope)
-    }
-
-    override fun convertAndReturnCompat(
-        parameters: List<ShortcutQueryParameter>,
-        adapters: Map<String, Pair<XPropertySpec, Any>>,
-        dbProperty: XPropertySpec,
-        scope: CodeGenScope
-    ) {
-        val adapterScope = scope.fork()
-        val callableImpl =
-            CallableTypeSpecBuilder(scope.language, typeArg.asTypeName()) {
-                    addCode(
-                        XCodeBlock.builder(language)
-                            .apply {
-                                adapter?.generateMethodBodyCompat(
-                                    parameters = parameters,
-                                    adapters = adapters,
-                                    dbProperty = dbProperty,
-                                    scope = adapterScope
-                                )
-                                addCode(adapterScope.generate())
-                            }
-                            .build()
-                    )
-                }
-                .build()
-
-        scope.builder.apply { addStmntBlock(callableImpl, dbProperty) }
-    }
-}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binderprovider/RxCallableDeleteOrUpdateMethodBinderProvider.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binderprovider/RxCallableDeleteOrUpdateMethodBinderProvider.kt
index fa50cc4..535e608 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binderprovider/RxCallableDeleteOrUpdateMethodBinderProvider.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binderprovider/RxCallableDeleteOrUpdateMethodBinderProvider.kt
@@ -18,10 +18,11 @@
 
 import androidx.room.compiler.processing.XRawType
 import androidx.room.compiler.processing.XType
+import androidx.room.ext.KotlinTypeNames
 import androidx.room.processor.Context
 import androidx.room.solver.RxType
-import androidx.room.solver.shortcut.binder.CallableDeleteOrUpdateMethodBinder.Companion.createDeleteOrUpdateBinder
 import androidx.room.solver.shortcut.binder.DeleteOrUpdateMethodBinder
+import androidx.room.solver.shortcut.binder.LambdaDeleteOrUpdateMethodBinder
 
 /** Provider for Rx Callable binders. */
 open class RxCallableDeleteOrUpdateMethodBinderProvider
@@ -44,9 +45,11 @@
     override fun provide(declared: XType): DeleteOrUpdateMethodBinder {
         val typeArg = extractTypeArg(declared)
         val adapter = context.typeAdapterStore.findDeleteOrUpdateAdapter(typeArg)
-        return createDeleteOrUpdateBinder(typeArg, adapter) { callableImpl, _ ->
-            addStatement("return %T.fromCallable(%L)", rxType.className, callableImpl)
-        }
+        return LambdaDeleteOrUpdateMethodBinder(
+            typeArg = typeArg,
+            functionName = rxType.factoryMethodName,
+            adapter = adapter
+        )
     }
 
     companion object {
@@ -70,25 +73,24 @@
     }
 
     /**
-     * Since Completable is not a generic, the supported return type should be Void (nullable). Like
-     * this, the generated Callable.call method will return Void.
+     * Since Completable has no type argument, the supported return type is Unit (non-nullable)
+     * since the 'createCompletable" factory method take a Kotlin lambda.
      */
-    override fun extractTypeArg(declared: XType): XType = context.COMMON_TYPES.VOID.makeNullable()
+    override fun extractTypeArg(declared: XType): XType =
+        context.processingEnv.requireType(KotlinTypeNames.UNIT)
 
     override fun matches(declared: XType): Boolean = isCompletable(declared)
 
     private fun isCompletable(declared: XType): Boolean {
-        if (completableType == null) {
-            return false
-        }
-        return declared.rawType.isAssignableFrom(completableType!!)
+        val completableType = this.completableType ?: return false
+        return declared.rawType.isAssignableFrom(completableType)
     }
 }
 
 private class RxSingleOrMaybeDeleteOrUpdateMethodBinderProvider(context: Context, rxType: RxType) :
     RxCallableDeleteOrUpdateMethodBinderProvider(context, rxType) {
 
-    /** Since Maybe can have null values, the Callable returned must allow for null values. */
+    /** Since Maybe can have null values, the lambda returned must allow for null values. */
     override fun extractTypeArg(declared: XType): XType =
         declared.typeArguments.first().makeNullable()
 }
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binderprovider/RxCallableInsertOrUpsertMethodBinderProvider.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binderprovider/RxCallableInsertOrUpsertMethodBinderProvider.kt
index fc23f43..9b0223d 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binderprovider/RxCallableInsertOrUpsertMethodBinderProvider.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/shortcut/binderprovider/RxCallableInsertOrUpsertMethodBinderProvider.kt
@@ -18,10 +18,11 @@
 
 import androidx.room.compiler.processing.XRawType
 import androidx.room.compiler.processing.XType
+import androidx.room.ext.KotlinTypeNames
 import androidx.room.processor.Context
 import androidx.room.solver.RxType
-import androidx.room.solver.shortcut.binder.CallableInsertOrUpsertMethodBinder.Companion.createInsertOrUpsertBinder
 import androidx.room.solver.shortcut.binder.InsertOrUpsertMethodBinder
+import androidx.room.solver.shortcut.binder.LambdaInsertOrUpsertMethodBinder
 import androidx.room.vo.ShortcutQueryParameter
 
 /** Provider for Rx Callable binders. */
@@ -54,9 +55,11 @@
             } else {
                 context.typeAdapterStore.findInsertAdapter(typeArg, params)
             }
-        return createInsertOrUpsertBinder(typeArg, adapter) { callableImpl, _ ->
-            addStatement("return %T.fromCallable(%L)", rxType.className, callableImpl)
-        }
+        return LambdaInsertOrUpsertMethodBinder(
+            typeArg = typeArg,
+            functionName = rxType.factoryMethodName,
+            adapter = adapter
+        )
     }
 
     companion object {
@@ -80,25 +83,24 @@
     }
 
     /**
-     * Since Completable is not a generic, the supported return type should be Void (nullable). Like
-     * this, the generated Callable.call method will return Void.
+     * Since Completable has no type argument, the supported return type is Unit (non-nullable)
+     * since the 'createCompletable" factory method take a Kotlin lambda.
      */
-    override fun extractTypeArg(declared: XType): XType = context.COMMON_TYPES.VOID.makeNullable()
+    override fun extractTypeArg(declared: XType): XType =
+        context.processingEnv.requireType(KotlinTypeNames.UNIT)
 
     override fun matches(declared: XType): Boolean = isCompletable(declared)
 
     private fun isCompletable(declared: XType): Boolean {
-        if (completableType == null) {
-            return false
-        }
-        return declared.rawType.isAssignableFrom(completableType!!)
+        val completableType = this.completableType ?: return false
+        return declared.rawType.isAssignableFrom(completableType)
     }
 }
 
 private class RxSingleOrMaybeInsertOrUpsertMethodBinderProvider(context: Context, rxType: RxType) :
     RxCallableInsertOrUpsertMethodBinderProvider(context, rxType) {
 
-    /** Since Maybe can have null values, the Callable returned must allow for null values. */
+    /** Since Maybe can have null values, the lambda returned must allow for null values. */
     override fun extractTypeArg(declared: XType): XType =
         declared.typeArguments.first().makeNullable()
 }
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 0ebee23..8aa2f41 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
@@ -29,6 +29,7 @@
 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.CommonTypeNames
 import androidx.room.ext.GuavaUtilConcurrentTypeNames
@@ -697,15 +698,20 @@
         listOf(COMMON.RX2_FLOWABLE to COMMON.RX2_ROOM, COMMON.RX3_FLOWABLE to COMMON.RX3_ROOM)
             .forEach { (rxTypeSrc, rxRoomSrc) ->
                 runProcessorTest(
-                    sources =
-                        listOf(
-                            COMMON.RX2_SINGLE,
-                            COMMON.RX3_SINGLE,
-                            COMMON.RX2_OBSERVABLE,
-                            COMMON.RX3_OBSERVABLE,
-                            COMMON.PUBLISHER,
-                            rxTypeSrc,
-                            rxRoomSrc
+                    sources = listOf(rxTypeSrc, rxRoomSrc),
+                    classpath =
+                        compileFiles(
+                            listOf(
+                                COMMON.RX2_SINGLE,
+                                COMMON.RX2_MAYBE,
+                                COMMON.RX2_COMPLETABLE,
+                                COMMON.RX2_OBSERVABLE,
+                                COMMON.RX3_SINGLE,
+                                COMMON.RX3_MAYBE,
+                                COMMON.RX3_COMPLETABLE,
+                                COMMON.RX3_OBSERVABLE,
+                                COMMON.PUBLISHER,
+                            )
                         )
                 ) { invocation ->
                     val publisher =
@@ -731,15 +737,20 @@
             )
             .forEach { (rxTypeSrc, rxRoomSrc, rxTypeClassName) ->
                 runProcessorTest(
-                    sources =
-                        listOf(
-                            COMMON.RX2_SINGLE,
-                            COMMON.RX3_SINGLE,
-                            COMMON.RX2_OBSERVABLE,
-                            COMMON.RX3_OBSERVABLE,
-                            COMMON.PUBLISHER,
-                            rxTypeSrc,
-                            rxRoomSrc
+                    sources = listOf(rxTypeSrc, rxRoomSrc),
+                    classpath =
+                        compileFiles(
+                            listOf(
+                                COMMON.RX2_SINGLE,
+                                COMMON.RX2_MAYBE,
+                                COMMON.RX2_COMPLETABLE,
+                                COMMON.RX2_OBSERVABLE,
+                                COMMON.RX3_SINGLE,
+                                COMMON.RX3_MAYBE,
+                                COMMON.RX3_COMPLETABLE,
+                                COMMON.RX3_OBSERVABLE,
+                                COMMON.PUBLISHER,
+                            )
                         )
                 ) { invocation ->
                     val flowable = invocation.processingEnv.requireTypeElement(rxTypeClassName)
@@ -761,15 +772,22 @@
             )
             .forEach { (rxTypeSrc, rxRoomSrc, rxTypeClassName) ->
                 runProcessorTest(
-                    sources =
-                        listOf(
-                            COMMON.RX2_SINGLE,
-                            COMMON.RX3_SINGLE,
-                            COMMON.RX2_FLOWABLE,
-                            COMMON.RX3_FLOWABLE,
-                            COMMON.PUBLISHER,
-                            rxTypeSrc,
-                            rxRoomSrc
+                    sources = listOf(rxTypeSrc, rxRoomSrc),
+                    classpath =
+                        compileFiles(
+                            listOf(
+                                COMMON.RX2_SINGLE,
+                                COMMON.RX2_MAYBE,
+                                COMMON.RX2_COMPLETABLE,
+                                COMMON.RX2_OBSERVABLE,
+                                COMMON.RX2_FLOWABLE,
+                                COMMON.RX3_SINGLE,
+                                COMMON.RX3_MAYBE,
+                                COMMON.RX3_COMPLETABLE,
+                                COMMON.RX3_OBSERVABLE,
+                                COMMON.RX3_FLOWABLE,
+                                COMMON.PUBLISHER,
+                            )
                         )
                 ) { invocation ->
                     val observable = invocation.processingEnv.requireTypeElement(rxTypeClassName)
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/testing/test_util.kt b/room/room-compiler/src/test/kotlin/androidx/room/testing/test_util.kt
index 7f3e54e..95c2eb0 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/testing/test_util.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/testing/test_util.kt
@@ -31,8 +31,6 @@
 import androidx.room.ext.KotlinTypeNames
 import androidx.room.ext.LifecyclesTypeNames
 import androidx.room.ext.ReactiveStreamsTypeNames
-import androidx.room.ext.RoomRxJava2TypeNames
-import androidx.room.ext.RoomRxJava3TypeNames
 import androidx.room.ext.RxJava2TypeNames
 import androidx.room.ext.RxJava3TypeNames
 import androidx.room.processor.DatabaseViewProcessor
@@ -123,9 +121,7 @@
         )
     }
 
-    val RX2_ROOM by lazy {
-        loadJavaCode("common/input/Rx2Room.java", RoomRxJava2TypeNames.RX_ROOM.canonicalName)
-    }
+    val RX2_ROOM by lazy { loadKotlinCode("common/input/Rx2Room.kt") }
 
     val RX3_FLOWABLE by lazy {
         loadJavaCode("common/input/rxjava3/Flowable.java", RxJava3TypeNames.FLOWABLE.canonicalName)
@@ -150,9 +146,7 @@
         )
     }
 
-    val RX3_ROOM by lazy {
-        loadJavaCode("common/input/Rx3Room.java", RoomRxJava3TypeNames.RX_ROOM.canonicalName)
-    }
+    val RX3_ROOM by lazy { loadKotlinCode("common/input/Rx3Room.kt") }
 
     val DATA_SOURCE_FACTORY by lazy { loadKotlinCode("common/input/DataSource.kt") }
 
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt
index 02646fd..b6130b5 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt
@@ -1982,18 +1982,6 @@
 
                 @Query("SELECT * FROM MyEntity WHERE pk IN (:arg)")
                 fun getMaybe(vararg arg: String?): Maybe<MyEntity>
-
-                @Query("SELECT * FROM MyEntity WHERE pk IN (:arg)")
-                fun getFlowableNullable(vararg arg: String?): Flowable<MyEntity?>
-
-                @Query("SELECT * FROM MyEntity WHERE pk IN (:arg)")
-                fun getObservableNullable(vararg arg: String?): Observable<MyEntity?>
-
-                @Query("SELECT * FROM MyEntity WHERE pk IN (:arg)")
-                fun getSingleNullable(vararg arg: String?): Single<MyEntity?>
-
-                @Query("SELECT * FROM MyEntity WHERE pk IN (:arg)")
-                fun getMaybeNullable(vararg arg: String?): Maybe<MyEntity?>
             }
 
             @Entity
@@ -2010,13 +1998,19 @@
                 listOf(
                     src,
                     databaseSrc,
-                    COMMON.RX2_ROOM,
-                    COMMON.RX2_FLOWABLE,
-                    COMMON.RX2_OBSERVABLE,
-                    COMMON.RX2_SINGLE,
-                    COMMON.RX2_MAYBE,
-                    COMMON.PUBLISHER,
-                    COMMON.RX2_EMPTY_RESULT_SET_EXCEPTION
+                ),
+            compiledFiles =
+                compileFiles(
+                    listOf(
+                        COMMON.RX2_ROOM,
+                        COMMON.RX2_FLOWABLE,
+                        COMMON.RX2_OBSERVABLE,
+                        COMMON.RX2_SINGLE,
+                        COMMON.RX2_MAYBE,
+                        COMMON.RX2_COMPLETABLE,
+                        COMMON.PUBLISHER,
+                        COMMON.RX2_EMPTY_RESULT_SET_EXCEPTION
+                    )
                 ),
             expectedFilePath = getTestGoldenPath(testName.methodName)
         )
@@ -2044,18 +2038,6 @@
 
                 @Query("SELECT * FROM MyEntity WHERE pk IN (:arg)")
                 fun getMaybe(vararg arg: String?): Maybe<MyEntity>
-
-                @Query("SELECT * FROM MyEntity WHERE pk IN (:arg)")
-                fun getFlowableNullable(vararg arg: String?): Flowable<MyEntity?>
-
-                @Query("SELECT * FROM MyEntity WHERE pk IN (:arg)")
-                fun getObservableNullable(vararg arg: String?): Observable<MyEntity?>
-
-                @Query("SELECT * FROM MyEntity WHERE pk IN (:arg)")
-                fun getSingleNullable(vararg arg: String?): Single<MyEntity?>
-
-                @Query("SELECT * FROM MyEntity WHERE pk IN (:arg)")
-                fun getMaybeNullable(vararg arg: String?): Maybe<MyEntity?>
             }
 
             @Entity
@@ -2072,13 +2054,19 @@
                 listOf(
                     src,
                     databaseSrc,
-                    COMMON.RX3_ROOM,
-                    COMMON.RX3_FLOWABLE,
-                    COMMON.RX3_OBSERVABLE,
-                    COMMON.RX3_SINGLE,
-                    COMMON.RX3_MAYBE,
-                    COMMON.PUBLISHER,
-                    COMMON.RX3_EMPTY_RESULT_SET_EXCEPTION
+                ),
+            compiledFiles =
+                compileFiles(
+                    listOf(
+                        COMMON.RX3_ROOM,
+                        COMMON.RX3_FLOWABLE,
+                        COMMON.RX3_OBSERVABLE,
+                        COMMON.RX3_SINGLE,
+                        COMMON.RX3_MAYBE,
+                        COMMON.RX3_COMPLETABLE,
+                        COMMON.PUBLISHER,
+                        COMMON.RX3_EMPTY_RESULT_SET_EXCEPTION
+                    )
                 ),
             expectedFilePath = getTestGoldenPath(testName.methodName)
         )
@@ -2119,14 +2107,19 @@
                 listOf(
                     src,
                     databaseSrc,
-                    COMMON.RX2_ROOM,
-                    COMMON.RX2_FLOWABLE,
-                    COMMON.RX2_OBSERVABLE,
-                    COMMON.RX2_SINGLE,
-                    COMMON.RX2_MAYBE,
-                    COMMON.RX2_COMPLETABLE,
-                    COMMON.PUBLISHER,
-                    COMMON.RX2_EMPTY_RESULT_SET_EXCEPTION
+                ),
+            compiledFiles =
+                compileFiles(
+                    listOf(
+                        COMMON.RX2_ROOM,
+                        COMMON.RX2_FLOWABLE,
+                        COMMON.RX2_OBSERVABLE,
+                        COMMON.RX2_SINGLE,
+                        COMMON.RX2_MAYBE,
+                        COMMON.RX2_COMPLETABLE,
+                        COMMON.PUBLISHER,
+                        COMMON.RX2_EMPTY_RESULT_SET_EXCEPTION
+                    )
                 ),
             expectedFilePath = getTestGoldenPath(testName.methodName)
         )
@@ -2167,14 +2160,19 @@
                 listOf(
                     src,
                     databaseSrc,
-                    COMMON.RX3_ROOM,
-                    COMMON.RX3_FLOWABLE,
-                    COMMON.RX3_OBSERVABLE,
-                    COMMON.RX3_SINGLE,
-                    COMMON.RX3_MAYBE,
-                    COMMON.RX3_COMPLETABLE,
-                    COMMON.PUBLISHER,
-                    COMMON.RX3_EMPTY_RESULT_SET_EXCEPTION
+                ),
+            compiledFiles =
+                compileFiles(
+                    listOf(
+                        COMMON.RX3_ROOM,
+                        COMMON.RX3_FLOWABLE,
+                        COMMON.RX3_OBSERVABLE,
+                        COMMON.RX3_SINGLE,
+                        COMMON.RX3_MAYBE,
+                        COMMON.RX3_COMPLETABLE,
+                        COMMON.PUBLISHER,
+                        COMMON.RX3_EMPTY_RESULT_SET_EXCEPTION
+                    )
                 ),
             expectedFilePath = getTestGoldenPath(testName.methodName)
         )
@@ -2286,13 +2284,19 @@
                     .trimIndent()
             )
         runTest(
-            sources =
-                listOf(
-                    src,
-                    databaseSrc,
-                    COMMON.RX2_SINGLE,
-                    COMMON.RX2_COMPLETABLE,
-                    COMMON.RX2_EMPTY_RESULT_SET_EXCEPTION,
+            sources = listOf(src, databaseSrc),
+            compiledFiles =
+                compileFiles(
+                    listOf(
+                        COMMON.RX2_ROOM,
+                        COMMON.RX2_SINGLE,
+                        COMMON.RX2_MAYBE,
+                        COMMON.RX2_COMPLETABLE,
+                        COMMON.RX2_FLOWABLE,
+                        COMMON.RX2_OBSERVABLE,
+                        COMMON.RX2_EMPTY_RESULT_SET_EXCEPTION,
+                        COMMON.PUBLISHER,
+                    )
                 ),
             expectedFilePath = getTestGoldenPath(testName.methodName)
         )
@@ -2344,13 +2348,19 @@
                     .trimIndent()
             )
         runTest(
-            sources =
-                listOf(
-                    src,
-                    databaseSrc,
-                    COMMON.RX3_SINGLE,
-                    COMMON.RX3_COMPLETABLE,
-                    COMMON.RX3_EMPTY_RESULT_SET_EXCEPTION
+            sources = listOf(src, databaseSrc),
+            compiledFiles =
+                compileFiles(
+                    listOf(
+                        COMMON.RX3_ROOM,
+                        COMMON.RX3_SINGLE,
+                        COMMON.RX3_MAYBE,
+                        COMMON.RX3_COMPLETABLE,
+                        COMMON.RX3_FLOWABLE,
+                        COMMON.RX3_OBSERVABLE,
+                        COMMON.RX3_EMPTY_RESULT_SET_EXCEPTION,
+                        COMMON.PUBLISHER,
+                    )
                 ),
             expectedFilePath = getTestGoldenPath(testName.methodName)
         )
diff --git a/room/room-compiler/src/test/test-data/common/input/Rx2Room.java b/room/room-compiler/src/test/test-data/common/input/Rx2Room.java
deleted file mode 100644
index 9e06920..0000000
--- a/room/room-compiler/src/test/test-data/common/input/Rx2Room.java
+++ /dev/null
@@ -1,39 +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.
- */
-
-// mock rx2 helper
-package androidx.room;
-
-import androidx.room.RoomDatabase;
-import java.util.concurrent.Callable;
-import io.reactivex.Flowable;
-import io.reactivex.Observable;
-import io.reactivex.Single;
-public class RxRoom {
-    public static <T> Flowable<T> createFlowable(final RoomDatabase database,
-            final boolean inTransaction, final String[] tableNames, final Callable<T> callable) {
-        return null;
-    }
-
-    public static <T> Observable<T> createObservable(final RoomDatabase database,
-            final boolean inTransaction, final String[] tableNames, final Callable<T> callable) {
-        return null;
-    }
-
-    public static <T> Single<T> createSingle(final Callable<? extends T> callable) {
-        return null;
-    }
-}
diff --git a/room/room-compiler/src/test/test-data/common/input/Rx2Room.kt b/room/room-compiler/src/test/test-data/common/input/Rx2Room.kt
new file mode 100644
index 0000000..771a7c1
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/common/input/Rx2Room.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.room
+
+import androidx.sqlite.SQLiteConnection
+import io.reactivex.Completable
+import io.reactivex.Flowable
+import io.reactivex.Maybe
+import io.reactivex.Observable
+import io.reactivex.Single
+import java.util.concurrent.Callable
+
+// mock rx2 helper
+ class RxRoom {
+
+    companion object {
+
+        @JvmField
+        val NOTHING: Any = Any()
+
+        @JvmStatic
+        fun <T : Any> createFlowable(
+            db: RoomDatabase,
+            inTransaction: Boolean,
+            tableNames: Array<String>,
+            block: (SQLiteConnection) -> T?
+        ): Flowable<T> {
+            TODO()
+        }
+
+        @JvmStatic
+        fun <T : Any> createObservable(
+            db: RoomDatabase,
+            inTransaction: Boolean,
+            tableNames: Array<String>,
+            block: (SQLiteConnection) -> T?
+        ): Observable<T> {
+            TODO()
+        }
+
+        @JvmStatic
+        fun <T : Any> createMaybe(
+            db: RoomDatabase,
+            isReadOnly: Boolean,
+            inTransaction: Boolean,
+            block: (SQLiteConnection) -> T?
+        ): Maybe<T> {
+            TODO()
+        }
+
+        @JvmStatic
+        fun createCompletable(
+            db: RoomDatabase,
+            isReadOnly: Boolean,
+            inTransaction: Boolean,
+            block: (SQLiteConnection) -> Unit
+        ): Completable {
+            TODO()
+        }
+
+        @JvmStatic
+        fun <T : Any> createSingle(
+            db: RoomDatabase,
+            isReadOnly: Boolean,
+            inTransaction: Boolean,
+            block: (SQLiteConnection) -> T?
+        ): Single<T> {
+            TODO()
+        }
+
+        @JvmStatic
+        fun createFlowable(database: RoomDatabase, vararg tableNames: String): Flowable<Any> {
+            TODO()
+        }
+
+        @JvmStatic
+        fun createObservable(database: RoomDatabase, vararg tableNames: String): Observable<Any> {
+            TODO()
+        }
+    }
+}
diff --git a/room/room-compiler/src/test/test-data/common/input/Rx3Room.java b/room/room-compiler/src/test/test-data/common/input/Rx3Room.java
deleted file mode 100644
index 518609d..0000000
--- a/room/room-compiler/src/test/test-data/common/input/Rx3Room.java
+++ /dev/null
@@ -1,40 +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.
- */
-
-// mock rx3 helper
-package androidx.room.rxjava3;
-
-import androidx.room.RoomDatabase;
-import java.util.concurrent.Callable;
-import io.reactivex.rxjava3.core.Flowable;
-import io.reactivex.rxjava3.core.Observable;
-import io.reactivex.rxjava3.core.Single;
-
-public class RxRoom {
-    public static <T> Flowable<T> createFlowable(final RoomDatabase database,
-            final boolean inTransaction, final String[] tableNames, final Callable<T> callable) {
-        return null;
-    }
-
-    public static <T> Observable<T> createObservable(final RoomDatabase database,
-            final boolean inTransaction, final String[] tableNames, final Callable<T> callable) {
-        return null;
-    }
-
-    public static <T> Single<T> createSingle(final Callable<? extends T> callable) {
-        return null;
-    }
-}
diff --git a/room/room-compiler/src/test/test-data/common/input/Rx3Room.kt b/room/room-compiler/src/test/test-data/common/input/Rx3Room.kt
new file mode 100644
index 0000000..663e479
--- /dev/null
+++ b/room/room-compiler/src/test/test-data/common/input/Rx3Room.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// mock rx2 helper
+@file:JvmName("RxRoom")
+
+package androidx.room.rxjava3
+
+import androidx.room.RoomDatabase
+import androidx.sqlite.SQLiteConnection
+import io.reactivex.rxjava3.core.Completable
+import io.reactivex.rxjava3.core.Flowable
+import io.reactivex.rxjava3.core.Maybe
+import io.reactivex.rxjava3.core.Observable
+import io.reactivex.rxjava3.core.Single
+import java.util.concurrent.Callable
+
+class Rx3RoomArtifactMarker private constructor()
+
+@JvmField
+val NOTHING: Any = Any()
+
+fun <T : Any> createFlowable(
+    db: RoomDatabase,
+    inTransaction: Boolean,
+    tableNames: Array<String>,
+    block: (SQLiteConnection) -> T?
+): Flowable<T> {
+    TODO()
+}
+
+fun <T : Any> createObservable(
+    db: RoomDatabase,
+    inTransaction: Boolean,
+    tableNames: Array<String>,
+    block: (SQLiteConnection) -> T?
+): Observable<T> {
+    TODO()
+}
+
+fun <T : Any> createMaybe(
+    db: RoomDatabase,
+    isReadOnly: Boolean,
+    inTransaction: Boolean,
+    block: (SQLiteConnection) -> T?
+): Maybe<T> {
+    TODO()
+}
+
+fun createCompletable(
+    db: RoomDatabase,
+    isReadOnly: Boolean,
+    inTransaction: Boolean,
+    block: (SQLiteConnection) -> Unit
+): Completable {
+    TODO()
+}
+
+fun <T : Any> createSingle(
+    db: RoomDatabase,
+    isReadOnly: Boolean,
+    inTransaction: Boolean,
+    block: (SQLiteConnection) -> T?
+): Single<T> {
+    TODO()
+}
+
+fun createFlowable(
+    database: RoomDatabase,
+    vararg tableNames: String
+): Flowable<Any> {
+    TODO()
+}
+
+fun <T : Any> createFlowable(
+    database: RoomDatabase,
+    inTransaction: Boolean,
+    tableNames: Array<String>,
+    callable: Callable<out T>
+): Flowable<T> {
+    TODO()
+}
+
+fun createObservable(
+    database: RoomDatabase,
+    vararg tableNames: String
+): Observable<Any> {
+    TODO()
+}
+
+fun <T : Any> createObservable(
+    database: RoomDatabase,
+    inTransaction: Boolean,
+    tableNames: Array<String>,
+    callable: Callable<out T>
+): Observable<T> {
+    TODO()
+}
+
+fun <T : Any> createSingle(callable: Callable<out T>): Single<T> {
+    TODO()
+}
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/withLambda/DeletionDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withLambda/DeletionDao.java
index 996eaf5..12f1fc8 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/javac/withLambda/DeletionDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withLambda/DeletionDao.java
@@ -1,30 +1,26 @@
 package foo.bar;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.room.EntityDeleteOrUpdateAdapter;
-import androidx.room.EntityDeletionOrUpdateAdapter;
 import androidx.room.RoomDatabase;
+import androidx.room.RxRoom;
 import androidx.room.util.DBUtil;
 import androidx.room.util.SQLiteConnectionUtil;
 import androidx.room.util.StringUtil;
 import androidx.sqlite.SQLiteStatement;
-import androidx.sqlite.db.SupportSQLiteStatement;
 import io.reactivex.Completable;
 import io.reactivex.Maybe;
 import io.reactivex.Single;
 import java.lang.Class;
-import java.lang.Exception;
 import java.lang.Integer;
 import java.lang.Override;
 import java.lang.String;
 import java.lang.StringBuilder;
 import java.lang.SuppressWarnings;
-import java.lang.Void;
 import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.Callable;
 import javax.annotation.processing.Generated;
+import kotlin.Unit;
 
 @Generated("androidx.room.RoomProcessor")
 @SuppressWarnings({"unchecked", "deprecation", "removal"})
@@ -33,8 +29,6 @@
 
   private final EntityDeleteOrUpdateAdapter<User> __deleteAdapterOfUser;
 
-  private final EntityDeletionOrUpdateAdapter<User> __deleteCompatAdapterOfUser;
-
   private final EntityDeleteOrUpdateAdapter<MultiPKeyEntity> __deleteAdapterOfMultiPKeyEntity;
 
   private final EntityDeleteOrUpdateAdapter<Book> __deleteAdapterOfBook;
@@ -53,18 +47,6 @@
         statement.bindLong(1, entity.uid);
       }
     };
-    this.__deleteCompatAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
-      @Override
-      @NonNull
-      protected String createQuery() {
-        return "DELETE FROM `User` WHERE `uid` = ?";
-      }
-
-      @Override
-      protected void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
-        statement.bindLong(1, entity.uid);
-      }
-    };
     this.__deleteAdapterOfMultiPKeyEntity = new EntityDeleteOrUpdateAdapter<MultiPKeyEntity>() {
       @Override
       @NonNull
@@ -164,57 +146,27 @@
 
   @Override
   public Completable deleteUserCompletable(final User user) {
-    return Completable.fromCallable(new Callable<Void>() {
-      @Override
-      @Nullable
-      public Void call() throws Exception {
-        __db.beginTransaction();
-        try {
-          __deleteCompatAdapterOfUser.handle(user);
-          __db.setTransactionSuccessful();
-          return null;
-        } finally {
-          __db.endTransaction();
-        }
-      }
+    return RxRoom.createCompletable(__db, false, true, (_connection) -> {
+      __deleteAdapterOfUser.handle(_connection, user);
+      return Unit.INSTANCE;
     });
   }
 
   @Override
   public Single<Integer> deleteUserSingle(final User user) {
-    return Single.fromCallable(new Callable<Integer>() {
-      @Override
-      @Nullable
-      public Integer call() throws Exception {
-        int _total = 0;
-        __db.beginTransaction();
-        try {
-          _total += __deleteCompatAdapterOfUser.handle(user);
-          __db.setTransactionSuccessful();
-          return _total;
-        } finally {
-          __db.endTransaction();
-        }
-      }
+    return RxRoom.createSingle(__db, false, true, (_connection) -> {
+      int _result = 0;
+      _result += __deleteAdapterOfUser.handle(_connection, user);
+      return _result;
     });
   }
 
   @Override
   public Maybe<Integer> deleteUserMaybe(final User user) {
-    return Maybe.fromCallable(new Callable<Integer>() {
-      @Override
-      @Nullable
-      public Integer call() throws Exception {
-        int _total = 0;
-        __db.beginTransaction();
-        try {
-          _total += __deleteCompatAdapterOfUser.handle(user);
-          __db.setTransactionSuccessful();
-          return _total;
-        } finally {
-          __db.endTransaction();
-        }
-      }
+    return RxRoom.createMaybe(__db, false, true, (_connection) -> {
+      int _result = 0;
+      _result += __deleteAdapterOfUser.handle(_connection, user);
+      return _result;
     });
   }
 
@@ -254,66 +206,48 @@
 
   @Override
   public Completable deleteByUidCompletable(final int uid) {
-    return Completable.fromCallable(new Callable<Void>() {
-      @Override
-      @Nullable
-      public Void call() throws Exception {
-        final String _sql = "DELETE FROM user where uid = ?";
-        final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
+    final String _sql = "DELETE FROM user where uid = ?";
+    return RxRoom.createCompletable(__db, false, true, (_connection) -> {
+      final SQLiteStatement _stmt = _connection.prepare(_sql);
+      try {
         int _argIndex = 1;
         _stmt.bindLong(_argIndex, uid);
-        __db.beginTransaction();
-        try {
-          _stmt.executeUpdateDelete();
-          __db.setTransactionSuccessful();
-          return null;
-        } finally {
-          __db.endTransaction();
-        }
+        _stmt.step();
+        return Unit.INSTANCE;
+      } finally {
+        _stmt.close();
       }
     });
   }
 
   @Override
   public Single<Integer> deleteByUidSingle(final int uid) {
-    return Single.fromCallable(new Callable<Integer>() {
-      @Override
-      @Nullable
-      public Integer call() throws Exception {
-        final String _sql = "DELETE FROM user where uid = ?";
-        final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
+    final String _sql = "DELETE FROM user where uid = ?";
+    return RxRoom.createSingle(__db, false, true, (_connection) -> {
+      final SQLiteStatement _stmt = _connection.prepare(_sql);
+      try {
         int _argIndex = 1;
         _stmt.bindLong(_argIndex, uid);
-        __db.beginTransaction();
-        try {
-          final Integer _result = _stmt.executeUpdateDelete();
-          __db.setTransactionSuccessful();
-          return _result;
-        } finally {
-          __db.endTransaction();
-        }
+        _stmt.step();
+        return SQLiteConnectionUtil.getTotalChangedRows(_connection);
+      } finally {
+        _stmt.close();
       }
     });
   }
 
   @Override
   public Maybe<Integer> deleteByUidMaybe(final int uid) {
-    return Maybe.fromCallable(new Callable<Integer>() {
-      @Override
-      @Nullable
-      public Integer call() throws Exception {
-        final String _sql = "DELETE FROM user where uid = ?";
-        final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
+    final String _sql = "DELETE FROM user where uid = ?";
+    return RxRoom.createMaybe(__db, false, true, (_connection) -> {
+      final SQLiteStatement _stmt = _connection.prepare(_sql);
+      try {
         int _argIndex = 1;
         _stmt.bindLong(_argIndex, uid);
-        __db.beginTransaction();
-        try {
-          final Integer _result = _stmt.executeUpdateDelete();
-          __db.setTransactionSuccessful();
-          return _result;
-        } finally {
-          __db.endTransaction();
-        }
+        _stmt.step();
+        return SQLiteConnectionUtil.getTotalChangedRows(_connection);
+      } finally {
+        _stmt.close();
       }
     });
   }
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/withLambda/UpdateDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withLambda/UpdateDao.java
index 6214715..4f809d6 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/javac/withLambda/UpdateDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withLambda/UpdateDao.java
@@ -1,27 +1,24 @@
 package foo.bar;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.room.EntityDeleteOrUpdateAdapter;
-import androidx.room.EntityDeletionOrUpdateAdapter;
 import androidx.room.RoomDatabase;
+import androidx.room.RxRoom;
 import androidx.room.util.DBUtil;
+import androidx.room.util.SQLiteConnectionUtil;
 import androidx.sqlite.SQLiteStatement;
-import androidx.sqlite.db.SupportSQLiteStatement;
 import io.reactivex.Completable;
 import io.reactivex.Maybe;
 import io.reactivex.Single;
 import java.lang.Class;
-import java.lang.Exception;
 import java.lang.Integer;
 import java.lang.Override;
 import java.lang.String;
 import java.lang.SuppressWarnings;
-import java.lang.Void;
 import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.Callable;
 import javax.annotation.processing.Generated;
+import kotlin.Unit;
 
 @Generated("androidx.room.RoomProcessor")
 @SuppressWarnings({"unchecked", "deprecation", "removal"})
@@ -32,8 +29,6 @@
 
   private final EntityDeleteOrUpdateAdapter<User> __updateAdapterOfUser_1;
 
-  private final EntityDeletionOrUpdateAdapter<User> __updateCompatAdapterOfUser;
-
   private final EntityDeleteOrUpdateAdapter<MultiPKeyEntity> __updateAdapterOfMultiPKeyEntity;
 
   private final EntityDeleteOrUpdateAdapter<Book> __updateAdapterOfBook;
@@ -88,30 +83,6 @@
         statement.bindLong(5, entity.uid);
       }
     };
-    this.__updateCompatAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
-      @Override
-      @NonNull
-      protected String createQuery() {
-        return "UPDATE OR ABORT `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
-      }
-
-      @Override
-      protected void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
-        statement.bindLong(1, entity.uid);
-        if (entity.name == null) {
-          statement.bindNull(2);
-        } else {
-          statement.bindString(2, entity.name);
-        }
-        if (entity.getLastName() == null) {
-          statement.bindNull(3);
-        } else {
-          statement.bindString(3, entity.getLastName());
-        }
-        statement.bindLong(4, entity.age);
-        statement.bindLong(5, entity.uid);
-      }
-    };
     this.__updateAdapterOfMultiPKeyEntity = new EntityDeleteOrUpdateAdapter<MultiPKeyEntity>() {
       @Override
       @NonNull
@@ -232,57 +203,27 @@
 
   @Override
   public Completable updateUserAndReturnCountCompletable(final User user) {
-    return Completable.fromCallable(new Callable<Void>() {
-      @Override
-      @Nullable
-      public Void call() throws Exception {
-        __db.beginTransaction();
-        try {
-          __updateCompatAdapterOfUser.handle(user);
-          __db.setTransactionSuccessful();
-          return null;
-        } finally {
-          __db.endTransaction();
-        }
-      }
+    return RxRoom.createCompletable(__db, false, true, (_connection) -> {
+      __updateAdapterOfUser.handle(_connection, user);
+      return Unit.INSTANCE;
     });
   }
 
   @Override
   public Single<Integer> updateUserAndReturnCountSingle(final User user) {
-    return Single.fromCallable(new Callable<Integer>() {
-      @Override
-      @Nullable
-      public Integer call() throws Exception {
-        int _total = 0;
-        __db.beginTransaction();
-        try {
-          _total += __updateCompatAdapterOfUser.handle(user);
-          __db.setTransactionSuccessful();
-          return _total;
-        } finally {
-          __db.endTransaction();
-        }
-      }
+    return RxRoom.createSingle(__db, false, true, (_connection) -> {
+      int _result = 0;
+      _result += __updateAdapterOfUser.handle(_connection, user);
+      return _result;
     });
   }
 
   @Override
   public Maybe<Integer> updateUserAndReturnCountMaybe(final User user) {
-    return Maybe.fromCallable(new Callable<Integer>() {
-      @Override
-      @Nullable
-      public Integer call() throws Exception {
-        int _total = 0;
-        __db.beginTransaction();
-        try {
-          _total += __updateCompatAdapterOfUser.handle(user);
-          __db.setTransactionSuccessful();
-          return _total;
-        } finally {
-          __db.endTransaction();
-        }
-      }
+    return RxRoom.createMaybe(__db, false, true, (_connection) -> {
+      int _result = 0;
+      _result += __updateAdapterOfUser.handle(_connection, user);
+      return _result;
     });
   }
 
@@ -340,60 +281,42 @@
 
   @Override
   public Completable ageUserAllCompletable() {
-    return Completable.fromCallable(new Callable<Void>() {
-      @Override
-      @Nullable
-      public Void call() throws Exception {
-        final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
-        final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
-        __db.beginTransaction();
-        try {
-          _stmt.executeUpdateDelete();
-          __db.setTransactionSuccessful();
-          return null;
-        } finally {
-          __db.endTransaction();
-        }
+    final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
+    return RxRoom.createCompletable(__db, false, true, (_connection) -> {
+      final SQLiteStatement _stmt = _connection.prepare(_sql);
+      try {
+        _stmt.step();
+        return Unit.INSTANCE;
+      } finally {
+        _stmt.close();
       }
     });
   }
 
   @Override
   public Single<Integer> ageUserAllSingle() {
-    return Single.fromCallable(new Callable<Integer>() {
-      @Override
-      @Nullable
-      public Integer call() throws Exception {
-        final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
-        final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
-        __db.beginTransaction();
-        try {
-          final Integer _result = _stmt.executeUpdateDelete();
-          __db.setTransactionSuccessful();
-          return _result;
-        } finally {
-          __db.endTransaction();
-        }
+    final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
+    return RxRoom.createSingle(__db, false, true, (_connection) -> {
+      final SQLiteStatement _stmt = _connection.prepare(_sql);
+      try {
+        _stmt.step();
+        return SQLiteConnectionUtil.getTotalChangedRows(_connection);
+      } finally {
+        _stmt.close();
       }
     });
   }
 
   @Override
   public Maybe<Integer> ageUserAllMaybe() {
-    return Maybe.fromCallable(new Callable<Integer>() {
-      @Override
-      @Nullable
-      public Integer call() throws Exception {
-        final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
-        final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
-        __db.beginTransaction();
-        try {
-          final Integer _result = _stmt.executeUpdateDelete();
-          __db.setTransactionSuccessful();
-          return _result;
-        } finally {
-          __db.endTransaction();
-        }
+    final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
+    return RxRoom.createMaybe(__db, false, true, (_connection) -> {
+      final SQLiteStatement _stmt = _connection.prepare(_sql);
+      try {
+        _stmt.step();
+        return SQLiteConnectionUtil.getTotalChangedRows(_connection);
+      } finally {
+        _stmt.close();
       }
     });
   }
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/withoutLambda/DeletionDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withoutLambda/DeletionDao.java
index e287d17..bbbac5e 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/javac/withoutLambda/DeletionDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withoutLambda/DeletionDao.java
@@ -3,19 +3,17 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.room.EntityDeleteOrUpdateAdapter;
-import androidx.room.EntityDeletionOrUpdateAdapter;
 import androidx.room.RoomDatabase;
+import androidx.room.RxRoom;
 import androidx.room.util.DBUtil;
 import androidx.room.util.SQLiteConnectionUtil;
 import androidx.room.util.StringUtil;
 import androidx.sqlite.SQLiteConnection;
 import androidx.sqlite.SQLiteStatement;
-import androidx.sqlite.db.SupportSQLiteStatement;
 import io.reactivex.Completable;
 import io.reactivex.Maybe;
 import io.reactivex.Single;
 import java.lang.Class;
-import java.lang.Exception;
 import java.lang.Integer;
 import java.lang.Override;
 import java.lang.String;
@@ -24,8 +22,8 @@
 import java.lang.Void;
 import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.Callable;
 import javax.annotation.processing.Generated;
+import kotlin.Unit;
 import kotlin.jvm.functions.Function1;
 
 @Generated("androidx.room.RoomProcessor")
@@ -35,8 +33,6 @@
 
   private final EntityDeleteOrUpdateAdapter<User> __deleteAdapterOfUser;
 
-  private final EntityDeletionOrUpdateAdapter<User> __deleteCompatAdapterOfUser;
-
   private final EntityDeleteOrUpdateAdapter<MultiPKeyEntity> __deleteAdapterOfMultiPKeyEntity;
 
   private final EntityDeleteOrUpdateAdapter<Book> __deleteAdapterOfBook;
@@ -55,18 +51,6 @@
         statement.bindLong(1, entity.uid);
       }
     };
-    this.__deleteCompatAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
-      @Override
-      @NonNull
-      protected String createQuery() {
-        return "DELETE FROM `User` WHERE `uid` = ?";
-      }
-
-      @Override
-      protected void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
-        statement.bindLong(1, entity.uid);
-      }
-    };
     this.__deleteAdapterOfMultiPKeyEntity = new EntityDeleteOrUpdateAdapter<MultiPKeyEntity>() {
       @Override
       @NonNull
@@ -194,56 +178,38 @@
 
   @Override
   public Completable deleteUserCompletable(final User user) {
-    return Completable.fromCallable(new Callable<Void>() {
+    return RxRoom.createCompletable(__db, false, true, new Function1<SQLiteConnection, Unit>() {
       @Override
-      @Nullable
-      public Void call() throws Exception {
-        __db.beginTransaction();
-        try {
-          __deleteCompatAdapterOfUser.handle(user);
-          __db.setTransactionSuccessful();
-          return null;
-        } finally {
-          __db.endTransaction();
-        }
+      @NonNull
+      public Unit invoke(@NonNull final SQLiteConnection _connection) {
+        __deleteAdapterOfUser.handle(_connection, user);
+        return Unit.INSTANCE;
       }
     });
   }
 
   @Override
   public Single<Integer> deleteUserSingle(final User user) {
-    return Single.fromCallable(new Callable<Integer>() {
+    return RxRoom.createSingle(__db, false, true, new Function1<SQLiteConnection, Integer>() {
       @Override
       @Nullable
-      public Integer call() throws Exception {
-        int _total = 0;
-        __db.beginTransaction();
-        try {
-          _total += __deleteCompatAdapterOfUser.handle(user);
-          __db.setTransactionSuccessful();
-          return _total;
-        } finally {
-          __db.endTransaction();
-        }
+      public Integer invoke(@NonNull final SQLiteConnection _connection) {
+        int _result = 0;
+        _result += __deleteAdapterOfUser.handle(_connection, user);
+        return _result;
       }
     });
   }
 
   @Override
   public Maybe<Integer> deleteUserMaybe(final User user) {
-    return Maybe.fromCallable(new Callable<Integer>() {
+    return RxRoom.createMaybe(__db, false, true, new Function1<SQLiteConnection, Integer>() {
       @Override
       @Nullable
-      public Integer call() throws Exception {
-        int _total = 0;
-        __db.beginTransaction();
-        try {
-          _total += __deleteCompatAdapterOfUser.handle(user);
-          __db.setTransactionSuccessful();
-          return _total;
-        } finally {
-          __db.endTransaction();
-        }
+      public Integer invoke(@NonNull final SQLiteConnection _connection) {
+        int _result = 0;
+        _result += __deleteAdapterOfUser.handle(_connection, user);
+        return _result;
       }
     });
   }
@@ -296,21 +262,19 @@
 
   @Override
   public Completable deleteByUidCompletable(final int uid) {
-    return Completable.fromCallable(new Callable<Void>() {
+    final String _sql = "DELETE FROM user where uid = ?";
+    return RxRoom.createCompletable(__db, false, true, new Function1<SQLiteConnection, Unit>() {
       @Override
-      @Nullable
-      public Void call() throws Exception {
-        final String _sql = "DELETE FROM user where uid = ?";
-        final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
-        int _argIndex = 1;
-        _stmt.bindLong(_argIndex, uid);
-        __db.beginTransaction();
+      @NonNull
+      public Unit invoke(@NonNull final SQLiteConnection _connection) {
+        final SQLiteStatement _stmt = _connection.prepare(_sql);
         try {
-          _stmt.executeUpdateDelete();
-          __db.setTransactionSuccessful();
-          return null;
+          int _argIndex = 1;
+          _stmt.bindLong(_argIndex, uid);
+          _stmt.step();
+          return Unit.INSTANCE;
         } finally {
-          __db.endTransaction();
+          _stmt.close();
         }
       }
     });
@@ -318,21 +282,19 @@
 
   @Override
   public Single<Integer> deleteByUidSingle(final int uid) {
-    return Single.fromCallable(new Callable<Integer>() {
+    final String _sql = "DELETE FROM user where uid = ?";
+    return RxRoom.createSingle(__db, false, true, new Function1<SQLiteConnection, Integer>() {
       @Override
       @Nullable
-      public Integer call() throws Exception {
-        final String _sql = "DELETE FROM user where uid = ?";
-        final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
-        int _argIndex = 1;
-        _stmt.bindLong(_argIndex, uid);
-        __db.beginTransaction();
+      public Integer invoke(@NonNull final SQLiteConnection _connection) {
+        final SQLiteStatement _stmt = _connection.prepare(_sql);
         try {
-          final Integer _result = _stmt.executeUpdateDelete();
-          __db.setTransactionSuccessful();
-          return _result;
+          int _argIndex = 1;
+          _stmt.bindLong(_argIndex, uid);
+          _stmt.step();
+          return SQLiteConnectionUtil.getTotalChangedRows(_connection);
         } finally {
-          __db.endTransaction();
+          _stmt.close();
         }
       }
     });
@@ -340,21 +302,19 @@
 
   @Override
   public Maybe<Integer> deleteByUidMaybe(final int uid) {
-    return Maybe.fromCallable(new Callable<Integer>() {
+    final String _sql = "DELETE FROM user where uid = ?";
+    return RxRoom.createMaybe(__db, false, true, new Function1<SQLiteConnection, Integer>() {
       @Override
       @Nullable
-      public Integer call() throws Exception {
-        final String _sql = "DELETE FROM user where uid = ?";
-        final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
-        int _argIndex = 1;
-        _stmt.bindLong(_argIndex, uid);
-        __db.beginTransaction();
+      public Integer invoke(@NonNull final SQLiteConnection _connection) {
+        final SQLiteStatement _stmt = _connection.prepare(_sql);
         try {
-          final Integer _result = _stmt.executeUpdateDelete();
-          __db.setTransactionSuccessful();
-          return _result;
+          int _argIndex = 1;
+          _stmt.bindLong(_argIndex, uid);
+          _stmt.step();
+          return SQLiteConnectionUtil.getTotalChangedRows(_connection);
         } finally {
-          __db.endTransaction();
+          _stmt.close();
         }
       }
     });
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/withoutLambda/UpdateDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withoutLambda/UpdateDao.java
index f3ca3df..8bbaf88 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/javac/withoutLambda/UpdateDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/withoutLambda/UpdateDao.java
@@ -3,17 +3,16 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.room.EntityDeleteOrUpdateAdapter;
-import androidx.room.EntityDeletionOrUpdateAdapter;
 import androidx.room.RoomDatabase;
+import androidx.room.RxRoom;
 import androidx.room.util.DBUtil;
+import androidx.room.util.SQLiteConnectionUtil;
 import androidx.sqlite.SQLiteConnection;
 import androidx.sqlite.SQLiteStatement;
-import androidx.sqlite.db.SupportSQLiteStatement;
 import io.reactivex.Completable;
 import io.reactivex.Maybe;
 import io.reactivex.Single;
 import java.lang.Class;
-import java.lang.Exception;
 import java.lang.Integer;
 import java.lang.Override;
 import java.lang.String;
@@ -21,8 +20,8 @@
 import java.lang.Void;
 import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.Callable;
 import javax.annotation.processing.Generated;
+import kotlin.Unit;
 import kotlin.jvm.functions.Function1;
 
 @Generated("androidx.room.RoomProcessor")
@@ -34,8 +33,6 @@
 
   private final EntityDeleteOrUpdateAdapter<User> __updateAdapterOfUser_1;
 
-  private final EntityDeletionOrUpdateAdapter<User> __updateCompatAdapterOfUser;
-
   private final EntityDeleteOrUpdateAdapter<MultiPKeyEntity> __updateAdapterOfMultiPKeyEntity;
 
   private final EntityDeleteOrUpdateAdapter<Book> __updateAdapterOfBook;
@@ -90,30 +87,6 @@
         statement.bindLong(5, entity.uid);
       }
     };
-    this.__updateCompatAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
-      @Override
-      @NonNull
-      protected String createQuery() {
-        return "UPDATE OR ABORT `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
-      }
-
-      @Override
-      protected void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
-        statement.bindLong(1, entity.uid);
-        if (entity.name == null) {
-          statement.bindNull(2);
-        } else {
-          statement.bindString(2, entity.name);
-        }
-        if (entity.getLastName() == null) {
-          statement.bindNull(3);
-        } else {
-          statement.bindString(3, entity.getLastName());
-        }
-        statement.bindLong(4, entity.age);
-        statement.bindLong(5, entity.uid);
-      }
-    };
     this.__updateAdapterOfMultiPKeyEntity = new EntityDeleteOrUpdateAdapter<MultiPKeyEntity>() {
       @Override
       @NonNull
@@ -266,56 +239,38 @@
 
   @Override
   public Completable updateUserAndReturnCountCompletable(final User user) {
-    return Completable.fromCallable(new Callable<Void>() {
+    return RxRoom.createCompletable(__db, false, true, new Function1<SQLiteConnection, Unit>() {
       @Override
-      @Nullable
-      public Void call() throws Exception {
-        __db.beginTransaction();
-        try {
-          __updateCompatAdapterOfUser.handle(user);
-          __db.setTransactionSuccessful();
-          return null;
-        } finally {
-          __db.endTransaction();
-        }
+      @NonNull
+      public Unit invoke(@NonNull final SQLiteConnection _connection) {
+        __updateAdapterOfUser.handle(_connection, user);
+        return Unit.INSTANCE;
       }
     });
   }
 
   @Override
   public Single<Integer> updateUserAndReturnCountSingle(final User user) {
-    return Single.fromCallable(new Callable<Integer>() {
+    return RxRoom.createSingle(__db, false, true, new Function1<SQLiteConnection, Integer>() {
       @Override
       @Nullable
-      public Integer call() throws Exception {
-        int _total = 0;
-        __db.beginTransaction();
-        try {
-          _total += __updateCompatAdapterOfUser.handle(user);
-          __db.setTransactionSuccessful();
-          return _total;
-        } finally {
-          __db.endTransaction();
-        }
+      public Integer invoke(@NonNull final SQLiteConnection _connection) {
+        int _result = 0;
+        _result += __updateAdapterOfUser.handle(_connection, user);
+        return _result;
       }
     });
   }
 
   @Override
   public Maybe<Integer> updateUserAndReturnCountMaybe(final User user) {
-    return Maybe.fromCallable(new Callable<Integer>() {
+    return RxRoom.createMaybe(__db, false, true, new Function1<SQLiteConnection, Integer>() {
       @Override
       @Nullable
-      public Integer call() throws Exception {
-        int _total = 0;
-        __db.beginTransaction();
-        try {
-          _total += __updateCompatAdapterOfUser.handle(user);
-          __db.setTransactionSuccessful();
-          return _total;
-        } finally {
-          __db.endTransaction();
-        }
+      public Integer invoke(@NonNull final SQLiteConnection _connection) {
+        int _result = 0;
+        _result += __updateAdapterOfUser.handle(_connection, user);
+        return _result;
       }
     });
   }
@@ -390,19 +345,17 @@
 
   @Override
   public Completable ageUserAllCompletable() {
-    return Completable.fromCallable(new Callable<Void>() {
+    final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
+    return RxRoom.createCompletable(__db, false, true, new Function1<SQLiteConnection, Unit>() {
       @Override
-      @Nullable
-      public Void call() throws Exception {
-        final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
-        final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
-        __db.beginTransaction();
+      @NonNull
+      public Unit invoke(@NonNull final SQLiteConnection _connection) {
+        final SQLiteStatement _stmt = _connection.prepare(_sql);
         try {
-          _stmt.executeUpdateDelete();
-          __db.setTransactionSuccessful();
-          return null;
+          _stmt.step();
+          return Unit.INSTANCE;
         } finally {
-          __db.endTransaction();
+          _stmt.close();
         }
       }
     });
@@ -410,19 +363,17 @@
 
   @Override
   public Single<Integer> ageUserAllSingle() {
-    return Single.fromCallable(new Callable<Integer>() {
+    final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
+    return RxRoom.createSingle(__db, false, true, new Function1<SQLiteConnection, Integer>() {
       @Override
       @Nullable
-      public Integer call() throws Exception {
-        final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
-        final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
-        __db.beginTransaction();
+      public Integer invoke(@NonNull final SQLiteConnection _connection) {
+        final SQLiteStatement _stmt = _connection.prepare(_sql);
         try {
-          final Integer _result = _stmt.executeUpdateDelete();
-          __db.setTransactionSuccessful();
-          return _result;
+          _stmt.step();
+          return SQLiteConnectionUtil.getTotalChangedRows(_connection);
         } finally {
-          __db.endTransaction();
+          _stmt.close();
         }
       }
     });
@@ -430,19 +381,17 @@
 
   @Override
   public Maybe<Integer> ageUserAllMaybe() {
-    return Maybe.fromCallable(new Callable<Integer>() {
+    final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
+    return RxRoom.createMaybe(__db, false, true, new Function1<SQLiteConnection, Integer>() {
       @Override
       @Nullable
-      public Integer call() throws Exception {
-        final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
-        final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
-        __db.beginTransaction();
+      public Integer invoke(@NonNull final SQLiteConnection _connection) {
+        final SQLiteStatement _stmt = _connection.prepare(_sql);
         try {
-          final Integer _result = _stmt.executeUpdateDelete();
-          __db.setTransactionSuccessful();
-          return _result;
+          _stmt.step();
+          return SQLiteConnectionUtil.getTotalChangedRows(_connection);
         } finally {
-          __db.endTransaction();
+          _stmt.close();
         }
       }
     });
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/DeletionDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/DeletionDao.java
index 4d0654d..d81f936 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/DeletionDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/DeletionDao.java
@@ -1,30 +1,26 @@
 package foo.bar;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.room.EntityDeleteOrUpdateAdapter;
-import androidx.room.EntityDeletionOrUpdateAdapter;
 import androidx.room.RoomDatabase;
+import androidx.room.RxRoom;
 import androidx.room.util.DBUtil;
 import androidx.room.util.SQLiteConnectionUtil;
 import androidx.room.util.StringUtil;
 import androidx.sqlite.SQLiteStatement;
-import androidx.sqlite.db.SupportSQLiteStatement;
 import io.reactivex.Completable;
 import io.reactivex.Maybe;
 import io.reactivex.Single;
 import java.lang.Class;
-import java.lang.Exception;
 import java.lang.Integer;
 import java.lang.Override;
 import java.lang.String;
 import java.lang.StringBuilder;
 import java.lang.SuppressWarnings;
-import java.lang.Void;
 import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.Callable;
 import javax.annotation.processing.Generated;
+import kotlin.Unit;
 
 @Generated("androidx.room.RoomProcessor")
 @SuppressWarnings({"unchecked", "deprecation", "removal"})
@@ -33,8 +29,6 @@
 
   private final EntityDeleteOrUpdateAdapter<User> __deleteAdapterOfUser;
 
-  private final EntityDeletionOrUpdateAdapter<User> __deleteCompatAdapterOfUser;
-
   private final EntityDeleteOrUpdateAdapter<MultiPKeyEntity> __deleteAdapterOfMultiPKeyEntity;
 
   private final EntityDeleteOrUpdateAdapter<Book> __deleteAdapterOfBook;
@@ -53,19 +47,6 @@
         statement.bindLong(1, entity.uid);
       }
     };
-    this.__deleteCompatAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
-      @Override
-      @NonNull
-      protected String createQuery() {
-        return "DELETE FROM `User` WHERE `uid` = ?";
-      }
-
-      @Override
-      protected void bind(@NonNull final SupportSQLiteStatement statement,
-          @NonNull final User entity) {
-        statement.bindLong(1, entity.uid);
-      }
-    };
     this.__deleteAdapterOfMultiPKeyEntity = new EntityDeleteOrUpdateAdapter<MultiPKeyEntity>() {
       @Override
       @NonNull
@@ -158,57 +139,27 @@
 
   @Override
   public Completable deleteUserCompletable(final User user) {
-    return Completable.fromCallable(new Callable<Void>() {
-      @Override
-      @Nullable
-      public Void call() throws Exception {
-        __db.beginTransaction();
-        try {
-          __deleteCompatAdapterOfUser.handle(user);
-          __db.setTransactionSuccessful();
-          return null;
-        } finally {
-          __db.endTransaction();
-        }
-      }
+    return RxRoom.createCompletable(__db, false, true, (_connection) -> {
+      __deleteAdapterOfUser.handle(_connection, user);
+      return Unit.INSTANCE;
     });
   }
 
   @Override
   public Single<Integer> deleteUserSingle(final User user) {
-    return Single.fromCallable(new Callable<Integer>() {
-      @Override
-      @Nullable
-      public Integer call() throws Exception {
-        int _total = 0;
-        __db.beginTransaction();
-        try {
-          _total += __deleteCompatAdapterOfUser.handle(user);
-          __db.setTransactionSuccessful();
-          return _total;
-        } finally {
-          __db.endTransaction();
-        }
-      }
+    return RxRoom.createSingle(__db, false, true, (_connection) -> {
+      int _result = 0;
+      _result += __deleteAdapterOfUser.handle(_connection, user);
+      return _result;
     });
   }
 
   @Override
   public Maybe<Integer> deleteUserMaybe(final User user) {
-    return Maybe.fromCallable(new Callable<Integer>() {
-      @Override
-      @Nullable
-      public Integer call() throws Exception {
-        int _total = 0;
-        __db.beginTransaction();
-        try {
-          _total += __deleteCompatAdapterOfUser.handle(user);
-          __db.setTransactionSuccessful();
-          return _total;
-        } finally {
-          __db.endTransaction();
-        }
-      }
+    return RxRoom.createMaybe(__db, false, true, (_connection) -> {
+      int _result = 0;
+      _result += __deleteAdapterOfUser.handle(_connection, user);
+      return _result;
     });
   }
 
@@ -248,66 +199,48 @@
 
   @Override
   public Completable deleteByUidCompletable(final int uid) {
-    return Completable.fromCallable(new Callable<Void>() {
-      @Override
-      @Nullable
-      public Void call() throws Exception {
-        final String _sql = "DELETE FROM user where uid = ?";
-        final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
+    final String _sql = "DELETE FROM user where uid = ?";
+    return RxRoom.createCompletable(__db, false, true, (_connection) -> {
+      final SQLiteStatement _stmt = _connection.prepare(_sql);
+      try {
         int _argIndex = 1;
         _stmt.bindLong(_argIndex, uid);
-        __db.beginTransaction();
-        try {
-          _stmt.executeUpdateDelete();
-          __db.setTransactionSuccessful();
-          return null;
-        } finally {
-          __db.endTransaction();
-        }
+        _stmt.step();
+        return Unit.INSTANCE;
+      } finally {
+        _stmt.close();
       }
     });
   }
 
   @Override
   public Single<Integer> deleteByUidSingle(final int uid) {
-    return Single.fromCallable(new Callable<Integer>() {
-      @Override
-      @Nullable
-      public Integer call() throws Exception {
-        final String _sql = "DELETE FROM user where uid = ?";
-        final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
+    final String _sql = "DELETE FROM user where uid = ?";
+    return RxRoom.createSingle(__db, false, true, (_connection) -> {
+      final SQLiteStatement _stmt = _connection.prepare(_sql);
+      try {
         int _argIndex = 1;
         _stmt.bindLong(_argIndex, uid);
-        __db.beginTransaction();
-        try {
-          final Integer _result = _stmt.executeUpdateDelete();
-          __db.setTransactionSuccessful();
-          return _result;
-        } finally {
-          __db.endTransaction();
-        }
+        _stmt.step();
+        return SQLiteConnectionUtil.getTotalChangedRows(_connection);
+      } finally {
+        _stmt.close();
       }
     });
   }
 
   @Override
   public Maybe<Integer> deleteByUidMaybe(final int uid) {
-    return Maybe.fromCallable(new Callable<Integer>() {
-      @Override
-      @Nullable
-      public Integer call() throws Exception {
-        final String _sql = "DELETE FROM user where uid = ?";
-        final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
+    final String _sql = "DELETE FROM user where uid = ?";
+    return RxRoom.createMaybe(__db, false, true, (_connection) -> {
+      final SQLiteStatement _stmt = _connection.prepare(_sql);
+      try {
         int _argIndex = 1;
         _stmt.bindLong(_argIndex, uid);
-        __db.beginTransaction();
-        try {
-          final Integer _result = _stmt.executeUpdateDelete();
-          __db.setTransactionSuccessful();
-          return _result;
-        } finally {
-          __db.endTransaction();
-        }
+        _stmt.step();
+        return SQLiteConnectionUtil.getTotalChangedRows(_connection);
+      } finally {
+        _stmt.close();
       }
     });
   }
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpdateDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpdateDao.java
index a94f1d5..41ba21c 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpdateDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpdateDao.java
@@ -1,27 +1,24 @@
 package foo.bar;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.room.EntityDeleteOrUpdateAdapter;
-import androidx.room.EntityDeletionOrUpdateAdapter;
 import androidx.room.RoomDatabase;
+import androidx.room.RxRoom;
 import androidx.room.util.DBUtil;
+import androidx.room.util.SQLiteConnectionUtil;
 import androidx.sqlite.SQLiteStatement;
-import androidx.sqlite.db.SupportSQLiteStatement;
 import io.reactivex.Completable;
 import io.reactivex.Maybe;
 import io.reactivex.Single;
 import java.lang.Class;
-import java.lang.Exception;
 import java.lang.Integer;
 import java.lang.Override;
 import java.lang.String;
 import java.lang.SuppressWarnings;
-import java.lang.Void;
 import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.Callable;
 import javax.annotation.processing.Generated;
+import kotlin.Unit;
 
 @Generated("androidx.room.RoomProcessor")
 @SuppressWarnings({"unchecked", "deprecation", "removal"})
@@ -32,8 +29,6 @@
 
   private final EntityDeleteOrUpdateAdapter<User> __updateAdapterOfUser_1;
 
-  private final EntityDeletionOrUpdateAdapter<User> __updateCompatAdapterOfUser;
-
   private final EntityDeleteOrUpdateAdapter<MultiPKeyEntity> __updateAdapterOfMultiPKeyEntity;
 
   private final EntityDeleteOrUpdateAdapter<Book> __updateAdapterOfBook;
@@ -88,31 +83,6 @@
         statement.bindLong(5, entity.uid);
       }
     };
-    this.__updateCompatAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
-      @Override
-      @NonNull
-      protected String createQuery() {
-        return "UPDATE OR ABORT `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
-      }
-
-      @Override
-      protected void bind(@NonNull final SupportSQLiteStatement statement,
-          @NonNull final User entity) {
-        statement.bindLong(1, entity.uid);
-        if (entity.name == null) {
-          statement.bindNull(2);
-        } else {
-          statement.bindString(2, entity.name);
-        }
-        if (entity.getLastName() == null) {
-          statement.bindNull(3);
-        } else {
-          statement.bindString(3, entity.getLastName());
-        }
-        statement.bindLong(4, entity.age);
-        statement.bindLong(5, entity.uid);
-      }
-    };
     this.__updateAdapterOfMultiPKeyEntity = new EntityDeleteOrUpdateAdapter<MultiPKeyEntity>() {
       @Override
       @NonNull
@@ -218,57 +188,27 @@
 
   @Override
   public Completable updateUserAndReturnCountCompletable(final User user) {
-    return Completable.fromCallable(new Callable<Void>() {
-      @Override
-      @Nullable
-      public Void call() throws Exception {
-        __db.beginTransaction();
-        try {
-          __updateCompatAdapterOfUser.handle(user);
-          __db.setTransactionSuccessful();
-          return null;
-        } finally {
-          __db.endTransaction();
-        }
-      }
+    return RxRoom.createCompletable(__db, false, true, (_connection) -> {
+      __updateAdapterOfUser.handle(_connection, user);
+      return Unit.INSTANCE;
     });
   }
 
   @Override
   public Single<Integer> updateUserAndReturnCountSingle(final User user) {
-    return Single.fromCallable(new Callable<Integer>() {
-      @Override
-      @Nullable
-      public Integer call() throws Exception {
-        int _total = 0;
-        __db.beginTransaction();
-        try {
-          _total += __updateCompatAdapterOfUser.handle(user);
-          __db.setTransactionSuccessful();
-          return _total;
-        } finally {
-          __db.endTransaction();
-        }
-      }
+    return RxRoom.createSingle(__db, false, true, (_connection) -> {
+      int _result = 0;
+      _result += __updateAdapterOfUser.handle(_connection, user);
+      return _result;
     });
   }
 
   @Override
   public Maybe<Integer> updateUserAndReturnCountMaybe(final User user) {
-    return Maybe.fromCallable(new Callable<Integer>() {
-      @Override
-      @Nullable
-      public Integer call() throws Exception {
-        int _total = 0;
-        __db.beginTransaction();
-        try {
-          _total += __updateCompatAdapterOfUser.handle(user);
-          __db.setTransactionSuccessful();
-          return _total;
-        } finally {
-          __db.endTransaction();
-        }
-      }
+    return RxRoom.createMaybe(__db, false, true, (_connection) -> {
+      int _result = 0;
+      _result += __updateAdapterOfUser.handle(_connection, user);
+      return _result;
     });
   }
 
@@ -326,60 +266,42 @@
 
   @Override
   public Completable ageUserAllCompletable() {
-    return Completable.fromCallable(new Callable<Void>() {
-      @Override
-      @Nullable
-      public Void call() throws Exception {
-        final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
-        final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
-        __db.beginTransaction();
-        try {
-          _stmt.executeUpdateDelete();
-          __db.setTransactionSuccessful();
-          return null;
-        } finally {
-          __db.endTransaction();
-        }
+    final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
+    return RxRoom.createCompletable(__db, false, true, (_connection) -> {
+      final SQLiteStatement _stmt = _connection.prepare(_sql);
+      try {
+        _stmt.step();
+        return Unit.INSTANCE;
+      } finally {
+        _stmt.close();
       }
     });
   }
 
   @Override
   public Single<Integer> ageUserAllSingle() {
-    return Single.fromCallable(new Callable<Integer>() {
-      @Override
-      @Nullable
-      public Integer call() throws Exception {
-        final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
-        final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
-        __db.beginTransaction();
-        try {
-          final Integer _result = _stmt.executeUpdateDelete();
-          __db.setTransactionSuccessful();
-          return _result;
-        } finally {
-          __db.endTransaction();
-        }
+    final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
+    return RxRoom.createSingle(__db, false, true, (_connection) -> {
+      final SQLiteStatement _stmt = _connection.prepare(_sql);
+      try {
+        _stmt.step();
+        return SQLiteConnectionUtil.getTotalChangedRows(_connection);
+      } finally {
+        _stmt.close();
       }
     });
   }
 
   @Override
   public Maybe<Integer> ageUserAllMaybe() {
-    return Maybe.fromCallable(new Callable<Integer>() {
-      @Override
-      @Nullable
-      public Integer call() throws Exception {
-        final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
-        final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
-        __db.beginTransaction();
-        try {
-          final Integer _result = _stmt.executeUpdateDelete();
-          __db.setTransactionSuccessful();
-          return _result;
-        } finally {
-          __db.endTransaction();
-        }
+    final String _sql = "UPDATE User SET ageColumn = ageColumn + 1";
+    return RxRoom.createMaybe(__db, false, true, (_connection) -> {
+      final SQLiteStatement _stmt = _connection.prepare(_sql);
+      try {
+        _stmt.step();
+        return SQLiteConnectionUtil.getTotalChangedRows(_connection);
+      } finally {
+        _stmt.close();
       }
     });
   }
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/callableQuery_rx2.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/callableQuery_rx2.kt
index 3ac3f8a..18e3a2b 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/callableQuery_rx2.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/callableQuery_rx2.kt
@@ -1,17 +1,15 @@
-import android.database.Cursor
-import androidx.room.EmptyResultSetException
 import androidx.room.RoomDatabase
-import androidx.room.RoomSQLiteQuery
-import androidx.room.RoomSQLiteQuery.Companion.acquire
-import androidx.room.RxRoom
+import androidx.room.RxRoom.Companion.createFlowable
+import androidx.room.RxRoom.Companion.createMaybe
+import androidx.room.RxRoom.Companion.createObservable
+import androidx.room.RxRoom.Companion.createSingle
 import androidx.room.util.appendPlaceholders
 import androidx.room.util.getColumnIndexOrThrow
-import androidx.room.util.query
+import androidx.sqlite.SQLiteStatement
 import io.reactivex.Flowable
 import io.reactivex.Maybe
 import io.reactivex.Observable
 import io.reactivex.Single
-import java.util.concurrent.Callable
 import javax.`annotation`.processing.Generated
 import kotlin.Int
 import kotlin.String
@@ -37,43 +35,35 @@
     appendPlaceholders(_stringBuilder, _inputSize)
     _stringBuilder.append(")")
     val _sql: String = _stringBuilder.toString()
-    val _argCount: Int = 0 + _inputSize
-    val _statement: RoomSQLiteQuery = acquire(_sql, _argCount)
-    var _argIndex: Int = 1
-    for (_item: String? in arg) {
-      if (_item == null) {
-        _statement.bindNull(_argIndex)
-      } else {
-        _statement.bindString(_argIndex, _item)
-      }
-      _argIndex++
-    }
-    return RxRoom.createFlowable(__db, false, arrayOf("MyEntity"), object : Callable<MyEntity> {
-      public override fun call(): MyEntity {
-        val _cursor: Cursor = query(__db, _statement, false, null)
-        try {
-          val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
-          val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_cursor, "other")
-          val _result: MyEntity
-          if (_cursor.moveToFirst()) {
-            val _tmpPk: Int
-            _tmpPk = _cursor.getInt(_cursorIndexOfPk)
-            val _tmpOther: String
-            _tmpOther = _cursor.getString(_cursorIndexOfOther)
-            _result = MyEntity(_tmpPk,_tmpOther)
+    return createFlowable(__db, false, arrayOf("MyEntity")) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
+      try {
+        var _argIndex: Int = 1
+        for (_item: String? in arg) {
+          if (_item == null) {
+            _stmt.bindNull(_argIndex)
           } else {
-            error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
+            _stmt.bindText(_argIndex, _item)
           }
-          return _result
-        } finally {
-          _cursor.close()
+          _argIndex++
         }
+        val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_stmt, "pk")
+        val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_stmt, "other")
+        val _result: MyEntity
+        if (_stmt.step()) {
+          val _tmpPk: Int
+          _tmpPk = _stmt.getLong(_cursorIndexOfPk).toInt()
+          val _tmpOther: String
+          _tmpOther = _stmt.getText(_cursorIndexOfOther)
+          _result = MyEntity(_tmpPk,_tmpOther)
+        } else {
+          error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
+        }
+        _result
+      } finally {
+        _stmt.close()
       }
-
-      protected fun finalize() {
-        _statement.release()
-      }
-    })
+    }
   }
 
   public override fun getObservable(vararg arg: String?): Observable<MyEntity> {
@@ -83,43 +73,35 @@
     appendPlaceholders(_stringBuilder, _inputSize)
     _stringBuilder.append(")")
     val _sql: String = _stringBuilder.toString()
-    val _argCount: Int = 0 + _inputSize
-    val _statement: RoomSQLiteQuery = acquire(_sql, _argCount)
-    var _argIndex: Int = 1
-    for (_item: String? in arg) {
-      if (_item == null) {
-        _statement.bindNull(_argIndex)
-      } else {
-        _statement.bindString(_argIndex, _item)
-      }
-      _argIndex++
-    }
-    return RxRoom.createObservable(__db, false, arrayOf("MyEntity"), object : Callable<MyEntity> {
-      public override fun call(): MyEntity {
-        val _cursor: Cursor = query(__db, _statement, false, null)
-        try {
-          val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
-          val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_cursor, "other")
-          val _result: MyEntity
-          if (_cursor.moveToFirst()) {
-            val _tmpPk: Int
-            _tmpPk = _cursor.getInt(_cursorIndexOfPk)
-            val _tmpOther: String
-            _tmpOther = _cursor.getString(_cursorIndexOfOther)
-            _result = MyEntity(_tmpPk,_tmpOther)
+    return createObservable(__db, false, arrayOf("MyEntity")) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
+      try {
+        var _argIndex: Int = 1
+        for (_item: String? in arg) {
+          if (_item == null) {
+            _stmt.bindNull(_argIndex)
           } else {
-            error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
+            _stmt.bindText(_argIndex, _item)
           }
-          return _result
-        } finally {
-          _cursor.close()
+          _argIndex++
         }
+        val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_stmt, "pk")
+        val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_stmt, "other")
+        val _result: MyEntity
+        if (_stmt.step()) {
+          val _tmpPk: Int
+          _tmpPk = _stmt.getLong(_cursorIndexOfPk).toInt()
+          val _tmpOther: String
+          _tmpOther = _stmt.getText(_cursorIndexOfOther)
+          _result = MyEntity(_tmpPk,_tmpOther)
+        } else {
+          error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
+        }
+        _result
+      } finally {
+        _stmt.close()
       }
-
-      protected fun finalize() {
-        _statement.release()
-      }
-    })
+    }
   }
 
   public override fun getSingle(vararg arg: String?): Single<MyEntity> {
@@ -129,46 +111,35 @@
     appendPlaceholders(_stringBuilder, _inputSize)
     _stringBuilder.append(")")
     val _sql: String = _stringBuilder.toString()
-    val _argCount: Int = 0 + _inputSize
-    val _statement: RoomSQLiteQuery = acquire(_sql, _argCount)
-    var _argIndex: Int = 1
-    for (_item: String? in arg) {
-      if (_item == null) {
-        _statement.bindNull(_argIndex)
-      } else {
-        _statement.bindString(_argIndex, _item)
-      }
-      _argIndex++
-    }
-    return RxRoom.createSingle(object : Callable<MyEntity?> {
-      public override fun call(): MyEntity? {
-        val _cursor: Cursor = query(__db, _statement, false, null)
-        try {
-          val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
-          val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_cursor, "other")
-          val _result: MyEntity?
-          if (_cursor.moveToFirst()) {
-            val _tmpPk: Int
-            _tmpPk = _cursor.getInt(_cursorIndexOfPk)
-            val _tmpOther: String
-            _tmpOther = _cursor.getString(_cursorIndexOfOther)
-            _result = MyEntity(_tmpPk,_tmpOther)
+    return createSingle(__db, true, false) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
+      try {
+        var _argIndex: Int = 1
+        for (_item: String? in arg) {
+          if (_item == null) {
+            _stmt.bindNull(_argIndex)
           } else {
-            _result = null
+            _stmt.bindText(_argIndex, _item)
           }
-          if (_result == null) {
-            throw EmptyResultSetException("Query returned empty result set: " + _statement.sql)
-          }
-          return _result
-        } finally {
-          _cursor.close()
+          _argIndex++
         }
+        val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_stmt, "pk")
+        val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_stmt, "other")
+        val _result: MyEntity?
+        if (_stmt.step()) {
+          val _tmpPk: Int
+          _tmpPk = _stmt.getLong(_cursorIndexOfPk).toInt()
+          val _tmpOther: String
+          _tmpOther = _stmt.getText(_cursorIndexOfOther)
+          _result = MyEntity(_tmpPk,_tmpOther)
+        } else {
+          _result = null
+        }
+        _result
+      } finally {
+        _stmt.close()
       }
-
-      protected fun finalize() {
-        _statement.release()
-      }
-    })
+    }
   }
 
   public override fun getMaybe(vararg arg: String?): Maybe<MyEntity> {
@@ -178,230 +149,35 @@
     appendPlaceholders(_stringBuilder, _inputSize)
     _stringBuilder.append(")")
     val _sql: String = _stringBuilder.toString()
-    val _argCount: Int = 0 + _inputSize
-    val _statement: RoomSQLiteQuery = acquire(_sql, _argCount)
-    var _argIndex: Int = 1
-    for (_item: String? in arg) {
-      if (_item == null) {
-        _statement.bindNull(_argIndex)
-      } else {
-        _statement.bindString(_argIndex, _item)
-      }
-      _argIndex++
-    }
-    return Maybe.fromCallable(object : Callable<MyEntity?> {
-      public override fun call(): MyEntity? {
-        val _cursor: Cursor = query(__db, _statement, false, null)
-        try {
-          val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
-          val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_cursor, "other")
-          val _result: MyEntity?
-          if (_cursor.moveToFirst()) {
-            val _tmpPk: Int
-            _tmpPk = _cursor.getInt(_cursorIndexOfPk)
-            val _tmpOther: String
-            _tmpOther = _cursor.getString(_cursorIndexOfOther)
-            _result = MyEntity(_tmpPk,_tmpOther)
+    return createMaybe(__db, true, false) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
+      try {
+        var _argIndex: Int = 1
+        for (_item: String? in arg) {
+          if (_item == null) {
+            _stmt.bindNull(_argIndex)
           } else {
-            _result = null
+            _stmt.bindText(_argIndex, _item)
           }
-          return _result
-        } finally {
-          _cursor.close()
+          _argIndex++
         }
+        val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_stmt, "pk")
+        val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_stmt, "other")
+        val _result: MyEntity?
+        if (_stmt.step()) {
+          val _tmpPk: Int
+          _tmpPk = _stmt.getLong(_cursorIndexOfPk).toInt()
+          val _tmpOther: String
+          _tmpOther = _stmt.getText(_cursorIndexOfOther)
+          _result = MyEntity(_tmpPk,_tmpOther)
+        } else {
+          _result = null
+        }
+        _result
+      } finally {
+        _stmt.close()
       }
-
-      protected fun finalize() {
-        _statement.release()
-      }
-    })
-  }
-
-  public override fun getFlowableNullable(vararg arg: String?): Flowable<MyEntity?> {
-    val _stringBuilder: StringBuilder = StringBuilder()
-    _stringBuilder.append("SELECT * FROM MyEntity WHERE pk IN (")
-    val _inputSize: Int = arg.size
-    appendPlaceholders(_stringBuilder, _inputSize)
-    _stringBuilder.append(")")
-    val _sql: String = _stringBuilder.toString()
-    val _argCount: Int = 0 + _inputSize
-    val _statement: RoomSQLiteQuery = acquire(_sql, _argCount)
-    var _argIndex: Int = 1
-    for (_item: String? in arg) {
-      if (_item == null) {
-        _statement.bindNull(_argIndex)
-      } else {
-        _statement.bindString(_argIndex, _item)
-      }
-      _argIndex++
     }
-    return RxRoom.createFlowable(__db, false, arrayOf("MyEntity"), object : Callable<MyEntity?> {
-      public override fun call(): MyEntity? {
-        val _cursor: Cursor = query(__db, _statement, false, null)
-        try {
-          val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
-          val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_cursor, "other")
-          val _result: MyEntity?
-          if (_cursor.moveToFirst()) {
-            val _tmpPk: Int
-            _tmpPk = _cursor.getInt(_cursorIndexOfPk)
-            val _tmpOther: String
-            _tmpOther = _cursor.getString(_cursorIndexOfOther)
-            _result = MyEntity(_tmpPk,_tmpOther)
-          } else {
-            _result = null
-          }
-          return _result
-        } finally {
-          _cursor.close()
-        }
-      }
-
-      protected fun finalize() {
-        _statement.release()
-      }
-    })
-  }
-
-  public override fun getObservableNullable(vararg arg: String?): Observable<MyEntity?> {
-    val _stringBuilder: StringBuilder = StringBuilder()
-    _stringBuilder.append("SELECT * FROM MyEntity WHERE pk IN (")
-    val _inputSize: Int = arg.size
-    appendPlaceholders(_stringBuilder, _inputSize)
-    _stringBuilder.append(")")
-    val _sql: String = _stringBuilder.toString()
-    val _argCount: Int = 0 + _inputSize
-    val _statement: RoomSQLiteQuery = acquire(_sql, _argCount)
-    var _argIndex: Int = 1
-    for (_item: String? in arg) {
-      if (_item == null) {
-        _statement.bindNull(_argIndex)
-      } else {
-        _statement.bindString(_argIndex, _item)
-      }
-      _argIndex++
-    }
-    return RxRoom.createObservable(__db, false, arrayOf("MyEntity"), object : Callable<MyEntity?> {
-      public override fun call(): MyEntity? {
-        val _cursor: Cursor = query(__db, _statement, false, null)
-        try {
-          val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
-          val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_cursor, "other")
-          val _result: MyEntity?
-          if (_cursor.moveToFirst()) {
-            val _tmpPk: Int
-            _tmpPk = _cursor.getInt(_cursorIndexOfPk)
-            val _tmpOther: String
-            _tmpOther = _cursor.getString(_cursorIndexOfOther)
-            _result = MyEntity(_tmpPk,_tmpOther)
-          } else {
-            _result = null
-          }
-          return _result
-        } finally {
-          _cursor.close()
-        }
-      }
-
-      protected fun finalize() {
-        _statement.release()
-      }
-    })
-  }
-
-  public override fun getSingleNullable(vararg arg: String?): Single<MyEntity?> {
-    val _stringBuilder: StringBuilder = StringBuilder()
-    _stringBuilder.append("SELECT * FROM MyEntity WHERE pk IN (")
-    val _inputSize: Int = arg.size
-    appendPlaceholders(_stringBuilder, _inputSize)
-    _stringBuilder.append(")")
-    val _sql: String = _stringBuilder.toString()
-    val _argCount: Int = 0 + _inputSize
-    val _statement: RoomSQLiteQuery = acquire(_sql, _argCount)
-    var _argIndex: Int = 1
-    for (_item: String? in arg) {
-      if (_item == null) {
-        _statement.bindNull(_argIndex)
-      } else {
-        _statement.bindString(_argIndex, _item)
-      }
-      _argIndex++
-    }
-    return RxRoom.createSingle(object : Callable<MyEntity?> {
-      public override fun call(): MyEntity? {
-        val _cursor: Cursor = query(__db, _statement, false, null)
-        try {
-          val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
-          val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_cursor, "other")
-          val _result: MyEntity?
-          if (_cursor.moveToFirst()) {
-            val _tmpPk: Int
-            _tmpPk = _cursor.getInt(_cursorIndexOfPk)
-            val _tmpOther: String
-            _tmpOther = _cursor.getString(_cursorIndexOfOther)
-            _result = MyEntity(_tmpPk,_tmpOther)
-          } else {
-            _result = null
-          }
-          if (_result == null) {
-            throw EmptyResultSetException("Query returned empty result set: " + _statement.sql)
-          }
-          return _result
-        } finally {
-          _cursor.close()
-        }
-      }
-
-      protected fun finalize() {
-        _statement.release()
-      }
-    })
-  }
-
-  public override fun getMaybeNullable(vararg arg: String?): Maybe<MyEntity?> {
-    val _stringBuilder: StringBuilder = StringBuilder()
-    _stringBuilder.append("SELECT * FROM MyEntity WHERE pk IN (")
-    val _inputSize: Int = arg.size
-    appendPlaceholders(_stringBuilder, _inputSize)
-    _stringBuilder.append(")")
-    val _sql: String = _stringBuilder.toString()
-    val _argCount: Int = 0 + _inputSize
-    val _statement: RoomSQLiteQuery = acquire(_sql, _argCount)
-    var _argIndex: Int = 1
-    for (_item: String? in arg) {
-      if (_item == null) {
-        _statement.bindNull(_argIndex)
-      } else {
-        _statement.bindString(_argIndex, _item)
-      }
-      _argIndex++
-    }
-    return Maybe.fromCallable(object : Callable<MyEntity?> {
-      public override fun call(): MyEntity? {
-        val _cursor: Cursor = query(__db, _statement, false, null)
-        try {
-          val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
-          val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_cursor, "other")
-          val _result: MyEntity?
-          if (_cursor.moveToFirst()) {
-            val _tmpPk: Int
-            _tmpPk = _cursor.getInt(_cursorIndexOfPk)
-            val _tmpOther: String
-            _tmpOther = _cursor.getString(_cursorIndexOfOther)
-            _result = MyEntity(_tmpPk,_tmpOther)
-          } else {
-            _result = null
-          }
-          return _result
-        } finally {
-          _cursor.close()
-        }
-      }
-
-      protected fun finalize() {
-        _statement.release()
-      }
-    })
   }
 
   public companion object {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/callableQuery_rx3.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/callableQuery_rx3.kt
index 491de60..463fa8b 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/callableQuery_rx3.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/callableQuery_rx3.kt
@@ -1,17 +1,15 @@
-import android.database.Cursor
 import androidx.room.RoomDatabase
-import androidx.room.RoomSQLiteQuery
-import androidx.room.RoomSQLiteQuery.Companion.acquire
-import androidx.room.rxjava3.EmptyResultSetException
-import androidx.room.rxjava3.RxRoom
+import androidx.room.rxjava3.createFlowable
+import androidx.room.rxjava3.createMaybe
+import androidx.room.rxjava3.createObservable
+import androidx.room.rxjava3.createSingle
 import androidx.room.util.appendPlaceholders
 import androidx.room.util.getColumnIndexOrThrow
-import androidx.room.util.query
+import androidx.sqlite.SQLiteStatement
 import io.reactivex.rxjava3.core.Flowable
 import io.reactivex.rxjava3.core.Maybe
 import io.reactivex.rxjava3.core.Observable
 import io.reactivex.rxjava3.core.Single
-import java.util.concurrent.Callable
 import javax.`annotation`.processing.Generated
 import kotlin.Int
 import kotlin.String
@@ -37,43 +35,35 @@
     appendPlaceholders(_stringBuilder, _inputSize)
     _stringBuilder.append(")")
     val _sql: String = _stringBuilder.toString()
-    val _argCount: Int = 0 + _inputSize
-    val _statement: RoomSQLiteQuery = acquire(_sql, _argCount)
-    var _argIndex: Int = 1
-    for (_item: String? in arg) {
-      if (_item == null) {
-        _statement.bindNull(_argIndex)
-      } else {
-        _statement.bindString(_argIndex, _item)
-      }
-      _argIndex++
-    }
-    return RxRoom.createFlowable(__db, false, arrayOf("MyEntity"), object : Callable<MyEntity> {
-      public override fun call(): MyEntity {
-        val _cursor: Cursor = query(__db, _statement, false, null)
-        try {
-          val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
-          val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_cursor, "other")
-          val _result: MyEntity
-          if (_cursor.moveToFirst()) {
-            val _tmpPk: Int
-            _tmpPk = _cursor.getInt(_cursorIndexOfPk)
-            val _tmpOther: String
-            _tmpOther = _cursor.getString(_cursorIndexOfOther)
-            _result = MyEntity(_tmpPk,_tmpOther)
+    return createFlowable(__db, false, arrayOf("MyEntity")) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
+      try {
+        var _argIndex: Int = 1
+        for (_item: String? in arg) {
+          if (_item == null) {
+            _stmt.bindNull(_argIndex)
           } else {
-            error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
+            _stmt.bindText(_argIndex, _item)
           }
-          return _result
-        } finally {
-          _cursor.close()
+          _argIndex++
         }
+        val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_stmt, "pk")
+        val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_stmt, "other")
+        val _result: MyEntity
+        if (_stmt.step()) {
+          val _tmpPk: Int
+          _tmpPk = _stmt.getLong(_cursorIndexOfPk).toInt()
+          val _tmpOther: String
+          _tmpOther = _stmt.getText(_cursorIndexOfOther)
+          _result = MyEntity(_tmpPk,_tmpOther)
+        } else {
+          error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
+        }
+        _result
+      } finally {
+        _stmt.close()
       }
-
-      protected fun finalize() {
-        _statement.release()
-      }
-    })
+    }
   }
 
   public override fun getObservable(vararg arg: String?): Observable<MyEntity> {
@@ -83,43 +73,35 @@
     appendPlaceholders(_stringBuilder, _inputSize)
     _stringBuilder.append(")")
     val _sql: String = _stringBuilder.toString()
-    val _argCount: Int = 0 + _inputSize
-    val _statement: RoomSQLiteQuery = acquire(_sql, _argCount)
-    var _argIndex: Int = 1
-    for (_item: String? in arg) {
-      if (_item == null) {
-        _statement.bindNull(_argIndex)
-      } else {
-        _statement.bindString(_argIndex, _item)
-      }
-      _argIndex++
-    }
-    return RxRoom.createObservable(__db, false, arrayOf("MyEntity"), object : Callable<MyEntity> {
-      public override fun call(): MyEntity {
-        val _cursor: Cursor = query(__db, _statement, false, null)
-        try {
-          val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
-          val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_cursor, "other")
-          val _result: MyEntity
-          if (_cursor.moveToFirst()) {
-            val _tmpPk: Int
-            _tmpPk = _cursor.getInt(_cursorIndexOfPk)
-            val _tmpOther: String
-            _tmpOther = _cursor.getString(_cursorIndexOfOther)
-            _result = MyEntity(_tmpPk,_tmpOther)
+    return createObservable(__db, false, arrayOf("MyEntity")) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
+      try {
+        var _argIndex: Int = 1
+        for (_item: String? in arg) {
+          if (_item == null) {
+            _stmt.bindNull(_argIndex)
           } else {
-            error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
+            _stmt.bindText(_argIndex, _item)
           }
-          return _result
-        } finally {
-          _cursor.close()
+          _argIndex++
         }
+        val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_stmt, "pk")
+        val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_stmt, "other")
+        val _result: MyEntity
+        if (_stmt.step()) {
+          val _tmpPk: Int
+          _tmpPk = _stmt.getLong(_cursorIndexOfPk).toInt()
+          val _tmpOther: String
+          _tmpOther = _stmt.getText(_cursorIndexOfOther)
+          _result = MyEntity(_tmpPk,_tmpOther)
+        } else {
+          error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
+        }
+        _result
+      } finally {
+        _stmt.close()
       }
-
-      protected fun finalize() {
-        _statement.release()
-      }
-    })
+    }
   }
 
   public override fun getSingle(vararg arg: String?): Single<MyEntity> {
@@ -129,46 +111,35 @@
     appendPlaceholders(_stringBuilder, _inputSize)
     _stringBuilder.append(")")
     val _sql: String = _stringBuilder.toString()
-    val _argCount: Int = 0 + _inputSize
-    val _statement: RoomSQLiteQuery = acquire(_sql, _argCount)
-    var _argIndex: Int = 1
-    for (_item: String? in arg) {
-      if (_item == null) {
-        _statement.bindNull(_argIndex)
-      } else {
-        _statement.bindString(_argIndex, _item)
-      }
-      _argIndex++
-    }
-    return RxRoom.createSingle(object : Callable<MyEntity?> {
-      public override fun call(): MyEntity? {
-        val _cursor: Cursor = query(__db, _statement, false, null)
-        try {
-          val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
-          val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_cursor, "other")
-          val _result: MyEntity?
-          if (_cursor.moveToFirst()) {
-            val _tmpPk: Int
-            _tmpPk = _cursor.getInt(_cursorIndexOfPk)
-            val _tmpOther: String
-            _tmpOther = _cursor.getString(_cursorIndexOfOther)
-            _result = MyEntity(_tmpPk,_tmpOther)
+    return createSingle(__db, true, false) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
+      try {
+        var _argIndex: Int = 1
+        for (_item: String? in arg) {
+          if (_item == null) {
+            _stmt.bindNull(_argIndex)
           } else {
-            _result = null
+            _stmt.bindText(_argIndex, _item)
           }
-          if (_result == null) {
-            throw EmptyResultSetException("Query returned empty result set: " + _statement.sql)
-          }
-          return _result
-        } finally {
-          _cursor.close()
+          _argIndex++
         }
+        val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_stmt, "pk")
+        val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_stmt, "other")
+        val _result: MyEntity?
+        if (_stmt.step()) {
+          val _tmpPk: Int
+          _tmpPk = _stmt.getLong(_cursorIndexOfPk).toInt()
+          val _tmpOther: String
+          _tmpOther = _stmt.getText(_cursorIndexOfOther)
+          _result = MyEntity(_tmpPk,_tmpOther)
+        } else {
+          _result = null
+        }
+        _result
+      } finally {
+        _stmt.close()
       }
-
-      protected fun finalize() {
-        _statement.release()
-      }
-    })
+    }
   }
 
   public override fun getMaybe(vararg arg: String?): Maybe<MyEntity> {
@@ -178,230 +149,35 @@
     appendPlaceholders(_stringBuilder, _inputSize)
     _stringBuilder.append(")")
     val _sql: String = _stringBuilder.toString()
-    val _argCount: Int = 0 + _inputSize
-    val _statement: RoomSQLiteQuery = acquire(_sql, _argCount)
-    var _argIndex: Int = 1
-    for (_item: String? in arg) {
-      if (_item == null) {
-        _statement.bindNull(_argIndex)
-      } else {
-        _statement.bindString(_argIndex, _item)
-      }
-      _argIndex++
-    }
-    return Maybe.fromCallable(object : Callable<MyEntity?> {
-      public override fun call(): MyEntity? {
-        val _cursor: Cursor = query(__db, _statement, false, null)
-        try {
-          val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
-          val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_cursor, "other")
-          val _result: MyEntity?
-          if (_cursor.moveToFirst()) {
-            val _tmpPk: Int
-            _tmpPk = _cursor.getInt(_cursorIndexOfPk)
-            val _tmpOther: String
-            _tmpOther = _cursor.getString(_cursorIndexOfOther)
-            _result = MyEntity(_tmpPk,_tmpOther)
+    return createMaybe(__db, true, false) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
+      try {
+        var _argIndex: Int = 1
+        for (_item: String? in arg) {
+          if (_item == null) {
+            _stmt.bindNull(_argIndex)
           } else {
-            _result = null
+            _stmt.bindText(_argIndex, _item)
           }
-          return _result
-        } finally {
-          _cursor.close()
+          _argIndex++
         }
+        val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_stmt, "pk")
+        val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_stmt, "other")
+        val _result: MyEntity?
+        if (_stmt.step()) {
+          val _tmpPk: Int
+          _tmpPk = _stmt.getLong(_cursorIndexOfPk).toInt()
+          val _tmpOther: String
+          _tmpOther = _stmt.getText(_cursorIndexOfOther)
+          _result = MyEntity(_tmpPk,_tmpOther)
+        } else {
+          _result = null
+        }
+        _result
+      } finally {
+        _stmt.close()
       }
-
-      protected fun finalize() {
-        _statement.release()
-      }
-    })
-  }
-
-  public override fun getFlowableNullable(vararg arg: String?): Flowable<MyEntity?> {
-    val _stringBuilder: StringBuilder = StringBuilder()
-    _stringBuilder.append("SELECT * FROM MyEntity WHERE pk IN (")
-    val _inputSize: Int = arg.size
-    appendPlaceholders(_stringBuilder, _inputSize)
-    _stringBuilder.append(")")
-    val _sql: String = _stringBuilder.toString()
-    val _argCount: Int = 0 + _inputSize
-    val _statement: RoomSQLiteQuery = acquire(_sql, _argCount)
-    var _argIndex: Int = 1
-    for (_item: String? in arg) {
-      if (_item == null) {
-        _statement.bindNull(_argIndex)
-      } else {
-        _statement.bindString(_argIndex, _item)
-      }
-      _argIndex++
     }
-    return RxRoom.createFlowable(__db, false, arrayOf("MyEntity"), object : Callable<MyEntity?> {
-      public override fun call(): MyEntity? {
-        val _cursor: Cursor = query(__db, _statement, false, null)
-        try {
-          val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
-          val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_cursor, "other")
-          val _result: MyEntity?
-          if (_cursor.moveToFirst()) {
-            val _tmpPk: Int
-            _tmpPk = _cursor.getInt(_cursorIndexOfPk)
-            val _tmpOther: String
-            _tmpOther = _cursor.getString(_cursorIndexOfOther)
-            _result = MyEntity(_tmpPk,_tmpOther)
-          } else {
-            _result = null
-          }
-          return _result
-        } finally {
-          _cursor.close()
-        }
-      }
-
-      protected fun finalize() {
-        _statement.release()
-      }
-    })
-  }
-
-  public override fun getObservableNullable(vararg arg: String?): Observable<MyEntity?> {
-    val _stringBuilder: StringBuilder = StringBuilder()
-    _stringBuilder.append("SELECT * FROM MyEntity WHERE pk IN (")
-    val _inputSize: Int = arg.size
-    appendPlaceholders(_stringBuilder, _inputSize)
-    _stringBuilder.append(")")
-    val _sql: String = _stringBuilder.toString()
-    val _argCount: Int = 0 + _inputSize
-    val _statement: RoomSQLiteQuery = acquire(_sql, _argCount)
-    var _argIndex: Int = 1
-    for (_item: String? in arg) {
-      if (_item == null) {
-        _statement.bindNull(_argIndex)
-      } else {
-        _statement.bindString(_argIndex, _item)
-      }
-      _argIndex++
-    }
-    return RxRoom.createObservable(__db, false, arrayOf("MyEntity"), object : Callable<MyEntity?> {
-      public override fun call(): MyEntity? {
-        val _cursor: Cursor = query(__db, _statement, false, null)
-        try {
-          val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
-          val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_cursor, "other")
-          val _result: MyEntity?
-          if (_cursor.moveToFirst()) {
-            val _tmpPk: Int
-            _tmpPk = _cursor.getInt(_cursorIndexOfPk)
-            val _tmpOther: String
-            _tmpOther = _cursor.getString(_cursorIndexOfOther)
-            _result = MyEntity(_tmpPk,_tmpOther)
-          } else {
-            _result = null
-          }
-          return _result
-        } finally {
-          _cursor.close()
-        }
-      }
-
-      protected fun finalize() {
-        _statement.release()
-      }
-    })
-  }
-
-  public override fun getSingleNullable(vararg arg: String?): Single<MyEntity?> {
-    val _stringBuilder: StringBuilder = StringBuilder()
-    _stringBuilder.append("SELECT * FROM MyEntity WHERE pk IN (")
-    val _inputSize: Int = arg.size
-    appendPlaceholders(_stringBuilder, _inputSize)
-    _stringBuilder.append(")")
-    val _sql: String = _stringBuilder.toString()
-    val _argCount: Int = 0 + _inputSize
-    val _statement: RoomSQLiteQuery = acquire(_sql, _argCount)
-    var _argIndex: Int = 1
-    for (_item: String? in arg) {
-      if (_item == null) {
-        _statement.bindNull(_argIndex)
-      } else {
-        _statement.bindString(_argIndex, _item)
-      }
-      _argIndex++
-    }
-    return RxRoom.createSingle(object : Callable<MyEntity?> {
-      public override fun call(): MyEntity? {
-        val _cursor: Cursor = query(__db, _statement, false, null)
-        try {
-          val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
-          val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_cursor, "other")
-          val _result: MyEntity?
-          if (_cursor.moveToFirst()) {
-            val _tmpPk: Int
-            _tmpPk = _cursor.getInt(_cursorIndexOfPk)
-            val _tmpOther: String
-            _tmpOther = _cursor.getString(_cursorIndexOfOther)
-            _result = MyEntity(_tmpPk,_tmpOther)
-          } else {
-            _result = null
-          }
-          if (_result == null) {
-            throw EmptyResultSetException("Query returned empty result set: " + _statement.sql)
-          }
-          return _result
-        } finally {
-          _cursor.close()
-        }
-      }
-
-      protected fun finalize() {
-        _statement.release()
-      }
-    })
-  }
-
-  public override fun getMaybeNullable(vararg arg: String?): Maybe<MyEntity?> {
-    val _stringBuilder: StringBuilder = StringBuilder()
-    _stringBuilder.append("SELECT * FROM MyEntity WHERE pk IN (")
-    val _inputSize: Int = arg.size
-    appendPlaceholders(_stringBuilder, _inputSize)
-    _stringBuilder.append(")")
-    val _sql: String = _stringBuilder.toString()
-    val _argCount: Int = 0 + _inputSize
-    val _statement: RoomSQLiteQuery = acquire(_sql, _argCount)
-    var _argIndex: Int = 1
-    for (_item: String? in arg) {
-      if (_item == null) {
-        _statement.bindNull(_argIndex)
-      } else {
-        _statement.bindString(_argIndex, _item)
-      }
-      _argIndex++
-    }
-    return Maybe.fromCallable(object : Callable<MyEntity?> {
-      public override fun call(): MyEntity? {
-        val _cursor: Cursor = query(__db, _statement, false, null)
-        try {
-          val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
-          val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_cursor, "other")
-          val _result: MyEntity?
-          if (_cursor.moveToFirst()) {
-            val _tmpPk: Int
-            _tmpPk = _cursor.getInt(_cursorIndexOfPk)
-            val _tmpOther: String
-            _tmpOther = _cursor.getString(_cursorIndexOfOther)
-            _result = MyEntity(_tmpPk,_tmpOther)
-          } else {
-            _result = null
-          }
-          return _result
-        } finally {
-          _cursor.close()
-        }
-      }
-
-      protected fun finalize() {
-        _statement.release()
-      }
-    })
   }
 
   public companion object {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/preparedCallableQuery_rx2.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/preparedCallableQuery_rx2.kt
index e3f48cc..fbc5b8f2 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/preparedCallableQuery_rx2.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/preparedCallableQuery_rx2.kt
@@ -1,10 +1,12 @@
 import androidx.room.RoomDatabase
-import androidx.sqlite.db.SupportSQLiteStatement
+import androidx.room.RxRoom.Companion.createCompletable
+import androidx.room.RxRoom.Companion.createMaybe
+import androidx.room.RxRoom.Companion.createSingle
+import androidx.room.util.getLastInsertedRowId
+import androidx.sqlite.SQLiteStatement
 import io.reactivex.Completable
 import io.reactivex.Maybe
 import io.reactivex.Single
-import java.lang.Void
-import java.util.concurrent.Callable
 import javax.`annotation`.processing.Generated
 import kotlin.Int
 import kotlin.Long
@@ -23,65 +25,55 @@
     this.__db = __db
   }
 
-  public override fun insertPublisherSingle(id: String, name: String): Single<Long> =
-      Single.fromCallable(object : Callable<Long?> {
-    public override fun call(): Long? {
-      val _sql: String = "INSERT INTO MyEntity (pk, other) VALUES (?, ?)"
-      val _stmt: SupportSQLiteStatement = __db.compileStatement(_sql)
-      var _argIndex: Int = 1
-      _stmt.bindString(_argIndex, id)
-      _argIndex = 2
-      _stmt.bindString(_argIndex, name)
-      __db.beginTransaction()
+  public override fun insertPublisherSingle(id: String, name: String): Single<Long> {
+    val _sql: String = "INSERT INTO MyEntity (pk, other) VALUES (?, ?)"
+    return createSingle(__db, false, true) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
       try {
-        val _result: Long? = _stmt.executeInsert()
-        __db.setTransactionSuccessful()
-        return _result
+        var _argIndex: Int = 1
+        _stmt.bindText(_argIndex, id)
+        _argIndex = 2
+        _stmt.bindText(_argIndex, name)
+        _stmt.step()
+        getLastInsertedRowId(_connection)
       } finally {
-        __db.endTransaction()
+        _stmt.close()
       }
     }
-  })
+  }
 
-  public override fun insertPublisherMaybe(id: String, name: String): Maybe<Long> =
-      Maybe.fromCallable(object : Callable<Long?> {
-    public override fun call(): Long? {
-      val _sql: String = "INSERT INTO MyEntity (pk, other) VALUES (?, ?)"
-      val _stmt: SupportSQLiteStatement = __db.compileStatement(_sql)
-      var _argIndex: Int = 1
-      _stmt.bindString(_argIndex, id)
-      _argIndex = 2
-      _stmt.bindString(_argIndex, name)
-      __db.beginTransaction()
+  public override fun insertPublisherMaybe(id: String, name: String): Maybe<Long> {
+    val _sql: String = "INSERT INTO MyEntity (pk, other) VALUES (?, ?)"
+    return createMaybe(__db, false, true) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
       try {
-        val _result: Long? = _stmt.executeInsert()
-        __db.setTransactionSuccessful()
-        return _result
+        var _argIndex: Int = 1
+        _stmt.bindText(_argIndex, id)
+        _argIndex = 2
+        _stmt.bindText(_argIndex, name)
+        _stmt.step()
+        getLastInsertedRowId(_connection)
       } finally {
-        __db.endTransaction()
+        _stmt.close()
       }
     }
-  })
+  }
 
-  public override fun insertPublisherCompletable(id: String, name: String): Completable =
-      Completable.fromCallable(object : Callable<Void?> {
-    public override fun call(): Void? {
-      val _sql: String = "INSERT INTO MyEntity (pk, other) VALUES (?, ?)"
-      val _stmt: SupportSQLiteStatement = __db.compileStatement(_sql)
-      var _argIndex: Int = 1
-      _stmt.bindString(_argIndex, id)
-      _argIndex = 2
-      _stmt.bindString(_argIndex, name)
-      __db.beginTransaction()
+  public override fun insertPublisherCompletable(id: String, name: String): Completable {
+    val _sql: String = "INSERT INTO MyEntity (pk, other) VALUES (?, ?)"
+    return createCompletable(__db, false, true) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
       try {
-        _stmt.executeInsert()
-        __db.setTransactionSuccessful()
-        return null
+        var _argIndex: Int = 1
+        _stmt.bindText(_argIndex, id)
+        _argIndex = 2
+        _stmt.bindText(_argIndex, name)
+        _stmt.step()
       } finally {
-        __db.endTransaction()
+        _stmt.close()
       }
     }
-  })
+  }
 
   public companion object {
     public fun getRequiredConverters(): List<KClass<*>> = emptyList()
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/preparedCallableQuery_rx3.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/preparedCallableQuery_rx3.kt
index 85d36b7..91a3312 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/preparedCallableQuery_rx3.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/preparedCallableQuery_rx3.kt
@@ -1,10 +1,12 @@
 import androidx.room.RoomDatabase
-import androidx.sqlite.db.SupportSQLiteStatement
+import androidx.room.rxjava3.createCompletable
+import androidx.room.rxjava3.createMaybe
+import androidx.room.rxjava3.createSingle
+import androidx.room.util.getLastInsertedRowId
+import androidx.sqlite.SQLiteStatement
 import io.reactivex.rxjava3.core.Completable
 import io.reactivex.rxjava3.core.Maybe
 import io.reactivex.rxjava3.core.Single
-import java.lang.Void
-import java.util.concurrent.Callable
 import javax.`annotation`.processing.Generated
 import kotlin.Int
 import kotlin.Long
@@ -23,65 +25,55 @@
     this.__db = __db
   }
 
-  public override fun insertPublisherSingle(id: String, name: String): Single<Long> =
-      Single.fromCallable(object : Callable<Long?> {
-    public override fun call(): Long? {
-      val _sql: String = "INSERT INTO MyEntity (pk, other) VALUES (?, ?)"
-      val _stmt: SupportSQLiteStatement = __db.compileStatement(_sql)
-      var _argIndex: Int = 1
-      _stmt.bindString(_argIndex, id)
-      _argIndex = 2
-      _stmt.bindString(_argIndex, name)
-      __db.beginTransaction()
+  public override fun insertPublisherSingle(id: String, name: String): Single<Long> {
+    val _sql: String = "INSERT INTO MyEntity (pk, other) VALUES (?, ?)"
+    return createSingle(__db, false, true) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
       try {
-        val _result: Long? = _stmt.executeInsert()
-        __db.setTransactionSuccessful()
-        return _result
+        var _argIndex: Int = 1
+        _stmt.bindText(_argIndex, id)
+        _argIndex = 2
+        _stmt.bindText(_argIndex, name)
+        _stmt.step()
+        getLastInsertedRowId(_connection)
       } finally {
-        __db.endTransaction()
+        _stmt.close()
       }
     }
-  })
+  }
 
-  public override fun insertPublisherMaybe(id: String, name: String): Maybe<Long> =
-      Maybe.fromCallable(object : Callable<Long?> {
-    public override fun call(): Long? {
-      val _sql: String = "INSERT INTO MyEntity (pk, other) VALUES (?, ?)"
-      val _stmt: SupportSQLiteStatement = __db.compileStatement(_sql)
-      var _argIndex: Int = 1
-      _stmt.bindString(_argIndex, id)
-      _argIndex = 2
-      _stmt.bindString(_argIndex, name)
-      __db.beginTransaction()
+  public override fun insertPublisherMaybe(id: String, name: String): Maybe<Long> {
+    val _sql: String = "INSERT INTO MyEntity (pk, other) VALUES (?, ?)"
+    return createMaybe(__db, false, true) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
       try {
-        val _result: Long? = _stmt.executeInsert()
-        __db.setTransactionSuccessful()
-        return _result
+        var _argIndex: Int = 1
+        _stmt.bindText(_argIndex, id)
+        _argIndex = 2
+        _stmt.bindText(_argIndex, name)
+        _stmt.step()
+        getLastInsertedRowId(_connection)
       } finally {
-        __db.endTransaction()
+        _stmt.close()
       }
     }
-  })
+  }
 
-  public override fun insertPublisherCompletable(id: String, name: String): Completable =
-      Completable.fromCallable(object : Callable<Void?> {
-    public override fun call(): Void? {
-      val _sql: String = "INSERT INTO MyEntity (pk, other) VALUES (?, ?)"
-      val _stmt: SupportSQLiteStatement = __db.compileStatement(_sql)
-      var _argIndex: Int = 1
-      _stmt.bindString(_argIndex, id)
-      _argIndex = 2
-      _stmt.bindString(_argIndex, name)
-      __db.beginTransaction()
+  public override fun insertPublisherCompletable(id: String, name: String): Completable {
+    val _sql: String = "INSERT INTO MyEntity (pk, other) VALUES (?, ?)"
+    return createCompletable(__db, false, true) { _connection ->
+      val _stmt: SQLiteStatement = _connection.prepare(_sql)
       try {
-        _stmt.executeInsert()
-        __db.setTransactionSuccessful()
-        return null
+        var _argIndex: Int = 1
+        _stmt.bindText(_argIndex, id)
+        _argIndex = 2
+        _stmt.bindText(_argIndex, name)
+        _stmt.step()
       } finally {
-        __db.endTransaction()
+        _stmt.close()
       }
     }
-  })
+  }
 
   public companion object {
     public fun getRequiredConverters(): List<KClass<*>> = emptyList()
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_rx2.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_rx2.kt
index 57b5fce..34749e2 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_rx2.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_rx2.kt
@@ -1,12 +1,12 @@
-import androidx.room.EntityDeletionOrUpdateAdapter
-import androidx.room.EntityInsertionAdapter
-import androidx.room.EntityUpsertionAdapter
+import androidx.room.EntityDeleteOrUpdateAdapter
+import androidx.room.EntityInsertAdapter
+import androidx.room.EntityUpsertAdapter
 import androidx.room.RoomDatabase
-import androidx.sqlite.db.SupportSQLiteStatement
+import androidx.room.RxRoom.Companion.createCompletable
+import androidx.room.RxRoom.Companion.createSingle
+import androidx.sqlite.SQLiteStatement
 import io.reactivex.Completable
 import io.reactivex.Single
-import java.lang.Void
-import java.util.concurrent.Callable
 import javax.`annotation`.processing.Generated
 import kotlin.Int
 import kotlin.Long
@@ -22,175 +22,109 @@
 ) : MyDao {
   private val __db: RoomDatabase
 
-  private val __insertionAdapterOfMyEntity: EntityInsertionAdapter<MyEntity>
+  private val __insertAdapterOfMyEntity: EntityInsertAdapter<MyEntity>
 
-  private val __deleteCompatAdapterOfMyEntity: EntityDeletionOrUpdateAdapter<MyEntity>
+  private val __deleteAdapterOfMyEntity: EntityDeleteOrUpdateAdapter<MyEntity>
 
-  private val __updateCompatAdapterOfMyEntity: EntityDeletionOrUpdateAdapter<MyEntity>
+  private val __updateAdapterOfMyEntity: EntityDeleteOrUpdateAdapter<MyEntity>
 
-  private val __upsertionAdapterOfMyEntity: EntityUpsertionAdapter<MyEntity>
+  private val __upsertAdapterOfMyEntity: EntityUpsertAdapter<MyEntity>
   init {
     this.__db = __db
-    this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
+    this.__insertAdapterOfMyEntity = object : EntityInsertAdapter<MyEntity>() {
       protected override fun createQuery(): String =
           "INSERT OR ABORT INTO `MyEntity` (`pk`,`other`) VALUES (?,?)"
 
-      protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+      protected override fun bind(statement: SQLiteStatement, entity: MyEntity) {
         statement.bindLong(1, entity.pk.toLong())
-        statement.bindString(2, entity.other)
+        statement.bindText(2, entity.other)
       }
     }
-    this.__deleteCompatAdapterOfMyEntity = object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
+    this.__deleteAdapterOfMyEntity = object : EntityDeleteOrUpdateAdapter<MyEntity>() {
       protected override fun createQuery(): String = "DELETE FROM `MyEntity` WHERE `pk` = ?"
 
-      protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+      protected override fun bind(statement: SQLiteStatement, entity: MyEntity) {
         statement.bindLong(1, entity.pk.toLong())
       }
     }
-    this.__updateCompatAdapterOfMyEntity = object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
+    this.__updateAdapterOfMyEntity = object : EntityDeleteOrUpdateAdapter<MyEntity>() {
       protected override fun createQuery(): String =
           "UPDATE OR ABORT `MyEntity` SET `pk` = ?,`other` = ? WHERE `pk` = ?"
 
-      protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+      protected override fun bind(statement: SQLiteStatement, entity: MyEntity) {
         statement.bindLong(1, entity.pk.toLong())
-        statement.bindString(2, entity.other)
+        statement.bindText(2, entity.other)
         statement.bindLong(3, entity.pk.toLong())
       }
     }
-    this.__upsertionAdapterOfMyEntity = EntityUpsertionAdapter<MyEntity>(object :
-        EntityInsertionAdapter<MyEntity>(__db) {
+    this.__upsertAdapterOfMyEntity = EntityUpsertAdapter<MyEntity>(object :
+        EntityInsertAdapter<MyEntity>() {
       protected override fun createQuery(): String =
           "INSERT INTO `MyEntity` (`pk`,`other`) VALUES (?,?)"
 
-      protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+      protected override fun bind(statement: SQLiteStatement, entity: MyEntity) {
         statement.bindLong(1, entity.pk.toLong())
-        statement.bindString(2, entity.other)
+        statement.bindText(2, entity.other)
       }
-    }, object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
+    }, object : EntityDeleteOrUpdateAdapter<MyEntity>() {
       protected override fun createQuery(): String =
           "UPDATE `MyEntity` SET `pk` = ?,`other` = ? WHERE `pk` = ?"
 
-      protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+      protected override fun bind(statement: SQLiteStatement, entity: MyEntity) {
         statement.bindLong(1, entity.pk.toLong())
-        statement.bindString(2, entity.other)
+        statement.bindText(2, entity.other)
         statement.bindLong(3, entity.pk.toLong())
       }
     })
   }
 
   public override fun insertSingle(vararg entities: MyEntity): Single<List<Long>> =
-      Single.fromCallable(object : Callable<List<Long>?> {
-    public override fun call(): List<Long>? {
-      __db.beginTransaction()
-      try {
-        val _result: List<Long>? = __insertionAdapterOfMyEntity.insertAndReturnIdsList(entities)
-        __db.setTransactionSuccessful()
-        return _result
-      } finally {
-        __db.endTransaction()
-      }
-    }
-  })
+      createSingle(__db, false, true) { _connection ->
+    val _result: List<Long>? = __insertAdapterOfMyEntity.insertAndReturnIdsList(_connection,
+        entities)
+    _result
+  }
 
   public override fun insertCompletable(vararg entities: MyEntity): Completable =
-      Completable.fromCallable(object : Callable<Void?> {
-    public override fun call(): Void? {
-      __db.beginTransaction()
-      try {
-        __insertionAdapterOfMyEntity.insert(entities)
-        __db.setTransactionSuccessful()
-        return null
-      } finally {
-        __db.endTransaction()
-      }
-    }
-  })
+      createCompletable(__db, false, true) { _connection ->
+    __insertAdapterOfMyEntity.insert(_connection, entities)
+  }
 
-  public override fun deleteSingle(entity: MyEntity): Single<Int> = Single.fromCallable(object :
-      Callable<Int?> {
-    public override fun call(): Int? {
-      var _total: Int = 0
-      __db.beginTransaction()
-      try {
-        _total += __deleteCompatAdapterOfMyEntity.handle(entity)
-        __db.setTransactionSuccessful()
-        return _total
-      } finally {
-        __db.endTransaction()
-      }
-    }
-  })
+  public override fun deleteSingle(entity: MyEntity): Single<Int> = createSingle(__db, false, true)
+      { _connection ->
+    var _result: Int = 0
+    _result += __deleteAdapterOfMyEntity.handle(_connection, entity)
+    _result
+  }
 
-  public override fun deleteCompletable(entity: MyEntity): Completable =
-      Completable.fromCallable(object : Callable<Void?> {
-    public override fun call(): Void? {
-      __db.beginTransaction()
-      try {
-        __deleteCompatAdapterOfMyEntity.handle(entity)
-        __db.setTransactionSuccessful()
-        return null
-      } finally {
-        __db.endTransaction()
-      }
-    }
-  })
+  public override fun deleteCompletable(entity: MyEntity): Completable = createCompletable(__db,
+      false, true) { _connection ->
+    __deleteAdapterOfMyEntity.handle(_connection, entity)
+  }
 
-  public override fun updateSingle(entity: MyEntity): Single<Int> = Single.fromCallable(object :
-      Callable<Int?> {
-    public override fun call(): Int? {
-      var _total: Int = 0
-      __db.beginTransaction()
-      try {
-        _total += __updateCompatAdapterOfMyEntity.handle(entity)
-        __db.setTransactionSuccessful()
-        return _total
-      } finally {
-        __db.endTransaction()
-      }
-    }
-  })
+  public override fun updateSingle(entity: MyEntity): Single<Int> = createSingle(__db, false, true)
+      { _connection ->
+    var _result: Int = 0
+    _result += __updateAdapterOfMyEntity.handle(_connection, entity)
+    _result
+  }
 
-  public override fun updateCompletable(entity: MyEntity): Completable =
-      Completable.fromCallable(object : Callable<Void?> {
-    public override fun call(): Void? {
-      __db.beginTransaction()
-      try {
-        __updateCompatAdapterOfMyEntity.handle(entity)
-        __db.setTransactionSuccessful()
-        return null
-      } finally {
-        __db.endTransaction()
-      }
-    }
-  })
+  public override fun updateCompletable(entity: MyEntity): Completable = createCompletable(__db,
+      false, true) { _connection ->
+    __updateAdapterOfMyEntity.handle(_connection, entity)
+  }
 
   public override fun upsertSingle(vararg entities: MyEntity): Single<List<Long>> =
-      Single.fromCallable(object : Callable<List<Long>?> {
-    public override fun call(): List<Long>? {
-      __db.beginTransaction()
-      try {
-        val _result: List<Long>? = __upsertionAdapterOfMyEntity.upsertAndReturnIdsList(entities)
-        __db.setTransactionSuccessful()
-        return _result
-      } finally {
-        __db.endTransaction()
-      }
-    }
-  })
+      createSingle(__db, false, true) { _connection ->
+    val _result: List<Long>? = __upsertAdapterOfMyEntity.upsertAndReturnIdsList(_connection,
+        entities)
+    _result
+  }
 
   public override fun upsertCompletable(vararg entities: MyEntity): Completable =
-      Completable.fromCallable(object : Callable<Void?> {
-    public override fun call(): Void? {
-      __db.beginTransaction()
-      try {
-        __upsertionAdapterOfMyEntity.upsert(entities)
-        __db.setTransactionSuccessful()
-        return null
-      } finally {
-        __db.endTransaction()
-      }
-    }
-  })
+      createCompletable(__db, false, true) { _connection ->
+    __upsertAdapterOfMyEntity.upsert(_connection, entities)
+  }
 
   public companion object {
     public fun getRequiredConverters(): List<KClass<*>> = emptyList()
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_rx3.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_rx3.kt
index e64428f..6867046 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_rx3.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_rx3.kt
@@ -1,12 +1,12 @@
-import androidx.room.EntityDeletionOrUpdateAdapter
-import androidx.room.EntityInsertionAdapter
-import androidx.room.EntityUpsertionAdapter
+import androidx.room.EntityDeleteOrUpdateAdapter
+import androidx.room.EntityInsertAdapter
+import androidx.room.EntityUpsertAdapter
 import androidx.room.RoomDatabase
-import androidx.sqlite.db.SupportSQLiteStatement
+import androidx.room.rxjava3.createCompletable
+import androidx.room.rxjava3.createSingle
+import androidx.sqlite.SQLiteStatement
 import io.reactivex.rxjava3.core.Completable
 import io.reactivex.rxjava3.core.Single
-import java.lang.Void
-import java.util.concurrent.Callable
 import javax.`annotation`.processing.Generated
 import kotlin.Int
 import kotlin.Long
@@ -22,175 +22,109 @@
 ) : MyDao {
   private val __db: RoomDatabase
 
-  private val __insertionAdapterOfMyEntity: EntityInsertionAdapter<MyEntity>
+  private val __insertAdapterOfMyEntity: EntityInsertAdapter<MyEntity>
 
-  private val __deleteCompatAdapterOfMyEntity: EntityDeletionOrUpdateAdapter<MyEntity>
+  private val __deleteAdapterOfMyEntity: EntityDeleteOrUpdateAdapter<MyEntity>
 
-  private val __updateCompatAdapterOfMyEntity: EntityDeletionOrUpdateAdapter<MyEntity>
+  private val __updateAdapterOfMyEntity: EntityDeleteOrUpdateAdapter<MyEntity>
 
-  private val __upsertionAdapterOfMyEntity: EntityUpsertionAdapter<MyEntity>
+  private val __upsertAdapterOfMyEntity: EntityUpsertAdapter<MyEntity>
   init {
     this.__db = __db
-    this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
+    this.__insertAdapterOfMyEntity = object : EntityInsertAdapter<MyEntity>() {
       protected override fun createQuery(): String =
           "INSERT OR ABORT INTO `MyEntity` (`pk`,`other`) VALUES (?,?)"
 
-      protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+      protected override fun bind(statement: SQLiteStatement, entity: MyEntity) {
         statement.bindLong(1, entity.pk.toLong())
-        statement.bindString(2, entity.other)
+        statement.bindText(2, entity.other)
       }
     }
-    this.__deleteCompatAdapterOfMyEntity = object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
+    this.__deleteAdapterOfMyEntity = object : EntityDeleteOrUpdateAdapter<MyEntity>() {
       protected override fun createQuery(): String = "DELETE FROM `MyEntity` WHERE `pk` = ?"
 
-      protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+      protected override fun bind(statement: SQLiteStatement, entity: MyEntity) {
         statement.bindLong(1, entity.pk.toLong())
       }
     }
-    this.__updateCompatAdapterOfMyEntity = object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
+    this.__updateAdapterOfMyEntity = object : EntityDeleteOrUpdateAdapter<MyEntity>() {
       protected override fun createQuery(): String =
           "UPDATE OR ABORT `MyEntity` SET `pk` = ?,`other` = ? WHERE `pk` = ?"
 
-      protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+      protected override fun bind(statement: SQLiteStatement, entity: MyEntity) {
         statement.bindLong(1, entity.pk.toLong())
-        statement.bindString(2, entity.other)
+        statement.bindText(2, entity.other)
         statement.bindLong(3, entity.pk.toLong())
       }
     }
-    this.__upsertionAdapterOfMyEntity = EntityUpsertionAdapter<MyEntity>(object :
-        EntityInsertionAdapter<MyEntity>(__db) {
+    this.__upsertAdapterOfMyEntity = EntityUpsertAdapter<MyEntity>(object :
+        EntityInsertAdapter<MyEntity>() {
       protected override fun createQuery(): String =
           "INSERT INTO `MyEntity` (`pk`,`other`) VALUES (?,?)"
 
-      protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+      protected override fun bind(statement: SQLiteStatement, entity: MyEntity) {
         statement.bindLong(1, entity.pk.toLong())
-        statement.bindString(2, entity.other)
+        statement.bindText(2, entity.other)
       }
-    }, object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
+    }, object : EntityDeleteOrUpdateAdapter<MyEntity>() {
       protected override fun createQuery(): String =
           "UPDATE `MyEntity` SET `pk` = ?,`other` = ? WHERE `pk` = ?"
 
-      protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+      protected override fun bind(statement: SQLiteStatement, entity: MyEntity) {
         statement.bindLong(1, entity.pk.toLong())
-        statement.bindString(2, entity.other)
+        statement.bindText(2, entity.other)
         statement.bindLong(3, entity.pk.toLong())
       }
     })
   }
 
   public override fun insertSingle(vararg entities: MyEntity): Single<List<Long>> =
-      Single.fromCallable(object : Callable<List<Long>?> {
-    public override fun call(): List<Long>? {
-      __db.beginTransaction()
-      try {
-        val _result: List<Long>? = __insertionAdapterOfMyEntity.insertAndReturnIdsList(entities)
-        __db.setTransactionSuccessful()
-        return _result
-      } finally {
-        __db.endTransaction()
-      }
-    }
-  })
+      createSingle(__db, false, true) { _connection ->
+    val _result: List<Long>? = __insertAdapterOfMyEntity.insertAndReturnIdsList(_connection,
+        entities)
+    _result
+  }
 
   public override fun insertCompletable(vararg entities: MyEntity): Completable =
-      Completable.fromCallable(object : Callable<Void?> {
-    public override fun call(): Void? {
-      __db.beginTransaction()
-      try {
-        __insertionAdapterOfMyEntity.insert(entities)
-        __db.setTransactionSuccessful()
-        return null
-      } finally {
-        __db.endTransaction()
-      }
-    }
-  })
+      createCompletable(__db, false, true) { _connection ->
+    __insertAdapterOfMyEntity.insert(_connection, entities)
+  }
 
-  public override fun deleteSingle(entity: MyEntity): Single<Int> = Single.fromCallable(object :
-      Callable<Int?> {
-    public override fun call(): Int? {
-      var _total: Int = 0
-      __db.beginTransaction()
-      try {
-        _total += __deleteCompatAdapterOfMyEntity.handle(entity)
-        __db.setTransactionSuccessful()
-        return _total
-      } finally {
-        __db.endTransaction()
-      }
-    }
-  })
+  public override fun deleteSingle(entity: MyEntity): Single<Int> = createSingle(__db, false, true)
+      { _connection ->
+    var _result: Int = 0
+    _result += __deleteAdapterOfMyEntity.handle(_connection, entity)
+    _result
+  }
 
-  public override fun deleteCompletable(entity: MyEntity): Completable =
-      Completable.fromCallable(object : Callable<Void?> {
-    public override fun call(): Void? {
-      __db.beginTransaction()
-      try {
-        __deleteCompatAdapterOfMyEntity.handle(entity)
-        __db.setTransactionSuccessful()
-        return null
-      } finally {
-        __db.endTransaction()
-      }
-    }
-  })
+  public override fun deleteCompletable(entity: MyEntity): Completable = createCompletable(__db,
+      false, true) { _connection ->
+    __deleteAdapterOfMyEntity.handle(_connection, entity)
+  }
 
-  public override fun updateSingle(entity: MyEntity): Single<Int> = Single.fromCallable(object :
-      Callable<Int?> {
-    public override fun call(): Int? {
-      var _total: Int = 0
-      __db.beginTransaction()
-      try {
-        _total += __updateCompatAdapterOfMyEntity.handle(entity)
-        __db.setTransactionSuccessful()
-        return _total
-      } finally {
-        __db.endTransaction()
-      }
-    }
-  })
+  public override fun updateSingle(entity: MyEntity): Single<Int> = createSingle(__db, false, true)
+      { _connection ->
+    var _result: Int = 0
+    _result += __updateAdapterOfMyEntity.handle(_connection, entity)
+    _result
+  }
 
-  public override fun updateCompletable(entity: MyEntity): Completable =
-      Completable.fromCallable(object : Callable<Void?> {
-    public override fun call(): Void? {
-      __db.beginTransaction()
-      try {
-        __updateCompatAdapterOfMyEntity.handle(entity)
-        __db.setTransactionSuccessful()
-        return null
-      } finally {
-        __db.endTransaction()
-      }
-    }
-  })
+  public override fun updateCompletable(entity: MyEntity): Completable = createCompletable(__db,
+      false, true) { _connection ->
+    __updateAdapterOfMyEntity.handle(_connection, entity)
+  }
 
   public override fun upsertSingle(vararg entities: MyEntity): Single<List<Long>> =
-      Single.fromCallable(object : Callable<List<Long>?> {
-    public override fun call(): List<Long>? {
-      __db.beginTransaction()
-      try {
-        val _result: List<Long>? = __upsertionAdapterOfMyEntity.upsertAndReturnIdsList(entities)
-        __db.setTransactionSuccessful()
-        return _result
-      } finally {
-        __db.endTransaction()
-      }
-    }
-  })
+      createSingle(__db, false, true) { _connection ->
+    val _result: List<Long>? = __upsertAdapterOfMyEntity.upsertAndReturnIdsList(_connection,
+        entities)
+    _result
+  }
 
   public override fun upsertCompletable(vararg entities: MyEntity): Completable =
-      Completable.fromCallable(object : Callable<Void?> {
-    public override fun call(): Void? {
-      __db.beginTransaction()
-      try {
-        __upsertionAdapterOfMyEntity.upsert(entities)
-        __db.setTransactionSuccessful()
-        return null
-      } finally {
-        __db.endTransaction()
-      }
-    }
-  })
+      createCompletable(__db, false, true) { _connection ->
+    __upsertAdapterOfMyEntity.upsert(_connection, entities)
+  }
 
   public companion object {
     public fun getRequiredConverters(): List<KClass<*>> = emptyList()
diff --git a/room/room-gradle-plugin/src/test/test-data/multiplatform-project/src/jvmMain/kotlin/room/testapp/MyDatabase.kt b/room/room-gradle-plugin/src/test/test-data/multiplatform-project/src/jvmMain/kotlin/room/testapp/MyDatabase.kt
index 3690e65..c31091d 100644
--- a/room/room-gradle-plugin/src/test/test-data/multiplatform-project/src/jvmMain/kotlin/room/testapp/MyDatabase.kt
+++ b/room/room-gradle-plugin/src/test/test-data/multiplatform-project/src/jvmMain/kotlin/room/testapp/MyDatabase.kt
@@ -19,13 +19,10 @@
 import androidx.room.*
 
 @Database(entities = [JvmEntity::class], version = 1)
-@ConstructedBy(MyDatabaseCtor::class)
 abstract class MyDatabase : RoomDatabase() {
     abstract fun getMyDao(): MyDao
 }
 
-expect object MyDatabaseCtor : RoomDatabaseConstructor<MyDatabase>
-
 @Entity
 data class JvmEntity(
     @PrimaryKey val id: Long
diff --git a/room/room-paging-rxjava2/src/main/java/androidx/room/paging/rxjava2/LimitOffsetRxPagingSource.kt b/room/room-paging-rxjava2/src/main/java/androidx/room/paging/rxjava2/LimitOffsetRxPagingSource.kt
index db0f5ae..7bd8f30 100644
--- a/room/room-paging-rxjava2/src/main/java/androidx/room/paging/rxjava2/LimitOffsetRxPagingSource.kt
+++ b/room/room-paging-rxjava2/src/main/java/androidx/room/paging/rxjava2/LimitOffsetRxPagingSource.kt
@@ -24,7 +24,7 @@
 import androidx.paging.rxjava2.RxPagingSource
 import androidx.room.RoomDatabase
 import androidx.room.RoomSQLiteQuery
-import androidx.room.RxRoom.createSingle
+import androidx.room.RxRoom
 import androidx.room.paging.util.INITIAL_ITEM_COUNT
 import androidx.room.paging.util.INVALID
 import androidx.room.paging.util.ThreadSafeInvalidationObserver
@@ -56,7 +56,7 @@
 
     override fun loadSingle(params: LoadParams<Int>): Single<LoadResult<Int, Value>> {
         val scheduler = Schedulers.from(db.queryExecutor)
-        return createSingle {
+        return RxRoom.createSingle {
                 observer.registerIfNecessary(db)
                 val tempCount = itemCount.get()
                 if (tempCount == INITIAL_ITEM_COUNT) {
diff --git a/room/room-paging-rxjava3/src/main/java/androidx/room/paging/rxjava3/LimitOffsetRxPagingSource.kt b/room/room-paging-rxjava3/src/main/java/androidx/room/paging/rxjava3/LimitOffsetRxPagingSource.kt
index 6067c44..a2792a0 100644
--- a/room/room-paging-rxjava3/src/main/java/androidx/room/paging/rxjava3/LimitOffsetRxPagingSource.kt
+++ b/room/room-paging-rxjava3/src/main/java/androidx/room/paging/rxjava3/LimitOffsetRxPagingSource.kt
@@ -30,7 +30,7 @@
 import androidx.room.paging.util.getClippedRefreshKey
 import androidx.room.paging.util.queryDatabase
 import androidx.room.paging.util.queryItemCount
-import androidx.room.rxjava3.RxRoom.createSingle
+import androidx.room.rxjava3.createSingle
 import androidx.sqlite.db.SupportSQLiteQuery
 import io.reactivex.rxjava3.core.Single
 import io.reactivex.rxjava3.schedulers.Schedulers
diff --git a/room/room-rxjava2/api/current.txt b/room/room-rxjava2/api/current.txt
index 73d416e..4df9b3d 100644
--- a/room/room-rxjava2/api/current.txt
+++ b/room/room-rxjava2/api/current.txt
@@ -2,14 +2,20 @@
 package androidx.room {
 
   public class EmptyResultSetException extends java.lang.RuntimeException {
-    ctor public EmptyResultSetException(String!);
+    ctor public EmptyResultSetException(String message);
   }
 
   public class RxRoom {
     ctor @Deprecated public RxRoom();
-    method public static io.reactivex.Flowable<java.lang.Object!>! createFlowable(androidx.room.RoomDatabase!, java.lang.String!...!);
-    method public static io.reactivex.Observable<java.lang.Object!>! createObservable(androidx.room.RoomDatabase!, java.lang.String!...!);
-    field public static final Object! NOTHING;
+    method public static final io.reactivex.Flowable<java.lang.Object> createFlowable(androidx.room.RoomDatabase database, java.lang.String... tableNames);
+    method public static final io.reactivex.Observable<java.lang.Object> createObservable(androidx.room.RoomDatabase database, java.lang.String... tableNames);
+    field public static final androidx.room.RxRoom.Companion Companion;
+    field public static final Object NOTHING;
+  }
+
+  public static final class RxRoom.Companion {
+    method public io.reactivex.Flowable<java.lang.Object> createFlowable(androidx.room.RoomDatabase database, java.lang.String... tableNames);
+    method public io.reactivex.Observable<java.lang.Object> createObservable(androidx.room.RoomDatabase database, java.lang.String... tableNames);
   }
 
 }
diff --git a/room/room-rxjava2/api/restricted_current.txt b/room/room-rxjava2/api/restricted_current.txt
index a3ecfa2..9a2d59c 100644
--- a/room/room-rxjava2/api/restricted_current.txt
+++ b/room/room-rxjava2/api/restricted_current.txt
@@ -2,19 +2,40 @@
 package androidx.room {
 
   public class EmptyResultSetException extends java.lang.RuntimeException {
-    ctor public EmptyResultSetException(String!);
+    ctor public EmptyResultSetException(String message);
   }
 
   public class RxRoom {
     ctor @Deprecated public RxRoom();
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static <T> io.reactivex.Flowable<T!>! createFlowable(androidx.room.RoomDatabase!, boolean, String![]!, java.util.concurrent.Callable<T!>!);
-    method public static io.reactivex.Flowable<java.lang.Object!>! createFlowable(androidx.room.RoomDatabase!, java.lang.String!...!);
-    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static <T> io.reactivex.Flowable<T!>! createFlowable(androidx.room.RoomDatabase!, String![]!, java.util.concurrent.Callable<T!>!);
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static <T> io.reactivex.Observable<T!>! createObservable(androidx.room.RoomDatabase!, boolean, String![]!, java.util.concurrent.Callable<T!>!);
-    method public static io.reactivex.Observable<java.lang.Object!>! createObservable(androidx.room.RoomDatabase!, java.lang.String!...!);
-    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static <T> io.reactivex.Observable<T!>! createObservable(androidx.room.RoomDatabase!, String![]!, java.util.concurrent.Callable<T!>!);
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static <T> io.reactivex.Single<T!>! createSingle(java.util.concurrent.Callable<? extends T!>!);
-    field public static final Object! NOTHING;
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final io.reactivex.Completable createCompletable(androidx.room.RoomDatabase db, boolean isReadOnly, boolean inTransaction, kotlin.jvm.functions.Function1<? super androidx.sqlite.SQLiteConnection,kotlin.Unit> block);
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final <T> io.reactivex.Flowable<T> createFlowable(androidx.room.RoomDatabase database, boolean inTransaction, String[] tableNames, java.util.concurrent.Callable<? extends T> callable);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final <T> io.reactivex.Flowable<T> createFlowable(androidx.room.RoomDatabase db, boolean inTransaction, String[] tableNames, kotlin.jvm.functions.Function1<? super androidx.sqlite.SQLiteConnection,? extends T?> block);
+    method public static final io.reactivex.Flowable<java.lang.Object> createFlowable(androidx.room.RoomDatabase database, java.lang.String... tableNames);
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final <T> io.reactivex.Flowable<T> createFlowable(androidx.room.RoomDatabase database, String[] tableNames, java.util.concurrent.Callable<? extends T> callable);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final <T> io.reactivex.Maybe<T> createMaybe(androidx.room.RoomDatabase db, boolean isReadOnly, boolean inTransaction, kotlin.jvm.functions.Function1<? super androidx.sqlite.SQLiteConnection,? extends T?> block);
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final <T> io.reactivex.Observable<T> createObservable(androidx.room.RoomDatabase database, boolean inTransaction, String[] tableNames, java.util.concurrent.Callable<? extends T> callable);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final <T> io.reactivex.Observable<T> createObservable(androidx.room.RoomDatabase db, boolean inTransaction, String[] tableNames, kotlin.jvm.functions.Function1<? super androidx.sqlite.SQLiteConnection,? extends T?> block);
+    method public static final io.reactivex.Observable<java.lang.Object> createObservable(androidx.room.RoomDatabase database, java.lang.String... tableNames);
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final <T> io.reactivex.Observable<T> createObservable(androidx.room.RoomDatabase database, String[] tableNames, java.util.concurrent.Callable<? extends T> callable);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final <T> io.reactivex.Single<T> createSingle(androidx.room.RoomDatabase db, boolean isReadOnly, boolean inTransaction, kotlin.jvm.functions.Function1<? super androidx.sqlite.SQLiteConnection,? extends T?> block);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final <T> io.reactivex.Single<T> createSingle(java.util.concurrent.Callable<? extends T> callable);
+    field public static final androidx.room.RxRoom.Companion Companion;
+    field public static final Object NOTHING;
+  }
+
+  public static final class RxRoom.Companion {
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public io.reactivex.Completable createCompletable(androidx.room.RoomDatabase db, boolean isReadOnly, boolean inTransaction, kotlin.jvm.functions.Function1<? super androidx.sqlite.SQLiteConnection,kotlin.Unit> block);
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T> io.reactivex.Flowable<T> createFlowable(androidx.room.RoomDatabase database, boolean inTransaction, String[] tableNames, java.util.concurrent.Callable<? extends T> callable);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T> io.reactivex.Flowable<T> createFlowable(androidx.room.RoomDatabase db, boolean inTransaction, String[] tableNames, kotlin.jvm.functions.Function1<? super androidx.sqlite.SQLiteConnection,? extends T?> block);
+    method public io.reactivex.Flowable<java.lang.Object> createFlowable(androidx.room.RoomDatabase database, java.lang.String... tableNames);
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T> io.reactivex.Flowable<T> createFlowable(androidx.room.RoomDatabase database, String[] tableNames, java.util.concurrent.Callable<? extends T> callable);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T> io.reactivex.Maybe<T> createMaybe(androidx.room.RoomDatabase db, boolean isReadOnly, boolean inTransaction, kotlin.jvm.functions.Function1<? super androidx.sqlite.SQLiteConnection,? extends T?> block);
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T> io.reactivex.Observable<T> createObservable(androidx.room.RoomDatabase database, boolean inTransaction, String[] tableNames, java.util.concurrent.Callable<? extends T> callable);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T> io.reactivex.Observable<T> createObservable(androidx.room.RoomDatabase db, boolean inTransaction, String[] tableNames, kotlin.jvm.functions.Function1<? super androidx.sqlite.SQLiteConnection,? extends T?> block);
+    method public io.reactivex.Observable<java.lang.Object> createObservable(androidx.room.RoomDatabase database, java.lang.String... tableNames);
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T> io.reactivex.Observable<T> createObservable(androidx.room.RoomDatabase database, String[] tableNames, java.util.concurrent.Callable<? extends T> callable);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T> io.reactivex.Single<T> createSingle(androidx.room.RoomDatabase db, boolean isReadOnly, boolean inTransaction, kotlin.jvm.functions.Function1<? super androidx.sqlite.SQLiteConnection,? extends T?> block);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T> io.reactivex.Single<T> createSingle(java.util.concurrent.Callable<? extends T> callable);
   }
 
 }
diff --git a/room/room-rxjava2/build.gradle b/room/room-rxjava2/build.gradle
index c49ef63..2b23d3f 100644
--- a/room/room-rxjava2/build.gradle
+++ b/room/room-rxjava2/build.gradle
@@ -36,6 +36,7 @@
 
     implementation("androidx.arch.core:core-runtime:2.2.0")
     implementation(libs.kotlinStdlib)
+    implementation(libs.kotlinCoroutinesRx2)
 
     testImplementation(project(":kruth:kruth"))
     testImplementation(libs.kotlinTest)
diff --git a/room/room-rxjava2/src/main/java/androidx/room/EmptyResultSetException.java b/room/room-rxjava2/src/main/java/androidx/room/EmptyResultSetException.java
deleted file mode 100644
index a36d8a9..0000000
--- a/room/room-rxjava2/src/main/java/androidx/room/EmptyResultSetException.java
+++ /dev/null
@@ -1,36 +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;
-
-/**
- * Thrown by Room when the query in a Single&lt;T&gt; DAO method needs to return a result but the
- * returned result from the database is empty.
- * <p>
- * Since a Single&lt;T&gt; must either emit a single non-null value or an error, this exception is
- * thrown instead of emitting a null value when the query resulted empty. If the Single&lt;T&gt;
- * contains a type argument of a collection (e.g. Single&lt;List&lt;Song&gt&gt;) then this
- * exception is not thrown an an empty collection is emitted instead.
- */
-public class EmptyResultSetException extends RuntimeException {
-    /**
-     * Constructs a new EmptyResultSetException with the exception.
-     * @param message The SQL query which didn't return any results.
-     */
-    public EmptyResultSetException(String message) {
-        super(message);
-    }
-}
diff --git a/room/room-rxjava2/src/main/java/androidx/room/EmptyResultSetException.kt b/room/room-rxjava2/src/main/java/androidx/room/EmptyResultSetException.kt
new file mode 100644
index 0000000..e2674f6
--- /dev/null
+++ b/room/room-rxjava2/src/main/java/androidx/room/EmptyResultSetException.kt
@@ -0,0 +1,27 @@
+/*
+ * 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
+
+/**
+ * Thrown by Room when the query in a [io.reactivex.Single] DAO method needs to return a result but
+ * the returned result from the database is empty.
+ *
+ * Since a [io.reactivex.Single] must either emit a single non-null value or an error, this
+ * exception is thrown instead of emitting a null value when the query resulted empty. If the
+ * [io.reactivex.Single] contains a type argument of a collection (e.g. `Single<List<Song>>`) the
+ * this exception is not thrown an an empty collection is emitted instead.
+ */
+open class EmptyResultSetException(message: String) : RuntimeException(message)
diff --git a/room/room-rxjava2/src/main/java/androidx/room/RxRoom.java b/room/room-rxjava2/src/main/java/androidx/room/RxRoom.java
deleted file mode 100644
index 42f3f79..0000000
--- a/room/room-rxjava2/src/main/java/androidx/room/RxRoom.java
+++ /dev/null
@@ -1,248 +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;
-
-import android.annotation.SuppressLint;
-
-import androidx.annotation.RestrictTo;
-
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.Executor;
-
-import io.reactivex.BackpressureStrategy;
-import io.reactivex.Flowable;
-import io.reactivex.FlowableEmitter;
-import io.reactivex.FlowableOnSubscribe;
-import io.reactivex.Maybe;
-import io.reactivex.MaybeSource;
-import io.reactivex.Observable;
-import io.reactivex.ObservableEmitter;
-import io.reactivex.ObservableOnSubscribe;
-import io.reactivex.Scheduler;
-import io.reactivex.Single;
-import io.reactivex.SingleEmitter;
-import io.reactivex.SingleOnSubscribe;
-import io.reactivex.disposables.Disposables;
-import io.reactivex.functions.Action;
-import io.reactivex.functions.Function;
-import io.reactivex.schedulers.Schedulers;
-
-/**
- * Helper class to add RxJava2 support to Room.
- */
-@SuppressLint("PrivateConstructorForUtilityClass")
-@SuppressWarnings("WeakerAccess")
-public class RxRoom {
-    /**
-     * Data dispatched by the publisher created by {@link #createFlowable(RoomDatabase, String...)}.
-     */
-    public static final Object NOTHING = new Object();
-
-    /**
-     * Creates a {@link Flowable} that emits at least once and also re-emits whenever one of the
-     * observed tables is updated.
-     * <p>
-     * You can easily chain a database operation to downstream of this {@link Flowable} to ensure
-     * that it re-runs when database is modified.
-     * <p>
-     * Since database invalidation is batched, multiple changes in the database may results in just
-     * 1 emission.
-     *
-     * @param database   The database instance
-     * @param tableNames The list of table names that should be observed
-     * @return A {@link Flowable} which emits {@link #NOTHING} when one of the observed tables
-     * is modified (also once when the invalidation tracker connection is established).
-     */
-    public static Flowable<Object> createFlowable(final RoomDatabase database,
-            final String... tableNames) {
-        return Flowable.create(new FlowableOnSubscribe<Object>() {
-            @Override
-            public void subscribe(final FlowableEmitter<Object> emitter) throws Exception {
-                final InvalidationTracker.Observer observer = new InvalidationTracker.Observer(
-                        tableNames) {
-                    @Override
-                    public void onInvalidated(@androidx.annotation.NonNull Set<String> tables) {
-                        if (!emitter.isCancelled()) {
-                            emitter.onNext(NOTHING);
-                        }
-                    }
-                };
-                if (!emitter.isCancelled()) {
-                    database.getInvalidationTracker().addObserver(observer);
-                    emitter.setDisposable(Disposables.fromAction(new Action() {
-                        @Override
-                        public void run() throws Exception {
-                            database.getInvalidationTracker().removeObserver(observer);
-                        }
-                    }));
-                }
-
-                // emit once to avoid missing any data and also easy chaining
-                if (!emitter.isCancelled()) {
-                    emitter.onNext(NOTHING);
-                }
-            }
-        }, BackpressureStrategy.LATEST);
-    }
-
-    /**
-     * Helper method used by generated code to bind a Callable such that it will be run in
-     * our disk io thread and will automatically block null values since RxJava2 does not like null.
-     *
-     * @deprecated Use {@link #createFlowable(RoomDatabase, boolean, String[], Callable)}
-     *
-     */
-    @Deprecated
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
-    public static <T> Flowable<T> createFlowable(final RoomDatabase database,
-            final String[] tableNames, final Callable<T> callable) {
-        return createFlowable(database, false, tableNames, callable);
-    }
-
-    /**
-     * Helper method used by generated code to bind a Callable such that it will be run in
-     * our disk io thread and will automatically block null values since RxJava2 does not like null.
-     *
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
-    public static <T> Flowable<T> createFlowable(final RoomDatabase database,
-            final boolean inTransaction, final String[] tableNames, final Callable<T> callable) {
-        Scheduler scheduler = Schedulers.from(getExecutor(database, inTransaction));
-        final Maybe<T> maybe = Maybe.fromCallable(callable);
-        return createFlowable(database, tableNames)
-                .subscribeOn(scheduler)
-                .unsubscribeOn(scheduler)
-                .observeOn(scheduler)
-                .flatMapMaybe(new Function<Object, MaybeSource<T>>() {
-                    @Override
-                    public MaybeSource<T> apply(Object o) throws Exception {
-                        return maybe;
-                    }
-                });
-    }
-
-    /**
-     * Creates a {@link Observable} that emits at least once and also re-emits whenever one of the
-     * observed tables is updated.
-     * <p>
-     * You can easily chain a database operation to downstream of this {@link Observable} to ensure
-     * that it re-runs when database is modified.
-     * <p>
-     * Since database invalidation is batched, multiple changes in the database may results in just
-     * 1 emission.
-     *
-     * @param database   The database instance
-     * @param tableNames The list of table names that should be observed
-     * @return A {@link Observable} which emits {@link #NOTHING} when one of the observed tables
-     * is modified (also once when the invalidation tracker connection is established).
-     */
-    public static Observable<Object> createObservable(final RoomDatabase database,
-            final String... tableNames) {
-        return Observable.create(new ObservableOnSubscribe<Object>() {
-            @Override
-            public void subscribe(final ObservableEmitter<Object> emitter) throws Exception {
-                final InvalidationTracker.Observer observer = new InvalidationTracker.Observer(
-                        tableNames) {
-                    @Override
-                    public void onInvalidated(@androidx.annotation.NonNull Set<String> tables) {
-                        emitter.onNext(NOTHING);
-                    }
-                };
-                database.getInvalidationTracker().addObserver(observer);
-                emitter.setDisposable(Disposables.fromAction(new Action() {
-                    @Override
-                    public void run() throws Exception {
-                        database.getInvalidationTracker().removeObserver(observer);
-                    }
-                }));
-
-                // emit once to avoid missing any data and also easy chaining
-                emitter.onNext(NOTHING);
-            }
-        });
-    }
-
-    /**
-     * Helper method used by generated code to bind a Callable such that it will be run in
-     * our disk io thread and will automatically block null values since RxJava2 does not like null.
-     *
-     * @deprecated Use {@link #createObservable(RoomDatabase, boolean, String[], Callable)}
-     *
-     */
-    @Deprecated
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
-    public static <T> Observable<T> createObservable(final RoomDatabase database,
-            final String[] tableNames, final Callable<T> callable) {
-        return createObservable(database, false, tableNames, callable);
-    }
-
-    /**
-     * Helper method used by generated code to bind a Callable such that it will be run in
-     * our disk io thread and will automatically block null values since RxJava2 does not like null.
-     *
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
-    public static <T> Observable<T> createObservable(final RoomDatabase database,
-            final boolean inTransaction, final String[] tableNames, final Callable<T> callable) {
-        Scheduler scheduler = Schedulers.from(getExecutor(database, inTransaction));
-        final Maybe<T> maybe = Maybe.fromCallable(callable);
-        return createObservable(database, tableNames)
-                .subscribeOn(scheduler)
-                .unsubscribeOn(scheduler)
-                .observeOn(scheduler)
-                .flatMapMaybe(new Function<Object, MaybeSource<T>>() {
-                    @Override
-                    public MaybeSource<T> apply(Object o) throws Exception {
-                        return maybe;
-                    }
-                });
-    }
-
-    /**
-     * Helper method used by generated code to create a Single from a Callable that will ignore
-     * the EmptyResultSetException if the stream is already disposed.
-     *
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
-    public static <T> Single<T> createSingle(final Callable<? extends T> callable) {
-        return Single.create(new SingleOnSubscribe<T>() {
-            @Override
-            public void subscribe(SingleEmitter<T> emitter) throws Exception {
-                try {
-                    emitter.onSuccess(callable.call());
-                } catch (EmptyResultSetException e) {
-                    emitter.tryOnError(e);
-                }
-            }
-        });
-    }
-
-    private static Executor getExecutor(RoomDatabase database, boolean inTransaction) {
-        if (inTransaction) {
-            return database.getTransactionExecutor();
-        } else {
-            return database.getQueryExecutor();
-        }
-    }
-
-    /** @deprecated This type should not be instantiated as it contains only static methods. */
-    @Deprecated
-    @SuppressWarnings("PrivateConstructorForUtilityClass")
-    public RxRoom() {
-    }
-}
diff --git a/room/room-rxjava2/src/main/java/androidx/room/RxRoom.kt b/room/room-rxjava2/src/main/java/androidx/room/RxRoom.kt
new file mode 100644
index 0000000..2ae5937
--- /dev/null
+++ b/room/room-rxjava2/src/main/java/androidx/room/RxRoom.kt
@@ -0,0 +1,295 @@
+/*
+ * 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
+
+import androidx.annotation.RestrictTo
+import androidx.room.coroutines.createFlow
+import androidx.room.util.performBlocking
+import androidx.sqlite.SQLiteConnection
+import io.reactivex.BackpressureStrategy
+import io.reactivex.Completable
+import io.reactivex.Flowable
+import io.reactivex.Maybe
+import io.reactivex.Observable
+import io.reactivex.Single
+import io.reactivex.disposables.Disposables
+import io.reactivex.schedulers.Schedulers
+import java.util.concurrent.Callable
+import java.util.concurrent.Executor
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.rx2.asObservable
+
+open class RxRoom
+@Deprecated("This type should not be instantiated as it contains only utility functions.")
+constructor() {
+
+    companion object {
+
+        /** Data dispatched by the publisher created by [createFlowable]. */
+        @JvmField val NOTHING: Any = Any()
+
+        /** Helper function used by generated code to create a [Flowable] */
+        @JvmStatic
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+        fun <T : Any> createFlowable(
+            db: RoomDatabase,
+            inTransaction: Boolean,
+            tableNames: Array<String>,
+            block: (SQLiteConnection) -> T?
+        ): Flowable<T> =
+            createObservable(db, inTransaction, tableNames, block)
+                .toFlowable(BackpressureStrategy.LATEST)
+
+        /** Helper function used by generated code to create a [Observable] */
+        @JvmStatic
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+        fun <T : Any> createObservable(
+            db: RoomDatabase,
+            inTransaction: Boolean,
+            tableNames: Array<String>,
+            block: (SQLiteConnection) -> T?
+        ): Observable<T> =
+            createFlow(db, inTransaction, tableNames, block)
+                .filterNotNull()
+                .asObservable(db.getQueryContext())
+
+        /** Helper function used by generated code to create a [Maybe] */
+        @JvmStatic
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+        fun <T : Any> createMaybe(
+            db: RoomDatabase,
+            isReadOnly: Boolean,
+            inTransaction: Boolean,
+            block: (SQLiteConnection) -> T?
+        ): Maybe<T> = Maybe.fromCallable { performBlocking(db, isReadOnly, inTransaction, block) }
+
+        /** Helper function used by generated code to create a [Completable] */
+        @JvmStatic
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+        fun createCompletable(
+            db: RoomDatabase,
+            isReadOnly: Boolean,
+            inTransaction: Boolean,
+            block: (SQLiteConnection) -> Unit
+        ): Completable =
+            Completable.fromAction { performBlocking(db, isReadOnly, inTransaction, block) }
+
+        /** Helper function used by generated code to create a [Single] */
+        @JvmStatic
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+        fun <T : Any> createSingle(
+            db: RoomDatabase,
+            isReadOnly: Boolean,
+            inTransaction: Boolean,
+            block: (SQLiteConnection) -> T?
+        ): Single<T> =
+            Single.create { emitter ->
+                if (emitter.isDisposed) return@create
+                try {
+                    val result = performBlocking(db, isReadOnly, inTransaction, block)
+                    if (result != null) {
+                        emitter.onSuccess(result)
+                    } else {
+                        throw EmptyResultSetException("Query returned empty result set.")
+                    }
+                } catch (e: EmptyResultSetException) {
+                    emitter.tryOnError(e)
+                }
+            }
+
+        /**
+         * Creates a [Flowable] that emits at least once and also re-emits whenever one of the
+         * observed tables is updated.
+         *
+         * You can easily chain a database operation to downstream of this [Flowable] to ensure that
+         * it re-runs when database is modified.
+         *
+         * Since database invalidation is batched, multiple changes in the database may results in
+         * just 1 emission.
+         *
+         * @param database The database instance
+         * @param tableNames The list of table names that should be observed
+         * @return A [Flowable] which emits [NOTHING] when one of the observed tables is modified
+         *   (also once when the invalidation tracker connection is established).
+         */
+        @JvmStatic
+        fun createFlowable(database: RoomDatabase, vararg tableNames: String): Flowable<Any> {
+            return Flowable.create(
+                { emitter ->
+                    val observer =
+                        object : InvalidationTracker.Observer(tableNames) {
+                            override fun onInvalidated(tables: Set<String>) {
+                                if (!emitter.isCancelled) {
+                                    emitter.onNext(NOTHING)
+                                }
+                            }
+                        }
+                    if (!emitter.isCancelled) {
+                        database.invalidationTracker.addObserver(observer)
+                        emitter.setDisposable(
+                            Disposables.fromAction {
+                                database.invalidationTracker.removeObserver(observer)
+                            }
+                        )
+                    }
+
+                    // emit once to avoid missing any data and also easy chaining
+                    if (!emitter.isCancelled) {
+                        emitter.onNext(NOTHING)
+                    }
+                },
+                BackpressureStrategy.LATEST
+            )
+        }
+
+        /**
+         * Helper method used by generated code to bind a [Callable] such that it will be run in our
+         * disk io thread and will automatically block null values since RxJava2 does not like null.
+         */
+        @JvmStatic
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+        @Deprecated("No longer used by generated code.")
+        fun <T : Any> createFlowable(
+            database: RoomDatabase,
+            tableNames: Array<String>,
+            callable: Callable<out T>
+        ): Flowable<T> {
+            @Suppress("DEPRECATION") return createFlowable(database, false, tableNames, callable)
+        }
+
+        /**
+         * Helper method used by generated code to bind a [Callable] such that it will be run in our
+         * disk io thread and will automatically block null values since RxJava2 does not like null.
+         */
+        @JvmStatic
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+        @Deprecated("No longer used by generated code.")
+        fun <T : Any> createFlowable(
+            database: RoomDatabase,
+            inTransaction: Boolean,
+            tableNames: Array<String>,
+            callable: Callable<out T>
+        ): Flowable<T> {
+            val scheduler = Schedulers.from(getExecutor(database, inTransaction))
+            val maybe = Maybe.fromCallable(callable)
+            return createFlowable(database, *tableNames)
+                .subscribeOn(scheduler)
+                .unsubscribeOn(scheduler)
+                .observeOn(scheduler)
+                .flatMapMaybe { maybe }
+        }
+
+        /**
+         * Creates a [Observable] that emits at least once and also re-emits whenever one of the
+         * observed tables is updated.
+         *
+         * You can easily chain a database operation to downstream of this [Observable] to ensure
+         * that it re-runs when database is modified.
+         *
+         * Since database invalidation is batched, multiple changes in the database may results in
+         * just 1 emission.
+         *
+         * @param database The database instance
+         * @param tableNames The list of table names that should be observed
+         * @return A [Observable] which emits [.NOTHING] when one of the observed tables is modified
+         *   (also once when the invalidation tracker connection is established).
+         */
+        @JvmStatic
+        fun createObservable(database: RoomDatabase, vararg tableNames: String): Observable<Any> {
+            return Observable.create { emitter ->
+                val observer =
+                    object : InvalidationTracker.Observer(tableNames) {
+                        override fun onInvalidated(tables: Set<String>) {
+                            emitter.onNext(NOTHING)
+                        }
+                    }
+                database.invalidationTracker.addObserver(observer)
+                emitter.setDisposable(
+                    Disposables.fromAction { database.invalidationTracker.removeObserver(observer) }
+                )
+
+                // emit once to avoid missing any data and also easy chaining
+                emitter.onNext(NOTHING)
+            }
+        }
+
+        /**
+         * Helper method used by generated code to bind a [Callable] such that it will be run in our
+         * disk io thread and will automatically block null values since RxJava2 does not like null.
+         */
+        @JvmStatic
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+        @Deprecated("No longer used by generated code.")
+        fun <T : Any> createObservable(
+            database: RoomDatabase,
+            tableNames: Array<String>,
+            callable: Callable<out T>
+        ): Observable<T> {
+            @Suppress("DEPRECATION") return createObservable(database, false, tableNames, callable)
+        }
+
+        /**
+         * Helper method used by generated code to bind a [Callable] such that it will be run in our
+         * disk io thread and will automatically block null values since RxJava2 does not like null.
+         */
+        @JvmStatic
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+        @Deprecated("No longer used by generated code.")
+        fun <T : Any> createObservable(
+            database: RoomDatabase,
+            inTransaction: Boolean,
+            tableNames: Array<String>,
+            callable: Callable<out T>
+        ): Observable<T> {
+            val scheduler = Schedulers.from(getExecutor(database, inTransaction))
+            val maybe = Maybe.fromCallable(callable)
+            return createObservable(database, *tableNames)
+                .subscribeOn(scheduler)
+                .unsubscribeOn(scheduler)
+                .observeOn(scheduler)
+                .flatMapMaybe { maybe }
+        }
+
+        /**
+         * Helper method used by generated code to create a [Single] from a [Callable] that will
+         * ignore the [EmptyResultSetException] if the stream is already disposed.
+         */
+        @JvmStatic
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+        fun <T : Any> createSingle(callable: Callable<out T>): Single<T> {
+            return Single.create { emitter ->
+                try {
+                    val result = callable.call()
+                    if (result != null) {
+                        emitter.onSuccess(result)
+                    } else {
+                        throw EmptyResultSetException("Query returned empty result set.")
+                    }
+                } catch (e: EmptyResultSetException) {
+                    emitter.tryOnError(e)
+                }
+            }
+        }
+
+        private fun getExecutor(database: RoomDatabase, inTransaction: Boolean): Executor {
+            return if (inTransaction) {
+                database.transactionExecutor
+            } else {
+                database.queryExecutor
+            }
+        }
+    }
+}
diff --git a/room/room-rxjava2/src/test/java/androidx/room/RxRoomTest.kt b/room/room-rxjava2/src/test/java/androidx/room/RxRoomTest.kt
index 32b23a8..2dcc9d5 100644
--- a/room/room-rxjava2/src/test/java/androidx/room/RxRoomTest.kt
+++ b/room/room-rxjava2/src/test/java/androidx/room/RxRoomTest.kt
@@ -21,6 +21,7 @@
 import io.reactivex.functions.Consumer
 import io.reactivex.observers.TestObserver
 import io.reactivex.subscribers.TestSubscriber
+import java.util.concurrent.Callable
 import java.util.concurrent.TimeUnit
 import java.util.concurrent.atomic.AtomicReference
 import org.junit.Before
@@ -138,12 +139,12 @@
     }
 
     @Test
-    @Throws(Exception::class)
+    @Suppress("DEPRECATION")
     fun internalCallable_Flowable() {
         val value = AtomicReference<Any>(null)
         val tables = arrayOf("a", "b")
         val tableSet: Set<String> = HashSet(listOf(*tables))
-        val flowable = RxRoom.createFlowable(mDatabase, false, tables) { value.get() }
+        val flowable = RxRoom.createFlowable(mDatabase, false, tables, Callable { value.get() })
         val consumer = CountingConsumer()
         flowable.subscribe(consumer)
         drain()
@@ -167,12 +168,12 @@
     }
 
     @Test
-    @Throws(Exception::class)
+    @Suppress("DEPRECATION")
     fun internalCallable_Observable() {
         val value = AtomicReference<Any>(null)
         val tables = arrayOf("a", "b")
         val tableSet: Set<String> = HashSet(listOf(*tables))
-        val flowable = RxRoom.createObservable(mDatabase, false, tables) { value.get() }
+        val flowable = RxRoom.createObservable(mDatabase, false, tables, Callable { value.get() })
         val consumer = CountingConsumer()
         flowable.subscribe(consumer)
         drain()
@@ -196,11 +197,15 @@
     }
 
     @Test
+    @Suppress("DEPRECATION")
     fun exception_Flowable() {
         val flowable =
-            RxRoom.createFlowable<String>(mDatabase, false, arrayOf("a")) {
-                throw Exception("i want exception")
-            }
+            RxRoom.createFlowable<String>(
+                mDatabase,
+                false,
+                arrayOf("a"),
+                Callable { throw Exception("i want exception") }
+            )
         val subscriber = TestSubscriber<String>()
         flowable.subscribe(subscriber)
         drain()
@@ -209,11 +214,15 @@
     }
 
     @Test
+    @Suppress("DEPRECATION")
     fun exception_Observable() {
         val flowable =
-            RxRoom.createObservable<String>(mDatabase, false, arrayOf("a")) {
-                throw Exception("i want exception")
-            }
+            RxRoom.createObservable<String>(
+                mDatabase,
+                false,
+                arrayOf("a"),
+                Callable { throw Exception("i want exception") }
+            )
         val observer = TestObserver<String>()
         flowable.subscribe(observer)
         drain()
diff --git a/room/room-rxjava3/api/current.txt b/room/room-rxjava3/api/current.txt
index 6b78281..bbddba6 100644
--- a/room/room-rxjava3/api/current.txt
+++ b/room/room-rxjava3/api/current.txt
@@ -2,12 +2,12 @@
 package androidx.room.rxjava3 {
 
   public final class EmptyResultSetException extends java.lang.RuntimeException {
-    ctor public EmptyResultSetException(String);
+    ctor public EmptyResultSetException(String message);
   }
 
   public final class RxRoom {
-    method public static io.reactivex.rxjava3.core.Flowable<java.lang.Object!> createFlowable(androidx.room.RoomDatabase, java.lang.String!...);
-    method public static io.reactivex.rxjava3.core.Observable<java.lang.Object!> createObservable(androidx.room.RoomDatabase, java.lang.String!...);
+    method public static io.reactivex.rxjava3.core.Flowable<java.lang.Object> createFlowable(androidx.room.RoomDatabase database, java.lang.String... tableNames);
+    method public static io.reactivex.rxjava3.core.Observable<java.lang.Object> createObservable(androidx.room.RoomDatabase database, java.lang.String... tableNames);
     field public static final Object NOTHING;
   }
 
diff --git a/room/room-rxjava3/api/restricted_current.txt b/room/room-rxjava3/api/restricted_current.txt
index 4fb4dbd..9b71011 100644
--- a/room/room-rxjava3/api/restricted_current.txt
+++ b/room/room-rxjava3/api/restricted_current.txt
@@ -2,15 +2,20 @@
 package androidx.room.rxjava3 {
 
   public final class EmptyResultSetException extends java.lang.RuntimeException {
-    ctor public EmptyResultSetException(String);
+    ctor public EmptyResultSetException(String message);
   }
 
   public final class RxRoom {
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static <T> io.reactivex.rxjava3.core.Flowable<T!> createFlowable(androidx.room.RoomDatabase, boolean, String![], java.util.concurrent.Callable<T!>);
-    method public static io.reactivex.rxjava3.core.Flowable<java.lang.Object!> createFlowable(androidx.room.RoomDatabase, java.lang.String!...);
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static <T> io.reactivex.rxjava3.core.Observable<T!> createObservable(androidx.room.RoomDatabase, boolean, String![], java.util.concurrent.Callable<T!>);
-    method public static io.reactivex.rxjava3.core.Observable<java.lang.Object!> createObservable(androidx.room.RoomDatabase, java.lang.String!...);
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static <T> io.reactivex.rxjava3.core.Single<T!> createSingle(java.util.concurrent.Callable<? extends T!>);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static io.reactivex.rxjava3.core.Completable createCompletable(androidx.room.RoomDatabase db, boolean isReadOnly, boolean inTransaction, kotlin.jvm.functions.Function1<? super androidx.sqlite.SQLiteConnection,kotlin.Unit> block);
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static <T> io.reactivex.rxjava3.core.Flowable<T> createFlowable(androidx.room.RoomDatabase database, boolean inTransaction, String[] tableNames, java.util.concurrent.Callable<? extends T> callable);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static <T> io.reactivex.rxjava3.core.Flowable<T> createFlowable(androidx.room.RoomDatabase db, boolean inTransaction, String[] tableNames, kotlin.jvm.functions.Function1<? super androidx.sqlite.SQLiteConnection,? extends T?> block);
+    method public static io.reactivex.rxjava3.core.Flowable<java.lang.Object> createFlowable(androidx.room.RoomDatabase database, java.lang.String... tableNames);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static <T> io.reactivex.rxjava3.core.Maybe<T> createMaybe(androidx.room.RoomDatabase db, boolean isReadOnly, boolean inTransaction, kotlin.jvm.functions.Function1<? super androidx.sqlite.SQLiteConnection,? extends T?> block);
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static <T> io.reactivex.rxjava3.core.Observable<T> createObservable(androidx.room.RoomDatabase database, boolean inTransaction, String[] tableNames, java.util.concurrent.Callable<? extends T> callable);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static <T> io.reactivex.rxjava3.core.Observable<T> createObservable(androidx.room.RoomDatabase db, boolean inTransaction, String[] tableNames, kotlin.jvm.functions.Function1<? super androidx.sqlite.SQLiteConnection,? extends T?> block);
+    method public static io.reactivex.rxjava3.core.Observable<java.lang.Object> createObservable(androidx.room.RoomDatabase database, java.lang.String... tableNames);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static <T> io.reactivex.rxjava3.core.Single<T> createSingle(androidx.room.RoomDatabase db, boolean isReadOnly, boolean inTransaction, kotlin.jvm.functions.Function1<? super androidx.sqlite.SQLiteConnection,? extends T?> block);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static <T> io.reactivex.rxjava3.core.Single<T> createSingle(java.util.concurrent.Callable<? extends T> callable);
     field public static final Object NOTHING;
   }
 
diff --git a/room/room-rxjava3/build.gradle b/room/room-rxjava3/build.gradle
index dd068ad..96c86a1 100644
--- a/room/room-rxjava3/build.gradle
+++ b/room/room-rxjava3/build.gradle
@@ -27,7 +27,6 @@
     id("AndroidXPlugin")
     id("com.android.library")
     id("kotlin-android")
-    id("com.google.devtools.ksp")
 }
 
 dependencies {
@@ -37,6 +36,7 @@
 
     implementation("androidx.arch.core:core-runtime:2.2.0")
     implementation(libs.kotlinStdlib)
+    implementation(libs.kotlinCoroutinesRx3)
 
     testImplementation(project(":kruth:kruth"))
     testImplementation(libs.kotlinTest)
diff --git a/room/room-rxjava3/src/main/java/androidx/room/rxjava3/EmptyResultSetException.java b/room/room-rxjava3/src/main/java/androidx/room/rxjava3/EmptyResultSetException.java
deleted file mode 100644
index 3b2058d..0000000
--- a/room/room-rxjava3/src/main/java/androidx/room/rxjava3/EmptyResultSetException.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.room.rxjava3;
-
-import androidx.annotation.NonNull;
-
-/**
- * Thrown by Room when the query in a Single&lt;T&gt; DAO method needs to return a result but the
- * returned result from the database is empty.
- * <p>
- * Since a Single&lt;T&gt; must either emit a single non-null value or an error, this exception is
- * thrown instead of emitting a null value when the query resulted empty. If the Single&lt;T&gt;
- * contains a type argument of a collection (e.g. Single&lt;List&lt;Song&gt&gt;) then this
- * exception is not thrown an an empty collection is emitted instead.
- */
-@SuppressWarnings("serial")
-public final class EmptyResultSetException extends RuntimeException {
-    /**
-     * Constructs a new EmptyResultSetException with the exception.
-     * @param message The SQL query which didn't return any results.
-     */
-    public EmptyResultSetException(@NonNull String message) {
-        super(message);
-    }
-}
diff --git a/room/room-rxjava3/src/main/java/androidx/room/rxjava3/EmptyResultSetException.kt b/room/room-rxjava3/src/main/java/androidx/room/rxjava3/EmptyResultSetException.kt
new file mode 100644
index 0000000..b009f6e
--- /dev/null
+++ b/room/room-rxjava3/src/main/java/androidx/room/rxjava3/EmptyResultSetException.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.rxjava3
+
+/**
+ * Thrown by Room when the query in a [io.reactivex.rxjava3.core.Single] DAO method needs to return
+ * a result but the returned result from the database is empty.
+ *
+ * Since a [io.reactivex.rxjava3.core.Single] must either emit a single non-null value or an error,
+ * this exception is thrown instead of emitting a null value when the query resulted empty. If the
+ * [io.reactivex.rxjava3.core.Single] contains a type argument of a collection (e.g.
+ * `Single<List<Song>>`) the this exception is not thrown an an empty collection is emitted instead.
+ */
+class EmptyResultSetException(message: String) : RuntimeException(message)
diff --git a/room/room-rxjava3/src/main/java/androidx/room/rxjava3/RxRoom.java b/room/room-rxjava3/src/main/java/androidx/room/rxjava3/RxRoom.java
deleted file mode 100644
index e520efd..0000000
--- a/room/room-rxjava3/src/main/java/androidx/room/rxjava3/RxRoom.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.room.rxjava3;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-import androidx.room.InvalidationTracker;
-import androidx.room.RoomDatabase;
-
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.Executor;
-
-import io.reactivex.rxjava3.core.BackpressureStrategy;
-import io.reactivex.rxjava3.core.Flowable;
-import io.reactivex.rxjava3.core.Maybe;
-import io.reactivex.rxjava3.core.MaybeSource;
-import io.reactivex.rxjava3.core.Observable;
-import io.reactivex.rxjava3.core.Scheduler;
-import io.reactivex.rxjava3.core.Single;
-import io.reactivex.rxjava3.disposables.Disposable;
-import io.reactivex.rxjava3.functions.Function;
-import io.reactivex.rxjava3.schedulers.Schedulers;
-
-/**
- * Helper class to add RxJava3 support to Room.
- */
-public final class RxRoom {
-    /**
-     * Data dispatched by the publisher created by {@link #createFlowable(RoomDatabase, String...)}.
-     */
-    @NonNull
-    public static final Object NOTHING = new Object();
-
-    /**
-     * Creates a {@link Flowable} that emits at least once and also re-emits whenever one of the
-     * observed tables is updated.
-     * <p>
-     * You can easily chain a database operation to downstream of this {@link Flowable} to ensure
-     * that it re-runs when database is modified.
-     * <p>
-     * Since database invalidation is batched, multiple changes in the database may results in just
-     * 1 emission.
-     *
-     * @param database   The database instance
-     * @param tableNames The list of table names that should be observed
-     * @return A {@link Flowable} which emits {@link #NOTHING} when one of the observed tables
-     * is modified (also once when the invalidation tracker connection is established).
-     */
-    @NonNull
-    public static Flowable<Object> createFlowable(@NonNull final RoomDatabase database,
-            @NonNull final String... tableNames) {
-        return Flowable.create(emitter -> {
-            final InvalidationTracker.Observer observer = new InvalidationTracker.Observer(
-                    tableNames) {
-                @Override
-                public void onInvalidated(@androidx.annotation.NonNull Set<String> tables) {
-                    if (!emitter.isCancelled()) {
-                        emitter.onNext(NOTHING);
-                    }
-                }
-            };
-            if (!emitter.isCancelled()) {
-                database.getInvalidationTracker().addObserver(observer);
-                emitter.setDisposable(Disposable.fromAction(
-                        () -> database.getInvalidationTracker().removeObserver(observer)));
-            }
-
-            // emit once to avoid missing any data and also easy chaining
-            if (!emitter.isCancelled()) {
-                emitter.onNext(NOTHING);
-            }
-        }, BackpressureStrategy.LATEST);
-    }
-
-    /**
-     * Helper method used by generated code to bind a Callable such that it will be run in
-     * our disk io thread and will automatically block null values since RxJava3 does not like null.
-     *
-     */
-    @NonNull
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
-    public static <T> Flowable<T> createFlowable(@NonNull final RoomDatabase database,
-            final boolean inTransaction, @NonNull final String[] tableNames,
-            @NonNull final Callable<T> callable) {
-        Scheduler scheduler = Schedulers.from(getExecutor(database, inTransaction));
-        final Maybe<T> maybe = Maybe.fromCallable(callable);
-        return createFlowable(database, tableNames)
-                .subscribeOn(scheduler)
-                .unsubscribeOn(scheduler)
-                .observeOn(scheduler)
-                .flatMapMaybe((Function<Object, MaybeSource<T>>) o -> maybe);
-    }
-
-    /**
-     * Creates a {@link Observable} that emits at least once and also re-emits whenever one of the
-     * observed tables is updated.
-     * <p>
-     * You can easily chain a database operation to downstream of this {@link Observable} to ensure
-     * that it re-runs when database is modified.
-     * <p>
-     * Since database invalidation is batched, multiple changes in the database may results in just
-     * 1 emission.
-     *
-     * @param database   The database instance
-     * @param tableNames The list of table names that should be observed
-     * @return A {@link Observable} which emits {@link #NOTHING} when one of the observed tables
-     * is modified (also once when the invalidation tracker connection is established).
-     */
-    @NonNull
-    public static Observable<Object> createObservable(@NonNull final RoomDatabase database,
-            @NonNull final String... tableNames) {
-        return Observable.create(emitter -> {
-            final InvalidationTracker.Observer observer = new InvalidationTracker.Observer(
-                    tableNames) {
-                @Override
-                public void onInvalidated(@androidx.annotation.NonNull Set<String> tables) {
-                    emitter.onNext(NOTHING);
-                }
-            };
-            database.getInvalidationTracker().addObserver(observer);
-            emitter.setDisposable(Disposable.fromAction(
-                    () -> database.getInvalidationTracker().removeObserver(observer)));
-
-            // emit once to avoid missing any data and also easy chaining
-            emitter.onNext(NOTHING);
-        });
-    }
-
-    /**
-     * Helper method used by generated code to bind a Callable such that it will be run in
-     * our disk io thread and will automatically block null values since RxJava3 does not like null.
-     *
-     */
-    @NonNull
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
-    public static <T> Observable<T> createObservable(@NonNull final RoomDatabase database,
-            final boolean inTransaction, @NonNull final String[] tableNames,
-            @NonNull final Callable<T> callable) {
-        Scheduler scheduler = Schedulers.from(getExecutor(database, inTransaction));
-        final Maybe<T> maybe = Maybe.fromCallable(callable);
-        return createObservable(database, tableNames)
-                .subscribeOn(scheduler)
-                .unsubscribeOn(scheduler)
-                .observeOn(scheduler)
-                .flatMapMaybe(o -> maybe);
-    }
-
-    /**
-     * Helper method used by generated code to create a Single from a Callable that will ignore
-     * the EmptyResultSetException if the stream is already disposed.
-     *
-     */
-    @NonNull
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
-    public static <T> Single<T> createSingle(@NonNull final Callable<? extends T> callable) {
-        return Single.create(emitter -> {
-            try {
-                emitter.onSuccess(callable.call());
-            } catch (EmptyResultSetException e) {
-                emitter.tryOnError(e);
-            }
-        });
-    }
-
-    private static Executor getExecutor(@NonNull RoomDatabase database, boolean inTransaction) {
-        if (inTransaction) {
-            return database.getTransactionExecutor();
-        } else {
-            return database.getQueryExecutor();
-        }
-    }
-
-    private RxRoom() {
-    }
-}
diff --git a/room/room-rxjava3/src/main/java/androidx/room/rxjava3/RxRoom.kt b/room/room-rxjava3/src/main/java/androidx/room/rxjava3/RxRoom.kt
new file mode 100644
index 0000000..82ee4b5
--- /dev/null
+++ b/room/room-rxjava3/src/main/java/androidx/room/rxjava3/RxRoom.kt
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:JvmName("RxRoom")
+
+package androidx.room.rxjava3
+
+import androidx.annotation.RestrictTo
+import androidx.room.InvalidationTracker
+import androidx.room.RoomDatabase
+import androidx.room.coroutines.createFlow
+import androidx.room.util.performBlocking
+import androidx.sqlite.SQLiteConnection
+import io.reactivex.rxjava3.core.BackpressureStrategy
+import io.reactivex.rxjava3.core.Completable
+import io.reactivex.rxjava3.core.Flowable
+import io.reactivex.rxjava3.core.FlowableEmitter
+import io.reactivex.rxjava3.core.Maybe
+import io.reactivex.rxjava3.core.Observable
+import io.reactivex.rxjava3.core.ObservableEmitter
+import io.reactivex.rxjava3.core.Single
+import io.reactivex.rxjava3.disposables.Disposable
+import io.reactivex.rxjava3.schedulers.Schedulers
+import java.util.concurrent.Callable
+import java.util.concurrent.Executor
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.rx3.asObservable
+
+/** Marker class used by annotation processor to identify dependency is in the classpath. */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) class Rx3RoomArtifactMarker private constructor()
+
+/** Data dispatched by the publisher created by [createFlowable]. */
+@JvmField val NOTHING: Any = Any()
+
+/** Helper function used by generated code to create a [Flowable] */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+fun <T : Any> createFlowable(
+    db: RoomDatabase,
+    inTransaction: Boolean,
+    tableNames: Array<String>,
+    block: (SQLiteConnection) -> T?
+): Flowable<T> =
+    createObservable(db, inTransaction, tableNames, block).toFlowable(BackpressureStrategy.LATEST)
+
+/** Helper function used by generated code to create a [Observable] */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+fun <T : Any> createObservable(
+    db: RoomDatabase,
+    inTransaction: Boolean,
+    tableNames: Array<String>,
+    block: (SQLiteConnection) -> T?
+): Observable<T> =
+    createFlow(db, inTransaction, tableNames, block)
+        .filterNotNull()
+        .asObservable(db.getQueryContext())
+
+/** Helper function used by generated code to create a [Maybe] */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+fun <T : Any> createMaybe(
+    db: RoomDatabase,
+    isReadOnly: Boolean,
+    inTransaction: Boolean,
+    block: (SQLiteConnection) -> T?
+): Maybe<T> =
+    Maybe.fromCallable(Callable<T> { performBlocking(db, isReadOnly, inTransaction, block) })
+
+/** Helper function used by generated code to create a [Completable] */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+fun createCompletable(
+    db: RoomDatabase,
+    isReadOnly: Boolean,
+    inTransaction: Boolean,
+    block: (SQLiteConnection) -> Unit
+): Completable = Completable.fromCallable { performBlocking(db, isReadOnly, inTransaction, block) }
+
+/** Helper function used by generated code to create a [Single] */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+fun <T : Any> createSingle(
+    db: RoomDatabase,
+    isReadOnly: Boolean,
+    inTransaction: Boolean,
+    block: (SQLiteConnection) -> T?
+): Single<T> =
+    Single.create { emitter ->
+        if (emitter.isDisposed) return@create
+        try {
+            val result = performBlocking(db, isReadOnly, inTransaction, block)
+            if (result != null) {
+                emitter.onSuccess(result)
+            } else {
+                throw EmptyResultSetException("Query returned empty result set.")
+            }
+        } catch (e: EmptyResultSetException) {
+            emitter.tryOnError(e)
+        }
+    }
+
+/**
+ * Creates a [Flowable] that emits at least once and also re-emits whenever one of the observed
+ * tables is updated.
+ *
+ * You can easily chain a database operation to downstream of this [Flowable] to ensure that it
+ * re-runs when database is modified.
+ *
+ * Since database invalidation is batched, multiple changes in the database may results in just 1
+ * emission.
+ *
+ * @param database The database instance
+ * @param tableNames The list of table names that should be observed
+ * @return A [Flowable] which emits [NOTHING] when one of the observed tables is modified (also once
+ *   when the invalidation tracker connection is established).
+ */
+fun createFlowable(database: RoomDatabase, vararg tableNames: String): Flowable<Any> {
+    return Flowable.create(
+        { emitter: FlowableEmitter<Any> ->
+            val observer =
+                object : InvalidationTracker.Observer(tableNames) {
+                    override fun onInvalidated(tables: Set<String>) {
+                        if (!emitter.isCancelled) {
+                            emitter.onNext(NOTHING)
+                        }
+                    }
+                }
+            if (!emitter.isCancelled) {
+                database.invalidationTracker.addObserver(observer)
+                emitter.setDisposable(
+                    Disposable.fromAction { database.invalidationTracker.removeObserver(observer) }
+                )
+            }
+
+            // emit once to avoid missing any data and also easy chaining
+            if (!emitter.isCancelled) {
+                emitter.onNext(NOTHING)
+            }
+        },
+        BackpressureStrategy.LATEST
+    )
+}
+
+/**
+ * Helper method used by generated code to bind a Callable such that it will be run in our disk io
+ * thread and will automatically block null values since RxJava3 does not like null.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@Deprecated("No longer used by generated code.")
+fun <T : Any> createFlowable(
+    database: RoomDatabase,
+    inTransaction: Boolean,
+    tableNames: Array<String>,
+    callable: Callable<out T>
+): Flowable<T> {
+    val scheduler = Schedulers.from(getExecutor(database, inTransaction))
+    val maybe = Maybe.fromCallable(callable)
+    return createFlowable(database, *tableNames)
+        .subscribeOn(scheduler)
+        .unsubscribeOn(scheduler)
+        .observeOn(scheduler)
+        .flatMapMaybe { maybe }
+}
+
+/**
+ * Creates a [Observable] that emits at least once and also re-emits whenever one of the observed
+ * tables is updated.
+ *
+ * You can easily chain a database operation to downstream of this [Observable] to ensure that it
+ * re-runs when database is modified.
+ *
+ * Since database invalidation is batched, multiple changes in the database may results in just 1
+ * emission.
+ *
+ * @param database The database instance
+ * @param tableNames The list of table names that should be observed
+ * @return A [Observable] which emits [NOTHING] when one of the observed tables is modified (also
+ *   once when the invalidation tracker connection is established).
+ */
+fun createObservable(database: RoomDatabase, vararg tableNames: String): Observable<Any> {
+    return Observable.create { emitter: ObservableEmitter<Any> ->
+        val observer =
+            object : InvalidationTracker.Observer(tableNames) {
+                override fun onInvalidated(tables: Set<String>) {
+                    emitter.onNext(NOTHING)
+                }
+            }
+        database.invalidationTracker.addObserver(observer)
+        emitter.setDisposable(
+            Disposable.fromAction { database.invalidationTracker.removeObserver(observer) }
+        )
+
+        // emit once to avoid missing any data and also easy chaining
+        emitter.onNext(NOTHING)
+    }
+}
+
+/**
+ * Helper method used by generated code to bind a Callable such that it will be run in our disk io
+ * thread and will automatically block null values since RxJava3 does not like null.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@Deprecated("No longer used by generated code.")
+fun <T : Any> createObservable(
+    database: RoomDatabase,
+    inTransaction: Boolean,
+    tableNames: Array<String>,
+    callable: Callable<out T>
+): Observable<T> {
+    val scheduler = Schedulers.from(getExecutor(database, inTransaction))
+    val maybe = Maybe.fromCallable(callable)
+    return createObservable(database, *tableNames)
+        .subscribeOn(scheduler)
+        .unsubscribeOn(scheduler)
+        .observeOn(scheduler)
+        .flatMapMaybe { maybe }
+}
+
+/**
+ * Helper method used by generated code to create a Single from a Callable that will ignore the
+ * EmptyResultSetException if the stream is already disposed.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+fun <T : Any> createSingle(callable: Callable<out T>): Single<T> {
+    return Single.create { emitter ->
+        try {
+            val result = callable.call()
+            if (result != null) {
+                emitter.onSuccess(result)
+            } else {
+                throw EmptyResultSetException("Query returned empty result set.")
+            }
+        } catch (e: EmptyResultSetException) {
+            emitter.tryOnError(e)
+        }
+    }
+}
+
+private fun getExecutor(database: RoomDatabase, inTransaction: Boolean): Executor {
+    return if (inTransaction) {
+        database.transactionExecutor
+    } else {
+        database.queryExecutor
+    }
+}
diff --git a/room/room-rxjava3/src/test/java/androidx/room/rxjava3/RxRoomTest.kt b/room/room-rxjava3/src/test/java/androidx/room/rxjava3/RxRoomTest.kt
index 256e5c2..27f5606 100644
--- a/room/room-rxjava3/src/test/java/androidx/room/rxjava3/RxRoomTest.kt
+++ b/room/room-rxjava3/src/test/java/androidx/room/rxjava3/RxRoomTest.kt
@@ -23,6 +23,7 @@
 import io.reactivex.rxjava3.functions.Consumer
 import io.reactivex.rxjava3.observers.TestObserver
 import io.reactivex.rxjava3.subscribers.TestSubscriber
+import java.util.concurrent.Callable
 import java.util.concurrent.TimeUnit
 import java.util.concurrent.atomic.AtomicReference
 import org.junit.Before
@@ -62,7 +63,7 @@
 
     @Test
     fun basicAddRemove_Flowable() {
-        val flowable = RxRoom.createFlowable(mDatabase, "a", "b")
+        val flowable = createFlowable(mDatabase, "a", "b")
         verify(mInvalidationTracker, never()).addObserver(any())
         var disposable = flowable.subscribe()
         verify(mInvalidationTracker).addObserver(any())
@@ -82,7 +83,7 @@
 
     @Test
     fun basicAddRemove_Observable() {
-        val observable = RxRoom.createObservable(mDatabase, "a", "b")
+        val observable = createObservable(mDatabase, "a", "b")
         verify(mInvalidationTracker, never()).addObserver(any())
         var disposable = observable.subscribe()
         verify(mInvalidationTracker).addObserver(any())
@@ -104,7 +105,7 @@
     fun basicNotify_Flowable() {
         val tables = arrayOf("a", "b")
         val tableSet: Set<String> = HashSet(listOf(*tables))
-        val flowable = RxRoom.createFlowable(mDatabase, *tables)
+        val flowable = createFlowable(mDatabase, *tables)
         val consumer = CountingConsumer()
         val disposable = flowable.subscribe(consumer)
         assertThat(mAddedObservers.size).isEqualTo(1)
@@ -123,7 +124,7 @@
     fun basicNotify_Observable() {
         val tables = arrayOf("a", "b")
         val tableSet: Set<String> = HashSet(listOf(*tables))
-        val observable = RxRoom.createObservable(mDatabase, *tables)
+        val observable = createObservable(mDatabase, *tables)
         val consumer = CountingConsumer()
         val disposable = observable.subscribe(consumer)
         assertThat(mAddedObservers.size).isEqualTo(1)
@@ -139,12 +140,12 @@
     }
 
     @Test
-    @Throws(Exception::class)
+    @Suppress("DEPRECATION")
     fun internalCallable_Flowable() {
         val value = AtomicReference<Any>(null)
         val tables = arrayOf("a", "b")
         val tableSet: Set<String> = HashSet(listOf(*tables))
-        val flowable = RxRoom.createFlowable(mDatabase, false, tables) { value.get() }
+        val flowable = createFlowable(mDatabase, false, tables, Callable { value.get() })
         val consumer = CountingConsumer()
         val disposable = flowable.subscribe(consumer)
         drain()
@@ -169,12 +170,12 @@
     }
 
     @Test
-    @Throws(Exception::class)
+    @Suppress("DEPRECATION")
     fun internalCallable_Observable() {
         val value = AtomicReference<Any>(null)
         val tables = arrayOf("a", "b")
         val tableSet: Set<String> = HashSet(listOf(*tables))
-        val flowable = RxRoom.createObservable(mDatabase, false, tables) { value.get() }
+        val flowable = createObservable(mDatabase, false, tables, Callable { value.get() })
         val consumer = CountingConsumer()
         val disposable = flowable.subscribe(consumer)
         drain()
@@ -199,12 +200,15 @@
     }
 
     @Test
-    @Throws(Exception::class)
+    @Suppress("DEPRECATION")
     fun exception_Flowable() {
         val flowable =
-            RxRoom.createFlowable<String>(mDatabase, false, arrayOf("a")) {
-                throw Exception("i want exception")
-            }
+            createFlowable<String>(
+                mDatabase,
+                false,
+                arrayOf("a"),
+                Callable { throw Exception("i want exception") }
+            )
         val subscriber = TestSubscriber<String>()
         flowable.subscribe(subscriber)
         drain()
@@ -212,12 +216,15 @@
     }
 
     @Test
-    @Throws(Exception::class)
+    @Suppress("DEPRECATION")
     fun exception_Observable() {
         val flowable =
-            RxRoom.createObservable<String>(mDatabase, false, arrayOf("a")) {
-                throw Exception("i want exception")
-            }
+            createObservable<String>(
+                mDatabase,
+                false,
+                arrayOf("a"),
+                Callable { throw Exception("i want exception") }
+            )
         val observer = TestObserver<String>()
         flowable.subscribe(observer)
         drain()
diff --git a/sqlite/sqlite-bundled/build.gradle b/sqlite/sqlite-bundled/build.gradle
index 6c4caeb..3bc7daf 100644
--- a/sqlite/sqlite-bundled/build.gradle
+++ b/sqlite/sqlite-bundled/build.gradle
@@ -249,5 +249,4 @@
     inceptionYear = "2023"
     description = "The implementation of SQLite library using the bundled SQLite."
     metalavaK2UastEnabled = false
-    legacyDisableKotlinStrictApiMode = true
 }
diff --git a/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLite.kt b/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLite.kt
index 6adf9ca..30bd8d1 100644
--- a/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLite.kt
+++ b/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLite.kt
@@ -23,39 +23,39 @@
 import kotlin.jvm.JvmName
 
 /** Opens the database in read-only mode. */
-const val SQLITE_OPEN_READONLY = 0x00000001
+public const val SQLITE_OPEN_READONLY: Int = 0x00000001
 
 /** Opens the database for reading and writing. */
-const val SQLITE_OPEN_READWRITE = 0x00000002
+public const val SQLITE_OPEN_READWRITE: Int = 0x00000002
 
 /** Create the database if it does not already exist. */
-const val SQLITE_OPEN_CREATE = 0x00000004
+public const val SQLITE_OPEN_CREATE: Int = 0x00000004
 
 /** Interpret the filename as a URI. */
-const val SQLITE_OPEN_URI = 0x00000040
+public const val SQLITE_OPEN_URI: Int = 0x00000040
 
 /** Opens the database as a in-memory database. */
-const val SQLITE_OPEN_MEMORY = 0x00000080
+public const val SQLITE_OPEN_MEMORY: Int = 0x00000080
 
 /**
  * The database connection will use the "multi-thread" threading mode.
  *
  * See also [SQLite In Multi-Threaded Applications](https://www.sqlite.org/threadsafe.html)
  */
-const val SQLITE_OPEN_NOMUTEX = 0x00008000
+public const val SQLITE_OPEN_NOMUTEX: Int = 0x00008000
 
 /**
  * The database connection will use the "serialized" threading mode.
  *
  * See also [SQLite In Multi-Threaded Applications](https://www.sqlite.org/threadsafe.html)
  */
-const val SQLITE_OPEN_FULLMUTEX = 0x00010000
+public const val SQLITE_OPEN_FULLMUTEX: Int = 0x00010000
 
 /** The filename is not allowed to contain a symbolic link. */
-const val SQLITE_OPEN_NOFOLLOW = 0x01000000
+public const val SQLITE_OPEN_NOFOLLOW: Int = 0x01000000
 
 /** The database connection will use extended result codes. */
-const val SQLITE_OPEN_EXRESCODE = 0x02000000
+public const val SQLITE_OPEN_EXRESCODE: Int = 0x02000000
 
 /** The flags constant that can be used with [BundledSQLiteDriver.open]. */
 @IntDef(
@@ -75,4 +75,4 @@
 )
 @Retention(AnnotationRetention.SOURCE)
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-expect annotation class OpenFlag()
+public expect annotation class OpenFlag()
diff --git a/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteConnection.kt b/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteConnection.kt
index 49b5731..0eec41a 100644
--- a/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteConnection.kt
+++ b/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteConnection.kt
@@ -20,4 +20,5 @@
 import androidx.sqlite.SQLiteConnection
 
 // Restricted instead of internal due to KT-37316
-@RestrictTo(RestrictTo.Scope.LIBRARY) expect class BundledSQLiteConnection : SQLiteConnection
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public expect class BundledSQLiteConnection : SQLiteConnection
diff --git a/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.kt b/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.kt
index b55c476..66ee02b 100644
--- a/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.kt
+++ b/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.kt
@@ -24,14 +24,14 @@
  * A [SQLiteDriver] that uses a bundled version of SQLite included as a native component of this
  * library.
  */
-expect class BundledSQLiteDriver() : SQLiteDriver {
+public expect class BundledSQLiteDriver() : SQLiteDriver {
 
     /**
      * The thread safe mode SQLite was compiled with.
      *
      * See also [SQLite In Multi-Threaded Applications](https://www.sqlite.org/threadsafe.html)
      */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) val threadingMode: Int
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public val threadingMode: Int
 
     /**
      * Opens a new database connection.
@@ -42,5 +42,5 @@
      * @param flags Connection open flags.
      * @return the database connection.
      */
-    fun open(fileName: String, @OpenFlag flags: Int): SQLiteConnection
+    public fun open(fileName: String, @OpenFlag flags: Int): SQLiteConnection
 }
diff --git a/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteStatement.kt b/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteStatement.kt
index a48d56e..7b82a28 100644
--- a/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteStatement.kt
+++ b/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteStatement.kt
@@ -20,4 +20,4 @@
 import androidx.sqlite.SQLiteStatement
 
 // Restricted instead of internal due to KT-37316
-@RestrictTo(RestrictTo.Scope.LIBRARY) expect class BundledSQLiteStatement : SQLiteStatement
+@RestrictTo(RestrictTo.Scope.LIBRARY) public expect class BundledSQLiteStatement : SQLiteStatement
diff --git a/sqlite/sqlite-bundled/src/jvmAndroidMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLite.jvmAndroid.kt b/sqlite/sqlite-bundled/src/jvmAndroidMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLite.jvmAndroid.kt
index 1717770..d283118 100644
--- a/sqlite/sqlite-bundled/src/jvmAndroidMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLite.jvmAndroid.kt
+++ b/sqlite/sqlite-bundled/src/jvmAndroidMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLite.jvmAndroid.kt
@@ -36,7 +36,7 @@
 )
 @Retention(AnnotationRetention.SOURCE)
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-actual annotation class OpenFlag
+public actual annotation class OpenFlag
 
 internal object ResultCode {
     const val SQLITE_MISUSE = 21
diff --git a/sqlite/sqlite-bundled/src/jvmAndroidMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteConnection.jvmAndroid.kt b/sqlite/sqlite-bundled/src/jvmAndroidMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteConnection.jvmAndroid.kt
index 65be6da..3d34667 100644
--- a/sqlite/sqlite-bundled/src/jvmAndroidMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteConnection.jvmAndroid.kt
+++ b/sqlite/sqlite-bundled/src/jvmAndroidMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteConnection.jvmAndroid.kt
@@ -24,7 +24,8 @@
 import androidx.sqlite.throwSQLiteException
 
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-actual class BundledSQLiteConnection(private val connectionPointer: Long) : SQLiteConnection {
+public actual class BundledSQLiteConnection(private val connectionPointer: Long) :
+    SQLiteConnection {
 
     @OptIn(ExperimentalStdlibApi::class) @Volatile private var isClosed = false
 
diff --git a/sqlite/sqlite-bundled/src/jvmAndroidMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.jvmAndroid.kt b/sqlite/sqlite-bundled/src/jvmAndroidMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.jvmAndroid.kt
index de1e6eb..a38a069 100644
--- a/sqlite/sqlite-bundled/src/jvmAndroidMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.jvmAndroid.kt
+++ b/sqlite/sqlite-bundled/src/jvmAndroidMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.jvmAndroid.kt
@@ -27,7 +27,7 @@
  */
 // TODO(b/313895287): Explore usability of @FastNative and @CriticalNative for the external
 // functions.
-actual class BundledSQLiteDriver : SQLiteDriver {
+public actual class BundledSQLiteDriver : SQLiteDriver {
 
     /**
      * The thread safe mode SQLite was compiled with.
@@ -35,7 +35,7 @@
      * See also [SQLite In Multi-Threaded Applications](https://www.sqlite.org/threadsafe.html)
      */
     @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
-    actual val threadingMode: Int
+    public actual val threadingMode: Int
         get() = nativeThreadSafeMode()
 
     override fun open(fileName: String): SQLiteConnection {
@@ -51,7 +51,7 @@
      * @param flags Connection open flags.
      * @return the database connection.
      */
-    actual fun open(fileName: String, @OpenFlag flags: Int): SQLiteConnection {
+    public actual fun open(fileName: String, @OpenFlag flags: Int): SQLiteConnection {
         val address = nativeOpen(fileName, flags)
         return BundledSQLiteConnection(address)
     }
diff --git a/sqlite/sqlite-bundled/src/jvmAndroidMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteStatement.jvmAndroid.kt b/sqlite/sqlite-bundled/src/jvmAndroidMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteStatement.jvmAndroid.kt
index f33c4ab..f2577c5 100644
--- a/sqlite/sqlite-bundled/src/jvmAndroidMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteStatement.jvmAndroid.kt
+++ b/sqlite/sqlite-bundled/src/jvmAndroidMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteStatement.jvmAndroid.kt
@@ -23,7 +23,7 @@
 import androidx.sqlite.throwSQLiteException
 
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-actual class BundledSQLiteStatement(
+public actual class BundledSQLiteStatement(
     private val connectionPointer: Long,
     private val statementPointer: Long
 ) : SQLiteStatement {
@@ -118,7 +118,7 @@
         }
     }
 
-    companion object {
+    private companion object {
         private const val COLUMN_TYPE_INTEGER = 1
         private const val COLUMN_TYPE_FLOAT = 2
         private const val COLUMN_TYPE_TEXT = 3
diff --git a/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLite.nativeCommon.kt b/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLite.nativeCommon.kt
index d3d1d5d..2a65818 100644
--- a/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLite.nativeCommon.kt
+++ b/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLite.nativeCommon.kt
@@ -16,4 +16,4 @@
 
 package androidx.sqlite.driver.bundled
 
-actual typealias OpenFlag = androidx.sqlite.driver.OpenFlag
+public actual typealias OpenFlag = androidx.sqlite.driver.OpenFlag
diff --git a/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteConnection.native.kt b/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteConnection.native.kt
index d466e1a..a368590 100644
--- a/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteConnection.native.kt
+++ b/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteConnection.native.kt
@@ -20,4 +20,4 @@
 
 import androidx.annotation.RestrictTo
 
-actual typealias BundledSQLiteConnection = androidx.sqlite.driver.NativeSQLiteConnection
+public actual typealias BundledSQLiteConnection = androidx.sqlite.driver.NativeSQLiteConnection
diff --git a/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.native.kt b/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.native.kt
index c39d148..4d2be0d 100644
--- a/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.native.kt
+++ b/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.native.kt
@@ -22,4 +22,4 @@
  * A [SQLiteDriver] that uses a bundled version of SQLite included as a native component of this
  * library.
  */
-actual typealias BundledSQLiteDriver = androidx.sqlite.driver.NativeSQLiteDriver
+public actual typealias BundledSQLiteDriver = androidx.sqlite.driver.NativeSQLiteDriver
diff --git a/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteStatement.native.kt b/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteStatement.native.kt
index 92e09eb..7afcd68 100644
--- a/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteStatement.native.kt
+++ b/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteStatement.native.kt
@@ -20,4 +20,4 @@
 
 import androidx.annotation.RestrictTo
 
-actual typealias BundledSQLiteStatement = androidx.sqlite.driver.NativeSQLiteStatement
+public actual typealias BundledSQLiteStatement = androidx.sqlite.driver.NativeSQLiteStatement
diff --git a/sqlite/sqlite-framework/build.gradle b/sqlite/sqlite-framework/build.gradle
index 7ad2380..411b332f 100644
--- a/sqlite/sqlite-framework/build.gradle
+++ b/sqlite/sqlite-framework/build.gradle
@@ -146,7 +146,6 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2017"
     description = "The implementation of SQLite library using the framework code."
-    legacyDisableKotlinStrictApiMode = true
 }
 
 android {
diff --git a/sqlite/sqlite-framework/src/androidMain/kotlin/androidx/sqlite/db/framework/FrameworkSQLiteOpenHelperFactory.android.kt b/sqlite/sqlite-framework/src/androidMain/kotlin/androidx/sqlite/db/framework/FrameworkSQLiteOpenHelperFactory.android.kt
index f4a55ee..fc1617e 100644
--- a/sqlite/sqlite-framework/src/androidMain/kotlin/androidx/sqlite/db/framework/FrameworkSQLiteOpenHelperFactory.android.kt
+++ b/sqlite/sqlite-framework/src/androidMain/kotlin/androidx/sqlite/db/framework/FrameworkSQLiteOpenHelperFactory.android.kt
@@ -20,7 +20,7 @@
 /**
  * Implements [SupportSQLiteOpenHelper.Factory] using the SQLite implementation in the framework.
  */
-class FrameworkSQLiteOpenHelperFactory : SupportSQLiteOpenHelper.Factory {
+public class FrameworkSQLiteOpenHelperFactory : SupportSQLiteOpenHelper.Factory {
     override fun create(
         configuration: SupportSQLiteOpenHelper.Configuration
     ): SupportSQLiteOpenHelper {
diff --git a/sqlite/sqlite-framework/src/androidMain/kotlin/androidx/sqlite/driver/AndroidSQLiteConnection.android.kt b/sqlite/sqlite-framework/src/androidMain/kotlin/androidx/sqlite/driver/AndroidSQLiteConnection.android.kt
index 375a279..75ae53e 100644
--- a/sqlite/sqlite-framework/src/androidMain/kotlin/androidx/sqlite/driver/AndroidSQLiteConnection.android.kt
+++ b/sqlite/sqlite-framework/src/androidMain/kotlin/androidx/sqlite/driver/AndroidSQLiteConnection.android.kt
@@ -24,7 +24,7 @@
 import androidx.sqlite.throwSQLiteException
 
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
-class AndroidSQLiteConnection(val db: SQLiteDatabase) : SQLiteConnection {
+public class AndroidSQLiteConnection(public val db: SQLiteDatabase) : SQLiteConnection {
     override fun prepare(sql: String): SQLiteStatement {
         if (db.isOpen) {
             return AndroidSQLiteStatement.create(db, sql)
diff --git a/sqlite/sqlite-framework/src/androidMain/kotlin/androidx/sqlite/driver/AndroidSQLiteDriver.android.kt b/sqlite/sqlite-framework/src/androidMain/kotlin/androidx/sqlite/driver/AndroidSQLiteDriver.android.kt
index 245be46..9db9d96 100644
--- a/sqlite/sqlite-framework/src/androidMain/kotlin/androidx/sqlite/driver/AndroidSQLiteDriver.android.kt
+++ b/sqlite/sqlite-framework/src/androidMain/kotlin/androidx/sqlite/driver/AndroidSQLiteDriver.android.kt
@@ -23,7 +23,7 @@
 /**
  * A [SQLiteDriver] implemented by [android.database] and that uses the Android's SDK SQLite APIs.
  */
-class AndroidSQLiteDriver : SQLiteDriver {
+public class AndroidSQLiteDriver : SQLiteDriver {
     override fun open(fileName: String): SQLiteConnection {
         val database = SQLiteDatabase.openOrCreateDatabase(fileName, null)
         return AndroidSQLiteConnection(database)
diff --git a/sqlite/sqlite-framework/src/androidMain/kotlin/androidx/sqlite/util/ProcessLock.android.kt b/sqlite/sqlite-framework/src/androidMain/kotlin/androidx/sqlite/util/ProcessLock.android.kt
index ebb9046..706903c 100644
--- a/sqlite/sqlite-framework/src/androidMain/kotlin/androidx/sqlite/util/ProcessLock.android.kt
+++ b/sqlite/sqlite-framework/src/androidMain/kotlin/androidx/sqlite/util/ProcessLock.android.kt
@@ -47,7 +47,7 @@
  *   can be overridden via the [lock] method.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-class ProcessLock(name: String, lockDir: File?, private val processLock: Boolean) {
+public class ProcessLock(name: String, lockDir: File?, private val processLock: Boolean) {
     private val lockFile: File? = lockDir?.let { File(it, "$name.lck") }
     private val threadLock: Lock = getThreadLock(name)
     private var lockChannel: FileChannel? = null
@@ -57,7 +57,7 @@
      *
      * @param [processLock] whether to use file for process level locking or not.
      */
-    fun lock(processLock: Boolean = this.processLock) {
+    public fun lock(processLock: Boolean = this.processLock) {
         threadLock.lock()
         if (processLock) {
             try {
@@ -76,14 +76,14 @@
     }
 
     /** Releases the lock. */
-    fun unlock() {
+    public fun unlock() {
         try {
             lockChannel?.close()
         } catch (ignored: IOException) {}
         threadLock.unlock()
     }
 
-    companion object {
+    private companion object {
         private const val TAG = "SupportSQLiteLock"
         // in-process lock map
         private val threadLocksMap: MutableMap<String, Lock> = HashMap()
diff --git a/sqlite/sqlite-framework/src/nativeMain/kotlin/androidx/sqlite/driver/NativeSQLite.kt b/sqlite/sqlite-framework/src/nativeMain/kotlin/androidx/sqlite/driver/NativeSQLite.kt
index 0e060fa..2534593 100644
--- a/sqlite/sqlite-framework/src/nativeMain/kotlin/androidx/sqlite/driver/NativeSQLite.kt
+++ b/sqlite/sqlite-framework/src/nativeMain/kotlin/androidx/sqlite/driver/NativeSQLite.kt
@@ -52,7 +52,7 @@
 )
 @Retention(AnnotationRetention.SOURCE)
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-annotation class OpenFlag
+public annotation class OpenFlag
 
 internal fun CPointer<sqlite3>.getErrorMsg(): String? {
     return sqlite3_errmsg16(this)?.reinterpret<UShortVar>()?.toKStringFromUtf16()
diff --git a/sqlite/sqlite-framework/src/nativeMain/kotlin/androidx/sqlite/driver/NativeSQLiteConnection.kt b/sqlite/sqlite-framework/src/nativeMain/kotlin/androidx/sqlite/driver/NativeSQLiteConnection.kt
index c3fce96..6ff1dbb 100644
--- a/sqlite/sqlite-framework/src/nativeMain/kotlin/androidx/sqlite/driver/NativeSQLiteConnection.kt
+++ b/sqlite/sqlite-framework/src/nativeMain/kotlin/androidx/sqlite/driver/NativeSQLiteConnection.kt
@@ -35,7 +35,7 @@
 import sqlite3.sqlite3_prepare16_v2
 
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // For actual typealias in unbundled
-class NativeSQLiteConnection(private val dbPointer: CPointer<sqlite3>) : SQLiteConnection {
+public class NativeSQLiteConnection(private val dbPointer: CPointer<sqlite3>) : SQLiteConnection {
 
     @OptIn(ExperimentalStdlibApi::class) @Volatile private var isClosed = false
 
diff --git a/sqlite/sqlite-framework/src/nativeMain/kotlin/androidx/sqlite/driver/NativeSQLiteDriver.kt b/sqlite/sqlite-framework/src/nativeMain/kotlin/androidx/sqlite/driver/NativeSQLiteDriver.kt
index 5040a1e..ebfe6d8 100644
--- a/sqlite/sqlite-framework/src/nativeMain/kotlin/androidx/sqlite/driver/NativeSQLiteDriver.kt
+++ b/sqlite/sqlite-framework/src/nativeMain/kotlin/androidx/sqlite/driver/NativeSQLiteDriver.kt
@@ -36,7 +36,7 @@
  *
  * Usage of this driver expects that `libsqlite` can be found in the shared library path.
  */
-class NativeSQLiteDriver : SQLiteDriver {
+public class NativeSQLiteDriver : SQLiteDriver {
 
     /**
      * The thread safe mode SQLite was compiled with.
@@ -44,7 +44,7 @@
      * See also [SQLite In Multi-Threaded Applications](https://www.sqlite.org/threadsafe.html)
      */
     @get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
-    val threadingMode: Int
+    public val threadingMode: Int
         get() = sqlite3_threadsafe()
 
     override fun open(fileName: String): SQLiteConnection {
@@ -60,7 +60,7 @@
      * @param flags Connection open flags.
      * @return the database connection.
      */
-    fun open(fileName: String, @OpenFlag flags: Int): SQLiteConnection = memScoped {
+    public fun open(fileName: String, @OpenFlag flags: Int): SQLiteConnection = memScoped {
         val dbPointer = allocPointerTo<sqlite3>()
         val resultCode =
             sqlite3_open_v2(filename = fileName, ppDb = dbPointer.ptr, flags = flags, zVfs = null)
diff --git a/sqlite/sqlite-framework/src/nativeMain/kotlin/androidx/sqlite/driver/NativeSQLiteStatement.kt b/sqlite/sqlite-framework/src/nativeMain/kotlin/androidx/sqlite/driver/NativeSQLiteStatement.kt
index 1995de0..dfd2829 100644
--- a/sqlite/sqlite-framework/src/nativeMain/kotlin/androidx/sqlite/driver/NativeSQLiteStatement.kt
+++ b/sqlite/sqlite-framework/src/nativeMain/kotlin/androidx/sqlite/driver/NativeSQLiteStatement.kt
@@ -57,7 +57,7 @@
 import sqlite3.sqlite3_step
 
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // For actual typealias in unbundled
-class NativeSQLiteStatement(
+public class NativeSQLiteStatement(
     private val dbPointer: CPointer<sqlite3>,
     private val stmtPointer: CPointer<sqlite3_stmt>
 ) : SQLiteStatement {
diff --git a/sqlite/sqlite-ktx/build.gradle b/sqlite/sqlite-ktx/build.gradle
index b03f306..90d69c5 100644
--- a/sqlite/sqlite-ktx/build.gradle
+++ b/sqlite/sqlite-ktx/build.gradle
@@ -42,7 +42,6 @@
     type = LibraryType.PUBLISHED_LIBRARY_ONLY_USED_BY_KOTLIN_CONSUMERS
     inceptionYear = "2018"
     description = "Kotlin extensions for DB"
-    legacyDisableKotlinStrictApiMode = true
 }
 
 android {
diff --git a/sqlite/sqlite-ktx/src/main/java/androidx/sqlite/db/SupportSQLiteDatabaseExt.kt b/sqlite/sqlite-ktx/src/main/java/androidx/sqlite/db/SupportSQLiteDatabaseExt.kt
index e637a991..51d244f 100644
--- a/sqlite/sqlite-ktx/src/main/java/androidx/sqlite/db/SupportSQLiteDatabaseExt.kt
+++ b/sqlite/sqlite-ktx/src/main/java/androidx/sqlite/db/SupportSQLiteDatabaseExt.kt
@@ -23,7 +23,7 @@
  * @param exclusive Run in `EXCLUSIVE` mode when true, `IMMEDIATE` mode otherwise.
  * @param body Lambda to be run in the transaction.
  */
-inline fun <T> SupportSQLiteDatabase.transaction(
+public inline fun <T> SupportSQLiteDatabase.transaction(
     exclusive: Boolean = true,
     body: SupportSQLiteDatabase.() -> T
 ): T {
diff --git a/sqlite/sqlite/build.gradle b/sqlite/sqlite/build.gradle
index 59c4baf..3c4a438 100644
--- a/sqlite/sqlite/build.gradle
+++ b/sqlite/sqlite/build.gradle
@@ -90,5 +90,4 @@
     type = LibraryType.PUBLISHED_LIBRARY
     inceptionYear = "2017"
     description = "SQLite API"
-    legacyDisableKotlinStrictApiMode = true
 }
diff --git a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/SQLiteException.android.kt b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/SQLiteException.android.kt
index febb4af..6401e1f 100644
--- a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/SQLiteException.android.kt
+++ b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/SQLiteException.android.kt
@@ -15,4 +15,4 @@
  */
 package androidx.sqlite
 
-actual typealias SQLiteException = android.database.SQLException
+public actual typealias SQLiteException = android.database.SQLException
diff --git a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SimpleSQLiteQuery.android.kt b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SimpleSQLiteQuery.android.kt
index bbe86d9..1939a45 100644
--- a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SimpleSQLiteQuery.android.kt
+++ b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SimpleSQLiteQuery.android.kt
@@ -24,7 +24,7 @@
  * @constructor Creates an SQL query with the sql string and the bind arguments.
  */
 @Suppress("AcronymName") // SQL is a known term and should remain capitalized
-class SimpleSQLiteQuery(
+public class SimpleSQLiteQuery(
     private val query: String,
     @Suppress("ArrayReturn") // Due to legacy API
     private val bindArgs: Array<out Any?>?
@@ -35,7 +35,7 @@
      *
      * @param query The SQL query to execute. Cannot include bind parameters.
      */
-    constructor(query: String) : this(query, null)
+    public constructor(query: String) : this(query, null)
 
     override val sql: String
         get() = this.query
@@ -53,7 +53,7 @@
     override val argCount: Int
         get() = bindArgs?.size ?: 0
 
-    companion object {
+    public companion object {
         /**
          * Binds the given arguments into the given sqlite statement.
          *
@@ -61,7 +61,7 @@
          * @param [bindArgs] The list of bind arguments
          */
         @JvmStatic
-        fun bind(
+        public fun bind(
             @Suppress("AcronymName") // SQL is a known term and should remain capitalized
             statement: SupportSQLiteProgram,
             @Suppress("ArrayReturn") // Due to legacy API
diff --git a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteCompat.android.kt b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteCompat.android.kt
index 856d8ba..8e56dc1 100644
--- a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteCompat.android.kt
+++ b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteCompat.android.kt
@@ -26,11 +26,11 @@
 
 /** Helper for accessing features in [SupportSQLiteOpenHelper]. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-class SupportSQLiteCompat private constructor() {
+public class SupportSQLiteCompat private constructor() {
     /** Helper for accessing functions that require SDK version 21 and higher. */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     @RequiresApi(21)
-    object Api21Impl {
+    public object Api21Impl {
         /**
          * Returns the absolute path to the directory on the filesystem.
          *
@@ -39,7 +39,7 @@
          */
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         @JvmStatic
-        fun getNoBackupFilesDir(context: Context): File {
+        public fun getNoBackupFilesDir(context: Context): File {
             return context.noBackupFilesDir
         }
     }
@@ -47,7 +47,7 @@
     /** Helper for accessing functions that require SDK version 23 and higher. */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     @RequiresApi(23)
-    object Api23Impl {
+    public object Api23Impl {
         /**
          * Sets a [Bundle] that will be returned by [Cursor.getExtras].
          *
@@ -55,7 +55,7 @@
          */
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         @JvmStatic
-        fun setExtras(cursor: Cursor, extras: Bundle) {
+        public fun setExtras(cursor: Cursor, extras: Bundle) {
             cursor.extras = extras
         }
     }
@@ -63,7 +63,7 @@
     /** Helper for accessing functions that require SDK version 29 and higher. */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     @RequiresApi(29)
-    object Api29Impl {
+    public object Api29Impl {
         /**
          * Similar to [Cursor.setNotificationUri], except this version allows to watch multiple
          * content URIs for changes.
@@ -74,7 +74,7 @@
          */
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         @JvmStatic
-        fun setNotificationUris(cursor: Cursor, cr: ContentResolver, uris: List<Uri?>) {
+        public fun setNotificationUris(cursor: Cursor, cr: ContentResolver, uris: List<Uri?>) {
             cursor.setNotificationUris(cr, uris)
         }
 
@@ -88,7 +88,7 @@
          */
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         @JvmStatic
-        fun getNotificationUris(cursor: Cursor): List<Uri> {
+        public fun getNotificationUris(cursor: Cursor): List<Uri> {
             return cursor.notificationUris!!
         }
     }
diff --git a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteDatabase.android.kt b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteDatabase.android.kt
index 4f4e98c..5c6d9dd 100644
--- a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteDatabase.android.kt
+++ b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteDatabase.android.kt
@@ -30,14 +30,14 @@
  * versions. It mimics the behavior of [android.database.sqlite.SQLiteDatabase]
  */
 @Suppress("AcronymName") // SQL is a known term and should remain capitalized
-interface SupportSQLiteDatabase : Closeable {
+public interface SupportSQLiteDatabase : Closeable {
     /**
      * Compiles the given SQL statement.
      *
      * @param sql The sql query.
      * @return Compiled statement.
      */
-    fun compileStatement(sql: String): SupportSQLiteStatement
+    public fun compileStatement(sql: String): SupportSQLiteStatement
 
     /**
      * Begins a transaction in EXCLUSIVE mode.
@@ -58,7 +58,7 @@
      *  }
      * ```
      */
-    fun beginTransaction()
+    public fun beginTransaction()
 
     /**
      * Begins a transaction in IMMEDIATE mode. Transactions can be nested. When the outer
@@ -78,7 +78,7 @@
      *  }
      *  ```
      */
-    fun beginTransactionNonExclusive()
+    public fun beginTransactionNonExclusive()
 
     /**
      * Begins a transaction in DEFERRED mode, with the android-specific constraint that the
@@ -107,7 +107,7 @@
      * If the implementation does not support read-only transactions then the default implementation
      * delegates to [beginTransaction].
      */
-    fun beginTransactionReadOnly() {
+    public fun beginTransactionReadOnly() {
         beginTransaction()
     }
 
@@ -133,7 +133,7 @@
      * @param transactionListener listener that should be notified when the transaction begins,
      *   commits, or is rolled back, either explicitly or by a call to [yieldIfContendedSafely].
      */
-    fun beginTransactionWithListener(transactionListener: SQLiteTransactionListener)
+    public fun beginTransactionWithListener(transactionListener: SQLiteTransactionListener)
 
     /**
      * Begins a transaction in IMMEDIATE mode. Transactions can be nested. When the outer
@@ -156,7 +156,9 @@
      * @param transactionListener listener that should be notified when the transaction begins,
      *   commits, or is rolled back, either explicitly or by a call to [yieldIfContendedSafely].
      */
-    fun beginTransactionWithListenerNonExclusive(transactionListener: SQLiteTransactionListener)
+    public fun beginTransactionWithListenerNonExclusive(
+        transactionListener: SQLiteTransactionListener
+    )
 
     /**
      * Begins a transaction in read-only mode with a {@link SQLiteTransactionListener} listener. The
@@ -182,7 +184,9 @@
      * delegates to [beginTransactionWithListener].
      */
     @Suppress("ExecutorRegistration")
-    fun beginTransactionWithListenerReadOnly(transactionListener: SQLiteTransactionListener) {
+    public fun beginTransactionWithListenerReadOnly(
+        transactionListener: SQLiteTransactionListener
+    ) {
         beginTransactionWithListener(transactionListener)
     }
 
@@ -190,7 +194,7 @@
      * End a transaction. See beginTransaction for notes about how to use this and when transactions
      * are committed and rolled back.
      */
-    fun endTransaction()
+    public fun endTransaction()
 
     /**
      * Marks the current transaction as successful. Do not do any more database work between calling
@@ -201,14 +205,14 @@
      * @throws IllegalStateException if the current thread is not in a transaction or the
      *   transaction is already marked as successful.
      */
-    fun setTransactionSuccessful()
+    public fun setTransactionSuccessful()
 
     /**
      * Returns true if the current thread has a transaction pending.
      *
      * @return True if the current thread is in a transaction.
      */
-    fun inTransaction(): Boolean
+    public fun inTransaction(): Boolean
 
     /**
      * True if the current thread is holding an active connection to the database.
@@ -218,7 +222,7 @@
      * longer a true "database lock" although threads may block if they cannot acquire a database
      * connection to perform a particular operation.
      */
-    val isDbLockedByCurrentThread: Boolean
+    public val isDbLockedByCurrentThread: Boolean
 
     /**
      * Temporarily end the transaction to let other threads run. The transaction is assumed to be
@@ -229,7 +233,7 @@
      *
      * @return true if the transaction was yielded
      */
-    fun yieldIfContendedSafely(): Boolean
+    public fun yieldIfContendedSafely(): Boolean
 
     /**
      * Temporarily end the transaction to let other threads run. The transaction is assumed to be
@@ -243,11 +247,11 @@
      *   more progress than they would if we started the transaction immediately.
      * @return true if the transaction was yielded
      */
-    fun yieldIfContendedSafely(sleepAfterYieldDelayMillis: Long): Boolean
+    public fun yieldIfContendedSafely(sleepAfterYieldDelayMillis: Long): Boolean
 
     /** Is true if [execPerConnectionSQL] is supported by the implementation. */
     @get:Suppress("AcronymName") // To keep consistency with framework method name.
-    val isExecPerConnectionSQLSupported: Boolean
+    public val isExecPerConnectionSQLSupported: Boolean
         get() = false
 
     /**
@@ -272,15 +276,18 @@
      *   supported use [isExecPerConnectionSQLSupported]
      */
     @Suppress("AcronymName") // To keep consistency with framework method name.
-    fun execPerConnectionSQL(sql: String, @SuppressLint("ArrayReturn") bindArgs: Array<out Any?>?) {
+    public fun execPerConnectionSQL(
+        sql: String,
+        @SuppressLint("ArrayReturn") bindArgs: Array<out Any?>?
+    ) {
         throw UnsupportedOperationException()
     }
 
     /** The database version. */
-    var version: Int
+    public var version: Int
 
     /** The maximum size the database may grow to. */
-    val maximumSize: Long
+    public val maximumSize: Long
 
     /**
      * Sets the maximum size the database will grow to. The maximum size cannot be set below the
@@ -289,7 +296,7 @@
      * @param numBytes the maximum database size, in bytes
      * @return the new maximum database size
      */
-    fun setMaximumSize(numBytes: Long): Long
+    public fun setMaximumSize(numBytes: Long): Long
 
     /**
      * The current database page size, in bytes.
@@ -297,7 +304,7 @@
      * The page size must be a power of two. This method does not work if any data has been written
      * to the database file, and must be called right after the database has been created.
      */
-    var pageSize: Long
+    public var pageSize: Long
 
     /**
      * Runs the given query on the database. If you would like to have typed bind arguments, use
@@ -308,7 +315,7 @@
      * @return A [Cursor] object, which is positioned before the first entry. Note that [Cursor]s
      *   are not synchronized, see the documentation for more details.
      */
-    fun query(query: String): Cursor
+    public fun query(query: String): Cursor
 
     /**
      * Runs the given query on the database. If you would like to have bind arguments, use [query].
@@ -319,7 +326,7 @@
      * @return A [Cursor] object, which is positioned before the first entry. Note that [Cursor]s
      *   are not synchronized, see the documentation for more details.
      */
-    fun query(query: String, bindArgs: Array<out Any?>): Cursor
+    public fun query(query: String, bindArgs: Array<out Any?>): Cursor
 
     /**
      * Runs the given query on the database.
@@ -331,7 +338,7 @@
      * @return A [Cursor] object, which is positioned before the first entry. Note that [Cursor]s
      *   are not synchronized, see the documentation for more details.
      */
-    fun query(query: SupportSQLiteQuery): Cursor
+    public fun query(query: SupportSQLiteQuery): Cursor
 
     /**
      * Runs the given query on the database.
@@ -346,7 +353,7 @@
      * @return A [Cursor] object, which is positioned before the first entry. Note that [Cursor]s
      *   are not synchronized, see the documentation for more details.
      */
-    fun query(query: SupportSQLiteQuery, cancellationSignal: CancellationSignal?): Cursor
+    public fun query(query: SupportSQLiteQuery, cancellationSignal: CancellationSignal?): Cursor
 
     /**
      * Convenience method for inserting a row into the database.
@@ -365,7 +372,7 @@
      * @throws SQLException If the insert fails
      */
     @Throws(SQLException::class)
-    fun insert(table: String, conflictAlgorithm: Int, values: ContentValues): Long
+    public fun insert(table: String, conflictAlgorithm: Int, values: ContentValues): Long
 
     /**
      * Convenience method for deleting rows in the database.
@@ -378,7 +385,7 @@
      * @return the number of rows affected if a whereClause is passed in, 0 otherwise. To remove all
      *   rows and get a count pass "1" as the whereClause.
      */
-    fun delete(table: String, whereClause: String?, whereArgs: Array<out Any?>?): Int
+    public fun delete(table: String, whereClause: String?, whereArgs: Array<out Any?>?): Int
 
     /**
      * Convenience method for updating rows in the database.
@@ -399,7 +406,7 @@
      *   from whereArgs. The values will be bound as Strings.
      * @return the number of rows affected
      */
-    fun update(
+    public fun update(
         table: String,
         conflictAlgorithm: Int,
         values: ContentValues,
@@ -420,7 +427,7 @@
      */
     @Suppress("AcronymName") // SQL is a known term and should remain capitalized
     @Throws(SQLException::class)
-    fun execSQL(sql: String)
+    public fun execSQL(sql: String)
 
     /**
      * Execute a single SQL statement that does not return any data.
@@ -436,13 +443,13 @@
      */
     @Suppress("AcronymName") // SQL is a known term and should remain capitalized
     @Throws(SQLException::class)
-    fun execSQL(sql: String, bindArgs: Array<out Any?>)
+    public fun execSQL(sql: String, bindArgs: Array<out Any?>)
 
     /** Is true if the database is opened as read only. */
-    val isReadOnly: Boolean
+    public val isReadOnly: Boolean
 
     /** Is true if the database is currently open. */
-    val isOpen: Boolean
+    public val isOpen: Boolean
 
     /**
      * Returns true if the new version code is greater than the current database version.
@@ -450,10 +457,10 @@
      * @param newVersion The new version code.
      * @return True if the new version code is greater than the current database version.
      */
-    fun needUpgrade(newVersion: Int): Boolean
+    public fun needUpgrade(newVersion: Int): Boolean
 
     /** The path to the database file. */
-    val path: String?
+    public val path: String?
 
     /**
      * Sets the locale for this database. Does nothing if this database has the
@@ -465,7 +472,7 @@
      *   there is no collator available for the locale you requested. In this case the database
      *   remains unchanged.
      */
-    fun setLocale(locale: Locale)
+    public fun setLocale(locale: Locale)
 
     /**
      * Sets the maximum size of the prepared-statement cache for this database. (size of the cache =
@@ -482,7 +489,7 @@
      * @throws IllegalStateException if input cacheSize is over the max.
      *   [android.database.sqlite.SQLiteDatabase.MAX_SQL_CACHE_SIZE].
      */
-    fun setMaxSqlCacheSize(cacheSize: Int)
+    public fun setMaxSqlCacheSize(cacheSize: Int)
 
     /**
      * Sets whether foreign key constraints are enabled for the database.
@@ -509,7 +516,7 @@
      * @throws IllegalStateException if the are transactions is in progress when this method is
      *   called.
      */
-    fun setForeignKeyConstraintsEnabled(enabled: Boolean)
+    public fun setForeignKeyConstraintsEnabled(enabled: Boolean)
 
     /**
      * This method enables parallel execution of queries from multiple threads on the same database.
@@ -573,7 +580,7 @@
      * @throws IllegalStateException if there are transactions in progress at the time this method
      *   is called. WAL mode can only be changed when there are no transactions in progress.
      */
-    fun enableWriteAheadLogging(): Boolean
+    public fun enableWriteAheadLogging(): Boolean
 
     /**
      * This method disables the features enabled by [enableWriteAheadLogging].
@@ -581,20 +588,20 @@
      * @throws IllegalStateException if there are transactions in progress at the time this method
      *   is called. WAL mode can only be changed when there are no transactions in progress.
      */
-    fun disableWriteAheadLogging()
+    public fun disableWriteAheadLogging()
 
     /** Is true if write-ahead logging has been enabled for this database. */
-    val isWriteAheadLoggingEnabled: Boolean
+    public val isWriteAheadLoggingEnabled: Boolean
 
     /**
      * The list of full path names of all attached databases including the main database by
      * executing 'pragma database_list' on the database.
      */
-    @get:Suppress("NullableCollection") val attachedDbs: List<Pair<String, String>>?
+    @get:Suppress("NullableCollection") public val attachedDbs: List<Pair<String, String>>?
 
     /**
      * Is true if the given database (and all its attached databases) pass integrity_check, false
      * otherwise.
      */
-    val isDatabaseIntegrityOk: Boolean
+    public val isDatabaseIntegrityOk: Boolean
 }
diff --git a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteOpenHelper.android.kt b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteOpenHelper.android.kt
index 90aee80..2be3a94 100644
--- a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteOpenHelper.android.kt
+++ b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteOpenHelper.android.kt
@@ -32,12 +32,12 @@
  * create this and [Callback] to implement the methods that should be overridden.
  */
 @Suppress("AcronymName") // SQL is a known term and should remain capitalized
-interface SupportSQLiteOpenHelper : Closeable {
+public interface SupportSQLiteOpenHelper : Closeable {
     /**
      * Return the name of the SQLite database being opened, as given to the constructor. `null`
      * indicates an in-memory database.
      */
-    val databaseName: String?
+    public val databaseName: String?
 
     /**
      * Enables or disables the use of write-ahead logging for the database.
@@ -49,7 +49,7 @@
      *
      * @param enabled True if write-ahead logging should be enabled, false if it should be disabled.
      */
-    fun setWriteAheadLoggingEnabled(enabled: Boolean)
+    public fun setWriteAheadLoggingEnabled(enabled: Boolean)
 
     /**
      * Create and/or open a database that will be used for reading and writing. The first time this
@@ -67,7 +67,7 @@
      * @return a read/write database object valid until [close] is called
      * @throws SQLiteException if the database cannot be opened for writing
      */
-    val writableDatabase: SupportSQLiteDatabase
+    public val writableDatabase: SupportSQLiteDatabase
 
     /**
      * Create and/or open a database. This will be the same object returned by [writableDatabase]
@@ -82,7 +82,7 @@
      * @return a database object valid until [writableDatabase] or [close] is called.
      * @throws SQLiteException if the database cannot be opened
      */
-    val readableDatabase: SupportSQLiteDatabase
+    public val readableDatabase: SupportSQLiteDatabase
 
     /** Close any open database object. */
     override fun close()
@@ -93,13 +93,13 @@
      * Handles various lifecycle events for the SQLite connection, similar to
      * [room-runtime.SQLiteOpenHelper].
      */
-    abstract class Callback(
+    public abstract class Callback(
         /**
          * Version number of the database (starting at 1); if the database is older,
          * [Callback.onUpgrade] will be used to upgrade the database; if the database is newer,
          * [Callback.onDowngrade] will be used to downgrade the database.
          */
-        @JvmField val version: Int
+        @JvmField public val version: Int
     ) {
         /**
          * Called when the database connection is being configured, to enable features such as
@@ -117,7 +117,7 @@
          *
          * @param db The database.
          */
-        open fun onConfigure(db: SupportSQLiteDatabase) {}
+        public open fun onConfigure(db: SupportSQLiteDatabase) {}
 
         /**
          * Called when the database is created for the first time. This is where the creation of
@@ -125,7 +125,7 @@
          *
          * @param db The database.
          */
-        abstract fun onCreate(db: SupportSQLiteDatabase)
+        public abstract fun onCreate(db: SupportSQLiteDatabase)
 
         /**
          * Called when the database needs to be upgraded. The implementation should use this method
@@ -145,7 +145,7 @@
          * @param oldVersion The old database version.
          * @param newVersion The new database version.
          */
-        abstract fun onUpgrade(db: SupportSQLiteDatabase, oldVersion: Int, newVersion: Int)
+        public abstract fun onUpgrade(db: SupportSQLiteDatabase, oldVersion: Int, newVersion: Int)
 
         /**
          * Called when the database needs to be downgraded. This is strictly similar to [onUpgrade]
@@ -160,7 +160,7 @@
          * @param oldVersion The old database version.
          * @param newVersion The new database version.
          */
-        open fun onDowngrade(db: SupportSQLiteDatabase, oldVersion: Int, newVersion: Int) {
+        public open fun onDowngrade(db: SupportSQLiteDatabase, oldVersion: Int, newVersion: Int) {
             throw SQLiteException(
                 "Can't downgrade database from version $oldVersion to $newVersion"
             )
@@ -177,7 +177,7 @@
          *
          * @param db The database.
          */
-        open fun onOpen(db: SupportSQLiteDatabase) {}
+        public open fun onOpen(db: SupportSQLiteDatabase) {}
 
         /**
          * The method invoked when database corruption is detected. Default implementation will
@@ -186,7 +186,7 @@
          * @param db the [SupportSQLiteDatabase] object representing the database on which
          *   corruption is detected.
          */
-        open fun onCorruption(db: SupportSQLiteDatabase) {
+        public open fun onCorruption(db: SupportSQLiteDatabase) {
             // the following implementation is taken from {@link DefaultDatabaseErrorHandler}.
             Log.e(TAG, "Corruption reported by sqlite on database: $db.path")
             // is the corruption detected even before database could be 'opened'?
@@ -245,26 +245,26 @@
     }
 
     /** The configuration to create an SQLite open helper object using [Factory]. */
-    class Configuration
+    public class Configuration
     @Suppress("ExecutorRegistration") // For backwards compatibility
     constructor(
         /** Context to use to open or create the database. */
-        @JvmField val context: Context,
+        @JvmField public val context: Context,
         /** Name of the database file, or null for an in-memory database. */
-        @JvmField val name: String?,
+        @JvmField public val name: String?,
         /** The callback class to handle creation, upgrade and downgrade. */
-        @JvmField val callback: Callback,
+        @JvmField public val callback: Callback,
         /** If `true` the database will be stored in the no-backup directory. */
-        @JvmField @Suppress("ListenerLast") val useNoBackupDirectory: Boolean = false,
+        @JvmField @Suppress("ListenerLast") public val useNoBackupDirectory: Boolean = false,
         /**
          * If `true` the database will be delete and its data loss in the case that it cannot be
          * opened.
          */
-        @JvmField @Suppress("ListenerLast") val allowDataLossOnRecovery: Boolean = false
+        @JvmField @Suppress("ListenerLast") public val allowDataLossOnRecovery: Boolean = false
     ) {
 
         /** Builder class for [Configuration]. */
-        open class Builder internal constructor(context: Context) {
+        public open class Builder internal constructor(context: Context) {
             private val context: Context
             private var name: String? = null
             private var callback: Callback? = null
@@ -281,7 +281,7 @@
              *
              * @return The [Configuration] instance
              */
-            open fun build(): Configuration {
+            public open fun build(): Configuration {
                 val callback = callback
                 requireNotNull(callback) { "Must set a callback to create the configuration." }
                 require(!useNoBackupDirectory || !name.isNullOrEmpty()) {
@@ -305,13 +305,15 @@
              * @param name Name of the database file, or null for an in-memory database.
              * @return This builder instance.
              */
-            open fun name(name: String?): Builder = apply { this.name = name }
+            public open fun name(name: String?): Builder = apply { this.name = name }
 
             /**
              * @param callback The callback class to handle creation, upgrade and downgrade.
              * @return This builder instance.
              */
-            open fun callback(callback: Callback): Builder = apply { this.callback = callback }
+            public open fun callback(callback: Callback): Builder = apply {
+                this.callback = callback
+            }
 
             /**
              * Sets whether to use a no backup directory or not.
@@ -320,7 +322,7 @@
              *   no-backup directory.
              * @return This builder instance.
              */
-            open fun noBackupDirectory(useNoBackupDirectory: Boolean): Builder = apply {
+            public open fun noBackupDirectory(useNoBackupDirectory: Boolean): Builder = apply {
                 this.useNoBackupDirectory = useNoBackupDirectory
             }
 
@@ -332,32 +334,33 @@
              *   case that it cannot be opened.
              * @return this
              */
-            open fun allowDataLossOnRecovery(allowDataLossOnRecovery: Boolean): Builder = apply {
-                this.allowDataLossOnRecovery = allowDataLossOnRecovery
-            }
+            public open fun allowDataLossOnRecovery(allowDataLossOnRecovery: Boolean): Builder =
+                apply {
+                    this.allowDataLossOnRecovery = allowDataLossOnRecovery
+                }
         }
 
-        companion object {
+        public companion object {
             /**
              * Creates a new Configuration.Builder to create an instance of Configuration.
              *
              * @param context to use to open or create the database.
              */
             @JvmStatic
-            fun builder(context: Context): Builder {
+            public fun builder(context: Context): Builder {
                 return Builder(context)
             }
         }
     }
 
     /** Factory class to create instances of [SupportSQLiteOpenHelper] using [Configuration]. */
-    fun interface Factory {
+    public fun interface Factory {
         /**
          * Creates an instance of [SupportSQLiteOpenHelper] using the given configuration.
          *
          * @param configuration The configuration to use while creating the open helper.
          * @return A SupportSQLiteOpenHelper which can be used to open a database.
          */
-        fun create(configuration: Configuration): SupportSQLiteOpenHelper
+        public fun create(configuration: Configuration): SupportSQLiteOpenHelper
     }
 }
diff --git a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteProgram.android.kt b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteProgram.android.kt
index 9bff8c4..572879d 100644
--- a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteProgram.android.kt
+++ b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteProgram.android.kt
@@ -19,14 +19,14 @@
 
 /** An interface to map the behavior of [android.database.sqlite.SQLiteProgram]. */
 @Suppress("AcronymName") // SQL is a known term and should remain capitalized
-interface SupportSQLiteProgram : Closeable {
+public interface SupportSQLiteProgram : Closeable {
     /**
      * Bind a NULL value to this statement. The value remains bound until [.clearBindings] is
      * called.
      *
      * @param index The 1-based index to the parameter to bind null to
      */
-    fun bindNull(index: Int)
+    public fun bindNull(index: Int)
 
     /**
      * Bind a long value to this statement. The value remains bound until [clearBindings] is called.
@@ -35,7 +35,7 @@
      * @param index The 1-based index to the parameter to bind
      * @param value The value to bind
      */
-    fun bindLong(index: Int, value: Long)
+    public fun bindLong(index: Int, value: Long)
 
     /**
      * Bind a double value to this statement. The value remains bound until [.clearBindings] is
@@ -44,7 +44,7 @@
      * @param index The 1-based index to the parameter to bind
      * @param value The value to bind
      */
-    fun bindDouble(index: Int, value: Double)
+    public fun bindDouble(index: Int, value: Double)
 
     /**
      * Bind a String value to this statement. The value remains bound until [.clearBindings] is
@@ -53,7 +53,7 @@
      * @param index The 1-based index to the parameter to bind
      * @param value The value to bind, must not be null
      */
-    fun bindString(index: Int, value: String)
+    public fun bindString(index: Int, value: String)
 
     /**
      * Bind a byte array value to this statement. The value remains bound until [.clearBindings] is
@@ -62,8 +62,8 @@
      * @param index The 1-based index to the parameter to bind
      * @param value The value to bind, must not be null
      */
-    fun bindBlob(index: Int, value: ByteArray)
+    public fun bindBlob(index: Int, value: ByteArray)
 
     /** Clears all existing bindings. Unset bindings are treated as NULL. */
-    fun clearBindings()
+    public fun clearBindings()
 }
diff --git a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteQuery.android.kt b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteQuery.android.kt
index b5ffb7e..3db42f5 100644
--- a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteQuery.android.kt
+++ b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteQuery.android.kt
@@ -20,20 +20,20 @@
  * [android.database.sqlite.SQLiteDatabase.rawQuery] because it allows binding type safe parameters.
  */
 @Suppress("AcronymName") // SQL is a known term and should remain capitalized
-interface SupportSQLiteQuery {
+public interface SupportSQLiteQuery {
     /** The SQL query. This query can have placeholders(?) for bind arguments. */
-    val sql: String
+    public val sql: String
 
     /**
      * Callback to bind the query parameters to the compiled statement.
      *
      * @param statement The compiled statement
      */
-    fun bindTo(statement: SupportSQLiteProgram)
+    public fun bindTo(statement: SupportSQLiteProgram)
 
     /**
      * Is the number of arguments in this query. This is equal to the number of placeholders in the
      * query string. See: https://www.sqlite.org/c3ref/bind_blob.html for details.
      */
-    val argCount: Int
+    public val argCount: Int
 }
diff --git a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteQueryBuilder.android.kt b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteQueryBuilder.android.kt
index 60f5817..770820c 100644
--- a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteQueryBuilder.android.kt
+++ b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteQueryBuilder.android.kt
@@ -19,7 +19,7 @@
 
 /** A simple query builder to create SQL SELECT queries. */
 @Suppress("AcronymName") // SQL is a known term and should remain capitalized
-class SupportSQLiteQueryBuilder private constructor(private val table: String) {
+public class SupportSQLiteQueryBuilder private constructor(private val table: String) {
     private var distinct = false
     private var columns: Array<out String>? = null
     private var selection: String? = null
@@ -34,7 +34,7 @@
      *
      * @return this
      */
-    fun distinct(): SupportSQLiteQueryBuilder = apply { this.distinct = true }
+    public fun distinct(): SupportSQLiteQueryBuilder = apply { this.distinct = true }
 
     /**
      * Sets the given list of columns as the columns that will be returned.
@@ -42,7 +42,7 @@
      * @param columns The list of column names that should be returned.
      * @return this
      */
-    fun columns(columns: Array<out String>?): SupportSQLiteQueryBuilder = apply {
+    public fun columns(columns: Array<out String>?): SupportSQLiteQueryBuilder = apply {
         this.columns = columns
     }
 
@@ -53,11 +53,13 @@
      * @param bindArgs The list of bind arguments to match against these columns
      * @return this
      */
-    fun selection(selection: String?, bindArgs: Array<out Any?>?): SupportSQLiteQueryBuilder =
-        apply {
-            this.selection = selection
-            this.bindArgs = bindArgs
-        }
+    public fun selection(
+        selection: String?,
+        bindArgs: Array<out Any?>?
+    ): SupportSQLiteQueryBuilder = apply {
+        this.selection = selection
+        this.bindArgs = bindArgs
+    }
 
     /**
      * Adds a GROUP BY statement.
@@ -65,7 +67,9 @@
      * @param groupBy The value of the GROUP BY statement.
      * @return this
      */
-    fun groupBy(groupBy: String?): SupportSQLiteQueryBuilder = apply { this.groupBy = groupBy }
+    public fun groupBy(groupBy: String?): SupportSQLiteQueryBuilder = apply {
+        this.groupBy = groupBy
+    }
 
     /**
      * Adds a HAVING statement. You must also provide [groupBy] for this to work.
@@ -73,7 +77,7 @@
      * @param having The having clause.
      * @return this
      */
-    fun having(having: String?): SupportSQLiteQueryBuilder = apply { this.having = having }
+    public fun having(having: String?): SupportSQLiteQueryBuilder = apply { this.having = having }
 
     /**
      * Adds an ORDER BY statement.
@@ -81,7 +85,9 @@
      * @param orderBy The order clause.
      * @return this
      */
-    fun orderBy(orderBy: String?): SupportSQLiteQueryBuilder = apply { this.orderBy = orderBy }
+    public fun orderBy(orderBy: String?): SupportSQLiteQueryBuilder = apply {
+        this.orderBy = orderBy
+    }
 
     /**
      * Adds a LIMIT statement.
@@ -89,7 +95,7 @@
      * @param limit The limit value.
      * @return this
      */
-    fun limit(limit: String): SupportSQLiteQueryBuilder = apply {
+    public fun limit(limit: String): SupportSQLiteQueryBuilder = apply {
         val patternMatches = limitPattern.matcher(limit).matches()
         require(limit.isEmpty() || patternMatches) { "invalid LIMIT clauses:$limit" }
         this.limit = limit
@@ -100,7 +106,7 @@
      *
      * @return a new query
      */
-    fun create(): SupportSQLiteQuery {
+    public fun create(): SupportSQLiteQuery {
         require(!groupBy.isNullOrEmpty() || having.isNullOrEmpty()) {
             "HAVING clauses are only permitted when using a groupBy clause"
         }
@@ -146,7 +152,7 @@
         append(' ')
     }
 
-    companion object {
+    public companion object {
         private val limitPattern = Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?")
 
         /**
@@ -156,7 +162,7 @@
          * @return A builder to create a query.
          */
         @JvmStatic
-        fun builder(tableName: String): SupportSQLiteQueryBuilder {
+        public fun builder(tableName: String): SupportSQLiteQueryBuilder {
             return SupportSQLiteQueryBuilder(tableName)
         }
     }
diff --git a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteStatement.android.kt b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteStatement.android.kt
index 829612c..a5c36ae 100644
--- a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteStatement.android.kt
+++ b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteStatement.android.kt
@@ -17,14 +17,14 @@
 
 /** An interface to map the behavior of [android.database.sqlite.SQLiteStatement]. */
 @Suppress("AcronymName") // SQL is a known term and should remain capitalized
-interface SupportSQLiteStatement : SupportSQLiteProgram {
+public interface SupportSQLiteStatement : SupportSQLiteProgram {
     /**
      * Execute this SQL statement, if it is not a SELECT / INSERT / DELETE / UPDATE, for example
      * CREATE / DROP table, view, trigger, index etc.
      *
      * @throws [android.database.SQLException] If the SQL string is invalid for some reason
      */
-    fun execute()
+    public fun execute()
 
     /**
      * Execute this SQL statement, if the the number of rows affected by execution of this SQL
@@ -33,7 +33,7 @@
      * @return the number of rows affected by this SQL statement execution.
      * @throws [android.database.SQLException] If the SQL string is invalid for some reason
      */
-    fun executeUpdateDelete(): Int
+    public fun executeUpdateDelete(): Int
 
     /**
      * Execute this SQL statement and return the ID of the row inserted due to this call. The SQL
@@ -42,7 +42,7 @@
      * @return the row ID of the last row inserted, if this insert is successful. -1 otherwise.
      * @throws [android.database.SQLException] If the SQL string is invalid for some reason
      */
-    fun executeInsert(): Long
+    public fun executeInsert(): Long
 
     /**
      * Execute a statement that returns a 1 by 1 table with a numeric value. For example, SELECT
@@ -51,7 +51,7 @@
      * @return The result of the query.
      * @throws [android.database.sqlite.SQLiteDoneException] if the query returns zero rows
      */
-    fun simpleQueryForLong(): Long
+    public fun simpleQueryForLong(): Long
 
     /**
      * Execute a statement that returns a 1 by 1 table with a text value. For example, SELECT
@@ -60,5 +60,5 @@
      * @return The result of the query.
      * @throws [android.database.sqlite.SQLiteDoneException] if the query returns zero rows
      */
-    fun simpleQueryForString(): String?
+    public fun simpleQueryForString(): String?
 }
diff --git a/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLite.kt b/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLite.kt
index b997417..f930e84 100644
--- a/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLite.kt
+++ b/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLite.kt
@@ -22,14 +22,14 @@
 
 /** Executes a single SQL statement that returns no values. */
 @Suppress("AcronymName") // SQL is a known term and should remain capitalized
-fun SQLiteConnection.execSQL(sql: String) {
+public fun SQLiteConnection.execSQL(sql: String) {
     prepare(sql).use { it.step() }
 }
 
 /** Use the receiver statement within the [block] and closes it once it is done. */
 // TODO(b/315461431): Migrate to a Closeable interface in KMP
 @Suppress("AcronymName") // SQL is a known term and should remain capitalized
-inline fun <R> SQLiteStatement.use(block: (SQLiteStatement) -> R): R {
+public inline fun <R> SQLiteStatement.use(block: (SQLiteStatement) -> R): R {
     try {
         return block.invoke(this)
     } finally {
@@ -39,7 +39,7 @@
 
 /** Throws a [SQLiteException] with its message formed by the given [errorCode] amd [errorMsg]. */
 @Suppress("AcronymName") // SQL is a known term and should remain capitalized
-fun throwSQLiteException(errorCode: Int, errorMsg: String?): Nothing {
+public fun throwSQLiteException(errorCode: Int, errorMsg: String?): Nothing {
     val message = buildString {
         append("Error code: $errorCode")
         if (errorMsg != null) {
diff --git a/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteConnection.kt b/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteConnection.kt
index 5922ca2..128ef24 100644
--- a/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteConnection.kt
+++ b/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteConnection.kt
@@ -26,7 +26,7 @@
  */
 // TODO(b/315461431): No common Closeable interface in KMP
 @Suppress("NotCloseable", "AcronymName") // SQL is a known term and should remain capitalized
-interface SQLiteConnection {
+public interface SQLiteConnection {
     /**
      * Prepares a new SQL statement.
      *
@@ -35,7 +35,7 @@
      * @param sql the SQL statement to prepare
      * @return the prepared statement.
      */
-    fun prepare(sql: String): SQLiteStatement
+    public fun prepare(sql: String): SQLiteStatement
 
     /**
      * Closes the database connection.
@@ -43,5 +43,5 @@
      * Once a connection is closed it should no longer be used. Calling this function on an already
      * closed database connection is a no-op.
      */
-    fun close()
+    public fun close()
 }
diff --git a/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteDriver.kt b/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteDriver.kt
index a0fa75c..5cb23bf 100644
--- a/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteDriver.kt
+++ b/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteDriver.kt
@@ -18,12 +18,12 @@
 
 /** An interface to open database connections. */
 @Suppress("AcronymName") // SQL is a known term and should remain capitalized
-interface SQLiteDriver {
+public interface SQLiteDriver {
     /**
      * Opens a new database connection.
      *
      * @param fileName Name of the database file.
      * @return the database connection.
      */
-    fun open(fileName: String): SQLiteConnection
+    public fun open(fileName: String): SQLiteConnection
 }
diff --git a/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteException.kt b/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteException.kt
index 1276395..c2d575e 100644
--- a/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteException.kt
+++ b/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteException.kt
@@ -23,6 +23,6 @@
  *
  * See [Result and Error codes](https://www.sqlite.org/rescode.html)
  */
-expect class SQLiteException
+public expect class SQLiteException
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 constructor(message: String) : RuntimeException
diff --git a/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteStatement.kt b/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteStatement.kt
index 039d211..65ba44c 100644
--- a/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteStatement.kt
+++ b/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteStatement.kt
@@ -26,14 +26,14 @@
  */
 // TODO(b/315461431): No common Closeable interface in KMP
 @Suppress("NotCloseable", "AcronymName") // SQL is a known term and should remain capitalized
-interface SQLiteStatement {
+public interface SQLiteStatement {
     /**
      * Binds a ByteArray value to this statement at an index.
      *
      * @param index the 1-based index of the parameter to bind
      * @param value the value to bind
      */
-    fun bindBlob(index: Int, value: ByteArray)
+    public fun bindBlob(index: Int, value: ByteArray)
 
     /**
      * Binds a Double value to this statement at an index.
@@ -41,7 +41,7 @@
      * @param index the 1-based index of the parameter to bind
      * @param value the value to bind
      */
-    fun bindDouble(index: Int, value: Double)
+    public fun bindDouble(index: Int, value: Double)
 
     /**
      * Binds a Float value to this statement at an index.
@@ -49,7 +49,7 @@
      * @param index the 1-based index of the parameter to bind
      * @param value the value to bind
      */
-    fun bindFloat(index: Int, value: Float) {
+    public fun bindFloat(index: Int, value: Float) {
         bindDouble(index, value.toDouble())
     }
 
@@ -59,7 +59,7 @@
      * @param index the 1-based index of the parameter to bind
      * @param value the value to bind
      */
-    fun bindLong(index: Int, value: Long)
+    public fun bindLong(index: Int, value: Long)
 
     /**
      * Binds a Int value to this statement at an index.
@@ -67,7 +67,7 @@
      * @param index the 1-based index of the parameter to bind
      * @param value the value to bind
      */
-    fun bindInt(index: Int, value: Int) {
+    public fun bindInt(index: Int, value: Int) {
         bindLong(index, value.toLong())
     }
 
@@ -77,7 +77,7 @@
      * @param index the 1-based index of the parameter to bind
      * @param value the value to bind
      */
-    fun bindBoolean(index: Int, value: Boolean) {
+    public fun bindBoolean(index: Int, value: Boolean) {
         bindLong(index, if (value) 1L else 0L)
     }
 
@@ -87,14 +87,14 @@
      * @param index the 1-based index of the parameter to bind
      * @param value the value to bind
      */
-    fun bindText(index: Int, value: String)
+    public fun bindText(index: Int, value: String)
 
     /**
      * Binds a NULL value to this statement at an index.
      *
      * @param index the 1-based index of the parameter to bind
      */
-    fun bindNull(index: Int)
+    public fun bindNull(index: Int)
 
     /**
      * Returns the value of the column at [index] as a ByteArray.
@@ -102,7 +102,7 @@
      * @param index the 0-based index of the column
      * @return the value of the column
      */
-    fun getBlob(index: Int): ByteArray
+    public fun getBlob(index: Int): ByteArray
 
     /**
      * Returns the value of the column at [index] as a Double.
@@ -110,7 +110,7 @@
      * @param index the 0-based index of the column
      * @return the value of the column
      */
-    fun getDouble(index: Int): Double
+    public fun getDouble(index: Int): Double
 
     /**
      * Returns the value of the column at [index] as a Float.
@@ -118,7 +118,7 @@
      * @param index the 0-based index of the column
      * @return the value of the column
      */
-    fun getFloat(index: Int): Float {
+    public fun getFloat(index: Int): Float {
         return getDouble(index).toFloat()
     }
 
@@ -128,7 +128,7 @@
      * @param index the 0-based index of the column
      * @return the value of the column
      */
-    fun getLong(index: Int): Long
+    public fun getLong(index: Int): Long
 
     /**
      * Returns the value of the column at [index] as a Int.
@@ -136,7 +136,7 @@
      * @param index the 0-based index of the column
      * @return the value of the column
      */
-    fun getInt(index: Int): Int {
+    public fun getInt(index: Int): Int {
         return getLong(index).toInt()
     }
 
@@ -146,7 +146,7 @@
      * @param index the 0-based index of the column
      * @return the value of the column
      */
-    fun getBoolean(index: Int): Boolean {
+    public fun getBoolean(index: Int): Boolean {
         return getLong(index) != 0L
     }
 
@@ -156,7 +156,7 @@
      * @param index the 0-based index of the column
      * @return the value of the column
      */
-    fun getText(index: Int): String
+    public fun getText(index: Int): String
 
     /**
      * Returns true if the value of the column at [index] is NULL.
@@ -164,14 +164,14 @@
      * @param index the 0-based index of the column
      * @return true if the column value is NULL, false otherwise
      */
-    fun isNull(index: Int): Boolean
+    public fun isNull(index: Int): Boolean
 
     /**
      * Returns the number of columns in the result of the statement.
      *
      * @return the number of columns
      */
-    fun getColumnCount(): Int
+    public fun getColumnCount(): Int
 
     /**
      * Returns the name of a column at [index] in the result of the statement.
@@ -179,14 +179,14 @@
      * @param index the 0-based index of the column
      * @return the name of the column
      */
-    fun getColumnName(index: Int): String
+    public fun getColumnName(index: Int): String
 
     /**
      * Returns the name of the columns in the result of the statement ordered by their index.
      *
      * @return the names of the columns
      */
-    fun getColumnNames(): List<String> {
+    public fun getColumnNames(): List<String> {
         return List(getColumnCount()) { i -> getColumnName(i) }
     }
 
@@ -199,16 +199,16 @@
      *
      * @return true if there are more rows to evaluate or false if the statement is done executing
      */
-    fun step(): Boolean
+    public fun step(): Boolean
 
     /**
      * Resets the prepared statement back to initial state so that it can be re-executed via [step].
      * Any parameter bound via the bind*() APIs will retain their value.
      */
-    fun reset()
+    public fun reset()
 
     /** Clears all parameter bindings. Unset bindings are treated as NULL. */
-    fun clearBindings()
+    public fun clearBindings()
 
     /**
      * Closes the statement.
@@ -216,5 +216,5 @@
      * Once a statement is closed it should no longer be used. Calling this function on an already
      * closed statement is a no-op.
      */
-    fun close()
+    public fun close()
 }
diff --git a/sqlite/sqlite/src/jvmMain/kotlin/androidx/sqlite/SQLiteException.jvm.kt b/sqlite/sqlite/src/jvmMain/kotlin/androidx/sqlite/SQLiteException.jvm.kt
index cf6e78c..a3f49cb 100644
--- a/sqlite/sqlite/src/jvmMain/kotlin/androidx/sqlite/SQLiteException.jvm.kt
+++ b/sqlite/sqlite/src/jvmMain/kotlin/androidx/sqlite/SQLiteException.jvm.kt
@@ -18,6 +18,6 @@
 
 import androidx.annotation.RestrictTo
 
-actual class SQLiteException
+public actual class SQLiteException
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 actual constructor(message: String) : RuntimeException(message)
diff --git a/sqlite/sqlite/src/nativeMain/kotlin/androidx/sqlite/SQLiteException.native.kt b/sqlite/sqlite/src/nativeMain/kotlin/androidx/sqlite/SQLiteException.native.kt
index cf6e78c..a3f49cb 100644
--- a/sqlite/sqlite/src/nativeMain/kotlin/androidx/sqlite/SQLiteException.native.kt
+++ b/sqlite/sqlite/src/nativeMain/kotlin/androidx/sqlite/SQLiteException.native.kt
@@ -18,6 +18,6 @@
 
 import androidx.annotation.RestrictTo
 
-actual class SQLiteException
+public actual class SQLiteException
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 actual constructor(message: String) : RuntimeException(message)
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/AnimatedCornerShapeScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/AnimatedCornerShapeScreenshotTest.kt
new file mode 100644
index 0000000..df52e66
--- /dev/null
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/AnimatedCornerShapeScreenshotTest.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3
+
+import android.os.Build
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.shape.AbsoluteCutCornerShape
+import androidx.compose.foundation.shape.AbsoluteRoundedCornerShape
+import androidx.compose.foundation.shape.CutCornerShape
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.testutils.assertAgainstGolden
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.screenshot.AndroidXScreenshotTestRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestName
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
+class AnimatedCornerShapeScreenshotTest {
+
+    @get:Rule val rule = createComposeRule()
+
+    @get:Rule val screenshotRule = AndroidXScreenshotTestRule(SCREENSHOT_GOLDEN_PATH)
+
+    @get:Rule val testName = TestName()
+
+    @Test
+    fun cutCornerShapeLtr() = verifyScreenshot {
+        FilledIconButton(
+            onClick = {},
+            shape =
+                CutCornerShape(topStart = 2.dp, topEnd = 4.dp, bottomEnd = 6.dp, bottomStart = 8.dp)
+        ) {}
+    }
+
+    @Test
+    fun cutCornerShapeRtl() =
+        verifyScreenshot(layoutDirection = LayoutDirection.Rtl) {
+            FilledIconButton(
+                onClick = {},
+                shape =
+                    CutCornerShape(
+                        topStart = 2.dp,
+                        topEnd = 4.dp,
+                        bottomEnd = 6.dp,
+                        bottomStart = 8.dp
+                    )
+            ) {}
+        }
+
+    @Test
+    fun absoluteCutCornerShapeLtr() = verifyScreenshot {
+        FilledIconButton(
+            onClick = {},
+            shape =
+                AbsoluteCutCornerShape(
+                    topLeft = 2.dp,
+                    topRight = 4.dp,
+                    bottomRight = 6.dp,
+                    bottomLeft = 8.dp
+                )
+        ) {}
+    }
+
+    @Test
+    fun absoluteCutCornerShapeRtl() =
+        verifyScreenshot(layoutDirection = LayoutDirection.Rtl) {
+            FilledIconButton(
+                onClick = {},
+                shape =
+                    AbsoluteCutCornerShape(
+                        topLeft = 2.dp,
+                        topRight = 4.dp,
+                        bottomRight = 6.dp,
+                        bottomLeft = 8.dp
+                    )
+            ) {}
+        }
+
+    @Test
+    fun absoluteRoundedCornerShapeLtr() = verifyScreenshot {
+        FilledIconButton(
+            onClick = {},
+            shape =
+                AbsoluteRoundedCornerShape(
+                    topLeft = 2.dp,
+                    topRight = 4.dp,
+                    bottomRight = 6.dp,
+                    bottomLeft = 8.dp
+                )
+        ) {}
+    }
+
+    @Test
+    fun absoluteRoundedCornerShapeRtl() =
+        verifyScreenshot(layoutDirection = LayoutDirection.Rtl) {
+            FilledIconButton(
+                onClick = {},
+                shape =
+                    AbsoluteRoundedCornerShape(
+                        topLeft = 2.dp,
+                        topRight = 4.dp,
+                        bottomRight = 6.dp,
+                        bottomLeft = 8.dp
+                    )
+            ) {}
+        }
+
+    private fun verifyScreenshot(
+        layoutDirection: LayoutDirection = LayoutDirection.Ltr,
+        content: @Composable () -> Unit
+    ) {
+        rule.setContentWithTheme {
+            CompositionLocalProvider(LocalLayoutDirection provides layoutDirection) {
+                Box(modifier = Modifier.background(Color.Black).testTag(TEST_TAG)) { content() }
+            }
+        }
+
+        rule
+            .onNodeWithTag(TEST_TAG)
+            .captureToImage()
+            .assertAgainstGolden(screenshotRule, testName.methodName)
+    }
+}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/AnimatedCornerShapeTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/AnimatedCornerShapeTest.kt
new file mode 100644
index 0000000..5886659
--- /dev/null
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/AnimatedCornerShapeTest.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3
+
+import androidx.compose.foundation.shape.AbsoluteCutCornerShape
+import androidx.compose.foundation.shape.AbsoluteRoundedCornerShape
+import androidx.compose.foundation.shape.CutCornerShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+
+class AnimatedCornerShapeTest {
+    @get:Rule val rule = createComposeRule()
+
+    @Test
+    fun convertsRoundedCornerShape() {
+        val roundedCornerShape = RoundedCornerShape(10.dp)
+
+        val roundedPolygon =
+            roundedCornerShape.toRoundedPolygonOrNull(
+                size = Size(100f, 100f),
+                density = Density(density = 2f),
+                layoutDirection = LayoutDirection.Ltr
+            )
+
+        assertThat(roundedPolygon).isNotNull()
+        assertThat(roundedPolygon!!.calculateBounds()).isEqualTo(floatArrayOf(0f, 0f, 1f, 1f))
+        assertThat(roundedPolygon.cubics.size).isEqualTo(9)
+
+        val points =
+            roundedPolygon.cubics.flatMap {
+                listOf(Offset(it.anchor0X, it.anchor0Y), Offset(it.anchor1X, it.anchor1Y))
+            }
+        assertThat(points)
+            .containsAtLeast(
+                Offset(1.0f, 0.8f),
+                Offset(0.8f, 1.0f),
+                Offset(0.2f, 1.0f),
+                Offset(0.0f, 0.8f),
+                Offset(0.0f, 0.2f),
+                Offset(0.2f, 0.0f),
+                Offset(0.8f, 0.0f),
+                Offset(1.0f, 0.2f),
+            )
+    }
+
+    @Test
+    fun convertsCutCornerShape() {
+        val cutCornerShape = CutCornerShape(10.dp)
+
+        val roundedPolygon =
+            cutCornerShape.toRoundedPolygonOrNull(
+                size = Size(100f, 100f),
+                density = Density(density = 2f),
+                layoutDirection = LayoutDirection.Ltr
+            )
+
+        assertThat(roundedPolygon).isNotNull()
+        assertThat(roundedPolygon!!.calculateBounds()).isEqualTo(floatArrayOf(0f, 0f, 1f, 1f))
+        assertThat(roundedPolygon.cubics.size).isEqualTo(8)
+
+        val points =
+            roundedPolygon.cubics.flatMap {
+                listOf(Offset(it.anchor0X, it.anchor0Y), Offset(it.anchor1X, it.anchor1Y))
+            }
+        assertThat(points)
+            .containsAtLeast(
+                Offset(1.0f, 0.8f),
+                Offset(0.8f, 1.0f),
+                Offset(0.2f, 1.0f),
+                Offset(0.0f, 0.8f),
+                Offset(0.0f, 0.2f),
+                Offset(0.2f, 0.0f),
+                Offset(0.8f, 0.0f),
+                Offset(1.0f, 0.2f),
+            )
+    }
+
+    @Test
+    fun convertsAbsoluteRoundedCornerShape() {
+        val roundedCornerShape = AbsoluteRoundedCornerShape(5.dp)
+
+        val roundedPolygon =
+            roundedCornerShape.toRoundedPolygonOrNull(
+                size = Size(100f, 100f),
+                density = Density(density = 2f),
+                layoutDirection = LayoutDirection.Ltr
+            )
+
+        assertThat(roundedPolygon).isNotNull()
+        assertThat(roundedPolygon!!.calculateBounds()).isEqualTo(floatArrayOf(0f, 0f, 1f, 1f))
+        assertThat(roundedPolygon.cubics.size).isEqualTo(9)
+
+        val points =
+            roundedPolygon.cubics.flatMap {
+                listOf(Offset(it.anchor0X, it.anchor0Y), Offset(it.anchor1X, it.anchor1Y))
+            }
+        assertThat(points)
+            .containsAtLeast(
+                Offset(1.0f, 0.9f),
+                Offset(0.9f, 1.0f),
+                Offset(0.1f, 1.0f),
+                Offset(0.0f, 0.9f),
+                Offset(0.0f, 0.1f),
+                Offset(0.1f, 0.0f),
+                Offset(0.9f, 0.0f),
+                Offset(1.0f, 0.1f),
+            )
+    }
+
+    @Test
+    fun convertsAbsoluteCutCornerShape() {
+        val cutCornerShape = AbsoluteCutCornerShape(5.dp)
+
+        val roundedPolygon =
+            cutCornerShape.toRoundedPolygonOrNull(
+                size = Size(100f, 100f),
+                density = Density(density = 2f),
+                layoutDirection = LayoutDirection.Ltr
+            )
+
+        assertThat(roundedPolygon).isNotNull()
+        assertThat(roundedPolygon!!.calculateBounds()).isEqualTo(floatArrayOf(0f, 0f, 1f, 1f))
+        assertThat(roundedPolygon.cubics.size).isEqualTo(8)
+
+        val points =
+            roundedPolygon.cubics.flatMap {
+                listOf(Offset(it.anchor0X, it.anchor0Y), Offset(it.anchor1X, it.anchor1Y))
+            }
+        assertThat(points)
+            .containsAtLeast(
+                Offset(1.0f, 0.9f),
+                Offset(0.9f, 1.0f),
+                Offset(0.1f, 1.0f),
+                Offset(0.0f, 0.9f),
+                Offset(0.0f, 0.1f),
+                Offset(0.1f, 0.0f),
+                Offset(0.9f, 0.0f),
+                Offset(1.0f, 0.1f),
+            )
+    }
+}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/AnimatedCornerShape.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/AnimatedCornerShape.kt
index 70fa44b..afca2cd 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/AnimatedCornerShape.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/AnimatedCornerShape.kt
@@ -170,7 +170,7 @@
  * @param size The size of the final Composable such as a Button.
  * @param density The density of the composition.
  */
-private fun CornerBasedShape.toRoundedPolygonOrNull(
+internal fun CornerBasedShape.toRoundedPolygonOrNull(
     size: Size,
     density: Density,
     layoutDirection: LayoutDirection