Merge "Add project dependency constraint between lifecycle-livedata and lifecycle-livedata-core" into androidx-main
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupProfilesTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupProfilesTest.kt
index 9d5eced..f99856a 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupProfilesTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/StartupProfilesTest.kt
@@ -52,10 +52,13 @@
             Landroidx/Foo/Bar;
             Lfoo/bar/Baz$dollar<Suffix>;
             HSPLjava/io/DataOutputStream;->writeByte(I)V+]Ljava/io/OutputStream;missing_types
+            HPLandroidx/startup/AppInitializer;->**(**)**
         """.trimIndent()
 
         val startupRules = startupProfile(profile, includeStartupOnly = true)
         val expectedRules = """
+            SLandroidx/Foo/Bar;
+            SLfoo/bar/Baz;
             SLjava/io/DataOutputStream;
         """.trimIndent()
         assertEquals(expectedRules, startupRules)
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/StartupProfiles.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/StartupProfiles.kt
index 527791a..8b50582 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/StartupProfiles.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/StartupProfiles.kt
@@ -33,7 +33,10 @@
             null -> null
             else -> {
                 val (flags, classPrefix, _) = result.destructured
-                if (includeStartupOnly && !flags.contains("S")) {
+                // Empty flags are indicative that the class needs to be aggressively preloaded
+                // Therefore the class belongs in the primary dex.
+                val isStartup = flags.isEmpty() || flags.contains("S")
+                if (includeStartupOnly && !isStartup) {
                     null
                 } else {
                     "SL$classPrefix;"
diff --git a/bluetooth/bluetooth-core/src/androidTest/java/androidx/bluetooth/core/ScanFilterTest.kt b/bluetooth/bluetooth-core/src/androidTest/java/androidx/bluetooth/core/ScanFilterTest.kt
index 1c4ab30..82ce1a1 100644
--- a/bluetooth/bluetooth-core/src/androidTest/java/androidx/bluetooth/core/ScanFilterTest.kt
+++ b/bluetooth/bluetooth-core/src/androidTest/java/androidx/bluetooth/core/ScanFilterTest.kt
@@ -1,6 +1,7 @@
 package androidx.bluetooth.core
 
 import android.bluetooth.le.ScanFilter as FwkScanFilter
+import android.os.Build
 import android.os.ParcelUuid
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
@@ -18,6 +19,8 @@
         val TEST_SERVICE_DATA = "SERVICE-DATA".toByteArray()
         val TEST_MANUFACTURER_ID = 1000
         val TEST_MANUFACTURER_DATA = "MANUFACTURER-DATA".toByteArray()
+        val TEST_SERVICE_SOLICITATION_UUID =
+            ParcelUuid.fromString("CCCCCCC0-CCCC-CCCC-CCCC-CCCCCCCCCCCC")
     }
 
     @Test
@@ -30,8 +33,9 @@
             serviceData = TEST_SERVICE_DATA,
             manufacturerId = TEST_MANUFACTURER_ID,
             manufacturerData = TEST_MANUFACTURER_DATA,
+            serviceSolicitationUuid = TEST_SERVICE_SOLICITATION_UUID
         )
-        val fwkScanFilter = scanFilter.fwkInstance
+        val fwkScanFilter = (scanFilter.impl as ScanFilterImpl).fwkInstance
         assertThat(fwkScanFilter.deviceName).isEqualTo(TEST_DEVICE_NAME)
         assertThat(fwkScanFilter.deviceAddress).isEqualTo(TEST_DEVICE_ADDRESS)
         assertThat(fwkScanFilter.serviceUuid).isEqualTo(TEST_SERVICE_UUID)
@@ -39,18 +43,25 @@
         assertThat(fwkScanFilter.serviceData).isEqualTo(TEST_SERVICE_DATA)
         assertThat(fwkScanFilter.manufacturerId).isEqualTo(TEST_MANUFACTURER_ID)
         assertThat(fwkScanFilter.manufacturerData).isEqualTo(TEST_MANUFACTURER_DATA)
+        if (Build.VERSION.SDK_INT > 29) {
+            assertThat(fwkScanFilter.serviceSolicitationUuid)
+                .isEqualTo(TEST_SERVICE_SOLICITATION_UUID)
+        }
     }
 
     @Test
     fun constructorWithFwkInstance_createsScanFilterCorrectly() {
-        val fwkScanFilter = FwkScanFilter.Builder()
+        val fwkScanFilterBuilder = FwkScanFilter.Builder()
             .setDeviceName(TEST_DEVICE_NAME)
             .setDeviceAddress(TEST_DEVICE_ADDRESS)
             .setServiceUuid(TEST_SERVICE_UUID)
             .setServiceData(TEST_SERVICE_DATA_UUID, TEST_SERVICE_DATA)
             .setManufacturerData(TEST_MANUFACTURER_ID, TEST_MANUFACTURER_DATA)
-            .build()
-        val scanFilter = ScanFilter(fwkScanFilter)
+        if (Build.VERSION.SDK_INT > 29) {
+            fwkScanFilterBuilder.setServiceSolicitationUuid(TEST_SERVICE_SOLICITATION_UUID)
+        }
+
+        val scanFilter = ScanFilter(fwkScanFilterBuilder.build())
 
         assertThat(scanFilter.deviceName).isEqualTo(TEST_DEVICE_NAME)
         assertThat(scanFilter.deviceAddress).isEqualTo(TEST_DEVICE_ADDRESS)
@@ -59,6 +70,10 @@
         assertThat(scanFilter.serviceData).isEqualTo(TEST_SERVICE_DATA)
         assertThat(scanFilter.manufacturerId).isEqualTo(TEST_MANUFACTURER_ID)
         assertThat(scanFilter.manufacturerData).isEqualTo(TEST_MANUFACTURER_DATA)
+        if (Build.VERSION.SDK_INT > 29) {
+            assertThat(scanFilter.serviceSolicitationUuid)
+                .isEqualTo(TEST_SERVICE_SOLICITATION_UUID)
+        }
     }
 
     @Test
@@ -71,6 +86,7 @@
             serviceData = TEST_SERVICE_DATA,
             manufacturerId = TEST_MANUFACTURER_ID,
             manufacturerData = TEST_MANUFACTURER_DATA,
+            serviceSolicitationUuid = TEST_SERVICE_SOLICITATION_UUID
         )
         val bundle = scanFilter.toBundle()
 
@@ -82,5 +98,7 @@
         assertThat(scanFilterFromBundle.serviceData).isEqualTo(TEST_SERVICE_DATA)
         assertThat(scanFilterFromBundle.manufacturerId).isEqualTo(TEST_MANUFACTURER_ID)
         assertThat(scanFilterFromBundle.manufacturerData).isEqualTo(TEST_MANUFACTURER_DATA)
+        assertThat(scanFilterFromBundle.serviceSolicitationUuid)
+            .isEqualTo(TEST_SERVICE_SOLICITATION_UUID)
     }
 }
diff --git a/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/ScanFilter.kt b/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/ScanFilter.kt
index 98fbd7f..d274970 100644
--- a/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/ScanFilter.kt
+++ b/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/ScanFilter.kt
@@ -17,27 +17,237 @@
 package androidx.bluetooth.core
 
 import android.bluetooth.le.ScanFilter as FwkScanFilter
+import android.os.Build
 import android.os.Bundle
 import android.os.ParcelUuid
+import androidx.annotation.RequiresApi
 import androidx.bluetooth.core.utils.Bundleable
 import androidx.bluetooth.core.utils.Utils
 
 /**
  * TODO: Copy docs
- * TODO: Implement ScanFilterImplXxx
- * TODO: Implement APIs added in API 29
- *       getters: getServiceSolicitationUuid, getServiceSolicitationUuidMask
- *       setters: setServiceSolicitationUuid
  * TODO: Implement APIs added in API 33
  *       getters: getAdvertisingData, getAdvertisingDataMask, getAdvertisingDataType
  *       setters: setAdvertisingDataTypeWithData, setAdvertisingDataType,
  * @hide
  */
-class ScanFilter internal constructor(internal val fwkInstance: FwkScanFilter) : Bundleable {
+class ScanFilter internal constructor(
+    internal val impl: ScanFilterInterface
+) : ScanFilterInterface by impl {
+    companion object {
+        val CREATOR: Bundleable.Creator<ScanFilter> =
+            if (Build.VERSION.SDK_INT < 29) {
+                ScanFilterImplBase.CREATOR
+            } else {
+                ScanFilterImplApi29.CREATOR
+            }
 
+        internal fun createScanFilterImpl(args: ScanFilterArgs): ScanFilterInterface {
+            return if (Build.VERSION.SDK_INT < 29) {
+                ScanFilterImplBase(
+                    ScanFilterImplBase.getFwkScanFilterBuilder(args).build(),
+                    args.serviceSolicitationUuid,
+                    args.serviceSolicitationUuidMask
+                )
+            } else {
+                ScanFilterImplApi29(
+                    ScanFilterImplApi29.getFwkScanFilterBuilder(args).build()
+                )
+            }
+        }
+    }
+    internal constructor(fwkScanFilter: FwkScanFilter) : this(
+        if (Build.VERSION.SDK_INT < 29) {
+            ScanFilterImplBase(fwkScanFilter)
+        } else {
+            ScanFilterImplApi29(fwkScanFilter)
+        }
+    )
+
+    constructor(
+        deviceName: String? = null,
+        deviceAddress: String? = null,
+        serviceUuid: ParcelUuid? = null,
+        serviceUuidMask: ParcelUuid? = null,
+        serviceDataUuid: ParcelUuid? = null,
+        serviceData: ByteArray? = null,
+        serviceDataMask: ByteArray? = null,
+        manufacturerId: Int = -1,
+        manufacturerData: ByteArray? = null,
+        manufacturerDataMask: ByteArray? = null,
+        serviceSolicitationUuid: ParcelUuid? = null,
+        serviceSolicitationUuidMask: ParcelUuid? = null
+    ) : this(createScanFilterImpl(ScanFilterArgs(
+        deviceName,
+        deviceAddress,
+        serviceUuid,
+        serviceUuidMask,
+        serviceDataUuid,
+        serviceData,
+        serviceDataMask,
+        manufacturerId,
+        manufacturerData,
+        manufacturerDataMask,
+        serviceSolicitationUuid,
+        serviceSolicitationUuidMask
+    )))
+}
+
+internal data class ScanFilterArgs(
+    var deviceName: String? = null,
+    var deviceAddress: String? = null,
+    var serviceUuid: ParcelUuid? = null,
+    var serviceUuidMask: ParcelUuid? = null,
+    var serviceDataUuid: ParcelUuid? = null,
+    var serviceData: ByteArray? = null,
+    var serviceDataMask: ByteArray? = null,
+    var manufacturerId: Int = -1,
+    var manufacturerData: ByteArray? = null,
+    var manufacturerDataMask: ByteArray? = null,
+    var serviceSolicitationUuid: ParcelUuid? = null,
+    var serviceSolicitationUuidMask: ParcelUuid? = null
+)
+
+internal interface ScanFilterInterface : Bundleable {
+    val deviceName: String?
+    val deviceAddress: String?
+    val serviceUuid: ParcelUuid?
+    val serviceUuidMask: ParcelUuid?
+    val serviceDataUuid: ParcelUuid?
+    val serviceData: ByteArray?
+    val serviceDataMask: ByteArray?
+    val manufacturerId: Int
+    val manufacturerData: ByteArray?
+    val manufacturerDataMask: ByteArray?
+    val serviceSolicitationUuid: ParcelUuid?
+    val serviceSolicitationUuidMask: ParcelUuid?
+}
+
+internal abstract class ScanFilterImpl internal constructor(
+    internal val fwkInstance: FwkScanFilter
+) : ScanFilterInterface {
     companion object {
         internal const val FIELD_FWK_SCAN_FILTER = 0
+        internal const val FIELD_SERVICE_SOLICITATION_UUID = 1
+        internal const val FIELD_SERVICE_SOLICITATION_UUID_MASK = 2
 
+        internal fun keyForField(field: Int): String {
+            return field.toString(Character.MAX_RADIX)
+        }
+    }
+
+    override val deviceName: String?
+        get() = fwkInstance.deviceName
+    override val deviceAddress: String?
+        get() = fwkInstance.deviceAddress
+    override val serviceUuid: ParcelUuid?
+        get() = fwkInstance.serviceUuid
+    override val serviceUuidMask: ParcelUuid?
+        get() = fwkInstance.serviceUuidMask
+    override val serviceDataUuid: ParcelUuid?
+        get() = fwkInstance.serviceDataUuid
+    override val serviceData: ByteArray?
+        get() = fwkInstance.serviceData
+    override val serviceDataMask: ByteArray?
+        get() = fwkInstance.serviceDataMask
+    override val manufacturerId: Int
+        get() = fwkInstance.manufacturerId
+    override val manufacturerData: ByteArray?
+        get() = fwkInstance.manufacturerData
+    override val manufacturerDataMask: ByteArray?
+        get() = fwkInstance.manufacturerDataMask
+}
+
+internal class ScanFilterImplBase internal constructor(
+    fwkInstance: FwkScanFilter,
+    override val serviceSolicitationUuid: ParcelUuid? = null,
+    override val serviceSolicitationUuidMask: ParcelUuid? = null
+) : ScanFilterImpl(fwkInstance) {
+    companion object {
+        val CREATOR: Bundleable.Creator<ScanFilter> =
+            object : Bundleable.Creator<ScanFilter> {
+                override fun fromBundle(bundle: Bundle): ScanFilter {
+                    val fwkScanFilter =
+                        Utils.getParcelableFromBundle(
+                            bundle,
+                            keyForField(FIELD_FWK_SCAN_FILTER),
+                            FwkScanFilter::class.java
+                        ) ?: throw IllegalArgumentException(
+                            "Bundle doesn't include a framework scan filter"
+                        )
+                    val serviceSolicitationUuid =
+                        Utils.getParcelableFromBundle(
+                            bundle,
+                            keyForField(FIELD_SERVICE_SOLICITATION_UUID),
+                            ParcelUuid::class.java
+                        )
+                    val serviceSolicitationUuidMask =
+                        Utils.getParcelableFromBundle(
+                            bundle,
+                            keyForField(FIELD_SERVICE_SOLICITATION_UUID_MASK),
+                            ParcelUuid::class.java
+                        )
+                    return ScanFilter(ScanFilterImplBase(
+                        fwkScanFilter,
+                        serviceSolicitationUuid,
+                        serviceSolicitationUuidMask))
+                }
+            }
+
+        internal fun getFwkScanFilterBuilder(args: ScanFilterArgs): FwkScanFilter.Builder {
+            val builder = FwkScanFilter.Builder()
+                .setDeviceName(args.deviceName)
+                .setDeviceAddress(args.deviceAddress)
+                .setServiceUuid(args.serviceUuid)
+
+            if (args.serviceDataUuid != null) {
+                if (args.serviceDataMask == null) {
+                    builder.setServiceData(args.serviceDataUuid, args.serviceData)
+                } else {
+                    builder.setServiceData(
+                        args.serviceDataUuid, args.serviceData, args.serviceDataMask)
+                }
+            }
+            if (args.manufacturerId >= 0) {
+                if (args.manufacturerDataMask == null) {
+                    builder.setManufacturerData(args.manufacturerId, args.manufacturerData)
+                } else {
+                    builder.setManufacturerData(
+                        args.manufacturerId, args.manufacturerData, args.manufacturerDataMask)
+                }
+            }
+            return builder
+        }
+    }
+
+    override fun toBundle(): Bundle {
+        val bundle = Bundle()
+        bundle.putParcelable(keyForField(FIELD_FWK_SCAN_FILTER), fwkInstance)
+        if (serviceSolicitationUuid != null) {
+            bundle.putParcelable(
+                keyForField(FIELD_SERVICE_SOLICITATION_UUID), serviceSolicitationUuid)
+        }
+        if (serviceSolicitationUuidMask != null) {
+            bundle.putParcelable(
+                keyForField(FIELD_SERVICE_SOLICITATION_UUID_MASK), serviceSolicitationUuidMask)
+        }
+        return bundle
+    }
+}
+
+@RequiresApi(Build.VERSION_CODES.Q)
+internal abstract class ScanFilterImpl29(fwkInstance: FwkScanFilter) : ScanFilterImpl(fwkInstance) {
+    override val serviceSolicitationUuid: ParcelUuid?
+        get() = fwkInstance.serviceSolicitationUuid
+    override val serviceSolicitationUuidMask: ParcelUuid?
+        get() = fwkInstance.serviceSolicitationUuidMask
+}
+
+@RequiresApi(Build.VERSION_CODES.Q)
+internal class ScanFilterImplApi29 internal constructor(
+    fwkInstance: FwkScanFilter
+) : ScanFilterImpl29(fwkInstance) {
+    companion object {
         val CREATOR: Bundleable.Creator<ScanFilter> =
             object : Bundleable.Creator<ScanFilter> {
                 override fun fromBundle(bundle: Bundle): ScanFilter {
@@ -53,91 +263,24 @@
                 }
             }
 
-        internal fun keyForField(field: Int): String {
-            return field.toString(Character.MAX_RADIX)
-        }
+        internal fun getFwkScanFilterBuilder(args: ScanFilterArgs): FwkScanFilter.Builder {
+            val builder = ScanFilterImplBase.getFwkScanFilterBuilder(args)
 
-        internal fun buildFwkScanFilter(
-            deviceName: String? = null,
-            deviceAddress: String? = null,
-            serviceUuid: ParcelUuid? = null,
-            serviceDataUuid: ParcelUuid? = null,
-            serviceData: ByteArray? = null,
-            serviceDataMask: ByteArray? = null,
-            manufacturerId: Int = -1,
-            manufacturerData: ByteArray? = null,
-            manufacturerDataMask: ByteArray? = null
-        ): FwkScanFilter {
-            var builder = FwkScanFilter.Builder()
-                .setDeviceName(deviceName)
-                .setDeviceAddress(deviceAddress)
-                .setServiceUuid(serviceUuid)
-
-            if (serviceDataUuid != null) {
-                if (serviceDataMask == null) {
-                    builder.setServiceData(serviceDataUuid, serviceData)
+            if (args.serviceSolicitationUuid != null) {
+                if (args.serviceSolicitationUuidMask == null) {
+                    builder.setServiceSolicitationUuid(args.serviceSolicitationUuid)
                 } else {
-                    builder.setServiceData(serviceDataUuid, serviceData, serviceDataMask)
+                    builder.setServiceSolicitationUuid(
+                        args.serviceSolicitationUuid, args.serviceSolicitationUuidMask)
                 }
             }
-            if (manufacturerId >= 0) {
-                if (manufacturerDataMask == null) {
-                    builder.setManufacturerData(manufacturerId, manufacturerData)
-                } else {
-                    builder.setManufacturerData(
-                        manufacturerId, manufacturerData, manufacturerDataMask)
-                }
-            }
-
-            return builder.build()
+            return builder
         }
     }
 
-    val deviceName: String?
-        get() = fwkInstance.deviceName
-    val deviceAddress: String?
-        get() = fwkInstance.deviceAddress
-    val serviceUuid: ParcelUuid?
-        get() = fwkInstance.serviceUuid
-    val serviceUuidMask: ParcelUuid?
-        get() = fwkInstance.serviceUuidMask
-    val serviceDataUuid: ParcelUuid?
-        get() = fwkInstance.serviceDataUuid
-    val serviceData: ByteArray?
-        get() = fwkInstance.serviceData
-    val serviceDataMask: ByteArray?
-        get() = fwkInstance.serviceDataMask
-    val manufacturerId: Int
-        get() = fwkInstance.manufacturerId
-    val manufacturerData: ByteArray?
-        get() = fwkInstance.manufacturerData
-    val manufacturerDataMask: ByteArray?
-        get() = fwkInstance.manufacturerDataMask
-
-    constructor(
-        deviceName: String? = null,
-        deviceAddress: String? = null,
-        serviceUuid: ParcelUuid? = null,
-        serviceDataUuid: ParcelUuid? = null,
-        serviceData: ByteArray? = null,
-        serviceDataMask: ByteArray? = null,
-        manufacturerId: Int = -1,
-        manufacturerData: ByteArray? = null,
-        manufacturerDataMask: ByteArray? = null
-    ) : this(buildFwkScanFilter(
-        deviceName,
-        deviceAddress,
-        serviceUuid,
-        serviceDataUuid,
-        serviceData,
-        serviceDataMask,
-        manufacturerId,
-        manufacturerData,
-        manufacturerDataMask
-    ))
     override fun toBundle(): Bundle {
         val bundle = Bundle()
         bundle.putParcelable(keyForField(FIELD_FWK_SCAN_FILTER), fwkInstance)
         return bundle
     }
-}
+}
\ No newline at end of file
diff --git a/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/utils/Utils.kt b/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/utils/Utils.kt
index 1887469..2eb25fc 100644
--- a/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/utils/Utils.kt
+++ b/bluetooth/bluetooth-core/src/main/java/androidx/bluetooth/core/utils/Utils.kt
@@ -26,51 +26,48 @@
  * @hide
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY)
-internal class Utils {
-    companion object {
+internal object Utils {
+    // TODO: Migrate to BundleCompat when available
+    @SuppressLint("ClassVerificationFailure") // bundle.getParcelable(key, clazz)
+    @Suppress("DEPRECATION") // bundle.getParcelable(key)
+    fun <T : Parcelable> getParcelableFromBundle(
+        bundle: Bundle,
+        key: String,
+        clazz: Class<T>
+    ): T? {
+        val parcelable: T?
+        bundle.classLoader = clazz.classLoader
+        try {
+            if (Build.VERSION.SDK_INT >= 33) {
+                parcelable = bundle.getParcelable(key, clazz)
+            } else {
+                parcelable = bundle.getParcelable(key)
+            }
+        } catch (e: Exception) {
+            return null
+        }
+        return parcelable
+    }
 
-        // TODO: Migrate to BundleCompat when available
-        @SuppressLint("ClassVerificationFailure") // bundle.getParcelable(key, clazz)
-        @Suppress("DEPRECATION") // bundle.getParcelable(key)
-        fun <T : Parcelable> getParcelableFromBundle(
-            bundle: Bundle,
-            key: String,
-            clazz: Class<T>
-        ): T? {
-            val parcelable: T?
-            bundle.classLoader = clazz.classLoader
+    @Suppress("DEPRECATION")
+    fun <T : Parcelable> getParcelableArrayListFromBundle(
+        bundle: Bundle,
+        key: String,
+        clazz: Class<T>
+    ): List<T> {
+        bundle.classLoader = clazz.classLoader
+        if (Build.VERSION.SDK_INT >= 33) {
+            // TODO: Return framework's getParcelableArrayList when SDK 33 is available
+            // return bundle.getParcelableArrayList(key, clazz)
+            TODO()
+        } else {
+            val parcelable: List<T>
             try {
-                if (Build.VERSION.SDK_INT >= 33) {
-                    parcelable = bundle.getParcelable(key, clazz)
-                } else {
-                    parcelable = bundle.getParcelable(key)
-                }
+                parcelable = bundle.getParcelableArrayList(key) ?: emptyList()
             } catch (e: Exception) {
-                return null
+                return emptyList()
             }
             return parcelable
         }
-
-        @Suppress("DEPRECATION")
-        fun <T : Parcelable> getParcelableArrayListFromBundle(
-            bundle: Bundle,
-            key: String,
-            clazz: Class<T>
-        ): List<T> {
-            bundle.classLoader = clazz.classLoader
-            if (Build.VERSION.SDK_INT >= 33) {
-                // TODO: Return framework's getParcelableArrayList when SDK 33 is available
-                // return bundle.getParcelableArrayList(key, clazz)
-                TODO()
-            } else {
-                val parcelable: List<T>
-                try {
-                    parcelable = bundle.getParcelableArrayList(key) ?: emptyList()
-                } catch (e: Exception) {
-                    return emptyList()
-                }
-                return parcelable
-            }
-        }
     }
 }
\ No newline at end of file
diff --git a/buildSrc/OWNERS b/buildSrc/OWNERS
index 0aa4786..d675db7 100644
--- a/buildSrc/OWNERS
+++ b/buildSrc/OWNERS
@@ -6,10 +6,5 @@
 [email protected]
 [email protected]
 
-per-file *Dependencies.kt=file://OWNERS
-per-file *LibraryVersions.kt=file://OWNERS
-per-file *PublishDocsRules.kt=file://OWNERS
-per-file *build_dependencies.gradle=file://OWNERS
-
 per-file *AndroidXPlaygroundRootPlugin.kt = [email protected], [email protected], [email protected]
-per-file *LibraryVersions.kt = [email protected]
+per-file *LintConfiguration.kt = [email protected], [email protected]
diff --git a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
index 6b988a0..41cd730 100644
--- a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
+++ b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
@@ -165,6 +165,8 @@
     // Possible values for this intent key: "switch_test_case", "preview_test_case" or
     // "default_test_case".
     private static final String INTENT_EXTRA_E2E_TEST_CASE = "e2e_test_case";
+    // Launch the activity with the specified video quality.
+    private static final String INTENT_EXTRA_VIDEO_QUALITY = "video_quality";
     public static final String INTENT_EXTRA_CAMERA_IMPLEMENTATION = "camera_implementation";
     // Launch the activity with the specified camera id.
     @VisibleForTesting
@@ -1025,6 +1027,31 @@
         mAnalysisToggle.setChecked((useCaseCombination & BIND_IMAGE_ANALYSIS) != 0L);
     }
 
+    private void updateVideoQualityByIntent(@NonNull Intent intent) {
+        Bundle bundle = intent.getExtras();
+        if (bundle == null) {
+            return;
+        }
+
+        Quality quality = itemIdToQuality(bundle.getInt(INTENT_EXTRA_VIDEO_QUALITY, 0));
+        if (quality == QUALITY_AUTO || !mVideoToggle.isChecked()) {
+            return;
+        }
+
+        if (mCameraProvider == null) {
+            throw new IllegalStateException("Need to obtain mCameraProvider first!");
+        }
+
+        // Check and set specific quality.
+        Camera targetCamera = mCameraProvider.bindToLifecycle(this, mCurrentCameraSelector);
+        List<Quality> supportedQualities =
+                QualitySelector.getSupportedQualities(targetCamera.getCameraInfo());
+        if (supportedQualities.contains(quality)) {
+            mVideoQuality = quality;
+            mRecordUi.getButtonQuality().setText(getQualityIconName(mVideoQuality));
+        }
+    }
+
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -1149,6 +1176,7 @@
             mInitializationIdlingResource.decrement();
             if (cameraProviderResult.hasProvider()) {
                 mCameraProvider = cameraProviderResult.getProvider();
+                updateVideoQualityByIntent(getIntent());
                 tryBindUseCases();
             } else {
                 Log.e(TAG, "Failed to retrieve ProcessCameraProvider",
@@ -1221,7 +1249,6 @@
                 mLaunchingCameraLensFacing = camera2CameraInfo.getCameraCharacteristic(
                         CameraCharacteristics.LENS_FACING);
             }
-
             List<UseCase> useCases = buildUseCases();
             mCamera = bindToLifecycleSafely(useCases);
 
diff --git a/compose/foundation/foundation/api/1.3.0-beta01.txt b/compose/foundation/foundation/api/1.3.0-beta01.txt
index 963ea75..11a455e 100644
--- a/compose/foundation/foundation/api/1.3.0-beta01.txt
+++ b/compose/foundation/foundation/api/1.3.0-beta01.txt
@@ -722,6 +722,9 @@
   public final class LazyStaggeredGridMeasureKt {
   }
 
+  public final class LazyStaggeredGridMeasurePolicyKt {
+  }
+
 }
 
 package androidx.compose.foundation.relocation {
diff --git a/compose/foundation/foundation/api/current.txt b/compose/foundation/foundation/api/current.txt
index 963ea75..11a455e 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -722,6 +722,9 @@
   public final class LazyStaggeredGridMeasureKt {
   }
 
+  public final class LazyStaggeredGridMeasurePolicyKt {
+  }
+
 }
 
 package androidx.compose.foundation.relocation {
diff --git a/compose/foundation/foundation/api/public_plus_experimental_1.3.0-beta01.txt b/compose/foundation/foundation/api/public_plus_experimental_1.3.0-beta01.txt
index b54f553..fc23475 100644
--- a/compose/foundation/foundation/api/public_plus_experimental_1.3.0-beta01.txt
+++ b/compose/foundation/foundation/api/public_plus_experimental_1.3.0-beta01.txt
@@ -353,19 +353,22 @@
 package androidx.compose.foundation.gestures.snapping {
 
   public final class LazyListSnapLayoutInfoProviderKt {
-    method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.LazyListState lazyListState, optional kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Float,java.lang.Float> positionInLayout);
+    method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.LazyListState lazyListState, optional kotlin.jvm.functions.Function3<? super androidx.compose.ui.unit.Density,? super java.lang.Float,? super java.lang.Float,java.lang.Float> positionInLayout);
+  }
+
+  @androidx.compose.foundation.ExperimentalFoundationApi public final class SnapFlingBehavior implements androidx.compose.foundation.gestures.FlingBehavior {
+    ctor public SnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> approachAnimationSpec, androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec, androidx.compose.ui.unit.Density density, optional float shortSnapVelocityThreshold);
+    method public suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.coroutines.Continuation<? super java.lang.Float>);
   }
 
   public final class SnapFlingBehaviorKt {
-    method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.FlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> approachAnimationSpec, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec);
-    method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.FlingBehavior snapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> approachAnimationSpec, androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec, androidx.compose.ui.unit.Density density, optional float shortSnapVelocityThreshold);
+    method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.snapping.SnapFlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> approachAnimationSpec, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec);
   }
 
   @androidx.compose.foundation.ExperimentalFoundationApi public interface SnapLayoutInfoProvider {
-    method public float calculateApproachOffset(float initialVelocity);
-    method public kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> calculateSnappingOffsetBounds();
-    method public float getSnapStepSize();
-    property public abstract float snapStepSize;
+    method public float calculateApproachOffset(androidx.compose.ui.unit.Density, float initialVelocity);
+    method public kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> calculateSnappingOffsetBounds(androidx.compose.ui.unit.Density);
+    method public float snapStepSize(androidx.compose.ui.unit.Density);
   }
 
 }
@@ -872,6 +875,9 @@
   public final class LazyStaggeredGridMeasureKt {
   }
 
+  public final class LazyStaggeredGridMeasurePolicyKt {
+  }
+
 }
 
 package androidx.compose.foundation.relocation {
diff --git a/compose/foundation/foundation/api/public_plus_experimental_current.txt b/compose/foundation/foundation/api/public_plus_experimental_current.txt
index b54f553..fc23475 100644
--- a/compose/foundation/foundation/api/public_plus_experimental_current.txt
+++ b/compose/foundation/foundation/api/public_plus_experimental_current.txt
@@ -353,19 +353,22 @@
 package androidx.compose.foundation.gestures.snapping {
 
   public final class LazyListSnapLayoutInfoProviderKt {
-    method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.LazyListState lazyListState, optional kotlin.jvm.functions.Function2<? super java.lang.Float,? super java.lang.Float,java.lang.Float> positionInLayout);
+    method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider SnapLayoutInfoProvider(androidx.compose.foundation.lazy.LazyListState lazyListState, optional kotlin.jvm.functions.Function3<? super androidx.compose.ui.unit.Density,? super java.lang.Float,? super java.lang.Float,java.lang.Float> positionInLayout);
+  }
+
+  @androidx.compose.foundation.ExperimentalFoundationApi public final class SnapFlingBehavior implements androidx.compose.foundation.gestures.FlingBehavior {
+    ctor public SnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> approachAnimationSpec, androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec, androidx.compose.ui.unit.Density density, optional float shortSnapVelocityThreshold);
+    method public suspend Object? performFling(androidx.compose.foundation.gestures.ScrollScope, float initialVelocity, kotlin.coroutines.Continuation<? super java.lang.Float>);
   }
 
   public final class SnapFlingBehaviorKt {
-    method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.FlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> approachAnimationSpec, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec);
-    method @androidx.compose.foundation.ExperimentalFoundationApi public static androidx.compose.foundation.gestures.FlingBehavior snapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> approachAnimationSpec, androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec, androidx.compose.ui.unit.Density density, optional float shortSnapVelocityThreshold);
+    method @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static androidx.compose.foundation.gestures.snapping.SnapFlingBehavior rememberSnapFlingBehavior(androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider snapLayoutInfoProvider, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> approachAnimationSpec, optional androidx.compose.animation.core.AnimationSpec<java.lang.Float> snapAnimationSpec);
   }
 
   @androidx.compose.foundation.ExperimentalFoundationApi public interface SnapLayoutInfoProvider {
-    method public float calculateApproachOffset(float initialVelocity);
-    method public kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> calculateSnappingOffsetBounds();
-    method public float getSnapStepSize();
-    property public abstract float snapStepSize;
+    method public float calculateApproachOffset(androidx.compose.ui.unit.Density, float initialVelocity);
+    method public kotlin.ranges.ClosedFloatingPointRange<java.lang.Float> calculateSnappingOffsetBounds(androidx.compose.ui.unit.Density);
+    method public float snapStepSize(androidx.compose.ui.unit.Density);
   }
 
 }
@@ -872,6 +875,9 @@
   public final class LazyStaggeredGridMeasureKt {
   }
 
+  public final class LazyStaggeredGridMeasurePolicyKt {
+  }
+
 }
 
 package androidx.compose.foundation.relocation {
diff --git a/compose/foundation/foundation/api/restricted_1.3.0-beta01.txt b/compose/foundation/foundation/api/restricted_1.3.0-beta01.txt
index 963ea75..11a455e 100644
--- a/compose/foundation/foundation/api/restricted_1.3.0-beta01.txt
+++ b/compose/foundation/foundation/api/restricted_1.3.0-beta01.txt
@@ -722,6 +722,9 @@
   public final class LazyStaggeredGridMeasureKt {
   }
 
+  public final class LazyStaggeredGridMeasurePolicyKt {
+  }
+
 }
 
 package androidx.compose.foundation.relocation {
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index 963ea75..11a455e 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -722,6 +722,9 @@
   public final class LazyStaggeredGridMeasureKt {
   }
 
+  public final class LazyStaggeredGridMeasurePolicyKt {
+  }
+
 }
 
 package androidx.compose.foundation.relocation {
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/SnappingDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/SnappingDemos.kt
index 7f172b1..804914a 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/SnappingDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/snapping/SnappingDemos.kt
@@ -50,6 +50,7 @@
 import androidx.compose.ui.graphics.drawscope.Stroke
 import androidx.compose.ui.layout.LayoutCoordinates
 import androidx.compose.ui.layout.onPlaced
+import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
@@ -83,7 +84,7 @@
     private val decayAnimationSpec: DecayAnimationSpec<Float>,
     private val lazySnapLayoutInfoProvider: SnapLayoutInfoProvider
 ) : SnapLayoutInfoProvider by lazySnapLayoutInfoProvider {
-    override fun calculateApproachOffset(initialVelocity: Float): Float {
+    override fun Density.calculateApproachOffset(initialVelocity: Float): Float {
         return decayAnimationSpec.calculateTargetValue(0f, initialVelocity) / 2f
     }
 }
@@ -110,7 +111,7 @@
     private val lazySnapLayoutInfoProvider: SnapLayoutInfoProvider,
     private val lazyLayoutState: LazyListState
 ) : SnapLayoutInfoProvider by lazySnapLayoutInfoProvider {
-    override fun calculateApproachOffset(initialVelocity: Float): Float {
+    override fun Density.calculateApproachOffset(initialVelocity: Float): Float {
         return lazyLayoutState.layoutInfo.visibleItemsInfo.sumOf { it.size }.toFloat()
     }
 }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyListSnapFlingBehaviorTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyListSnapFlingBehaviorTest.kt
index bc5b486..179cc7d 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyListSnapFlingBehaviorTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyListSnapFlingBehaviorTest.kt
@@ -19,8 +19,8 @@
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.gestures.snapping.MinFlingVelocityDp
-import androidx.compose.foundation.gestures.snapping.calculateDistanceToDesiredSnapPosition
 import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider
+import androidx.compose.foundation.gestures.snapping.calculateDistanceToDesiredSnapPosition
 import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.size
@@ -39,6 +39,7 @@
 import androidx.compose.ui.test.swipeLeft
 import androidx.compose.ui.test.swipeUp
 import androidx.compose.ui.test.swipeWithVelocity
+import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.dp
 import androidx.test.filters.LargeTest
 import kotlin.math.abs
@@ -53,24 +54,27 @@
 class LazyListSnapFlingBehaviorTest(private val orientation: Orientation) :
     BaseLazyListTestWithOrientation(orientation) {
 
+    private val density: Density
+        get() = rule.density
+
     @Test
     fun belowThresholdVelocity_lessThanAnItemScroll_shouldStayInSamePage() {
         var lazyListState: LazyListState? = null
         var stepSize = 0f
         var velocityThreshold = 0f
-
         // arrange
         rule.setContent {
+            val density = LocalDensity.current
             val state = rememberLazyListState().also { lazyListState = it }
-            stepSize = with(LocalDensity.current) { ItemSize.toPx() }
-            velocityThreshold = with(LocalDensity.current) { MinFlingVelocityDp.toPx() }
+            stepSize = with(density) { ItemSize.toPx() }
+            velocityThreshold = with(density) { MinFlingVelocityDp.toPx() }
             MainLayout(state = state)
         }
 
         // Scroll a bit
         onMainList().swipeOnMainAxis()
         rule.waitForIdle()
-        val currentItem = getCurrentSnappedItem(lazyListState)
+        val currentItem = density.getCurrentSnappedItem(lazyListState)
 
         // act
         onMainList().performTouchInput {
@@ -79,7 +83,7 @@
 
         // assert
         rule.runOnIdle {
-            val nextItem = getCurrentSnappedItem(lazyListState)
+            val nextItem = density.getCurrentSnappedItem(lazyListState)
             assertEquals(currentItem, nextItem)
         }
     }
@@ -92,16 +96,17 @@
 
         // arrange
         rule.setContent {
+            val density = LocalDensity.current
             val state = rememberLazyListState().also { lazyListState = it }
-            stepSize = with(LocalDensity.current) { ItemSize.toPx() }
-            velocityThreshold = with(LocalDensity.current) { MinFlingVelocityDp.toPx() }
+            stepSize = with(density) { ItemSize.toPx() }
+            velocityThreshold = with(density) { MinFlingVelocityDp.toPx() }
             MainLayout(state = state)
         }
 
         // Scroll a bit
         onMainList().swipeOnMainAxis()
         rule.waitForIdle()
-        val currentItem = getCurrentSnappedItem(lazyListState)
+        val currentItem = density.getCurrentSnappedItem(lazyListState)
 
         // act
         onMainList().performTouchInput {
@@ -113,7 +118,7 @@
 
         // assert
         rule.runOnIdle {
-            val nextItem = getCurrentSnappedItem(lazyListState)
+            val nextItem = density.getCurrentSnappedItem(lazyListState)
             assertEquals(currentItem + 1, nextItem)
         }
     }
@@ -126,16 +131,17 @@
 
         // arrange
         rule.setContent {
+            val density = LocalDensity.current
             val state = rememberLazyListState().also { lazyListState = it }
-            stepSize = with(LocalDensity.current) { ItemSize.toPx() }
-            velocityThreshold = with(LocalDensity.current) { MinFlingVelocityDp.toPx() }
+            stepSize = with(density) { ItemSize.toPx() }
+            velocityThreshold = with(density) { MinFlingVelocityDp.toPx() }
             MainLayout(state = state)
         }
 
         // Scroll a bit
         onMainList().swipeOnMainAxis()
         rule.waitForIdle()
-        val currentItem = getCurrentSnappedItem(lazyListState)
+        val currentItem = density.getCurrentSnappedItem(lazyListState)
 
         // act
         onMainList().performTouchInput {
@@ -147,7 +153,7 @@
 
         // assert
         rule.runOnIdle {
-            val nextItem = getCurrentSnappedItem(lazyListState)
+            val nextItem = density.getCurrentSnappedItem(lazyListState)
             assertEquals(currentItem + 1, nextItem)
         }
     }
@@ -160,16 +166,17 @@
 
         // arrange
         rule.setContent {
+            val density = LocalDensity.current
             val state = rememberLazyListState().also { lazyListState = it }
-            stepSize = with(LocalDensity.current) { ItemSize.toPx() }
-            velocityThreshold = with(LocalDensity.current) { MinFlingVelocityDp.toPx() }
+            stepSize = with(density) { ItemSize.toPx() }
+            velocityThreshold = with(density) { MinFlingVelocityDp.toPx() }
             MainLayout(state = state)
         }
 
         // Scroll a bit
         onMainList().swipeOnMainAxis()
         rule.waitForIdle()
-        val currentItem = getCurrentSnappedItem(lazyListState)
+        val currentItem = density.getCurrentSnappedItem(lazyListState)
 
         // act
         onMainList().performTouchInput {
@@ -182,7 +189,7 @@
 
         // assert
         rule.runOnIdle {
-            val nextItem = getCurrentSnappedItem(lazyListState)
+            val nextItem = density.getCurrentSnappedItem(lazyListState)
             assertEquals(currentItem - 1, nextItem)
         }
     }
@@ -195,16 +202,17 @@
 
         // arrange
         rule.setContent {
+            val density = LocalDensity.current
             val state = rememberLazyListState().also { lazyListState = it }
-            stepSize = with(LocalDensity.current) { ItemSize.toPx() }
-            velocityThreshold = with(LocalDensity.current) { MinFlingVelocityDp.toPx() }
+            stepSize = with(density) { ItemSize.toPx() }
+            velocityThreshold = with(density) { MinFlingVelocityDp.toPx() }
             MainLayout(state = state)
         }
 
         // Scroll a bit
         onMainList().swipeOnMainAxis()
         rule.waitForIdle()
-        val currentItem = getCurrentSnappedItem(lazyListState)
+        val currentItem = density.getCurrentSnappedItem(lazyListState)
 
         // act
         onMainList().performTouchInput {
@@ -216,7 +224,7 @@
 
         // assert
         rule.runOnIdle {
-            val nextItem = getCurrentSnappedItem(lazyListState)
+            val nextItem = density.getCurrentSnappedItem(lazyListState)
             assertEquals(currentItem + 2, nextItem)
         }
     }
@@ -226,7 +234,6 @@
     @Composable
     fun MainLayout(state: LazyListState) {
         val layoutInfoProvider = remember(state) { SnapLayoutInfoProvider(state) }
-
         LazyColumnOrRow(
             state = state,
             modifier = Modifier.testTag(TestTag),
@@ -248,13 +255,16 @@
         }
     }
 
-    private fun getCurrentSnappedItem(state: LazyListState?): Int {
+    private fun Density.getCurrentSnappedItem(state: LazyListState?): Int {
         var itemIndex = -1
         if (state == null) return -1
         var minDistance = Float.POSITIVE_INFINITY
         (state.layoutInfo.visibleItemsInfo).forEach {
-            val distance =
-                calculateDistanceToDesiredSnapPosition(state.layoutInfo, it, CenterToCenter)
+            val distance = calculateDistanceToDesiredSnapPosition(
+                state.layoutInfo,
+                it,
+                CenterToCenter
+            )
             if (abs(distance) < minDistance) {
                 minDistance = abs(distance)
                 itemIndex = it.index
@@ -287,7 +297,7 @@
 
         val ItemSize = 200.dp
         const val TestTag = "MainList"
-        val CenterToCenter: (Float, Float) -> Float =
+        val CenterToCenter: Density.(Float, Float) -> Float =
             { layoutSize, itemSize -> layoutSize / 2f - itemSize / 2f }
     }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyListSnapLayoutInfoProviderTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyListSnapLayoutInfoProviderTest.kt
index 909db89..d889aac 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyListSnapLayoutInfoProviderTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyListSnapLayoutInfoProviderTest.kt
@@ -30,6 +30,7 @@
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import androidx.test.filters.LargeTest
@@ -46,17 +47,21 @@
 class LazyListSnapLayoutInfoProviderTest(orientation: Orientation) :
     BaseLazyListTestWithOrientation(orientation) {
 
+    private val density: Density
+        get() = rule.density
+
     @Test
     fun snapStepSize_sameSizeItems_shouldBeAverageItemSize() {
         var expectedItemSize = 0f
         var actualItemSize = 0f
 
         rule.setContent {
+            val density = LocalDensity.current
             val state = rememberLazyListState()
             val layoutInfoProvider = remember(state) { createLayoutInfo(state) }.also {
-                actualItemSize = it.snapStepSize
+                actualItemSize = with(it) { density.snapStepSize() }
             }
-            expectedItemSize = with(LocalDensity.current) { FixedItemSize.toPx() }
+            expectedItemSize = with(density) { FixedItemSize.toPx() }
             MainLayout(
                 state = state,
                 layoutInfo = layoutInfoProvider,
@@ -76,9 +81,10 @@
         var expectedItemSize = 0f
 
         rule.setContent {
+            val density = LocalDensity.current
             val state = rememberLazyListState()
             val layoutInfoProvider = remember(state) { createLayoutInfo(state) }.also {
-                actualItemSize = it.snapStepSize
+                actualItemSize = with(it) { density.snapStepSize() }
             }
             expectedItemSize = state.layoutInfo.visibleItemsInfo.map { it.size }.average().toFloat()
 
@@ -95,13 +101,14 @@
         var snapStepSize = 0f
         var actualItemSize = 0f
         rule.setContent {
+            val density = LocalDensity.current
             val state = rememberLazyListState()
             val layoutInfoProvider = remember(state) { createLayoutInfo(state) }.also {
-                snapStepSize = it.snapStepSize
+                snapStepSize = with(it) { density.snapStepSize() }
             }
 
-            actualItemSize =
-                with(LocalDensity.current) { (FixedItemSize + FixedItemSize / 2).toPx() }
+            actualItemSize = with(density) { (FixedItemSize + FixedItemSize / 2).toPx() }
+
             MainLayout(
                 state = state,
                 layoutInfo = layoutInfoProvider,
@@ -122,9 +129,10 @@
         var upperBound = 0f
         var lowerBound = 0f
         rule.setContent {
+            val density = LocalDensity.current
             val state = rememberLazyListState()
             val layoutInfoProvider = remember(state) { createLayoutInfo(state) }
-            val bounds = layoutInfoProvider.calculateSnappingOffsetBounds()
+            val bounds = with(layoutInfoProvider) { density.calculateSnappingOffsetBounds() }
             lowerBound = bounds.start
             upperBound = bounds.endInclusive
             MainLayout(
@@ -143,12 +151,10 @@
 
     @Test
     fun calculateApproachOffset_approachOffsetIsAlwaysZero() {
-        var snapLayoutInfoProvider: SnapLayoutInfoProvider? = null
+        lateinit var layoutInfoProvider: SnapLayoutInfoProvider
         rule.setContent {
             val state = rememberLazyListState()
-            val layoutInfoProvider = remember(state) { createLayoutInfo(state) }.also {
-                snapLayoutInfoProvider = it
-            }
+            layoutInfoProvider = remember(state) { createLayoutInfo(state) }
             LazyColumnOrRow(
                 state = state,
                 flingBehavior = rememberSnapFlingBehavior(layoutInfoProvider)
@@ -160,8 +166,8 @@
         }
 
         rule.runOnIdle {
-            assertEquals(snapLayoutInfoProvider?.calculateApproachOffset(1000f), 0f)
-            assertEquals(snapLayoutInfoProvider?.calculateApproachOffset(-1000f), 0f)
+            assertEquals(with(layoutInfoProvider) { density.calculateApproachOffset(1000f) }, 0f)
+            assertEquals(with(layoutInfoProvider) { density.calculateApproachOffset(-1000f) }, 0f)
         }
     }
 
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/SnapFlingBehaviorTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/SnapFlingBehaviorTest.kt
index 4931030..b3d3b3d 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/SnapFlingBehaviorTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/SnapFlingBehaviorTest.kt
@@ -38,6 +38,7 @@
 import androidx.compose.runtime.remember
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.Density
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import kotlin.test.assertEquals
@@ -54,6 +55,9 @@
 
     private val inspectSpringAnimationSpec = InspectSpringAnimationSpec(spring())
 
+    private val density: Density
+        get() = rule.density
+
     @Test
     fun performFling_whenVelocityIsBelowThreshold_shouldShortSnap() {
         val testLayoutInfoProvider = TestLayoutInfoProvider()
@@ -105,15 +109,15 @@
     @Test
     fun findClosestOffset_noFlingDirection_shouldReturnAbsoluteDistance() {
         val testLayoutInfoProvider = TestLayoutInfoProvider()
-        val offset = findClosestOffset(0f, testLayoutInfoProvider)
+        val offset = findClosestOffset(0f, testLayoutInfoProvider, density)
         assertEquals(offset, MinOffset)
     }
 
     @Test
     fun findClosestOffset_flingDirection_shouldReturnCorrectBound() {
         val testLayoutInfoProvider = TestLayoutInfoProvider()
-        val forwardOffset = findClosestOffset(1f, testLayoutInfoProvider)
-        val backwardOffset = findClosestOffset(-1f, testLayoutInfoProvider)
+        val forwardOffset = findClosestOffset(1f, testLayoutInfoProvider, density)
+        val backwardOffset = findClosestOffset(-1f, testLayoutInfoProvider, density)
         assertEquals(forwardOffset, MaxOffset)
         assertEquals(backwardOffset, MinOffset)
     }
@@ -266,14 +270,15 @@
 ) : SnapLayoutInfoProvider {
     var calculateApproachOffsetCount = 0
 
-    override val snapStepSize: Float
-        get() = snapStep
+    override fun Density.snapStepSize(): Float {
+        return snapStep
+    }
 
-    override fun calculateSnappingOffsetBounds(): ClosedFloatingPointRange<Float> {
+    override fun Density.calculateSnappingOffsetBounds(): ClosedFloatingPointRange<Float> {
         return minOffset.rangeTo(maxOffset)
     }
 
-    override fun calculateApproachOffset(initialVelocity: Float): Float {
+    override fun Density.calculateApproachOffset(initialVelocity: Float): Float {
         calculateApproachOffsetCount++
         return approachOffset
     }
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridPrefetcherTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridPrefetcherTest.kt
new file mode 100644
index 0000000..fdfbecc
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridPrefetcherTest.kt
@@ -0,0 +1,461 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy.staggeredgrid
+
+import androidx.compose.foundation.AutoTestFrameClock
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.border
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.scrollBy
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.Remeasurement
+import androidx.compose.ui.layout.RemeasurementModifier
+import androidx.compose.ui.layout.SubcomposeLayout
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.Dp
+import androidx.test.filters.LargeTest
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@OptIn(ExperimentalFoundationApi::class)
+@LargeTest
+@RunWith(Parameterized::class)
+class LazyStaggeredGridPrefetcherTest(
+    orientation: Orientation
+) : BaseLazyStaggeredGridWithOrientation(orientation) {
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun initParameters(): Array<Any> = arrayOf(
+            Orientation.Vertical,
+            Orientation.Horizontal,
+        )
+    }
+
+    val itemsSizePx = 30
+    val itemsSizeDp = with(rule.density) { itemsSizePx.toDp() }
+
+    internal lateinit var state: LazyStaggeredGridState
+
+    @Test
+    fun notPrefetchingForwardInitially() {
+        composeStaggeredGrid()
+
+        rule.onNodeWithTag("4")
+            .assertDoesNotExist()
+    }
+
+    @Test
+    fun notPrefetchingBackwardInitially() {
+        composeStaggeredGrid(firstItem = 4)
+
+        rule.onNodeWithTag("0")
+            .assertDoesNotExist()
+    }
+
+    @Test
+    fun prefetchingForwardAfterSmallScroll() {
+        composeStaggeredGrid()
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollBy(5f)
+            }
+        }
+
+        waitForPrefetch(4)
+
+        rule.onNodeWithTag("4")
+            .assertExists()
+        rule.onNodeWithTag("5")
+            .assertExists()
+        rule.onNodeWithTag("6")
+            .assertDoesNotExist()
+    }
+
+    @Test
+    fun prefetchingBackwardAfterSmallScroll() {
+        composeStaggeredGrid(firstItem = 4, itemOffset = 10)
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollBy(-5f)
+            }
+        }
+
+        waitForPrefetch(2)
+
+        rule.onNodeWithTag("2")
+            .assertExists()
+        rule.onNodeWithTag("3")
+            .assertExists()
+        rule.onNodeWithTag("0")
+            .assertDoesNotExist()
+    }
+
+    @Test
+    fun prefetchingForwardAndBackward() {
+        composeStaggeredGrid(firstItem = 4)
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollBy(5f)
+            }
+        }
+
+        waitForPrefetch(8)
+
+        rule.onNodeWithTag("8")
+            .assertExists()
+        rule.onNodeWithTag("9")
+            .assertExists()
+        rule.onNodeWithTag("0")
+            .assertDoesNotExist()
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollBy(-5f)
+            }
+        }
+
+        waitForPrefetch(0)
+
+        rule.onNodeWithTag("0")
+            .assertExists()
+        rule.onNodeWithTag("1")
+            .assertExists()
+        rule.onNodeWithTag("8")
+            .assertDoesNotExist()
+    }
+
+    @Test
+    fun prefetchingForwardTwice() {
+        composeStaggeredGrid()
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollBy(5f)
+            }
+        }
+
+        waitForPrefetch(4)
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollBy(itemsSizePx / 2f)
+                state.scrollBy(itemsSizePx / 2f)
+            }
+        }
+
+        waitForPrefetch(6)
+
+        rule.onNodeWithTag("4")
+            .assertIsDisplayed()
+        rule.onNodeWithTag("6")
+            .assertExists()
+        rule.onNodeWithTag("8")
+            .assertDoesNotExist()
+    }
+
+    @Test
+    fun prefetchingBackwardTwice() {
+        composeStaggeredGrid(firstItem = 8)
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollBy(-5f)
+            }
+        }
+
+        waitForPrefetch(4)
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollBy(-itemsSizePx / 2f)
+                state.scrollBy(-itemsSizePx / 2f)
+            }
+        }
+
+        waitForPrefetch(2)
+
+        rule.onNodeWithTag("4")
+            .assertIsDisplayed()
+        rule.onNodeWithTag("6")
+            .assertIsDisplayed()
+        rule.onNodeWithTag("2")
+            .assertExists()
+        rule.onNodeWithTag("0")
+            .assertDoesNotExist()
+    }
+
+    @Test
+    fun prefetchingCancelledIfScrollDirectionChanged() {
+        composeStaggeredGrid(firstItem = 8)
+
+        rule.runOnIdle {
+            runBlocking {
+                state.scrollBy(-5f)
+                state.scrollBy(5f)
+            }
+        }
+
+        waitForPrefetch(12)
+
+        rule.onNodeWithTag("12")
+            .assertExists()
+            .assertIsNotDisplayed()
+        rule.onNodeWithTag("13")
+            .assertExists()
+            .assertIsNotDisplayed()
+        rule.onNodeWithTag("4")
+            .assertDoesNotExist()
+        rule.onNodeWithTag("5")
+            .assertDoesNotExist()
+    }
+
+//    @Test
+//    fun prefetchingForwardAndBackwardReverseLayout() {
+//        composeStaggeredGrid(firstItem = 2, reverseLayout = true)
+//
+//        rule.runOnIdle {
+//            runBlocking {
+//                state.scrollBy(5f)
+//            }
+//        }
+//
+//        waitForPrefetch(6)
+//
+//        rule.onNodeWithTag("6")
+//            .assertExists()
+//        rule.onNodeWithTag("7")
+//            .assertExists()
+//        rule.onNodeWithTag("0")
+//            .assertDoesNotExist()
+//        rule.onNodeWithTag("1")
+//            .assertDoesNotExist()
+//
+//        rule.runOnIdle {
+//            runBlocking {
+//                state.scrollBy(-2f)
+//                state.scrollBy(-1f)
+//            }
+//        }
+//
+//        waitForPrefetch(0)
+//
+//        rule.onNodeWithTag("0")
+//            .assertExists()
+//        rule.onNodeWithTag("1")
+//            .assertExists()
+//        rule.onNodeWithTag("6")
+//            .assertDoesNotExist()
+//        rule.onNodeWithTag("7")
+//            .assertDoesNotExist()
+//    }
+
+//    @Test
+//    fun prefetchingForwardAndBackwardWithContentPadding() {
+//        val halfItemSize = itemsSizeDp / 2f
+//        composeStaggeredGrid(
+//            firstItem = 4,
+//            itemOffset = 5,
+//            contentPadding = PaddingValues(mainAxis = halfItemSize)
+//        )
+//
+//        rule.onNodeWithTag("2")
+//            .assertIsDisplayed()
+//        rule.onNodeWithTag("4")
+//            .assertIsDisplayed()
+//        rule.onNodeWithTag("6")
+//            .assertIsDisplayed()
+//        rule.onNodeWithTag("0")
+//            .assertDoesNotExist()
+//        rule.onNodeWithTag("8")
+//            .assertDoesNotExist()
+//
+//        rule.runOnIdle {
+//            runBlocking {
+//                state.scrollBy(5f)
+//            }
+//        }
+//
+//        waitForPrefetch(6)
+//
+//        rule.onNodeWithTag("8")
+//            .assertExists()
+//        rule.onNodeWithTag("0")
+//            .assertDoesNotExist()
+//
+//        rule.runOnIdle {
+//            runBlocking {
+//                state.scrollBy(-2f)
+//            }
+//        }
+//
+//        waitForPrefetch(0)
+//
+//        rule.onNodeWithTag("0")
+//            .assertExists()
+//    }
+
+    @Test
+    fun disposingWhilePrefetchingScheduled() {
+        var emit = true
+        lateinit var remeasure: Remeasurement
+        state = LazyStaggeredGridState()
+        rule.setContent {
+            SubcomposeLayout(
+                modifier = object : RemeasurementModifier {
+                    override fun onRemeasurementAvailable(remeasurement: Remeasurement) {
+                        remeasure = remeasurement
+                    }
+                }
+            ) { constraints ->
+                val placeable = if (emit) {
+                    subcompose(Unit) {
+                        LazyStaggeredGrid(
+                            2,
+                            Modifier.mainAxisSize(itemsSizeDp * 1.5f),
+                            state,
+                        ) {
+                            items(1000) {
+                                Spacer(
+                                    Modifier.mainAxisSize(itemsSizeDp)
+                                )
+                            }
+                        }
+                    }.first().measure(constraints)
+                } else {
+                    null
+                }
+                layout(constraints.maxWidth, constraints.maxHeight) {
+                    placeable?.place(0, 0)
+                }
+            }
+        }
+
+        rule.runOnIdle {
+            // this will schedule the prefetching
+            runBlocking(AutoTestFrameClock()) {
+                state.scrollBy(itemsSizePx.toFloat())
+            }
+            // then we synchronously dispose LazyColumn
+            emit = false
+            remeasure.forceRemeasure()
+        }
+
+        rule.runOnIdle { }
+    }
+
+//    @Test
+//    fun snappingToOtherPositionWhilePrefetchIsScheduled() {
+//        val composedItems = mutableListOf<Int>()
+//        rule.setContent {
+//            state = rememberLazyGridState()
+//            LazyGrid(
+//                1,
+//                Modifier.mainAxisSize(itemsSizeDp * 1.5f),
+//                state,
+//            ) {
+//                items(1000) {
+//                    composedItems.add(it)
+//                    Spacer(Modifier.mainAxisSize(itemsSizeDp))
+//                }
+//            }
+//        }
+//
+//        rule.runOnIdle {
+//            // now we have items 0 and 1 visible
+//            runBlocking(AutoTestFrameClock()) {
+//                // this will move the viewport so items 1 and 2 are visible
+//                // and schedule a prefetching for 3
+//                state.scrollBy(itemsSizePx.toFloat())
+//                // then we move so that items 100 and 101 are visible.
+//                // this should cancel the prefetch for 3
+//                state.scrollToItem(100)
+//            }
+//        }
+//
+//        // wait a few frames to make sure prefetch happens if was scheduled
+//        rule.waitForIdle()
+//        rule.waitForIdle()
+//        rule.waitForIdle()
+//
+//        rule.runOnIdle {
+//            Truth.assertThat(composedItems).doesNotContain(3)
+//        }
+//    }
+
+    private fun waitForPrefetch(index: Int) {
+        rule.waitUntil {
+            activeNodes.contains(index) && activeMeasuredNodes.contains(index)
+        }
+    }
+
+    private val activeNodes = mutableSetOf<Int>()
+    private val activeMeasuredNodes = mutableSetOf<Int>()
+
+    private fun composeStaggeredGrid(
+        firstItem: Int = 0,
+        itemOffset: Int = 0,
+    ) {
+        state = LazyStaggeredGridState(
+            initialFirstVisibleItems = intArrayOf(firstItem, firstItem + 1),
+            initialFirstVisibleOffsets = intArrayOf(itemOffset, itemOffset)
+        )
+        rule.setContent {
+            LazyStaggeredGrid(
+                2,
+                Modifier.mainAxisSize(itemsSizeDp * 1.5f),
+                state,
+            ) {
+                items(100) {
+                    DisposableEffect(it) {
+                        activeNodes.add(it)
+                        onDispose {
+                            activeNodes.remove(it)
+                            activeMeasuredNodes.remove(it)
+                        }
+                    }
+                    Spacer(
+                        Modifier
+                            .mainAxisSize(itemsSizeDp)
+                            .border(Dp.Hairline, Color.Black)
+                            .testTag("$it")
+                            .layout { measurable, constraints ->
+                                val placeable = measurable.measure(constraints)
+                                activeMeasuredNodes.add(it)
+                                layout(placeable.width, placeable.height) {
+                                    placeable.place(0, 0)
+                                }
+                            }
+                    )
+                }
+            }
+        }
+    }
+}
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridSpansTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridSpansTest.kt
new file mode 100644
index 0000000..63621ea
--- /dev/null
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridSpansTest.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy.staggeredgrid
+
+import kotlin.test.assertEquals
+import org.junit.Test
+
+class LazyStaggeredGridSpansTest {
+    private val spans = LazyStaggeredGridSpans()
+
+    @Test
+    fun emptySpan_unset() {
+        assertEquals(LazyStaggeredGridSpans.Unset, spans.getSpan(0))
+    }
+
+    @Test
+    fun setSpan() {
+        spans.setSpan(0, 42)
+        spans.setSpan(1, 0)
+
+        assertEquals(42, spans.getSpan(0))
+        assertEquals(0, spans.getSpan(1))
+    }
+
+    @Test
+    fun setSpan_beyondBound() {
+        val bound = spans.upperBound()
+        spans.setSpan(bound - 1, 42)
+        spans.setSpan(bound, 42)
+
+        assertEquals(42, spans.getSpan(bound - 1))
+        assertEquals(42, spans.getSpan(bound))
+    }
+
+    @Test
+    fun setSpan_largeNumber() {
+        spans.setSpan(Int.MAX_VALUE / 2, 42)
+
+        assertEquals(42, spans.getSpan(Int.MAX_VALUE / 2))
+    }
+
+    @Test
+    fun setSpan_decreaseBound() {
+        spans.setSpan(Int.MAX_VALUE / 2, 42)
+        spans.setSpan(0, 42)
+
+        assertEquals(-1, spans.getSpan(Int.MAX_VALUE / 2))
+        assertEquals(42, spans.getSpan(0))
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun setSpan_negative() {
+        spans.setSpan(-1, 0)
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridTest.kt
index df3d0f3..358ed79 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridTest.kt
@@ -17,11 +17,19 @@
 package androidx.compose.foundation.lazy.staggeredgrid
 
 import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.border
 import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.grid.scrollBy
+import androidx.compose.foundation.text.BasicText
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.composed
+import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.assertIsNotDisplayed
@@ -284,6 +292,418 @@
     }
 
     @Test
+    fun itemsAreCorrectedWhenSizeIncreased() {
+        val state = LazyStaggeredGridState()
+        var expanded by mutableStateOf(false)
+        rule.setContent {
+            LazyStaggeredGrid(
+                lanes = 2,
+                state = state,
+                modifier = Modifier.axisSize(
+                    crossAxis = itemSizeDp * 2,
+                    mainAxis = itemSizeDp * 2
+                ),
+            ) {
+                item {
+                    Spacer(
+                        Modifier.axisSize(
+                            crossAxis = itemSizeDp,
+                            mainAxis = if (expanded) itemSizeDp * 2 else itemSizeDp
+                        ).testTag("$it")
+                    )
+                }
+                items(5) {
+                    Spacer(
+                        Modifier.axisSize(
+                            crossAxis = itemSizeDp,
+                            mainAxis = itemSizeDp
+                        ).testTag("${it + 1}")
+                    )
+                }
+            }
+        }
+
+        rule.onNodeWithTag("0")
+            .assertMainAxisSizeIsEqualTo(itemSizeDp)
+            .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+
+        rule.onNodeWithTag("1")
+            .assertMainAxisSizeIsEqualTo(itemSizeDp)
+            .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+
+        rule.onNodeWithTag("2")
+            .assertMainAxisSizeIsEqualTo(itemSizeDp)
+            .assertCrossAxisStartPositionInRootIsEqualTo(0.dp)
+            .assertMainAxisStartPositionInRootIsEqualTo(itemSizeDp)
+
+        state.scrollBy(itemSizeDp * 3)
+
+        expanded = true
+
+        state.scrollBy(-itemSizeDp * 3)
+
+        rule.onNodeWithTag("0")
+            .assertCrossAxisStartPositionInRootIsEqualTo(0.dp)
+            .assertMainAxisSizeIsEqualTo(itemSizeDp * 2)
+
+        rule.onNodeWithTag("2")
+            .assertMainAxisSizeIsEqualTo(itemSizeDp)
+            .assertCrossAxisStartPositionInRootIsEqualTo(itemSizeDp)
+            .assertMainAxisStartPositionInRootIsEqualTo(itemSizeDp)
+    }
+
+    @Test
+    fun itemsAreCorrectedWhenSizeDecreased() {
+        val state = LazyStaggeredGridState()
+        var expanded by mutableStateOf(true)
+        rule.setContent {
+            LazyStaggeredGrid(
+                lanes = 2,
+                state = state,
+                modifier = Modifier.axisSize(
+                    crossAxis = itemSizeDp * 2,
+                    mainAxis = itemSizeDp * 2
+                ),
+            ) {
+                item {
+                    Spacer(
+                        Modifier.axisSize(
+                            crossAxis = itemSizeDp,
+                            mainAxis = if (expanded) itemSizeDp * 2 else itemSizeDp
+                        ).testTag("$it")
+                    )
+                }
+                items(5) {
+                    Spacer(
+                        Modifier.axisSize(
+                            crossAxis = itemSizeDp,
+                            mainAxis = itemSizeDp
+                        ).testTag("${it + 1}")
+                    )
+                }
+            }
+        }
+
+        rule.onNodeWithTag("0")
+            .assertMainAxisSizeIsEqualTo(itemSizeDp * 2)
+            .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+
+        rule.onNodeWithTag("1")
+            .assertMainAxisSizeIsEqualTo(itemSizeDp)
+            .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+
+        rule.onNodeWithTag("2")
+            .assertMainAxisSizeIsEqualTo(itemSizeDp)
+            .assertCrossAxisStartPositionInRootIsEqualTo(itemSizeDp)
+            .assertMainAxisStartPositionInRootIsEqualTo(itemSizeDp)
+
+        state.scrollBy(itemSizeDp * 3)
+
+        expanded = false
+
+        state.scrollBy(-itemSizeDp * 3)
+
+        rule.onNodeWithTag("0")
+            .assertCrossAxisStartPositionInRootIsEqualTo(0.dp)
+            .assertMainAxisSizeIsEqualTo(itemSizeDp)
+
+        rule.onNodeWithTag("2")
+            .assertMainAxisSizeIsEqualTo(itemSizeDp)
+            .assertCrossAxisStartPositionInRootIsEqualTo(0.dp)
+            .assertMainAxisStartPositionInRootIsEqualTo(itemSizeDp)
+    }
+
+    @Test
+    fun itemsAreCorrectedWithWrongColumns() {
+        val state = LazyStaggeredGridState(
+            // intentionally wrong values, normally items should be [0, 1][2, 3][4, 5]
+            initialFirstVisibleItems = intArrayOf(3, 4),
+            initialFirstVisibleOffsets = intArrayOf(itemSizePx / 2, itemSizePx / 2)
+        )
+        rule.setContent {
+            LazyStaggeredGrid(
+                lanes = 2,
+                state = state,
+                modifier = Modifier.axisSize(
+                    crossAxis = itemSizeDp * 2,
+                    mainAxis = itemSizeDp
+                ),
+            ) {
+                items(6) {
+                    Spacer(
+                        Modifier.axisSize(
+                            crossAxis = itemSizeDp,
+                            mainAxis = itemSizeDp
+                        ).testTag("$it")
+                    )
+                }
+            }
+        }
+
+        rule.onNodeWithTag("0")
+            .assertDoesNotExist()
+
+        rule.onNodeWithTag("3")
+            .assertMainAxisStartPositionInRootIsEqualTo(-itemSizeDp / 2)
+
+        rule.onNodeWithTag("4")
+            .assertMainAxisSizeIsEqualTo(itemSizeDp)
+            .assertMainAxisStartPositionInRootIsEqualTo(-itemSizeDp / 2)
+
+        state.scrollBy(-itemSizeDp * 3)
+
+        rule.onNodeWithTag("0")
+            .assertCrossAxisStartPositionInRootIsEqualTo(0.dp)
+            .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+
+        rule.onNodeWithTag("1")
+            .assertCrossAxisStartPositionInRootIsEqualTo(itemSizeDp)
+            .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+    }
+
+    @Test
+    fun addItems() {
+        val state = LazyStaggeredGridState()
+        var itemsCount by mutableStateOf(1)
+        rule.setContent {
+            LazyStaggeredGrid(
+                lanes = 2,
+                state = state,
+                modifier = Modifier.axisSize(
+                    crossAxis = itemSizeDp * 2,
+                    mainAxis = itemSizeDp
+                ),
+            ) {
+                items(itemsCount) {
+                    Spacer(
+                        Modifier.axisSize(
+                            crossAxis = itemSizeDp,
+                            mainAxis = itemSizeDp
+                        ).testTag("$it")
+                    )
+                }
+            }
+        }
+
+        rule.onNodeWithTag("0")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("1")
+            .assertDoesNotExist()
+
+        itemsCount = 10
+
+        rule.waitForIdle()
+
+        state.scrollBy(itemSizeDp * 10)
+
+        rule.onNodeWithTag("8")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("9")
+            .assertIsDisplayed()
+
+        itemsCount = 20
+
+        rule.waitForIdle()
+
+        state.scrollBy(itemSizeDp * 10)
+
+        rule.onNodeWithTag("18")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("19")
+            .assertIsDisplayed()
+    }
+
+    @Test
+    fun removeItems() {
+        val state = LazyStaggeredGridState()
+        var itemsCount by mutableStateOf(20)
+        rule.setContent {
+            LazyStaggeredGrid(
+                lanes = 2,
+                state = state,
+                modifier = Modifier.axisSize(
+                    crossAxis = itemSizeDp * 2,
+                    mainAxis = itemSizeDp
+                ),
+            ) {
+                items(itemsCount) {
+                    Spacer(
+                        Modifier.axisSize(
+                            crossAxis = itemSizeDp,
+                            mainAxis = itemSizeDp
+                        ).testTag("$it")
+                    )
+                }
+            }
+        }
+
+        state.scrollBy(itemSizeDp * 20)
+
+        rule.onNodeWithTag("18")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("19")
+            .assertIsDisplayed()
+
+        itemsCount = 10
+
+        rule.onNodeWithTag("8")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("9")
+            .assertIsDisplayed()
+
+        itemsCount = 1
+
+        rule.onNodeWithTag("0")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("1")
+            // seems like reuse keeps the node around?
+            .assertIsNotDisplayed()
+    }
+
+    @Test
+    fun resizingItems_maintainsScrollingRange() {
+        val state = LazyStaggeredGridState()
+        var itemSizes by mutableStateOf(
+            List(20) {
+                itemSizeDp * (it % 4 + 1)
+            }
+        )
+        rule.setContent {
+            LazyStaggeredGrid(
+                lanes = 2,
+                state = state,
+                modifier = Modifier.axisSize(
+                    crossAxis = itemSizeDp * 2,
+                    mainAxis = itemSizeDp * 5
+                ).testTag("lazy").border(1.dp, Color.Red),
+            ) {
+                items(20) {
+                    Box(
+                        Modifier.axisSize(
+                            crossAxis = itemSizeDp,
+                            mainAxis = itemSizes[it]
+                        ).testTag("$it").border(1.dp, Color.Black)
+                    ) {
+                        BasicText("$it")
+                    }
+                }
+            }
+        }
+
+        rule.onNodeWithTag("lazy")
+            .scrollMainAxisBy(itemSizeDp * 20)
+
+        rule.onNodeWithTag("18")
+            .assertMainAxisSizeIsEqualTo(itemSizes[18])
+
+        rule.onNodeWithTag("19")
+            .assertMainAxisSizeIsEqualTo(itemSizes[19])
+
+        itemSizes = itemSizes.reversed()
+
+        rule.onNodeWithTag("18")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("19")
+            .assertIsDisplayed()
+
+        rule.onNodeWithTag("lazy")
+            .scrollMainAxisBy(-itemSizeDp * 20)
+
+        rule.onNodeWithTag("0")
+            .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+
+        rule.onNodeWithTag("1")
+            .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+    }
+
+    @Test
+    fun removingItems_maintainsCorrectOffsets() {
+        val state = LazyStaggeredGridState(
+            initialFirstVisibleItems = intArrayOf(10, 11),
+            initialFirstVisibleOffsets = intArrayOf(0, 0)
+        )
+        var itemCount by mutableStateOf(20)
+        rule.setContent {
+            LazyStaggeredGrid(
+                lanes = 2,
+                state = state,
+                modifier = Modifier.axisSize(
+                    crossAxis = itemSizeDp * 2,
+                    mainAxis = itemSizeDp * 5
+                ).testTag("lazy").border(1.dp, Color.Red),
+            ) {
+                items(itemCount) {
+                    Box(
+                        Modifier.axisSize(
+                            crossAxis = itemSizeDp,
+                            mainAxis = itemSizeDp * (it % 3 + 1)
+                        ).testTag("$it").border(1.dp, Color.Black)
+                    ) {
+                        BasicText("$it")
+                    }
+                }
+            }
+        }
+
+        itemCount = 3
+
+        rule.waitForIdle()
+
+        rule.onNodeWithTag("0")
+            .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+
+        rule.onNodeWithTag("1")
+            .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+    }
+
+    @Test
+    fun staggeredGrid_supportsLargeIndices() {
+        val state = LazyStaggeredGridState(
+            initialFirstVisibleItems = intArrayOf(Int.MAX_VALUE / 2, Int.MAX_VALUE / 2 + 1),
+            initialFirstVisibleOffsets = intArrayOf(0, 0)
+        )
+        rule.setContent {
+            LazyStaggeredGrid(
+                lanes = 2,
+                state = state,
+                modifier = Modifier.axisSize(
+                    crossAxis = itemSizeDp * 2,
+                    mainAxis = itemSizeDp * 5
+                ).testTag("lazy").border(1.dp, Color.Red),
+            ) {
+                items(Int.MAX_VALUE) {
+                    Spacer(
+                        Modifier.axisSize(
+                            crossAxis = itemSizeDp,
+                            mainAxis = itemSizeDp * (it % 3 + 1)
+                        ).testTag("$it").border(1.dp, Color.Black)
+                    )
+                }
+            }
+        }
+
+        rule.onNodeWithTag("${Int.MAX_VALUE / 2}")
+            .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+
+        rule.onNodeWithTag("${Int.MAX_VALUE / 2 + 1}")
+            .assertMainAxisStartPositionInRootIsEqualTo(0.dp)
+
+        // check that scrolling back and forth doesn't crash
+        rule.onNodeWithTag("lazy")
+            .scrollMainAxisBy(10000.dp)
+
+        rule.onNodeWithTag("lazy")
+            .scrollMainAxisBy(-10000.dp)
+    }
+
+    @Test
     fun scrollingALot_layoutIsNotRecomposed() {
         val state = LazyStaggeredGridState()
         var recomposed = 0
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt
index 084ef75..99deaa7 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt
@@ -21,6 +21,7 @@
 import androidx.compose.foundation.lazy.LazyListItemInfo
 import androidx.compose.foundation.lazy.LazyListLayoutInfo
 import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.ui.unit.Density
 import androidx.compose.ui.util.fastForEach
 import androidx.compose.ui.util.fastSumBy
 
@@ -32,23 +33,22 @@
  * This position should be considered with regard to the start edge of the item and the placement
  * within the viewport.
  *
- * @return A [SnapLayoutInfoProvider] that can be used with [snapFlingBehavior]
+ * @return A [SnapLayoutInfoProvider] that can be used with [SnapFlingBehavior]
  */
 @ExperimentalFoundationApi
 fun SnapLayoutInfoProvider(
     lazyListState: LazyListState,
-    positionInLayout: (layoutSize: Float, itemSize: Float) -> Float = { layoutSize, itemSize ->
-        layoutSize / 2f - itemSize / 2f
-    }
-) = object : SnapLayoutInfoProvider {
+    positionInLayout: Density.(layoutSize: Float, itemSize: Float) -> Float =
+        { layoutSize, itemSize -> (layoutSize / 2f - itemSize / 2f) }
+): SnapLayoutInfoProvider = object : SnapLayoutInfoProvider {
 
     private val layoutInfo: LazyListLayoutInfo
         get() = lazyListState.layoutInfo
 
     // Single page snapping is the default
-    override fun calculateApproachOffset(initialVelocity: Float): Float = 0f
+    override fun Density.calculateApproachOffset(initialVelocity: Float): Float = 0f
 
-    override fun calculateSnappingOffsetBounds(): ClosedFloatingPointRange<Float> {
+    override fun Density.calculateSnappingOffsetBounds(): ClosedFloatingPointRange<Float> {
         var lowerBoundOffset = Float.NEGATIVE_INFINITY
         var upperBoundOffset = Float.POSITIVE_INFINITY
 
@@ -70,20 +70,19 @@
         return lowerBoundOffset.rangeTo(upperBoundOffset)
     }
 
-    override val snapStepSize: Float
-        get() = with(layoutInfo) {
-            if (visibleItemsInfo.isNotEmpty()) {
-                visibleItemsInfo.fastSumBy { it.size } / visibleItemsInfo.size.toFloat()
-            } else {
-                0f
-            }
+    override fun Density.snapStepSize(): Float = with(layoutInfo) {
+        if (visibleItemsInfo.isNotEmpty()) {
+            visibleItemsInfo.fastSumBy { it.size } / visibleItemsInfo.size.toFloat()
+        } else {
+            0f
         }
+    }
 }
 
-internal fun calculateDistanceToDesiredSnapPosition(
+internal fun Density.calculateDistanceToDesiredSnapPosition(
     layoutInfo: LazyListLayoutInfo,
     item: LazyListItemInfo,
-    positionInLayout: (layoutSize: Float, itemSize: Float) -> Float
+    positionInLayout: Density.(layoutSize: Float, itemSize: Float) -> Float
 ): Float {
     val containerSize =
         with(layoutInfo) { singleAxisViewportSize - beforeContentPadding - afterContentPadding }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehavior.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehavior.kt
index ffae738..3bdcfcb 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehavior.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehavior.kt
@@ -73,14 +73,15 @@
  *
  */
 @ExperimentalFoundationApi
-fun snapFlingBehavior(
-    snapLayoutInfoProvider: SnapLayoutInfoProvider,
-    approachAnimationSpec: DecayAnimationSpec<Float>,
-    snapAnimationSpec: AnimationSpec<Float>,
-    density: Density,
-    shortSnapVelocityThreshold: Dp = MinFlingVelocityDp
-) = object : FlingBehavior {
-    val velocityThreshold = with(density) { shortSnapVelocityThreshold.toPx() }
+class SnapFlingBehavior(
+    private val snapLayoutInfoProvider: SnapLayoutInfoProvider,
+    private val approachAnimationSpec: DecayAnimationSpec<Float>,
+    private val snapAnimationSpec: AnimationSpec<Float>,
+    private val density: Density,
+    private val shortSnapVelocityThreshold: Dp = MinFlingVelocityDp
+) : FlingBehavior {
+
+    private val velocityThreshold = with(density) { shortSnapVelocityThreshold.toPx() }
 
     override suspend fun ScrollScope.performFling(initialVelocity: Float): Float {
         // If snapping from scroll (short snap) or fling (long snap)
@@ -93,15 +94,16 @@
     }
 
     private suspend fun ScrollScope.shortSnap(velocity: Float) {
-        val closestOffset = findClosestOffset(0f, snapLayoutInfoProvider)
+        val closestOffset = findClosestOffset(0f, snapLayoutInfoProvider, density)
         val animationState = AnimationState(NoDistance, velocity)
         animateSnap(closestOffset, closestOffset, animationState, snapAnimationSpec)
     }
 
     private suspend fun ScrollScope.longSnap(initialVelocity: Float) {
-        val initialOffset = snapLayoutInfoProvider.calculateApproachOffset(initialVelocity).let {
-            abs(it) * sign(initialVelocity) // ensure offset sign is correct
-        }
+        val initialOffset =
+            with(snapLayoutInfoProvider) { density.calculateApproachOffset(initialVelocity) }.let {
+                abs(it) * sign(initialVelocity) // ensure offset sign is correct
+            }
 
         val (remainingOffset, animationState) = runApproach(initialOffset, initialVelocity)
 
@@ -115,12 +117,18 @@
 
         val animation =
             if (isDecayApproachPossible(offset = initialTargetOffset, velocity = initialVelocity)) {
-                DecayApproachAnimation(approachAnimationSpec, snapLayoutInfoProvider)
+                DecayApproachAnimation(approachAnimationSpec, snapLayoutInfoProvider, density)
             } else {
-                SnapApproachAnimation(snapAnimationSpec, snapLayoutInfoProvider)
+                SnapApproachAnimation(snapAnimationSpec, snapLayoutInfoProvider, density)
             }
 
-        return approach(initialTargetOffset, initialVelocity, animation, snapLayoutInfoProvider)
+        return approach(
+            initialTargetOffset,
+            initialVelocity,
+            animation,
+            snapLayoutInfoProvider,
+            density
+        )
     }
 
     /**
@@ -131,9 +139,28 @@
         velocity: Float
     ): Boolean {
         val decayOffset = approachAnimationSpec.calculateTargetValue(NoDistance, velocity)
-        val stepSize = snapLayoutInfoProvider.snapStepSize
+        val stepSize = with(snapLayoutInfoProvider) { density.snapStepSize() }
         return abs(decayOffset) > stepSize && abs(decayOffset) - stepSize > abs(offset)
     }
+
+    override fun equals(other: Any?): Boolean {
+        return if (other is SnapFlingBehavior) {
+            other.snapAnimationSpec == this.snapAnimationSpec &&
+                other.approachAnimationSpec == this.approachAnimationSpec &&
+                other.snapLayoutInfoProvider == this.snapLayoutInfoProvider &&
+                other.density == this.density &&
+                other.shortSnapVelocityThreshold == this.shortSnapVelocityThreshold
+        } else {
+            false
+        }
+    }
+
+    override fun hashCode(): Int = 0
+        .let { 31 * it + snapAnimationSpec.hashCode() }
+        .let { 31 * it + approachAnimationSpec.hashCode() }
+        .let { 31 * it + snapLayoutInfoProvider.hashCode() }
+        .let { 31 * it + density.hashCode() }
+        .let { 31 * it + shortSnapVelocityThreshold.hashCode() }
 }
 
 /**
@@ -148,20 +175,18 @@
     snapLayoutInfoProvider: SnapLayoutInfoProvider,
     approachAnimationSpec: DecayAnimationSpec<Float> = rememberSplineBasedDecay(),
     snapAnimationSpec: AnimationSpec<Float> = spring(stiffness = Spring.StiffnessMediumLow)
-): FlingBehavior {
-
-    val currentDensity = LocalDensity.current
+): SnapFlingBehavior {
+    val density = LocalDensity.current
     return remember(
         snapLayoutInfoProvider,
-        currentDensity,
         approachAnimationSpec,
         snapAnimationSpec
     ) {
-        snapFlingBehavior(
+        SnapFlingBehavior(
             snapLayoutInfoProvider,
             approachAnimationSpec,
             snapAnimationSpec,
-            currentDensity
+            density
         )
     }
 }
@@ -183,16 +208,17 @@
     initialTargetOffset: Float,
     initialVelocity: Float,
     animation: ApproachAnimation<Float, AnimationVector1D>,
-    snapLayoutInfoProvider: SnapLayoutInfoProvider
+    snapLayoutInfoProvider: SnapLayoutInfoProvider,
+    density: Density
 ): ApproachStepResult {
 
     var currentAnimationState =
         animation.approachAnimation(this, initialTargetOffset, initialVelocity)
 
     var remainingOffset =
-        findClosestOffset(currentAnimationState.velocity, snapLayoutInfoProvider)
+        findClosestOffset(currentAnimationState.velocity, snapLayoutInfoProvider, density)
 
-    val currentHalfStep = snapLayoutInfoProvider.halfStep
+    val currentHalfStep = snapLayoutInfoProvider.halfStep(density)
     if (abs(remainingOffset) > currentHalfStep) {
         currentAnimationState =
             animation.halfStepAnimation(this, remainingOffset, currentAnimationState)
@@ -221,13 +247,16 @@
 @OptIn(ExperimentalFoundationApi::class)
 internal fun findClosestOffset(
     velocity: Float,
-    snapLayoutInfoProvider: SnapLayoutInfoProvider
+    snapLayoutInfoProvider: SnapLayoutInfoProvider,
+    density: Density
 ): Float {
 
     fun Float.isValidDistance(): Boolean {
         return this != Float.POSITIVE_INFINITY && this != Float.NEGATIVE_INFINITY
     }
-    val (lowerBound, upperBound) = snapLayoutInfoProvider.calculateSnappingOffsetBounds()
+    val (lowerBound, upperBound) = with(snapLayoutInfoProvider) {
+        density.calculateSnappingOffsetBounds()
+    }
 
     val finalDistance = when (sign(velocity)) {
         0f -> {
@@ -324,8 +353,9 @@
 }
 
 @OptIn(ExperimentalFoundationApi::class)
-private val SnapLayoutInfoProvider.halfStep
-    get() = snapStepSize / 2f
+private fun SnapLayoutInfoProvider.halfStep(density: Density): Float {
+    return density.snapStepSize() / 2f
+}
 
 /**
  * The animations used to approach offset and approach half a step offset.
@@ -347,7 +377,8 @@
 @OptIn(ExperimentalFoundationApi::class)
 private class SnapApproachAnimation(
     private val snapAnimationSpec: AnimationSpec<Float>,
-    private val snapLayoutInfoProvider: SnapLayoutInfoProvider
+    private val layoutInfoProvider: SnapLayoutInfoProvider,
+    private val density: Density
 ) : ApproachAnimation<Float, AnimationVector1D> {
     override suspend fun approachAnimation(
         scope: ScrollScope,
@@ -355,9 +386,11 @@
         velocity: Float
     ): AnimationState<Float, AnimationVector1D> {
         val animationState = AnimationState(initialValue = 0f, initialVelocity = velocity)
+        val targetOffset =
+            (abs(offset) + with(layoutInfoProvider) { density.snapStepSize() }) * sign(velocity)
         return with(scope) {
             animateSnap(
-                targetOffset = (abs(offset) + snapLayoutInfoProvider.snapStepSize) * sign(velocity),
+                targetOffset = targetOffset,
                 cancelOffset = offset,
                 animationState = animationState,
                 snapAnimationSpec = snapAnimationSpec,
@@ -375,7 +408,7 @@
         return with(scope) {
             animateSnap(
                 targetOffset = offset,
-                cancelOffset = snapLayoutInfoProvider.halfStep * sign(animationState.velocity),
+                cancelOffset = layoutInfoProvider.halfStep(density) * sign(animationState.velocity),
                 animationState = animationState,
                 snapAnimationSpec = snapAnimationSpec
             )
@@ -386,7 +419,8 @@
 @OptIn(ExperimentalFoundationApi::class)
 private class DecayApproachAnimation(
     private val decayAnimationSpec: DecayAnimationSpec<Float>,
-    private val snapLayoutInfoProvider: SnapLayoutInfoProvider
+    private val snapLayoutInfoProvider: SnapLayoutInfoProvider,
+    private val density: Density
 ) : ApproachAnimation<Float, AnimationVector1D> {
     override suspend fun approachAnimation(
         scope: ScrollScope,
@@ -407,7 +441,7 @@
         val animationState = previousAnimationState.copy(value = NoDistance)
         return with(scope) {
             animateDecay(
-                snapLayoutInfoProvider.halfStep * sign(animationState.velocity),
+                snapLayoutInfoProvider.halfStep(density) * sign(animationState.velocity),
                 animationState,
                 decayAnimationSpec
             )
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapLayoutInfoProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapLayoutInfoProvider.kt
index d790179..9b6ca10 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapLayoutInfoProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/SnapLayoutInfoProvider.kt
@@ -17,6 +17,7 @@
 package androidx.compose.foundation.gestures.snapping
 
 import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.ui.unit.Density
 
 /**
  * Provides information about the layout that is using a SnapFlingBehavior.
@@ -30,7 +31,7 @@
     /**
      * The minimum offset that snapping will use to animate. (e.g. an item size)
      */
-    val snapStepSize: Float
+    fun Density.snapStepSize(): Float
 
     /**
      * Calculate the distance to navigate before settling into the next snapping bound.
@@ -38,7 +39,7 @@
      * @param initialVelocity The current fling movement velocity. You can use this tho calculate a
      * velocity based offset.
      */
-    fun calculateApproachOffset(initialVelocity: Float): Float
+    fun Density.calculateApproachOffset(initialVelocity: Float): Float
 
     /**
      * Given a target placement in a layout, the snapping bounds should be the closest offset we
@@ -48,5 +49,5 @@
      *
      * Bounds are *always* a negative (lower bound) and a positive (upper bound) value.
      */
-    fun calculateSnappingOffsetBounds(): ClosedFloatingPointRange<Float>
+    fun Density.calculateSnappingOffsetBounds(): ClosedFloatingPointRange<Float>
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyDsl.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyDsl.kt
index 37dc71a..606aafa 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyDsl.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyDsl.kt
@@ -21,14 +21,14 @@
 import androidx.compose.foundation.gestures.ScrollableDefaults
 import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider
 import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
+import androidx.compose.foundation.internal.JvmDefaultWithCompatibility
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
-import androidx.compose.foundation.internal.JvmDefaultWithCompatibility
-import androidx.compose.runtime.remember
 
 /**
  * Receiver scope which is used by [LazyColumn] and [LazyRow].
@@ -440,8 +440,7 @@
     @ExperimentalFoundationApi
     @Composable
     fun snapFlingBehavior(lazyListState: LazyListState): FlingBehavior {
-        val snappingLayout =
-            remember(lazyListState) { SnapLayoutInfoProvider(lazyListState) }
+        val snappingLayout = remember(lazyListState) { SnapLayoutInfoProvider(lazyListState) }
         return rememberSnapFlingBehavior(snappingLayout)
     }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGrid.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGrid.kt
index eb8ebd4..67a8e48 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGrid.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGrid.kt
@@ -38,24 +38,24 @@
 internal fun LazyStaggeredGrid(
     /** State controlling the scroll position */
     state: LazyStaggeredGridState,
+    /** The layout orientation of the grid */
+    orientation: Orientation,
+    /** Prefix sums of cross axis sizes of slots per line, e.g. the columns for vertical grid. */
+    slotSizesSums: Density.(Constraints) -> IntArray,
     /** Modifier to be applied for the inner layout */
     modifier: Modifier = Modifier,
     /** The inner padding to be added for the whole content (not for each individual item) */
     contentPadding: PaddingValues = PaddingValues(0.dp),
     /** reverse the direction of scrolling and layout */
     reverseLayout: Boolean = false,
-    /** The layout orientation of the grid */
-    orientation: Orientation,
     /** fling behavior to be used for flinging */
     flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
     /** Whether scrolling via the user gestures is allowed. */
-    userScrollEnabled: Boolean,
+    userScrollEnabled: Boolean = true,
     /** The vertical arrangement for items/lines. */
-    verticalArrangement: Arrangement.Vertical,
+    verticalArrangement: Arrangement.Vertical = Arrangement.Top,
     /** The horizontal arrangement for items/lines. */
-    horizontalArrangement: Arrangement.Horizontal,
-    /** Prefix sums of cross axis sizes of slots per line, e.g. the columns for vertical grid. */
-    slotSizesSums: Density.(Constraints) -> IntArray,
+    horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
     /** The content of the grid */
     content: LazyStaggeredGridScope.() -> Unit
 ) {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt
index 617cbbe..513e3be 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt
@@ -17,406 +17,478 @@
 package androidx.compose.foundation.lazy.staggeredgrid
 
 import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.OverscrollEffect
-import androidx.compose.foundation.checkScrollableContainerConstraints
 import androidx.compose.foundation.fastFold
 import androidx.compose.foundation.fastMaxOfOrNull
-import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider
 import androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
+import androidx.compose.runtime.collection.MutableVector
 import androidx.compose.runtime.snapshots.Snapshot
 import androidx.compose.ui.layout.Placeable
 import androidx.compose.ui.unit.Constraints
-import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.constrainHeight
 import androidx.compose.ui.unit.constrainWidth
 import androidx.compose.ui.util.fastForEach
 import kotlin.math.abs
+import kotlin.math.min
 import kotlin.math.roundToInt
 import kotlin.math.sign
 
-@Composable
 @ExperimentalFoundationApi
-internal fun rememberStaggeredGridMeasurePolicy(
+internal fun LazyLayoutMeasureScope.measure(
     state: LazyStaggeredGridState,
     itemProvider: LazyLayoutItemProvider,
-    contentPadding: PaddingValues,
-    reverseLayout: Boolean,
-    orientation: Orientation,
-    verticalArrangement: Arrangement.Vertical,
-    horizontalArrangement: Arrangement.Horizontal,
-    slotSizesSums: Density.(Constraints) -> IntArray,
-    overscrollEffect: OverscrollEffect
-): LazyLayoutMeasureScope.(Constraints) -> LazyStaggeredGridMeasureResult = remember(
-    state,
-    itemProvider,
-    contentPadding,
-    reverseLayout,
-    orientation,
-    verticalArrangement,
-    horizontalArrangement,
-    slotSizesSums,
-    overscrollEffect,
-) {
-    { constraints ->
-        checkScrollableContainerConstraints(
-            constraints,
-            orientation
-        )
-        val isVertical = orientation == Orientation.Vertical
+    resolvedSlotSums: IntArray,
+    constraints: Constraints,
+    isVertical: Boolean,
+    beforeContentPadding: Int,
+    afterContentPadding: Int,
+): LazyStaggeredGridMeasureResult {
+    val initialItemIndices: IntArray
+    val initialItemOffsets: IntArray
 
-        val resolvedSlotSums = slotSizesSums(this, constraints)
+    Snapshot.withoutReadObservation {
+        initialItemIndices =
+            if (state.firstVisibleItems.size == resolvedSlotSums.size) {
+                state.firstVisibleItems
+            } else {
+                IntArray(resolvedSlotSums.size) { -1 }
+            }
+        initialItemOffsets =
+            if (state.firstVisibleItemScrollOffsets.size == resolvedSlotSums.size) {
+                state.firstVisibleItemScrollOffsets
+            } else {
+                IntArray(resolvedSlotSums.size) { 0 }
+            }
+    }
+
+    val context = LazyStaggeredGridMeasureContext(
+        state = state,
+        itemProvider = itemProvider,
+        resolvedSlotSums = resolvedSlotSums,
+        constraints = constraints,
+        isVertical = isVertical,
+        beforeContentPadding = beforeContentPadding,
+        afterContentPadding = afterContentPadding,
+        measureScope = this
+    )
+
+    return context.measure(
+        initialScrollDelta = state.scrollToBeConsumed.roundToInt(),
+        initialItemIndices = initialItemIndices,
+        initialItemOffsets = initialItemOffsets,
+        canRestartMeasure = true,
+    )
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+private class LazyStaggeredGridMeasureContext(
+    val state: LazyStaggeredGridState,
+    val itemProvider: LazyLayoutItemProvider,
+    val resolvedSlotSums: IntArray,
+    val constraints: Constraints,
+    val isVertical: Boolean,
+    val measureScope: LazyLayoutMeasureScope,
+    val beforeContentPadding: Int,
+    val afterContentPadding: Int,
+) {
+    val measuredItemProvider = LazyStaggeredGridMeasureProvider(
+        isVertical,
+        itemProvider,
+        measureScope,
+        resolvedSlotSums
+    ) { index, key, placeables ->
+        LazyStaggeredGridMeasuredItem(
+            index,
+            key,
+            placeables,
+            isVertical
+        )
+    }
+
+    val spans = state.spans
+}
+
+@ExperimentalFoundationApi
+private fun LazyStaggeredGridMeasureContext.measure(
+    initialScrollDelta: Int,
+    initialItemIndices: IntArray,
+    initialItemOffsets: IntArray,
+    canRestartMeasure: Boolean,
+): LazyStaggeredGridMeasureResult {
+    with(measureScope) {
         val itemCount = itemProvider.itemCount
 
-        val mainAxisAvailableSize =
-            if (isVertical) constraints.maxHeight else constraints.maxWidth
-
-        val measuredItemProvider = LazyStaggeredGridMeasureProvider(
-            isVertical,
-            itemProvider,
-            this,
-            resolvedSlotSums
-        ) { index, key, placeables ->
-            LazyStaggeredGridMeasuredItem(
-                index,
-                key,
-                placeables,
-                isVertical
-            )
-        }
-
-        val beforeContentPadding = 0
-        val afterContentPadding = 0
-
-        val initialItemIndices: IntArray
-        val initialItemOffsets: IntArray
-
-        Snapshot.withoutReadObservation {
-            initialItemIndices =
-                if (state.firstVisibleItems.size == resolvedSlotSums.size) {
-                    state.firstVisibleItems
-                } else {
-                    IntArray(resolvedSlotSums.size) { -1 }
-                }
-            initialItemOffsets =
-                if (state.firstVisibleItemScrollOffsets.size == resolvedSlotSums.size) {
-                    state.firstVisibleItemScrollOffsets
-                } else {
-                    IntArray(resolvedSlotSums.size) { 0 }
-                }
-        }
-
-        val spans = state.spans
-        val firstItemIndices = initialItemIndices.copyOf()
-        val firstItemOffsets = initialItemOffsets.copyOf()
-
-        // Measure items
-
-        if (itemCount <= 0) {
-            LazyStaggeredGridMeasureResult(
+        if (itemCount <= 0 || resolvedSlotSums.isEmpty()) {
+            return LazyStaggeredGridMeasureResult(
                 firstVisibleItemIndices = IntArray(0),
                 firstVisibleItemScrollOffsets = IntArray(0),
                 consumedScroll = 0f,
                 measureResult = layout(constraints.minWidth, constraints.minHeight) {},
                 canScrollForward = false,
                 canScrollBackward = false,
-                visibleItemsInfo = emptyArray()
+                visibleItemsInfo = emptyList(),
+                totalItemsCount = 0
             )
-        } else {
-            // todo(b/182882362): content padding
+        }
 
-            // represents the real amount of scroll we applied as a result of this measure pass.
-            var scrollDelta = state.scrollToBeConsumed.roundToInt()
+        // todo(b/182882362): content padding
 
-            // applying the whole requested scroll offset. we will figure out if we can't consume
-            // all of it later
-            firstItemOffsets.offsetBy(-scrollDelta)
+        val mainAxisAvailableSize =
+            if (isVertical) constraints.maxHeight else constraints.maxWidth
 
-            // if the current scroll offset is less than minimally possible
-            if (firstItemIndices[0] == 0 && firstItemOffsets[0] < 0) {
-                scrollDelta += firstItemOffsets[0]
-                firstItemOffsets.fill(0)
-            }
+        // represents the real amount of scroll we applied as a result of this measure pass.
+        var scrollDelta = initialScrollDelta
 
-            // this will contain all the MeasuredItems representing the visible items
-            val measuredItems = Array(resolvedSlotSums.size) {
-                mutableListOf<LazyStaggeredGridMeasuredItem>()
-            }
+        val firstItemIndices = initialItemIndices.copyOf()
+        val firstItemOffsets = initialItemOffsets.copyOf()
 
-            // include the start padding so we compose items in the padding area. before starting
-            // scrolling forward we would remove it back
-            firstItemOffsets.offsetBy(-beforeContentPadding)
-
-            // define min and max offsets (min offset currently includes beforeContentPadding)
-            val minOffset = -beforeContentPadding
-            val maxOffset = mainAxisAvailableSize
-
-            fun hasSpaceOnTop(): Boolean {
-                for (column in firstItemIndices.indices) {
-                    val itemIndex = firstItemIndices[column]
-                    val itemOffset = firstItemOffsets[column]
-
-                    if (itemOffset <= 0 && itemIndex > 0) {
-                        return true
-                    }
-                }
-
-                return false
-            }
-
-            // we had scrolled backward or we compose items in the start padding area, which means
-            // items before current firstItemScrollOffset should be visible. compose them and update
-            // firstItemScrollOffset
-            while (hasSpaceOnTop()) {
-                val columnIndex = firstItemOffsets.indexOfMinValue()
-                val previousItemIndex = spans.findPreviousItemIndex(
-                    item = firstItemIndices[columnIndex],
-                    column = columnIndex
-                )
-
-                if (previousItemIndex < 0) {
-                    break
-                }
-
-                if (spans.getSpan(previousItemIndex) == SpanLookup.SpanUnset) {
-                    spans.setSpan(previousItemIndex, columnIndex)
-                }
-
-                val measuredItem = measuredItemProvider.getAndMeasure(
-                    previousItemIndex,
-                    columnIndex
-                )
-                measuredItems[columnIndex].add(0, measuredItem)
-
-                firstItemIndices[columnIndex] = previousItemIndex
-                firstItemOffsets[columnIndex] += measuredItem.sizeWithSpacings
-            }
-
-            // if we were scrolled backward, but there were not enough items before. this means
-            // not the whole scroll was consumed
-            if (firstItemOffsets[0] < minOffset) {
-                scrollDelta += firstItemOffsets[0]
-                firstItemOffsets.offsetBy(minOffset - firstItemOffsets[0])
-            }
-
-            val currentItemIndices = initialItemIndices.copyOf()
-            val currentItemOffsets = IntArray(initialItemOffsets.size) {
-                -(initialItemOffsets[it] - scrollDelta)
-            }
-
-            // neutralize previously added start padding as we stopped filling the before content padding
-            firstItemOffsets.offsetBy(beforeContentPadding)
-
-            val maxMainAxis = (maxOffset + afterContentPadding).coerceAtLeast(0)
-
-            // compose first visible items we received from state
-            currentItemIndices.forEachIndexed { columnIndex, itemIndex ->
-                if (itemIndex == -1) return@forEachIndexed
-
-                val measuredItem = measuredItemProvider.getAndMeasure(itemIndex, columnIndex)
-                currentItemOffsets[columnIndex] += measuredItem.sizeWithSpacings
-
-                if (
-                    currentItemOffsets[columnIndex] <= minOffset &&
-                        measuredItem.index != itemCount - 1
-                ) {
-                    // this item is offscreen and will not be placed. advance item index
-                    firstItemIndices[columnIndex] = -1
-                    firstItemOffsets[columnIndex] -= measuredItem.sizeWithSpacings
-                } else {
-                    measuredItems[columnIndex].add(measuredItem)
-                }
-            }
-
-            // then composing visible items forward until we fill the whole viewport.
-            // we want to have at least one item in visibleItems even if in fact all the items are
-            // offscreen, this can happen if the content padding is larger than the available size.
-            while (
-                currentItemOffsets.any { it <= maxMainAxis } ||
-                    measuredItems.all { it.isEmpty() }
-            ) {
-                val columnIndex = currentItemOffsets.indexOfMinValue()
-                val nextItemIndex = spans.findNextItemIndex(
-                    currentItemIndices[columnIndex],
-                    columnIndex
-                )
-
-                if (nextItemIndex == itemCount) {
-                    break
-                }
-
-                if (firstItemIndices[columnIndex] == -1) {
-                    firstItemIndices[columnIndex] = nextItemIndex
-                }
-                spans.setSpan(nextItemIndex, columnIndex)
-
-                val measuredItem = measuredItemProvider.getAndMeasure(nextItemIndex, columnIndex)
-                currentItemOffsets[columnIndex] += measuredItem.sizeWithSpacings
-
-                if (
-                    currentItemOffsets[columnIndex] <= minOffset &&
-                        measuredItem.index != itemCount - 1
-                ) {
-                    // this item is offscreen and will not be placed. advance item index
-                    firstItemIndices[columnIndex] = -1
-                    firstItemOffsets[columnIndex] -= measuredItem.sizeWithSpacings
-                } else {
-                    measuredItems[columnIndex].add(measuredItem)
-                }
-
-                currentItemIndices[columnIndex] = nextItemIndex
-            }
-
-            // we didn't fill the whole viewport with items starting from firstVisibleItemIndex.
-            // lets try to scroll back if we have enough items before firstVisibleItemIndex.
-            if (currentItemOffsets.all { it < maxOffset }) {
-                val maxOffsetColumn = currentItemOffsets.indexOfMaxValue()
-                val toScrollBack = maxOffset - currentItemOffsets[maxOffsetColumn]
-                firstItemOffsets.offsetBy(-toScrollBack)
-                currentItemOffsets.offsetBy(toScrollBack)
-                while (
-                    firstItemOffsets.any { it < beforeContentPadding } &&
-                        firstItemIndices.all { it != 0 }
-                ) {
-                    val columnIndex = firstItemOffsets.indexOfMinValue()
-                    val currentIndex =
-                        if (firstItemIndices[columnIndex] == -1) {
-                            itemCount
-                        } else {
-                            firstItemIndices[columnIndex]
-                        }
-
-                    val previousIndex =
-                        spans.findPreviousItemIndex(currentIndex, columnIndex)
-
-                    if (previousIndex < 0) {
-                        break
-                    }
-
-                    val measuredItem = measuredItemProvider.getAndMeasure(
-                        previousIndex,
-                        columnIndex
-                    )
-                    measuredItems[columnIndex].add(0, measuredItem)
-                    firstItemOffsets[columnIndex] += measuredItem.sizeWithSpacings
-                    firstItemIndices[columnIndex] = previousIndex
-                }
-                scrollDelta += toScrollBack
-
-                val minOffsetColumn = firstItemOffsets.indexOfMinValue()
-                if (firstItemOffsets[minOffsetColumn] < 0) {
-                    val offsetValue = firstItemOffsets[minOffsetColumn]
-                    scrollDelta += offsetValue
-                    currentItemOffsets.offsetBy(offsetValue)
-                    firstItemOffsets.offsetBy(-offsetValue)
-                }
-            }
-
-            // report the amount of pixels we consumed. scrollDelta can be smaller than
-            // scrollToBeConsumed if there were not enough items to fill the offered space or it
-            // can be larger if items were resized, or if, for example, we were previously
-            // displaying the item 15, but now we have only 10 items in total in the data set.
-            val consumedScroll = if (
-                state.scrollToBeConsumed.roundToInt().sign == scrollDelta.sign &&
-                    abs(state.scrollToBeConsumed.roundToInt()) >= abs(scrollDelta)
-            ) {
-                scrollDelta.toFloat()
-            } else {
-                state.scrollToBeConsumed
-            }
-
-            // todo(b/182882362):
-            // even if we compose items to fill before content padding we should ignore items fully
-            // located there for the state's scroll position calculation (first item + first offset)
-
-            // end measure
-
-            val layoutWidth = if (isVertical) {
-                constraints.maxWidth
-            } else {
-                constraints.constrainWidth(currentItemOffsets.max())
-            }
-            val layoutHeight = if (isVertical) {
-                constraints.constrainHeight(currentItemOffsets.max())
-            } else {
-                constraints.maxHeight
-            }
-
-            // Placement
-
-            val itemScrollOffsets = firstItemOffsets.map { -it }
-            val positionedItems = Array(measuredItems.size) {
-                mutableListOf<LazyStaggeredGridPositionedItem>()
-            }
-
-            var currentCrossAxis = 0
-            measuredItems.forEachIndexed { i, columnItems ->
-                var currentMainAxis = itemScrollOffsets[i]
-
-                // todo(b/182882362): arrangement/spacing support
-
-                columnItems.fastForEach { item ->
-                    positionedItems[i] += item.position(
-                        currentMainAxis,
-                        currentCrossAxis,
-                    )
-                    currentMainAxis += item.sizeWithSpacings
-                }
-                if (columnItems.isNotEmpty()) {
-                    currentCrossAxis += columnItems[0].crossAxisSize
-                }
-            }
-
-            // End placement
-
-            // todo: reverse layout support
-            // only scroll backward if the first item is not on screen or fully visible
-            val canScrollBackward = !(firstItemIndices[0] == 0 && firstItemOffsets[0] <= 0)
-            // only scroll forward if the last item is not on screen or fully visible
-            val canScrollForward = currentItemIndices.indexOf(itemCount - 1).let { columnIndex ->
-                if (columnIndex == -1) {
-                    true
-                } else {
-                    (currentItemOffsets[columnIndex] -
-                        measuredItems[columnIndex].last().sizeWithSpacings) < mainAxisAvailableSize
-                }
-            }
-
-            @Suppress("UNCHECKED_CAST")
-            LazyStaggeredGridMeasureResult(
-                firstVisibleItemIndices = firstItemIndices,
-                firstVisibleItemScrollOffsets = firstItemOffsets,
-                consumedScroll = consumedScroll,
-                measureResult = layout(layoutWidth, layoutHeight) {
-                    positionedItems.forEach {
-                        it.fastForEach { item ->
-                            item.place(this)
-                        }
-                    }
-                },
-                canScrollForward = canScrollForward,
-                canScrollBackward = canScrollBackward,
-                visibleItemsInfo = positionedItems as Array<List<LazyStaggeredGridItemInfo>>
-            ).also {
-                state.applyMeasureResult(it)
-                refreshOverscrollInfo(overscrollEffect, it)
+        // record first span assignments for first items in case they haven't been recorded
+        // before
+        firstItemIndices.forEachIndexed { laneIndex, itemIndex ->
+            if (itemIndex != -1) {
+                spans.setSpan(itemIndex, laneIndex)
             }
         }
-    }
-}
 
-@OptIn(ExperimentalFoundationApi::class)
-private fun refreshOverscrollInfo(
-    overscrollEffect: OverscrollEffect,
-    result: LazyStaggeredGridMeasureResult
-) {
-    overscrollEffect.isEnabled = result.canScrollForward || result.canScrollBackward
+        // update spans in case item count is lower than before
+        ensureIndicesInRange(firstItemIndices, itemCount)
+
+        // applying the whole requested scroll offset. we will figure out if we can't consume
+        // all of it later
+        firstItemOffsets.offsetBy(-scrollDelta)
+
+        // if the current scroll offset is less than minimally possible
+        if (firstItemIndices[0] == 0 && firstItemOffsets[0] < 0) {
+            scrollDelta += firstItemOffsets[0]
+            firstItemOffsets.fill(0)
+        }
+
+        // this will contain all the MeasuredItems representing the visible items
+        val measuredItems = Array(resolvedSlotSums.size) {
+            ArrayDeque<LazyStaggeredGridMeasuredItem>()
+        }
+
+        // include the start padding so we compose items in the padding area. before starting
+        // scrolling forward we would remove it back
+        firstItemOffsets.offsetBy(-beforeContentPadding)
+
+        // define min and max offsets (min offset currently includes beforeContentPadding)
+        val minOffset = -beforeContentPadding
+        val maxOffset = mainAxisAvailableSize
+
+        fun hasSpaceOnTop(): Boolean {
+            for (lane in firstItemIndices.indices) {
+                val itemIndex = firstItemIndices[lane]
+                val itemOffset = firstItemOffsets[lane]
+
+                if (itemOffset <= 0 && itemIndex > 0) {
+                    return true
+                }
+            }
+
+            return false
+        }
+
+        fun misalignedTop(laneIndex: Int): Boolean {
+            // If we scrolled past the first item in the lane, we have a point of reference
+            // to re-align items.
+            // Case 1: Item offsets for first item are not aligned
+            val misalignedOffsets = firstItemOffsets.any {
+                it != firstItemOffsets[laneIndex]
+            }
+            // Case 2: Other lanes have more items than the current one
+            val moreItemsInOtherLanes = firstItemIndices.indices.any { lane ->
+                findPreviousItemIndex(firstItemIndices[lane], lane) != -1
+            }
+            // Case 3: the first item is in the wrong lane (it should always be in
+            // the first one)
+            val firstItemInWrongLane = spans.getSpan(0) != 0
+            // If items are not aligned, reset all measurement data we gathered before and
+            // proceed with initial measure
+            return misalignedOffsets || moreItemsInOtherLanes || firstItemInWrongLane
+        }
+
+        // we had scrolled backward or we compose items in the start padding area, which means
+        // items before current firstItemScrollOffset should be visible. compose them and update
+        // firstItemScrollOffset
+        while (hasSpaceOnTop()) {
+            val laneIndex = firstItemOffsets.indexOfMinValue()
+            val previousItemIndex = findPreviousItemIndex(
+                item = firstItemIndices[laneIndex],
+                lane = laneIndex
+            )
+
+            if (previousItemIndex < 0) {
+                if (misalignedTop(laneIndex) && canRestartMeasure) {
+                    spans.reset()
+                    return measure(
+                        initialScrollDelta = scrollDelta,
+                        initialItemIndices = IntArray(firstItemIndices.size) { -1 },
+                        initialItemOffsets = IntArray(firstItemOffsets.size) {
+                            initialItemOffsets[laneIndex]
+                        },
+                        canRestartMeasure = false
+                    )
+                }
+                break
+            }
+
+            if (spans.getSpan(previousItemIndex) == LazyStaggeredGridSpans.Unset) {
+                spans.setSpan(previousItemIndex, laneIndex)
+            }
+
+            val measuredItem = measuredItemProvider.getAndMeasure(
+                previousItemIndex,
+                laneIndex
+            )
+            measuredItems[laneIndex].addFirst(measuredItem)
+
+            firstItemIndices[laneIndex] = previousItemIndex
+            firstItemOffsets[laneIndex] += measuredItem.sizeWithSpacings
+        }
+
+        // if we were scrolled backward, but there were not enough items before. this means
+        // not the whole scroll was consumed
+        if (firstItemOffsets[0] < minOffset) {
+            scrollDelta += firstItemOffsets[0]
+            firstItemOffsets.offsetBy(minOffset - firstItemOffsets[0])
+        }
+
+        val currentItemIndices = initialItemIndices.copyOf().apply {
+            // ensure indices match item count, in case it decreased
+            ensureIndicesInRange(this, itemCount)
+        }
+        val currentItemOffsets = IntArray(initialItemOffsets.size) {
+            -(initialItemOffsets[it] - scrollDelta)
+        }
+
+        // neutralize previously added start padding as we stopped filling the before content padding
+        firstItemOffsets.offsetBy(beforeContentPadding)
+
+        val maxMainAxis = (maxOffset + afterContentPadding).coerceAtLeast(0)
+
+        // compose first visible items we received from state
+        currentItemIndices.forEachIndexed { laneIndex, itemIndex ->
+            if (itemIndex < 0) return@forEachIndexed
+
+            val measuredItem = measuredItemProvider.getAndMeasure(itemIndex, laneIndex)
+            currentItemOffsets[laneIndex] += measuredItem.sizeWithSpacings
+
+            spans.setSpan(itemIndex, laneIndex)
+
+            if (
+                currentItemOffsets[laneIndex] <= minOffset &&
+                measuredItem.index != itemCount - 1
+            ) {
+                // this item is offscreen and will not be placed. advance item index
+                firstItemIndices[laneIndex] = -1
+                firstItemOffsets[laneIndex] -= measuredItem.sizeWithSpacings
+            } else {
+                measuredItems[laneIndex].addLast(measuredItem)
+            }
+        }
+
+        // then composing visible items forward until we fill the whole viewport.
+        // we want to have at least one item in visibleItems even if in fact all the items are
+        // offscreen, this can happen if the content padding is larger than the available size.
+        while (
+            currentItemOffsets.any { it <= maxMainAxis } || measuredItems.all { it.isEmpty() }
+        ) {
+            val currentLaneIndex = currentItemOffsets.indexOfMinValue()
+            val nextItemIndex =
+                findNextItemIndex(currentItemIndices[currentLaneIndex], currentLaneIndex)
+
+            if (nextItemIndex >= itemCount) {
+                // if any items changed its size, the spans may not behave correctly
+                // there are no more items in this lane, but there could be more in others
+                // recheck if we can add more items and reset spans accordingly
+                var missedItemIndex = Int.MAX_VALUE
+                currentItemIndices.forEachIndexed { laneIndex, i ->
+                    if (laneIndex == currentLaneIndex) return@forEachIndexed
+                    var itemIndex = findNextItemIndex(i, laneIndex)
+                    while (itemIndex < itemCount) {
+                        missedItemIndex = minOf(itemIndex, missedItemIndex)
+                        spans.setSpan(itemIndex, LazyStaggeredGridSpans.Unset)
+                        itemIndex = findNextItemIndex(itemIndex, laneIndex)
+                    }
+                }
+                // there's at least one missed item which may fit current lane
+                if (missedItemIndex != Int.MAX_VALUE && canRestartMeasure) {
+                    // reset current lane to the missed item index and restart measure
+                    initialItemIndices[currentLaneIndex] =
+                        min(initialItemIndices[currentLaneIndex], missedItemIndex)
+                    return measure(
+                        initialScrollDelta = initialScrollDelta,
+                        initialItemIndices = initialItemIndices,
+                        initialItemOffsets = initialItemOffsets,
+                        canRestartMeasure = false
+                    )
+                } else {
+                    break
+                }
+            }
+
+            if (firstItemIndices[currentLaneIndex] == -1) {
+                firstItemIndices[currentLaneIndex] = nextItemIndex
+            }
+            spans.setSpan(nextItemIndex, currentLaneIndex)
+
+            val measuredItem =
+                measuredItemProvider.getAndMeasure(nextItemIndex, currentLaneIndex)
+            currentItemOffsets[currentLaneIndex] += measuredItem.sizeWithSpacings
+
+            if (
+                currentItemOffsets[currentLaneIndex] <= minOffset &&
+                measuredItem.index != itemCount - 1
+            ) {
+                // this item is offscreen and will not be placed. advance item index
+                firstItemIndices[currentLaneIndex] = -1
+                firstItemOffsets[currentLaneIndex] -= measuredItem.sizeWithSpacings
+            } else {
+                measuredItems[currentLaneIndex].addLast(measuredItem)
+            }
+
+            currentItemIndices[currentLaneIndex] = nextItemIndex
+        }
+
+        // we didn't fill the whole viewport with items starting from firstVisibleItemIndex.
+        // lets try to scroll back if we have enough items before firstVisibleItemIndex.
+        if (currentItemOffsets.all { it < maxOffset }) {
+            val maxOffsetLane = currentItemOffsets.indexOfMaxValue()
+            val toScrollBack = maxOffset - currentItemOffsets[maxOffsetLane]
+            firstItemOffsets.offsetBy(-toScrollBack)
+            currentItemOffsets.offsetBy(toScrollBack)
+            while (
+                firstItemOffsets.any { it < beforeContentPadding }
+            ) {
+                val laneIndex = firstItemOffsets.indexOfMinValue()
+                val currentIndex =
+                    if (firstItemIndices[laneIndex] == -1) {
+                        itemCount
+                    } else {
+                        firstItemIndices[laneIndex]
+                    }
+
+                val previousIndex =
+                    findPreviousItemIndex(currentIndex, laneIndex)
+
+                if (previousIndex < 0) {
+                    if (misalignedTop(laneIndex) && canRestartMeasure) {
+                        spans.reset()
+                        return measure(
+                            initialScrollDelta = scrollDelta,
+                            initialItemIndices = IntArray(firstItemIndices.size) { -1 },
+                            initialItemOffsets = IntArray(firstItemOffsets.size) { 0 },
+                            canRestartMeasure = false
+                        )
+                    }
+                    break
+                }
+
+                spans.setSpan(previousIndex, laneIndex)
+
+                val measuredItem = measuredItemProvider.getAndMeasure(
+                    previousIndex,
+                    laneIndex
+                )
+                measuredItems[laneIndex].addFirst(measuredItem)
+                firstItemOffsets[laneIndex] += measuredItem.sizeWithSpacings
+                firstItemIndices[laneIndex] = previousIndex
+            }
+            scrollDelta += toScrollBack
+
+            val minOffsetLane = firstItemOffsets.indexOfMinValue()
+            if (firstItemOffsets[minOffsetLane] < 0) {
+                val offsetValue = firstItemOffsets[minOffsetLane]
+                scrollDelta += offsetValue
+                currentItemOffsets.offsetBy(offsetValue)
+                firstItemOffsets.offsetBy(-offsetValue)
+            }
+        }
+
+        // report the amount of pixels we consumed. scrollDelta can be smaller than
+        // scrollToBeConsumed if there were not enough items to fill the offered space or it
+        // can be larger if items were resized, or if, for example, we were previously
+        // displaying the item 15, but now we have only 10 items in total in the data set.
+        val consumedScroll = if (
+            state.scrollToBeConsumed.roundToInt().sign == scrollDelta.sign &&
+            abs(state.scrollToBeConsumed.roundToInt()) >= abs(scrollDelta)
+        ) {
+            scrollDelta.toFloat()
+        } else {
+            state.scrollToBeConsumed
+        }
+
+        // todo(b/182882362):
+        // even if we compose items to fill before content padding we should ignore items fully
+        // located there for the state's scroll position calculation (first item + first offset)
+
+        // end measure
+
+        val layoutWidth = if (isVertical) {
+            constraints.maxWidth
+        } else {
+            constraints.constrainWidth(currentItemOffsets.max())
+        }
+        val layoutHeight = if (isVertical) {
+            constraints.constrainHeight(currentItemOffsets.max())
+        } else {
+            constraints.maxHeight
+        }
+
+        // Placement
+
+        val itemScrollOffsets = firstItemOffsets.copyOf().transform { -it }
+        val positionedItems = MutableVector<LazyStaggeredGridPositionedItem>(
+            capacity = measuredItems.sumOf { it.size }
+        )
+        while (measuredItems.any { it.isNotEmpty() }) {
+            // find the next item to position
+            val laneIndex = measuredItems.indexOfMinBy {
+                it.firstOrNull()?.index ?: Int.MAX_VALUE
+            }
+            val item = measuredItems[laneIndex].removeFirst()
+
+            // todo(b/182882362): arrangement/spacing support
+            val mainAxisOffset = itemScrollOffsets[laneIndex]
+            val crossAxisOffset = if (laneIndex == 0) 0 else resolvedSlotSums[laneIndex - 1]
+            positionedItems.add(
+                item.position(
+                    laneIndex,
+                    mainAxisOffset,
+                    crossAxisOffset,
+                )
+            )
+            itemScrollOffsets[laneIndex] += item.sizeWithSpacings
+        }
+
+        // todo: reverse layout support
+
+        // End placement
+
+        // only scroll backward if the first item is not on screen or fully visible
+        val canScrollBackward = !(firstItemIndices[0] == 0 && firstItemOffsets[0] <= 0)
+        // only scroll forward if the last item is not on screen or fully visible
+        val canScrollForward = currentItemOffsets.any { it > maxOffset }
+
+        @Suppress("UNCHECKED_CAST")
+        return LazyStaggeredGridMeasureResult(
+            firstVisibleItemIndices = firstItemIndices,
+            firstVisibleItemScrollOffsets = firstItemOffsets,
+            consumedScroll = consumedScroll,
+            measureResult = layout(layoutWidth, layoutHeight) {
+                positionedItems.forEach { item ->
+                    item.place(this)
+                }
+            },
+            canScrollForward = canScrollForward,
+            canScrollBackward = canScrollBackward,
+            visibleItemsInfo = positionedItems.asMutableList(),
+            totalItemsCount = itemCount
+        )
+    }
 }
 
 private fun IntArray.offsetBy(delta: Int) {
@@ -438,6 +510,20 @@
     return result
 }
 
+private inline fun <T> Array<T>.indexOfMinBy(block: (T) -> Int): Int {
+    var result = -1
+    var min = Int.MAX_VALUE
+    for (i in indices) {
+        val value = block(this[i])
+        if (min > value) {
+            min = value
+            result = i
+        }
+    }
+
+    return result
+}
+
 private fun IntArray.indexOfMaxValue(): Int {
     var result = -1
     var max = Int.MIN_VALUE
@@ -451,26 +537,34 @@
     return result
 }
 
-private fun SpanLookup.findPreviousItemIndex(item: Int, column: Int): Int {
-    for (i in (item - 1) downTo 0) {
-        val span = getSpan(i)
-        if (span == column || span == SpanLookup.SpanUnset) {
-            return i
-        }
+private inline fun IntArray.transform(block: (Int) -> Int): IntArray {
+    for (i in indices) {
+        this[i] = block(this[i])
     }
-    return -1
+    return this
 }
 
-private fun SpanLookup.findNextItemIndex(item: Int, column: Int): Int {
-    for (i in (item + 1) until capacity()) {
-        val span = getSpan(i)
-        if (span == column || span == SpanLookup.SpanUnset) {
-            return i
+private fun LazyStaggeredGridMeasureContext.ensureIndicesInRange(
+    indices: IntArray,
+    itemCount: Int
+) {
+    for (i in indices.indices) {
+        while (indices[i] >= itemCount) {
+            indices[i] = findPreviousItemIndex(indices[i], i)
+        }
+        if (indices[i] != LazyStaggeredGridSpans.Unset) {
+            // reserve item for span
+            spans.setSpan(indices[i], i)
         }
     }
-    return capacity()
 }
 
+private fun LazyStaggeredGridMeasureContext.findPreviousItemIndex(item: Int, lane: Int): Int =
+    spans.findPreviousItemIndex(item, lane)
+
+private fun LazyStaggeredGridMeasureContext.findNextItemIndex(item: Int, lane: Int): Int =
+    spans.findNextItemIndex(item, lane)
+
 @OptIn(ExperimentalFoundationApi::class)
 private class LazyStaggeredGridMeasureProvider(
     private val isVertical: Boolean,
@@ -479,7 +573,7 @@
     private val resolvedSlotSums: IntArray,
     private val measuredItemFactory: MeasuredItemFactory
 ) {
-    fun childConstraints(slot: Int): Constraints {
+    private fun childConstraints(slot: Int): Constraints {
         val previousSum = if (slot == 0) 0 else resolvedSlotSums[slot - 1]
         val crossAxisSize = resolvedSlotSums[slot] - previousSum
         return if (isVertical) {
@@ -496,6 +590,15 @@
     }
 }
 
+// This interface allows to avoid autoboxing on index param
+private fun interface MeasuredItemFactory {
+    fun createItem(
+        index: Int,
+        key: Any,
+        placeables: List<Placeable>
+    ): LazyStaggeredGridMeasuredItem
+}
+
 private class LazyStaggeredGridMeasuredItem(
     val index: Int,
     val key: Any,
@@ -511,6 +614,7 @@
     }!!
 
     fun position(
+        lane: Int,
         mainAxis: Int,
         crossAxis: Int,
     ): LazyStaggeredGridPositionedItem =
@@ -520,6 +624,7 @@
             } else {
                 IntOffset(mainAxis, crossAxis)
             },
+            lane = lane,
             index = index,
             key = key,
             size = IntSize(sizeWithSpacings, crossAxisSize),
@@ -530,6 +635,7 @@
 private class LazyStaggeredGridPositionedItem(
     override val offset: IntOffset,
     override val index: Int,
+    override val lane: Int,
     override val key: Any,
     override val size: IntSize,
     private val placeables: List<Placeable>
@@ -540,12 +646,3 @@
         }
     }
 }
-
-// This interface allows to avoid autoboxing on index param
-private fun interface MeasuredItemFactory {
-    fun createItem(
-        index: Int,
-        key: Any,
-        placeables: List<Placeable>
-    ): LazyStaggeredGridMeasuredItem
-}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasurePolicy.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasurePolicy.kt
new file mode 100644
index 0000000..43b03ef
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasurePolicy.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy.staggeredgrid
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.OverscrollEffect
+import androidx.compose.foundation.checkScrollableContainerConstraints
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider
+import androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
+
+@Composable
+@ExperimentalFoundationApi
+internal fun rememberStaggeredGridMeasurePolicy(
+    state: LazyStaggeredGridState,
+    itemProvider: LazyLayoutItemProvider,
+    contentPadding: PaddingValues,
+    reverseLayout: Boolean,
+    orientation: Orientation,
+    verticalArrangement: Arrangement.Vertical,
+    horizontalArrangement: Arrangement.Horizontal,
+    slotSizesSums: Density.(Constraints) -> IntArray,
+    overscrollEffect: OverscrollEffect
+): LazyLayoutMeasureScope.(Constraints) -> LazyStaggeredGridMeasureResult = remember(
+    state,
+    itemProvider,
+    contentPadding,
+    reverseLayout,
+    orientation,
+    verticalArrangement,
+    horizontalArrangement,
+    slotSizesSums,
+    overscrollEffect,
+) {
+    { constraints ->
+        checkScrollableContainerConstraints(
+            constraints,
+            orientation
+        )
+        val resolvedSlotSums = slotSizesSums(this, constraints)
+
+        // setup information for prefetch
+        state.prefetchLaneWidths = resolvedSlotSums
+        state.isVertical = orientation == Orientation.Vertical
+
+        measure(
+            state,
+            itemProvider,
+            resolvedSlotSums,
+            constraints,
+            isVertical = orientation == Orientation.Vertical,
+            beforeContentPadding = 0,
+            afterContentPadding = 0,
+        ).also {
+            state.applyMeasureResult(it)
+            overscrollEffect.isEnabled = it.canScrollForward || it.canScrollBackward
+        }
+    }
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasureResult.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasureResult.kt
index 4abccf9..a01732e 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasureResult.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasureResult.kt
@@ -27,12 +27,24 @@
     val measureResult: MeasureResult,
     val canScrollForward: Boolean,
     val canScrollBackward: Boolean,
-    val visibleItemsInfo: Array<List<LazyStaggeredGridItemInfo>>
-) : MeasureResult by measureResult
+    override val totalItemsCount: Int,
+    override val visibleItemsInfo: List<LazyStaggeredGridItemInfo>
+) : LazyStaggeredGridLayoutInfo, MeasureResult by measureResult
 
 internal interface LazyStaggeredGridItemInfo {
     val offset: IntOffset
     val index: Int
+    val lane: Int
     val key: Any
     val size: IntSize
+}
+
+internal interface LazyStaggeredGridLayoutInfo {
+    val visibleItemsInfo: List<LazyStaggeredGridItemInfo>
+    val totalItemsCount: Int
+
+    companion object Empty : LazyStaggeredGridLayoutInfo {
+        override val visibleItemsInfo: List<LazyStaggeredGridItemInfo> = emptyList()
+        override val totalItemsCount: Int = 0
+    }
 }
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridSpans.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridSpans.kt
new file mode 100644
index 0000000..acb28d0
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridSpans.kt
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.lazy.staggeredgrid
+
+/**
+ * Utility class to remember grid lane assignments (spans) in a sliding window relative to requested
+ * item position (usually reflected by scroll position).
+ * Remembers the maximum range of remembered items is reflected by [MaxCapacity], if index is beyond
+ * the bounds, [anchor] moves to reflect new position.
+ */
+internal class LazyStaggeredGridSpans {
+    private var anchor = 0
+    private var spans = IntArray(16)
+
+    /**
+     * Sets given span for given item index.
+     */
+    fun setSpan(item: Int, span: Int) {
+        require(item >= 0) { "Negative spans are not supported" }
+        ensureValidIndex(item)
+        spans[item - anchor] = span + 1
+    }
+
+    /**
+     * Get span for given item index.
+     * @return span previously recorded for given item or [Unset] if it doesn't exist.
+     */
+    fun getSpan(item: Int): Int {
+        if (item < lowerBound() || item >= upperBound()) {
+            return Unset
+        }
+        return spans[item - anchor] - 1
+    }
+
+    /**
+     * @return upper bound of currently valid span range
+     */
+    /* @VisibleForTests */
+    fun upperBound(): Int = anchor + spans.size
+
+    /**
+     * @return lower bound of currently valid span range
+     */
+    /* @VisibleForTests */
+    fun lowerBound(): Int = anchor
+
+    /**
+     * Delete remembered span assignments.
+     */
+    fun reset() {
+        spans.fill(0)
+    }
+
+    /**
+     * Find the previous item relative to [item] set to target span
+     * @return found item index or [Unset] if it doesn't exist.
+     */
+    fun findPreviousItemIndex(item: Int, target: Int): Int {
+        for (i in (item - 1) downTo 0) {
+            val span = getSpan(i)
+            if (span == target || span == Unset) {
+                return i
+            }
+        }
+        return Unset
+    }
+
+    /**
+     * Find the next item relative to [item] set to target span
+     * @return found item index or [upperBound] if it doesn't exist.
+     */
+    fun findNextItemIndex(item: Int, target: Int): Int {
+        for (i in item + 1 until upperBound()) {
+            val span = getSpan(i)
+            if (span == target || span == Unset) {
+                return i
+            }
+        }
+        return upperBound()
+    }
+
+    private fun ensureValidIndex(requestedIndex: Int) {
+        val requestedCapacity = requestedIndex - anchor
+
+        if (requestedCapacity in 0 until MaxCapacity) {
+            // simplest path - just grow array to given capacity
+            ensureCapacity(requestedCapacity + 1)
+        } else {
+            // requested index is beyond current span bounds
+            // rebase anchor so that requested index is in the middle of span array
+            val oldAnchor = anchor
+            anchor = maxOf(requestedIndex - (spans.size / 2), 0)
+            var delta = anchor - oldAnchor
+
+            if (delta >= 0) {
+                // copy previous span data if delta is smaller than span size
+                if (delta < spans.size) {
+                    spans.copyInto(
+                        spans,
+                        destinationOffset = 0,
+                        startIndex = delta,
+                        endIndex = spans.size
+                    )
+                }
+                // fill the rest of the spans with default values
+                spans.fill(0, maxOf(0, spans.size - delta), spans.size)
+            } else {
+                delta = -delta
+                // check if we can grow spans to match delta
+                if (spans.size + delta < MaxCapacity) {
+                    // grow spans and leave space in the start
+                    ensureCapacity(spans.size + delta + 1, delta)
+                } else {
+                    // otherwise, just move data that fits
+                    if (delta < spans.size) {
+                        spans.copyInto(
+                            spans,
+                            destinationOffset = delta,
+                            startIndex = 0,
+                            endIndex = spans.size - delta
+                        )
+                    }
+                    // fill the rest of the spans with default values
+                    spans.fill(0, 0, minOf(spans.size, delta))
+                }
+            }
+        }
+    }
+
+    private fun ensureCapacity(capacity: Int, newOffset: Int = 0) {
+        require(capacity <= MaxCapacity) {
+            "Requested span capacity $capacity is larger than max supported: $MaxCapacity!"
+        }
+        if (spans.size < capacity) {
+            var newSize = spans.size
+            while (newSize < capacity) newSize *= 2
+            spans = spans.copyInto(IntArray(newSize), destinationOffset = newOffset)
+        }
+    }
+
+    companion object {
+        private const val MaxCapacity = 131_072 // Closest to 100_000, 2 ^ 17
+        internal const val Unset = -1
+    }
+}
\ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt
index cef7880..e413576 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt
@@ -21,22 +21,33 @@
 import androidx.compose.foundation.gestures.ScrollScope
 import androidx.compose.foundation.gestures.ScrollableState
 import androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState
+import androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState.PrefetchHandle
+import androidx.compose.runtime.collection.mutableVectorOf
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.layout.Remeasurement
 import androidx.compose.ui.layout.RemeasurementModifier
+import androidx.compose.ui.unit.Constraints
 import kotlin.math.abs
 
 @ExperimentalFoundationApi
-internal class LazyStaggeredGridState : ScrollableState {
-    var firstVisibleItems: IntArray by mutableStateOf(IntArray(0))
+internal class LazyStaggeredGridState(
+    initialFirstVisibleItems: IntArray = IntArray(0),
+    initialFirstVisibleOffsets: IntArray = IntArray(0)
+) : ScrollableState {
+    var firstVisibleItems: IntArray by mutableStateOf(initialFirstVisibleItems)
         private set
 
-    var firstVisibleItemScrollOffsets: IntArray by mutableStateOf(IntArray(0))
+    var firstVisibleItemScrollOffsets: IntArray by mutableStateOf(initialFirstVisibleOffsets)
         private set
 
-    internal val spans: SpanLookup = SpanLookup()
+    internal var layoutInfo: LazyStaggeredGridLayoutInfo by mutableStateOf(
+        LazyStaggeredGridLayoutInfo.Empty
+    )
+        private set
+
+    internal val spans = LazyStaggeredGridSpans()
 
     private var canScrollForward = true
     private var canScrollBackward = true
@@ -61,6 +72,12 @@
     /* @VisibleForTesting */
     internal var measurePassCount = 0
 
+    private var wasScrollingForward = true
+    internal var isVertical = false
+    internal var prefetchLaneWidths: IntArray = IntArray(0)
+    private var prefetchBaseIndex: Int = -1
+    private val currentItemPrefetchHandles = mutableVectorOf<PrefetchHandle>()
+
     override suspend fun scroll(
         scrollPriority: MutatePriority,
         block: suspend ScrollScope.() -> Unit
@@ -81,12 +98,11 @@
         // inside measuring we do scrollToBeConsumed.roundToInt() so there will be no scroll if
         // we have less than 0.5 pixels
         if (abs(scrollToBeConsumed) > 0.5f) {
+            val preScrollToBeConsumed = scrollToBeConsumed
             remeasurement?.forceRemeasure()
-            // todo(b/182882362): notify prefetch
-//            if (prefetchingEnabled) {
-//                val leftoverScroll = preScrollToBeConsumed - scrollToBeConsumed
-//                notifyPrefetch(preScrollToBeConsumed - scrollToBeConsumed)
-//            }
+            if (prefetchingEnabled) {
+                notifyPrefetch(preScrollToBeConsumed - scrollToBeConsumed)
+            }
         }
 
         // here scrollToBeConsumed is already consumed during the forceRemeasure invocation
@@ -106,45 +122,95 @@
     override fun dispatchRawDelta(delta: Float): Float =
         scrollableState.dispatchRawDelta(delta)
 
+    private fun notifyPrefetch(delta: Float) {
+        if (!prefetchingEnabled) {
+            return
+        }
+        val info = layoutInfo
+        if (info.visibleItemsInfo.isNotEmpty()) {
+            val scrollingForward = delta < 0
+
+            if (wasScrollingForward != scrollingForward) {
+                // the scrolling direction has been changed which means the last prefetched item
+                // is not going to be reached anytime soon so it is safer to dispose it.
+                // if this item is already visible it is safe to call the method anyway
+                // as it will be a no-op
+                currentItemPrefetchHandles.forEach { it.cancel() }
+            }
+
+            wasScrollingForward = scrollingForward
+            val prefetchIndex = if (scrollingForward) {
+                info.visibleItemsInfo.last().index
+            } else {
+                info.visibleItemsInfo.first().index
+            }
+
+            if (prefetchIndex == prefetchBaseIndex) {
+                // Already prefetched based on this index
+                return
+            }
+            prefetchBaseIndex = prefetchIndex
+            currentItemPrefetchHandles.clear()
+
+            var targetIndex = prefetchIndex
+            for (lane in prefetchLaneWidths.indices) {
+                val previousIndex = targetIndex
+
+                // find the next item for each line and prefetch if it is valid
+                targetIndex = if (scrollingForward) {
+                    spans.findNextItemIndex(previousIndex, lane)
+                } else {
+                    spans.findPreviousItemIndex(previousIndex, lane)
+                }
+                if (
+                    targetIndex !in (0 until info.totalItemsCount) ||
+                        previousIndex == targetIndex
+                ) {
+                    return
+                }
+
+                val crossAxisSize = prefetchLaneWidths[lane] -
+                    if (lane == 0) 0 else prefetchLaneWidths[lane - 1]
+
+                val constraints = if (isVertical) {
+                    Constraints.fixedWidth(crossAxisSize)
+                } else {
+                    Constraints.fixedHeight(crossAxisSize)
+                }
+
+                currentItemPrefetchHandles.add(
+                    prefetchState.schedulePrefetch(
+                        index = targetIndex,
+                        constraints = constraints
+                    )
+                )
+            }
+        }
+    }
+
+    private fun cancelPrefetchIfVisibleItemsChanged(info: LazyStaggeredGridLayoutInfo) {
+        val items = info.visibleItemsInfo
+        if (prefetchBaseIndex != -1 && items.isNotEmpty()) {
+            if (prefetchBaseIndex !in items.first().index..items.last().index) {
+                prefetchBaseIndex = -1
+                currentItemPrefetchHandles.forEach { it.cancel() }
+                currentItemPrefetchHandles.clear()
+            }
+        }
+    }
+
     internal fun applyMeasureResult(result: LazyStaggeredGridMeasureResult) {
         scrollToBeConsumed -= result.consumedScroll
         firstVisibleItems = result.firstVisibleItemIndices
         firstVisibleItemScrollOffsets = result.firstVisibleItemScrollOffsets
         canScrollBackward = result.canScrollBackward
         canScrollForward = result.canScrollForward
+        layoutInfo = result
+        cancelPrefetchIfVisibleItemsChanged(result)
 
         measurePassCount++
     }
 
     override val isScrollInProgress: Boolean
         get() = scrollableState.isScrollInProgress
-}
-
-internal class SpanLookup {
-    private var spans = IntArray(16)
-
-    fun setSpan(item: Int, span: Int) {
-        ensureCapacity(item + 1)
-        spans[item] = span + 1
-    }
-
-    fun getSpan(item: Int): Int =
-        spans[item] - 1
-
-    fun capacity(): Int =
-        spans.size
-
-    private fun ensureCapacity(capacity: Int) {
-        if (spans.size < capacity) {
-            spans = spans.copyInto(IntArray(spans.size * 2))
-        }
-    }
-
-    fun reset() {
-        spans.fill(0)
-    }
-
-    companion object {
-        internal const val SpanUnset = -1
-    }
 }
\ No newline at end of file
diff --git a/docs/OWNERS b/docs/OWNERS
new file mode 100644
index 0000000..636ac16
--- /dev/null
+++ b/docs/OWNERS
@@ -0,0 +1,2 @@
[email protected]
[email protected]
diff --git a/docs/api_guidelines/deprecation.md b/docs/api_guidelines/deprecation.md
index ae4246a..0ca80db 100644
--- a/docs/api_guidelines/deprecation.md
+++ b/docs/api_guidelines/deprecation.md
@@ -5,12 +5,22 @@
 API, there are many other ways to influence how developers interact with your
 library.
 
-### Deprecation (`@deprecated`)
+### Deprecation (`@Deprecated`)
 
 Deprecation lets a developer know that they should stop using an API or class.
-All deprecations must be marked with a `@Deprecated` Java annotation as well as
-a `@deprecated <migration-docs>` docs annotation explaining how the developer
-should migrate away from the API.
+All deprecations must be marked with a `@Deprecated` code annotation as well as
+a `@deprecated <explanation>` docs annotation (for Java) or
+[`@Deprecated(message = <explanation>)`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-deprecated/)
+(for Kotlin) explaining the rationale and how the developer should migrate away
+from the API.
+
+Deprecations in Kotlin are encouraged to provide an automatic migration by
+specifying the
+[`replaceWith = ReplaceWith(<replacement>)`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-replace-with/)
+parameter to `@Deprecated` in cases where the migration is a straightforward
+replacement and *may* specify
+[`level = DeprecationLevel.ERROR`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-deprecation-level/)
+(source-breaking) in cases where the API on track to be fully removed.
 
 Deprecation is an non-breaking API change that must occur in a **major** or
 **minor** release.
@@ -19,7 +29,7 @@
 within the same cycle, e.g. added in `alpha01` and deprecated in `alpha06`,
 [must be removed](versioning.md#beta-checklist) before moving to `beta01`.
 
-### Soft removal (@removed)
+### Soft removal (`@removed` or `DeprecationLevel.HIDDEN`)
 
 Soft removal preserves binary compatibility while preventing source code from
 compiling against an API. It is a *source-breaking change* and not recommended.
@@ -27,8 +37,11 @@
 Soft removals **must** do the following:
 
 *   Mark the API as deprecated for at least one stable release prior to removal.
-*   Mark the API with a `@RestrictTo(LIBRARY)` Java annotation as well as a
-    `@removed <reason>` docs annotation explaining why the API was removed.
+*   In Java sources, mark the API with a `@RestrictTo(LIBRARY)` Java annotation
+    as well as a `@removed <reason>` docs annotation explaining why the API was
+    removed.
+*   In Kotlin sources, mark the API with `@Deprecated(message = <reason>,
+    level = DeprecationLevel.HIDDEN)` explaining why the API was removed.
 *   Maintain binary compatibility, as the API may still be called by existing
     dependent libraries.
 *   Maintain behavioral compatibility and existing tests.
diff --git a/docs/api_guidelines/platform_compat.md b/docs/api_guidelines/platform_compat.md
index 6de372e..0148e18 100644
--- a/docs/api_guidelines/platform_compat.md
+++ b/docs/api_guidelines/platform_compat.md
@@ -1,24 +1,34 @@
 ## Platform compatibility API patterns {#platform-compatibility-apis}
 
-NOTE For all library APIs that wrap or provide parity with platform APIs,
-*parity with the platform APIs overrides API guidelines*. For example, if the
-platform API being wrapped has incorrect `Executor` and `Callback` ordering
-according to the API Guidelines, the corresponding library API should have the
-exact same (incorrect) ordering.
+NOTE For all library APIs that wrap or provide parity with platform APIs, we
+prefer to follow modern API guidelines; however, developers *may* choose to
+prioritize parity with the platform APIs over adherence to modern guidelines.
+For example, if the platform API being wrapped has incorrect `Executor` and
+`Callback` ordering according to the API Guidelines, the corresponding library
+API *should* re-order the arguments.
 
-### Static shims (ex. [ViewCompat](https://developer.android.com/reference/android/support/v4/view/ViewCompat.html)) {#static-shim}
+### Static shims (ex. [ViewCompat](https://developer.android.com/reference/androidx/core/view/ViewCompat)) {#static-shim}
 
 When to use?
 
 *   Platform class exists at module's `minSdkVersion`
 *   Compatibility implementation does not need to store additional metadata
 
-Implementation requirements
+#### API guidelines {#static-shim-api-guidelines}
 
+##### Naming {#static-shim-naming}
+
+*   Class *should* be added to the `androidx.core:core` library
 *   Class name **must** be `<PlatformClass>Compat`
 *   Package name **must** be `androidx.<feature>.<platform.package>`
 *   Superclass **must** be `Object`
+
+##### Construction {#static-shim-construction}
+
 *   Class **must** be non-instantiable, i.e. constructor is private no-op
+
+#### Implementation {#static-shim-implementation}
+
 *   Static fields and static methods **must** match match signatures with
     `<PlatformClass>`
     *   Static fields that can be inlined, ex. integer constants, **must not**
@@ -34,7 +44,9 @@
 The following sample provides static helper methods for the platform class
 `android.os.Process`.
 
-```java
+~~~java
+package androidx.core.os;
+
 /**
  * Helper for accessing features in {@link Process}.
  */
@@ -61,51 +73,16 @@
     public static boolean isApplicationUid(int uid) {
         if (Build.VERSION.SDK_INT >= 24) {
             return Api24Impl.isApplicationUid(uid);
-        } else if (Build.VERSION.SDK_INT >= 17) {
-            return Api17Impl.isApplicationUid(uid);
-        } else if (Build.VERSION.SDK_INT == 16) {
-            return Api16Impl.isApplicationUid(uid);
+        } else if (Build.VERSION.SDK_INT >= 16) {
+            // Fall back to using reflection on private APIs.
+            // ...
         } else {
             return true;
         }
     }
-
-    @RequiresApi(24)
-    static class Api24Impl {
-        static boolean isApplicationUid(int uid) {
-            // In N, the method was made public on android.os.Process.
-            return Process.isApplicationUid(uid);
-        }
-    }
-
-    @RequiresApi(17)
-    static class Api17Impl {
-        private static Method sMethod_isAppMethod;
-        private static boolean sResolved;
-
-        static boolean isApplicationUid(int uid) {
-            // In JELLY_BEAN_MR2, the equivalent isApp(int) hidden method moved to public class
-            // android.os.UserHandle.
-            try {
-                if (!sResolved) {
-                    sResolved = true;
-                    sMethod_isAppMethod = UserHandle.class.getDeclaredMethod("isApp",int.class);
-                }
-                if (sMethod_isAppMethod != null) {
-                    return (Boolean) sMethod_isAppMethod.invoke(null, uid);
-                }
-            } catch (Exception e) {
-                e.printStackTrace();
-            }
-            return true;
-        }
-    }
-
-    ...
 }
-```
 
-### Wrapper (ex. [AccessibilityNodeInfoCompat](https://developer.android.com/reference/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.html)) {#wrapper}
+### Wrapper (ex. [AccessibilityNodeInfoCompat](https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo)) {#wrapper}
 
 When to use?
 
@@ -116,6 +93,42 @@
     very difficult to deprecate classes and migrate source code when the
     `minSdkVersion` is raised
 
+#### API guidelines {#wrapper-api-guidelines}
+
+##### Naming {#wrapper-naming}
+
+*   Class name **must** be `<PlatformClass>Compat`
+*   Package name **must** be `androidx.core.<platform.package>`
+*   Superclass **must not** be `<PlatformClass>`
+
+##### Construction {#wrapper-construction}
+
+*   Class *may* have public constructor(s) to provide parity with public
+    `PlatformClass` constructors
+    *   Constructor used to wrap `PlatformClass` **must not** be public
+*   Class **must** implement a static `PlatformClassCompat
+    toPlatformClassCompat(PlatformClass)` method to wrap `PlatformClass` on
+    supported SDK levels
+    *   If class does not exist at module's `minSdkVersion`, method must be
+        annotated with `@RequiresApi(<sdk>)` for SDK version where class was
+        introduced
+
+#### Implementation {#wrapper-implementation}
+
+*   Class *should* be added to the `androidx.core:core` library
+*   Class **must** implement a `PlatformClass toPlatformClass()` method to
+    unwrap `PlatformClass` on supported SDK levels
+    *   If class does not exist at module's `minSdkVersion`, method must be
+        annotated with `@RequiresApi(<sdk>)` for SDK version where class was
+        introduced
+*   Implementation *may* delegate to `PlatformClass` methods when available (see
+    below note for caveats)
+*   To avoid runtime class verification issues, all operations that interact
+    with the internal structure of `PlatformClass` must be implemented in inner
+    classes targeted to the SDK level at which the operation was added.
+    *   See the [sample](#wrapper-sample) for an example of interacting with a
+        method that was added in SDK level 23.
+
 #### Sample {#wrapper-sample}
 
 The following sample wraps a hypothetical platform class `ModemInfo` that was
@@ -126,7 +139,7 @@
   // Only guaranteed to be non-null on SDK_INT >= 23. Note that referencing the
   // class itself directly is fine -- only references to class members need to
   // be pushed into static inner classes.
-  private final ModemInfo wrappedObj;
+  private final Object wrappedObj;
 
   /**
    * [Copy platform docs for matching constructor.]
@@ -137,7 +150,6 @@
     } else {
       wrappedObj = null;
     }
-    ...
   }
 
   @RequiresApi(23)
@@ -192,7 +204,7 @@
       return Api23Impl.isLteSupported(mWrapped);
     } else if (SDK_INT >= 18) {
       // Smart fallback behavior based on earlier APIs.
-      ...
+      // ...
     }
     // Default behavior.
     return false;
@@ -210,12 +222,12 @@
     }
 
     @DoNotInline
-    static boolean isLteSupported(ModemInfo obj) {
-      return obj.isLteSupported();
+    static boolean isLteSupported(Object obj) {
+      return ((ModemInfo) obj).isLteSupported();
     }
   }
 }
-```
+~~~
 
 Note that libraries written in Java should express conversion to and from the
 platform class differently than Kotlin classes. For Java classes, conversion
@@ -254,91 +266,7 @@
 }
 ```
 
-#### API guidelines {#wrapper-api-guidelines}
-
-##### Naming {#wrapper-naming}
-
-*   Class name **must** be `<PlatformClass>Compat`
-*   Package name **must** be `androidx.core.<platform.package>`
-*   Superclass **must not** be `<PlatformClass>`
-
-##### Construction {#wrapper-construction}
-
-*   Class *may* have public constructor(s) to provide parity with public
-    `PlatformClass` constructors
-    *   Constructor used to wrap `PlatformClass` **must not** be public
-*   Class **must** implement a static `PlatformClassCompat
-    toPlatformClassCompat(PlatformClass)` method to wrap `PlatformClass` on
-    supported SDK levels
-    *   If class does not exist at module's `minSdkVersion`, method must be
-        annotated with `@RequiresApi(<sdk>)` for SDK version where class was
-        introduced
-
-#### Implementation {#wrapper-implementation}
-
-*   Class **must** implement a `PlatformClass toPlatformClass()` method to
-    unwrap `PlatformClass` on supported SDK levels
-    *   If class does not exist at module's `minSdkVersion`, method must be
-        annotated with `@RequiresApi(<sdk>)` for SDK version where class was
-        introduced
-*   Implementation *may* delegate to `PlatformClass` methods when available (see
-    below note for caveats)
-*   To avoid runtime class verification issues, all operations that interact
-    with the internal structure of `PlatformClass` must be implemented in inner
-    classes targeted to the SDK level at which the operation was added.
-    *   See the [sample](#wrapper-sample) for an example of interacting with a
-        method that was added in SDK level 23.
-
-### Safe super. invocation {#safe-super-calls}
-
-When to use?
-
-*   When invoking `method.superMethodIntroducedSinceMinSdk()`
-
-Implementation requirements
-
-*   Class must be a *non-static* **inner class** (captures `this` pointer)
-*   Class may not be exposed in public API
-
-This should only be used when calling `super` methods that will not verify (such
-as when overriding a new method to provide back compat).
-
-Super calls is not available in a `static` context in Java. It can however be
-called from an inner class.
-
-#### Sample {#safe-super-calls-sample}
-
-```java
-class AppCompatTextView : TextView {
-
-  @Nullable
-  SuperCaller mSuperCaller = null;
-
-  @Override
-  int getPropertyFromApi99() {
-  if (Build.VERSION.SDK_INT > 99) {
-    getSuperCaller().getPropertyFromApi99)();
-  }
-
-  @NonNull
-  @RequiresApi(99)
-  SuperCaller getSuperCaller() {
-    if (mSuperCaller == null) {
-      mSuperCaller = new SuperCaller();
-    }
-    return mSuperCaller;
-  }
-
-  @RequiresApi(99)
-  class SuperCaller {
-    int getPropertyFromApi99() {
-      return AppCompatTextView.super.getPropertyFromApi99();
-    }
-  }
-}
-```
-
-### Standalone (ex. [ArraySet](https://developer.android.com/reference/android/support/v4/util/ArraySet.html), [Fragment](https://developer.android.com/reference/android/support/v4/app/Fragment.html)) {#standalone}
+### Standalone (ex. [ArraySet](https://developer.android.com/reference/androidx/collection/ArraySet), [Fragment](https://developer.android.com/jetpack/androidx/releases/fragment)) {#standalone}
 
 When to use?
 
@@ -361,7 +289,7 @@
         `static toCompat<PlatformClass>` method naming convention.
 *   Implementation *may* delegate to `PlatformClass` methods when available
 
-### Standalone JAR library (no Android dependencies) {#standalone-jar-library-no-android-dependencies}
+### Standalone JAR library (no Android dependencies) {#standalone-jvm}
 
 When to use
 
@@ -392,3 +320,52 @@
 the fact that Room generates code dynamically, means that Room interfaces can be
 used in host-side tests (though actual DB code should be tested on device, since
 DB impls may be significantly different on host).
+
+### Addressing class verification failures on `super.` invocation {#compat-super}
+
+Invoking a `super` call on a method introduced in an API level higher than a
+class's minimum SDK level will raise a run-time class verification failure, and
+will be detected by the `ClassVerificationFailure` lint check.
+
+```java {.bad}
+public void performAction() {
+  if (SDK_INT >= 31) {
+    super.performAction(); // This will cause a verification failure.
+  }
+}
+```
+
+These failures can be addressed by out-of-lining the `super` call to a
+non-static inner class.
+
+#### Sample {#compat-super-sample}
+
+```java
+class AppCompatTextView : TextView {
+
+  @Nullable
+  SuperCaller mSuperCaller = null;
+
+  @Override
+  int getPropertyFromApi99() {
+  if (Build.VERSION.SDK_INT > 99) {
+    getSuperCaller().getPropertyFromApi99)();
+  }
+
+  @NonNull
+  @RequiresApi(99)
+  private SuperCaller getSuperCaller() {
+    if (mSuperCaller == null) {
+      mSuperCaller = new Api99SuperCaller();
+    }
+    return mSuperCaller;
+  }
+
+  @RequiresApi(99)
+  private class Api99SuperCaller {
+    int getPropertyFromApi99() {
+      return AppCompatTextView.super.getPropertyFromApi99();
+    }
+  }
+}
+```
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 7d66694..bbab205 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -23,6 +23,7 @@
 atomicFu = "0.17.0"
 autoService = "1.0-rc6"
 autoValue = "1.6.3"
+asm = "9.3"
 cmake = "3.22.1"
 dagger = "2.42"
 dexmaker = "2.28.3"
@@ -74,6 +75,8 @@
 apacheCommonsCodec = { module = "commons-codec:commons-codec", version = "1.15" }
 apacheCommonIo = { module = "commons-io:commons-io", version = "2.4" }
 assertj = { module = "org.assertj:assertj-core", version = "3.11.1" }
+asm = { module = "org.ow2.asm:asm", version = "asm"}
+asmCommons = { module = "org.ow2.asm:asm-commons", version = "asm"}
 checkerframework = { module = "org.checkerframework:checker-qual", version = "2.5.3" }
 checkmark = { module = "net.saff.checkmark:checkmark", version = "0.1.6" }
 constraintLayout = { module = "androidx.constraintlayout:constraintlayout", version = "2.0.1"}
diff --git a/lifecycle/lifecycle-common/build.gradle b/lifecycle/lifecycle-common/build.gradle
index c62e819..2e59a5d 100644
--- a/lifecycle/lifecycle-common/build.gradle
+++ b/lifecycle/lifecycle-common/build.gradle
@@ -29,7 +29,7 @@
 
     constraints {
         implementation(project(":lifecycle:lifecycle-common-java8"))
-        implementation(project(":lifecycle:lifecycle-runtime"))
+        implementation(projectOrArtifact(":lifecycle:lifecycle-runtime"))
     }
 }
 
diff --git a/lifecycle/lifecycle-livedata-ktx/build.gradle b/lifecycle/lifecycle-livedata-ktx/build.gradle
index 85c3906..a46628b 100644
--- a/lifecycle/lifecycle-livedata-ktx/build.gradle
+++ b/lifecycle/lifecycle-livedata-ktx/build.gradle
@@ -44,6 +44,7 @@
     androidTestImplementation(libs.truth)
     androidTestImplementation(libs.testExtJunit)
     androidTestImplementation(libs.testCore)
+    androidTestImplementation(libs.testRules)
     androidTestImplementation(libs.testRunner)
     androidTestImplementation(libs.kotlinCoroutinesTest)
     androidTestImplementation(libs.kotlinCoroutinesAndroid)
diff --git a/lifecycle/lifecycle-livedata-ktx/src/androidTest/java/androidx.lifecycle/FlowAsLiveDataIntegrationTest.kt b/lifecycle/lifecycle-livedata-ktx/src/androidTest/java/androidx.lifecycle/FlowAsLiveDataIntegrationTest.kt
index 66dd70c..315032a 100644
--- a/lifecycle/lifecycle-livedata-ktx/src/androidTest/java/androidx.lifecycle/FlowAsLiveDataIntegrationTest.kt
+++ b/lifecycle/lifecycle-livedata-ktx/src/androidTest/java/androidx.lifecycle/FlowAsLiveDataIntegrationTest.kt
@@ -17,6 +17,7 @@
 package androidx.lifecycle
 
 import androidx.arch.core.executor.ArchTaskExecutor
+import androidx.test.annotation.UiThreadTest
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import com.google.common.truth.Truth.assertThat
@@ -24,6 +25,7 @@
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.FlowPreview
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.channelFlow
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.runBlocking
@@ -67,4 +69,16 @@
             stopChannelFlow.complete(Unit)
         }
     }
+
+    @UiThreadTest
+    @Test
+    @MediumTest
+    fun asLiveData_preserveStateFlowInitialValue() {
+        val value = "init"
+        val result = MutableStateFlow(value)
+            .asLiveData()
+            .value
+        assertThat(result).isNotNull()
+        assertThat(result).isEqualTo(value)
+    }
 }
diff --git a/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/FlowLiveData.kt b/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/FlowLiveData.kt
index 9c907b9..7c2d8ed 100644
--- a/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/FlowLiveData.kt
+++ b/lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/FlowLiveData.kt
@@ -20,6 +20,7 @@
 
 import android.os.Build
 import androidx.annotation.RequiresApi
+import androidx.arch.core.executor.ArchTaskExecutor
 import java.time.Duration
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
@@ -28,7 +29,7 @@
 import kotlinx.coroutines.GlobalScope
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
@@ -36,6 +37,9 @@
 /**
  * Creates a LiveData that has values collected from the origin [Flow].
  *
+ * If the origin [Flow] is a [StateFlow], then the initial value will be populated
+ * to the [LiveData]'s value field on the main thread.
+ *
  * The upstream flow collection starts when the returned [LiveData] becomes active
  * ([LiveData.onActive]).
  * If the [LiveData] becomes inactive ([LiveData.onInactive]) while the flow has not completed,
@@ -75,6 +79,15 @@
     collect {
         emit(it)
     }
+}.also { liveData ->
+    val flow = this
+    if (flow is StateFlow<T>) {
+        if (ArchTaskExecutor.getInstance().isMainThread) {
+            liveData.value = flow.value
+        } else {
+            liveData.postValue(flow.value)
+        }
+    }
 }
 
 /**
diff --git a/room/integration-tests/kotlintestapp/build.gradle b/room/integration-tests/kotlintestapp/build.gradle
index 22916dc..a88e0f2 100644
--- a/room/integration-tests/kotlintestapp/build.gradle
+++ b/room/integration-tests/kotlintestapp/build.gradle
@@ -112,7 +112,7 @@
     androidTestImplementation(project(":internal-testutils-common"))
     androidTestImplementation("androidx.arch.core:core-testing:2.0.1")
     androidTestImplementation("androidx.paging:paging-runtime:3.1.1")
-    androidTestImplementation(project(":lifecycle:lifecycle-runtime-testing"))
+    androidTestImplementation(projectOrArtifact(":lifecycle:lifecycle-runtime-testing"))
     androidTestImplementation(libs.rxjava2)
     testImplementation(libs.mockitoCore)
 }
diff --git a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObjectTest.java b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObjectTest.java
index 7d76076..8907b5e 100644
--- a/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObjectTest.java
+++ b/test/uiautomator/integration-tests/testapp/src/androidTest/java/androidx/test/uiautomator/testapp/UiObjectTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 
+import android.graphics.Point;
 import android.graphics.Rect;
 
 import androidx.test.filters.FlakyTest;
@@ -678,10 +679,24 @@
         assertThrows(IllegalStateException.class, () -> smallArea.pinchIn(100, 10));
     }
 
-    /* TODO(b/241158642): Implement these tests, and the tests for exceptions of each tested method.
+    @Test
+    public void testPerformTwoPointerGesture_withZeroSteps() throws Exception {
+        // Note that most part of `performTwoPointerGesture` (and `performMultiPointerGesture`)
+        // has already been indirectly tested in other tests. This test only test the case when
+        // the `step` parameter is set to zero.
+        launchTestActivity(PointerGestureTestActivity.class);
 
-    public void testPerformTwoPointerGesture() {}
+        UiObject touchRegion = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
+                + "/touch_region"));
 
-    public void testPerformMultiPointerGesture() {}
-    */
+        Rect visibleBounds = touchRegion.getVisibleBounds();
+        Point startPoint1 = new Point(visibleBounds.left + 50, visibleBounds.top + 50);
+        Point startPoint2 = new Point(visibleBounds.right - 50, visibleBounds.top + 50);
+        Point endPoint1 = new Point(visibleBounds.left + 50, visibleBounds.bottom - 50);
+        Point endPoint2 = new Point(visibleBounds.right - 50, visibleBounds.bottom - 50);
+
+        assertTrue(touchRegion.performTwoPointerGesture(startPoint1, startPoint2, endPoint1,
+                endPoint2, 0));
+        assertEquals("2 touch(es) received", touchRegion.getText());
+    }
 }
diff --git a/test/uiautomator/integration-tests/testapp/src/main/AndroidManifest.xml b/test/uiautomator/integration-tests/testapp/src/main/AndroidManifest.xml
index 2e3d6d6..07b71cf 100644
--- a/test/uiautomator/integration-tests/testapp/src/main/AndroidManifest.xml
+++ b/test/uiautomator/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -134,6 +134,13 @@
                 <action android:name="android.intent.action.MAIN" />
             </intent-filter>
         </activity>
+        <activity android:name=".PointerGestureTestActivity"
+            android:exported="true"
+            android:theme="@android:style/Theme.Holo.NoActionBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
         <activity android:name=".SplitScreenTestActivity"
             android:exported="true"
             android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
diff --git a/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/PointerGestureTestActivity.java b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/PointerGestureTestActivity.java
new file mode 100644
index 0000000..249f805
--- /dev/null
+++ b/test/uiautomator/integration-tests/testapp/src/main/java/androidx/test/uiautomator/testapp/PointerGestureTestActivity.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.test.uiautomator.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.MotionEvent;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+public class PointerGestureTestActivity extends Activity {
+    private int mPointerGestureCount = 0;
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.pointer_gesture_test_activity);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent e) {
+        int maskedAction = e.getActionMasked();
+
+        if (maskedAction == MotionEvent.ACTION_DOWN
+                || maskedAction == MotionEvent.ACTION_POINTER_DOWN) {
+            mPointerGestureCount++;
+        }
+
+        if (maskedAction == MotionEvent.ACTION_UP) {
+            TextView touchRegion = findViewById(R.id.touch_region);
+            touchRegion.setText(String.format("%d touch(es) received", mPointerGestureCount));
+        }
+
+        return true;
+    }
+}
diff --git a/test/uiautomator/integration-tests/testapp/src/main/res/layout/pointer_gesture_test_activity.xml b/test/uiautomator/integration-tests/testapp/src/main/res/layout/pointer_gesture_test_activity.xml
new file mode 100644
index 0000000..5c6125c
--- /dev/null
+++ b/test/uiautomator/integration-tests/testapp/src/main/res/layout/pointer_gesture_test_activity.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    tools:context=".PointerGestureTestActivity">
+
+    <TextView
+        android:id="@+id/touch_region"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center" />
+
+</LinearLayout>
diff --git a/test/uiautomator/uiautomator/api/current.txt b/test/uiautomator/uiautomator/api/current.txt
index 90bec63..ca4de56 100644
--- a/test/uiautomator/uiautomator/api/current.txt
+++ b/test/uiautomator/uiautomator/api/current.txt
@@ -143,32 +143,32 @@
     method public void clearLastTraversedText();
     method public boolean click(int, int);
     method public boolean drag(int, int, int, int, int);
-    method @Deprecated public void dumpWindowHierarchy(String!);
-    method public void dumpWindowHierarchy(java.io.File!) throws java.io.IOException;
-    method public void dumpWindowHierarchy(java.io.OutputStream!) throws java.io.IOException;
-    method public androidx.test.uiautomator.UiObject! findObject(androidx.test.uiautomator.UiSelector!);
-    method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector!);
-    method public java.util.List<androidx.test.uiautomator.UiObject2!>! findObjects(androidx.test.uiautomator.BySelector!);
+    method @Deprecated public void dumpWindowHierarchy(String);
+    method public void dumpWindowHierarchy(java.io.File) throws java.io.IOException;
+    method public void dumpWindowHierarchy(java.io.OutputStream) throws java.io.IOException;
+    method public androidx.test.uiautomator.UiObject findObject(androidx.test.uiautomator.UiSelector);
+    method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector);
+    method public java.util.List<androidx.test.uiautomator.UiObject2!> findObjects(androidx.test.uiautomator.BySelector);
     method public void freezeRotation() throws android.os.RemoteException;
     method @Deprecated public String! getCurrentActivityName();
     method public String! getCurrentPackageName();
     method public int getDisplayHeight();
     method public int getDisplayRotation();
-    method public android.graphics.Point! getDisplaySizeDp();
+    method public android.graphics.Point getDisplaySizeDp();
     method public int getDisplayWidth();
-    method @Deprecated public static androidx.test.uiautomator.UiDevice! getInstance();
-    method public static androidx.test.uiautomator.UiDevice! getInstance(android.app.Instrumentation!);
+    method @Deprecated public static androidx.test.uiautomator.UiDevice getInstance();
+    method public static androidx.test.uiautomator.UiDevice getInstance(android.app.Instrumentation);
     method public String! getLastTraversedText();
     method public String! getLauncherPackageName();
-    method public String! getProductName();
+    method public String getProductName();
     method public boolean hasAnyWatcherTriggered();
-    method public boolean hasObject(androidx.test.uiautomator.BySelector!);
-    method public boolean hasWatcherTriggered(String!);
+    method public boolean hasObject(androidx.test.uiautomator.BySelector);
+    method public boolean hasWatcherTriggered(String?);
     method public boolean isNaturalOrientation();
     method public boolean isScreenOn() throws android.os.RemoteException;
     method public boolean openNotification();
     method public boolean openQuickSettings();
-    method public <R> R! performActionAndWait(Runnable!, androidx.test.uiautomator.EventCondition<R!>!, long);
+    method public <R> R! performActionAndWait(Runnable, androidx.test.uiautomator.EventCondition<R!>, long);
     method public boolean pressBack();
     method public boolean pressDPadCenter();
     method public boolean pressDPadDown();
@@ -183,8 +183,8 @@
     method public boolean pressMenu();
     method public boolean pressRecentApps() throws android.os.RemoteException;
     method public boolean pressSearch();
-    method public void registerWatcher(String!, androidx.test.uiautomator.UiWatcher!);
-    method public void removeWatcher(String!);
+    method public void registerWatcher(String?, androidx.test.uiautomator.UiWatcher?);
+    method public void removeWatcher(String?);
     method public void resetWatcherTriggers();
     method public void runWatchers();
     method public void setCompressedLayoutHeirarchy(boolean);
@@ -193,14 +193,14 @@
     method public void setOrientationRight() throws android.os.RemoteException;
     method public void sleep() throws android.os.RemoteException;
     method public boolean swipe(int, int, int, int, int);
-    method public boolean swipe(android.graphics.Point![]!, int);
-    method public boolean takeScreenshot(java.io.File!);
-    method public boolean takeScreenshot(java.io.File!, float, int);
+    method public boolean swipe(android.graphics.Point![], int);
+    method public boolean takeScreenshot(java.io.File);
+    method public boolean takeScreenshot(java.io.File, float, int);
     method public void unfreezeRotation() throws android.os.RemoteException;
-    method public <R> R! wait(androidx.test.uiautomator.SearchCondition<R!>!, long);
+    method public <R> R! wait(androidx.test.uiautomator.SearchCondition<R!>, long);
     method public void waitForIdle();
     method public void waitForIdle(long);
-    method public boolean waitForWindowUpdate(String!, long);
+    method public boolean waitForWindowUpdate(String?, long);
     method public void wakeUp() throws android.os.RemoteException;
   }
 
@@ -212,20 +212,20 @@
     method public boolean clickAndWaitForNewWindow(long) throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean clickBottomRight() throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean clickTopLeft() throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public boolean dragTo(androidx.test.uiautomator.UiObject!, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public boolean dragTo(androidx.test.uiautomator.UiObject, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean dragTo(int, int, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean exists();
-    method protected android.view.accessibility.AccessibilityNodeInfo! findAccessibilityNodeInfo(long);
-    method public android.graphics.Rect! getBounds() throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public androidx.test.uiautomator.UiObject! getChild(androidx.test.uiautomator.UiSelector!) throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method protected android.view.accessibility.AccessibilityNodeInfo? findAccessibilityNodeInfo(long);
+    method public android.graphics.Rect getBounds() throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public androidx.test.uiautomator.UiObject getChild(androidx.test.uiautomator.UiSelector) throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public int getChildCount() throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public String! getClassName() throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public String! getContentDescription() throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public androidx.test.uiautomator.UiObject! getFromParent(androidx.test.uiautomator.UiSelector!) throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public String! getPackageName() throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public final androidx.test.uiautomator.UiSelector! getSelector();
-    method public String! getText() throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public android.graphics.Rect! getVisibleBounds() throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public String getClassName() throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public String getContentDescription() throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public androidx.test.uiautomator.UiObject getFromParent(androidx.test.uiautomator.UiSelector) throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public String getPackageName() throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public final androidx.test.uiautomator.UiSelector getSelector();
+    method public String getText() throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public android.graphics.Rect getVisibleBounds() throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean isCheckable() throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean isChecked() throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean isClickable() throws androidx.test.uiautomator.UiObjectNotFoundException;
@@ -239,10 +239,10 @@
     method public boolean longClickBottomRight() throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean longClickTopLeft() throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean performMultiPointerGesture(android.view.MotionEvent.PointerCoords![]!...);
-    method public boolean performTwoPointerGesture(android.graphics.Point!, android.graphics.Point!, android.graphics.Point!, android.graphics.Point!, int);
+    method public boolean performTwoPointerGesture(android.graphics.Point, android.graphics.Point, android.graphics.Point, android.graphics.Point, int);
     method public boolean pinchIn(int, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean pinchOut(int, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public boolean setText(String!) throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public boolean setText(String?) throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean swipeDown(int) throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean swipeLeft(int) throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean swipeRight(int) throws androidx.test.uiautomator.UiObjectNotFoundException;
@@ -260,29 +260,29 @@
   public class UiObject2 {
     method public void clear();
     method public void click();
-    method public void click(android.graphics.Point!);
+    method public void click(android.graphics.Point);
     method public void click(long);
-    method public void click(android.graphics.Point!, long);
-    method public <R> R! clickAndWait(androidx.test.uiautomator.EventCondition<R!>!, long);
-    method public <R> R! clickAndWait(android.graphics.Point!, androidx.test.uiautomator.EventCondition<R!>!, long);
-    method public void drag(android.graphics.Point!);
-    method public void drag(android.graphics.Point!, int);
-    method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector!);
-    method public java.util.List<androidx.test.uiautomator.UiObject2!>! findObjects(androidx.test.uiautomator.BySelector!);
-    method public boolean fling(androidx.test.uiautomator.Direction!);
-    method public boolean fling(androidx.test.uiautomator.Direction!, int);
+    method public void click(android.graphics.Point, long);
+    method public <R> R! clickAndWait(androidx.test.uiautomator.EventCondition<R!>, long);
+    method public <R> R! clickAndWait(android.graphics.Point, androidx.test.uiautomator.EventCondition<R!>, long);
+    method public void drag(android.graphics.Point);
+    method public void drag(android.graphics.Point, int);
+    method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector);
+    method public java.util.List<androidx.test.uiautomator.UiObject2!> findObjects(androidx.test.uiautomator.BySelector);
+    method public boolean fling(androidx.test.uiautomator.Direction);
+    method public boolean fling(androidx.test.uiautomator.Direction, int);
     method public String! getApplicationPackage();
     method public int getChildCount();
-    method public java.util.List<androidx.test.uiautomator.UiObject2!>! getChildren();
+    method public java.util.List<androidx.test.uiautomator.UiObject2!> getChildren();
     method public String! getClassName();
     method public String! getContentDescription();
     method public int getDisplayId();
     method public androidx.test.uiautomator.UiObject2! getParent();
     method public String! getResourceName();
     method public String! getText();
-    method public android.graphics.Rect! getVisibleBounds();
-    method public android.graphics.Point! getVisibleCenter();
-    method public boolean hasObject(androidx.test.uiautomator.BySelector!);
+    method public android.graphics.Rect getVisibleBounds();
+    method public android.graphics.Point getVisibleCenter();
+    method public boolean hasObject(androidx.test.uiautomator.BySelector);
     method public boolean isCheckable();
     method public boolean isChecked();
     method public boolean isClickable();
@@ -298,15 +298,15 @@
     method public void pinchOpen(float);
     method public void pinchOpen(float, int);
     method public void recycle();
-    method public boolean scroll(androidx.test.uiautomator.Direction!, float);
-    method public boolean scroll(androidx.test.uiautomator.Direction!, float, int);
+    method public boolean scroll(androidx.test.uiautomator.Direction, float);
+    method public boolean scroll(androidx.test.uiautomator.Direction, float, int);
     method public void setGestureMargin(int);
     method public void setGestureMargins(int, int, int, int);
-    method public void setText(String!);
-    method public void swipe(androidx.test.uiautomator.Direction!, float);
-    method public void swipe(androidx.test.uiautomator.Direction!, float, int);
-    method public <R> R! wait(androidx.test.uiautomator.UiObject2Condition<R!>!, long);
-    method public <R> R! wait(androidx.test.uiautomator.SearchCondition<R!>!, long);
+    method public void setText(String?);
+    method public void swipe(androidx.test.uiautomator.Direction, float);
+    method public void swipe(androidx.test.uiautomator.Direction, float, int);
+    method public <R> R! wait(androidx.test.uiautomator.UiObject2Condition<R!>, long);
+    method public <R> R! wait(androidx.test.uiautomator.SearchCondition<R!>, long);
   }
 
   public abstract class UiObject2Condition<R> {
@@ -350,35 +350,35 @@
 
   public class UiSelector {
     ctor public UiSelector();
-    method public androidx.test.uiautomator.UiSelector! checkable(boolean);
-    method public androidx.test.uiautomator.UiSelector! checked(boolean);
-    method public androidx.test.uiautomator.UiSelector! childSelector(androidx.test.uiautomator.UiSelector!);
-    method public androidx.test.uiautomator.UiSelector! className(String!);
-    method public <T> androidx.test.uiautomator.UiSelector! className(Class<T!>!);
-    method public androidx.test.uiautomator.UiSelector! classNameMatches(String!);
-    method public androidx.test.uiautomator.UiSelector! clickable(boolean);
-    method protected androidx.test.uiautomator.UiSelector! cloneSelector();
-    method public androidx.test.uiautomator.UiSelector! description(String!);
-    method public androidx.test.uiautomator.UiSelector! descriptionContains(String!);
-    method public androidx.test.uiautomator.UiSelector! descriptionMatches(String!);
-    method public androidx.test.uiautomator.UiSelector! descriptionStartsWith(String!);
-    method public androidx.test.uiautomator.UiSelector! enabled(boolean);
-    method public androidx.test.uiautomator.UiSelector! focusable(boolean);
-    method public androidx.test.uiautomator.UiSelector! focused(boolean);
-    method public androidx.test.uiautomator.UiSelector! fromParent(androidx.test.uiautomator.UiSelector!);
-    method public androidx.test.uiautomator.UiSelector! index(int);
-    method public androidx.test.uiautomator.UiSelector! instance(int);
-    method public androidx.test.uiautomator.UiSelector! longClickable(boolean);
-    method public androidx.test.uiautomator.UiSelector! packageName(String!);
-    method public androidx.test.uiautomator.UiSelector! packageNameMatches(String!);
-    method public androidx.test.uiautomator.UiSelector! resourceId(String!);
-    method public androidx.test.uiautomator.UiSelector! resourceIdMatches(String!);
-    method public androidx.test.uiautomator.UiSelector! scrollable(boolean);
-    method public androidx.test.uiautomator.UiSelector! selected(boolean);
-    method public androidx.test.uiautomator.UiSelector! text(String!);
-    method public androidx.test.uiautomator.UiSelector! textContains(String!);
-    method public androidx.test.uiautomator.UiSelector! textMatches(String!);
-    method public androidx.test.uiautomator.UiSelector! textStartsWith(String!);
+    method public androidx.test.uiautomator.UiSelector checkable(boolean);
+    method public androidx.test.uiautomator.UiSelector checked(boolean);
+    method public androidx.test.uiautomator.UiSelector childSelector(androidx.test.uiautomator.UiSelector);
+    method public androidx.test.uiautomator.UiSelector className(String);
+    method public <T> androidx.test.uiautomator.UiSelector className(Class<T!>);
+    method public androidx.test.uiautomator.UiSelector classNameMatches(String);
+    method public androidx.test.uiautomator.UiSelector clickable(boolean);
+    method protected androidx.test.uiautomator.UiSelector cloneSelector();
+    method public androidx.test.uiautomator.UiSelector description(String);
+    method public androidx.test.uiautomator.UiSelector descriptionContains(String);
+    method public androidx.test.uiautomator.UiSelector descriptionMatches(String);
+    method public androidx.test.uiautomator.UiSelector descriptionStartsWith(String);
+    method public androidx.test.uiautomator.UiSelector enabled(boolean);
+    method public androidx.test.uiautomator.UiSelector focusable(boolean);
+    method public androidx.test.uiautomator.UiSelector focused(boolean);
+    method public androidx.test.uiautomator.UiSelector fromParent(androidx.test.uiautomator.UiSelector);
+    method public androidx.test.uiautomator.UiSelector index(int);
+    method public androidx.test.uiautomator.UiSelector instance(int);
+    method public androidx.test.uiautomator.UiSelector longClickable(boolean);
+    method public androidx.test.uiautomator.UiSelector packageName(String);
+    method public androidx.test.uiautomator.UiSelector packageNameMatches(String);
+    method public androidx.test.uiautomator.UiSelector resourceId(String);
+    method public androidx.test.uiautomator.UiSelector resourceIdMatches(String);
+    method public androidx.test.uiautomator.UiSelector scrollable(boolean);
+    method public androidx.test.uiautomator.UiSelector selected(boolean);
+    method public androidx.test.uiautomator.UiSelector text(String);
+    method public androidx.test.uiautomator.UiSelector textContains(String);
+    method public androidx.test.uiautomator.UiSelector textMatches(String);
+    method public androidx.test.uiautomator.UiSelector textStartsWith(String);
   }
 
   public interface UiWatcher {
@@ -386,35 +386,34 @@
   }
 
   public class Until {
-    ctor public Until();
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! checkable(boolean);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! checked(boolean);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! clickable(boolean);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descContains(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descEndsWith(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descEquals(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descMatches(java.util.regex.Pattern!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descMatches(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descStartsWith(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! enabled(boolean);
-    method public static androidx.test.uiautomator.SearchCondition<androidx.test.uiautomator.UiObject2!>! findObject(androidx.test.uiautomator.BySelector!);
-    method public static androidx.test.uiautomator.SearchCondition<java.util.List<androidx.test.uiautomator.UiObject2!>!>! findObjects(androidx.test.uiautomator.BySelector!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! focusable(boolean);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! focused(boolean);
-    method public static androidx.test.uiautomator.SearchCondition<java.lang.Boolean!>! gone(androidx.test.uiautomator.BySelector!);
-    method public static androidx.test.uiautomator.SearchCondition<java.lang.Boolean!>! hasObject(androidx.test.uiautomator.BySelector!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! longClickable(boolean);
-    method public static androidx.test.uiautomator.EventCondition<java.lang.Boolean!>! newWindow();
-    method public static androidx.test.uiautomator.EventCondition<java.lang.Boolean!>! scrollFinished(androidx.test.uiautomator.Direction!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! scrollable(boolean);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! selected(boolean);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textContains(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textEndsWith(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textEquals(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textMatches(java.util.regex.Pattern!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textMatches(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textNotEquals(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textStartsWith(String!);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> checkable(boolean);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> checked(boolean);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> clickable(boolean);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descContains(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descEndsWith(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descEquals(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descMatches(java.util.regex.Pattern);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descMatches(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descStartsWith(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> enabled(boolean);
+    method public static androidx.test.uiautomator.SearchCondition<androidx.test.uiautomator.UiObject2!> findObject(androidx.test.uiautomator.BySelector);
+    method public static androidx.test.uiautomator.SearchCondition<java.util.List<androidx.test.uiautomator.UiObject2!>!> findObjects(androidx.test.uiautomator.BySelector);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> focusable(boolean);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> focused(boolean);
+    method public static androidx.test.uiautomator.SearchCondition<java.lang.Boolean!> gone(androidx.test.uiautomator.BySelector);
+    method public static androidx.test.uiautomator.SearchCondition<java.lang.Boolean!> hasObject(androidx.test.uiautomator.BySelector);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> longClickable(boolean);
+    method public static androidx.test.uiautomator.EventCondition<java.lang.Boolean!> newWindow();
+    method public static androidx.test.uiautomator.EventCondition<java.lang.Boolean!> scrollFinished(androidx.test.uiautomator.Direction);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> scrollable(boolean);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> selected(boolean);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textContains(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textEndsWith(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textEquals(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textMatches(java.util.regex.Pattern);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textMatches(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textNotEquals(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textStartsWith(String);
   }
 
 }
diff --git a/test/uiautomator/uiautomator/api/public_plus_experimental_current.txt b/test/uiautomator/uiautomator/api/public_plus_experimental_current.txt
index 90bec63..ca4de56 100644
--- a/test/uiautomator/uiautomator/api/public_plus_experimental_current.txt
+++ b/test/uiautomator/uiautomator/api/public_plus_experimental_current.txt
@@ -143,32 +143,32 @@
     method public void clearLastTraversedText();
     method public boolean click(int, int);
     method public boolean drag(int, int, int, int, int);
-    method @Deprecated public void dumpWindowHierarchy(String!);
-    method public void dumpWindowHierarchy(java.io.File!) throws java.io.IOException;
-    method public void dumpWindowHierarchy(java.io.OutputStream!) throws java.io.IOException;
-    method public androidx.test.uiautomator.UiObject! findObject(androidx.test.uiautomator.UiSelector!);
-    method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector!);
-    method public java.util.List<androidx.test.uiautomator.UiObject2!>! findObjects(androidx.test.uiautomator.BySelector!);
+    method @Deprecated public void dumpWindowHierarchy(String);
+    method public void dumpWindowHierarchy(java.io.File) throws java.io.IOException;
+    method public void dumpWindowHierarchy(java.io.OutputStream) throws java.io.IOException;
+    method public androidx.test.uiautomator.UiObject findObject(androidx.test.uiautomator.UiSelector);
+    method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector);
+    method public java.util.List<androidx.test.uiautomator.UiObject2!> findObjects(androidx.test.uiautomator.BySelector);
     method public void freezeRotation() throws android.os.RemoteException;
     method @Deprecated public String! getCurrentActivityName();
     method public String! getCurrentPackageName();
     method public int getDisplayHeight();
     method public int getDisplayRotation();
-    method public android.graphics.Point! getDisplaySizeDp();
+    method public android.graphics.Point getDisplaySizeDp();
     method public int getDisplayWidth();
-    method @Deprecated public static androidx.test.uiautomator.UiDevice! getInstance();
-    method public static androidx.test.uiautomator.UiDevice! getInstance(android.app.Instrumentation!);
+    method @Deprecated public static androidx.test.uiautomator.UiDevice getInstance();
+    method public static androidx.test.uiautomator.UiDevice getInstance(android.app.Instrumentation);
     method public String! getLastTraversedText();
     method public String! getLauncherPackageName();
-    method public String! getProductName();
+    method public String getProductName();
     method public boolean hasAnyWatcherTriggered();
-    method public boolean hasObject(androidx.test.uiautomator.BySelector!);
-    method public boolean hasWatcherTriggered(String!);
+    method public boolean hasObject(androidx.test.uiautomator.BySelector);
+    method public boolean hasWatcherTriggered(String?);
     method public boolean isNaturalOrientation();
     method public boolean isScreenOn() throws android.os.RemoteException;
     method public boolean openNotification();
     method public boolean openQuickSettings();
-    method public <R> R! performActionAndWait(Runnable!, androidx.test.uiautomator.EventCondition<R!>!, long);
+    method public <R> R! performActionAndWait(Runnable, androidx.test.uiautomator.EventCondition<R!>, long);
     method public boolean pressBack();
     method public boolean pressDPadCenter();
     method public boolean pressDPadDown();
@@ -183,8 +183,8 @@
     method public boolean pressMenu();
     method public boolean pressRecentApps() throws android.os.RemoteException;
     method public boolean pressSearch();
-    method public void registerWatcher(String!, androidx.test.uiautomator.UiWatcher!);
-    method public void removeWatcher(String!);
+    method public void registerWatcher(String?, androidx.test.uiautomator.UiWatcher?);
+    method public void removeWatcher(String?);
     method public void resetWatcherTriggers();
     method public void runWatchers();
     method public void setCompressedLayoutHeirarchy(boolean);
@@ -193,14 +193,14 @@
     method public void setOrientationRight() throws android.os.RemoteException;
     method public void sleep() throws android.os.RemoteException;
     method public boolean swipe(int, int, int, int, int);
-    method public boolean swipe(android.graphics.Point![]!, int);
-    method public boolean takeScreenshot(java.io.File!);
-    method public boolean takeScreenshot(java.io.File!, float, int);
+    method public boolean swipe(android.graphics.Point![], int);
+    method public boolean takeScreenshot(java.io.File);
+    method public boolean takeScreenshot(java.io.File, float, int);
     method public void unfreezeRotation() throws android.os.RemoteException;
-    method public <R> R! wait(androidx.test.uiautomator.SearchCondition<R!>!, long);
+    method public <R> R! wait(androidx.test.uiautomator.SearchCondition<R!>, long);
     method public void waitForIdle();
     method public void waitForIdle(long);
-    method public boolean waitForWindowUpdate(String!, long);
+    method public boolean waitForWindowUpdate(String?, long);
     method public void wakeUp() throws android.os.RemoteException;
   }
 
@@ -212,20 +212,20 @@
     method public boolean clickAndWaitForNewWindow(long) throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean clickBottomRight() throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean clickTopLeft() throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public boolean dragTo(androidx.test.uiautomator.UiObject!, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public boolean dragTo(androidx.test.uiautomator.UiObject, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean dragTo(int, int, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean exists();
-    method protected android.view.accessibility.AccessibilityNodeInfo! findAccessibilityNodeInfo(long);
-    method public android.graphics.Rect! getBounds() throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public androidx.test.uiautomator.UiObject! getChild(androidx.test.uiautomator.UiSelector!) throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method protected android.view.accessibility.AccessibilityNodeInfo? findAccessibilityNodeInfo(long);
+    method public android.graphics.Rect getBounds() throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public androidx.test.uiautomator.UiObject getChild(androidx.test.uiautomator.UiSelector) throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public int getChildCount() throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public String! getClassName() throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public String! getContentDescription() throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public androidx.test.uiautomator.UiObject! getFromParent(androidx.test.uiautomator.UiSelector!) throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public String! getPackageName() throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public final androidx.test.uiautomator.UiSelector! getSelector();
-    method public String! getText() throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public android.graphics.Rect! getVisibleBounds() throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public String getClassName() throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public String getContentDescription() throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public androidx.test.uiautomator.UiObject getFromParent(androidx.test.uiautomator.UiSelector) throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public String getPackageName() throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public final androidx.test.uiautomator.UiSelector getSelector();
+    method public String getText() throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public android.graphics.Rect getVisibleBounds() throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean isCheckable() throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean isChecked() throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean isClickable() throws androidx.test.uiautomator.UiObjectNotFoundException;
@@ -239,10 +239,10 @@
     method public boolean longClickBottomRight() throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean longClickTopLeft() throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean performMultiPointerGesture(android.view.MotionEvent.PointerCoords![]!...);
-    method public boolean performTwoPointerGesture(android.graphics.Point!, android.graphics.Point!, android.graphics.Point!, android.graphics.Point!, int);
+    method public boolean performTwoPointerGesture(android.graphics.Point, android.graphics.Point, android.graphics.Point, android.graphics.Point, int);
     method public boolean pinchIn(int, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean pinchOut(int, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public boolean setText(String!) throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public boolean setText(String?) throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean swipeDown(int) throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean swipeLeft(int) throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean swipeRight(int) throws androidx.test.uiautomator.UiObjectNotFoundException;
@@ -260,29 +260,29 @@
   public class UiObject2 {
     method public void clear();
     method public void click();
-    method public void click(android.graphics.Point!);
+    method public void click(android.graphics.Point);
     method public void click(long);
-    method public void click(android.graphics.Point!, long);
-    method public <R> R! clickAndWait(androidx.test.uiautomator.EventCondition<R!>!, long);
-    method public <R> R! clickAndWait(android.graphics.Point!, androidx.test.uiautomator.EventCondition<R!>!, long);
-    method public void drag(android.graphics.Point!);
-    method public void drag(android.graphics.Point!, int);
-    method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector!);
-    method public java.util.List<androidx.test.uiautomator.UiObject2!>! findObjects(androidx.test.uiautomator.BySelector!);
-    method public boolean fling(androidx.test.uiautomator.Direction!);
-    method public boolean fling(androidx.test.uiautomator.Direction!, int);
+    method public void click(android.graphics.Point, long);
+    method public <R> R! clickAndWait(androidx.test.uiautomator.EventCondition<R!>, long);
+    method public <R> R! clickAndWait(android.graphics.Point, androidx.test.uiautomator.EventCondition<R!>, long);
+    method public void drag(android.graphics.Point);
+    method public void drag(android.graphics.Point, int);
+    method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector);
+    method public java.util.List<androidx.test.uiautomator.UiObject2!> findObjects(androidx.test.uiautomator.BySelector);
+    method public boolean fling(androidx.test.uiautomator.Direction);
+    method public boolean fling(androidx.test.uiautomator.Direction, int);
     method public String! getApplicationPackage();
     method public int getChildCount();
-    method public java.util.List<androidx.test.uiautomator.UiObject2!>! getChildren();
+    method public java.util.List<androidx.test.uiautomator.UiObject2!> getChildren();
     method public String! getClassName();
     method public String! getContentDescription();
     method public int getDisplayId();
     method public androidx.test.uiautomator.UiObject2! getParent();
     method public String! getResourceName();
     method public String! getText();
-    method public android.graphics.Rect! getVisibleBounds();
-    method public android.graphics.Point! getVisibleCenter();
-    method public boolean hasObject(androidx.test.uiautomator.BySelector!);
+    method public android.graphics.Rect getVisibleBounds();
+    method public android.graphics.Point getVisibleCenter();
+    method public boolean hasObject(androidx.test.uiautomator.BySelector);
     method public boolean isCheckable();
     method public boolean isChecked();
     method public boolean isClickable();
@@ -298,15 +298,15 @@
     method public void pinchOpen(float);
     method public void pinchOpen(float, int);
     method public void recycle();
-    method public boolean scroll(androidx.test.uiautomator.Direction!, float);
-    method public boolean scroll(androidx.test.uiautomator.Direction!, float, int);
+    method public boolean scroll(androidx.test.uiautomator.Direction, float);
+    method public boolean scroll(androidx.test.uiautomator.Direction, float, int);
     method public void setGestureMargin(int);
     method public void setGestureMargins(int, int, int, int);
-    method public void setText(String!);
-    method public void swipe(androidx.test.uiautomator.Direction!, float);
-    method public void swipe(androidx.test.uiautomator.Direction!, float, int);
-    method public <R> R! wait(androidx.test.uiautomator.UiObject2Condition<R!>!, long);
-    method public <R> R! wait(androidx.test.uiautomator.SearchCondition<R!>!, long);
+    method public void setText(String?);
+    method public void swipe(androidx.test.uiautomator.Direction, float);
+    method public void swipe(androidx.test.uiautomator.Direction, float, int);
+    method public <R> R! wait(androidx.test.uiautomator.UiObject2Condition<R!>, long);
+    method public <R> R! wait(androidx.test.uiautomator.SearchCondition<R!>, long);
   }
 
   public abstract class UiObject2Condition<R> {
@@ -350,35 +350,35 @@
 
   public class UiSelector {
     ctor public UiSelector();
-    method public androidx.test.uiautomator.UiSelector! checkable(boolean);
-    method public androidx.test.uiautomator.UiSelector! checked(boolean);
-    method public androidx.test.uiautomator.UiSelector! childSelector(androidx.test.uiautomator.UiSelector!);
-    method public androidx.test.uiautomator.UiSelector! className(String!);
-    method public <T> androidx.test.uiautomator.UiSelector! className(Class<T!>!);
-    method public androidx.test.uiautomator.UiSelector! classNameMatches(String!);
-    method public androidx.test.uiautomator.UiSelector! clickable(boolean);
-    method protected androidx.test.uiautomator.UiSelector! cloneSelector();
-    method public androidx.test.uiautomator.UiSelector! description(String!);
-    method public androidx.test.uiautomator.UiSelector! descriptionContains(String!);
-    method public androidx.test.uiautomator.UiSelector! descriptionMatches(String!);
-    method public androidx.test.uiautomator.UiSelector! descriptionStartsWith(String!);
-    method public androidx.test.uiautomator.UiSelector! enabled(boolean);
-    method public androidx.test.uiautomator.UiSelector! focusable(boolean);
-    method public androidx.test.uiautomator.UiSelector! focused(boolean);
-    method public androidx.test.uiautomator.UiSelector! fromParent(androidx.test.uiautomator.UiSelector!);
-    method public androidx.test.uiautomator.UiSelector! index(int);
-    method public androidx.test.uiautomator.UiSelector! instance(int);
-    method public androidx.test.uiautomator.UiSelector! longClickable(boolean);
-    method public androidx.test.uiautomator.UiSelector! packageName(String!);
-    method public androidx.test.uiautomator.UiSelector! packageNameMatches(String!);
-    method public androidx.test.uiautomator.UiSelector! resourceId(String!);
-    method public androidx.test.uiautomator.UiSelector! resourceIdMatches(String!);
-    method public androidx.test.uiautomator.UiSelector! scrollable(boolean);
-    method public androidx.test.uiautomator.UiSelector! selected(boolean);
-    method public androidx.test.uiautomator.UiSelector! text(String!);
-    method public androidx.test.uiautomator.UiSelector! textContains(String!);
-    method public androidx.test.uiautomator.UiSelector! textMatches(String!);
-    method public androidx.test.uiautomator.UiSelector! textStartsWith(String!);
+    method public androidx.test.uiautomator.UiSelector checkable(boolean);
+    method public androidx.test.uiautomator.UiSelector checked(boolean);
+    method public androidx.test.uiautomator.UiSelector childSelector(androidx.test.uiautomator.UiSelector);
+    method public androidx.test.uiautomator.UiSelector className(String);
+    method public <T> androidx.test.uiautomator.UiSelector className(Class<T!>);
+    method public androidx.test.uiautomator.UiSelector classNameMatches(String);
+    method public androidx.test.uiautomator.UiSelector clickable(boolean);
+    method protected androidx.test.uiautomator.UiSelector cloneSelector();
+    method public androidx.test.uiautomator.UiSelector description(String);
+    method public androidx.test.uiautomator.UiSelector descriptionContains(String);
+    method public androidx.test.uiautomator.UiSelector descriptionMatches(String);
+    method public androidx.test.uiautomator.UiSelector descriptionStartsWith(String);
+    method public androidx.test.uiautomator.UiSelector enabled(boolean);
+    method public androidx.test.uiautomator.UiSelector focusable(boolean);
+    method public androidx.test.uiautomator.UiSelector focused(boolean);
+    method public androidx.test.uiautomator.UiSelector fromParent(androidx.test.uiautomator.UiSelector);
+    method public androidx.test.uiautomator.UiSelector index(int);
+    method public androidx.test.uiautomator.UiSelector instance(int);
+    method public androidx.test.uiautomator.UiSelector longClickable(boolean);
+    method public androidx.test.uiautomator.UiSelector packageName(String);
+    method public androidx.test.uiautomator.UiSelector packageNameMatches(String);
+    method public androidx.test.uiautomator.UiSelector resourceId(String);
+    method public androidx.test.uiautomator.UiSelector resourceIdMatches(String);
+    method public androidx.test.uiautomator.UiSelector scrollable(boolean);
+    method public androidx.test.uiautomator.UiSelector selected(boolean);
+    method public androidx.test.uiautomator.UiSelector text(String);
+    method public androidx.test.uiautomator.UiSelector textContains(String);
+    method public androidx.test.uiautomator.UiSelector textMatches(String);
+    method public androidx.test.uiautomator.UiSelector textStartsWith(String);
   }
 
   public interface UiWatcher {
@@ -386,35 +386,34 @@
   }
 
   public class Until {
-    ctor public Until();
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! checkable(boolean);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! checked(boolean);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! clickable(boolean);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descContains(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descEndsWith(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descEquals(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descMatches(java.util.regex.Pattern!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descMatches(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descStartsWith(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! enabled(boolean);
-    method public static androidx.test.uiautomator.SearchCondition<androidx.test.uiautomator.UiObject2!>! findObject(androidx.test.uiautomator.BySelector!);
-    method public static androidx.test.uiautomator.SearchCondition<java.util.List<androidx.test.uiautomator.UiObject2!>!>! findObjects(androidx.test.uiautomator.BySelector!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! focusable(boolean);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! focused(boolean);
-    method public static androidx.test.uiautomator.SearchCondition<java.lang.Boolean!>! gone(androidx.test.uiautomator.BySelector!);
-    method public static androidx.test.uiautomator.SearchCondition<java.lang.Boolean!>! hasObject(androidx.test.uiautomator.BySelector!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! longClickable(boolean);
-    method public static androidx.test.uiautomator.EventCondition<java.lang.Boolean!>! newWindow();
-    method public static androidx.test.uiautomator.EventCondition<java.lang.Boolean!>! scrollFinished(androidx.test.uiautomator.Direction!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! scrollable(boolean);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! selected(boolean);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textContains(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textEndsWith(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textEquals(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textMatches(java.util.regex.Pattern!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textMatches(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textNotEquals(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textStartsWith(String!);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> checkable(boolean);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> checked(boolean);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> clickable(boolean);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descContains(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descEndsWith(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descEquals(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descMatches(java.util.regex.Pattern);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descMatches(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descStartsWith(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> enabled(boolean);
+    method public static androidx.test.uiautomator.SearchCondition<androidx.test.uiautomator.UiObject2!> findObject(androidx.test.uiautomator.BySelector);
+    method public static androidx.test.uiautomator.SearchCondition<java.util.List<androidx.test.uiautomator.UiObject2!>!> findObjects(androidx.test.uiautomator.BySelector);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> focusable(boolean);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> focused(boolean);
+    method public static androidx.test.uiautomator.SearchCondition<java.lang.Boolean!> gone(androidx.test.uiautomator.BySelector);
+    method public static androidx.test.uiautomator.SearchCondition<java.lang.Boolean!> hasObject(androidx.test.uiautomator.BySelector);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> longClickable(boolean);
+    method public static androidx.test.uiautomator.EventCondition<java.lang.Boolean!> newWindow();
+    method public static androidx.test.uiautomator.EventCondition<java.lang.Boolean!> scrollFinished(androidx.test.uiautomator.Direction);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> scrollable(boolean);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> selected(boolean);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textContains(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textEndsWith(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textEquals(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textMatches(java.util.regex.Pattern);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textMatches(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textNotEquals(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textStartsWith(String);
   }
 
 }
diff --git a/test/uiautomator/uiautomator/api/restricted_current.txt b/test/uiautomator/uiautomator/api/restricted_current.txt
index 90bec63..ca4de56 100644
--- a/test/uiautomator/uiautomator/api/restricted_current.txt
+++ b/test/uiautomator/uiautomator/api/restricted_current.txt
@@ -143,32 +143,32 @@
     method public void clearLastTraversedText();
     method public boolean click(int, int);
     method public boolean drag(int, int, int, int, int);
-    method @Deprecated public void dumpWindowHierarchy(String!);
-    method public void dumpWindowHierarchy(java.io.File!) throws java.io.IOException;
-    method public void dumpWindowHierarchy(java.io.OutputStream!) throws java.io.IOException;
-    method public androidx.test.uiautomator.UiObject! findObject(androidx.test.uiautomator.UiSelector!);
-    method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector!);
-    method public java.util.List<androidx.test.uiautomator.UiObject2!>! findObjects(androidx.test.uiautomator.BySelector!);
+    method @Deprecated public void dumpWindowHierarchy(String);
+    method public void dumpWindowHierarchy(java.io.File) throws java.io.IOException;
+    method public void dumpWindowHierarchy(java.io.OutputStream) throws java.io.IOException;
+    method public androidx.test.uiautomator.UiObject findObject(androidx.test.uiautomator.UiSelector);
+    method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector);
+    method public java.util.List<androidx.test.uiautomator.UiObject2!> findObjects(androidx.test.uiautomator.BySelector);
     method public void freezeRotation() throws android.os.RemoteException;
     method @Deprecated public String! getCurrentActivityName();
     method public String! getCurrentPackageName();
     method public int getDisplayHeight();
     method public int getDisplayRotation();
-    method public android.graphics.Point! getDisplaySizeDp();
+    method public android.graphics.Point getDisplaySizeDp();
     method public int getDisplayWidth();
-    method @Deprecated public static androidx.test.uiautomator.UiDevice! getInstance();
-    method public static androidx.test.uiautomator.UiDevice! getInstance(android.app.Instrumentation!);
+    method @Deprecated public static androidx.test.uiautomator.UiDevice getInstance();
+    method public static androidx.test.uiautomator.UiDevice getInstance(android.app.Instrumentation);
     method public String! getLastTraversedText();
     method public String! getLauncherPackageName();
-    method public String! getProductName();
+    method public String getProductName();
     method public boolean hasAnyWatcherTriggered();
-    method public boolean hasObject(androidx.test.uiautomator.BySelector!);
-    method public boolean hasWatcherTriggered(String!);
+    method public boolean hasObject(androidx.test.uiautomator.BySelector);
+    method public boolean hasWatcherTriggered(String?);
     method public boolean isNaturalOrientation();
     method public boolean isScreenOn() throws android.os.RemoteException;
     method public boolean openNotification();
     method public boolean openQuickSettings();
-    method public <R> R! performActionAndWait(Runnable!, androidx.test.uiautomator.EventCondition<R!>!, long);
+    method public <R> R! performActionAndWait(Runnable, androidx.test.uiautomator.EventCondition<R!>, long);
     method public boolean pressBack();
     method public boolean pressDPadCenter();
     method public boolean pressDPadDown();
@@ -183,8 +183,8 @@
     method public boolean pressMenu();
     method public boolean pressRecentApps() throws android.os.RemoteException;
     method public boolean pressSearch();
-    method public void registerWatcher(String!, androidx.test.uiautomator.UiWatcher!);
-    method public void removeWatcher(String!);
+    method public void registerWatcher(String?, androidx.test.uiautomator.UiWatcher?);
+    method public void removeWatcher(String?);
     method public void resetWatcherTriggers();
     method public void runWatchers();
     method public void setCompressedLayoutHeirarchy(boolean);
@@ -193,14 +193,14 @@
     method public void setOrientationRight() throws android.os.RemoteException;
     method public void sleep() throws android.os.RemoteException;
     method public boolean swipe(int, int, int, int, int);
-    method public boolean swipe(android.graphics.Point![]!, int);
-    method public boolean takeScreenshot(java.io.File!);
-    method public boolean takeScreenshot(java.io.File!, float, int);
+    method public boolean swipe(android.graphics.Point![], int);
+    method public boolean takeScreenshot(java.io.File);
+    method public boolean takeScreenshot(java.io.File, float, int);
     method public void unfreezeRotation() throws android.os.RemoteException;
-    method public <R> R! wait(androidx.test.uiautomator.SearchCondition<R!>!, long);
+    method public <R> R! wait(androidx.test.uiautomator.SearchCondition<R!>, long);
     method public void waitForIdle();
     method public void waitForIdle(long);
-    method public boolean waitForWindowUpdate(String!, long);
+    method public boolean waitForWindowUpdate(String?, long);
     method public void wakeUp() throws android.os.RemoteException;
   }
 
@@ -212,20 +212,20 @@
     method public boolean clickAndWaitForNewWindow(long) throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean clickBottomRight() throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean clickTopLeft() throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public boolean dragTo(androidx.test.uiautomator.UiObject!, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public boolean dragTo(androidx.test.uiautomator.UiObject, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean dragTo(int, int, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean exists();
-    method protected android.view.accessibility.AccessibilityNodeInfo! findAccessibilityNodeInfo(long);
-    method public android.graphics.Rect! getBounds() throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public androidx.test.uiautomator.UiObject! getChild(androidx.test.uiautomator.UiSelector!) throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method protected android.view.accessibility.AccessibilityNodeInfo? findAccessibilityNodeInfo(long);
+    method public android.graphics.Rect getBounds() throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public androidx.test.uiautomator.UiObject getChild(androidx.test.uiautomator.UiSelector) throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public int getChildCount() throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public String! getClassName() throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public String! getContentDescription() throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public androidx.test.uiautomator.UiObject! getFromParent(androidx.test.uiautomator.UiSelector!) throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public String! getPackageName() throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public final androidx.test.uiautomator.UiSelector! getSelector();
-    method public String! getText() throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public android.graphics.Rect! getVisibleBounds() throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public String getClassName() throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public String getContentDescription() throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public androidx.test.uiautomator.UiObject getFromParent(androidx.test.uiautomator.UiSelector) throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public String getPackageName() throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public final androidx.test.uiautomator.UiSelector getSelector();
+    method public String getText() throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public android.graphics.Rect getVisibleBounds() throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean isCheckable() throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean isChecked() throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean isClickable() throws androidx.test.uiautomator.UiObjectNotFoundException;
@@ -239,10 +239,10 @@
     method public boolean longClickBottomRight() throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean longClickTopLeft() throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean performMultiPointerGesture(android.view.MotionEvent.PointerCoords![]!...);
-    method public boolean performTwoPointerGesture(android.graphics.Point!, android.graphics.Point!, android.graphics.Point!, android.graphics.Point!, int);
+    method public boolean performTwoPointerGesture(android.graphics.Point, android.graphics.Point, android.graphics.Point, android.graphics.Point, int);
     method public boolean pinchIn(int, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean pinchOut(int, int) throws androidx.test.uiautomator.UiObjectNotFoundException;
-    method public boolean setText(String!) throws androidx.test.uiautomator.UiObjectNotFoundException;
+    method public boolean setText(String?) throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean swipeDown(int) throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean swipeLeft(int) throws androidx.test.uiautomator.UiObjectNotFoundException;
     method public boolean swipeRight(int) throws androidx.test.uiautomator.UiObjectNotFoundException;
@@ -260,29 +260,29 @@
   public class UiObject2 {
     method public void clear();
     method public void click();
-    method public void click(android.graphics.Point!);
+    method public void click(android.graphics.Point);
     method public void click(long);
-    method public void click(android.graphics.Point!, long);
-    method public <R> R! clickAndWait(androidx.test.uiautomator.EventCondition<R!>!, long);
-    method public <R> R! clickAndWait(android.graphics.Point!, androidx.test.uiautomator.EventCondition<R!>!, long);
-    method public void drag(android.graphics.Point!);
-    method public void drag(android.graphics.Point!, int);
-    method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector!);
-    method public java.util.List<androidx.test.uiautomator.UiObject2!>! findObjects(androidx.test.uiautomator.BySelector!);
-    method public boolean fling(androidx.test.uiautomator.Direction!);
-    method public boolean fling(androidx.test.uiautomator.Direction!, int);
+    method public void click(android.graphics.Point, long);
+    method public <R> R! clickAndWait(androidx.test.uiautomator.EventCondition<R!>, long);
+    method public <R> R! clickAndWait(android.graphics.Point, androidx.test.uiautomator.EventCondition<R!>, long);
+    method public void drag(android.graphics.Point);
+    method public void drag(android.graphics.Point, int);
+    method public androidx.test.uiautomator.UiObject2! findObject(androidx.test.uiautomator.BySelector);
+    method public java.util.List<androidx.test.uiautomator.UiObject2!> findObjects(androidx.test.uiautomator.BySelector);
+    method public boolean fling(androidx.test.uiautomator.Direction);
+    method public boolean fling(androidx.test.uiautomator.Direction, int);
     method public String! getApplicationPackage();
     method public int getChildCount();
-    method public java.util.List<androidx.test.uiautomator.UiObject2!>! getChildren();
+    method public java.util.List<androidx.test.uiautomator.UiObject2!> getChildren();
     method public String! getClassName();
     method public String! getContentDescription();
     method public int getDisplayId();
     method public androidx.test.uiautomator.UiObject2! getParent();
     method public String! getResourceName();
     method public String! getText();
-    method public android.graphics.Rect! getVisibleBounds();
-    method public android.graphics.Point! getVisibleCenter();
-    method public boolean hasObject(androidx.test.uiautomator.BySelector!);
+    method public android.graphics.Rect getVisibleBounds();
+    method public android.graphics.Point getVisibleCenter();
+    method public boolean hasObject(androidx.test.uiautomator.BySelector);
     method public boolean isCheckable();
     method public boolean isChecked();
     method public boolean isClickable();
@@ -298,15 +298,15 @@
     method public void pinchOpen(float);
     method public void pinchOpen(float, int);
     method public void recycle();
-    method public boolean scroll(androidx.test.uiautomator.Direction!, float);
-    method public boolean scroll(androidx.test.uiautomator.Direction!, float, int);
+    method public boolean scroll(androidx.test.uiautomator.Direction, float);
+    method public boolean scroll(androidx.test.uiautomator.Direction, float, int);
     method public void setGestureMargin(int);
     method public void setGestureMargins(int, int, int, int);
-    method public void setText(String!);
-    method public void swipe(androidx.test.uiautomator.Direction!, float);
-    method public void swipe(androidx.test.uiautomator.Direction!, float, int);
-    method public <R> R! wait(androidx.test.uiautomator.UiObject2Condition<R!>!, long);
-    method public <R> R! wait(androidx.test.uiautomator.SearchCondition<R!>!, long);
+    method public void setText(String?);
+    method public void swipe(androidx.test.uiautomator.Direction, float);
+    method public void swipe(androidx.test.uiautomator.Direction, float, int);
+    method public <R> R! wait(androidx.test.uiautomator.UiObject2Condition<R!>, long);
+    method public <R> R! wait(androidx.test.uiautomator.SearchCondition<R!>, long);
   }
 
   public abstract class UiObject2Condition<R> {
@@ -350,35 +350,35 @@
 
   public class UiSelector {
     ctor public UiSelector();
-    method public androidx.test.uiautomator.UiSelector! checkable(boolean);
-    method public androidx.test.uiautomator.UiSelector! checked(boolean);
-    method public androidx.test.uiautomator.UiSelector! childSelector(androidx.test.uiautomator.UiSelector!);
-    method public androidx.test.uiautomator.UiSelector! className(String!);
-    method public <T> androidx.test.uiautomator.UiSelector! className(Class<T!>!);
-    method public androidx.test.uiautomator.UiSelector! classNameMatches(String!);
-    method public androidx.test.uiautomator.UiSelector! clickable(boolean);
-    method protected androidx.test.uiautomator.UiSelector! cloneSelector();
-    method public androidx.test.uiautomator.UiSelector! description(String!);
-    method public androidx.test.uiautomator.UiSelector! descriptionContains(String!);
-    method public androidx.test.uiautomator.UiSelector! descriptionMatches(String!);
-    method public androidx.test.uiautomator.UiSelector! descriptionStartsWith(String!);
-    method public androidx.test.uiautomator.UiSelector! enabled(boolean);
-    method public androidx.test.uiautomator.UiSelector! focusable(boolean);
-    method public androidx.test.uiautomator.UiSelector! focused(boolean);
-    method public androidx.test.uiautomator.UiSelector! fromParent(androidx.test.uiautomator.UiSelector!);
-    method public androidx.test.uiautomator.UiSelector! index(int);
-    method public androidx.test.uiautomator.UiSelector! instance(int);
-    method public androidx.test.uiautomator.UiSelector! longClickable(boolean);
-    method public androidx.test.uiautomator.UiSelector! packageName(String!);
-    method public androidx.test.uiautomator.UiSelector! packageNameMatches(String!);
-    method public androidx.test.uiautomator.UiSelector! resourceId(String!);
-    method public androidx.test.uiautomator.UiSelector! resourceIdMatches(String!);
-    method public androidx.test.uiautomator.UiSelector! scrollable(boolean);
-    method public androidx.test.uiautomator.UiSelector! selected(boolean);
-    method public androidx.test.uiautomator.UiSelector! text(String!);
-    method public androidx.test.uiautomator.UiSelector! textContains(String!);
-    method public androidx.test.uiautomator.UiSelector! textMatches(String!);
-    method public androidx.test.uiautomator.UiSelector! textStartsWith(String!);
+    method public androidx.test.uiautomator.UiSelector checkable(boolean);
+    method public androidx.test.uiautomator.UiSelector checked(boolean);
+    method public androidx.test.uiautomator.UiSelector childSelector(androidx.test.uiautomator.UiSelector);
+    method public androidx.test.uiautomator.UiSelector className(String);
+    method public <T> androidx.test.uiautomator.UiSelector className(Class<T!>);
+    method public androidx.test.uiautomator.UiSelector classNameMatches(String);
+    method public androidx.test.uiautomator.UiSelector clickable(boolean);
+    method protected androidx.test.uiautomator.UiSelector cloneSelector();
+    method public androidx.test.uiautomator.UiSelector description(String);
+    method public androidx.test.uiautomator.UiSelector descriptionContains(String);
+    method public androidx.test.uiautomator.UiSelector descriptionMatches(String);
+    method public androidx.test.uiautomator.UiSelector descriptionStartsWith(String);
+    method public androidx.test.uiautomator.UiSelector enabled(boolean);
+    method public androidx.test.uiautomator.UiSelector focusable(boolean);
+    method public androidx.test.uiautomator.UiSelector focused(boolean);
+    method public androidx.test.uiautomator.UiSelector fromParent(androidx.test.uiautomator.UiSelector);
+    method public androidx.test.uiautomator.UiSelector index(int);
+    method public androidx.test.uiautomator.UiSelector instance(int);
+    method public androidx.test.uiautomator.UiSelector longClickable(boolean);
+    method public androidx.test.uiautomator.UiSelector packageName(String);
+    method public androidx.test.uiautomator.UiSelector packageNameMatches(String);
+    method public androidx.test.uiautomator.UiSelector resourceId(String);
+    method public androidx.test.uiautomator.UiSelector resourceIdMatches(String);
+    method public androidx.test.uiautomator.UiSelector scrollable(boolean);
+    method public androidx.test.uiautomator.UiSelector selected(boolean);
+    method public androidx.test.uiautomator.UiSelector text(String);
+    method public androidx.test.uiautomator.UiSelector textContains(String);
+    method public androidx.test.uiautomator.UiSelector textMatches(String);
+    method public androidx.test.uiautomator.UiSelector textStartsWith(String);
   }
 
   public interface UiWatcher {
@@ -386,35 +386,34 @@
   }
 
   public class Until {
-    ctor public Until();
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! checkable(boolean);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! checked(boolean);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! clickable(boolean);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descContains(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descEndsWith(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descEquals(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descMatches(java.util.regex.Pattern!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descMatches(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! descStartsWith(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! enabled(boolean);
-    method public static androidx.test.uiautomator.SearchCondition<androidx.test.uiautomator.UiObject2!>! findObject(androidx.test.uiautomator.BySelector!);
-    method public static androidx.test.uiautomator.SearchCondition<java.util.List<androidx.test.uiautomator.UiObject2!>!>! findObjects(androidx.test.uiautomator.BySelector!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! focusable(boolean);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! focused(boolean);
-    method public static androidx.test.uiautomator.SearchCondition<java.lang.Boolean!>! gone(androidx.test.uiautomator.BySelector!);
-    method public static androidx.test.uiautomator.SearchCondition<java.lang.Boolean!>! hasObject(androidx.test.uiautomator.BySelector!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! longClickable(boolean);
-    method public static androidx.test.uiautomator.EventCondition<java.lang.Boolean!>! newWindow();
-    method public static androidx.test.uiautomator.EventCondition<java.lang.Boolean!>! scrollFinished(androidx.test.uiautomator.Direction!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! scrollable(boolean);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! selected(boolean);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textContains(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textEndsWith(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textEquals(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textMatches(java.util.regex.Pattern!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textMatches(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textNotEquals(String!);
-    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!>! textStartsWith(String!);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> checkable(boolean);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> checked(boolean);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> clickable(boolean);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descContains(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descEndsWith(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descEquals(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descMatches(java.util.regex.Pattern);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descMatches(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> descStartsWith(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> enabled(boolean);
+    method public static androidx.test.uiautomator.SearchCondition<androidx.test.uiautomator.UiObject2!> findObject(androidx.test.uiautomator.BySelector);
+    method public static androidx.test.uiautomator.SearchCondition<java.util.List<androidx.test.uiautomator.UiObject2!>!> findObjects(androidx.test.uiautomator.BySelector);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> focusable(boolean);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> focused(boolean);
+    method public static androidx.test.uiautomator.SearchCondition<java.lang.Boolean!> gone(androidx.test.uiautomator.BySelector);
+    method public static androidx.test.uiautomator.SearchCondition<java.lang.Boolean!> hasObject(androidx.test.uiautomator.BySelector);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> longClickable(boolean);
+    method public static androidx.test.uiautomator.EventCondition<java.lang.Boolean!> newWindow();
+    method public static androidx.test.uiautomator.EventCondition<java.lang.Boolean!> scrollFinished(androidx.test.uiautomator.Direction);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> scrollable(boolean);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> selected(boolean);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textContains(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textEndsWith(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textEquals(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textMatches(java.util.regex.Pattern);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textMatches(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textNotEquals(String);
+    method public static androidx.test.uiautomator.UiObject2Condition<java.lang.Boolean!> textStartsWith(String);
   }
 
 }
diff --git a/test/uiautomator/uiautomator/lint-baseline.xml b/test/uiautomator/uiautomator/lint-baseline.xml
index 89c3bf6..4207262 100644
--- a/test/uiautomator/uiautomator/lint-baseline.xml
+++ b/test/uiautomator/uiautomator/lint-baseline.xml
@@ -11,19 +11,10 @@
     </issue>
 
     <issue
-        id="PrivateConstructorForUtilityClass"
-        message="Utility class is missing private constructor"
-        errorLine1="public class Until {"
-        errorLine2="             ~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
         id="LambdaLast"
         message="Functional interface parameters (such as parameter 1, &quot;action&quot;, in androidx.test.uiautomator.UiDevice.performActionAndWait) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions"
-        errorLine1="    public &lt;R> R performActionAndWait(Runnable action, EventCondition&lt;R> condition, long timeout) {"
-        errorLine2="                                                                                    ~~~~~~~~~~~~">
+        errorLine1="            @NonNull EventCondition&lt;R> condition, long timeout) {"
+        errorLine2="                                                  ~~~~~~~~~~~~">
         <location
             file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
     </issue>
@@ -49,357 +40,6 @@
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiObject findObject(UiSelector selector) {"
-        errorLine2="           ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiObject findObject(UiSelector selector) {"
-        errorLine2="                               ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public boolean hasObject(BySelector selector) {"
-        errorLine2="                             ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiObject2 findObject(BySelector selector) {"
-        errorLine2="           ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiObject2 findObject(BySelector selector) {"
-        errorLine2="                                ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public List&lt;UiObject2> findObjects(BySelector selector) {"
-        errorLine2="           ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public List&lt;UiObject2> findObjects(BySelector selector) {"
-        errorLine2="                                       ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public &lt;R> R wait(SearchCondition&lt;R> condition, long timeout) {"
-        errorLine2="                      ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public &lt;R> R performActionAndWait(Runnable action, EventCondition&lt;R> condition, long timeout) {"
-        errorLine2="                                      ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public &lt;R> R performActionAndWait(Runnable action, EventCondition&lt;R> condition, long timeout) {"
-        errorLine2="                                                       ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiDevice getInstance(Instrumentation instrumentation) {"
-        errorLine2="                  ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiDevice getInstance(Instrumentation instrumentation) {"
-        errorLine2="                                       ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public Point getDisplaySizeDp() {"
-        errorLine2="           ~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public String getProductName() {"
-        errorLine2="           ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public String getLastTraversedText() {"
-        errorLine2="           ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public boolean swipe(Point[] segments, int segmentSteps) {"
-        errorLine2="                         ~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public String getCurrentPackageName() {"
-        errorLine2="           ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void registerWatcher(String name, UiWatcher watcher) {"
-        errorLine2="                                ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void registerWatcher(String name, UiWatcher watcher) {"
-        errorLine2="                                             ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void removeWatcher(String name) {"
-        errorLine2="                              ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public boolean hasWatcherTriggered(String watcherName) {"
-        errorLine2="                                       ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void dumpWindowHierarchy(File dest) throws IOException {"
-        errorLine2="                                    ~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void dumpWindowHierarchy(OutputStream out) throws IOException {"
-        errorLine2="                                    ~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public boolean waitForWindowUpdate(final String packageName, long timeout) {"
-        errorLine2="                                             ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public boolean takeScreenshot(File storePath) {"
-        errorLine2="                                  ~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public boolean takeScreenshot(File storePath, float scale, int quality) {"
-        errorLine2="                                  ~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public String getLauncherPackageName() {"
-        errorLine2="           ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public String executeShellCommand(String cmd) throws IOException {"
-        errorLine2="           ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public String executeShellCommand(String cmd) throws IOException {"
-        errorLine2="                                      ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiDevice.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public final UiSelector getSelector() {"
-        errorLine2="                 ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiObject getChild(UiSelector selector) throws UiObjectNotFoundException {"
-        errorLine2="           ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiObject getChild(UiSelector selector) throws UiObjectNotFoundException {"
-        errorLine2="                             ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiObject getFromParent(UiSelector selector) throws UiObjectNotFoundException {"
-        errorLine2="           ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiObject getFromParent(UiSelector selector) throws UiObjectNotFoundException {"
-        errorLine2="                                  ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    protected AccessibilityNodeInfo findAccessibilityNodeInfo(long timeout) {"
-        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public boolean dragTo(UiObject destObj, int steps) throws UiObjectNotFoundException {"
-        errorLine2="                          ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public String getText() throws UiObjectNotFoundException {"
-        errorLine2="           ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public String getClassName() throws UiObjectNotFoundException {"
-        errorLine2="           ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public String getContentDescription() throws UiObjectNotFoundException {"
-        errorLine2="           ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
         errorLine1="    public void legacySetText(String text) throws UiObjectNotFoundException {"
         errorLine2="                              ~~~~~~">
         <location
@@ -409,1189 +49,10 @@
     <issue
         id="UnknownNullness"
         message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public boolean setText(String text) throws UiObjectNotFoundException {"
-        errorLine2="                           ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public String getPackageName() throws UiObjectNotFoundException {"
-        errorLine2="           ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public Rect getVisibleBounds() throws UiObjectNotFoundException {"
-        errorLine2="           ~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public Rect getBounds() throws UiObjectNotFoundException {"
-        errorLine2="           ~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public boolean performTwoPointerGesture(Point startPoint1, Point startPoint2, Point endPoint1,"
-        errorLine2="                                            ~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public boolean performTwoPointerGesture(Point startPoint1, Point startPoint2, Point endPoint1,"
-        errorLine2="                                                               ~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public boolean performTwoPointerGesture(Point startPoint1, Point startPoint2, Point endPoint1,"
-        errorLine2="                                                                                  ~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="            Point endPoint2, int steps) {"
-        errorLine2="            ~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public boolean performMultiPointerGesture(PointerCoords[] ...touches) {"
-        errorLine2="                                              ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public &lt;R> R wait(UiObject2Condition&lt;R> condition, long timeout) {"
-        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public &lt;R> R wait(SearchCondition&lt;R> condition, long timeout) {"
-        errorLine2="                      ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiObject2 getParent() {"
-        errorLine2="           ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public List&lt;UiObject2> getChildren() {"
-        errorLine2="           ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public boolean hasObject(BySelector selector) {"
-        errorLine2="                             ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiObject2 findObject(BySelector selector) {"
-        errorLine2="           ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiObject2 findObject(BySelector selector) {"
-        errorLine2="                                ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public List&lt;UiObject2> findObjects(BySelector selector) {"
-        errorLine2="           ~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public List&lt;UiObject2> findObjects(BySelector selector) {"
-        errorLine2="                                       ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public Rect getVisibleBounds() {"
-        errorLine2="           ~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public Point getVisibleCenter() {"
-        errorLine2="           ~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public String getClassName() {"
-        errorLine2="           ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public String getContentDescription() {"
-        errorLine2="           ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public String getApplicationPackage() {"
-        errorLine2="           ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public String getResourceName() {"
-        errorLine2="           ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public String getText() {"
-        errorLine2="           ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void click(Point point) {"
-        errorLine2="                      ~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void click(Point point, long duration) {"
-        errorLine2="                      ~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public &lt;R> R clickAndWait(EventCondition&lt;R> condition, long timeout) {"
-        errorLine2="                              ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public &lt;R> R clickAndWait(Point point, EventCondition&lt;R> condition, long timeout) {"
-        errorLine2="                              ~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public &lt;R> R clickAndWait(Point point, EventCondition&lt;R> condition, long timeout) {"
-        errorLine2="                                           ~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void drag(Point dest) {"
-        errorLine2="                     ~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void drag(Point dest, int speed) {"
-        errorLine2="                     ~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void swipe(Direction direction, float percent) {"
-        errorLine2="                      ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void swipe(Direction direction, float percent, int speed) {"
-        errorLine2="                      ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public boolean scroll(final Direction direction, final float percent) {"
-        errorLine2="                                ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public boolean scroll(Direction direction, float percent, final int speed) {"
-        errorLine2="                          ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public boolean fling(final Direction direction) {"
-        errorLine2="                               ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public boolean fling(final Direction direction, final int speed) {"
-        errorLine2="                               ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
         errorLine1="    public void legacySetText(String text) {"
         errorLine2="                              ~~~~~~">
         <location
             file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
     </issue>
 
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public void setText(String text) {"
-        errorLine2="                        ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiObject2.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    protected UiSelector cloneSelector() {"
-        errorLine2="              ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector text(String text) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector text(String text) {"
-        errorLine2="                           ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector textMatches(String regex) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector textMatches(String regex) {"
-        errorLine2="                                  ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector textStartsWith(String text) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector textStartsWith(String text) {"
-        errorLine2="                                     ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector textContains(String text) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector textContains(String text) {"
-        errorLine2="                                   ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector className(String className) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector className(String className) {"
-        errorLine2="                                ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector classNameMatches(String regex) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector classNameMatches(String regex) {"
-        errorLine2="                                       ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public &lt;T> UiSelector className(Class&lt;T> type) {"
-        errorLine2="               ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public &lt;T> UiSelector className(Class&lt;T> type) {"
-        errorLine2="                                    ~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector description(String desc) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector description(String desc) {"
-        errorLine2="                                  ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector descriptionMatches(String regex) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector descriptionMatches(String regex) {"
-        errorLine2="                                         ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector descriptionStartsWith(String desc) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector descriptionStartsWith(String desc) {"
-        errorLine2="                                            ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector descriptionContains(String desc) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector descriptionContains(String desc) {"
-        errorLine2="                                          ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector resourceId(String id) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector resourceId(String id) {"
-        errorLine2="                                 ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector resourceIdMatches(String regex) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector resourceIdMatches(String regex) {"
-        errorLine2="                                        ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector index(final int index) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector instance(final int instance) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector enabled(boolean val) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector focused(boolean val) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector focusable(boolean val) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector scrollable(boolean val) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector selected(boolean val) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector checked(boolean val) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector clickable(boolean val) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector checkable(boolean val) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector longClickable(boolean val) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector childSelector(UiSelector selector) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector childSelector(UiSelector selector) {"
-        errorLine2="                                    ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector fromParent(UiSelector selector) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector fromParent(UiSelector selector) {"
-        errorLine2="                                 ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector packageName(String name) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector packageName(String name) {"
-        errorLine2="                                  ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector packageNameMatches(String regex) {"
-        errorLine2="           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public UiSelector packageNameMatches(String regex) {"
-        errorLine2="                                         ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/UiSelector.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static SearchCondition&lt;Boolean> gone(final BySelector selector) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static SearchCondition&lt;Boolean> gone(final BySelector selector) {"
-        errorLine2="                                                      ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static SearchCondition&lt;Boolean> hasObject(final BySelector selector) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static SearchCondition&lt;Boolean> hasObject(final BySelector selector) {"
-        errorLine2="                                                           ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static SearchCondition&lt;UiObject2> findObject(final BySelector selector) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static SearchCondition&lt;UiObject2> findObject(final BySelector selector) {"
-        errorLine2="                                                              ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static SearchCondition&lt;List&lt;UiObject2>> findObjects(final BySelector selector) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static SearchCondition&lt;List&lt;UiObject2>> findObjects(final BySelector selector) {"
-        errorLine2="                                                                     ~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> checkable(final boolean isCheckable) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> checked(final boolean isChecked) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> clickable(final boolean isClickable) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> enabled(final boolean isEnabled) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> focusable(final boolean isFocusable) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> focused(final boolean isFocused) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> longClickable(final boolean isLongClickable) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> scrollable(final boolean isScrollable) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> selected(final boolean isSelected) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> descMatches(final Pattern regex) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> descMatches(final Pattern regex) {"
-        errorLine2="                                                                ~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> descMatches(String regex) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> descMatches(String regex) {"
-        errorLine2="                                                          ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> descEquals(String contentDescription) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> descEquals(String contentDescription) {"
-        errorLine2="                                                         ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> descContains(String substring) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> descContains(String substring) {"
-        errorLine2="                                                           ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> descStartsWith(String substring) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> descStartsWith(String substring) {"
-        errorLine2="                                                             ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> descEndsWith(String substring) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> descEndsWith(String substring) {"
-        errorLine2="                                                           ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> textMatches(final Pattern regex) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> textMatches(final Pattern regex) {"
-        errorLine2="                                                                ~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> textMatches(String regex) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> textMatches(String regex) {"
-        errorLine2="                                                          ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> textNotEquals(final String text) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> textNotEquals(final String text) {"
-        errorLine2="                                                                  ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> textEquals(String text) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> textEquals(String text) {"
-        errorLine2="                                                         ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> textContains(String substring) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> textContains(String substring) {"
-        errorLine2="                                                           ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> textStartsWith(String substring) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> textStartsWith(String substring) {"
-        errorLine2="                                                             ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> textEndsWith(String substring) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static UiObject2Condition&lt;Boolean> textEndsWith(String substring) {"
-        errorLine2="                                                           ~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static EventCondition&lt;Boolean> newWindow() {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static EventCondition&lt;Boolean> scrollFinished(final Direction direction) {"
-        errorLine2="                  ~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
-    <issue
-        id="UnknownNullness"
-        message="Unknown nullability; explicitly declare as `@Nullable` or `@NonNull` to improve Kotlin interoperability; see https://developer.android.com/kotlin/interop#nullability_annotations"
-        errorLine1="    public static EventCondition&lt;Boolean> scrollFinished(final Direction direction) {"
-        errorLine2="                                                               ~~~~~~~~~">
-        <location
-            file="src/main/java/androidx/test/uiautomator/Until.java"/>
-    </issue>
-
 </issues>
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Searchable.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Searchable.java
index 447a0f7..e796744 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Searchable.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Searchable.java
@@ -16,17 +16,20 @@
 
 package androidx.test.uiautomator;
 
+import androidx.annotation.NonNull;
+
 import java.util.List;
 
 /** The Searchable interface represents an object that can be searched for matching UI elements. */
 interface Searchable {
 
     /** Returns whether there is a match for the given {@code selector} criteria. */
-    public boolean hasObject(BySelector selector);
+    boolean hasObject(@NonNull BySelector selector);
 
     /** Returns the first object to match the {@code selector} criteria. */
-    public UiObject2 findObject(BySelector selector);
+    UiObject2 findObject(@NonNull BySelector selector);
 
     /** Returns all objects that match the {@code selector} criteria. */
-    public List<UiObject2> findObjects(BySelector selector);
+    @NonNull
+    List<UiObject2> findObjects(@NonNull BySelector selector);
 }
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
index 5f43d03..becd801 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiDevice.java
@@ -17,6 +17,7 @@
 package androidx.test.uiautomator;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.SuppressLint;
 import android.app.Instrumentation;
 import android.app.Service;
 import android.app.UiAutomation;
@@ -44,6 +45,8 @@
 import android.view.accessibility.AccessibilityWindowInfo;
 
 import androidx.annotation.DoNotInline;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 
 import java.io.BufferedOutputStream;
@@ -125,13 +128,14 @@
      * @param selector
      * @return UiObject object
      */
-    public UiObject findObject(UiSelector selector) {
+    @NonNull
+    public UiObject findObject(@NonNull UiSelector selector) {
         return new UiObject(this, selector);
     }
 
     /** Returns whether there is a match for the given {@code selector} criteria. */
     @Override
-    public boolean hasObject(BySelector selector) {
+    public boolean hasObject(@NonNull BySelector selector) {
         AccessibilityNodeInfo node = ByMatcher.findMatch(this, selector, getWindowRoots());
         if (node != null) {
             node.recycle();
@@ -145,14 +149,16 @@
      * or null if no matching objects are found.
      */
     @Override
-    public UiObject2 findObject(BySelector selector) {
+    @SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
+    public UiObject2 findObject(@NonNull BySelector selector) {
         AccessibilityNodeInfo node = ByMatcher.findMatch(this, selector, getWindowRoots());
         return node != null ? new UiObject2(this, selector, node) : null;
     }
 
     /** Returns all objects that match the {@code selector} criteria. */
     @Override
-    public List<UiObject2> findObjects(BySelector selector) {
+    @NonNull
+    public List<UiObject2> findObjects(@NonNull BySelector selector) {
         List<UiObject2> ret = new ArrayList<UiObject2>();
         for (AccessibilityNodeInfo node : ByMatcher.findMatches(this, selector, getWindowRoots())) {
             ret.add(new UiObject2(this, selector, node));
@@ -171,7 +177,7 @@
      * was not met before the {@code timeout}.
      */
     @SuppressWarnings("TypeNameShadowing")
-    public <R> R wait(SearchCondition<R> condition, long timeout) {
+    public <R> R wait(@NonNull SearchCondition<R> condition, long timeout) {
         return mWaitMixin.wait(condition, timeout);
     }
 
@@ -184,8 +190,8 @@
      * @return The final result returned by the condition.
      */
     @SuppressWarnings("TypeNameShadowing")
-    public <R> R performActionAndWait(Runnable action, EventCondition<R> condition, long timeout) {
-
+    public <R> R performActionAndWait(@NonNull Runnable action,
+            @NonNull EventCondition<R> condition, long timeout) {
         AccessibilityEvent event = null;
         try {
             event = getUiAutomation().executeAndWaitForEvent(
@@ -246,6 +252,7 @@
      * @since API Level 16
      */
     @Deprecated
+    @NonNull
     public static UiDevice getInstance() {
         if (sInstance == null) {
             throw new IllegalStateException("UiDevice singleton not initialized");
@@ -258,7 +265,8 @@
      *
      * @return UiDevice instance
      */
-    public static UiDevice getInstance(Instrumentation instrumentation) {
+    @NonNull
+    public static UiDevice getInstance(@NonNull Instrumentation instrumentation) {
         if (sInstance == null) {
             sInstance = new UiDevice(instrumentation);
         }
@@ -273,6 +281,7 @@
      *
      * @return a Point containing the display size in dp
      */
+    @NonNull
     public Point getDisplaySizeDp() {
         Tracer.trace();
         Display display = getDefaultDisplay();
@@ -296,6 +305,7 @@
      * @return product name of the device
      * @since API Level 17
      */
+    @NonNull
     public String getProductName() {
         Tracer.trace();
         return Build.PRODUCT;
@@ -316,6 +326,7 @@
      * @return text of the last traversal event, else return an empty string
      * @since API Level 16
      */
+    @SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
     public String getLastTraversedText() {
         Tracer.trace();
         return getQueryController().getLastTraversedText();
@@ -608,7 +619,7 @@
      * @return true on success
      * @since API Level 16
      */
-    public boolean swipe(Point[] segments, int segmentSteps) {
+    public boolean swipe(@NonNull Point[] segments, int segmentSteps) {
         Tracer.trace(segments, segmentSteps);
         return getInteractionController().swipe(segments, segmentSteps);
     }
@@ -640,6 +651,7 @@
      * @since API Level 16
      */
     @Deprecated
+    @SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
     public String getCurrentActivityName() {
         Tracer.trace();
         return getQueryController().getCurrentActivityName();
@@ -650,6 +662,7 @@
      * @return String name of package
      * @since API Level 16
      */
+    @SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
     public String getCurrentPackageName() {
         Tracer.trace();
         return getQueryController().getCurrentPackageName();
@@ -663,7 +676,7 @@
      * @param watcher {@link UiWatcher}
      * @since API Level 16
      */
-    public void registerWatcher(String name, UiWatcher watcher) {
+    public void registerWatcher(@Nullable String name, @Nullable UiWatcher watcher) {
         Tracer.trace(name, watcher);
         if (mInWatcherContext) {
             throw new IllegalStateException("Cannot register new watcher from within another");
@@ -678,7 +691,7 @@
      * @param name used to register the UiWatcher
      * @since API Level 16
      */
-    public void removeWatcher(String name) {
+    public void removeWatcher(@Nullable String name) {
         Tracer.trace(name);
         if (mInWatcherContext) {
             throw new IllegalStateException("Cannot remove a watcher from within another");
@@ -737,7 +750,7 @@
      * @return true if triggered else false
      * @since API Level 16
      */
-    public boolean hasWatcherTriggered(String watcherName) {
+    public boolean hasWatcherTriggered(@Nullable String watcherName) {
         Tracer.trace(watcherName);
         return mWatchersTriggers.contains(watcherName);
     }
@@ -908,7 +921,7 @@
      *     {@link UiDevice#dumpWindowHierarchy(OutputStream)} instead.
      */
     @Deprecated
-    public void dumpWindowHierarchy(String fileName) {
+    public void dumpWindowHierarchy(@NonNull String fileName) {
         Tracer.trace(fileName);
 
         File dumpFile = new File(fileName);
@@ -928,7 +941,7 @@
      * @param dest The file in which to store the window hierarchy information.
      * @throws IOException
      */
-    public void dumpWindowHierarchy(File dest) throws IOException {
+    public void dumpWindowHierarchy(@NonNull File dest) throws IOException {
         try (OutputStream stream = new BufferedOutputStream(new FileOutputStream(dest))) {
             dumpWindowHierarchy(stream);
         }
@@ -940,7 +953,7 @@
      * @param out The output stream that the window hierarchy information is written to.
      * @throws IOException
      */
-    public void dumpWindowHierarchy(OutputStream out) throws IOException {
+    public void dumpWindowHierarchy(@NonNull OutputStream out) throws IOException {
         AccessibilityNodeInfoDumper.dumpWindowHierarchy(this, out);
     }
 
@@ -958,7 +971,7 @@
      *         window does not have the specified package name
      * @since API Level 16
      */
-    public boolean waitForWindowUpdate(final String packageName, long timeout) {
+    public boolean waitForWindowUpdate(@Nullable String packageName, long timeout) {
         Tracer.trace(packageName, timeout);
         if (packageName != null) {
             if (!packageName.equals(getCurrentPackageName())) {
@@ -1001,7 +1014,7 @@
      * @return true if screen shot is created successfully, false otherwise
      * @since API Level 17
      */
-    public boolean takeScreenshot(File storePath) {
+    public boolean takeScreenshot(@NonNull File storePath) {
         Tracer.trace(storePath);
         return takeScreenshot(storePath, 1.0f, 90);
     }
@@ -1017,7 +1030,7 @@
      * @return true if screen shot is created successfully, false otherwise
      * @since API Level 17
      */
-    public boolean takeScreenshot(File storePath, float scale, int quality) {
+    public boolean takeScreenshot(@NonNull File storePath, float scale, int quality) {
         Tracer.trace(storePath, scale, quality);
         Bitmap screenshot = getUiAutomation().takeScreenshot();
         if (screenshot == null) {
@@ -1043,6 +1056,7 @@
      *
      * @return package name of the default launcher
      */
+    @SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
     public String getLauncherPackageName() {
         Intent intent = new Intent(Intent.ACTION_MAIN);
         intent.addCategory(Intent.CATEGORY_HOME);
@@ -1064,7 +1078,8 @@
      * @hide
      */
     @RequiresApi(21)
-    public String executeShellCommand(String cmd) throws IOException {
+    @NonNull
+    public String executeShellCommand(@NonNull String cmd) throws IOException {
         try (ParcelFileDescriptor pfd = Api21Impl.executeShellCommand(getUiAutomation(), cmd);
              FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
             byte[] buf = new byte[512];
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject.java
index 22c1a0f..fdf7cb3 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject.java
@@ -26,6 +26,9 @@
 import android.view.MotionEvent.PointerCoords;
 import android.view.accessibility.AccessibilityNodeInfo;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 /**
  * A UiObject is a representation of a view. It is not in any way directly bound to a
  * view as an object reference. A UiObject contains information to help it
@@ -102,6 +105,7 @@
      * @return {@link UiSelector}
      * @since API Level 16
      */
+    @NonNull
     public final UiSelector getSelector() {
         Tracer.trace();
         if (mUiSelector == null) {
@@ -141,7 +145,8 @@
      * @return a new UiObject representing the child view
      * @since API Level 16
      */
-    public UiObject getChild(UiSelector selector) throws UiObjectNotFoundException {
+    @NonNull
+    public UiObject getChild(@NonNull UiSelector selector) throws UiObjectNotFoundException {
         Tracer.trace(selector);
         return new UiObject(getSelector().childSelector(selector));
     }
@@ -155,7 +160,8 @@
      * @throws UiObjectNotFoundException
      * @since API Level 16
      */
-    public UiObject getFromParent(UiSelector selector) throws UiObjectNotFoundException {
+    @NonNull
+    public UiObject getFromParent(@NonNull UiSelector selector) throws UiObjectNotFoundException {
         Tracer.trace(selector);
         return new UiObject(getSelector().fromParent(selector));
     }
@@ -184,6 +190,7 @@
      * @return AccessibilityNodeInfo if found else null
      * @since API Level 16
      */
+    @Nullable
     protected AccessibilityNodeInfo findAccessibilityNodeInfo(long timeout) {
         AccessibilityNodeInfo node = null;
         long startMills = SystemClock.uptimeMillis();
@@ -216,7 +223,7 @@
      * @throws UiObjectNotFoundException
      * @since API Level 18
      */
-    public boolean dragTo(UiObject destObj, int steps) throws UiObjectNotFoundException {
+    public boolean dragTo(@NonNull UiObject destObj, int steps) throws UiObjectNotFoundException {
         Rect srcRect = getVisibleBounds();
         Rect dstRect = destObj.getVisibleBounds();
         return getInteractionController().swipe(srcRect.centerX(), srcRect.centerY(),
@@ -560,6 +567,7 @@
      * @throws UiObjectNotFoundException if no match could be found
      * @since API Level 16
      */
+    @NonNull
     public String getText() throws UiObjectNotFoundException {
         Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
@@ -578,6 +586,7 @@
      * @throws UiObjectNotFoundException if no match was found
      * @since API Level 18
      */
+    @NonNull
     public String getClassName() throws UiObjectNotFoundException {
         Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
@@ -596,6 +605,7 @@
      * @throws UiObjectNotFoundException
      * @since API Level 16
      */
+    @NonNull
     public String getContentDescription() throws UiObjectNotFoundException {
         Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
@@ -655,7 +665,7 @@
      * @throws UiObjectNotFoundException
      * @since API Level 16
      */
-    public boolean setText(String text) throws UiObjectNotFoundException {
+    public boolean setText(@Nullable String text) throws UiObjectNotFoundException {
         // per framework convention, setText with null means clearing it
         if (text == null) {
             text = "";
@@ -870,6 +880,7 @@
      * @throws UiObjectNotFoundException
      * @since API Level 16
      */
+    @NonNull
     public String getPackageName() throws UiObjectNotFoundException {
         Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
@@ -891,6 +902,7 @@
      *
      * @since API Level 17
      */
+    @NonNull
     public Rect getVisibleBounds() throws UiObjectNotFoundException {
         Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
@@ -907,6 +919,7 @@
      * @throws UiObjectNotFoundException
      * @since API Level 16
      */
+    @NonNull
     public Rect getBounds() throws UiObjectNotFoundException {
         Tracer.trace();
         AccessibilityNodeInfo node = findAccessibilityNodeInfo(mConfig.getWaitForSelectorTimeout());
@@ -1080,8 +1093,8 @@
      *         <code>false</code> otherwise
      * @since API Level 18
      */
-    public boolean performTwoPointerGesture(Point startPoint1, Point startPoint2, Point endPoint1,
-            Point endPoint2, int steps) {
+    public boolean performTwoPointerGesture(@NonNull Point startPoint1, @NonNull Point startPoint2,
+            @NonNull Point endPoint1, @NonNull Point endPoint2, int steps) {
 
         // avoid a divide by zero
         if(steps == 0)
@@ -1164,7 +1177,7 @@
      *         <code>false</code> otherwise
      * @since API Level 18
      */
-    public boolean performMultiPointerGesture(PointerCoords[] ...touches) {
+    public boolean performMultiPointerGesture(@NonNull PointerCoords[]... touches) {
         return getInteractionController().performMultiPointerGesture(touches);
     }
 }
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java
index 19ede49..cafe068 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiObject2.java
@@ -16,6 +16,7 @@
 
 package androidx.test.uiautomator;
 
+import android.annotation.SuppressLint;
 import android.app.Service;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -32,6 +33,8 @@
 import android.view.accessibility.AccessibilityWindowInfo;
 
 import androidx.annotation.DoNotInline;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 
 import java.util.ArrayList;
@@ -154,7 +157,7 @@
      * was not met before the {@code timeout}.
      */
     @SuppressWarnings("TypeNameShadowing")
-    public <R> R wait(UiObject2Condition<R> condition, long timeout) {
+    public <R> R wait(@NonNull UiObject2Condition<R> condition, long timeout) {
         return mWaitMixin.wait(condition, timeout);
     }
 
@@ -167,13 +170,14 @@
      * was not met before the {@code timeout}.
      */
     @SuppressWarnings("TypeNameShadowing")
-    public <R> R wait(SearchCondition<R> condition, long timeout) {
+    public <R> R wait(@NonNull SearchCondition<R> condition, long timeout) {
         return mWaitMixin.wait(condition, timeout);
     }
 
     // Search functions
 
     /** Returns this object's parent, or null if it has no parent. */
+    @SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
     public UiObject2 getParent() {
         AccessibilityNodeInfo parent = getAccessibilityNodeInfo().getParent();
         return parent != null ? new UiObject2(getDevice(), mSelector, parent) : null;
@@ -185,13 +189,14 @@
     }
 
     /** Returns a collection of the child elements directly under this object. */
+    @NonNull
     public List<UiObject2> getChildren() {
         return findObjects(By.depth(1));
     }
 
     /** Returns whether there is a match for the given criteria under this object. */
     @Override
-    public boolean hasObject(BySelector selector) {
+    public boolean hasObject(@NonNull BySelector selector) {
         AccessibilityNodeInfo node =
                 ByMatcher.findMatch(getDevice(), selector, getAccessibilityNodeInfo());
         if (node != null) {
@@ -206,7 +211,8 @@
      * or null if no matching objects are found.
      */
     @Override
-    public UiObject2 findObject(BySelector selector) {
+    @SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
+    public UiObject2 findObject(@NonNull BySelector selector) {
         AccessibilityNodeInfo node =
                 ByMatcher.findMatch(getDevice(), selector, getAccessibilityNodeInfo());
         return node != null ? new UiObject2(getDevice(), selector, node) : null;
@@ -214,7 +220,8 @@
 
     /** Searches all elements under this object and returns all objects that match the criteria. */
     @Override
-    public List<UiObject2> findObjects(BySelector selector) {
+    @NonNull
+    public List<UiObject2> findObjects(@NonNull BySelector selector) {
         List<UiObject2> ret = new ArrayList<UiObject2>();
         for (AccessibilityNodeInfo node :
                 ByMatcher.findMatches(getDevice(), selector, getAccessibilityNodeInfo())) {
@@ -239,6 +246,7 @@
     }
 
     /** Returns the visible bounds of this object in screen coordinates. */
+    @NonNull
     public Rect getVisibleBounds() {
         return getVisibleBounds(getAccessibilityNodeInfo());
     }
@@ -323,6 +331,7 @@
     }
 
     /** Returns a point in the center of the visible bounds of this object. */
+    @NonNull
     public Point getVisibleCenter() {
         Rect bounds = getVisibleBounds();
         return new Point(bounds.centerX(), bounds.centerY());
@@ -332,30 +341,35 @@
      * Returns the class name of the underlying {@link android.view.View} represented by this
      * object.
      */
+    @SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
     public String getClassName() {
         CharSequence chars = getAccessibilityNodeInfo().getClassName();
         return chars != null ? chars.toString() : null;
     }
 
     /** Returns the content description for this object. */
+    @SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
     public String getContentDescription() {
         CharSequence chars = getAccessibilityNodeInfo().getContentDescription();
         return chars != null ? chars.toString() : null;
     }
 
     /** Returns the package name of the app that this object belongs to. */
+    @SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
     public String getApplicationPackage() {
         CharSequence chars = getAccessibilityNodeInfo().getPackageName();
         return chars != null ? chars.toString() : null;
     }
 
     /** Returns the fully qualified resource name for this object's id. */
+    @SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
     public String getResourceName() {
         CharSequence chars = getAccessibilityNodeInfo().getViewIdResourceName();
         return chars != null ? chars.toString() : null;
     }
 
     /** Returns the text value for this object. */
+    @SuppressLint("UnknownNullness") // Avoid unnecessary null checks from nullable testing APIs.
     public String getText() {
         CharSequence chars = getAccessibilityNodeInfo().getText();
         return chars != null ? chars.toString() : null;
@@ -426,7 +440,7 @@
      * @param point The point to click. Clipped to the visible bounds of this object with gesture
      *        margins removed.
      */
-    public void click(Point point) {
+    public void click(@NonNull Point point) {
         clipToGestureBounds(point);
         Log.v(TAG, String.format("click(point=%s)", point));
         mGestureController.performGesture(mGestures.click(point, getDisplayId()));
@@ -448,7 +462,7 @@
      *        margins removed.
      * @param duration The duration in milliseconds to press {@code point}.
      */
-    public void click(Point point, long duration) {
+    public void click(@NonNull Point point, long duration) {
         clipToGestureBounds(point);
         Log.v(TAG, String.format("click(point=%s,duration=%d)", point, duration));
         mGestureController.performGesture(mGestures.click(point, duration, getDisplayId()));
@@ -456,7 +470,7 @@
 
     /** Clicks on this object, and waits for the given condition to become true. */
     @SuppressWarnings("TypeNameShadowing")
-    public <R> R clickAndWait(EventCondition<R> condition, long timeout) {
+    public <R> R clickAndWait(@NonNull EventCondition<R> condition, long timeout) {
         Log.v(TAG, String.format("clickAndWait(center=%s,timeout=%d)",
                 getVisibleCenter(), timeout));
         return mGestureController.performGestureAndWait(condition, timeout,
@@ -473,7 +487,8 @@
      * @param timeout The duration in milliseconds waiting for {@code condition} before timed out.
      */
     @SuppressWarnings("TypeNameShadowing")
-    public <R> R clickAndWait(Point point, EventCondition<R> condition, long timeout) {
+    public <R> R clickAndWait(@NonNull Point point, @NonNull EventCondition<R> condition,
+            long timeout) {
         clipToGestureBounds(point);
         Log.v(TAG, String.format("clickAndWait(point=%s,timeout=%d)", point, timeout));
         return mGestureController.performGestureAndWait(condition, timeout,
@@ -485,7 +500,7 @@
      *
      * @param dest The end point that this object should be dragged to.
      */
-    public void drag(Point dest) {
+    public void drag(@NonNull Point dest) {
         drag(dest, (int)(DEFAULT_DRAG_SPEED * mDisplayDensity));
     }
 
@@ -495,7 +510,7 @@
      * @param dest The end point that this object should be dragged to.
      * @param speed The speed at which to perform this gesture in pixels per second.
      */
-    public void drag(Point dest, int speed) {
+    public void drag(@NonNull Point dest, int speed) {
         if (speed < 0) {
             throw new IllegalArgumentException("Speed cannot be negative");
         }
@@ -576,7 +591,7 @@
      * @param direction The direction in which to swipe.
      * @param percent The length of the swipe as a percentage of this object's size.
      */
-    public void swipe(Direction direction, float percent) {
+    public void swipe(@NonNull Direction direction, float percent) {
         swipe(direction, percent, (int)(DEFAULT_SWIPE_SPEED * mDisplayDensity));
     }
 
@@ -587,7 +602,7 @@
      * @param percent The length of the swipe as a percentage of this object's size.
      * @param speed The speed at which to perform this gesture in pixels per second.
      */
-    public void swipe(Direction direction, float percent, int speed) {
+    public void swipe(@NonNull Direction direction, float percent, int speed) {
         if (percent < 0.0f || percent > 1.0f) {
             throw new IllegalArgumentException("Percent must be between 0.0f and 1.0f");
         }
@@ -608,7 +623,7 @@
      * @param percent The distance to scroll as a percentage of this object's visible size.
      * @return Whether the object can still scroll in the given direction.
      */
-    public boolean scroll(final Direction direction, final float percent) {
+    public boolean scroll(@NonNull Direction direction, final float percent) {
         return scroll(direction, percent, (int)(DEFAULT_SCROLL_SPEED * mDisplayDensity));
     }
 
@@ -620,7 +635,7 @@
      * @param speed The speed at which to perform this gesture in pixels per second.
      * @return Whether the object can still scroll in the given direction.
      */
-    public boolean scroll(Direction direction, float percent, final int speed) {
+    public boolean scroll(@NonNull Direction direction, float percent, final int speed) {
         if (percent < 0.0f) {
             throw new IllegalArgumentException("Percent must be greater than 0.0f");
         }
@@ -656,7 +671,7 @@
      * @param direction The direction in which to fling.
      * @return Whether the object can still scroll in the given direction.
      */
-    public boolean fling(final Direction direction) {
+    public boolean fling(@NonNull Direction direction) {
         return fling(direction, (int)(DEFAULT_FLING_SPEED * mDisplayDensity));
     }
 
@@ -667,7 +682,7 @@
      * @param speed The speed at which to perform this gesture in pixels per second.
      * @return Whether the object can still scroll in the given direction.
      */
-    public boolean fling(final Direction direction, final int speed) {
+    public boolean fling(@NonNull Direction direction, final int speed) {
         ViewConfiguration vc = ViewConfiguration.get(getDevice().getUiContext());
         if (speed < vc.getScaledMinimumFlingVelocity()) {
             throw new IllegalArgumentException("Speed is less than the minimum fling velocity");
@@ -720,7 +735,7 @@
     }
 
     /** Sets the text content if this object is an editable field. */
-    public void setText(String text) {
+    public void setText(@Nullable String text) {
         AccessibilityNodeInfo node = getAccessibilityNodeInfo();
 
         // Per framework convention, setText(null) means clearing it
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiSelector.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiSelector.java
index 8987c07..a09266a 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiSelector.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/UiSelector.java
@@ -19,6 +19,8 @@
 import android.util.SparseArray;
 import android.view.accessibility.AccessibilityNodeInfo;
 
+import androidx.annotation.NonNull;
+
 import java.util.regex.Pattern;
 
 /**
@@ -77,6 +79,7 @@
     /**
      * @since API Level 17
      */
+    @NonNull
     protected UiSelector cloneSelector() {
         UiSelector ret = new UiSelector();
         ret.mSelectorAttributes = mSelectorAttributes.clone();
@@ -112,7 +115,8 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 16
      */
-    public UiSelector text(String text) {
+    @NonNull
+    public UiSelector text(@NonNull String text) {
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -130,7 +134,8 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 17
      */
-    public UiSelector textMatches(String regex) {
+    @NonNull
+    public UiSelector textMatches(@NonNull String regex) {
         if (regex == null) {
             throw new IllegalArgumentException("regex cannot be null");
         }
@@ -147,7 +152,8 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 16
      */
-    public UiSelector textStartsWith(String text) {
+    @NonNull
+    public UiSelector textStartsWith(@NonNull String text) {
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -164,7 +170,8 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 16
      */
-    public UiSelector textContains(String text) {
+    @NonNull
+    public UiSelector textContains(@NonNull String text) {
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
@@ -179,7 +186,8 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 16
      */
-    public UiSelector className(String className) {
+    @NonNull
+    public UiSelector className(@NonNull String className) {
         if (className == null) {
             throw new IllegalArgumentException("className cannot be null");
         }
@@ -194,7 +202,8 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 17
      */
-    public UiSelector classNameMatches(String regex) {
+    @NonNull
+    public UiSelector classNameMatches(@NonNull String regex) {
         if (regex == null) {
             throw new IllegalArgumentException("regex cannot be null");
         }
@@ -209,7 +218,8 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 17
      */
-    public <T> UiSelector className(Class<T> type) {
+    @NonNull
+    public <T> UiSelector className(@NonNull Class<T> type) {
         if (type == null) {
             throw new IllegalArgumentException("type cannot be null");
         }
@@ -233,7 +243,8 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 16
      */
-    public UiSelector description(String desc) {
+    @NonNull
+    public UiSelector description(@NonNull String desc) {
         if (desc == null) {
             throw new IllegalArgumentException("desc cannot be null");
         }
@@ -255,7 +266,8 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 17
      */
-    public UiSelector descriptionMatches(String regex) {
+    @NonNull
+    public UiSelector descriptionMatches(@NonNull String regex) {
         if (regex == null) {
             throw new IllegalArgumentException("regex cannot be null");
         }
@@ -279,7 +291,8 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 16
      */
-    public UiSelector descriptionStartsWith(String desc) {
+    @NonNull
+    public UiSelector descriptionStartsWith(@NonNull String desc) {
         if (desc == null) {
             throw new IllegalArgumentException("desc cannot be null");
         }
@@ -303,7 +316,8 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 16
      */
-    public UiSelector descriptionContains(String desc) {
+    @NonNull
+    public UiSelector descriptionContains(@NonNull String desc) {
         if (desc == null) {
             throw new IllegalArgumentException("desc cannot be null");
         }
@@ -317,7 +331,8 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 18
      */
-    public UiSelector resourceId(String id) {
+    @NonNull
+    public UiSelector resourceId(@NonNull String id) {
         if (id == null) {
             throw new IllegalArgumentException("id cannot be null");
         }
@@ -332,7 +347,8 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 18
      */
-    public UiSelector resourceIdMatches(String regex) {
+    @NonNull
+    public UiSelector resourceIdMatches(@NonNull String regex) {
         if (regex == null) {
             throw new IllegalArgumentException("regex cannot be null");
         }
@@ -353,6 +369,7 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 16
      */
+    @NonNull
     public UiSelector index(final int index) {
         return buildSelector(SELECTOR_INDEX, index);
     }
@@ -379,6 +396,7 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 16
      */
+    @NonNull
     public UiSelector instance(final int instance) {
         return buildSelector(SELECTOR_INSTANCE, instance);
     }
@@ -398,6 +416,7 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 16
      */
+    @NonNull
     public UiSelector enabled(boolean val) {
         return buildSelector(SELECTOR_ENABLED, val);
     }
@@ -417,6 +436,7 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 16
      */
+    @NonNull
     public UiSelector focused(boolean val) {
         return buildSelector(SELECTOR_FOCUSED, val);
     }
@@ -436,6 +456,7 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 16
      */
+    @NonNull
     public UiSelector focusable(boolean val) {
         return buildSelector(SELECTOR_FOCUSABLE, val);
     }
@@ -455,6 +476,7 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 16
      */
+    @NonNull
     public UiSelector scrollable(boolean val) {
         return buildSelector(SELECTOR_SCROLLABLE, val);
     }
@@ -475,6 +497,7 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 16
      */
+    @NonNull
     public UiSelector selected(boolean val) {
         return buildSelector(SELECTOR_SELECTED, val);
     }
@@ -495,6 +518,7 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 16
      */
+    @NonNull
     public UiSelector checked(boolean val) {
         return buildSelector(SELECTOR_CHECKED, val);
     }
@@ -514,6 +538,7 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 16
      */
+    @NonNull
     public UiSelector clickable(boolean val) {
         return buildSelector(SELECTOR_CLICKABLE, val);
     }
@@ -533,6 +558,7 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 18
      */
+    @NonNull
     public UiSelector checkable(boolean val) {
         return buildSelector(SELECTOR_CHECKABLE, val);
     }
@@ -552,6 +578,7 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 17
      */
+    @NonNull
     public UiSelector longClickable(boolean val) {
         return buildSelector(SELECTOR_LONG_CLICKABLE, val);
     }
@@ -566,7 +593,8 @@
      * @return UiSelector with this added search criterion
      * @since API Level 16
      */
-    public UiSelector childSelector(UiSelector selector) {
+    @NonNull
+    public UiSelector childSelector(@NonNull UiSelector selector) {
         if (selector == null) {
             throw new IllegalArgumentException("selector cannot be null");
         }
@@ -592,7 +620,8 @@
      * @return UiSelector with this added search criterion
      * @since API Level 16
      */
-    public UiSelector fromParent(UiSelector selector) {
+    @NonNull
+    public UiSelector fromParent(@NonNull UiSelector selector) {
         if (selector == null) {
             throw new IllegalArgumentException("selector cannot be null");
         }
@@ -607,7 +636,8 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 16
      */
-    public UiSelector packageName(String name) {
+    @NonNull
+    public UiSelector packageName(@NonNull String name) {
         if (name == null) {
             throw new IllegalArgumentException("name cannot be null");
         }
@@ -622,7 +652,8 @@
      * @return UiSelector with the specified search criteria
      * @since API Level 17
      */
-    public UiSelector packageNameMatches(String regex) {
+    @NonNull
+    public UiSelector packageNameMatches(@NonNull String regex) {
         if (regex == null) {
             throw new IllegalArgumentException("regex cannot be null");
         }
diff --git a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Until.java b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Until.java
index b1eaebf..1a0d622 100644
--- a/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Until.java
+++ b/test/uiautomator/uiautomator/src/main/java/androidx/test/uiautomator/Until.java
@@ -18,6 +18,8 @@
 
 import android.view.accessibility.AccessibilityEvent;
 
+import androidx.annotation.NonNull;
+
 import java.util.List;
 import java.util.regex.Pattern;
 
@@ -26,13 +28,17 @@
  */
 public class Until {
 
+    private Until() {
+    }
+
     // Search conditions
 
     /**
      * Returns a {@link SearchCondition} that is satisfied when no elements matching the selector
      * can be found.
      */
-    public static SearchCondition<Boolean> gone(final BySelector selector) {
+    @NonNull
+    public static SearchCondition<Boolean> gone(@NonNull BySelector selector) {
         return new SearchCondition<Boolean>() {
             @Override
             Boolean apply(Searchable container) {
@@ -45,7 +51,8 @@
      * Returns a {@link SearchCondition} that is satisfied when at least one element matching the
      * selector can be found.
      */
-    public static SearchCondition<Boolean> hasObject(final BySelector selector) {
+    @NonNull
+    public static SearchCondition<Boolean> hasObject(@NonNull BySelector selector) {
         return new SearchCondition<Boolean>() {
             @Override
             Boolean apply(Searchable container) {
@@ -58,7 +65,8 @@
      * Returns a {@link SearchCondition} that is satisfied when at least one element matching the
      * selector can be found. The condition will return the first matching element.
      */
-    public static SearchCondition<UiObject2> findObject(final BySelector selector) {
+    @NonNull
+    public static SearchCondition<UiObject2> findObject(@NonNull BySelector selector) {
         return new SearchCondition<UiObject2>() {
             @Override
             UiObject2 apply(Searchable container) {
@@ -71,7 +79,8 @@
      * Returns a {@link SearchCondition} that is satisfied when at least one element matching the
      * selector can be found. The condition will return all matching elements.
      */
-    public static SearchCondition<List<UiObject2>> findObjects(final BySelector selector) {
+    @NonNull
+    public static SearchCondition<List<UiObject2>> findObjects(@NonNull BySelector selector) {
         return new SearchCondition<List<UiObject2>>() {
             @Override
             List<UiObject2> apply(Searchable container) {
@@ -89,6 +98,7 @@
      *
      * @param isCheckable Whether the object should be checkable to satisfy this condition.
      */
+    @NonNull
     public static UiObject2Condition<Boolean> checkable(final boolean isCheckable) {
         return new UiObject2Condition<Boolean>() {
             @Override
@@ -103,6 +113,7 @@
      *
      * @param isChecked Whether the object should be checked to satisfy this condition.
      */
+    @NonNull
     public static UiObject2Condition<Boolean> checked(final boolean isChecked) {
         return new UiObject2Condition<Boolean>() {
             @Override
@@ -117,6 +128,7 @@
      *
      * @param isClickable Whether the object should be clickable to satisfy this condition.
      */
+    @NonNull
     public static UiObject2Condition<Boolean> clickable(final boolean isClickable) {
         return new UiObject2Condition<Boolean>() {
             @Override
@@ -131,6 +143,7 @@
      *
      * @param isEnabled Whether the object should be enabled to satisfy this condition.
      */
+    @NonNull
     public static UiObject2Condition<Boolean> enabled(final boolean isEnabled) {
         return new UiObject2Condition<Boolean>() {
             @Override
@@ -145,6 +158,7 @@
      *
      * @param isFocusable Whether the object should be focusable to satisfy this condition.
      */
+    @NonNull
     public static UiObject2Condition<Boolean> focusable(final boolean isFocusable) {
         return new UiObject2Condition<Boolean>() {
             @Override
@@ -159,6 +173,7 @@
      *
      * @param isFocused Whether the object should be focused to satisfy this condition.
      */
+    @NonNull
     public static UiObject2Condition<Boolean> focused(final boolean isFocused) {
         return new UiObject2Condition<Boolean>() {
             @Override
@@ -173,6 +188,7 @@
      *
      * @param isLongClickable Whether the object should be long clickable to satisfy this condition.
      */
+    @NonNull
     public static UiObject2Condition<Boolean> longClickable(final boolean isLongClickable) {
         return new UiObject2Condition<Boolean>() {
             @Override
@@ -187,6 +203,7 @@
      *
      * @param isScrollable Whether the object should be scrollable to satisfy this condition.
      */
+    @NonNull
     public static UiObject2Condition<Boolean> scrollable(final boolean isScrollable) {
         return new UiObject2Condition<Boolean>() {
             @Override
@@ -201,6 +218,7 @@
      *
      * @param isSelected Whether the object should be selected to satisfy this condition.
      */
+    @NonNull
     public static UiObject2Condition<Boolean> selected(final boolean isSelected) {
         return new UiObject2Condition<Boolean>() {
             @Override
@@ -214,7 +232,8 @@
      * Returns a condition that is satisfied when the object's content description matches the given
      * regex.
      */
-    public static UiObject2Condition<Boolean> descMatches(final Pattern regex) {
+    @NonNull
+    public static UiObject2Condition<Boolean> descMatches(@NonNull Pattern regex) {
         return new UiObject2Condition<Boolean>() {
             @Override
             Boolean apply(UiObject2 object) {
@@ -228,7 +247,8 @@
      * Returns a condition that is satisfied when the object's content description matches the given
      * regex.
      */
-    public static UiObject2Condition<Boolean> descMatches(String regex) {
+    @NonNull
+    public static UiObject2Condition<Boolean> descMatches(@NonNull String regex) {
         return descMatches(Pattern.compile(regex));
     }
 
@@ -236,7 +256,8 @@
      * Returns a condition that is satisfied when the object's content description exactly matches
      * the given string.
      */
-    public static UiObject2Condition<Boolean> descEquals(String contentDescription) {
+    @NonNull
+    public static UiObject2Condition<Boolean> descEquals(@NonNull String contentDescription) {
         return descMatches(Pattern.compile(Pattern.quote(contentDescription)));
     }
 
@@ -244,7 +265,8 @@
      * Returns a condition that is satisfied when the object's content description contains the
      * given string.
      */
-    public static UiObject2Condition<Boolean> descContains(String substring) {
+    @NonNull
+    public static UiObject2Condition<Boolean> descContains(@NonNull String substring) {
         return descMatches(Pattern.compile(String.format("^.*%s.*$", Pattern.quote(substring))));
     }
 
@@ -252,7 +274,8 @@
      * Returns a condition that is satisfied when the object's content description starts with the
      * given string.
      */
-    public static UiObject2Condition<Boolean> descStartsWith(String substring) {
+    @NonNull
+    public static UiObject2Condition<Boolean> descStartsWith(@NonNull String substring) {
         return descMatches(Pattern.compile(String.format("^%s.*$", Pattern.quote(substring))));
     }
 
@@ -260,14 +283,16 @@
      * Returns a condition that is satisfied when the object's content description ends with the
      * given string.
      */
-    public static UiObject2Condition<Boolean> descEndsWith(String substring) {
+    @NonNull
+    public static UiObject2Condition<Boolean> descEndsWith(@NonNull String substring) {
         return descMatches(Pattern.compile(String.format("^.*%s$", Pattern.quote(substring))));
     }
 
     /**
      * Returns a condition that is satisfied when the object's text value matches the given regex.
      */
-    public static UiObject2Condition<Boolean> textMatches(final Pattern regex) {
+    @NonNull
+    public static UiObject2Condition<Boolean> textMatches(@NonNull Pattern regex) {
         return new UiObject2Condition<Boolean>() {
             @Override
             Boolean apply(UiObject2 object) {
@@ -280,11 +305,17 @@
     /**
      * Returns a condition that is satisfied when the object's text value matches the given regex.
      */
-    public static UiObject2Condition<Boolean> textMatches(String regex) {
+    @NonNull
+    public static UiObject2Condition<Boolean> textMatches(@NonNull String regex) {
         return textMatches(Pattern.compile(regex));
     }
 
-    public static UiObject2Condition<Boolean> textNotEquals(final String text) {
+    /**
+     * Returns a condition that is satisfied when the object's text value does not match the
+     * given string.
+     */
+    @NonNull
+    public static UiObject2Condition<Boolean> textNotEquals(@NonNull String text) {
         return new UiObject2Condition<Boolean>() {
             @Override
             Boolean apply(UiObject2 object) {
@@ -297,14 +328,16 @@
      * Returns a condition that is satisfied when the object's text value exactly matches the given
      * string.
      */
-    public static UiObject2Condition<Boolean> textEquals(String text) {
+    @NonNull
+    public static UiObject2Condition<Boolean> textEquals(@NonNull String text) {
         return textMatches(Pattern.compile(Pattern.quote(text)));
     }
 
     /**
      * Returns a condition that is satisfied when the object's text value contains the given string.
      */
-    public static UiObject2Condition<Boolean> textContains(String substring) {
+    @NonNull
+    public static UiObject2Condition<Boolean> textContains(@NonNull String substring) {
         return textMatches(Pattern.compile(String.format("^.*%s.*$", Pattern.quote(substring))));
     }
 
@@ -312,7 +345,8 @@
      * Returns a condition that is satisfied when the object's text value starts with the given
      * string.
      */
-    public static UiObject2Condition<Boolean> textStartsWith(String substring) {
+    @NonNull
+    public static UiObject2Condition<Boolean> textStartsWith(@NonNull String substring) {
         return textMatches(Pattern.compile(String.format("^%s.*$", Pattern.quote(substring))));
     }
 
@@ -320,7 +354,8 @@
      * Returns a condition that is satisfied when the object's text value ends with the given
      * string.
      */
-    public static UiObject2Condition<Boolean> textEndsWith(String substring) {
+    @NonNull
+    public static UiObject2Condition<Boolean> textEndsWith(@NonNull String substring) {
         return textMatches(Pattern.compile(String.format("^.*%s$", Pattern.quote(substring))));
     }
 
@@ -328,6 +363,7 @@
     // Event conditions
 
     /** Returns a condition that depends on a new window having appeared. */
+    @NonNull
     public static EventCondition<Boolean> newWindow() {
         return new EventCondition<Boolean>() {
             private int mMask = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED |
@@ -352,7 +388,8 @@
      *
      * @param direction The direction of the scroll.
      */
-    public static EventCondition<Boolean> scrollFinished(final Direction direction) {
+    @NonNull
+    public static EventCondition<Boolean> scrollFinished(@NonNull Direction direction) {
         return new EventCondition<Boolean>() {
             private Direction mDirection = direction;
             private Boolean mResult = null;
diff --git a/webkit/webkit/src/androidTest/java/androidx/webkit/CookieManagerCompatTest.java b/webkit/webkit/src/androidTest/java/androidx/webkit/CookieManagerCompatTest.java
new file mode 100644
index 0000000..5f68f6c
--- /dev/null
+++ b/webkit/webkit/src/androidTest/java/androidx/webkit/CookieManagerCompatTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.webkit;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Build;
+import android.webkit.CookieManager;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.M)
+public class CookieManagerCompatTest {
+    @Test
+    public void testGetCookieInfo() throws Throwable {
+        WebkitUtils.checkFeature(WebViewFeature.GET_COOKIE_INFO);
+        String url = "http://www.example.com";
+        String cookie = "foo=bar; domain=.example.com; path=/";
+        CookieManager cookieManager = CookieManager.getInstance();
+        cookieManager.setCookie(url, cookie);
+
+        List<String> cookies = CookieManagerCompat.getCookieInfo(
+                cookieManager, url);
+        assertEquals(1, cookies.size());
+        assertEquals(cookie, cookies.get(0));
+    }
+}
diff --git a/webkit/webkit/src/main/java/androidx/webkit/CookieManagerCompat.java b/webkit/webkit/src/main/java/androidx/webkit/CookieManagerCompat.java
new file mode 100644
index 0000000..d3ca97a
--- /dev/null
+++ b/webkit/webkit/src/main/java/androidx/webkit/CookieManagerCompat.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.webkit;
+
+import android.webkit.CookieManager;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresFeature;
+import androidx.annotation.RestrictTo;
+import androidx.webkit.internal.ApiFeature;
+import androidx.webkit.internal.CookieManagerAdapter;
+import androidx.webkit.internal.WebViewFeatureInternal;
+import androidx.webkit.internal.WebViewGlueCommunicator;
+
+import java.util.List;
+
+/**
+ * Compatibility version of {@link android.webkit.CookieManager}
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class CookieManagerCompat {
+    private CookieManagerCompat() {}
+
+    /**
+     * Gets all the cookies for the given URL along with their set attributes.
+     * The cookies are returned in the format of the HTTP 'Set-Cookie' header as defined in
+     * <a href="https://httpwg.org/specs/rfc6265.html#sane-set-cookie-syntax">the RFC6265 spec.</a>
+     *  eg. "name=value; domain=.example.com; path=/"
+     *
+     * @param url the URL for which the API retrieves all available cookies.
+     * @return the cookies as a list of strings.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @RequiresFeature(name = WebViewFeature.GET_COOKIE_INFO,
+            enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
+    public static @NonNull List<String> getCookieInfo(@NonNull CookieManager cookieManager,
+            @NonNull String url) {
+        ApiFeature.NoFramework feature = WebViewFeatureInternal.GET_COOKIE_INFO;
+        if (feature.isSupportedByWebView()) {
+            return getAdapter(cookieManager).getCookieInfo(url);
+        } else {
+            throw WebViewFeatureInternal.getUnsupportedOperationException();
+        }
+    }
+
+    private static CookieManagerAdapter getAdapter(CookieManager cookieManager) {
+        return WebViewGlueCommunicator.getCompatConverter().convertCookieManager(cookieManager);
+    }
+}
diff --git a/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java b/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
index 9f13c4d..ef39f1d 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.net.Uri;
 import android.os.Handler;
+import android.webkit.CookieManager;
 import android.webkit.ValueCallback;
 import android.webkit.WebResourceRequest;
 import android.webkit.WebResourceResponse;
@@ -99,6 +100,7 @@
             ALGORITHMIC_DARKENING,
             REQUESTED_WITH_HEADER_CONTROL,
             ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY,
+            GET_COOKIE_INFO,
     })
     @Retention(RetentionPolicy.SOURCE)
     @Target({ElementType.PARAMETER, ElementType.METHOD})
@@ -503,6 +505,15 @@
             "ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY";
 
     /**
+     * Feature for {@link #isFeatureSupported(String)}.
+     * This feature covers
+     * {@link CookieManagerCompat#getCookieInfo(CookieManager, String)}.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public static final String GET_COOKIE_INFO = "GET_COOKIE_INFO";
+
+    /**
      * Return whether a feature is supported at run-time. On devices running Android version {@link
      * android.os.Build.VERSION_CODES#LOLLIPOP} and higher, this will check whether a feature is
      * supported, depending on the combination of the desired feature, the Android version of
diff --git a/webkit/webkit/src/main/java/androidx/webkit/internal/CookieManagerAdapter.java b/webkit/webkit/src/main/java/androidx/webkit/internal/CookieManagerAdapter.java
new file mode 100644
index 0000000..98fe199
--- /dev/null
+++ b/webkit/webkit/src/main/java/androidx/webkit/internal/CookieManagerAdapter.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.webkit.internal;
+
+import androidx.annotation.NonNull;
+
+import org.chromium.support_lib_boundary.WebViewCookieManagerBoundaryInterface;
+
+import java.util.List;
+
+/**
+ * Adapter between CookieManagerCompat and
+ * {@link org.chromium.support_lib_boundary.CookieManagerBoundaryInterface} (the
+ * corresponding interface shared with the support library glue in the WebView APK).
+ */
+public class CookieManagerAdapter {
+    private final WebViewCookieManagerBoundaryInterface mBoundaryInterface;
+
+    public CookieManagerAdapter(@NonNull WebViewCookieManagerBoundaryInterface boundaryInterface) {
+        mBoundaryInterface = boundaryInterface;
+    }
+
+    /**
+     * Adapter method for {@link androidx.webkit.CookieManagerCompat#getCookieInfo}.
+     */
+    public @NonNull List<String> getCookieInfo(@NonNull String url) {
+        return mBoundaryInterface.getCookieInfo(url);
+    }
+}
diff --git a/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java b/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
index c3fa35b..c2c16cd 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
@@ -473,7 +473,6 @@
             new ApiFeature.NoFramework(WebViewFeature.REQUESTED_WITH_HEADER_CONTROL,
                     Features.REQUESTED_WITH_HEADER_CONTROL);
 
-
     /**
      * This feature covers
      * {@link androidx.webkit.WebSettingsCompat#setEnterpriseAuthenticationAppLinkPolicyEnabled(WebSettings, boolean)} and
@@ -482,6 +481,13 @@
     public static final ApiFeature.NoFramework ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY =
             new ApiFeature.NoFramework(WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY,
                     Features.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY);
+
+    /**
+     * This feature covers
+     * {@link androidx.webkit.CookieManager#getCookieInfo(CookieManager, String)}.
+     */
+    public static final ApiFeature.NoFramework GET_COOKIE_INFO =
+            new ApiFeature.NoFramework(WebViewFeature.GET_COOKIE_INFO, Features.GET_COOKIE_INFO);
     // --- Add new feature constants above this line ---
 
     private WebViewFeatureInternal() {
diff --git a/webkit/webkit/src/main/java/androidx/webkit/internal/WebkitToCompatConverter.java b/webkit/webkit/src/main/java/androidx/webkit/internal/WebkitToCompatConverter.java
index ef80a91..2d7327d4 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/internal/WebkitToCompatConverter.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/internal/WebkitToCompatConverter.java
@@ -16,6 +16,7 @@
 
 package androidx.webkit.internal;
 
+import android.webkit.CookieManager;
 import android.webkit.SafeBrowsingResponse;
 import android.webkit.ServiceWorkerWebSettings;
 import android.webkit.WebMessagePort;
@@ -31,6 +32,7 @@
 import org.chromium.support_lib_boundary.ServiceWorkerWebSettingsBoundaryInterface;
 import org.chromium.support_lib_boundary.WebResourceRequestBoundaryInterface;
 import org.chromium.support_lib_boundary.WebSettingsBoundaryInterface;
+import org.chromium.support_lib_boundary.WebViewCookieManagerBoundaryInterface;
 import org.chromium.support_lib_boundary.WebkitToCompatConverterBoundaryInterface;
 import org.chromium.support_lib_boundary.util.BoundaryInterfaceReflectionUtil;
 
@@ -160,4 +162,16 @@
             @NonNull /* SupportLibWebMessagePort */ InvocationHandler webMessagePort) {
         return (WebMessagePort) mImpl.convertWebMessagePort(webMessagePort);
     }
+
+    /**
+     * Return a CookieManagerAdapter linked to cookieManager such that calls on either of those
+     * objects affect the other object. That CookieManagerAdapter can be used to implement
+     * {@link androidx.webkit.CookieManagerCompat}.
+     */
+    @NonNull
+    public CookieManagerAdapter convertCookieManager(@NonNull CookieManager cookieManager) {
+        return new CookieManagerAdapter(BoundaryInterfaceReflectionUtil.castToSuppLibClass(
+                WebViewCookieManagerBoundaryInterface.class,
+                mImpl.convertCookieManager(cookieManager)));
+    }
 }