Merge "Move `generateAllEnumerations` to testutils-common" into androidx-main
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionCtsTestBase.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionCtsTestBase.java
index c25e29b..9d817be 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionCtsTestBase.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionCtsTestBase.java
@@ -73,6 +73,8 @@
     static final String DB_NAME_1 = "";
     static final String DB_NAME_2 = "testDb2";
 
+    private final Context mContext = ApplicationProvider.getApplicationContext();
+
     private AppSearchSession mDb1;
     private AppSearchSession mDb2;
 
@@ -84,8 +86,6 @@
 
     @Before
     public void setUp() throws Exception {
-        Context context = ApplicationProvider.getApplicationContext();
-
         mDb1 = createSearchSession(DB_NAME_1).get();
         mDb2 = createSearchSession(DB_NAME_2).get();
 
@@ -585,7 +585,7 @@
                 new PutDocumentsRequest.Builder().addGenericDocuments(email2).build()).get();
         assertThat(failResult2.isSuccess()).isFalse();
         assertThat(failResult2.getFailures().get("email2").getErrorMessage())
-                .isEqualTo("Schema type config 'androidx.appsearch.test$" + DB_NAME_1
+                .isEqualTo("Schema type config '" + mContext.getPackageName() + "$" + DB_NAME_1
                         + "/builtin:Email' not found");
     }
 
@@ -657,7 +657,7 @@
                 new PutDocumentsRequest.Builder().addGenericDocuments(email3).build()).get();
         assertThat(failResult2.isSuccess()).isFalse();
         assertThat(failResult2.getFailures().get("email3").getErrorMessage())
-                .isEqualTo("Schema type config 'androidx.appsearch.test$" + DB_NAME_1
+                .isEqualTo("Schema type config '" + mContext.getPackageName() + "$" + DB_NAME_1
                         + "/builtin:Email' not found");
 
         // Make sure email in database 2 still present.
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GlobalSearchSessionCtsTestBase.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GlobalSearchSessionCtsTestBase.java
index 4c639a9..1e03d4e 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GlobalSearchSessionCtsTestBase.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/GlobalSearchSessionCtsTestBase.java
@@ -752,7 +752,7 @@
         AppSearchException ase = (AppSearchException) exception.getCause();
         assertThat(ase.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR);
         assertThat(ase).hasMessageThat().contains(
-                "androidx.appsearch.test does not have access to report system usage");
+                mContext.getPackageName() + " does not have access to report system usage");
     }
 
     @Test
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/GlobalSearchSession.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/GlobalSearchSession.java
index 17698b8..6784803 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/GlobalSearchSession.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/GlobalSearchSession.java
@@ -111,9 +111,11 @@
      * @throws UnsupportedOperationException if this feature is not available on this
      *                                       AppSearch implementation.
      */
+    // @exportToFramework:startStrip()
     @RequiresFeature(
             enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
             name = Features.GLOBAL_SEARCH_SESSION_ADD_REMOVE_OBSERVER)
+    // @exportToFramework:endStrip()
     void addObserver(
             @NonNull String observedPackage,
             @NonNull ObserverSpec spec,
@@ -137,9 +139,11 @@
      * @throws UnsupportedOperationException if this feature is not available on this
      *                                       AppSearch implementation.
      */
+    // @exportToFramework:startStrip()
     @RequiresFeature(
             enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
             name = Features.GLOBAL_SEARCH_SESSION_ADD_REMOVE_OBSERVER)
+    // @exportToFramework:endStrip()
     void removeObserver(
             @NonNull String observedPackage, @NonNull AppSearchObserverCallback observer);
 
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchResult.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchResult.java
index b9d71ad..34e14e2 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchResult.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/SearchResult.java
@@ -433,9 +433,11 @@
          * false.
          * <!--@exportToFramework:else()-->
          */
+        // @exportToFramework:startStrip()
         @RequiresFeature(
                 enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                 name = Features.SEARCH_RESULT_MATCH_INFO_SUBMATCH)
+        // @exportToFramework:endStrip()
         @NonNull
         public MatchRange getSubmatchRange() {
             checkSubmatchSupported();
@@ -461,9 +463,11 @@
          * false.
          * <!--@exportToFramework:else()-->
          */
+        // @exportToFramework:startStrip()
         @RequiresFeature(
                 enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
                 name = Features.SEARCH_RESULT_MATCH_INFO_SUBMATCH)
+        // @exportToFramework:endStrip()
         @NonNull
         public CharSequence getSubmatch() {
             checkSubmatchSupported();
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/SurfaceOrientedMeteringPointFactoryTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/SurfaceOrientedMeteringPointFactoryTest.java
index 474ac38..4576db2 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/SurfaceOrientedMeteringPointFactoryTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/SurfaceOrientedMeteringPointFactoryTest.java
@@ -22,6 +22,7 @@
 
 import android.content.Context;
 import android.util.Rational;
+import android.util.Size;
 
 import androidx.camera.core.AspectRatio;
 import androidx.camera.core.CameraSelector;
@@ -114,7 +115,6 @@
         assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_BACK));
 
         ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
-                .setTargetAspectRatio(AspectRatio.RATIO_4_3)
                 .setTargetName("ImageAnalysis")
                 .build();
         CameraSelector cameraSelector =
@@ -123,10 +123,13 @@
         CameraUseCaseAdapter camera = CameraUtil.createCameraAndAttachUseCase(mContext,
                 cameraSelector, imageAnalysis);
 
+        Size surfaceResolution = imageAnalysis.getAttachedSurfaceResolution();
+
         SurfaceOrientedMeteringPointFactory factory = new SurfaceOrientedMeteringPointFactory(
                 WIDTH, HEIGHT, imageAnalysis);
         MeteringPoint point = factory.createPoint(0f, 0f);
-        assertThat(point.getSurfaceAspectRatio()).isEqualTo(new Rational(4, 3));
+        assertThat(point.getSurfaceAspectRatio()).isEqualTo(
+                new Rational(surfaceResolution.getWidth(), surfaceResolution.getHeight()));
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() ->
                 //TODO: The removeUseCases() call might be removed after clarifying the
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraImplCameraReopenTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraImplCameraReopenTest.java
index ed5d6dd..fe65573 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraImplCameraReopenTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraImplCameraReopenTest.java
@@ -16,12 +16,10 @@
 
 package androidx.camera.camera2.internal;
 
-import static androidx.camera.camera2.internal.Camera2CameraImpl.StateCallback.CameraReopenMonitor.REOPEN_LIMIT_MS;
-import static androidx.camera.camera2.internal.Camera2CameraImpl.StateCallback.REOPEN_DELAY_MS;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assume.assumeFalse;
+import static org.mockito.Mockito.mock;
 
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
@@ -70,6 +68,12 @@
 @RunWith(AndroidJUnit4.class)
 @SdkSuppress(minSdkVersion = 21)
 public final class Camera2CameraImplCameraReopenTest {
+    private static final int REOPEN_DELAY_MS =
+            Camera2CameraImpl.StateCallback.CameraReopenMonitor.REOPEN_DELAY_MS;
+    private static final int REOPEN_LIMIT_MS =
+            Camera2CameraImpl.StateCallback.CameraReopenMonitor.REOPEN_LIMIT_MS;
+    private static final int ACTIVE_REOPEN_DELAY_BASE_MS =
+            Camera2CameraImpl.StateCallback.CameraReopenMonitor.ACTIVE_REOPEN_DELAY_BASE_MS;
 
     private static final int WAIT_FOR_CAMERA_OPEN_TIMEOUT_MS = 2_000;
 
@@ -128,7 +132,8 @@
                 TimeUnit.MILLISECONDS)).isTrue();
 
         // Wait for half the max reopen attempts to occur
-        final int maxReopenAttempts = (int) Math.ceil(REOPEN_LIMIT_MS / (double) REOPEN_DELAY_MS);
+        final int maxReopenAttempts =
+                (int) Math.ceil(REOPEN_LIMIT_MS / (double) REOPEN_DELAY_MS);
         assertThat(cameraOpenSemaphore.tryAcquire(maxReopenAttempts / 2, REOPEN_LIMIT_MS,
                 TimeUnit.MILLISECONDS)).isTrue();
 
@@ -221,6 +226,100 @@
         awaitCameraOpen();
     }
 
+    @Test
+    public void activeResuming_openCameraAfterMultipleReopening_whenCameraUnavailable()
+            throws Exception {
+        // Set up the camera
+        final FailCameraOpenCameraManagerImpl cameraManagerImpl =
+                new FailCameraOpenCameraManagerImpl();
+        setUpCamera(cameraManagerImpl);
+
+        // Set up camera open attempt listener
+        final Semaphore cameraOpenSemaphore = new Semaphore(0);
+        cameraManagerImpl.setOnCameraOpenAttemptListener(cameraOpenSemaphore::release);
+
+        // Enable active reopening which will open the camera even when camera is unavailable.
+        mCamera2CameraImpl.setActiveResumingMode(true);
+        // Try opening the camera. This will fail and trigger reopening attempts
+        mCamera2CameraImpl.open();
+        // make camera unavailable.
+        mCamera2CameraImpl.getCameraAvailability().onCameraUnavailable(mCameraId);
+        assertThat(cameraOpenSemaphore.tryAcquire(1, WAIT_FOR_CAMERA_OPEN_TIMEOUT_MS,
+                TimeUnit.MILLISECONDS)).isTrue();
+
+        // Wait for half the max reopen attempts to occur
+        final int maxReopenAttempts =
+                (int) Math.ceil(ACTIVE_REOPEN_DELAY_BASE_MS / (double) 10000);
+        assertThat(cameraOpenSemaphore.tryAcquire(maxReopenAttempts / 2, 10000,
+                TimeUnit.MILLISECONDS)).isTrue();
+
+        // Allow camera opening to succeed
+        cameraManagerImpl.setShouldFailCameraOpen(false);
+
+        // Verify the camera opens
+        awaitCameraOpen();
+    }
+
+    @Test
+    public void activeResuming_reopenCamera_whenCameraInterrupted() throws Exception {
+        // Set up the camera
+        final FailCameraOpenCameraManagerImpl cameraManagerImpl =
+                new FailCameraOpenCameraManagerImpl();
+        setUpCamera(cameraManagerImpl);
+
+        // Set up camera open attempt listener
+        final Semaphore cameraOpenSemaphore = new Semaphore(0);
+        cameraManagerImpl.setOnCameraOpenAttemptListener(cameraOpenSemaphore::release);
+
+        // Open camera will succeed.
+        cameraManagerImpl.setShouldFailCameraOpen(false);
+        mCamera2CameraImpl.open();
+        assertThat(cameraOpenSemaphore.tryAcquire(1, WAIT_FOR_CAMERA_OPEN_TIMEOUT_MS,
+                TimeUnit.MILLISECONDS)).isTrue();
+
+        // Emulate camera is interrupted by other client.
+        mCamera2CameraImpl.getCameraAvailability().onCameraUnavailable(mCameraId);
+        CameraDevice cameraDevice = mock(CameraDevice.class);
+        cameraManagerImpl.getDeviceStateCallback().onDisconnected(cameraDevice);
+        cameraManagerImpl.getDeviceStateCallback().onClosed(cameraDevice);
+
+        // Enable active resuming which will open the camera even when camera is unavailable.
+        mCamera2CameraImpl.setActiveResumingMode(true);
+
+        // Verify the camera opens
+        awaitCameraOpen();
+    }
+
+    @Test
+    public void activeResuming_doNotReopenCamera_whenCameraDeviceError() throws Exception {
+        // Set up the camera
+        final FailCameraOpenCameraManagerImpl cameraManagerImpl =
+                new FailCameraOpenCameraManagerImpl();
+        setUpCamera(cameraManagerImpl);
+
+        // Set up camera open attempt listener
+        final Semaphore cameraOpenSemaphore = new Semaphore(0);
+        cameraManagerImpl.setOnCameraOpenAttemptListener(cameraOpenSemaphore::release);
+
+        // Open camera will succeed.
+        cameraManagerImpl.setShouldFailCameraOpen(false);
+        mCamera2CameraImpl.open();
+        assertThat(cameraOpenSemaphore.tryAcquire(1, WAIT_FOR_CAMERA_OPEN_TIMEOUT_MS,
+                TimeUnit.MILLISECONDS)).isTrue();
+
+        mCamera2CameraImpl.setActiveResumingMode(true);
+        // make camera unavailable.
+        mCamera2CameraImpl.getCameraAvailability().onCameraUnavailable(mCameraId);
+        CameraDevice cameraDevice = mock(CameraDevice.class);
+        cameraManagerImpl.getDeviceStateCallback().onError(cameraDevice,
+                CameraDevice.StateCallback.ERROR_CAMERA_DEVICE);
+        cameraManagerImpl.getDeviceStateCallback().onClosed(cameraDevice);
+
+        // 2nd camera open should not happen
+        assertThat(cameraOpenSemaphore.tryAcquire(1, WAIT_FOR_CAMERA_OPEN_TIMEOUT_MS,
+                TimeUnit.MILLISECONDS)).isFalse();
+    }
+
     private void setUpCamera(@NonNull final FailCameraOpenCameraManagerImpl cameraManagerImpl)
             throws CameraAccessExceptionCompat, CameraUnavailableException {
         // Build camera manager wrapper
@@ -288,6 +387,8 @@
         private OnCameraOpenAttemptListener mOnCameraOpenAttemptListener;
         @GuardedBy("mLock")
         private boolean mShouldFailCameraOpen = true;
+        @GuardedBy("mLock")
+        private CameraDevice.StateCallback mDeviceStateCallback;
 
         FailCameraOpenCameraManagerImpl() {
             mForwardCameraManagerCompatImpl = CameraManagerCompatImpl.from(
@@ -326,10 +427,18 @@
             return mForwardCameraManagerCompatImpl.getCameraManager();
         }
 
+        @NonNull
+        public CameraDevice.StateCallback getDeviceStateCallback() {
+            synchronized (mLock) {
+                return mDeviceStateCallback;
+            }
+        }
+
         @Override
         public void openCamera(@NonNull String cameraId, @NonNull Executor executor,
                 @NonNull CameraDevice.StateCallback callback) throws CameraAccessExceptionCompat {
             synchronized (mLock) {
+                mDeviceStateCallback = callback;
                 if (mOnCameraOpenAttemptListener != null) {
                     mOnCameraOpenAttemptListener.onCameraOpenAttempt();
                 }
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
index 82758a7..4d1eb81 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
@@ -182,6 +182,7 @@
     @GuardedBy("mLock")
     @Nullable
     private SessionProcessor mSessionProcessor;
+    boolean mIsActiveResumingMode = false;
 
     /**
      * Constructor for a camera.
@@ -263,7 +264,7 @@
         switch (mState) {
             case INITIALIZED:
             case PENDING_OPEN:
-                tryForceOpenCameraDevice();
+                tryForceOpenCameraDevice(/*fromScheduledCameraReopen*/false);
                 break;
             case CLOSING:
                 setState(InternalState.REOPENING);
@@ -951,10 +952,14 @@
      * Attempts to force open the camera device, which may result in stealing it from a lower
      * priority client. This should only happen if another client doesn't close the camera when
      * it should, e.g. when its process is moved to the background.
+     *
+     * @param fromScheduledCameraReopen True if the attempt to open the camera originated from a
+     *                                  {@linkplain StateCallback.ScheduledReopen scheduled
+     *                                  reopen of the camera}. False otherwise.
      */
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
     @ExecutedBy("mExecutor")
-    void tryForceOpenCameraDevice() {
+    void tryForceOpenCameraDevice(boolean fromScheduledCameraReopen) {
         debugLog("Attempting to force open the camera.");
         final boolean shouldTryOpenCamera = mCameraStateRegistry.tryOpenCamera(this);
         if (!shouldTryOpenCamera) {
@@ -962,12 +967,12 @@
             setState(InternalState.PENDING_OPEN);
             return;
         }
-        openCameraDevice(false);
+        openCameraDevice(fromScheduledCameraReopen);
     }
 
     /**
-     * Attempts to open the camera device. Unlike {@link #tryForceOpenCameraDevice()}, this method
-     * does not steal the camera away from other clients.
+     * Attempts to open the camera device. Unlike {@link #tryForceOpenCameraDevice(boolean)},
+     * this method does not steal the camera away from other clients.
      *
      * @param fromScheduledCameraReopen True if the attempt to open the camera originated from a
      *                                  {@linkplain StateCallback.ScheduledReopen scheduled
@@ -987,6 +992,23 @@
         openCameraDevice(fromScheduledCameraReopen);
     }
 
+    @Override
+    public void setActiveResumingMode(boolean enabled) {
+        mExecutor.execute(() -> {
+            // Enables/Disables active resuming mode which will reopen the camera regardless of the
+            // availability when camera is interrupted.
+            mIsActiveResumingMode = enabled;
+
+            // If camera is interrupted currently, force open the camera right now regardless of the
+            // camera availability.
+            if (enabled && (mState == InternalState.PENDING_OPEN
+                    || mState == InternalState.REOPENING)) {
+                tryForceOpenCameraDevice(/*fromScheduledCameraReopen*/false);
+            }
+        });
+    }
+
+
     /**
      * Opens the camera device.
      *
@@ -1469,11 +1491,6 @@
 
     @RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
     final class StateCallback extends CameraDevice.StateCallback {
-
-        // Delay long enough to guarantee the app could have been backgrounded.
-        // See ProcessLifecycleProvider for where this delay comes from.
-        static final int REOPEN_DELAY_MS = 700;
-
         @CameraExecutor
         private final Executor mExecutor;
         private final ScheduledExecutorService mScheduler;
@@ -1495,6 +1512,7 @@
             debugLog("CameraDevice.onOpened()");
             mCameraDevice = cameraDevice;
             mCameraDeviceError = ERROR_NONE;
+            resetReopenMonitor();
             switch (mState) {
                 case CLOSING:
                 case RELEASING:
@@ -1654,15 +1672,16 @@
 
             if (mCameraReopenMonitor.canScheduleCameraReopen()) {
                 mScheduledReopenRunnable = new ScheduledReopen(mExecutor);
-                debugLog("Attempting camera re-open in " + REOPEN_DELAY_MS + "ms: "
-                        + mScheduledReopenRunnable);
+                debugLog("Attempting camera re-open in "
+                        + mCameraReopenMonitor.getReopenDelayMs() + "ms: "
+                        + mScheduledReopenRunnable + " activeResuming = " + mIsActiveResumingMode);
                 mScheduledReopenHandle = mScheduler.schedule(mScheduledReopenRunnable,
-                        REOPEN_DELAY_MS, TimeUnit.MILLISECONDS);
+                        mCameraReopenMonitor.getReopenDelayMs(), TimeUnit.MILLISECONDS);
             } else {
                 // TODO(b/174685338): Report camera opening error to the user
                 Logger.e(TAG,
                         "Camera reopening attempted for "
-                                + CameraReopenMonitor.REOPEN_LIMIT_MS
+                                + mCameraReopenMonitor.getReopenLimitMs()
                                 + "ms without success.");
 
                 // Set the state to PENDING_OPEN, so that an attempt to reopen the camera is made if
@@ -1737,30 +1756,86 @@
                     // this is still the scheduled reopen.
                     if (!mCancelled) {
                         Preconditions.checkState(mState == InternalState.REOPENING);
-                        tryOpenCameraDevice(/*fromScheduledCameraReopen=*/true);
+                        if (shouldActiveResume()) {
+                            // Ignore the camera availability when in active resuming mode.
+                            tryForceOpenCameraDevice(/*fromScheduledCameraReopen*/true);
+                        } else {
+                            tryOpenCameraDevice(/*fromScheduledCameraReopen=*/true);
+                        }
                     }
                 });
             }
         }
 
-        /** Keeps track of camera reopen attempts in order to limit them. */
+        /**
+         * Enables active resume only when camera is stolen by other apps.
+         */
+        boolean shouldActiveResume() {
+            return mIsActiveResumingMode && (mCameraDeviceError != ERROR_CAMERA_DEVICE
+                    && mCameraDeviceError != ERROR_MAX_CAMERAS_IN_USE);
+        }
+
+        /**
+         * Keeps track of camera reopen attempts in order to limit them.
+         *
+         * When in active resuming mode, it will periodically retry opening the camera regardless
+         * of the camera availability.
+         * Elapsed time <= 2 minutes -> retry once per 1 second.
+         * Elapsed time 2 to 5 minutes -> retry once per 2 seconds.
+         * Elapsed time > 5 minutes -> retry once per 4 seconds.
+         * Retry will stop after 30 minutes.
+         *
+         * When not in active resuming mode, it will reopen in every 700ms within the 10 seconds
+         * limit. However, if the camera is unavailable the retry will stop immediately until it
+         * becomes available.
+         */
         class CameraReopenMonitor {
+            // Delay long enough to guarantee the app could have been backgrounded.
+            // See ProcessLifecycleOwner for where this delay comes from.
+            static final int REOPEN_DELAY_MS = 700;
             // Time limit since the first camera reopen attempt after which reopening the camera
             // should no longer be attempted.
             static final int REOPEN_LIMIT_MS = 10_000;
+            static final int ACTIVE_REOPEN_DELAY_BASE_MS = 1000;
+            static final int ACTIVE_REOPEN_LIMIT_MS = 30 * 60 * 1000; // 30 minutes
             static final int INVALID_TIME = -1;
             private long mFirstReopenTime = INVALID_TIME;
 
-            boolean canScheduleCameraReopen() {
-                final long now = SystemClock.uptimeMillis();
+            int getReopenDelayMs() {
+                if (!shouldActiveResume()) {
+                    return REOPEN_DELAY_MS;
+                } else {
+                    long elapsedTime = getElapsedTime();
+                    if (elapsedTime <= 2 * 60 * 1000) { // <= 2 minutes
+                        return ACTIVE_REOPEN_DELAY_BASE_MS;
+                    } else if (elapsedTime <= 5 * 60 * 1000) { // <= 5 minutes
+                        return ACTIVE_REOPEN_DELAY_BASE_MS * 2;
+                    } else { // > 5 minutes
+                        return ACTIVE_REOPEN_DELAY_BASE_MS * 4;
+                    }
+                }
+            }
 
+            int getReopenLimitMs() {
+                if (!shouldActiveResume()) {
+                    return REOPEN_LIMIT_MS;
+                } else {
+                    return ACTIVE_REOPEN_LIMIT_MS;
+                }
+            }
+
+            long getElapsedTime() {
+                final long now = SystemClock.uptimeMillis();
                 // If it's the first attempt to reopen the camera
                 if (mFirstReopenTime == INVALID_TIME) {
                     mFirstReopenTime = now;
-                    return true;
                 }
 
-                final boolean hasReachedLimit = now - mFirstReopenTime >= REOPEN_LIMIT_MS;
+                return now - mFirstReopenTime;
+            }
+
+            boolean canScheduleCameraReopen() {
+                final boolean hasReachedLimit = getElapsedTime() >= getReopenLimitMs();
 
                 // If the limit has been reached, prevent further attempts to reopen the camera,
                 // and reset [firstReopenTime].
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInternal.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInternal.java
index 5987245..25ebd61 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInternal.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraInternal.java
@@ -120,6 +120,17 @@
     void close();
 
     /**
+     * When in active resuming mode, it will actively retry opening the camera periodically to
+     * resume regardless of the camera availability if the camera is interrupted in
+     * OPEN/OPENING/PENDING_OPEN state.
+     *
+     * When not in active resuming mode, it will retry opening camera only when camera
+     * becomes available.
+     */
+    default void setActiveResumingMode(boolean enabled) {
+    }
+
+    /**
      * Release the camera.
      *
      * <p>Once the camera is released it is permanently closed. A new instance must be created to
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
index bdc3f0f8..88dc4db2 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
@@ -320,6 +320,19 @@
         }
     }
 
+
+    /**
+     * When in active resuming mode, it will actively retry opening the camera periodically to
+     * resume regardless of the camera availability if the camera is interrupted in
+     * OPEN/OPENING/PENDING_OPEN state.
+     *
+     * When not in actively resuming mode, it will retry opening camera only when camera
+     * becomes available.
+     */
+    public void setActiveResumingMode(boolean enabled) {
+        mCameraInternal.setActiveResumingMode(enabled);
+    }
+
     /**
      * Detach the UseCases from the {@link CameraInternal} so that the UseCases stop receiving data.
      *
diff --git a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCamera.java b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCamera.java
index 1c86571..f3f56b7 100644
--- a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCamera.java
+++ b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCamera.java
@@ -16,6 +16,8 @@
 
 package androidx.camera.lifecycle;
 
+import android.os.Build;
+
 import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -105,6 +107,23 @@
         }
     }
 
+    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
+    public void onResume(LifecycleOwner lifecycleOwner) {
+        // ActiveResumingMode is required for Multi-window which is supported since Android 7(N).
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            mCameraUseCaseAdapter.setActiveResumingMode(true);
+        }
+    }
+
+    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
+    public void onPause(LifecycleOwner lifecycleOwner) {
+        // ActiveResumingMode is required for Multi-window which is supported since Android 7(N).
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            mCameraUseCaseAdapter.setActiveResumingMode(false);
+        }
+    }
+
+
     /**
      * Suspend the camera so that it ignore lifecycle events.
      *
diff --git a/car/app/app-samples/navigation/common/build.gradle b/car/app/app-samples/navigation/common/build.gradle
index 75374b4..997d94a 100644
--- a/car/app/app-samples/navigation/common/build.gradle
+++ b/car/app/app-samples/navigation/common/build.gradle
@@ -28,7 +28,7 @@
 dependencies {
     implementation(project(":car:app:app"))
 
-    implementation 'androidx.core:core:1.5.0-alpha01'
+    implementation 'androidx.core:core:1.7.0'
     implementation project(path: ':annotation:annotation-experimental')
     // There is an implicit compile-only dep due to :annotation-experimental
     // Build will complain without this manual declaration.
diff --git a/car/app/app-samples/navigation/common/src/main/java/androidx/car/app/sample/navigation/common/car/NavigationSession.java b/car/app/app-samples/navigation/common/src/main/java/androidx/car/app/sample/navigation/common/car/NavigationSession.java
index 75a4d23..14dcab9 100644
--- a/car/app/app-samples/navigation/common/src/main/java/androidx/car/app/sample/navigation/common/car/NavigationSession.java
+++ b/car/app/app-samples/navigation/common/src/main/java/androidx/car/app/sample/navigation/common/car/NavigationSession.java
@@ -25,10 +25,8 @@
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.location.Location;
-import android.location.LocationListener;
 import android.location.LocationManager;
 import android.net.Uri;
-import android.os.Bundle;
 import android.os.IBinder;
 import android.util.Log;
 
@@ -49,6 +47,7 @@
 import androidx.car.app.sample.navigation.common.model.Instruction;
 import androidx.car.app.sample.navigation.common.nav.NavigationService;
 import androidx.core.graphics.drawable.IconCompat;
+import androidx.core.location.LocationListenerCompat;
 import androidx.lifecycle.DefaultLifecycleObserver;
 import androidx.lifecycle.Lifecycle;
 import androidx.lifecycle.LifecycleObserver;
@@ -106,27 +105,8 @@
             };
 
     // A listener to periodically update the surface with the location coordinates
-    LocationListener mLocationListener =
-            new LocationListener() {
-                @Override
-                public void onLocationChanged(Location location) {
-                    mNavigationCarSurface.updateLocationString(getLocationString(location));
-                }
-
-                /** @deprecated This callback will never be invoked on Android Q and above. */
-                @Override
-                @Deprecated
-                public void onStatusChanged(String provider, int status, Bundle extras) {
-                }
-
-                @Override
-                public void onProviderEnabled(String provider) {
-                }
-
-                @Override
-                public void onProviderDisabled(String provider) {
-                }
-            };
+    LocationListenerCompat mLocationListener =
+            location -> mNavigationCarSurface.updateLocationString(getLocationString(location));
 
     // Monitors the state of the connection to the Navigation service.
     final ServiceConnection mServiceConnection =
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/PlaceListTemplateBrowseDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/PlaceListTemplateBrowseDemoScreen.java
index 784ca36..cac11e3 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/PlaceListTemplateBrowseDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/PlaceListTemplateBrowseDemoScreen.java
@@ -21,7 +21,6 @@
 
 import android.content.pm.PackageManager;
 import android.location.Location;
-import android.location.LocationListener;
 import android.location.LocationManager;
 import android.os.HandlerThread;
 
@@ -37,6 +36,7 @@
 import androidx.car.app.model.PlaceListMapTemplate;
 import androidx.car.app.model.Row;
 import androidx.car.app.model.Template;
+import androidx.core.location.LocationListenerCompat;
 import androidx.lifecycle.DefaultLifecycleObserver;
 import androidx.lifecycle.LifecycleOwner;
 
@@ -50,7 +50,7 @@
     private static final int LOCATION_UPDATE_MIN_INTERVAL_MILLIS = 1000;
     private static final int LOCATION_UPDATE_MIN_DISTANCE_METER = 1;
 
-    final LocationListener mLocationListener;
+    final LocationListenerCompat mLocationListener;
     final HandlerThread mLocationUpdateHandlerThread;
 
     @Nullable
diff --git a/car/app/app/src/main/java/androidx/car/app/AppManager.java b/car/app/app/src/main/java/androidx/car/app/AppManager.java
index 7f69c27..9c2561e 100644
--- a/car/app/app/src/main/java/androidx/car/app/AppManager.java
+++ b/car/app/app/src/main/java/androidx/car/app/AppManager.java
@@ -25,7 +25,6 @@
 
 import android.annotation.SuppressLint;
 import android.content.pm.PackageManager;
-import android.location.LocationListener;
 import android.location.LocationManager;
 import android.os.HandlerThread;
 import android.os.Looper;
@@ -37,6 +36,7 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.car.app.managers.Manager;
 import androidx.car.app.utils.RemoteUtils;
+import androidx.core.location.LocationListenerCompat;
 import androidx.lifecycle.Lifecycle;
 
 /** Manages the communication between the app and the host. */
@@ -54,13 +54,12 @@
     private final Lifecycle mLifecycle;
 
     /**
-     * {@link LocationListener} for getting location updates within the app and sends them over to
-     * the car host.
+     * Listener for getting location updates within the app and sends them over to the car host.
      *
      * <p>This should only be enabled when the car host explicitly calls {@code IAppManager
      * .startLocationUpdates}.
      */
-    private final LocationListener mLocationListener;
+    private final LocationListenerCompat mLocationListener;
     @VisibleForTesting
     final HandlerThread mLocationUpdateHandlerThread;
 
@@ -230,14 +229,12 @@
         };
 
         mLocationUpdateHandlerThread = new HandlerThread("LocationUpdateThread");
-        mLocationListener = location -> {
-            mHostDispatcher.dispatch(
-                    CarContext.APP_SERVICE,
-                    "sendLocation", (IAppHost host) -> {
-                        host.sendLocation(location);
-                        return null;
-                    }
-            );
-        };
+        mLocationListener = location -> mHostDispatcher.dispatch(
+                CarContext.APP_SERVICE,
+                "sendLocation", (IAppHost host) -> {
+                    host.sendLocation(location);
+                    return null;
+                }
+        );
     }
 }
diff --git a/car/app/app/src/main/java/androidx/car/app/CarContext.java b/car/app/app/src/main/java/androidx/car/app/CarContext.java
index dea577a..23a392c 100644
--- a/car/app/app/src/main/java/androidx/car/app/CarContext.java
+++ b/car/app/app/src/main/java/androidx/car/app/CarContext.java
@@ -291,7 +291,8 @@
      * @param intent the {@link Intent} to send to the target application
      * @throws SecurityException    if the app attempts to start a different app explicitly or
      *                              does not have permissions for the requested action
-     * @throws HostException        if the remote call fails
+     * @throws HostException        if the remote call fails. For example, if the intent cannot be
+     *                              handled by the car host.
      * @throws NullPointerException if {@code intent} is {@code null}
      */
     public void startCarApp(@NonNull Intent intent) {
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt
index 58e5c02..90b4b3b 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/AbstractIrTransformTest.kt
@@ -69,6 +69,7 @@
     open val liveLiteralsV2Enabled get() = false
     open val generateFunctionKeyMetaClasses get() = false
     open val sourceInformationEnabled get() = true
+    open val intrinsicRememberEnabled get() = false
     open val decoysEnabled get() = false
     open val metricsDestination: String? get() = null
 
@@ -77,7 +78,7 @@
         liveLiteralsV2Enabled,
         generateFunctionKeyMetaClasses,
         sourceInformationEnabled,
-        intrinsicRememberEnabled = true,
+        intrinsicRememberEnabled,
         decoysEnabled,
         metricsDestination
     )
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/DefaultParamTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/DefaultParamTransformTests.kt
index becc6b8..e9f9486 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/DefaultParamTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/DefaultParamTransformTests.kt
@@ -184,8 +184,7 @@
             }
             ) { }
             fun Foo() {
-              Bar(
-              )
+              Bar()
             }
         """
     )
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt
index 9953eff..e9a6612 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt
@@ -1453,8 +1453,7 @@
                     a = 0
                   }
                   if (%default and 0b0010 !== 0) {
-                    b = Foo(
-                    )
+                    b = Foo()
                     %dirty = %dirty and 0b01110000.inv()
                   }
                 } else {
@@ -1521,8 +1520,7 @@
                 %composer.startDefaults()
                 if (%changed and 0b0001 === 0 || %composer.defaultsInvalid) {
                   if (%default and 0b0001 !== 0) {
-                    b = Foo(
-                    )
+                    b = Foo()
                     %dirty = %dirty and 0b01110000.inv()
                   }
                 } else {
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationRegressionTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationRegressionTests.kt
new file mode 100644
index 0000000..bb18ad9
--- /dev/null
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationRegressionTests.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.compiler.plugins.kotlin
+
+import org.junit.Test
+
+/**
+ * This test merely ensures that code gen changes are evaluated against potentially
+ * breaking Android Studio compose debugger integration, see change id
+ * I63ce10791fc3795a568f5f09ca6a24e801f5e3da
+ *
+ * The Android Studio debugger searches for `ComposableSingletons` classes by name.
+ * Any changes to the naming scheme have to be reflected in the Android Studio code.
+ */
+class LambdaMemoizationRegressionTests : ComposeIrTransformTest() {
+    @Test
+    fun testNestedComposableSingletonsClass() = verifyComposeIrTransform(
+        """
+            import androidx.compose.runtime.Composable
+
+            class A {
+                val x = @Composable {}
+            }
+        """,
+        """
+            @StabilityInferred(parameters = 0)
+            class A {
+              val x: Function2<Composer, Int, Unit> = ComposableSingletons%TestKt.lambda-1
+              static val %stable: Int = 0
+            }
+            internal object ComposableSingletons%TestKt {
+              val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false) { %composer: Composer?, %changed: Int ->
+                sourceInformation(%composer, "C:Test.kt")
+                if (%changed and 0b1011 !== 0b0010 || !%composer.skipping) {
+                  Unit
+                } else {
+                  %composer.skipToGroupEnd()
+                }
+              }
+            }
+        """
+    )
+
+    @Test
+    fun testNestedComposableSingletonsClass2() = verifyComposeIrTransform(
+        """
+            import androidx.compose.runtime.Composable
+
+            class A {
+                class B {
+                    val x = @Composable {}
+                }
+            }
+        """,
+        """
+            @StabilityInferred(parameters = 0)
+            class A {
+              @StabilityInferred(parameters = 0)
+              class B {
+                val x: Function2<Composer, Int, Unit> = ComposableSingletons%TestKt.lambda-1
+                static val %stable: Int = 0
+              }
+              static val %stable: Int = 0
+            }
+            internal object ComposableSingletons%TestKt {
+              val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false) { %composer: Composer?, %changed: Int ->
+                sourceInformation(%composer, "C:Test.kt")
+                if (%changed and 0b1011 !== 0b0010 || !%composer.skipping) {
+                  Unit
+                } else {
+                  %composer.skipToGroupEnd()
+                }
+              }
+            }
+        """
+    )
+
+    @Test
+    fun testJvmNameComposableSingletons() = verifyComposeIrTransform(
+        """
+            @file:JvmName("A")
+            import androidx.compose.runtime.Composable
+
+            val x = @Composable {}
+        """,
+        """
+            val x: Function2<Composer, Int, Unit> = ComposableSingletons%TestKt.lambda-1
+            internal object ComposableSingletons%TestKt {
+              val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false) { %composer: Composer?, %changed: Int ->
+                sourceInformation(%composer, "C:Test.kt")
+                if (%changed and 0b1011 !== 0b0010 || !%composer.skipping) {
+                  Unit
+                } else {
+                  %composer.skipToGroupEnd()
+                }
+              }
+            }
+        """
+    )
+}
\ No newline at end of file
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
index 77ca5c9..e2deb3c 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/LambdaMemoizationTransformTests.kt
@@ -19,7 +19,6 @@
 import org.junit.Test
 
 class LambdaMemoizationTransformTests : ComposeIrTransformTest() {
-
     @Test
     fun testCapturedThisFromFieldInitializer(): Unit = verifyComposeIrTransform(
         """
@@ -184,7 +183,7 @@
                 {
                   Local().something(2)
                 }
-              }, %composer, 0)
+              }, %composer, 0b1110 and %changed or 0b01110000 and %changed)
               %composer.endReplaceableGroup()
             }
         """,
@@ -220,7 +219,7 @@
                 {
                   Foo(1)
                 }
-              }, %composer, 0)
+              }, %composer, 0b1110 and %changed)
               %composer.endReplaceableGroup()
             }
         """,
@@ -654,7 +653,7 @@
               {
                 println("Captures a" + a)
               }
-            }, %composer, 0), %composer, 0)
+            }, %composer, 0b1110 and %dirty), %composer, 0)
           } else {
             %composer.skipToGroupEnd()
           }
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt
index b6b2d95..aeab392 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/RememberIntrinsicTransformTests.kt
@@ -21,6 +21,9 @@
 import org.junit.Test
 
 class RememberIntrinsicTransformTests : ComposeIrTransformTest() {
+    override val intrinsicRememberEnabled: Boolean
+        get() = true
+
     private fun comparisonPropagation(
         @Language("kotlin")
         unchecked: String,
@@ -792,7 +795,7 @@
               }
               if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
                 val a = InlineInt(123)
-                val foo = %composer.cache(%dirty and 0b1110 === 0b0100 or false) {
+                val foo = %composer.cache(%dirty and 0b1110 === 0b0100) {
                   val tmp0_return = Foo()
                   tmp0_return
                 }
@@ -918,6 +921,139 @@
         """
     )
 
+    @Test
+    fun testRememberMemoizedLambda(): Unit = comparisonPropagation(
+        "",
+        """
+            @Composable
+            fun Test(a: Int) {
+                used { a }
+            }
+        """,
+        """
+            @Composable
+            fun Test(a: Int, %composer: Composer?, %changed: Int) {
+              %composer = %composer.startRestartGroup(<>)
+              sourceInformation(%composer, "C(Test):Test.kt")
+              val %dirty = %changed
+              if (%changed and 0b1110 === 0) {
+                %dirty = %dirty or if (%composer.changed(a)) 0b0100 else 0b0010
+              }
+              if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
+                used(%composer.cache(%dirty and 0b1110 === 0b0100) {
+                  {
+                    a
+                  }
+                }
+                )
+              } else {
+                %composer.skipToGroupEnd()
+              }
+              %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                Test(a, %composer, %changed or 0b0001)
+              }
+            }
+        """
+    )
+
+    @Test
+    fun testRememberFunctionReference(): Unit = comparisonPropagation(
+        """
+            fun effect(): Int = 0
+        """,
+        """
+            @Composable
+            fun Test(a: Int) {
+                used(remember(a, ::effect))
+            }
+        """,
+        """
+            @Composable
+            fun Test(a: Int, %composer: Composer?, %changed: Int) {
+              %composer = %composer.startRestartGroup(<>)
+              sourceInformation(%composer, "C(Test):Test.kt")
+              val %dirty = %changed
+              if (%changed and 0b1110 === 0) {
+                %dirty = %dirty or if (%composer.changed(a)) 0b0100 else 0b0010
+              }
+              if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
+                used(%composer.cache(%dirty and 0b1110 === 0b0100, effect))
+              } else {
+                %composer.skipToGroupEnd()
+              }
+              %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                Test(a, %composer, %changed or 0b0001)
+              }
+            }
+        """
+    )
+
+    @Test
+    fun testRememberAdaptedFunctionReference(): Unit = comparisonPropagation(
+        """
+            fun effect(a: Int = 0): Int = a
+        """,
+        """
+            @Composable
+            fun Test(a: Int) {
+                used(remember(a, ::effect))
+            }
+        """,
+        """
+            @Composable
+            fun Test(a: Int, %composer: Composer?, %changed: Int) {
+              %composer = %composer.startRestartGroup(<>)
+              sourceInformation(%composer, "C(Test):Test.kt")
+              val %dirty = %changed
+              if (%changed and 0b1110 === 0) {
+                %dirty = %dirty or if (%composer.changed(a)) 0b0100 else 0b0010
+              }
+              if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
+                used(%composer.cache(%dirty and 0b1110 === 0b0100, {
+                  effect()
+                }
+                ))
+              } else {
+                %composer.skipToGroupEnd()
+              }
+              %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                Test(a, %composer, %changed or 0b0001)
+              }
+            }
+        """
+    )
+    @Test
+    fun testRememberPropertyReference(): Unit = comparisonPropagation(
+        """
+            class A(val value: Int)
+        """.trimIndent(),
+        """
+            @Composable
+            fun Test(a: A) {
+                used(remember(a, a::value))
+            }
+        """,
+        """
+            @Composable
+            fun Test(a: A, %composer: Composer?, %changed: Int) {
+              %composer = %composer.startRestartGroup(<>)
+              sourceInformation(%composer, "C(Test):Test.kt")
+              val %dirty = %changed
+              if (%changed and 0b1110 === 0) {
+                %dirty = %dirty or if (%composer.changed(a)) 0b0100 else 0b0010
+              }
+              if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
+                used(%composer.cache(%dirty and 0b1110 === 0b0100, a::value))
+              } else {
+                %composer.skipToGroupEnd()
+              }
+              %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+                Test(a, %composer, %changed or 0b0001)
+              }
+            }
+        """
+    )
+
     @Ignore("This test must pass before intrinsic remember can be turned on")
     fun xtestOptimizationFailsIfDefaultsGroupIsUsed(): Unit = comparisonPropagation(
         """
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableCheckerTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableCheckerTests.kt
index 1b21477..4bae40f 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableCheckerTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/analysis/ComposableCheckerTests.kt
@@ -21,7 +21,7 @@
         )
     }
 
-    fun doTest(text: String, expectPass: Boolean) {
+    private fun doTest(text: String, expectPass: Boolean) {
         val disposable = TestDisposable()
         val classPath = createClasspath()
         val configuration = newConfiguration()
@@ -53,11 +53,11 @@
 
     class ExpectedFailureException(message: String) : Exception(message)
 
-    fun check(expectedText: String) {
+    private fun check(expectedText: String) {
         doTest(expectedText, true)
     }
 
-    fun checkFail(expectedText: String) {
+    private fun checkFail(expectedText: String) {
         doTest(expectedText, false)
     }
 
@@ -114,6 +114,47 @@
     """
     )
 
+    fun testCinNoinlineNCLambdaArg() = check(
+        """
+        import androidx.compose.runtime.*
+        @Composable fun C() { }
+        <!NOTHING_TO_INLINE!>inline<!> fun NoinlineNC(noinline lambda: () -> Unit) { lambda() }
+        @Composable fun C3() {
+            NoinlineNC {
+                <!COMPOSABLE_INVOCATION!>C<!>()
+            }
+        }
+    """
+    )
+
+    fun testCinCrossinlineNCLambdaArg() = check(
+        """
+        import androidx.compose.runtime.*
+        @Composable fun C() { }
+        inline fun CrossinlineNC(crossinline lambda: () -> Unit) { lambda() }
+        @Composable fun C3() {
+            CrossinlineNC {
+                <!COMPOSABLE_INVOCATION!>C<!>()
+            }
+        }
+    """
+    )
+
+    fun testCinNestedInlinedNCLambdaArg() = check(
+        """
+        import androidx.compose.runtime.*
+        @Composable fun C() { }
+        inline fun InlineNC(lambda: () -> Unit) { lambda() }
+        @Composable fun C3() {
+            InlineNC {
+                InlineNC {
+                    C()
+                }
+            }
+        }
+    """
+    )
+
     fun testCinLambdaArgOfNC() = check(
         """
         import androidx.compose.runtime.*
@@ -178,6 +219,75 @@
     """
     )
 
+    fun testCfromComposableFunInterface() = check(
+        """
+        import androidx.compose.runtime.Composable
+
+        fun interface A { @Composable fun f() }
+        @Composable fun B() { A { B() } }
+    """
+    )
+
+    fun testCfromAnnotatedComposableFunInterface() = check(
+        """
+        import androidx.compose.runtime.Composable
+
+        fun interface A { @Composable fun f() }
+        @Composable fun B() {
+          val f = @Composable { B() }
+          A(f)
+        }
+    """
+    )
+
+    fun testCfromComposableFunInterfaceArgument() = check(
+        """
+        import androidx.compose.runtime.Composable
+
+        fun interface A { @Composable fun f() }
+
+        @Composable fun B(a: (A) -> Unit) { a { B(a) } }
+    """
+    )
+
+    fun testCfromComposableTypeAliasFunInterface() = check(
+        """
+        import androidx.compose.runtime.Composable
+
+        fun interface A { @Composable fun f() }
+        typealias B = A
+
+        @Composable fun C() { A { C() } }
+    """
+    )
+
+    fun testCfromNonComposableFunInterface() = check(
+        """
+        import androidx.compose.runtime.Composable
+
+        fun interface A { fun f() }
+        @Composable fun B() {
+          A {
+            <!COMPOSABLE_INVOCATION!>B<!>()
+          }
+        }
+    """
+    )
+
+    fun testCfromNonComposableFunInterfaceArgument() = check(
+        """
+        import androidx.compose.runtime.Composable
+
+        fun interface A { fun f() }
+
+        @Composable fun B(a: (A) -> Unit) {
+          a {
+            <!COMPOSABLE_INVOCATION!>B<!>(a)
+          }
+        }
+    """
+    )
+
     fun testPreventedCaptureOnInlineLambda() = check(
         """
         import androidx.compose.runtime.*
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposableCallChecker.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposableCallChecker.kt
index b21473c..2f3183b 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposableCallChecker.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposableCallChecker.kt
@@ -18,6 +18,7 @@
 
 import androidx.compose.compiler.plugins.kotlin.analysis.ComposeWritableSlices
 import com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType
 import org.jetbrains.kotlin.container.StorageComponentContainer
 import org.jetbrains.kotlin.container.useInstance
 import org.jetbrains.kotlin.descriptors.CallableDescriptor
@@ -28,6 +29,7 @@
 import org.jetbrains.kotlin.descriptors.PropertyGetterDescriptor
 import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
 import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
+import org.jetbrains.kotlin.descriptors.synthetic.FunctionInterfaceConstructorDescriptor
 import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
 import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
 import org.jetbrains.kotlin.platform.TargetPlatform
@@ -51,13 +53,11 @@
 import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker
 import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
 import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
+import org.jetbrains.kotlin.resolve.calls.context.CallPosition
 import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
 import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
 import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
 import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
-import org.jetbrains.kotlin.resolve.inline.InlineUtil.canBeInlineArgument
-import org.jetbrains.kotlin.resolve.inline.InlineUtil.isInline
-import org.jetbrains.kotlin.resolve.inline.InlineUtil.isInlineParameter
 import org.jetbrains.kotlin.resolve.inline.InlineUtil.isInlinedArgument
 import org.jetbrains.kotlin.resolve.sam.getSingleAbstractMethodOrNull
 import org.jetbrains.kotlin.types.KotlinType
@@ -80,7 +80,7 @@
         container.useInstance(this)
     }
 
-    fun checkInlineLambdaCall(
+    private fun checkInlineLambdaCall(
         resolvedCall: ResolvedCall<*>,
         reportOn: PsiElement,
         context: CallCheckerContext
@@ -166,23 +166,14 @@
                         )
                         return
                     }
-                    val argTypeDescriptor = arg
-                        ?.type
-                        ?.constructor
-                        ?.declarationDescriptor as? ClassDescriptor
-                    if (argTypeDescriptor != null) {
-                        val sam = getSingleAbstractMethodOrNull(argTypeDescriptor)
-                        if (sam != null && sam.hasComposableAnnotation()) {
-                            return
-                        }
-                    }
 
                     // TODO(lmr): in future, we should check for CALLS_IN_PLACE contract
-                    val inlined = arg != null &&
-                        canBeInlineArgument(node.functionLiteral) &&
-                        isInline(arg.containingDeclaration) &&
-                        isInlineParameter(arg)
-                    if (!inlined) {
+                    val isInlined = isInlinedArgument(
+                        node.functionLiteral,
+                        bindingContext,
+                        true
+                    )
+                    if (!isInlined) {
                         illegalCall(context, reportOn)
                         return
                     } else {
@@ -332,7 +323,7 @@
         if (expectedType === TypeUtils.NO_EXPECTED_TYPE) return
         if (expectedType === TypeUtils.UNIT_EXPECTED_TYPE) return
         if (expectedType.isAnyOrNullableAny()) return
-        val expectedComposable = expectedType.hasComposableAnnotation()
+        val expectedComposable = c.hasComposableExpectedType(expression)
         if (expression is KtLambdaExpression) {
             val descriptor = bindingContext[BindingContext.FUNCTION, expression.functionLiteral]
                 ?: return
@@ -406,8 +397,7 @@
     if (this is VariableAsFunctionResolvedCall) {
         return false
     }
-    val candidateDescriptor = candidateDescriptor
-    return when (candidateDescriptor) {
+    return when (val candidateDescriptor = candidateDescriptor) {
         is ValueParameterDescriptor -> false
         is LocalVariableDescriptor -> false
         is PropertyDescriptor -> {
@@ -498,10 +488,7 @@
         // this lambda was marked as inferred to be composable
         return true
     }
-    // TODO(lmr): i'm not sure that this is actually needed at this point, since this should have
-    //  been covered by the TypeResolutionInterceptorExtension
-    val arg = getArgumentDescriptor(functionLiteral, bindingContext) ?: return false
-    return arg.type.hasComposableAnnotation()
+    return false
 }
 
 // the body of this function can have composable calls in it, even if it itself is not
@@ -517,15 +504,89 @@
     ] == true
 }
 
-internal fun getArgumentDescriptor(
-    argument: KtFunction,
-    bindingContext: BindingContext
-): ValueParameterDescriptor? {
-    val call = KtPsiUtil.getParentCallIfPresent(argument) ?: return null
-    val resolvedCall = call.getResolvedCall(bindingContext) ?: return null
-    val valueArgument = resolvedCall.call.getValueArgumentForExpression(argument) ?: return null
-    val mapping = resolvedCall.getArgumentMapping(valueArgument) as? ArgumentMatch ?: return null
-    return mapping.valueParameter
+// The resolution context usually contains a call position, which records
+// the ResolvedCall and ValueParameterDescriptor for the call that we are
+// currently resolving. However, it is possible to end up in the
+// [ComposableCallChecker] or [ComposeTypeResolutionInterceptorExtension]
+// before the frontend computes the call position (e.g., when intercepting
+// function literal descriptors).
+//
+// In this case, the function below falls back to looking at the parse tree
+// for `expression`, to determine whether we are resolving a value argument.
+private fun ResolutionContext<*>.getValueArgumentPosition(
+    expression: KtExpression
+): CallPosition.ValueArgumentPosition? =
+    when (val position = callPosition) {
+        is CallPosition.ValueArgumentPosition ->
+            position
+
+        is CallPosition.Unknown ->
+            getValueArgumentPositionFromPsi(expression, trace.bindingContext)
+
+        else ->
+            null
+    }
+
+private fun getValueArgumentPositionFromPsi(
+    expression: KtExpression,
+    context: BindingContext,
+): CallPosition.ValueArgumentPosition? {
+    val resolvedCall = KtPsiUtil
+        .getParentCallIfPresent(expression)
+        .getResolvedCall(context)
+        ?: return null
+
+    val valueArgument = resolvedCall.call.getValueArgumentForExpression(expression)
+        ?: return null
+
+    val argumentMatch = resolvedCall.getArgumentMapping(valueArgument) as? ArgumentMatch
+        ?: return null
+
+    return CallPosition.ValueArgumentPosition(
+        resolvedCall,
+        argumentMatch.valueParameter,
+        valueArgument
+    )
+}
+
+private fun getArgumentDescriptor(
+    expression: KtExpression,
+    context: BindingContext
+): ValueParameterDescriptor? =
+    getValueArgumentPositionFromPsi(expression, context)?.valueParameter
+
+internal fun ResolutionContext<*>.hasComposableExpectedType(expression: KtExpression): Boolean {
+    if (expectedType.hasComposableAnnotation())
+        return true
+
+    // The Kotlin frontend discards all annotations when computing function
+    // types for fun interfaces. As a workaround we retrieve the fun interface
+    // from the current value argument position and check the annotations on the
+    // underlying method.
+    if (expectedType.isSpecialType || !expectedType.isBuiltinFunctionalType)
+        return false
+
+    val position = getValueArgumentPosition(expression)
+        ?: return false
+
+    // There are two kinds of SAM conversions in Kotlin.
+    //
+    // - Explicit SAM conversion by calling a synthetic fun interface constructor,
+    //   i.e., `A { ... }` or `A(f)` for a fun interface `A`.
+    // - Implicit SAM conversion by calling a function which expects a fun interface
+    //   in a value parameter.
+    //
+    // For explicit SAM conversion we check for the presence of a synthetic call,
+    // otherwise we check the type of the value parameter descriptor.
+    val callDescriptor = position.resolvedCall.resultingDescriptor.original
+    val samDescriptor = if (callDescriptor is FunctionInterfaceConstructorDescriptor) {
+        callDescriptor.baseDescriptorForSynthetic
+    } else {
+        position.valueParameter.type.constructor.declarationDescriptor as? ClassDescriptor
+            ?: return false
+    }
+
+    return getSingleAbstractMethodOrNull(samDescriptor)?.hasComposableAnnotation() == true
 }
 
 fun List<KtAnnotationEntry>.hasComposableAnnotation(bindingContext: BindingContext): Boolean {
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeTypeResolutionInterceptorExtension.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeTypeResolutionInterceptorExtension.kt
index e9efd60..cc6f27f 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeTypeResolutionInterceptorExtension.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/ComposeTypeResolutionInterceptorExtension.kt
@@ -18,14 +18,12 @@
 
 import androidx.compose.compiler.plugins.kotlin.analysis.ComposeWritableSlices
 import androidx.compose.compiler.plugins.kotlin.analysis.ComposeWritableSlices.INFERRED_COMPOSABLE_DESCRIPTOR
-import org.jetbrains.kotlin.descriptors.ClassDescriptor
 import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
 import org.jetbrains.kotlin.extensions.internal.TypeResolutionInterceptorExtension
 import org.jetbrains.kotlin.psi.KtElement
 import org.jetbrains.kotlin.psi.KtLambdaExpression
 import org.jetbrains.kotlin.psi.psiUtil.getAnnotationEntries
 import org.jetbrains.kotlin.resolve.descriptorUtil.module
-import org.jetbrains.kotlin.resolve.sam.getSingleAbstractMethodOrNull
 import org.jetbrains.kotlin.types.KotlinType
 import org.jetbrains.kotlin.types.TypeUtils
 import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext
@@ -41,31 +39,20 @@
         expression: KtLambdaExpression,
         context: ExpressionTypingContext,
         descriptor: AnonymousFunctionDescriptor
-    ): AnonymousFunctionDescriptor {
-        if (descriptor.isSuspend) return descriptor
+    ): AnonymousFunctionDescriptor =
         if (
-            context.expectedType.hasComposableAnnotation() &&
-            !descriptor.hasComposableAnnotation()
+            !descriptor.isSuspend &&
+            !descriptor.hasComposableAnnotation() &&
+            context.hasComposableExpectedType(expression)
         ) {
             // If the expected type has an @Composable annotation then the literal function
             // expression should infer a an @Composable annotation
-            context.trace.record(INFERRED_COMPOSABLE_DESCRIPTOR, descriptor, true)
-            return descriptor.annotateAsComposable(context.scope.ownerDescriptor.module)
-        }
-        val arg = getArgumentDescriptor(expression.functionLiteral, context.trace.bindingContext)
-
-        val argTypeDescriptor = arg
-            ?.type
-            ?.constructor
-            ?.declarationDescriptor as? ClassDescriptor
-        if (argTypeDescriptor != null) {
-            val sam = getSingleAbstractMethodOrNull(argTypeDescriptor)
-            if (sam != null && sam.hasComposableAnnotation()) {
-                context.trace.record(INFERRED_COMPOSABLE_DESCRIPTOR, descriptor, true)
+            descriptor.annotateAsComposable(context.scope.ownerDescriptor.module).also {
+                context.trace.record(INFERRED_COMPOSABLE_DESCRIPTOR, it, true)
             }
+        } else {
+            descriptor
         }
-        return descriptor
-    }
 
     override fun interceptType(
         element: KtElement,
@@ -75,27 +62,9 @@
         if (resultType === TypeUtils.NO_EXPECTED_TYPE) return resultType
         if (element !is KtLambdaExpression) return resultType
 
-        val arg = getArgumentDescriptor(element.functionLiteral, context.trace.bindingContext)
-
-        val argTypeDescriptor = arg
-            ?.type
-            ?.constructor
-            ?.declarationDescriptor as? ClassDescriptor
-        if (argTypeDescriptor != null) {
-            val sam = getSingleAbstractMethodOrNull(argTypeDescriptor)
-            if (sam != null && sam.hasComposableAnnotation()) {
-                context.trace.record(
-                    ComposeWritableSlices.INFERRED_COMPOSABLE_LITERAL,
-                    element,
-                    true
-                )
-                return resultType.makeComposable(context.scope.ownerDescriptor.module)
-            }
-        }
-
         if (
             element.getAnnotationEntries().hasComposableAnnotation(context.trace.bindingContext) ||
-            context.expectedType.hasComposableAnnotation()
+            context.hasComposableExpectedType(element)
         ) {
             context.trace.record(ComposeWritableSlices.INFERRED_COMPOSABLE_LITERAL, element, true)
             return resultType.makeComposable(context.scope.ownerDescriptor.module)
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
index 909607f..42e0f75 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
@@ -2770,7 +2770,7 @@
     private fun visitRememberCall(expression: IrCall): IrExpression {
         val inputArgs = mutableListOf<IrExpression>()
         var hasSpreadArgs = false
-        var calculationArg: IrFunctionExpression? = null
+        var calculationArg: IrExpression? = null
         for (i in 0 until expression.valueArgumentsCount) {
             val param = expression.symbol.owner.valueParameters[i]
             val arg = expression.getValueArgument(i)
@@ -2781,7 +2781,7 @@
                 break
 
             when {
-                param.name.identifier == "calculation" && arg is IrFunctionExpression -> {
+                param.name.identifier == "calculation" -> {
                     calculationArg = arg
                 }
                 arg is IrVararg -> {
@@ -2820,12 +2820,10 @@
 
         encounteredComposableCall(withGroups = false)
 
-        val invalidExpr = if (inputArgs.isEmpty())
-            irConst(false)
-        else
-            inputArgs
-                .map { irChangedOrInferredChanged(it) }
-                .reduce { acc, changed -> irBooleanOr(acc, changed) }
+        val invalidExpr = inputArgs
+            .mapNotNull(::irChangedOrInferredChanged)
+            .reduceOrNull { acc, changed -> irBooleanOr(acc, changed) }
+            ?: irConst(false)
 
         return irCache(
             expression.startOffset,
@@ -2836,12 +2834,12 @@
         )
     }
 
-    private fun irChangedOrInferredChanged(arg: IrExpression): IrExpression {
+    private fun irChangedOrInferredChanged(arg: IrExpression): IrExpression? {
         val meta = paramMetaOf(arg, isProvided = true)
         val param = meta.maskParam
 
         return when {
-            meta.isStatic -> irConst(false)
+            meta.isStatic -> null
             meta.isCertain &&
                 meta.stability.knownStable() &&
                 param is IrChangedBitMaskVariable -> {
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/FunctionReferenceBuilder.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/FunctionReferenceBuilder.kt
index 0eb6bb1..145a7dc 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/FunctionReferenceBuilder.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/FunctionReferenceBuilder.kt
@@ -118,7 +118,6 @@
             returnType = callee.returnType
             isSuspend = callee.isSuspend
         }.apply {
-            annotations += superMethod.owner.annotations
             overriddenSymbols += superMethod
             dispatchReceiverParameter = parentAsClass.thisReceiver!!.copyTo(this)
             createLambdaInvokeMethod()
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrSourcePrinter.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrSourcePrinter.kt
index bfe53c8..3b7b143 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrSourcePrinter.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrSourcePrinter.kt
@@ -414,22 +414,7 @@
             return
         }
 
-        val dispatchReceiver = expression.dispatchReceiver
-        val extensionReceiver = expression.extensionReceiver
-        val dispatchIsSpecial = dispatchReceiver.let {
-            it is IrGetValue && it.symbol.owner.name.isSpecial
-        }
-        val extensionIsSpecial = extensionReceiver.let {
-            it is IrGetValue && it.symbol.owner.name.isSpecial
-        }
-
-        if (dispatchReceiver != null && !dispatchIsSpecial) {
-            dispatchReceiver.print()
-            print(".")
-        } else if (extensionReceiver != null && !extensionIsSpecial) {
-            extensionReceiver.print()
-            print(".")
-        }
+        expression.printExplicitReceiver(".")
 
         val prop = (function as? IrSimpleFunction)?.correspondingPropertySymbol?.owner
 
@@ -454,6 +439,25 @@
         }
     }
 
+    private fun IrMemberAccessExpression<*>.printExplicitReceiver(suffix: String? = null) {
+        val dispatchReceiver = dispatchReceiver
+        val extensionReceiver = extensionReceiver
+        val dispatchIsSpecial = dispatchReceiver.let {
+            it is IrGetValue && it.symbol.owner.name.isSpecial
+        }
+        val extensionIsSpecial = extensionReceiver.let {
+            it is IrGetValue && it.symbol.owner.name.isSpecial
+        }
+
+        if (dispatchReceiver != null && !dispatchIsSpecial) {
+            dispatchReceiver.print()
+            suffix?.let(::print)
+        } else if (extensionReceiver != null && !extensionIsSpecial) {
+            extensionReceiver.print()
+            suffix?.let(::print)
+        }
+    }
+
     private fun IrFunctionAccessExpression.printArgumentList(
         forceParameterNames: Boolean = false,
         forceSingleLine: Boolean = false
@@ -481,7 +485,7 @@
                 useParameterNames = true
             }
         }
-        val multiline = useParameterNames && !forceSingleLine
+        val multiline = arguments.isNotEmpty() && useParameterNames && !forceSingleLine
         if (arguments.isNotEmpty() || trailingLambda == null) {
             print("(")
             if (multiline) {
@@ -751,7 +755,10 @@
     override fun visitReturn(expression: IrReturn) {
         val value = expression.value
         // only print the return statement directly if it is not a lambda
-        if (expression.returnTargetSymbol.descriptor.name.asString() != "<anonymous>") {
+        val returnTarget = expression.returnTargetSymbol.owner
+        if (returnTarget !is IrFunction ||
+            returnTarget.name.asString() != "<anonymous>" &&
+            returnTarget.origin != IrDeclarationOrigin.ADAPTER_FOR_CALLABLE_REFERENCE) {
             print("return ")
         }
         if (expression.type.isUnit() || value.type.isUnit()) {
@@ -780,7 +787,7 @@
                 lhs.print()
                 print("--")
             }
-            IrStatementOrigin.LAMBDA -> {
+            IrStatementOrigin.LAMBDA, IrStatementOrigin.ADAPTED_FUNCTION_REFERENCE -> {
                 val function = expression.statements[0] as IrFunction
                 function.printAsLambda()
             }
@@ -1188,22 +1195,7 @@
 
     override fun visitFunctionReference(expression: IrFunctionReference) {
         val function = expression.symbol.owner
-        val dispatchReceiver = expression.dispatchReceiver
-        val extensionReceiver = expression.extensionReceiver
-        val dispatchIsSpecial = dispatchReceiver.let {
-            it is IrGetValue && it.symbol.owner.name.isSpecial
-        }
-        val extensionIsSpecial = extensionReceiver.let {
-            it is IrGetValue && it.symbol.owner.name.isSpecial
-        }
-
-        if (dispatchReceiver != null && !dispatchIsSpecial) {
-            dispatchReceiver.print()
-            print("::")
-        } else if (extensionReceiver != null && !extensionIsSpecial) {
-            extensionReceiver.print()
-            print("::")
-        }
+        expression.printExplicitReceiver("::")
 
         val prop = (function as? IrSimpleFunction)?.correspondingPropertySymbol?.owner
 
@@ -1242,7 +1234,10 @@
     }
 
     override fun visitPropertyReference(expression: IrPropertyReference) {
-        print("<<PROPREF>>")
+        val property = expression.symbol.owner
+        expression.printExplicitReceiver()
+        print("::")
+        print(property.name)
     }
 
     override fun visitSpreadElement(spread: IrSpreadElement) {
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/CircularProgressIndicatorTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/CircularProgressIndicatorTokens.kt
new file mode 100644
index 0000000..4f418ce
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/CircularProgressIndicatorTokens.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+package androidx.compose.material3.tokens
+
+import androidx.compose.ui.unit.dp
+
+internal object CircularProgressIndicatorTokens {
+    val ActiveIndicatorColor = ColorSchemeKey.Primary
+    val ActiveIndicatorWidth = 4.0.dp
+    val FourColorActiveIndicatorFourColor = ColorSchemeKey.TertiaryContainer
+    val FourColorActiveIndicatorOneColor = ColorSchemeKey.Primary
+    val FourColorActiveIndicatorThreeColor = ColorSchemeKey.Tertiary
+    val FourColorActiveIndicatorTwoColor = ColorSchemeKey.PrimaryContainer
+    val Size = 48.0.dp
+}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/LinearProgressIndicatorTokens.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/LinearProgressIndicatorTokens.kt
new file mode 100644
index 0000000..d34e1c9
--- /dev/null
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/tokens/LinearProgressIndicatorTokens.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+package androidx.compose.material3.tokens
+
+import androidx.compose.ui.unit.dp
+
+internal object LinearProgressIndicatorTokens {
+    val ActiveIndicatorColor = ColorSchemeKey.Primary
+    val ActiveIndicatorHeight = 4.0.dp
+    val FourColorActiveIndicatorFourColor = ColorSchemeKey.TertiaryContainer
+    val FourColorActiveIndicatorOneColor = ColorSchemeKey.Primary
+    val FourColorActiveIndicatorThreeColor = ColorSchemeKey.Tertiary
+    val FourColorActiveIndicatorTwoColor = ColorSchemeKey.PrimaryContainer
+    val TrackColor = ColorSchemeKey.SurfaceVariant
+    val TrackHeight = 4.0.dp
+}
diff --git a/compose/ui/ui-tooling-data/build.gradle b/compose/ui/ui-tooling-data/build.gradle
index 1ef1e2f..5bea127 100644
--- a/compose/ui/ui-tooling-data/build.gradle
+++ b/compose/ui/ui-tooling-data/build.gradle
@@ -31,7 +31,7 @@
 
     api "androidx.annotation:annotation:1.1.0"
 
-    api("androidx.compose.runtime:runtime:1.1.0-rc01")
+    api(project(":compose:runtime:runtime"))
     api(project(":compose:ui:ui"))
 
     androidTestImplementation project(":compose:ui:ui-test-junit4")
diff --git a/compose/ui/ui/build.gradle b/compose/ui/ui/build.gradle
index bc1d1e6..d9a8068 100644
--- a/compose/ui/ui/build.gradle
+++ b/compose/ui/ui/build.gradle
@@ -51,7 +51,7 @@
         // any dependency on this module.
         compileOnly(project(":compose:ui:ui-android-stubs"))
 
-        implementation(project(":compose:runtime:runtime"))
+        implementation("androidx.compose.runtime:runtime:1.1.0-rc01")
         implementation(project(":compose:ui:ui-util"))
         implementation(libs.kotlinStdlib)
         implementation("androidx.autofill:autofill:1.0.0")
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopPlatformInput.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopPlatformInput.desktop.kt
index 8b2d35d..b2de30d 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopPlatformInput.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/ui/platform/DesktopPlatformInput.desktop.kt
@@ -199,7 +199,7 @@
 
                 val comp = input.value.composition
                 val text = input.value.text
-                val range = TextRange(beginIndex, endIndex)
+                val range = TextRange(beginIndex, endIndex.coerceAtMost(text.length))
                 if (comp == null) {
                     val res = text.substring(range)
                     return AttributedString(res).iterator
diff --git a/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImplTest.java b/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImplTest.java
index 1f5a7e0..3e98566 100644
--- a/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImplTest.java
+++ b/core/core-google-shortcuts/src/androidTest/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImplTest.java
@@ -26,6 +26,8 @@
 import android.content.Context;
 import android.content.Intent;
 
+import androidx.appsearch.app.ShortcutAdapter;
+import androidx.appsearch.builtintypes.Timer;
 import androidx.core.content.pm.ShortcutInfoCompat;
 import androidx.core.google.shortcuts.builders.CapabilityBuilder;
 import androidx.core.google.shortcuts.builders.ParameterBuilder;
@@ -213,6 +215,39 @@
 
     @Test
     @SmallTest
+    public void onShortcutUpdated_entityShortcut_savesToAppIndex() throws Exception {
+        ArgumentCaptor<Indexable> indexableCaptor = ArgumentCaptor.forClass(Indexable.class);
+
+        Timer timer = new Timer.Builder(ShortcutAdapter.DEFAULT_NAMESPACE, "timer_id")
+                .setCreationTimestampMillis(1000)
+                .build();
+        ShortcutInfoCompat timerShortcut =
+                ShortcutAdapter.createShortcutBuilderFromDocument(mContext, timer)
+                        .build();
+
+        mShortcutInfoChangeListener.onShortcutUpdated(Collections.singletonList(timerShortcut));
+
+        verify(mFirebaseAppIndex, only()).update(indexableCaptor.capture());
+        List<Indexable> allValues = indexableCaptor.getAllValues();
+
+        Indexable expected = new Indexable.Builder("Timer")
+                .setMetadata(new Indexable.Metadata.Builder().setScore(0))
+                .setUrl(ShortcutUtils.getIndexableUrl(mContext, "timer_id"))
+                .put("_namespace", ShortcutAdapter.DEFAULT_NAMESPACE)
+                .put("_ttlMillis", 0)
+                .put("_creationTimestampMillis", 1000)
+                .put("id", "timer_id")
+                .put("length", 0)
+                .put("name", ShortcutAdapter.DEFAULT_NAMESPACE)
+                .put("remainingTime", 0)
+                .put("timerStatus", "Unknown")
+                .put("vibrate", false)
+                .build();
+        assertThat(allValues).containsExactly(expected);
+    }
+
+    @Test
+    @SmallTest
     public void onShortcutAdded_savesToAppIndex() throws Exception {
         ArgumentCaptor<Indexable> indexableCaptor = ArgumentCaptor.forClass(Indexable.class);
 
diff --git a/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImpl.java b/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImpl.java
index d8ad9a1..a312b72 100644
--- a/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImpl.java
+++ b/core/core-google-shortcuts/src/main/java/androidx/core/google/shortcuts/ShortcutInfoChangeListenerImpl.java
@@ -29,11 +29,15 @@
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
+import androidx.appsearch.app.GenericDocument;
+import androidx.appsearch.app.ShortcutAdapter;
 import androidx.core.content.pm.ShortcutInfoChangeListener;
 import androidx.core.content.pm.ShortcutInfoCompat;
 import androidx.core.google.shortcuts.builders.CapabilityBuilder;
 import androidx.core.google.shortcuts.builders.ParameterBuilder;
 import androidx.core.google.shortcuts.builders.ShortcutBuilder;
+import androidx.core.google.shortcuts.converters.AppSearchDocumentConverter;
+import androidx.core.google.shortcuts.converters.AppSearchDocumentConverterFactory;
 import androidx.core.google.shortcuts.utils.EntityUriUtils;
 import androidx.core.google.shortcuts.utils.ShortcutUtils;
 import androidx.core.graphics.drawable.IconCompat;
@@ -93,14 +97,30 @@
         // shortcuts will be indexed under their respective schema type, and capability-instance
         // shortcuts will be indexed in the general shortcut corpus.
         for (ShortcutInfoCompat shortcut : shortcuts) {
-            ShortcutBuilder shortcutBuilder = buildShortcutIndexable(shortcut);
+            GenericDocument entity = null;
+            // ShortcutAdapter is only available for Lollipop and above.
+            if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+                entity = ShortcutAdapter.extractDocument(shortcut);
+            }
 
-            // Capability-instance shortcuts may contain references to entity shortcuts. If
-            // that's the case, report usage for those entity shortcuts.
-            // TODO (b/207161241): use capability binding api directly from shortcut once it's
-            //  available.
-            maybeReportEntityUsage(shortcutBuilder);
-            indexables.add(shortcutBuilder.build());
+            if (entity == null) {
+                // API level < Lollipop, or Shortcut might be a capability-instance shortcut.
+                ShortcutBuilder shortcutBuilder = buildShortcutIndexable(shortcut);
+
+                // Capability-instance shortcuts may contain references to entity shortcuts. If
+                // that's the case, report usage for those entity shortcuts.
+                // TODO (b/207161241): use capability binding api directly from shortcut once it's
+                //  available.
+                maybeReportEntityUsage(shortcutBuilder);
+                indexables.add(shortcutBuilder.build());
+            } else {
+                // Shortcut is an entity shortcut.
+                AppSearchDocumentConverter converter =
+                        AppSearchDocumentConverterFactory.getConverter(entity.getSchemaType());
+                Indexable.Builder entityIndexableBuilder =
+                        converter.convertGenericDocument(mContext, entity);
+                indexables.add(entityIndexableBuilder.build());
+            }
         }
         mFirebaseAppIndex.update(indexables.toArray(new Indexable[0]));
     }
diff --git a/development/importMaven/build.gradle.kts b/development/importMaven/build.gradle.kts
index 5e20af1..f0b65e7 100644
--- a/development/importMaven/build.gradle.kts
+++ b/development/importMaven/build.gradle.kts
@@ -80,8 +80,8 @@
     google()
     gradlePluginPortal()
 
-    val allowBintray: String? = findProperty("allowBintray") as String?
-    if (allowBintray != null) {
+    val allowJetbrainsDev: String? = findProperty("allowJetbrainsDev") as String?
+    if (allowJetbrainsDev != null) {
         maven {
             url = uri("https://maven.pkg.jetbrains.space/kotlin/p/dokka/dev/")
             metadataSources {
@@ -89,28 +89,6 @@
             }
         }
         maven {
-            url = uri("https://dl.bintray.com/kotlin/kotlin-dev/")
-            metadataSources {
-                artifact()
-            }
-        }
-        maven {
-            url = uri("https://dl.bintray.com/kotlin/kotlin-eap/")
-            metadataSources {
-                artifact()
-            }
-        }
-        maven {
-            url = uri("https://dl.bintray.com/kotlin/kotlinx/")
-            metadataSources {
-                artifact()
-            }
-        }
-    }
-
-    val allowJetbrainsDev: String? = findProperty("allowJetbrainsDev") as String?
-    if (allowJetbrainsDev != null) {
-        maven {
             url = uri("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev")
             metadataSources {
                 artifact()
@@ -124,7 +102,6 @@
         }
     }
 
-
     listOf("macos", "macos-x86_64", "linux", "linux-x86_64").forEach { platstring ->
         ivy {
             setUrl("https://download.jetbrains.com/kotlin/native/builds/releases")
diff --git a/development/importMaven/import_maven_artifacts.py b/development/importMaven/import_maven_artifacts.py
index 50e8261..bbc9cc3 100755
--- a/development/importMaven/import_maven_artifacts.py
+++ b/development/importMaven/import_maven_artifacts.py
@@ -28,11 +28,6 @@
   to use for metalava prebuilt fetching.
 '''
 
-ALLOW_BINTRAY_HELP = '''
-  Whether or not to allow artifacts to be fetched from bintray in addition to jcenter, mavenCentral, etc
-  E.g. https://dl.bintray.com/kotlin/kotlin-dev/ and https://dl.bintray.com/kotlin/kotlinx/
-'''
-
 ALLOW_JETBRAINS_DEV_HELP = '''
   Whether or not to allow artifacts to be fetched from Jetbrains' dev repository
   E.g. https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev
@@ -51,8 +46,6 @@
                         required=True, dest='name')
     parser.add_argument('-mb', '--metalava-build-id', help=METALAVA_BUILD_ID_HELP,
                         required=False, dest='metalava_build_id')
-    parser.add_argument('-ab', '--allow-bintray', help=ALLOW_BINTRAY_HELP,
-                        required=False, action='store_true')
     parser.add_argument('-ajd', '--allow-jetbrains-dev', help=ALLOW_JETBRAINS_DEV_HELP,
                         required=False, action='store_true')
     parse_result = parser.parse_args()
@@ -65,8 +58,6 @@
     metalava_build_id = parse_result.metalava_build_id
     if (metalava_build_id):
       command = command + ' -PmetalavaBuildId=%s' % (metalava_build_id)
-    if (parse_result.allow_bintray):
-      command = command + ' -PallowBintray'
     if (parse_result.allow_jetbrains_dev):
       command = command + ' -PallowJetbrainsDev'
 
diff --git a/external/libyuv/build.gradle b/external/libyuv/build.gradle
index 3c2d124..32f07d2 100644
--- a/external/libyuv/build.gradle
+++ b/external/libyuv/build.gradle
@@ -67,39 +67,47 @@
     description = "libyuv is an open source project that includes YUV scaling and conversion functionality."
 }
 
-// Entire block is to workaround b/203448887
+// this block exists to debug flakes from b/206464070
 afterEvaluate {
-    tasks.named("bundleDebugLocalLintAar").configure {
-        it.dependsOn("prefabDebugConfigurePackage")
-    }
-    tasks.named("bundleReleaseAar").configure {
-        it.dependsOn("prefabReleaseConfigurePackage")
-    }
-    tasks.named("reportLibraryMetrics").configure {
-        it.dependsOn("prefabReleaseConfigurePackage")
-    }
+    def buildDir = project.buildDir
     tasks.named("prefabReleaseConfigurePackage").configure {
-        def releaseAbiJson = project.file("$buildDir/intermediates/prefab_package/release/prefab/modules/yuv/libs/android.armeabi-v7a/abi.json")
-        def releasePrefabJson = project.file("$buildDir/intermediates/prefab_package/release/prefab/prefab.json")
         it.doLast {
-            if (!releaseAbiJson.exists()) {
-                throw new GradleException("$releaseAbiJson does not exist")
+            def abis = ["android.arm64-v8a", "android.armeabi-v7a", "android.x86", "android.x86_64"]
+            abis.each { abi ->
+                def releaseAbiJson = new File("$buildDir/intermediates/prefab_package/release/prefab/modules/yuv/libs/${abi}/abi.json")
+                if (!releaseAbiJson.exists()) {
+                    throw new GradleException("$releaseAbiJson does not exist")
+                }
             }
+            def releasePrefabJson = new File("$buildDir/intermediates/prefab_package/release/prefab/prefab.json")
             if (!releasePrefabJson.exists()) {
                 throw new GradleException("$releasePrefabJson does not exist")
             }
+
+            def releaseConvertArgb = new File("$buildDir/intermediates/prefab_package/release/prefab/modules/yuv/include/libyuv/convert_argb.h")
+            if (!releaseConvertArgb.exists()) {
+                throw new GradleException("$releaseConvertArgb does not exist")
+            }
         }
     }
     tasks.named("prefabDebugConfigurePackage").configure {
-        def debugAbiJson = project.file("$buildDir/intermediates/prefab_package/debug/prefab/modules/yuv/libs/android.armeabi-v7a/abi.json")
-        def debugPrefabJson = project.file("$buildDir/intermediates/prefab_package/debug/prefab/prefab.json")
         it.doLast {
-            if (!debugAbiJson.exists()) {
-                throw new GradleException("$debugAbiJson does not exist")
+            def abis = ["android.arm64-v8a", "android.armeabi-v7a", "android.x86", "android.x86_64"]
+            abis.each { abi ->
+                def debugAbiJson = new File("$buildDir/intermediates/prefab_package/debug/prefab/modules/yuv/libs/${abi}/abi.json")
+                if (!debugAbiJson.exists()) {
+                    throw new GradleException("$debugAbiJson does not exist")
+                }
             }
+            def debugPrefabJson = new File("$buildDir/intermediates/prefab_package/debug/prefab/prefab.json")
             if (!debugPrefabJson.exists()) {
                 throw new GradleException("$debugPrefabJson does not exist")
             }
+
+            def debugConvertArgb = new File("$buildDir/intermediates/prefab_package/debug/prefab/modules/yuv/include/libyuv/convert_argb.h")
+            if (!debugConvertArgb.exists()) {
+                throw new GradleException("$debugConvertArgb does not exist")
+            }
         }
     }
 }
diff --git a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidget.kt b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidget.kt
index 778b197..ada24a1 100644
--- a/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidget.kt
+++ b/glance/glance-appwidget/src/androidMain/kotlin/androidx/glance/appwidget/GlanceAppWidget.kt
@@ -464,9 +464,14 @@
 internal data class AppWidgetId(val appWidgetId: Int) : GlanceId
 
 // Extract the sizes from the bundle
-internal fun Bundle.extractAllSizes(minSize: () -> DpSize): List<DpSize> =
-    getParcelableArrayList<SizeF>(AppWidgetManager.OPTION_APPWIDGET_SIZES)
-        ?.map { DpSize(it.width.dp, it.height.dp) } ?: estimateSizes(minSize)
+internal fun Bundle.extractAllSizes(minSize: () -> DpSize): List<DpSize> {
+    val sizes = getParcelableArrayList<SizeF>(AppWidgetManager.OPTION_APPWIDGET_SIZES)
+    return if (sizes.isNullOrEmpty()) {
+        estimateSizes(minSize)
+    } else {
+        sizes.map { DpSize(it.width.dp, it.height.dp) }
+    }
+}
 
 // If the list of sizes is not available, estimate it from the min/max width and height.
 // We can assume that the min width and max height correspond to the portrait mode and the max
diff --git a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/GlanceAppWidgetTest.kt b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/GlanceAppWidgetTest.kt
index 628de2b..210187d 100644
--- a/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/GlanceAppWidgetTest.kt
+++ b/glance/glance-appwidget/src/test/kotlin/androidx/glance/appwidget/GlanceAppWidgetTest.kt
@@ -22,6 +22,7 @@
 import android.content.res.Configuration
 import android.os.Build
 import android.os.Bundle
+import android.util.SizeF
 import android.widget.FrameLayout
 import android.widget.TextView
 import androidx.compose.runtime.Composable
@@ -430,6 +431,27 @@
             .isEqualTo(DpSize(180.dp, 180.dp))
     }
 
+    // Testing on pre-S and post-S to test both when OPTION_APPWIDGET_SIZES is present or not.
+    @Test
+    @Config(sdk = [Build.VERSION_CODES.Q, Build.VERSION_CODES.S])
+    fun extractAllSizes_shouldExtractSizesWhenPresent() {
+        val bundle = optionsBundleOf(listOf(DpSize(140.dp, 110.dp), DpSize(100.dp, 150.dp)))
+        assertThat(bundle.extractAllSizes { DpSize.Zero }).containsExactly(
+            DpSize(140.dp, 110.dp),
+            DpSize(100.dp, 150.dp)
+        )
+    }
+
+    @Test
+    fun extractAllSizes_emptyAppWidgetSizes_shouldExtractFromMinMax() {
+        val bundle = optionsBundleOf(listOf(DpSize(140.dp, 110.dp), DpSize(100.dp, 150.dp)))
+        bundle.putParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES, ArrayList<SizeF>())
+        assertThat(bundle.extractAllSizes { DpSize.Zero }).containsExactly(
+            DpSize(140.dp, 110.dp),
+            DpSize(100.dp, 150.dp)
+        )
+    }
+
     private fun optionsBundleOf(sizes: List<DpSize>): Bundle {
         require(sizes.isNotEmpty()) { "There must be at least one size" }
         val (minSize, maxSize) = sizes.fold(sizes[0] to sizes[0]) { acc, s ->
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index c5b17de..1246430 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -148,7 +148,7 @@
 robolectric = { module = "org.robolectric:robolectric", version = "4.7" }
 rxjava2 = { module = "io.reactivex.rxjava2:rxjava", version = "2.2.9" }
 rxjava3 = { module = "io.reactivex.rxjava3:rxjava", version = "3.0.0" }
-shadow = { module = "gradle.plugin.com.github.johnrengelman:shadow", version = "7.1.0" }
+shadow = { module = "gradle.plugin.com.github.johnrengelman:shadow", version = "7.1.1" }
 skiko = { module = "org.jetbrains.skiko:skiko-jvm", version.ref = "skiko" }
 skikoCommon = { module = "org.jetbrains.skiko:skiko", version.ref = "skiko" }
 skikoMacOsArm64 = { module = "org.jetbrains.skiko:skiko-jvm-runtime-macos-arm64", version.ref = "skiko" }
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeExt.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeExt.kt
index 4155174..b773e09 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeExt.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeExt.kt
@@ -155,14 +155,7 @@
         Variance.CONTRAVARIANT -> WildcardTypeName.supertypeOf(resolveTypeName())
         Variance.COVARIANT -> WildcardTypeName.subtypeOf(resolveTypeName())
         Variance.STAR -> {
-            // for star projected types, JavaPoet uses the name from the declaration if
-            // * is not given explicitly
-            if (type == null) {
-                // explicit *
-                WildcardTypeName.subtypeOf(TypeName.OBJECT)
-            } else {
-                WildcardTypeName.subtypeOf(type.typeName(resolver, typeArgumentTypeLookup))
-            }
+            WildcardTypeName.subtypeOf(TypeName.OBJECT)
         }
         else -> {
             if (hasJvmWildcardAnnotation()) {
@@ -187,7 +180,7 @@
     resolver: Resolver,
     typeArgumentTypeLookup: TypeArgumentTypeLookup
 ): TypeName {
-    return if (this.arguments.isNotEmpty()) {
+    return if (this.arguments.isNotEmpty() && !isRaw()) {
         val args: Array<TypeName> = this.arguments
             .map { typeArg ->
                 typeArg.typeName(
@@ -331,4 +324,11 @@
     }
     val parent = parent ?: return false
     return parent.hasSuppressWildcardsAnnotationInHierarchy()
+}
+
+internal fun KSType.isRaw(): Boolean {
+    // yes this is gross but KSP itself seems to be doing it as well
+    // https://github.com/google/ksp/blob/main/compiler-plugin/
+    // src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeImpl.kt#L85
+    return toString().startsWith("raw ")
 }
\ No newline at end of file
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolver.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolver.kt
index 9807eef..263b75f 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolver.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KSTypeVarianceResolver.kt
@@ -24,7 +24,6 @@
 import com.google.devtools.ksp.symbol.KSType
 import com.google.devtools.ksp.symbol.KSTypeArgument
 import com.google.devtools.ksp.symbol.KSTypeParameter
-import com.google.devtools.ksp.symbol.KSTypeReference
 import com.google.devtools.ksp.symbol.Modifier
 import com.google.devtools.ksp.symbol.Variance
 
@@ -51,7 +50,7 @@
         ksType: KSType,
         wildcardMode: WildcardMode,
         declarationType: KSType?
-    ): KSType = ksType.inheritVariance(declarationType, wildcardMode)
+    ): KSType = ksType.inheritVariance(declarationType, wildcardMode, ReferenceStack())
 
     /**
      * Update the variance of the arguments of this type based on the types declaration.
@@ -61,62 +60,68 @@
     private fun KSType.inheritVariance(
         declarationType: KSType?,
         wildcardMode: WildcardMode,
+        referenceStack: ReferenceStack
     ): KSType {
         if (arguments.isEmpty()) return this
-        // arrays don't inherit variance unless it is in an inherited method
-        if (this.declaration.qualifiedName?.asString() == KOTLIN_ARRAY_Q_NAME &&
-            declarationType == null
-        ) return this
-
-        // if we have type arguments but the declarationType doesn't, we should consider it like
-        // star projection.
-        // This happens when a given List<X> overrides T. In this case, we need to force X's
-        // wildcards
-        val starProject = declarationType != null && declarationType.arguments.isEmpty()
-
-        // need to swap arguments with the variance from declaration
-        val newArguments = arguments.mapIndexed { index, typeArg ->
-            val param = declaration.typeParameters.getOrNull(index)
-            val declarationArg = declarationType?.arguments?.getOrNull(index)
-            val argWildcardMode = if (starProject) {
-                WildcardMode.FORCED
-            } else {
-                wildcardMode
+        return referenceStack.withReference(this) {
+            // arrays don't inherit variance unless it is in an inherited method
+            if (this.declaration.qualifiedName?.asString() == KOTLIN_ARRAY_Q_NAME &&
+                declarationType == null
+            ) {
+                return@withReference this
             }
-            typeArg.inheritVariance(declarationArg, argWildcardMode, param)
-        }
-        return this.replace(newArguments)
-    }
 
-    private fun KSTypeReference.inheritVariance(
-        declarationType: KSTypeReference?,
-        wildcardMode: WildcardMode
-    ): KSTypeReference {
-        return resolve()
-            .inheritVariance(declarationType = declarationType?.resolve(), wildcardMode)
-            .createTypeReference()
+            // if we have type arguments but the declarationType doesn't, we should consider it like
+            // star projection.
+            // This happens when a given List<X> overrides T. In this case, we need to force X's
+            // wildcards
+            val starProject = declarationType != null && declarationType.arguments.isEmpty()
+
+            // need to swap arguments with the variance from declaration
+            val newArguments = arguments.mapIndexed { index, typeArg ->
+                val param = declaration.typeParameters.getOrNull(index)
+                val declarationArg = declarationType?.arguments?.getOrNull(index)
+                val argWildcardMode = if (starProject) {
+                    WildcardMode.FORCED
+                } else {
+                    wildcardMode
+                }
+                typeArg.inheritVariance(declarationArg, argWildcardMode, param, referenceStack)
+            }
+            this.replace(newArguments)
+        }
     }
 
     private fun KSTypeArgument.inheritVariance(
         declarationType: KSTypeArgument?,
         wildcardMode: WildcardMode,
-        param: KSTypeParameter?
+        param: KSTypeParameter?,
+        referenceStack: ReferenceStack
     ): KSTypeArgument {
         if (param == null) {
             return this
         }
         val myTypeRef = type ?: return this
 
+        val myType = myTypeRef.resolve()
+
+        if (referenceStack.contains(myType)) {
+            // self referencing type
+            return this
+        }
         if (variance != Variance.INVARIANT) {
             return resolver.getTypeArgument(
-                typeRef = myTypeRef.inheritVariance(declarationType?.type, wildcardMode),
+                typeRef = myType.inheritVariance(
+                    declarationType?.type?.resolve(),
+                    wildcardMode,
+                    referenceStack
+                ).createTypeReference(),
                 variance = variance
             )
         }
 
         // Now we need to guess from this type. If the type is final, it does not inherit unless
         // the parameter is CONTRAVARIANT (`in`).
-        val myType = myTypeRef.resolve()
         val shouldInherit = when {
             hasJvmWildcardAnnotation() -> {
                 // we actually don't need to check for wildcard annotation here as the TypeName
@@ -151,12 +156,17 @@
         } ?: param.variance
         return if (shouldInherit) {
             resolver.getTypeArgument(
-                typeRef = myTypeRef.inheritVariance(declarationType?.type, wildcardMode),
+                typeRef = myType.inheritVariance(
+                    declarationType?.type?.resolve(),
+                    wildcardMode,
+                    referenceStack
+                ).createTypeReference(),
                 variance = newVariance
             )
         } else {
             resolver.getTypeArgument(
-                typeRef = myTypeRef.inheritVariance(null, wildcardMode),
+                typeRef = myType.inheritVariance(null, wildcardMode, referenceStack)
+                    .createTypeReference(),
                 variance = variance
             )
         }
@@ -179,3 +189,27 @@
         SUPPRESSED
     }
 }
+
+/**
+ * Inheriting variance for self referencing types (e.g. Foo<T : Foo>) could go into an infinite
+ * loop. To avoid that issue, every time we visit a type, we keep it in the reference stack and
+ * if a type argument resolves to it, it will stop recursion.
+ */
+private class ReferenceStack {
+    @Suppress("PropertyName")
+    val _queue = ArrayDeque<KSType>()
+
+    fun contains(ksType: KSType) = _queue.contains(ksType)
+
+    inline fun <T> withReference(
+        ksType: KSType,
+        crossinline block: () -> T
+    ): T {
+        return try {
+            _queue.addLast(ksType)
+            block()
+        } finally {
+            _queue.removeLast()
+        }
+    }
+}
\ No newline at end of file
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
index 741f59d..299efe5 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeTest.kt
@@ -408,15 +408,17 @@
         val src = Source.kotlin(
             "Foo.kt",
             """
-            class SelfReferencing<T : SelfReferencing<T>>
+            class SelfReferencing<T : SelfReferencing<T>> {
+                fun method(sr: SelfReferencing<*>) { TODO() }
+            }
             """.trimIndent()
         )
         runProcessorTest(
             sources = listOf(src)
         ) { invocation ->
-            val elm = invocation.processingEnv.requireTypeElement("SelfReferencing")
-            val typeName = elm.type.typeName
-            assertThat(typeName.dumpToString(5)).isEqualTo(
+            val typeElement = invocation.processingEnv.requireTypeElement("SelfReferencing")
+            val parameter = typeElement.getMethodByJvmName("method").parameters.single()
+            assertThat(typeElement.type.typeName.dumpToString(5)).isEqualTo(
                 """
                 SelfReferencing<T>
                 | T
@@ -426,6 +428,12 @@
                 | > | > | T
                 """.trimIndent()
             )
+            assertThat(parameter.type.typeName.dumpToString(5)).isEqualTo(
+                """
+                SelfReferencing<?>
+                | ?
+                """.trimIndent()
+            )
         }
     }
 
@@ -435,15 +443,16 @@
             "SelfReferencing",
             """
             class SelfReferencing<T extends SelfReferencing<T>> {
+                static void method(SelfReferencing sr) {}
             }
             """.trimIndent()
         )
         runProcessorTest(
             sources = listOf(src)
         ) { invocation ->
-            val elm = invocation.processingEnv.requireTypeElement("SelfReferencing")
-            val dump = elm.type.typeName.dumpToString(5)
-            assertThat(dump).isEqualTo(
+            val typeElement = invocation.processingEnv.requireTypeElement("SelfReferencing")
+            val parameter = typeElement.getMethodByJvmName("method").parameters.single()
+            assertThat(typeElement.type.typeName.dumpToString(5)).isEqualTo(
                 """
                 SelfReferencing<T>
                 | T
@@ -453,6 +462,11 @@
                 | > | > | T
                 """.trimIndent()
             )
+            assertThat(parameter.type.typeName.dumpToString(5)).isEqualTo(
+                """
+                SelfReferencing
+                """.trimIndent()
+            )
         }
     }
 
@@ -461,15 +475,19 @@
         val src = Source.kotlin(
             "Foo.kt",
             """
-        open class Node<TX : Node<TX, RX>, RX : Node<RX, TX>> {
-        }
+            open class Node<TX : Node<TX, RX>, RX : Node<RX, TX>> {
+                fun allStar(node : Node<*, *>) { TODO() }
+                fun secondStar(node : Node<TX, *>) { TODO() }
+                fun firstStar(node : Node<*, RX>) { TODO() }
+                fun noStar(node : Node<TX, RX>) { TODO() }
+            }
             """.trimIndent()
         )
         runProcessorTest(
             sources = listOf(src)
         ) { invocation ->
-            val nodeElm = invocation.processingEnv.requireType("Node")
-            val nodeTypeName = nodeElm.typeName
+            val nodeElm = invocation.processingEnv.requireTypeElement("Node")
+            val nodeTypeName = nodeElm.type.typeName
             assertThat(nodeTypeName.dumpToString(5)).isEqualTo(
                 """
                 Node<TX, RX>
@@ -495,6 +513,69 @@
                 | > | > | RX
                 """.trimIndent()
             )
+            val paramTypeNames = nodeElm.getDeclaredMethods().associate {
+                it.name to it.parameters.single().type.typeName.dumpToString(5)
+            }
+            assertThat(paramTypeNames).containsExactlyEntriesIn(
+                mapOf(
+                    "allStar" to """
+                        Node<?, ?>
+                        | ?
+                        | ?
+                        """.trimIndent(),
+                    "firstStar" to """
+                        Node<?, RX>
+                        | ?
+                        | RX
+                        | > Node<RX, TX>
+                        | > | RX
+                        | > | > Node<RX, TX>
+                        | > | > | RX
+                        | > | > | TX
+                        | > | TX
+                        | > | > Node<TX, RX>
+                        | > | > | TX
+                        | > | > | RX
+                    """.trimIndent(),
+                    "secondStar" to """
+                        Node<TX, ?>
+                        | TX
+                        | > Node<TX, RX>
+                        | > | TX
+                        | > | > Node<TX, RX>
+                        | > | > | TX
+                        | > | > | RX
+                        | > | RX
+                        | > | > Node<RX, TX>
+                        | > | > | RX
+                        | > | > | TX
+                        | ?
+                    """.trimIndent(),
+                    "noStar" to """
+                        Node<TX, RX>
+                        | TX
+                        | > Node<TX, RX>
+                        | > | TX
+                        | > | > Node<TX, RX>
+                        | > | > | TX
+                        | > | > | RX
+                        | > | RX
+                        | > | > Node<RX, TX>
+                        | > | > | RX
+                        | > | > | TX
+                        | RX
+                        | > Node<RX, TX>
+                        | > | RX
+                        | > | > Node<RX, TX>
+                        | > | > | RX
+                        | > | > | TX
+                        | > | TX
+                        | > | > Node<TX, RX>
+                        | > | > | TX
+                        | > | > | RX
+                    """.trimIndent()
+                )
+            )
         }
     }
 
@@ -600,6 +681,92 @@
     }
 
     /**
+     * Repro for b/208207043
+     */
+    @Test
+    fun selfReferencing_starProjectedJava() {
+        val src = Source.kotlin(
+            "StyleBuilder.kt",
+            """
+            class StyleApplier<X, Y>
+            class StyleBuilder<out B : StyleBuilder<B, A>, out A : StyleApplier<*, *>>
+            class KotlinSubject {
+                fun subject_1(builder: StyleBuilder<*, *>)  {
+                }
+            }
+            """.trimIndent()
+        )
+        val javaSource = Source.java(
+            "JavaSubject",
+            """
+                public class JavaSubject {
+                    static void subject_1(StyleBuilder<?, ?> builder)  {
+                    }
+                    static void subject_2(StyleBuilder builder)  {
+                    }
+                }
+            """.trimIndent()
+        )
+        runProcessorTest(
+            sources = listOf(src, javaSource)
+        ) { invocation ->
+            val styleApplier = invocation.processingEnv.requireType("StyleApplier")
+            val styleBuilder = invocation.processingEnv.requireType("StyleBuilder")
+            assertThat(styleApplier.typeName.dumpToString(5)).isEqualTo(
+                """
+                StyleApplier<X, Y>
+                | X
+                | Y
+                """.trimIndent()
+            )
+            // we don't match what kapt generates here so this test is kept here to acknowledge the
+            // skew. Otoh, definition of B extending a type that has a type parameter that
+            // extends B is very very weird in practice :).
+            val bArgSignature = if (invocation.isKsp) {
+                "StyleBuilder<B, A>"
+            } else {
+                "StyleBuilder<? extends B, ? extends A>"
+            }
+            assertThat(styleBuilder.typeName.dumpToString(2)).isEqualTo(
+                """
+                StyleBuilder<B, A>
+                | B
+                | > $bArgSignature
+                | A
+                | > StyleApplier<?, ?>
+                """.trimIndent()
+            )
+
+            val javaSubject = invocation.processingEnv.requireTypeElement("JavaSubject")
+            val kotlinSubject = invocation.processingEnv.requireTypeElement("KotlinSubject")
+            // detect raw java types properly to be consistent
+            assertThat(
+                javaSubject.getMethodByJvmName("subject_2").parameters.single()
+                    .type.typeName.dumpToString(5)
+            ).isEqualTo("StyleBuilder")
+
+            val javaTypeName = javaSubject.getMethodByJvmName("subject_1").parameters.single()
+                .type.typeName.dumpToString(5)
+            val kotlinTypeName = kotlinSubject.getMethodByJvmName("subject_1").parameters.single()
+                .type.typeName.dumpToString(5)
+            assertThat(javaTypeName).isEqualTo(
+                """
+                StyleBuilder<?, ?>
+                | ?
+                | ?
+                """.trimIndent()
+            )
+            assertThat(kotlinTypeName).isEqualTo(
+                """
+                StyleBuilder<?, ?>
+                | ?
+                | ?
+                """.trimIndent()
+            )
+        }
+    }
+
+    /**
      * Reproduces the first bug in b/204415667
      */
     @Test
@@ -638,10 +805,10 @@
             invocation.processingEnv.requireType("foo.bar.Baz").let {
                 val superTypes = it.superTypes
                 assertThat(superTypes).hasSize(2)
-                val superClass = superTypes.first {
-                        type -> type.rawType.toString() == "foo.bar.AbstractClass" }
-                val superInterface = superTypes.first {
-                        type -> type.rawType.toString() == "foo.bar.MyInterface" }
+                val superClass =
+                    superTypes.first { type -> type.rawType.toString() == "foo.bar.AbstractClass" }
+                val superInterface =
+                    superTypes.first { type -> type.rawType.toString() == "foo.bar.MyInterface" }
                 assertThat(superClass.typeArguments).hasSize(1)
                 assertThat(superClass.typeArguments[0].typeName)
                     .isEqualTo(ClassName.get("java.lang", "String"))
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeExtTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeExtTest.kt
index 8aa640d..ad531fd 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeExtTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KSTypeExtTest.kt
@@ -131,11 +131,7 @@
                     .that(
                         typeName
                     ).isEqualTo(
-                        // kotlin does not have raw types hence it becomes List<Object>
-                        ParameterizedTypeName.get(
-                            List::class.java,
-                            Object::class.java
-                        )
+                        ClassName.get(List::class.java)
                     )
                 val nestedTypeName = subject.propertyType("nested").typeName(invocation.kspResolver)
                 assertWithMessage(subject.qualifiedName!!.asString())
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt
index 43002a4..3539d9a 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspTypeNamesGoldenTest.kt
@@ -181,6 +181,7 @@
                 suppressWildcards = true
             ),
             createOverrideSubjects(pkg),
+            createSelfReferencingType(pkg),
             createOverridesWithGenericArguments(pkg),
             createKotlinOverridesJavaSubjects(pkg),
             createKotlinOverridesJavaSubjectsWithGenerics(pkg),
@@ -225,6 +226,34 @@
         )
     }
 
+    private fun createSelfReferencingType(pkg: String): TestInput {
+        return TestInput(
+            sources = listOf(
+                Source.java("$pkg.SelfReferencingJava", """
+                    package $pkg;
+                    public interface SelfReferencingJava<T extends SelfReferencingJava<T>> {
+                        void method1(SelfReferencingJava sr);
+                        void method2(SelfReferencingJava<?> sr);
+                    }
+                """.trimIndent()),
+                Source.java("$pkg.SubSelfReferencingJava", """
+                    package $pkg;
+                    public interface SubSelfReferencingJava extends SelfReferencingJava<SubSelfReferencingJava> {}
+                """.trimIndent()),
+                Source.kotlin("SelfReferencing.kt",
+                    """
+                    package $pkg
+                    interface SelfReferencingKotlin<T : SelfReferencingKotlin<T>> {
+                        fun method1(sr: SelfReferencingKotlin<*>)
+                    }
+                    interface SubSelfReferencingKotlin : SelfReferencingKotlin<SubSelfReferencingKotlin>
+                    """.trimIndent()),
+            ),
+            subjects = listOf("SubSelfReferencingJava", "SelfReferencingJava",
+                "SelfReferencingKotlin", "SubSelfReferencingKotlin")
+        )
+    }
+
     /**
      * Subjects with variance and star projections.
      */
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/NullAwareTypeConverterStore.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/NullAwareTypeConverterStore.kt
index a8dc8dc..453ffd0 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/NullAwareTypeConverterStore.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/NullAwareTypeConverterStore.kt
@@ -52,6 +52,9 @@
      */
     private val knownColumnTypes: List<XType>
 ) : TypeConverterStore {
+    private val knownColumnTypeNames = knownColumnTypes.map {
+        it.typeName
+    }
     override val typeConverters = if (context.processingEnv.backend == Backend.KSP) {
         val processedConverters = typeConverters.toMutableList()
         // create copies for converters that receive non-null values
@@ -154,6 +157,11 @@
         return null
     }
 
+    private fun isColumnType(type: XType): Boolean {
+        // compare using type names to handle both null and non-null.
+        return knownColumnTypeNames.contains(type.typeName)
+    }
+
     private fun findConverterIntoStatementInternal(
         input: XType,
         columnTypes: List<XType>
@@ -162,7 +170,8 @@
         val queue = TypeConverterQueue(
             sourceType = input,
             // each converter is keyed on which type they will take us to
-            keyType = TypeConverter::to
+            keyType = TypeConverter::to,
+            isKnownColumnType = this::isColumnType
         )
 
         while (true) {
@@ -178,7 +187,8 @@
             columnTypes.forEach { columnType ->
                 if (columnType.isAssignableFrom(current.type)) {
                     queue.maybeEnqueue(
-                        current.appendConverter(
+                        prevEntry = current,
+                        converter = current.appendConverter(
                             UpCastTypeConverter(
                                 upCastFrom = current.type,
                                 upCastTo = columnType
@@ -188,7 +198,10 @@
                 }
             }
             getAllTypeConvertersFrom(current.type).forEach {
-                queue.maybeEnqueue(current.appendConverter(it))
+                queue.maybeEnqueue(
+                    prevEntry = current,
+                    converter = current.appendConverter(it)
+                )
             }
         }
         return null
@@ -237,7 +250,8 @@
             sourceType = output,
             // each converter is keyed on which type they receive as we are doing pathfinding
             // reverse here
-            keyType = TypeConverter::from
+            keyType = TypeConverter::from,
+            isKnownColumnType = this::isColumnType
         )
 
         while (true) {
@@ -253,7 +267,8 @@
             columnTypes.forEach { columnType ->
                 if (current.type.isAssignableFrom(columnType)) {
                     queue.maybeEnqueue(
-                        current.prependConverter(
+                        prevEntry = current,
+                        converter = current.prependConverter(
                             UpCastTypeConverter(
                                 upCastFrom = columnType,
                                 upCastTo = current.type
@@ -263,7 +278,10 @@
                 }
             }
             getAllTypeConvertersTo(current.type).forEach {
-                queue.maybeEnqueue(current.prependConverter(it))
+                queue.maybeEnqueue(
+                    prevEntry = current,
+                    converter = current.prependConverter(it)
+                )
             }
         }
         return null
@@ -348,6 +366,7 @@
      */
     private class TypeConverterQueue(
         sourceType: XType,
+        val isKnownColumnType: (XType) -> Boolean,
         val keyType: TypeConverter.() -> XType
     ) {
         // using insertion order as the tie breaker for reproducible builds.
@@ -361,7 +380,8 @@
             val typeConverterEntry = TypeConverterEntry(
                 tieBreakerPriority = insertionOrder++,
                 type = sourceType,
-                converter = null
+                converter = null,
+                convertsBetweenDbAndNonDbType = false
             )
             cheapestEntry[sourceType] = typeConverterEntry
             queue.add(typeConverterEntry)
@@ -384,14 +404,31 @@
          * or visited with a more expensive converter.
          */
         fun maybeEnqueue(
+            prevEntry: TypeConverterEntry,
             converter: TypeConverter
         ): Boolean {
             val keyType = converter.keyType()
+            val convertsBetweenDbAndNonDbType =
+                isKnownColumnType(converter.from) != isKnownColumnType(converter.to)
+            if (prevEntry.convertsBetweenDbAndNonDbType) {
+                // if previous entry converted from db type to user type (or vice versa), the new
+                // converter must also be converting between db type and non-db type.
+                if (!convertsBetweenDbAndNonDbType) {
+                    // prev entry already visited a column type, we cannot add any converters that
+                    // will visit a non-column type
+                    return false
+                }
+            }
             val existing = cheapestEntry[keyType]
             if (existing == null ||
                 (existing.converter != null && existing.converter.cost > converter.cost)
             ) {
-                val entry = TypeConverterEntry(insertionOrder++, keyType, converter)
+                val entry = TypeConverterEntry(
+                    tieBreakerPriority = insertionOrder++,
+                    type = keyType,
+                    converter = converter,
+                    convertsBetweenDbAndNonDbType = convertsBetweenDbAndNonDbType
+                )
                 cheapestEntry[keyType] = entry
                 queue.add(entry)
                 return true
@@ -404,7 +441,16 @@
         // when costs are equal, tieBreakerPriority is used
         val tieBreakerPriority: Int,
         val type: XType,
-        val converter: TypeConverter?
+        val converter: TypeConverter?,
+        /**
+         * If true, this entry converts between a column type and a non column type. Once a
+         * converter entry converts between a column type and user type, it can never go back. This
+         * is to ensure that we don't find converters between unrelated user types just because they
+         * both convert to the same database type.
+         * For instance, both TypeA and TypeB might be convertible to `String` to be persisted, yet,
+         * this doesn't mean TypeA can be converted into TypeB.
+         */
+        val convertsBetweenDbAndNonDbType: Boolean
     ) : Comparable<TypeConverterEntry> {
         override fun compareTo(other: TypeConverterEntry): Int {
             if (converter == null) {
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/solver/NullabilityAwareTypeConverterStoreTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/solver/NullabilityAwareTypeConverterStoreTest.kt
index 654bb51..3cc910d 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/solver/NullabilityAwareTypeConverterStoreTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/solver/NullabilityAwareTypeConverterStoreTest.kt
@@ -22,6 +22,7 @@
 import androidx.room.compiler.processing.util.XTestInvocation
 import androidx.room.compiler.processing.util.compiler.TestCompilationArguments
 import androidx.room.compiler.processing.util.compiler.compile
+import androidx.room.compiler.processing.util.runKspTest
 import androidx.room.compiler.processing.util.runProcessorTest
 import androidx.room.processor.Context.BooleanProcessorOptions.USE_NULL_AWARE_CONVERTER
 import androidx.room.processor.CustomConverterProcessor
@@ -123,7 +124,8 @@
             MyClass? to String!: nullableMyClassToNullableString / checkNotNull(String?)
             String! to MyClass!: (String! as String?) / nullableStringToNullableMyClass / checkNotNull(MyClass?)
             MyClass! to String!: (MyClass! as MyClass?) / nullableMyClassToNullableString / checkNotNull(String?)
-            """.trimIndent())
+            """.trimIndent()
+        )
     }
 
     @Test
@@ -144,7 +146,8 @@
             MyClass? to Cursor: nullableMyClassToNullableString
             Cursor to MyClass!: nullableStringToNullableMyClass / checkNotNull(MyClass?)
             MyClass! to Cursor: (MyClass! as MyClass?) / nullableMyClassToNullableString
-            """.trimIndent())
+            """.trimIndent()
+        )
     }
 
     @Test
@@ -671,6 +674,118 @@
     }
 
     /**
+     * Repro for b/206961709
+     * Often times, user will provide type converters that convert user type to database type.
+     * This does not mean that two types that can be converted into db types can be converted into
+     * each-other. e.g. if you can serialize TypeA and TypeB to String, it doesn't mean you can
+     * convert TypeA to TypeB.
+     */
+    @Test
+    fun dontAssumeUserTypesCanBeConvertedIntoEachOther() {
+        val converters = Source.kotlin(
+            "Converters.kt",
+            """
+            import androidx.room.*
+            class TypeA
+            class TypeB
+            object MyConverters {
+                @TypeConverter
+                fun nullableStringToTypeA(input: String?): TypeA { TODO() }
+                @TypeConverter
+                fun nullableTypeAToString(input: TypeA): String { TODO() }
+                @TypeConverter
+                fun nullableTypeBToNullableString(input: TypeB?): String? { TODO() }
+                @TypeConverter
+                fun nullableStringToNullableTypeB(input: String?): TypeB? { TODO() }
+            }
+        """.trimIndent()
+        )
+        runKspTest(
+            sources = listOf(converters),
+            options = mapOf(
+                USE_NULL_AWARE_CONVERTER.argName to "true"
+            )
+        ) { invocation ->
+            val store = invocation.createStore("MyConverters")
+            val aType = invocation.processingEnv.requireType("TypeA")
+            val bType = invocation.processingEnv.requireType("TypeB")
+            val stringType = invocation.processingEnv.requireType("java.lang.String")
+            assertThat(
+                store.findTypeConverter(
+                    aType,
+                    bType
+                )?.toSignature()
+            ).isNull()
+            assertThat(
+                store.findTypeConverter(
+                    bType,
+                    aType
+                )?.toSignature()
+            ).isNull()
+            assertThat(
+                store.findTypeConverter(
+                    input = bType.makeNonNullable(),
+                    output = stringType
+                )?.toSignature()
+            ).isEqualTo(
+                """
+                (TypeB! as TypeB?) / nullableTypeBToNullableString / checkNotNull(String?)
+                """.trimIndent()
+            )
+        }
+    }
+
+    @Test // 3P provided test case from https://issuetracker.google.com/issues/206961709#comment4
+    fun dontAssumeTypesCanBeConvertedUserCase() {
+        val source = Source.kotlin(
+            "Foo.kt", """
+            import androidx.room.*
+            import java.time.Instant
+            enum class Awesomeness {
+                AWESOME,
+                SUPER_DUPER_AWESOME,
+            }
+            @TypeConverters(
+                TimeConverter::class,
+                AwesomenessConverter::class,
+            )
+            class TimeConverter {
+                @TypeConverter
+                fun instantToValue(value: Instant?): String? { TODO() }
+
+                @TypeConverter
+                fun valueToInstant(value: String?): Instant? { TODO() }
+            }
+
+            class AwesomenessConverter {
+                @TypeConverter
+                fun awesomenessToValue(value: Awesomeness): String { TODO() }
+
+                @TypeConverter
+                fun valueToAwesomeness(value: String?): Awesomeness { TODO() }
+            }
+        """.trimIndent()
+        )
+        runKspTest(
+            sources = listOf(source)
+        ) { invocation ->
+            val store = invocation.createStore(
+                "TimeConverter", "AwesomenessConverter"
+            )
+            val instantType = invocation.processingEnv.requireType("java.time.Instant")
+            val stringType = invocation.processingEnv.requireType("java.lang.String")
+            assertThat(
+                store.findTypeConverter(
+                    input = instantType,
+                    output = stringType
+                )?.toSignature()
+            ).isEqualTo(
+                "(Instant! as Instant?) / instantToValue / checkNotNull(String?)"
+            )
+        }
+    }
+
+    /**
      * Collect results for conversion from String to our type
      */
     private fun collectStringConversionResults(
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/solver/Signatures.kt b/room/room-compiler/src/test/kotlin/androidx/room/solver/Signatures.kt
index 4ddee69..4b6fbf2 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/solver/Signatures.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/solver/Signatures.kt
@@ -33,7 +33,7 @@
 }
 
 fun XType.toSignature() =
-    "$typeName${nullability.toSignature()}".substringAfter("java.lang.")
+    "$typeName${nullability.toSignature()}".substringAfterLast(".")
 
 fun TypeConverter.toSignature(): String {
     return when (this) {
diff --git a/wear/compose/compose-foundation/build.gradle b/wear/compose/compose-foundation/build.gradle
index d6779a2..28b5b05 100644
--- a/wear/compose/compose-foundation/build.gradle
+++ b/wear/compose/compose-foundation/build.gradle
@@ -38,6 +38,7 @@
 
         implementation(libs.kotlinStdlib)
         implementation("androidx.compose.foundation:foundation-layout:1.1.0-rc01")
+        implementation(project(":profileinstaller:profileinstaller"))
 
         testImplementation(libs.testRules)
         testImplementation(libs.testRunner)
diff --git a/wear/compose/compose-foundation/src/androidMain/baseline-prof.txt b/wear/compose/compose-foundation/src/androidMain/baseline-prof.txt
new file mode 100644
index 0000000..e65b9e1
--- /dev/null
+++ b/wear/compose/compose-foundation/src/androidMain/baseline-prof.txt
@@ -0,0 +1,18 @@
+SPLandroidx/wear/compose/foundation/AnchorType;->**(**)**
+HSPLandroidx/wear/compose/foundation/ArcPaddingPx;->**(**)**
+Landroidx/wear/compose/foundation/ArcPaddingValues;
+SPLandroidx/wear/compose/foundation/ArcPaddingValuesImpl;->**(**)**
+HSPLandroidx/wear/compose/foundation/BasicCurvedTextKt**->**(**)**
+SPLandroidx/wear/compose/foundation/CurvedMeasuredChild;->**(**)**
+HSPLandroidx/wear/compose/foundation/CurvedRowKt**->**(**)**
+SPLandroidx/wear/compose/foundation/CurvedRowParentData;->**(**)**
+Landroidx/wear/compose/foundation/CurvedRowScope;
+SPLandroidx/wear/compose/foundation/CurvedRowScopeInstance;->**(**)**
+HSPLandroidx/wear/compose/foundation/CurvedTextDelegate;->**(**)**
+SPLandroidx/wear/compose/foundation/CurvedTextModifier;->**(**)**
+HSPLandroidx/wear/compose/foundation/CurvedTextStyle;->**(**)**
+SPLandroidx/wear/compose/foundation/CurvedTextStyleKt**->**(**)**
+HSPLandroidx/wear/compose/foundation/MeasuredChild;->**(**)**
+Landroidx/wear/compose/foundation/NormalMeasuredChild;
+SPLandroidx/wear/compose/foundation/RadialAlignment;->**(**)**
+Landroidx/wear/compose/foundation/RadialAlignmentImpl;
diff --git a/wear/compose/compose-material/build.gradle b/wear/compose/compose-material/build.gradle
index 96963ae..9676951 100644
--- a/wear/compose/compose-material/build.gradle
+++ b/wear/compose/compose-material/build.gradle
@@ -42,6 +42,7 @@
         implementation("androidx.compose.material:material-ripple:1.1.0-rc01")
         implementation("androidx.compose.ui:ui-util:1.1.0-rc01")
         implementation(project(":wear:compose:compose-foundation"))
+        implementation(project(":profileinstaller:profileinstaller"))
 
         androidTestImplementation(project(":compose:ui:ui-test"))
         androidTestImplementation(project(":compose:ui:ui-test-junit4"))
diff --git a/wear/compose/compose-material/src/androidMain/baseline-prof.txt b/wear/compose/compose-material/src/androidMain/baseline-prof.txt
new file mode 100644
index 0000000..dc96f70
--- /dev/null
+++ b/wear/compose/compose-material/src/androidMain/baseline-prof.txt
@@ -0,0 +1,103 @@
+HSPLandroidx/wear/compose/material/BrushPainter;->**(**)**
+Landroidx/wear/compose/material/ButtonColors;
+HSPLandroidx/wear/compose/material/ButtonDefaults;->**(**)**
+HSPLandroidx/wear/compose/material/ButtonKt**->**(**)**
+HSPLandroidx/wear/compose/material/CardDefaults;->**(**)**
+HSPLandroidx/wear/compose/material/CardKt**->**(**)**
+Landroidx/wear/compose/material/ChipColors;
+HSPLandroidx/wear/compose/material/ChipDefaults;->**(**)**
+HSPLandroidx/wear/compose/material/ChipKt**->**(**)**
+HSPLandroidx/wear/compose/material/Colors;->**(**)**
+HSPLandroidx/wear/compose/material/ColorsKt**->**(**)**
+SPLandroidx/wear/compose/material/CombinedPaddingValues;->**(**)**
+SPLandroidx/wear/compose/material/ComposableSingletons;->**(**)**
+HSPLandroidx/wear/compose/material/ContentAlpha;->**(**)**
+HSPLandroidx/wear/compose/material/ContentAlphaKt**->**(**)**
+HSPLandroidx/wear/compose/material/ContentColorKt**->**(**)**
+HSPLandroidx/wear/compose/material/CurvedTextKt**->**(**)**
+HSPLandroidx/wear/compose/material/DefaultButtonColors;->**(**)**
+HSPLandroidx/wear/compose/material/DefaultChipColors;->**(**)**
+HSPLandroidx/wear/compose/material/DefaultInlineSliderColors;->**(**)**
+HPLandroidx/wear/compose/material/DefaultScalingLazyListItemInfo;->**(**)**
+HPLandroidx/wear/compose/material/DefaultScalingLazyListLayoutInfo;->**(**)**
+HSPLandroidx/wear/compose/material/DefaultScalingParams;->**(**)**
+SPLandroidx/wear/compose/material/DefaultTimeSource;->**(**)**
+HSPLandroidx/wear/compose/material/DefaultTimeSourceKt**->**(**)**
+SPLandroidx/wear/compose/material/DefaultToggleButtonColors;->**(**)**
+HSPLandroidx/wear/compose/material/DefaultToggleChipColors;->**(**)**
+PLandroidx/wear/compose/material/DialogDefaults;->**(**)**
+HPLandroidx/wear/compose/material/DialogKt**->**(**)**
+Landroidx/wear/compose/material/EmptyScalingLazyListLayoutInfo;
+HSPLandroidx/wear/compose/material/FortyFiveDegreeLinearGradient;->**(**)**
+Landroidx/wear/compose/material/FractionPositionIndicatorState;
+Landroidx/wear/compose/material/FractionalThreshold;
+HSPLandroidx/wear/compose/material/IconKt**->**(**)**
+SPLandroidx/wear/compose/material/ImageResources;->**(**)**
+Landroidx/wear/compose/material/ImageWithScrimPainter;
+Landroidx/wear/compose/material/InlineSliderColors;
+HSPLandroidx/wear/compose/material/InlineSliderDefaults;->**(**)**
+Landroidx/wear/compose/material/LazyColumnStateAdapter;
+HSPLandroidx/wear/compose/material/ListHeaderKt**->**(**)**
+HSPLandroidx/wear/compose/material/MaterialRippleTheme;->**(**)**
+SPLandroidx/wear/compose/material/MaterialTextSelectionColorsKt**->**(**)**
+HSPLandroidx/wear/compose/material/MaterialTheme;->**(**)**
+SPLandroidx/wear/compose/material/MaterialThemeKt**->**(**)**
+SPLandroidx/wear/compose/material/PickerDefaults;->**(**)**
+HSPLandroidx/wear/compose/material/PickerKt**->**(**)**
+Landroidx/wear/compose/material/PickerScope;
+SPLandroidx/wear/compose/material/PickerScopeImpl;->**(**)**
+SPLandroidx/wear/compose/material/PickerState;->**(**)**
+HSPLandroidx/wear/compose/material/PositionIndicatorKt**->**(**)**
+Landroidx/wear/compose/material/PositionIndicatorState;
+Landroidx/wear/compose/material/R;
+SPLandroidx/wear/compose/material/RangeDefaults;->**(**)**
+HSPLandroidx/wear/compose/material/RangeDefaultsKt**->**(**)**
+SPLandroidx/wear/compose/material/RangeIcons;->**(**)**
+SPLandroidx/wear/compose/material/ResistanceConfig;->**(**)**
+HSPLandroidx/wear/compose/material/Resources_androidKt**->**(**)**
+HSPLandroidx/wear/compose/material/ScaffoldKt**->**(**)**
+HPLandroidx/wear/compose/material/ScaleAndAlpha;->**(**)**
+Landroidx/wear/compose/material/ScalingLazyColumnDefaults;
+HSPLandroidx/wear/compose/material/ScalingLazyColumnKt**->**(**)**
+HPLandroidx/wear/compose/material/ScalingLazyColumnMeasureKt**->**(**)**
+Landroidx/wear/compose/material/ScalingLazyColumnStateAdapter;
+Landroidx/wear/compose/material/ScalingLazyListItemInfo;
+Landroidx/wear/compose/material/ScalingLazyListItemScope;
+SPLandroidx/wear/compose/material/ScalingLazyListItemScopeImpl;->**(**)**
+Landroidx/wear/compose/material/ScalingLazyListLayoutInfo;
+SPLandroidx/wear/compose/material/ScalingLazyListScope;->**(**)**
+HSPLandroidx/wear/compose/material/ScalingLazyListScopeImpl;->**(**)**
+HSPLandroidx/wear/compose/material/ScalingLazyListState;->**(**)**
+Landroidx/wear/compose/material/ScalingLazyListStateKt;
+Landroidx/wear/compose/material/ScalingParams;
+HSPLandroidx/wear/compose/material/ScrollStateAdapter;->**(**)**
+SPLandroidx/wear/compose/material/Shapes;->**(**)**
+SPLandroidx/wear/compose/material/ShapesKt**->**(**)**
+HSPLandroidx/wear/compose/material/SliderKt**->**(**)**
+SPLandroidx/wear/compose/material/SqueezeMotion;->**(**)**
+SPLandroidx/wear/compose/material/StepperDefaults;->**(**)**
+HSPLandroidx/wear/compose/material/StepperKt**->**(**)**
+SPLandroidx/wear/compose/material/SwipeDismissTarget;->**(**)**
+Landroidx/wear/compose/material/SwipeProgress;
+SPLandroidx/wear/compose/material/SwipeToDismissBoxDefaults;->**(**)**
+HSPLandroidx/wear/compose/material/SwipeToDismissBoxKt**->**(**)**
+SPLandroidx/wear/compose/material/SwipeToDismissBoxState;->**(**)**
+SPLandroidx/wear/compose/material/SwipeableDefaults;->**(**)**
+HSPLandroidx/wear/compose/material/SwipeableKt**->**(**)**
+HSPLandroidx/wear/compose/material/SwipeableState;->**(**)**
+HSPLandroidx/wear/compose/material/TextKt**->**(**)**
+Landroidx/wear/compose/material/ThresholdConfig;
+SPLandroidx/wear/compose/material/TimeBroadcastReceiver;->**(**)**
+Landroidx/wear/compose/material/TimeSource;
+SPLandroidx/wear/compose/material/TimeTextDefaults;->**(**)**
+HSPLandroidx/wear/compose/material/TimeTextKt**->**(**)**
+Landroidx/wear/compose/material/ToggleButtonColors;
+HSPLandroidx/wear/compose/material/ToggleButtonDefaults;->**(**)**
+HSPLandroidx/wear/compose/material/ToggleButtonKt**->**(**)**
+Landroidx/wear/compose/material/ToggleChipColors;
+HSPLandroidx/wear/compose/material/ToggleChipDefaults;->**(**)**
+HSPLandroidx/wear/compose/material/ToggleChipKt**->**(**)**
+HSPLandroidx/wear/compose/material/Typography;->**(**)**
+HSPLandroidx/wear/compose/material/TypographyKt**->**(**)**
+HSPLandroidx/wear/compose/material/VignetteKt**->**(**)**
+SPLandroidx/wear/compose/material/VignettePosition;->**(**)**
diff --git a/wear/compose/compose-navigation/build.gradle b/wear/compose/compose-navigation/build.gradle
index b9cf22d..ef1ab1c 100644
--- a/wear/compose/compose-navigation/build.gradle
+++ b/wear/compose/compose-navigation/build.gradle
@@ -35,6 +35,7 @@
 
     implementation(libs.kotlinStdlib)
     implementation("androidx.navigation:navigation-compose:2.4.0-rc01")
+    implementation(project(":profileinstaller:profileinstaller"))
 
     androidTestImplementation(project(":compose:test-utils"))
     androidTestImplementation(project(":compose:ui:ui-test-junit4"))
diff --git a/wear/compose/compose-navigation/src/androidMain/baseline-prof.txt b/wear/compose/compose-navigation/src/androidMain/baseline-prof.txt
new file mode 100644
index 0000000..a33330f
--- /dev/null
+++ b/wear/compose/compose-navigation/src/androidMain/baseline-prof.txt
@@ -0,0 +1,4 @@
+SPLandroidx/wear/compose/navigation/NavGraphBuilderKt**->**(**)**
+SPLandroidx/wear/compose/navigation/SwipeDismissableNavHostControllerKt**->**(**)**
+HSPLandroidx/wear/compose/navigation/SwipeDismissableNavHostKt**->**(**)**
+HSPLandroidx/wear/compose/navigation/WearNavigator;->**(**)**
diff --git a/wear/compose/integration-tests/macrobenchmark-target/build.gradle b/wear/compose/integration-tests/macrobenchmark-target/build.gradle
index df9ad99..c2c870b 100644
--- a/wear/compose/integration-tests/macrobenchmark-target/build.gradle
+++ b/wear/compose/integration-tests/macrobenchmark-target/build.gradle
@@ -43,7 +43,9 @@
     implementation(project(":compose:ui:ui-tooling"))
     implementation("androidx.activity:activity-compose:1.3.1")
     implementation(project(":profileinstaller:profileinstaller"))
+    implementation project(path: ':wear:compose:compose-foundation')
     implementation project(path: ':wear:compose:compose-material')
+    implementation project(path: ':wear:compose:compose-navigation')
 }
 
 android.defaultConfig.minSdkVersion 25
\ No newline at end of file
diff --git a/wear/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml b/wear/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
index 76fba1c..8abaf1d 100644
--- a/wear/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
+++ b/wear/compose/integration-tests/macrobenchmark-target/src/main/AndroidManifest.xml
@@ -72,6 +72,17 @@
             </intent-filter>
         </activity>
 
+        <activity
+            android:name=".BaselineActivity"
+            android:theme="@style/AppTheme"
+            android:exported="true">
+            <intent-filter>
+                <action android:name=
+                    "androidx.wear.compose.integration.macrobenchmark.target.BASELINE_ACTIVITY" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
     </application>
 
     <uses-permission android:name="android.permission.WAKE_LOCK" />
diff --git a/wear/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/wear/compose/integration/macrobenchmark/target/BaselineActivity.kt b/wear/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/wear/compose/integration/macrobenchmark/target/BaselineActivity.kt
new file mode 100644
index 0000000..a149f5c
--- /dev/null
+++ b/wear/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/wear/compose/integration/macrobenchmark/target/BaselineActivity.kt
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.integration.macrobenchmark.target
+
+import androidx.activity.ComponentActivity
+import android.os.Bundle
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.wear.compose.foundation.CurvedRow
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.navigation.NavHostController
+import androidx.wear.compose.foundation.ArcPaddingValues
+import androidx.wear.compose.foundation.BasicCurvedText
+import androidx.wear.compose.foundation.CurvedTextStyle
+import androidx.wear.compose.material.AlertDialog
+import androidx.wear.compose.material.AppCard
+import androidx.wear.compose.material.Button
+import androidx.wear.compose.material.Card
+import androidx.wear.compose.material.Chip
+import androidx.wear.compose.material.ChipDefaults
+import androidx.wear.compose.material.CompactButton
+import androidx.wear.compose.material.CompactChip
+import androidx.wear.compose.material.ConfirmationDialog
+import androidx.wear.compose.material.CurvedText
+import androidx.wear.compose.material.ExperimentalWearMaterialApi
+import androidx.wear.compose.material.InlineSlider
+import androidx.wear.compose.material.ListHeader
+import androidx.wear.compose.material.MaterialTheme
+import androidx.wear.compose.material.Picker
+import androidx.wear.compose.material.PositionIndicator
+import androidx.wear.compose.material.Scaffold
+import androidx.wear.compose.material.SplitToggleChip
+import androidx.wear.compose.material.Stepper
+import androidx.wear.compose.material.Text
+import androidx.wear.compose.material.TimeText
+import androidx.wear.compose.material.TitleCard
+import androidx.wear.compose.material.ToggleButton
+import androidx.wear.compose.material.ToggleChip
+import androidx.wear.compose.material.Vignette
+import androidx.wear.compose.material.VignettePosition
+import androidx.wear.compose.material.rememberPickerState
+import androidx.wear.compose.navigation.SwipeDismissableNavHost
+import androidx.wear.compose.navigation.composable
+import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
+
+private val ALERT_DIALOG = "alert-dialog"
+private val CONFIRMATION_DIALOG = "confirmation-dialog"
+private val STEPPER = "stepper"
+private val SWIPE_DISMISS = "swipe-dismiss"
+
+class BaselineActivity : ComponentActivity() {
+
+    @OptIn(ExperimentalWearMaterialApi::class)
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        setContent {
+            val navController = rememberSwipeDismissableNavController()
+            val scrollState = rememberScrollState()
+
+            MaterialTheme {
+                Scaffold(
+                    timeText = { TimeText() },
+                    positionIndicator = { PositionIndicator(scrollState = scrollState) },
+                    vignette = {
+                        Vignette(vignettePosition = VignettePosition.TopAndBottom)
+                    },
+                ) {
+                    SwipeDismissableNavHost(
+                        navController = navController,
+                        startDestination = "start",
+                        modifier = Modifier
+                            .background(MaterialTheme.colors.background)
+                            .semantics { contentDescription = SWIPE_DISMISS }
+                    ) {
+                        composable("start") {
+                            Box {
+                                CurvedTexts()
+                                Column(
+                                    modifier = Modifier
+                                        .fillMaxSize()
+                                        .verticalScroll(state = scrollState)
+                                        .padding(vertical = 16.dp)
+                                        .semantics { contentDescription = CONTENT_DESCRIPTION },
+                                    verticalArrangement = Arrangement.Center,
+                                    horizontalAlignment = Alignment.CenterHorizontally
+                                ) {
+                                    Dialogs(navController)
+                                    Steppers(navController)
+                                    Buttons()
+                                    Cards()
+                                    Chips()
+                                    Sliders()
+                                    Pickers()
+                                }
+                            }
+                        }
+                        composable(ALERT_DIALOG) {
+                            AlertDialog(
+                                title = { Text("Alert") },
+                                negativeButton = {},
+                                positiveButton = {},
+                            )
+                        }
+                        composable(CONFIRMATION_DIALOG) {
+                            ConfirmationDialog(
+                                onTimeout = { navController.popBackStack() },
+                                content = { Text("Confirmation") },
+                            )
+                        }
+                        composable(STEPPER) {
+                            var value by remember { mutableStateOf(2f) }
+                            Stepper(
+                                value = value,
+                                onValueChange = { value = it },
+                                valueRange = 1f..4f,
+                                steps = 7
+                            ) { Text("Value: $value") }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+@Composable
+fun Buttons() {
+    ListHeader { Text("Buttons") }
+    Button(onClick = {}) { Text("Button") }
+    CompactButton(onClick = {}) { Text("CompactButton") }
+    ToggleButton(checked = true, onCheckedChange = {}) { Text("ToggleButton") }
+}
+
+@Composable
+fun Cards() {
+    ListHeader { Text("Cards") }
+    Card(onClick = {}) { Text("Card") }
+    AppCard(onClick = {}, appName = {}, time = {}, title = {}) { Text("AppCard") }
+    TitleCard(onClick = {}, title = {}) { Text("TitleCard") }
+}
+
+@Composable
+fun Chips() {
+    ListHeader { Text("Chips") }
+    Chip(onClick = {}, colors = ChipDefaults.primaryChipColors()) { Text("Chip") }
+    CompactChip(onClick = {}, label = { Text("CompactChip") })
+    ToggleChip(true, onCheckedChange = {}, label = { Text("ToggleChip") })
+    SplitToggleChip(
+        checked = true,
+        onCheckedChange = {},
+        label = { Text("SplitToggleChip") },
+        onClick = {})
+}
+
+@Composable
+fun CurvedTexts() {
+    CurvedRow(anchor = 235f) {
+        BasicCurvedText(
+            "Basic",
+            CurvedTextStyle(
+                fontSize = 16.sp,
+                color = Color.White,
+                background = MaterialTheme.colors.background
+            ),
+            contentArcPadding = ArcPaddingValues(2.dp)
+        )
+    }
+    CurvedRow(anchor = 310f) {
+        CurvedText(text = "Curved")
+    }
+}
+
+@Composable
+fun Dialogs(navController: NavHostController) {
+    ListHeader { Text("Dialogs") }
+    CompactChip(
+        onClick = { navController.navigate(ALERT_DIALOG) },
+        colors = ChipDefaults.primaryChipColors(),
+        label = { Text(ALERT_DIALOG) },
+        modifier = Modifier.semantics { contentDescription = ALERT_DIALOG },
+
+        )
+    CompactChip(
+        onClick = { navController.navigate(CONFIRMATION_DIALOG) },
+        colors = ChipDefaults.primaryChipColors(),
+        label = { Text(CONFIRMATION_DIALOG) },
+        modifier = Modifier.semantics { contentDescription = CONFIRMATION_DIALOG },
+    )
+}
+
+@Composable
+fun Pickers() {
+    ListHeader { Text("Pickers") }
+    val items = listOf("One", "Two", "Three", "Four", "Five")
+    Picker(
+        items.size,
+        state = rememberPickerState(),
+        option = { Text(items[it]) },
+        modifier = Modifier.size(100.dp, 100.dp),
+    )
+}
+
+@Composable
+fun Sliders() {
+    ListHeader { Text("Sliders") }
+    var value by remember { mutableStateOf(4.5f) }
+    InlineSlider(
+        value = value,
+        onValueChange = { value = it },
+        valueRange = 3f..6f,
+        steps = 5,
+        segmented = false
+    )
+}
+
+@Composable
+fun Steppers(navController: NavHostController) {
+    ListHeader { Text("Steppers") }
+    CompactChip(
+        onClick = { navController.navigate(STEPPER) },
+        colors = ChipDefaults.primaryChipColors(),
+        label = { Text(STEPPER) },
+        modifier = Modifier.semantics { contentDescription = STEPPER },
+        )
+}
diff --git a/wear/compose/integration-tests/macrobenchmark/build.gradle b/wear/compose/integration-tests/macrobenchmark/build.gradle
index c4e3c35..a37be4c 100644
--- a/wear/compose/integration-tests/macrobenchmark/build.gradle
+++ b/wear/compose/integration-tests/macrobenchmark/build.gradle
@@ -1,3 +1,5 @@
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
 /*
  * Copyright 2021 The Android Open Source Project
  *
@@ -37,6 +39,13 @@
     androidTestImplementation(libs.testUiautomator)
 }
 
+// Allow usage of Kotlin's @OptIn.
+tasks.withType(KotlinCompile).configureEach {
+    kotlinOptions {
+        freeCompilerArgs += [ '-Xopt-in=kotlin.RequiresOptIn' ]
+    }
+}
+
 // Define a task dependency so the app is installed before we run macro benchmarks.
 tasks.getByPath(":wear:compose:integration-tests:macrobenchmark:connectedCheck")
  .dependsOn(tasks.getByPath(":wear:compose:integration-tests:macrobenchmark-target:installRelease"))
diff --git a/wear/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/wear/compose/integration/macrobenchmark/BaselineProfile.kt b/wear/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/wear/compose/integration/macrobenchmark/BaselineProfile.kt
new file mode 100644
index 0000000..3789840
--- /dev/null
+++ b/wear/compose/integration-tests/macrobenchmark/src/androidTest/java/androidx/wear/compose/integration/macrobenchmark/BaselineProfile.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.integration.macrobenchmark.test
+
+import android.content.Intent
+import android.graphics.Point
+import androidx.benchmark.macro.ExperimentalBaselineProfilesApi
+import androidx.benchmark.macro.junit4.BaselineProfileRule
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import androidx.testutils.createCompilationParams
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runners.Parameterized
+
+// This test generates a baseline profile rules file that can be parsed to produce the
+// baseline-prof.txt files for the Wear Compose libraries.
+// 1) Build and install debug build of androidx.wear.compose.integration.macrobenchmark-target
+//    onto a device (not minified, because we need non-obsfuscated method/class names)
+// 2) Run this test on the device - search for Benchmark in logcat to discover the location
+//    of the generated file
+//    e.g. /sdcard/Android/media/androidx.wear.compose.integration.macrobenchmark.test/
+//         additional_test_output/BaselineProfile_profile-baseline-prof.txt
+// 3) Copy the generated file to your workspace:
+//    adb pull <path-to-file-from-step-2 e.g. xxx/BaselineProfile_profile-baseline-prof.txt>
+//             <workspace path e.g. xxx/frameworks/support/wear/compose/>
+// 4) Build profileparser:
+//    If necessary, include it in settings.gradle:
+//      includeProject(":wear:compose:integration-tests:profileparser",
+//                     "wear/compose/integration-tests/profileparser",
+//                     [BuildType.MAIN])
+//    ./gradlew :wear:compose:integration-tests:profileparser:assemble
+// 5) Run profileparser for each of wear.compose.material, wear.compose.foundation and
+//    wear.compose.navigation. From <workspace>/frameworks/support:
+//    java -jar
+//      ../../out/androidx/wear/compose/integration-tests/profileparser/build/libs/profileparser-all.jar
+//      <input-generated-file eg ./wear/compose/BaselineProfile_profile-baseline-prof.txt>
+//      <library-name e.g. androidx/wear/compose/material>
+//      <output-file eg ./wear/compose/compose-material/src/androidMain/baseline-prof.txt>
+@LargeTest
+@SdkSuppress(minSdkVersion = 29)
+@OptIn(ExperimentalBaselineProfilesApi::class)
+class BaselineProfile {
+
+    @get:Rule
+    val baselineRule = BaselineProfileRule()
+
+    private lateinit var device: UiDevice
+    private val ALERT_DIALOG = "alert-dialog"
+    private val CONFIRMATION_DIALOG = "confirmation-dialog"
+    private val STEPPER = "stepper"
+
+    @Before
+    fun setUp() {
+        val instrumentation = InstrumentationRegistry.getInstrumentation()
+        device = UiDevice.getInstance(instrumentation)
+    }
+
+    @Test
+    fun profile() {
+        baselineRule.collectBaselineProfile(
+            packageName = PACKAGE_NAME,
+            setupBlock = {
+                val intent = Intent()
+                intent.action = ACTION
+                startActivityAndWait(intent)
+            },
+            profileBlock = {
+                testDestination(ALERT_DIALOG)
+                testDestination(CONFIRMATION_DIALOG)
+                testDestination(STEPPER)
+
+                // Scroll down to view remaining UI elements
+                // Setting a gesture margin is important otherwise gesture nav is triggered.
+                val list = device.findObject(By.desc(CONTENT_DESCRIPTION))
+                list.setGestureMargin(device.displayWidth / 5)
+                repeat(25) {
+                    list.drag(Point(list.visibleCenter.x, list.visibleCenter.y / 3))
+                    device.waitForIdle()
+                }
+            }
+        )
+    }
+
+    private fun testDestination(name: String) {
+        device.findObject(By.desc(name)).click()
+        device.waitForIdle()
+        device.pressBack()
+        device.waitForIdle()
+    }
+
+    companion object {
+        private const val PACKAGE_NAME = "androidx.wear.compose.integration.macrobenchmark.target"
+        private const val ACTION =
+            "androidx.wear.compose.integration.macrobenchmark.target.BASELINE_ACTIVITY"
+
+        @Parameterized.Parameters(name = "compilation={0}")
+        @JvmStatic
+        fun parameters() = createCompilationParams()
+    }
+}
\ No newline at end of file
diff --git a/wear/compose/integration-tests/profileparser/build.gradle.kts b/wear/compose/integration-tests/profileparser/build.gradle.kts
new file mode 100644
index 0000000..f6cd114
--- /dev/null
+++ b/wear/compose/integration-tests/profileparser/build.gradle.kts
@@ -0,0 +1,24 @@
+plugins {
+    application
+    kotlin("jvm")
+    // Use ShadowJar plugin to build fat jar.
+    id("com.github.johnrengelman.shadow")
+}
+
+application {
+    mainClass.set("androidx.wear.compose.integration.profileparser.ProfileParser")
+}
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    implementation(kotlin("stdlib"))
+}
+
+tasks.withType<Jar> {
+    manifest {
+        attributes["Main-Class"] = "androidx.wear.compose.integration.profileparser.ProfileParser"
+    }
+}
diff --git a/wear/compose/integration-tests/profileparser/src/main/AndroidManifest.xml b/wear/compose/integration-tests/profileparser/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..84bd075
--- /dev/null
+++ b/wear/compose/integration-tests/profileparser/src/main/AndroidManifest.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="androidx.wear.compose.integration.profileparser">
+
+    <application android:label="profileparser" />
+
+</manifest>
\ No newline at end of file
diff --git a/wear/compose/integration-tests/profileparser/src/main/java/androidx/wear/compose/integration/profileparser/ProfileParser.kt b/wear/compose/integration-tests/profileparser/src/main/java/androidx/wear/compose/integration/profileparser/ProfileParser.kt
new file mode 100644
index 0000000..37464eb
--- /dev/null
+++ b/wear/compose/integration-tests/profileparser/src/main/java/androidx/wear/compose/integration/profileparser/ProfileParser.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.integration.profileparser
+
+import java.io.File
+
+class ProfileParser {
+    companion object {
+        @JvmStatic
+        fun main(args: Array<String>) {
+            if (args.size < 3) {
+                println("Usage: profileparser <input file> <target e.g. wear/compose/material> " +
+                    "<output file>")
+                return
+            }
+
+            println("Input: ${args[0]}")
+            println("Parse: ${args[1]}")
+            println("Output: ${args[2]}")
+
+            val input = File(args[0])
+            val output = File(args[2]).printWriter()
+
+            val lines = input.useLines {
+                it
+                    .toList()
+                    .map { it.truncatedAt(';') }
+                    .map { it.truncatedAt('$') }
+                    .filter { it.contains(args[1]) }
+                    .fold(mutableMapOf()) {
+                            acc: MutableMap<String, MutableList<String>>, item: String ->
+                        // Collect unique keys based on the androidx/xxxx part of the name
+                        // and accumulate a list of flags as the map value e.g. HSPL, L, SPL
+                        val idx = item.indexOf("androidx")
+                        val key = item.substring(idx)
+                        val flags = item.substring(0, idx)
+                        acc.getOrPut(key) { mutableListOf() }.add(flags)
+                        acc
+                    }
+                    .map { (key, flags) ->
+                        val flag = "HSPL".filter { c -> flags.any { flag -> flag.contains(c) } }
+                        flag + key
+                    }
+                    .map {
+                        // Tag on wild cards.
+                        if (it.startsWith("L")) {
+                            it + ";"
+                        } else if (it.endsWith("Kt")) {
+                            it + "**->**(**)**"
+                        } else {
+                            it + ";->**(**)**"
+                        }
+                    }
+                    .sortedBy { it.substring(it.indexOf("androidx")) }
+            }
+            output.use { out ->
+                lines.forEach {
+                    out.println(it)
+                }
+            }
+            println("Success!")
+        }
+
+        private fun String.truncatedAt(c: Char): String {
+            val idx = this.indexOf(c)
+            return if (idx == -1) this else this.substring(0, idx)
+        }
+    }
+}
diff --git a/webkit/webkit/src/androidTest/java/androidx/webkit/WebViewRenderProcessClientTest.java b/webkit/webkit/src/androidTest/java/androidx/webkit/WebViewRenderProcessClientTest.java
index bc4ffb3..c31bb07 100644
--- a/webkit/webkit/src/androidTest/java/androidx/webkit/WebViewRenderProcessClientTest.java
+++ b/webkit/webkit/src/androidTest/java/androidx/webkit/WebViewRenderProcessClientTest.java
@@ -23,7 +23,6 @@
 import androidx.annotation.Nullable;
 import androidx.concurrent.futures.ResolvableFuture;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.LargeTest;
 
 import org.junit.After;
@@ -37,7 +36,6 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
-@FlakyTest(bugId = 204197604)
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class WebViewRenderProcessClientTest {
@@ -116,7 +114,14 @@
         WebkitUtils.onMainThreadSync(() -> {
             WebView webView = mWebViewOnUiThread.getWebViewOnCurrentThread();
             webView.evaluateJavascript("blocker.block();", null);
-            blocker.waitForBlocked();
+        });
+        // Wait on the test instrumentation thread not the main thread. Blocking the main thread
+        // may block other async calls such as initializing the GPU service channel that happens on
+        // the UI thread and has to finish before the renderer can execute any javascript,
+        // see https://crbug.com/1269552.
+        blocker.waitForBlocked();
+        WebkitUtils.onMainThreadSync(() -> {
+            WebView webView = mWebViewOnUiThread.getWebViewOnCurrentThread();
             // Sending an input event that does not get acknowledged will cause
             // the unresponsive renderer event to fire.
             webView.dispatchKeyEvent(
diff --git a/work/work-runtime/src/androidTest/java/androidx/work/DatabaseTest.java b/work/work-runtime/src/androidTest/java/androidx/work/DatabaseTest.java
index cdbd3e2..e7d6c4a 100644
--- a/work/work-runtime/src/androidTest/java/androidx/work/DatabaseTest.java
+++ b/work/work-runtime/src/androidTest/java/androidx/work/DatabaseTest.java
@@ -21,27 +21,34 @@
 import androidx.work.impl.model.WorkName;
 import androidx.work.impl.model.WorkTag;
 
+import com.google.common.truth.Truth;
+
 import org.junit.After;
 import org.junit.Before;
 
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
 
 /**
  * An abstract class for getting an in-memory instance of the {@link WorkDatabase}.
  */
 public abstract class DatabaseTest extends WorkManagerTest {
     protected WorkDatabase mDatabase;
+    private final ExecutorService mQueryExecutor = Executors.newCachedThreadPool();
 
     @Before
     public void initializeDb() {
         mDatabase = WorkDatabase.create(
                 ApplicationProvider.getApplicationContext(),
-                Executors.newCachedThreadPool(),
+                mQueryExecutor,
                 true);
     }
 
     @After
-    public void closeDb() {
+    public void closeDb() throws InterruptedException {
+        mQueryExecutor.shutdown();
+        Truth.assertThat(mQueryExecutor.awaitTermination(3, TimeUnit.SECONDS)).isTrue();
         mDatabase.close();
     }
 
diff --git a/work/work-runtime/src/androidTest/java/androidx/work/impl/ProcessorTests.kt b/work/work-runtime/src/androidTest/java/androidx/work/impl/ProcessorTests.kt
index b3d0f4f..10e1b9a 100644
--- a/work/work-runtime/src/androidTest/java/androidx/work/impl/ProcessorTests.kt
+++ b/work/work-runtime/src/androidTest/java/androidx/work/impl/ProcessorTests.kt
@@ -29,21 +29,29 @@
 import androidx.work.impl.utils.SerialExecutor
 import androidx.work.impl.utils.taskexecutor.TaskExecutor
 import androidx.work.worker.StopLatchWorker
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.runBlocking
 import org.junit.After
 import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.mock
+import java.util.concurrent.CountDownLatch
 import java.util.concurrent.Executor
 import java.util.concurrent.ExecutorService
 import java.util.concurrent.Executors
+import java.util.concurrent.TimeUnit
 
 @RunWith(AndroidJUnit4::class)
 class ProcessorTests : DatabaseTest() {
     lateinit var scheduler: Scheduler
     lateinit var factory: WorkerFactory
-    lateinit var workerMap: MutableMap<String, ListenableWorker?>
+    val lastCreatedWorker = MutableStateFlow<ListenableWorker?>(null)
     lateinit var processor: Processor
     lateinit var defaultExecutor: ExecutorService
     lateinit var backgroundExecutor: ExecutorService
@@ -52,7 +60,9 @@
     @Before
     fun setUp() {
         val context = ApplicationProvider.getApplicationContext<Context>().applicationContext
-        defaultExecutor = Executors.newSingleThreadExecutor()
+        // first worker will take over the first thread with its doWork
+        // second worker will execute on the second thread
+        defaultExecutor = Executors.newFixedThreadPool(2)
         backgroundExecutor = Executors.newSingleThreadExecutor()
         serialExecutor = SerialExecutor(backgroundExecutor)
         val taskExecutor = object : TaskExecutor {
@@ -68,20 +78,19 @@
                 return serialExecutor
             }
         }
-        workerMap = mutableMapOf()
         factory = object : WorkerFactory() {
             override fun createWorker(
                 appContext: Context,
                 workerClassName: String,
                 workerParameters: WorkerParameters
-            ): ListenableWorker? {
+            ): ListenableWorker {
                 val worker = getDefaultWorkerFactory()
                     .createWorkerWithDefaultFallback(
                         appContext,
                         workerClassName,
                         workerParameters
-                    )
-                workerMap[workerParameters.id.toString()] = worker
+                    )!!
+                lastCreatedWorker.value = worker
                 return worker
             }
         }
@@ -108,19 +117,37 @@
         }
         processor.addExecutionListener(listener)
         processor.startWork(request.workSpec.id)
-        processor.stopWork(request.workSpec.id)
+
+        val firstWorker = runBlocking { lastCreatedWorker.filterNotNull().first() }
+        val blockedThread = Executors.newSingleThreadExecutor()
+        blockedThread.execute {
+            // gonna stall for 10 seconds
+            processor.stopWork(request.workSpec.id)
+        }
+        assertTrue((firstWorker as StopLatchWorker).awaitOnStopCall())
+        // onStop call results in onExecuted. It happens on "main thread", which is instant
+        // in this case.
+        assertTrue(listenerCalled)
+        processor.removeExecutionListener(listener)
+        listenerCalled = false
+        val executionFinished = CountDownLatch(1)
+        processor.addExecutionListener { _, _ -> executionFinished.countDown() }
         // This would have previously failed trying to acquire a lock
         processor.startWork(request.workSpec.id)
-        (workerMap[request.workSpec.id] as? StopLatchWorker)?.countDown()
-        while (serialExecutor.hasPendingTasks()) {
-            // Wait until we are done
-        }
-        assertEquals(listenerCalled, true)
+        val secondWorker =
+            runBlocking { lastCreatedWorker.filterNotNull().filter { it != firstWorker }.first() }
+        (secondWorker as StopLatchWorker).countDown()
+        assertTrue(executionFinished.await(3, TimeUnit.SECONDS))
+        firstWorker.countDown()
+        blockedThread.shutdown()
+        assertTrue(blockedThread.awaitTermination(3, TimeUnit.SECONDS))
     }
 
     @After
     fun tearDown() {
         defaultExecutor.shutdownNow()
         backgroundExecutor.shutdownNow()
+        assertTrue(defaultExecutor.awaitTermination(3, TimeUnit.SECONDS))
+        assertTrue(backgroundExecutor.awaitTermination(3, TimeUnit.SECONDS))
     }
 }
diff --git a/work/work-runtime/src/androidTest/java/androidx/work/worker/StopLatchWorker.kt b/work/work-runtime/src/androidTest/java/androidx/work/worker/StopLatchWorker.kt
index 2805997..8a7b87b 100644
--- a/work/work-runtime/src/androidTest/java/androidx/work/worker/StopLatchWorker.kt
+++ b/work/work-runtime/src/androidTest/java/androidx/work/worker/StopLatchWorker.kt
@@ -25,20 +25,24 @@
 class StopLatchWorker(context: Context, parameters: WorkerParameters) :
     Worker(context, parameters) {
 
-    private val latch = CountDownLatch(1)
+    private val stopLatch = CountDownLatch(1)
+    private val onStopLatch = CountDownLatch(1)
 
     override fun doWork(): Result {
-        while (latch.count > 0) {
+        while (stopLatch.count > 0) {
             // do nothing.
         }
         return Result.success()
     }
 
+    fun awaitOnStopCall() = onStopLatch.await(5000, TimeUnit.SECONDS)
+
     fun countDown() {
-        latch.countDown()
+        stopLatch.countDown()
     }
 
     override fun onStopped() {
-        latch.await(10, TimeUnit.SECONDS)
+        onStopLatch.countDown()
+        stopLatch.await(5000, TimeUnit.SECONDS)
     }
 }