[go: nahoru, domu]

Merge "Fix invalidate range check for lineIndex" into androidx-master-dev
diff --git a/.github/workflows/presubmit.yml b/.github/workflows/presubmit.yml
index 3feb86f..88b05ec 100644
--- a/.github/workflows/presubmit.yml
+++ b/.github/workflows/presubmit.yml
@@ -1,11 +1,8 @@
 name: AndroidX Presubmits
 on:
   pull_request:
-    branches:
-      - master
-      - androidx-master-dev
-  repository_dispatch:
-    if: always()
+    types: [assigned, opened, synchronize, reopened, labeled]
+  workflow_dispatch:
 
 jobs:
   # This workflow contains a single job called "build"
diff --git a/biometric/biometric/src/main/java/androidx/biometric/CancellationSignalProvider.java b/biometric/biometric/src/main/java/androidx/biometric/CancellationSignalProvider.java
index 7cba5a4..e2218f5 100644
--- a/biometric/biometric/src/main/java/androidx/biometric/CancellationSignalProvider.java
+++ b/biometric/biometric/src/main/java/androidx/biometric/CancellationSignalProvider.java
@@ -17,10 +17,12 @@
 package androidx.biometric;
 
 import android.os.Build;
+import android.os.CancellationSignal;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
+import androidx.annotation.VisibleForTesting;
 
 /**
  * Creates and caches cancellation signal objects that are compatible with
@@ -30,6 +32,34 @@
 @SuppressWarnings("deprecation")
 class CancellationSignalProvider {
     /**
+     * An injector for various class dependencies. Used for testing.
+     */
+    @VisibleForTesting
+    interface Injector {
+        /**
+         * TODO
+         *
+         * @return TODO
+         */
+        @RequiresApi(Build.VERSION_CODES.JELLY_BEAN)
+        @NonNull
+        android.os.CancellationSignal getBiometricCancellationSignal();
+
+        /**
+         * TODO
+         *
+         * @return TODO
+         */
+        @NonNull
+        androidx.core.os.CancellationSignal getFingerprintCancellationSignal();
+    }
+
+    /**
+     * The injector for class dependencies used by this provider.
+     */
+    private final Injector mInjector;
+
+    /**
      * A cancellation signal object that is compatible with
      * {@link android.hardware.biometrics.BiometricPrompt}.
      */
@@ -42,6 +72,36 @@
     @Nullable private androidx.core.os.CancellationSignal mFingerprintCancellationSignal;
 
     /**
+     * Creates a new cancellation signal provider instance.
+     */
+    CancellationSignalProvider() {
+        mInjector = new Injector() {
+            @Override
+            @RequiresApi(Build.VERSION_CODES.JELLY_BEAN)
+            @NonNull
+            public CancellationSignal getBiometricCancellationSignal() {
+                return Api16Impl.create();
+            }
+
+            @Override
+            @NonNull
+            public androidx.core.os.CancellationSignal getFingerprintCancellationSignal() {
+                return new androidx.core.os.CancellationSignal();
+            }
+        };
+    }
+
+    /**
+     * Creates a new cancellation signal provider instance with the given injector.
+     *
+     * @param injector An injector for class and method dependencies.
+     */
+    @VisibleForTesting
+    CancellationSignalProvider(Injector injector) {
+        mInjector = injector;
+    }
+
+    /**
      * Provides a cancellation signal object that is compatible with
      * {@link android.hardware.biometrics.BiometricPrompt}.
      *
@@ -55,7 +115,7 @@
     @NonNull
     android.os.CancellationSignal getBiometricCancellationSignal() {
         if (mBiometricCancellationSignal == null) {
-            mBiometricCancellationSignal = Api16Impl.create();
+            mBiometricCancellationSignal = mInjector.getBiometricCancellationSignal();
         }
         return mBiometricCancellationSignal;
     }
@@ -73,7 +133,7 @@
     @NonNull
     androidx.core.os.CancellationSignal getFingerprintCancellationSignal() {
         if (mFingerprintCancellationSignal == null) {
-            mFingerprintCancellationSignal = new androidx.core.os.CancellationSignal();
+            mFingerprintCancellationSignal = mInjector.getFingerprintCancellationSignal();
         }
         return mFingerprintCancellationSignal;
     }
diff --git a/biometric/biometric/src/test/java/androidx/biometric/AuthenticationCallbackProviderTest.java b/biometric/biometric/src/test/java/androidx/biometric/AuthenticationCallbackProviderTest.java
new file mode 100644
index 0000000..d10f8bf
--- /dev/null
+++ b/biometric/biometric/src/test/java/androidx/biometric/AuthenticationCallbackProviderTest.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.biometric;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.os.Build;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+import javax.crypto.Cipher;
+
+@LargeTest
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
+public class AuthenticationCallbackProviderTest {
+    @Mock private AuthenticationCallbackProvider.Listener mListener;
+    @Mock private Cipher mCipher;
+
+    @Captor private ArgumentCaptor<BiometricPrompt.AuthenticationResult> mResultCaptor;
+
+    private AuthenticationCallbackProvider mAuthenticationCallbackProvider;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mAuthenticationCallbackProvider = new AuthenticationCallbackProvider(mListener);
+    }
+
+    @Test
+    @Config(minSdk = Build.VERSION_CODES.P)
+    public void testBiometricCallback_IsCached() {
+        final android.hardware.biometrics.BiometricPrompt.AuthenticationCallback callback =
+                mAuthenticationCallbackProvider.getBiometricCallback();
+        assertThat(mAuthenticationCallbackProvider.getBiometricCallback()).isEqualTo(callback);
+    }
+
+    @Test
+    @Config(minSdk = Build.VERSION_CODES.P)
+    public void testBiometricCallback_HandlesSuccess() {
+        final android.hardware.biometrics.BiometricPrompt.AuthenticationResult result =
+                mock(android.hardware.biometrics.BiometricPrompt.AuthenticationResult.class);
+        final android.hardware.biometrics.BiometricPrompt.CryptoObject crypto =
+                new android.hardware.biometrics.BiometricPrompt.CryptoObject(mCipher);
+        when(result.getCryptoObject()).thenReturn(crypto);
+
+        mAuthenticationCallbackProvider.getBiometricCallback().onAuthenticationSucceeded(result);
+
+        verify(mListener).onSuccess(mResultCaptor.capture());
+        verifyNoMoreInteractions(mListener);
+        assertThat(mResultCaptor.getValue().getCryptoObject()).isNotNull();
+        assertThat(mResultCaptor.getValue().getCryptoObject().getCipher()).isEqualTo(mCipher);
+    }
+
+    @Test
+    @Config(minSdk = Build.VERSION_CODES.P)
+    public void testBiometricCallback_HandlesError() {
+        final int errorCode = BiometricPrompt.ERROR_HW_NOT_PRESENT;
+        final String errorMessage = "Lorem ipsum";
+
+        mAuthenticationCallbackProvider.getBiometricCallback()
+                .onAuthenticationError(errorCode, errorMessage);
+
+        verify(mListener).onError(errorCode, errorMessage);
+        verifyNoMoreInteractions(mListener);
+    }
+
+    @Test
+    @Config(minSdk = Build.VERSION_CODES.P)
+    public void testBiometricCallback_IgnoresHelp() {
+        final int helpCode = BiometricPrompt.ERROR_UNABLE_TO_PROCESS;
+        final String helpMessage = "Dolor sit amet";
+
+        mAuthenticationCallbackProvider.getBiometricCallback()
+                .onAuthenticationHelp(helpCode, helpMessage);
+
+        verifyZeroInteractions(mListener);
+    }
+
+    @Test
+    @Config(minSdk = Build.VERSION_CODES.P)
+    public void testBiometricCallback_HandlesFailure() {
+        mAuthenticationCallbackProvider.getBiometricCallback().onAuthenticationFailed();
+
+        verify(mListener).onFailure();
+        verifyNoMoreInteractions(mListener);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testFingerprintCallback_IsCached() {
+        final androidx.core.hardware.fingerprint.FingerprintManagerCompat.AuthenticationCallback
+                callback = mAuthenticationCallbackProvider.getFingerprintCallback();
+        assertThat(mAuthenticationCallbackProvider.getFingerprintCallback()).isEqualTo(callback);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test
+    public void testFingerprintCallback_HandlesSuccess() {
+        final androidx.core.hardware.fingerprint.FingerprintManagerCompat.CryptoObject crypto =
+                new androidx.core.hardware.fingerprint.FingerprintManagerCompat.CryptoObject(
+                        mCipher);
+        final androidx.core.hardware.fingerprint.FingerprintManagerCompat.AuthenticationResult
+                result = new androidx.core.hardware.fingerprint.FingerprintManagerCompat
+                        .AuthenticationResult(crypto);
+
+        mAuthenticationCallbackProvider.getFingerprintCallback().onAuthenticationSucceeded(result);
+
+        verify(mListener).onSuccess(mResultCaptor.capture());
+        verifyNoMoreInteractions(mListener);
+        assertThat(mResultCaptor.getValue().getCryptoObject()).isNotNull();
+        assertThat(mResultCaptor.getValue().getCryptoObject().getCipher()).isEqualTo(mCipher);
+    }
+
+    @Test
+    public void testFingerprintCallback_HandlesError() {
+        final int errorCode = BiometricPrompt.ERROR_CANCELED;
+        final String errorMessage = "Foo bar";
+
+        mAuthenticationCallbackProvider.getFingerprintCallback()
+                .onAuthenticationError(errorCode, errorMessage);
+
+        verify(mListener).onError(errorCode, errorMessage);
+        verifyNoMoreInteractions(mListener);
+    }
+
+    @Test
+    public void testFingerprintCallback_HandlesHelp() {
+        final int helpCode = BiometricPrompt.ERROR_TIMEOUT;
+        final String helpMessage = "Baz qux";
+
+        mAuthenticationCallbackProvider.getFingerprintCallback()
+                .onAuthenticationHelp(helpCode, helpMessage);
+
+        verify(mListener).onHelp(helpMessage);
+        verifyNoMoreInteractions(mListener);
+    }
+
+    @Test
+    public void testFingerprintCallback_HandlesFailure() {
+        mAuthenticationCallbackProvider.getFingerprintCallback().onAuthenticationFailed();
+
+        verify(mListener).onFailure();
+        verifyNoMoreInteractions(mListener);
+    }
+}
diff --git a/biometric/biometric/src/test/java/androidx/biometric/CancellationSignalProviderTest.java b/biometric/biometric/src/test/java/androidx/biometric/CancellationSignalProviderTest.java
new file mode 100644
index 0000000..c81763a
--- /dev/null
+++ b/biometric/biometric/src/test/java/androidx/biometric/CancellationSignalProviderTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.biometric;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.LargeTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+@LargeTest
+@RunWith(RobolectricTestRunner.class)
+@DoNotInstrument
+public class CancellationSignalProviderTest {
+    @Test
+    @Config(minSdk = Build.VERSION_CODES.JELLY_BEAN)
+    public void testBiometricCancellationSignal_IsCached() {
+        final CancellationSignalProvider provider = new CancellationSignalProvider();
+        final android.os.CancellationSignal cancellationSignal =
+                provider.getBiometricCancellationSignal();
+        assertThat(provider.getBiometricCancellationSignal()).isEqualTo(cancellationSignal);
+    }
+
+    @Test
+    @Config(minSdk = Build.VERSION_CODES.JELLY_BEAN)
+    public void testBiometricCancellationSignal_ReceivesCancel() {
+        final android.os.CancellationSignal cancellationSignal =
+                mock(android.os.CancellationSignal.class);
+        final CancellationSignalProvider.Injector injector =
+                new CancellationSignalProvider.Injector() {
+                    @Override
+                    @NonNull
+                    public android.os.CancellationSignal getBiometricCancellationSignal() {
+                        return cancellationSignal;
+                    }
+
+                    @Override
+                    @NonNull
+                    public androidx.core.os.CancellationSignal getFingerprintCancellationSignal() {
+                        return mock(androidx.core.os.CancellationSignal.class);
+                    }
+                };
+        final CancellationSignalProvider provider = new CancellationSignalProvider(injector);
+
+        assertThat(provider.getBiometricCancellationSignal()).isEqualTo(cancellationSignal);
+
+        provider.cancel();
+
+        verify(cancellationSignal).cancel();
+    }
+
+    @Test
+    public void testFingerprintCancellationSignal_IsCached() {
+        final CancellationSignalProvider provider = new CancellationSignalProvider();
+        final androidx.core.os.CancellationSignal cancellationSignal =
+                provider.getFingerprintCancellationSignal();
+        assertThat(provider.getFingerprintCancellationSignal()).isEqualTo(cancellationSignal);
+    }
+
+    @Test
+    public void testFingerprintCancellationSignal_ReceivesCancel() {
+        final androidx.core.os.CancellationSignal cancellationSignal =
+                mock(androidx.core.os.CancellationSignal.class);
+        final CancellationSignalProvider.Injector injector =
+                new CancellationSignalProvider.Injector() {
+                    @Override
+                    @NonNull
+                    public android.os.CancellationSignal getBiometricCancellationSignal() {
+                        return mock(android.os.CancellationSignal.class);
+                    }
+
+                    @Override
+                    @NonNull
+                    public androidx.core.os.CancellationSignal getFingerprintCancellationSignal() {
+                        return  cancellationSignal;
+                    }
+                };
+        final CancellationSignalProvider provider = new CancellationSignalProvider(injector);
+
+        assertThat(provider.getFingerprintCancellationSignal()).isEqualTo(cancellationSignal);
+
+        provider.cancel();
+
+        verify(cancellationSignal).cancel();
+    }
+
+    @Test
+    @Config(minSdk = Build.VERSION_CODES.JELLY_BEAN)
+    public void testBothCancellationSignals_ReceiveCancel() {
+        final android.os.CancellationSignal biometricCancellationSignal =
+                mock(android.os.CancellationSignal.class);
+        final androidx.core.os.CancellationSignal fingerprintCancellationSignal =
+                mock(androidx.core.os.CancellationSignal.class);
+        final CancellationSignalProvider.Injector injector =
+                new CancellationSignalProvider.Injector() {
+                    @Override
+                    @NonNull
+                    public android.os.CancellationSignal getBiometricCancellationSignal() {
+                        return biometricCancellationSignal;
+                    }
+
+                    @Override
+                    @NonNull
+                    public androidx.core.os.CancellationSignal getFingerprintCancellationSignal() {
+                        return fingerprintCancellationSignal;
+                    }
+                };
+        final CancellationSignalProvider provider = new CancellationSignalProvider(injector);
+
+        assertThat(provider.getBiometricCancellationSignal())
+                .isEqualTo(biometricCancellationSignal);
+        assertThat(provider.getFingerprintCancellationSignal())
+                .isEqualTo(fingerprintCancellationSignal);
+
+        provider.cancel();
+
+        verify(biometricCancellationSignal).cancel();
+        verify(fingerprintCancellationSignal).cancel();
+    }
+}
diff --git a/buildSrc/build_dependencies.gradle b/buildSrc/build_dependencies.gradle
index db08734..3d430c4 100644
--- a/buildSrc/build_dependencies.gradle
+++ b/buildSrc/build_dependencies.gradle
@@ -26,7 +26,7 @@
     build_versions.lint = '27.1.0-alpha08'
 } else {
     build_versions.kotlin = "1.3.71"
-    build_versions.kotlin_coroutines = "1.3.4"
+    build_versions.kotlin_coroutines = "1.3.7"
     build_versions.agp = '4.1.0-alpha08'
     // NOTE: When updating the lint version we also need to update the `api` version supported
     //  by `IssueRegistry`'s.' For e.g. aosp/1331903
diff --git a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlaygroundRootPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlaygroundRootPlugin.kt
index 4efd240..338a4ff 100644
--- a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlaygroundRootPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlaygroundRootPlugin.kt
@@ -196,6 +196,6 @@
     }
 
     companion object {
-        private const val SNAPSHOT_MARKER = "REPLACE_WITH_SNAPSHOT"
+        const val SNAPSHOT_MARKER = "REPLACE_WITH_SNAPSHOT"
     }
 }
diff --git a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
index d8b4067..b95d4d5 100644
--- a/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/AndroidXPlugin.kt
@@ -187,10 +187,23 @@
     }
 
     private fun configureWithAppPlugin(project: Project, extension: AndroidXExtension) {
-        project.extensions.getByType<AppExtension>().apply {
+        val appExtension = project.extensions.getByType<AppExtension>().apply {
             configureAndroidCommonOptions(project, extension)
             configureAndroidApplicationOptions(project)
         }
+
+        // TODO: Replace this with a per-variant packagingOption for androidTest specifically once
+        //  b/69953968 is resolved.
+        // Workaround for b/161465530 in AGP that fails to strip these <module>.kotlin_module files,
+        // which causes mergeDebugAndroidTestJavaResource to fail for sample apps.
+        appExtension.packagingOptions.exclude("/META-INF/*.kotlin_module")
+        // Workaround a limitation in AGP that fails to merge these META-INF license files.
+        appExtension.packagingOptions.pickFirst("/META-INF/AL2.0")
+        // In addition to working around the above issue, we exclude the LGPL2.1 license as we're
+        // approved to distribute code via AL2.0 and the only dependencies which pull in LGPL2.1
+        // are currently dual-licensed with AL2.0 and LGPL2.1. The affected dependencies are:
+        //   - net.java.dev.jna:jna:5.5.0
+        appExtension.packagingOptions.exclude("/META-INF/LGPL2.1")
     }
 
     private fun configureWithLibraryPlugin(
@@ -202,6 +215,17 @@
             configureAndroidLibraryOptions(project, androidXExtension)
         }
         libraryExtension.packagingOptions {
+            // TODO: Replace this with a per-variant packagingOption for androidTest specifically
+            //  once b/69953968 is resolved.
+            // Workaround for b/161465530 in AGP that fails to merge these META-INF license files
+            // for libraries that publish Java resources under the same name.
+            pickFirst("/META-INF/AL2.0")
+            // In addition to working around the above issue, we exclude the LGPL2.1 license as we're
+            // approved to distribute code via AL2.0 and the only dependencies which pull in LGPL2.1
+            // currently are dual-licensed with AL2.0 and LGPL2.1. The affected dependencies are:
+            //   - net.java.dev.jna:jna:5.5.0
+            exclude("/META-INF/LGPL2.1")
+
             // We need this as a work-around for b/155721209
             // It can be removed when we have a newer plugin version
             // 2nd workaround - this DSL was made saner in a breaking way which hasn't landed
diff --git a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
index 2b43dbc..6e30585 100644
--- a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
@@ -100,6 +100,8 @@
     ignore(LibraryGroups.NAVIGATION.group, "navigation-safe-args-generator")
     ignore(LibraryGroups.NAVIGATION.group, "navigation-safe-args-gradle-plugin")
     prebuilts(LibraryGroups.NAVIGATION, "2.3.0")
+    // TODO: Remove during release phase of rxjava3 artifact
+    ignore(LibraryGroups.PAGING.group, "paging-rxjava3")
     prebuilts(LibraryGroups.PAGING, "3.0.0-alpha02")
     prebuilts(LibraryGroups.PALETTE, "1.0.0")
     // 1.0.1 was created to fix reference docs.  It contains no actual source changes from 1.0.0
@@ -114,6 +116,7 @@
     prebuilts(LibraryGroups.REMOTECALLBACK, "1.0.0-alpha02")
     // TODO: Remove this once b/157899389 is resolved
     ignore(LibraryGroups.ROOM.group, "room-compiler")
+    ignore(LibraryGroups.ROOM.group, "room-compiler-xprocessing")
     // TODO: Remove during release phase of rxjava3 artifact
     ignore(LibraryGroups.ROOM.group, "room-rxjava3")
     prebuilts(LibraryGroups.ROOM, "2.3.0-alpha01")
diff --git a/buildSrc/src/main/kotlin/androidx/build/VerifyDependencyVersionsTask.kt b/buildSrc/src/main/kotlin/androidx/build/VerifyDependencyVersionsTask.kt
index 2b1e311..03eec98 100644
--- a/buildSrc/src/main/kotlin/androidx/build/VerifyDependencyVersionsTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/VerifyDependencyVersionsTask.kt
@@ -121,5 +121,12 @@
     // Only verify dependencies within the scope of our versioning policies.
     if (dependency.group == null) return false
     if (!dependency.group.toString().startsWith("androidx.")) return false
+    if (dependency.version == AndroidXPlaygroundRootPlugin.SNAPSHOT_MARKER) {
+        // This only happens in playground builds where this magic version gets replaced with
+        // the version from the snapshotBuildId defined in playground-common/playground.properties.
+        // It is best to leave their validation to the aosp build to ensure it is the right
+        // version.
+        return false
+    }
     return true
 }
diff --git a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
index 5f13437..54a6632 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
@@ -142,6 +142,8 @@
     get() = "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinCoroutinesVersion"
 val KOTLIN_COROUTINES_RX2
     get() = "org.jetbrains.kotlinx:kotlinx-coroutines-rx2:$kotlinCoroutinesVersion"
+val KOTLIN_COROUTINES_RX3
+    get() = "org.jetbrains.kotlinx:kotlinx-coroutines-rx3:$kotlinCoroutinesVersion"
 
 internal lateinit var agpVersion: String
 
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/LiveDataObservableTest.kt b/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/LiveDataObservableTest.kt
index 40dfdec..5ca712c 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/LiveDataObservableTest.kt
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/impl/LiveDataObservableTest.kt
@@ -60,7 +60,7 @@
 
     @Test
     fun canSetAndFetchValue_onMainThread() {
-        var fetched: Int? = null
+        var fetched: Int?
         runBlocking(Dispatchers.Main) {
             val observable = LiveDataObservable<Int>().apply {
                 postValue(MAGIC_VALUE)
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformer.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformer.kt
index 836ca6c..e8eac7b 100644
--- a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformer.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformer.kt
@@ -48,6 +48,7 @@
             "androidx.navigation_navigation-dynamic-features-fragment.version",
             "androidx.navigation_navigation-dynamic-features-runtime.version",
             "androidx.navigation_navigation-testing.version",
+            "androidx.paging_paging-rxjava3.version",
             "androidx.savedstate_savedstate-ktx.version"
         )
     }
diff --git a/paging/rxjava3/api/3.0.0-alpha04.txt b/paging/rxjava3/api/3.0.0-alpha04.txt
new file mode 100644
index 0000000..598b727
--- /dev/null
+++ b/paging/rxjava3/api/3.0.0-alpha04.txt
@@ -0,0 +1,26 @@
+// Signature format: 3.0
+package androidx.paging.rxjava3 {
+
+  public final class PagingRx {
+    method public static <T> io.reactivex.rxjava3.core.Observable<androidx.paging.PagingData<T>> cachedIn(io.reactivex.rxjava3.core.Observable<androidx.paging.PagingData<T>>, kotlinx.coroutines.CoroutineScope scope);
+    method public static <T> io.reactivex.rxjava3.core.Flowable<androidx.paging.PagingData<T>> cachedIn(io.reactivex.rxjava3.core.Flowable<androidx.paging.PagingData<T>>, kotlinx.coroutines.CoroutineScope scope);
+    method public static <Key, Value> io.reactivex.rxjava3.core.Flowable<androidx.paging.PagingData<Value>> getFlowable(androidx.paging.Pager<Key,Value>);
+    method public static <Key, Value> io.reactivex.rxjava3.core.Observable<androidx.paging.PagingData<Value>> getObservable(androidx.paging.Pager<Key,Value>);
+  }
+
+  public abstract class RxPagingSource<Key, Value> extends androidx.paging.PagingSource<Key,Value> {
+    ctor public RxPagingSource();
+    method public final suspend Object? load(androidx.paging.PagingSource.LoadParams<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.PagingSource.LoadResult<Key,Value>> p);
+    method public abstract io.reactivex.rxjava3.core.Single<androidx.paging.PagingSource.LoadResult<Key,Value>> loadSingle(androidx.paging.PagingSource.LoadParams<Key> params);
+  }
+
+  @androidx.paging.ExperimentalPagingApi public abstract class RxRemoteMediator<Key, Value> extends androidx.paging.RemoteMediator<Key,Value> {
+    ctor public RxRemoteMediator();
+    method public final suspend Object? initialize(kotlin.coroutines.Continuation<? super androidx.paging.RemoteMediator.InitializeAction> p);
+    method public io.reactivex.rxjava3.core.Single<androidx.paging.RemoteMediator.InitializeAction> initializeSingle();
+    method public final suspend Object? load(androidx.paging.LoadType loadType, androidx.paging.PagingState<Key,Value> state, kotlin.coroutines.Continuation<? super androidx.paging.RemoteMediator.MediatorResult> p);
+    method public abstract io.reactivex.rxjava3.core.Single<androidx.paging.RemoteMediator.MediatorResult> loadSingle(androidx.paging.LoadType loadType, androidx.paging.PagingState<Key,Value> state);
+  }
+
+}
+
diff --git a/paging/rxjava3/api/current.txt b/paging/rxjava3/api/current.txt
new file mode 100644
index 0000000..598b727
--- /dev/null
+++ b/paging/rxjava3/api/current.txt
@@ -0,0 +1,26 @@
+// Signature format: 3.0
+package androidx.paging.rxjava3 {
+
+  public final class PagingRx {
+    method public static <T> io.reactivex.rxjava3.core.Observable<androidx.paging.PagingData<T>> cachedIn(io.reactivex.rxjava3.core.Observable<androidx.paging.PagingData<T>>, kotlinx.coroutines.CoroutineScope scope);
+    method public static <T> io.reactivex.rxjava3.core.Flowable<androidx.paging.PagingData<T>> cachedIn(io.reactivex.rxjava3.core.Flowable<androidx.paging.PagingData<T>>, kotlinx.coroutines.CoroutineScope scope);
+    method public static <Key, Value> io.reactivex.rxjava3.core.Flowable<androidx.paging.PagingData<Value>> getFlowable(androidx.paging.Pager<Key,Value>);
+    method public static <Key, Value> io.reactivex.rxjava3.core.Observable<androidx.paging.PagingData<Value>> getObservable(androidx.paging.Pager<Key,Value>);
+  }
+
+  public abstract class RxPagingSource<Key, Value> extends androidx.paging.PagingSource<Key,Value> {
+    ctor public RxPagingSource();
+    method public final suspend Object? load(androidx.paging.PagingSource.LoadParams<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.PagingSource.LoadResult<Key,Value>> p);
+    method public abstract io.reactivex.rxjava3.core.Single<androidx.paging.PagingSource.LoadResult<Key,Value>> loadSingle(androidx.paging.PagingSource.LoadParams<Key> params);
+  }
+
+  @androidx.paging.ExperimentalPagingApi public abstract class RxRemoteMediator<Key, Value> extends androidx.paging.RemoteMediator<Key,Value> {
+    ctor public RxRemoteMediator();
+    method public final suspend Object? initialize(kotlin.coroutines.Continuation<? super androidx.paging.RemoteMediator.InitializeAction> p);
+    method public io.reactivex.rxjava3.core.Single<androidx.paging.RemoteMediator.InitializeAction> initializeSingle();
+    method public final suspend Object? load(androidx.paging.LoadType loadType, androidx.paging.PagingState<Key,Value> state, kotlin.coroutines.Continuation<? super androidx.paging.RemoteMediator.MediatorResult> p);
+    method public abstract io.reactivex.rxjava3.core.Single<androidx.paging.RemoteMediator.MediatorResult> loadSingle(androidx.paging.LoadType loadType, androidx.paging.PagingState<Key,Value> state);
+  }
+
+}
+
diff --git a/paging/rxjava3/api/public_plus_experimental_3.0.0-alpha04.txt b/paging/rxjava3/api/public_plus_experimental_3.0.0-alpha04.txt
new file mode 100644
index 0000000..598b727
--- /dev/null
+++ b/paging/rxjava3/api/public_plus_experimental_3.0.0-alpha04.txt
@@ -0,0 +1,26 @@
+// Signature format: 3.0
+package androidx.paging.rxjava3 {
+
+  public final class PagingRx {
+    method public static <T> io.reactivex.rxjava3.core.Observable<androidx.paging.PagingData<T>> cachedIn(io.reactivex.rxjava3.core.Observable<androidx.paging.PagingData<T>>, kotlinx.coroutines.CoroutineScope scope);
+    method public static <T> io.reactivex.rxjava3.core.Flowable<androidx.paging.PagingData<T>> cachedIn(io.reactivex.rxjava3.core.Flowable<androidx.paging.PagingData<T>>, kotlinx.coroutines.CoroutineScope scope);
+    method public static <Key, Value> io.reactivex.rxjava3.core.Flowable<androidx.paging.PagingData<Value>> getFlowable(androidx.paging.Pager<Key,Value>);
+    method public static <Key, Value> io.reactivex.rxjava3.core.Observable<androidx.paging.PagingData<Value>> getObservable(androidx.paging.Pager<Key,Value>);
+  }
+
+  public abstract class RxPagingSource<Key, Value> extends androidx.paging.PagingSource<Key,Value> {
+    ctor public RxPagingSource();
+    method public final suspend Object? load(androidx.paging.PagingSource.LoadParams<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.PagingSource.LoadResult<Key,Value>> p);
+    method public abstract io.reactivex.rxjava3.core.Single<androidx.paging.PagingSource.LoadResult<Key,Value>> loadSingle(androidx.paging.PagingSource.LoadParams<Key> params);
+  }
+
+  @androidx.paging.ExperimentalPagingApi public abstract class RxRemoteMediator<Key, Value> extends androidx.paging.RemoteMediator<Key,Value> {
+    ctor public RxRemoteMediator();
+    method public final suspend Object? initialize(kotlin.coroutines.Continuation<? super androidx.paging.RemoteMediator.InitializeAction> p);
+    method public io.reactivex.rxjava3.core.Single<androidx.paging.RemoteMediator.InitializeAction> initializeSingle();
+    method public final suspend Object? load(androidx.paging.LoadType loadType, androidx.paging.PagingState<Key,Value> state, kotlin.coroutines.Continuation<? super androidx.paging.RemoteMediator.MediatorResult> p);
+    method public abstract io.reactivex.rxjava3.core.Single<androidx.paging.RemoteMediator.MediatorResult> loadSingle(androidx.paging.LoadType loadType, androidx.paging.PagingState<Key,Value> state);
+  }
+
+}
+
diff --git a/paging/rxjava3/api/public_plus_experimental_current.txt b/paging/rxjava3/api/public_plus_experimental_current.txt
new file mode 100644
index 0000000..598b727
--- /dev/null
+++ b/paging/rxjava3/api/public_plus_experimental_current.txt
@@ -0,0 +1,26 @@
+// Signature format: 3.0
+package androidx.paging.rxjava3 {
+
+  public final class PagingRx {
+    method public static <T> io.reactivex.rxjava3.core.Observable<androidx.paging.PagingData<T>> cachedIn(io.reactivex.rxjava3.core.Observable<androidx.paging.PagingData<T>>, kotlinx.coroutines.CoroutineScope scope);
+    method public static <T> io.reactivex.rxjava3.core.Flowable<androidx.paging.PagingData<T>> cachedIn(io.reactivex.rxjava3.core.Flowable<androidx.paging.PagingData<T>>, kotlinx.coroutines.CoroutineScope scope);
+    method public static <Key, Value> io.reactivex.rxjava3.core.Flowable<androidx.paging.PagingData<Value>> getFlowable(androidx.paging.Pager<Key,Value>);
+    method public static <Key, Value> io.reactivex.rxjava3.core.Observable<androidx.paging.PagingData<Value>> getObservable(androidx.paging.Pager<Key,Value>);
+  }
+
+  public abstract class RxPagingSource<Key, Value> extends androidx.paging.PagingSource<Key,Value> {
+    ctor public RxPagingSource();
+    method public final suspend Object? load(androidx.paging.PagingSource.LoadParams<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.PagingSource.LoadResult<Key,Value>> p);
+    method public abstract io.reactivex.rxjava3.core.Single<androidx.paging.PagingSource.LoadResult<Key,Value>> loadSingle(androidx.paging.PagingSource.LoadParams<Key> params);
+  }
+
+  @androidx.paging.ExperimentalPagingApi public abstract class RxRemoteMediator<Key, Value> extends androidx.paging.RemoteMediator<Key,Value> {
+    ctor public RxRemoteMediator();
+    method public final suspend Object? initialize(kotlin.coroutines.Continuation<? super androidx.paging.RemoteMediator.InitializeAction> p);
+    method public io.reactivex.rxjava3.core.Single<androidx.paging.RemoteMediator.InitializeAction> initializeSingle();
+    method public final suspend Object? load(androidx.paging.LoadType loadType, androidx.paging.PagingState<Key,Value> state, kotlin.coroutines.Continuation<? super androidx.paging.RemoteMediator.MediatorResult> p);
+    method public abstract io.reactivex.rxjava3.core.Single<androidx.paging.RemoteMediator.MediatorResult> loadSingle(androidx.paging.LoadType loadType, androidx.paging.PagingState<Key,Value> state);
+  }
+
+}
+
diff --git a/paging/rxjava3/api/res-3.0.0-alpha04.txt b/paging/rxjava3/api/res-3.0.0-alpha04.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/paging/rxjava3/api/res-3.0.0-alpha04.txt
diff --git a/paging/rxjava3/api/res-current.txt b/paging/rxjava3/api/res-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/paging/rxjava3/api/res-current.txt
diff --git a/paging/rxjava3/api/restricted_3.0.0-alpha04.txt b/paging/rxjava3/api/restricted_3.0.0-alpha04.txt
new file mode 100644
index 0000000..598b727
--- /dev/null
+++ b/paging/rxjava3/api/restricted_3.0.0-alpha04.txt
@@ -0,0 +1,26 @@
+// Signature format: 3.0
+package androidx.paging.rxjava3 {
+
+  public final class PagingRx {
+    method public static <T> io.reactivex.rxjava3.core.Observable<androidx.paging.PagingData<T>> cachedIn(io.reactivex.rxjava3.core.Observable<androidx.paging.PagingData<T>>, kotlinx.coroutines.CoroutineScope scope);
+    method public static <T> io.reactivex.rxjava3.core.Flowable<androidx.paging.PagingData<T>> cachedIn(io.reactivex.rxjava3.core.Flowable<androidx.paging.PagingData<T>>, kotlinx.coroutines.CoroutineScope scope);
+    method public static <Key, Value> io.reactivex.rxjava3.core.Flowable<androidx.paging.PagingData<Value>> getFlowable(androidx.paging.Pager<Key,Value>);
+    method public static <Key, Value> io.reactivex.rxjava3.core.Observable<androidx.paging.PagingData<Value>> getObservable(androidx.paging.Pager<Key,Value>);
+  }
+
+  public abstract class RxPagingSource<Key, Value> extends androidx.paging.PagingSource<Key,Value> {
+    ctor public RxPagingSource();
+    method public final suspend Object? load(androidx.paging.PagingSource.LoadParams<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.PagingSource.LoadResult<Key,Value>> p);
+    method public abstract io.reactivex.rxjava3.core.Single<androidx.paging.PagingSource.LoadResult<Key,Value>> loadSingle(androidx.paging.PagingSource.LoadParams<Key> params);
+  }
+
+  @androidx.paging.ExperimentalPagingApi public abstract class RxRemoteMediator<Key, Value> extends androidx.paging.RemoteMediator<Key,Value> {
+    ctor public RxRemoteMediator();
+    method public final suspend Object? initialize(kotlin.coroutines.Continuation<? super androidx.paging.RemoteMediator.InitializeAction> p);
+    method public io.reactivex.rxjava3.core.Single<androidx.paging.RemoteMediator.InitializeAction> initializeSingle();
+    method public final suspend Object? load(androidx.paging.LoadType loadType, androidx.paging.PagingState<Key,Value> state, kotlin.coroutines.Continuation<? super androidx.paging.RemoteMediator.MediatorResult> p);
+    method public abstract io.reactivex.rxjava3.core.Single<androidx.paging.RemoteMediator.MediatorResult> loadSingle(androidx.paging.LoadType loadType, androidx.paging.PagingState<Key,Value> state);
+  }
+
+}
+
diff --git a/paging/rxjava3/api/restricted_current.txt b/paging/rxjava3/api/restricted_current.txt
new file mode 100644
index 0000000..598b727
--- /dev/null
+++ b/paging/rxjava3/api/restricted_current.txt
@@ -0,0 +1,26 @@
+// Signature format: 3.0
+package androidx.paging.rxjava3 {
+
+  public final class PagingRx {
+    method public static <T> io.reactivex.rxjava3.core.Observable<androidx.paging.PagingData<T>> cachedIn(io.reactivex.rxjava3.core.Observable<androidx.paging.PagingData<T>>, kotlinx.coroutines.CoroutineScope scope);
+    method public static <T> io.reactivex.rxjava3.core.Flowable<androidx.paging.PagingData<T>> cachedIn(io.reactivex.rxjava3.core.Flowable<androidx.paging.PagingData<T>>, kotlinx.coroutines.CoroutineScope scope);
+    method public static <Key, Value> io.reactivex.rxjava3.core.Flowable<androidx.paging.PagingData<Value>> getFlowable(androidx.paging.Pager<Key,Value>);
+    method public static <Key, Value> io.reactivex.rxjava3.core.Observable<androidx.paging.PagingData<Value>> getObservable(androidx.paging.Pager<Key,Value>);
+  }
+
+  public abstract class RxPagingSource<Key, Value> extends androidx.paging.PagingSource<Key,Value> {
+    ctor public RxPagingSource();
+    method public final suspend Object? load(androidx.paging.PagingSource.LoadParams<Key> params, kotlin.coroutines.Continuation<? super androidx.paging.PagingSource.LoadResult<Key,Value>> p);
+    method public abstract io.reactivex.rxjava3.core.Single<androidx.paging.PagingSource.LoadResult<Key,Value>> loadSingle(androidx.paging.PagingSource.LoadParams<Key> params);
+  }
+
+  @androidx.paging.ExperimentalPagingApi public abstract class RxRemoteMediator<Key, Value> extends androidx.paging.RemoteMediator<Key,Value> {
+    ctor public RxRemoteMediator();
+    method public final suspend Object? initialize(kotlin.coroutines.Continuation<? super androidx.paging.RemoteMediator.InitializeAction> p);
+    method public io.reactivex.rxjava3.core.Single<androidx.paging.RemoteMediator.InitializeAction> initializeSingle();
+    method public final suspend Object? load(androidx.paging.LoadType loadType, androidx.paging.PagingState<Key,Value> state, kotlin.coroutines.Continuation<? super androidx.paging.RemoteMediator.MediatorResult> p);
+    method public abstract io.reactivex.rxjava3.core.Single<androidx.paging.RemoteMediator.MediatorResult> loadSingle(androidx.paging.LoadType loadType, androidx.paging.PagingState<Key,Value> state);
+  }
+
+}
+
diff --git a/paging/rxjava3/build.gradle b/paging/rxjava3/build.gradle
new file mode 100644
index 0000000..d7895df
--- /dev/null
+++ b/paging/rxjava3/build.gradle
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+import androidx.build.AndroidXExtension
+import androidx.build.LibraryGroups
+import androidx.build.Publish
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+import static androidx.build.dependencies.DependenciesKt.*
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("kotlin-android")
+}
+
+dependencies {
+    api(project(":paging:paging-common"))
+    api("androidx.arch.core:core-runtime:2.1.0")
+    api(RX_JAVA3)
+    implementation(KOTLIN_STDLIB)
+    implementation(KOTLIN_COROUTINES_RX3)
+
+    testImplementation(project(":internal-testutils-common"))
+    testImplementation(project(":internal-testutils-paging"))
+    testImplementation(JUNIT)
+    testImplementation(KOTLIN_TEST)
+    testImplementation(KOTLIN_COROUTINES_TEST)
+}
+
+androidx {
+    name = "Android Paging-RxJava3"
+    publish = Publish.SNAPSHOT_AND_RELEASE
+    mavenGroup = LibraryGroups.PAGING
+    inceptionYear = "2020"
+    description = "Android Paging-RxJava3"
+    url = AndroidXExtension.ARCHITECTURE_URL
+}
+
+// Allow usage of Kotlin's @OptIn.
+tasks.withType(KotlinCompile).configureEach {
+    kotlinOptions {
+        freeCompilerArgs += ["-Xopt-in=kotlin.RequiresOptIn"]
+    }
+}
diff --git a/paging/rxjava3/src/main/AndroidManifest.xml b/paging/rxjava3/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..df90e392
--- /dev/null
+++ b/paging/rxjava3/src/main/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="androidx.paging.rxjava3">
+</manifest>
diff --git a/paging/rxjava3/src/main/java/androidx/paging/rxjava3/PagingRx.kt b/paging/rxjava3/src/main/java/androidx/paging/rxjava3/PagingRx.kt
new file mode 100644
index 0000000..5c351c2
--- /dev/null
+++ b/paging/rxjava3/src/main/java/androidx/paging/rxjava3/PagingRx.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:JvmName("PagingRx")
+
+package androidx.paging.rxjava3
+
+import androidx.paging.Pager
+import androidx.paging.PagingData
+import androidx.paging.cachedIn
+import io.reactivex.rxjava3.core.BackpressureStrategy
+import io.reactivex.rxjava3.core.Flowable
+import io.reactivex.rxjava3.core.Observable
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.reactive.asFlow
+import kotlinx.coroutines.rx3.asFlowable
+import kotlinx.coroutines.rx3.asObservable
+
+/**
+ * An [Observable] of [PagingData], which mirrors the stream provided by [Pager.flow], but exposes
+ * it as an [Observable].
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+val <Key : Any, Value : Any> Pager<Key, Value>.observable: Observable<PagingData<Value>>
+    get() = flow
+        .conflate()
+        .asObservable()
+
+/**
+ * A [Flowable] of [PagingData], which mirrors the stream provided by [Pager.flow], but exposes
+ * it as a [Flowable].
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+val <Key : Any, Value : Any> Pager<Key, Value>.flowable: Flowable<PagingData<Value>>
+    get() = flow
+        .conflate()
+        .asFlowable()
+
+/**
+ * Operator which caches an [Observable] of [PagingData] within a [CoroutineScope].
+ *
+ * @param scope The [CoroutineScope] where the page cache will be kept alive. Typically this
+ * would be a managed scope such as `ViewModel.viewModelScope`, which automatically cancels after
+ * the [PagingData] stream is no longer needed. Otherwise, the provided [CoroutineScope] must be
+ * manually cancelled to avoid memory leaks.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+fun <T : Any> Observable<PagingData<T>>.cachedIn(scope: CoroutineScope): Observable<PagingData<T>> {
+    return toFlowable(BackpressureStrategy.LATEST)
+        .asFlow()
+        .cachedIn(scope)
+        .asObservable()
+}
+
+/**
+ * Operator which caches a [Flowable] of [PagingData] within a [CoroutineScope].
+ *
+ * @param scope The [CoroutineScope] where the page cache will be kept alive. Typically this
+ * would be a managed scope such as `ViewModel.viewModelScope`, which automatically cancels after
+ * the [PagingData] stream is no longer needed. Otherwise, the provided [CoroutineScope] must be
+ * manually cancelled to avoid memory leaks.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+fun <T : Any> Flowable<PagingData<T>>.cachedIn(scope: CoroutineScope): Flowable<PagingData<T>> {
+    return asFlow()
+        .cachedIn(scope)
+        .asFlowable()
+}
diff --git a/paging/rxjava3/src/main/java/androidx/paging/rxjava3/RxPagingSource.kt b/paging/rxjava3/src/main/java/androidx/paging/rxjava3/RxPagingSource.kt
new file mode 100644
index 0000000..cb53bfc
--- /dev/null
+++ b/paging/rxjava3/src/main/java/androidx/paging/rxjava3/RxPagingSource.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.paging.rxjava3
+
+import androidx.paging.PagingSource
+import io.reactivex.rxjava3.core.Single
+import kotlinx.coroutines.rx3.await
+
+/**
+ * Rx-based compatibility wrapper around [PagingSource]'s suspending APIs.
+ *
+ * @sample androidx.paging.samples.rxPagingSourceSample
+ */
+abstract class RxPagingSource<Key : Any, Value : Any> : PagingSource<Key, Value>() {
+    /**
+     * Loading API for [PagingSource].
+     *
+     * Implement this method to trigger your async load (e.g. from database or network).
+     */
+    abstract fun loadSingle(params: LoadParams<Key>): Single<LoadResult<Key, Value>>
+
+    final override suspend fun load(params: LoadParams<Key>): LoadResult<Key, Value> {
+        return loadSingle(params).await()
+    }
+}
\ No newline at end of file
diff --git a/paging/rxjava3/src/main/java/androidx/paging/rxjava3/RxRemoteMediator.kt b/paging/rxjava3/src/main/java/androidx/paging/rxjava3/RxRemoteMediator.kt
new file mode 100644
index 0000000..1d65a7f
--- /dev/null
+++ b/paging/rxjava3/src/main/java/androidx/paging/rxjava3/RxRemoteMediator.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.paging.rxjava3
+
+import androidx.paging.ExperimentalPagingApi
+import androidx.paging.LoadState
+import androidx.paging.LoadType
+import androidx.paging.PagingData
+import androidx.paging.PagingSource
+import androidx.paging.PagingState
+import androidx.paging.RemoteMediator
+import androidx.paging.RemoteMediator.InitializeAction.LAUNCH_INITIAL_REFRESH
+import androidx.paging.RemoteMediator.MediatorResult
+import io.reactivex.rxjava3.core.Single
+import kotlinx.coroutines.rx3.await
+
+/**
+ * RxJava3 compatibility wrapper around [RemoteMediator]'s suspending APIs.
+ */
+@ExperimentalPagingApi
+abstract class RxRemoteMediator<Key : Any, Value : Any> : RemoteMediator<Key, Value>() {
+    /**
+     * Implement this method to load additional remote data, which will then be stored for the
+     * [PagingSource] to access. These loads take one of two forms:
+     *  * type == [LoadType.PREPEND] / [LoadType.APPEND]
+     *  The [PagingSource] has loaded a 'boundary' page, with a `null` adjacent key. This means
+     *  this method should load additional remote data to append / prepend as appropriate, and store
+     *  it locally.
+     *  * type == [LoadType.REFRESH]
+     *  The app (or [initialize]) has requested a remote refresh of data. This means the method
+     *  should generally load remote data, and **replace** all local data.
+     *
+     * The runtime of this method defines loading state behavior in boundary conditions, which
+     * affects e.g., [LoadState] callbacks registered to [androidx.paging.PagingDataAdapter].
+     *
+     * NOTE: A [PagingSource.load] request which is fulfilled by a page that hits a boundary
+     * condition in either direction will trigger this callback with [LoadType.PREPEND] or
+     * [LoadType.APPEND] or both. [LoadType.REFRESH] occurs as a result of [initialize].
+     *
+     * @param loadType [LoadType] of the boundary condition which triggered this callback.
+     *  * [LoadType.PREPEND] indicates a boundary condition at the front of the list.
+     *  * [LoadType.APPEND] indicates a boundary condition at the end of the list.
+     *  * [LoadType.REFRESH] indicates this callback was triggered as the result of a requested
+     *  refresh - either driven by the UI, or by [initialize].
+     * @param state A copy of the state including the list of pages currently held in
+     * memory of the currently presented [PagingData] at the time of starting the load. E.g. for
+     * load(loadType = END), you can use the page or item at the end as input for what to load from
+     * the network.
+     *
+     * @return [MediatorResult] signifying what [LoadState] to be passed to the UI, and whether
+     * there's more data available.
+     */
+    abstract fun loadSingle(
+        loadType: LoadType,
+        state: PagingState<Key, Value>
+    ): Single<MediatorResult>
+
+    /**
+     * Callback fired during initialization of a [PagingData] stream, before initial load.
+     *
+     * This function runs to completion before any loading is performed.
+     *
+     * @return [InitializeAction] indicating the action to take after initialization:
+     *  * [LAUNCH_INITIAL_REFRESH] to immediately dispatch a [load] asynchronously with load type
+     *  [LoadType.REFRESH], to update paginated content when the stream is initialized.
+     *  * [SKIP_INITIAL_REFRESH][InitializeAction.SKIP_INITIAL_REFRESH] to wait for a
+     *  refresh request from the UI before dispatching a [load] with load type [LoadType.REFRESH].
+     */
+    open fun initializeSingle(): Single<InitializeAction> = Single.just(LAUNCH_INITIAL_REFRESH)
+
+    final override suspend fun load(loadType: LoadType, state: PagingState<Key, Value>):
+            MediatorResult {
+        return loadSingle(loadType, state).await()
+    }
+
+    final override suspend fun initialize(): InitializeAction {
+        return initializeSingle().await()
+    }
+}
\ No newline at end of file
diff --git a/paging/rxjava3/src/test/java/androidx/paging/RxPagingSourceTest.kt b/paging/rxjava3/src/test/java/androidx/paging/RxPagingSourceTest.kt
new file mode 100644
index 0000000..149b4bf
--- /dev/null
+++ b/paging/rxjava3/src/test/java/androidx/paging/RxPagingSourceTest.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.paging
+
+import androidx.paging.PagingSource.LoadResult.Page
+import androidx.paging.rxjava3.RxPagingSource
+import io.reactivex.rxjava3.core.Single
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
+
+@RunWith(JUnit4::class)
+class RxPagingSourceTest {
+    private fun loadInternal(params: PagingSource.LoadParams<Int>): Page<Int, Int> {
+        val key = params.key!! // Intentionally fail on null keys
+        return Page(
+            List(params.loadSize) { it + key },
+            prevKey = key - params.loadSize,
+            nextKey = key + params.loadSize
+        )
+    }
+
+    private val pagingSource = object : PagingSource<Int, Int>() {
+        override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Int> {
+            return loadInternal(params)
+        }
+    }
+
+    private val rxPagingSource = object : RxPagingSource<Int, Int>() {
+        override fun loadSingle(params: LoadParams<Int>): Single<LoadResult<Int, Int>> {
+            return Single.create { emitter ->
+                emitter.onSuccess(loadInternal(params))
+            }
+        }
+    }
+
+    @Test
+    fun basic() = runBlocking {
+        val params = PagingSource.LoadParams.Refresh(0, 2, false, 2)
+        assertEquals(pagingSource.load(params), rxPagingSource.load(params))
+    }
+
+    @Test
+    fun error() {
+        runBlocking {
+            val params = PagingSource.LoadParams.Refresh<Int>(null, 2, false, 2)
+            assertFailsWith<NullPointerException> { pagingSource.load(params) }
+            assertFailsWith<NullPointerException> { rxPagingSource.load(params) }
+        }
+    }
+}
\ No newline at end of file
diff --git a/paging/rxjava3/src/test/java/androidx/paging/RxRemoteMediatorTest.kt b/paging/rxjava3/src/test/java/androidx/paging/RxRemoteMediatorTest.kt
new file mode 100644
index 0000000..e7cd095
--- /dev/null
+++ b/paging/rxjava3/src/test/java/androidx/paging/RxRemoteMediatorTest.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.paging
+
+import androidx.paging.RemoteMediator.InitializeAction.LAUNCH_INITIAL_REFRESH
+import androidx.paging.RemoteMediator.InitializeAction.SKIP_INITIAL_REFRESH
+import androidx.paging.rxjava3.RxRemoteMediator
+import io.reactivex.rxjava3.core.Single
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runBlockingTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import kotlin.test.assertEquals
+import kotlin.test.fail
+
+@OptIn(ExperimentalCoroutinesApi::class, ExperimentalPagingApi::class)
+@RunWith(JUnit4::class)
+class RxRemoteMediatorTest {
+    @Test
+    fun initializeSingle() = runBlockingTest {
+        val remoteMediator = object : RxRemoteMediator<Int, Int>() {
+            override fun loadSingle(
+                loadType: LoadType,
+                state: PagingState<Int, Int>
+            ): Single<MediatorResult> {
+                fail("Unexpected call")
+            }
+
+            override fun initializeSingle(): Single<InitializeAction> {
+                return Single.just(SKIP_INITIAL_REFRESH)
+            }
+        }
+
+        assertEquals(SKIP_INITIAL_REFRESH, remoteMediator.initialize())
+    }
+
+    @Test
+    fun initializeSingleDefault() = runBlockingTest {
+        val remoteMediator = object : RxRemoteMediator<Int, Int>() {
+            override fun loadSingle(
+                loadType: LoadType,
+                state: PagingState<Int, Int>
+            ): Single<MediatorResult> {
+                fail("Unexpected call")
+            }
+        }
+
+        assertEquals(LAUNCH_INITIAL_REFRESH, remoteMediator.initialize())
+    }
+}
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/StaggeredGridLayoutManagerTest.java b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/StaggeredGridLayoutManagerTest.java
index 783307e..ae80cc9 100644
--- a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/StaggeredGridLayoutManagerTest.java
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/StaggeredGridLayoutManagerTest.java
@@ -1420,47 +1420,4 @@
                 Math.max(start, end), event.getToIndex());
 
     }
-
-    @Test
-    public void testItemDecorationLayoutParamSpanIndices() throws Throwable {
-        // This test validates that item decorators attached to the associated recycler view are
-        // invoked at the correct time (relative to the layout process) and more importantly,
-        // with the correct span indices readable on them.
-        Config config = new Config().spanCount(2).itemCount(20);
-        setupByConfig(config);
-
-        waitFirstLayout();
-
-        // Add an item decoration that can act as a validator for the values received.
-        mActivityRule.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
-                    @Override
-                    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view,
-                            @NonNull RecyclerView recyclerView, @NonNull RecyclerView.State state) {
-                        super.getItemOffsets(outRect, view, recyclerView, state);
-
-                        final int position = recyclerView.getChildAdapterPosition(view);
-                        final StaggeredGridLayoutManager.LayoutParams layoutParams =
-                                (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();
-
-                        if (position % 2 == 0) {
-                            // Even items should be on the left side
-                            assertEquals(0, layoutParams.getSpanIndex());
-                        } else {
-                            // Odd items should be on the right side
-                            assertEquals(1, layoutParams.getSpanIndex());
-                        }
-                    }
-                });
-            }
-        });
-
-        // Move an item to trigger a change in both position and span index.
-        mAdapter.moveAndNotify(1, 4);
-
-        requestLayoutOnUIThread(mRecyclerView);
-        mLayoutManager.waitForLayout(2000);
-    }
 }
diff --git a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
index eb7ef602..aa794c8 100644
--- a/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
+++ b/recyclerview/recyclerview/src/main/java/androidx/recyclerview/widget/RecyclerView.java
@@ -5249,10 +5249,7 @@
             return lp.mDecorInsets;
         }
 
-        final boolean positionWillChange =
-                (lp.mViewHolder.getLayoutPosition() != lp.mViewHolder.getAbsoluteAdapterPosition());
-        if (mState.isPreLayout()
-                && (lp.isItemChanged() || lp.isViewInvalid() || positionWillChange)) {
+        if (mState.isPreLayout() && (lp.isItemChanged() || lp.isViewInvalid())) {
             // changed/invalid items should not be updated until they are rebound.
             return lp.mDecorInsets;
         }
diff --git a/room/compiler-xprocessing/build.gradle b/room/compiler-xprocessing/build.gradle
new file mode 100644
index 0000000..a0a2ac3
--- /dev/null
+++ b/room/compiler-xprocessing/build.gradle
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.AndroidXExtension
+import androidx.build.CompilationTarget
+import androidx.build.LibraryGroups
+import androidx.build.Publish
+
+import static androidx.build.dependencies.DependenciesKt.*
+
+plugins {
+    id("AndroidXPlugin")
+    id("kotlin")
+}
+
+dependencies {
+    implementation("androidx.annotation:annotation:1.1.0")
+    implementation(GUAVA)
+    implementation(KOTLIN_STDLIB)
+    implementation(AUTO_COMMON)
+    implementation(AUTO_VALUE_ANNOTATIONS)
+    implementation(JAVAPOET)
+    implementation(KOTLIN_METADATA_JVM)
+    implementation(INTELLIJ_ANNOTATIONS)
+
+    testImplementation(GOOGLE_COMPILE_TESTING)
+    testImplementation(JUNIT)
+    testImplementation(JSR250)
+    testImplementation(KOTLIN_COMPILE_TESTING)
+}
+
+compileKotlin {
+    kotlinOptions {
+        freeCompilerArgs += "-Xopt-in=kotlin.contracts.ExperimentalContracts"
+    }
+}
+androidx {
+    name = "AndroidX Room XProcessor"
+    publish = Publish.NONE
+    toolingProject = true
+    mavenGroup = LibraryGroups.ROOM
+    inceptionYear = "2020"
+    description = "Processing Environment Abstraction for AndroidX Room"
+    url = AndroidXExtension.ARCHITECTURE_URL
+    compilationTarget = CompilationTarget.HOST
+}
\ No newline at end of file
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/JavaPoetExt.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/JavaPoetExt.kt
new file mode 100644
index 0000000..3e6ac41
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/JavaPoetExt.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.room.processing
+
+import androidx.room.processing.javac.JavacElement
+import androidx.room.processing.javac.JavacExecutableElement
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeSpec
+import javax.lang.model.element.Modifier
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeMirror
+
+/**
+ * Javapoet does not model NonType, unlike javac, which makes it hard to rely on TypeName for
+ * common functionality (e.g. ability to implement XType.isLong as typename() == TypeName.LONG
+ * instead of in the base class)
+ *
+ * For those cases, we have this hacky type so that we can always query TypeName on an XType.
+ *
+ * We should still strive to avoid these cases, maybe turn it to an error in tests.
+ */
+private val NONE_TYPE_NAME = ClassName.get("androidx.room.processing.error", "NotAType")
+
+internal fun TypeMirror.safeTypeName(): TypeName = if (kind == TypeKind.NONE) {
+    NONE_TYPE_NAME
+} else {
+    TypeName.get(this)
+}
+
+fun TypeSpec.Builder.addOriginatingElement(element: XElement) {
+    if (element is JavacElement) {
+        this.addOriginatingElement(element.element)
+    }
+}
+
+object MethodSpecHelper {
+    /**
+     * Custom override where we mark parameters as final, overidden method as public and also
+     * preserve method names from kotlin metadata.
+     */
+    fun overridingWithFinalParams(
+        elm: XExecutableElement,
+        owner: XDeclaredType
+    ): MethodSpec.Builder {
+        return overridingWithFinalParams(
+            elm,
+            elm.asMemberOf(owner)
+        )
+    }
+
+    private fun overridingWithFinalParams(
+        executableElement: XExecutableElement,
+        resolvedType: XExecutableType = executableElement.executableType
+    ): MethodSpec.Builder {
+        return MethodSpec.methodBuilder(executableElement.name).apply {
+            addTypeVariables(
+                resolvedType.typeVariableNames
+            )
+            resolvedType.parameterTypes.forEachIndexed { index, paramType ->
+                addParameter(
+                    ParameterSpec.builder(
+                        paramType.typeName,
+                        executableElement.parameters[index].name,
+                        Modifier.FINAL
+                    ).build()
+                )
+            }
+            if (executableElement.isPublic()) {
+                addModifiers(Modifier.PUBLIC)
+            } else if (executableElement.isProtected()) {
+                addModifiers(Modifier.PROTECTED)
+            }
+            addAnnotation(Override::class.java)
+            varargs(executableElement.isVarArgs())
+            if (executableElement is JavacExecutableElement) {
+                // copy throws for java
+                executableElement.element.thrownTypes.forEach {
+                    addException(TypeName.get(it))
+                }
+            }
+            returns(resolvedType.returnType.typeName)
+        }
+    }
+}
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/JavacTestProcessor.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/JavacTestProcessor.kt
new file mode 100644
index 0000000..89d6d13
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/JavacTestProcessor.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.room.processing
+
+import androidx.annotation.VisibleForTesting
+import androidx.room.processing.javac.JavacProcessingEnv
+import androidx.room.processing.javac.JavacRoundEnv
+import javax.annotation.processing.AbstractProcessor
+import javax.annotation.processing.RoundEnvironment
+import javax.lang.model.element.TypeElement
+
+/**
+ * Javac processor implementation that provides access to the round environment.
+ *
+ * This is only used in tests, the main processor uses an API similar to the processing step
+ * in Auto.
+ */
+@VisibleForTesting
+abstract class JavacTestProcessor : AbstractProcessor() {
+    val xProcessingEnv by lazy {
+        XProcessingEnv.create(super.processingEnv)
+    }
+
+    final override fun process(
+        annotations: MutableSet<out TypeElement>,
+        roundEnv: RoundEnvironment
+    ): Boolean {
+        if (roundEnv.processingOver()) {
+            return true
+        }
+        val env = xProcessingEnv as JavacProcessingEnv
+        val javacRoundEnv = JavacRoundEnv(env, roundEnv)
+        val xAnnotations = annotations.mapTo(mutableSetOf()) {
+            env.wrapTypeElement(it)
+        }
+        return doProcess(xAnnotations, javacRoundEnv)
+    }
+
+    abstract fun doProcess(
+        annotations: Set<XTypeElement>,
+        roundEnv: XRoundEnv
+    ): Boolean
+}
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/XAnnotationBox.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XAnnotationBox.kt
new file mode 100644
index 0000000..7a7ced9
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XAnnotationBox.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+/**
+ * This wraps an annotation element that is both accessible from the processor and runtime.
+ *
+ * It won't scale to a general purpose processing APIs where an equivelant of the AnnotationMirror
+ * API needs to be provided but works well for Room's case.
+ */
+interface XAnnotationBox<T> {
+    /**
+     * The value field of the annotation
+     */
+    val value: T
+
+    /**
+     * Returns the value of the given [methodName] as a type reference.
+     */
+    fun getAsType(methodName: String): XType?
+
+    /**
+     * Returns the value of the given [methodName] as a list of type references.
+     */
+    fun getAsTypeList(methodName: String): List<XType>
+
+    /**
+     * Returns the value of the given [methodName] as another boxed annotation.
+     */
+    fun <T : Annotation> getAsAnnotationBox(methodName: String): XAnnotationBox<T>
+
+    /**
+     * Returns the value of the given [methodName] as an array of boxed annotations.
+     */
+    fun <T : Annotation> getAsAnnotationBoxArray(methodName: String): Array<XAnnotationBox<T>>
+}
\ No newline at end of file
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/XArrayType.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XArrayType.kt
new file mode 100644
index 0000000..dd39007
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XArrayType.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+interface XArrayType : XType {
+    val componentType: XType
+}
\ No newline at end of file
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/XDeclaredType.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XDeclaredType.kt
new file mode 100644
index 0000000..dc1196d
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XDeclaredType.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+interface XDeclaredType : XType {
+    val typeArguments: List<XType>
+}
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/XElement.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XElement.kt
new file mode 100644
index 0000000..04b8180
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XElement.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+import kotlin.contracts.contract
+import kotlin.reflect.KClass
+
+interface XElement {
+    val name: String
+
+    val packageName: String
+
+    /**
+     * TODO:
+     *  Nullability is normally a property of Type not Element but currently Room relies on
+     *  Annotations to resolve nullability which exists only on Elements, not Types.
+     *  Once we implement KSP version, we might be able to move this to the type by making sure
+     *  we carry over nullability when type is resolved from an Element. We also need nullability
+     *  on Types to properly handle DAO return types (e.g. Flow<T> vs Flow<T?>)
+     */
+    val nullability: XNullability
+
+    val enclosingElement: XElement?
+
+    fun isPublic(): Boolean
+
+    fun isProtected(): Boolean
+
+    fun isAbstract(): Boolean
+
+    fun isPrivate(): Boolean
+
+    fun isStatic(): Boolean
+
+    fun isTransient(): Boolean
+
+    fun isFinal(): Boolean
+
+    fun kindName(): String
+
+    fun <T : Annotation> toAnnotationBox(annotation: KClass<T>): XAnnotationBox<T>?
+
+    // a very sad method but helps avoid abstraction annotation
+    fun hasAnnotationInPackage(pkg: String): Boolean
+
+    fun hasAnnotation(annotation: KClass<out Annotation>): Boolean
+
+    fun hasAnyOf(vararg annotations: KClass<out Annotation>) = annotations.any(this::hasAnnotation)
+
+    fun isNonNull() = nullability == XNullability.NONNULL
+
+    fun asTypeElement() = this as XTypeElement
+
+    fun asVariableElement() = this as XVariableElement
+
+    fun asExecutableElement() = this as XExecutableElement
+
+    fun asDeclaredType(): XDeclaredType {
+        return asTypeElement().type
+    }
+}
+
+// we keep these as extension methods to be able to use contracts
+fun XElement.isType(): Boolean {
+    contract {
+        returns(true) implies (this@isType is XTypeElement)
+    }
+    return this is XTypeElement
+}
+
+fun XElement.isField(): Boolean {
+    contract {
+        returns(true) implies (this@isField is XVariableElement)
+    }
+    return this is XVariableElement
+}
+
+fun XElement.isMethod(): Boolean {
+    contract {
+        returns(true) implies (this@isMethod is XExecutableElement)
+    }
+    return this is XExecutableElement
+}
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/XEquality.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XEquality.kt
new file mode 100644
index 0000000..fc3af21
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XEquality.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+/**
+ * Helper interface to enforce implementing equality in wrappers so that we don't by mistake
+ * create wrappers that do not properly handle equality.
+ *
+ * Enforcement is done in JavacType and JavacElement
+ */
+internal interface XEquality {
+    val equalityItems: Array<out Any?>
+
+    companion object {
+        fun hashCode(elements: Array<out Any?>): Int {
+            return elements.contentHashCode()
+        }
+
+        fun equals(first: Any?, second: Any?): Boolean {
+            if (first !is XEquality || second !is XEquality) {
+                return false
+            }
+            return equals(first.equalityItems, second.equalityItems)
+        }
+
+        fun equals(first: Array<out Any?>, second: Array<out Any?>): Boolean {
+            // TODO there is probably a better way to do this
+            if (first.size != second.size) {
+                return false
+            }
+            repeat(first.size) {
+                if (first[it] != second[it]) {
+                    return false
+                }
+            }
+            return true
+        }
+    }
+}
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/XExecutableElement.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XExecutableElement.kt
new file mode 100644
index 0000000..64ab3ff
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XExecutableElement.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+interface XExecutableElement : XElement {
+    override val enclosingElement: XElement
+
+    val parameters: List<XVariableElement>
+
+    val returnType: XType
+
+    val executableType: XExecutableType
+
+    fun isJavaDefault(): Boolean
+
+    fun isVarArgs(): Boolean
+
+    fun asMemberOf(other: XDeclaredType): XExecutableType
+
+    fun findKotlinDefaultImpl(): XExecutableElement?
+
+    fun isSuspendFunction(): Boolean
+
+    fun isOverrideableIgnoringContainer(): Boolean {
+        return !isFinal() && !isPrivate() && !isStatic()
+    }
+
+    fun isConstructor(): Boolean
+}
\ No newline at end of file
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/XExecutableType.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XExecutableType.kt
new file mode 100644
index 0000000..8a26e3a
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XExecutableType.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+import com.squareup.javapoet.TypeVariableName
+
+/**
+ * Represents a type information for an executable.
+ *
+ * It is not an XType as it does not represent a class or primitive.
+ */
+interface XExecutableType {
+    val returnType: XType
+
+    val parameterTypes: List<XType>
+
+    val typeVariableNames: List<TypeVariableName>
+
+    fun getSuspendFunctionReturnType(): XType
+}
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/XMessager.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XMessager.kt
new file mode 100644
index 0000000..0df763c
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XMessager.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+import javax.tools.Diagnostic
+
+interface XMessager {
+    fun printMessage(kind: Diagnostic.Kind, msg: String, element: XElement? = null)
+}
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/XNullability.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XNullability.kt
new file mode 100644
index 0000000..0cdee82
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XNullability.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+enum class XNullability {
+    NULLABLE,
+    NONNULL,
+    UNKNOWN
+}
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/XProcessingEnv.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XProcessingEnv.kt
new file mode 100644
index 0000000..f4ed124
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XProcessingEnv.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+import androidx.room.processing.javac.JavacProcessingEnv
+import com.squareup.javapoet.TypeName
+import javax.annotation.processing.Filer
+import javax.annotation.processing.ProcessingEnvironment
+import kotlin.reflect.KClass
+
+interface XProcessingEnv {
+
+    val messager: XMessager
+
+    val options: Map<String, String>
+
+    val filer: Filer
+
+    fun findTypeElement(qName: String): XTypeElement?
+
+    fun findType(qName: String): XType?
+
+    fun requireType(qName: String): XType = checkNotNull(findType(qName)) {
+        "cannot find required type $qName"
+    }
+
+    fun findGeneratedAnnotation(): XTypeElement?
+
+    fun getDeclaredType(type: XTypeElement, vararg types: XType): XDeclaredType
+
+    fun getArrayType(type: XType): XArrayType
+
+    fun requireTypeElement(qName: String): XTypeElement {
+        return checkNotNull(findTypeElement(qName)) {
+            "Cannot find required type element $qName"
+        }
+    }
+
+    // helpers for smooth migration, these could be extension methods
+    fun requireType(typeName: TypeName) = requireType(typeName.toString())
+
+    fun requireType(klass: KClass<*>) = requireType(klass.java.canonicalName!!)
+
+    fun findType(typeName: TypeName) = findType(typeName.toString())
+
+    fun findType(klass: KClass<*>) = findType(klass.java.canonicalName!!)
+
+    fun requireTypeElement(typeName: TypeName) = requireTypeElement(typeName.toString())
+
+    fun requireTypeElement(klass: KClass<*>) = requireTypeElement(klass.java.canonicalName!!)
+
+    fun findTypeElement(typeName: TypeName) = findTypeElement(typeName.toString())
+
+    fun findTypeElement(klass: KClass<*>) = findTypeElement(klass.java.canonicalName!!)
+
+    fun getArrayType(typeName: TypeName) = getArrayType(
+        requireType(typeName)
+    )
+
+    companion object {
+        fun create(env: ProcessingEnvironment): XProcessingEnv = JavacProcessingEnv(env)
+    }
+}
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/XProcessingStep.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XProcessingStep.kt
new file mode 100644
index 0000000..3cbca26
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XProcessingStep.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+import androidx.room.processing.javac.JavacElement
+import androidx.room.processing.javac.JavacProcessingEnv
+import com.google.auto.common.BasicAnnotationProcessor
+import com.google.auto.common.MoreElements
+import com.google.common.collect.SetMultimap
+import javax.lang.model.element.Element
+import javax.tools.Diagnostic
+import kotlin.reflect.KClass
+
+/**
+ * Specialized processing step which only supports annotations on TypeElements.
+ *
+ * We can generalize it but for now, Room only needs annotations on TypeElements to start
+ * processing.
+ */
+interface XProcessingStep {
+    fun process(
+        env: XProcessingEnv,
+        elementsByAnnotation: Map<KClass<out Annotation>, List<XTypeElement>>
+    ): Set<XTypeElement>
+
+    fun annotations(): Set<KClass<out Annotation>>
+
+    fun asAutoCommonProcessor(
+        env: XProcessingEnv
+    ): BasicAnnotationProcessor.ProcessingStep {
+        return JavacProcessingStepDelegate(
+            env = env as JavacProcessingEnv,
+            delegate = this
+        )
+    }
+}
+
+@Suppress("UnstableApiUsage")
+internal class JavacProcessingStepDelegate(
+    val env: JavacProcessingEnv,
+    val delegate: XProcessingStep
+) : BasicAnnotationProcessor.ProcessingStep {
+    override fun process(
+        elementsByAnnotation: SetMultimap<Class<out Annotation>, Element>
+    ): Set<Element> {
+        val converted = mutableMapOf<KClass<out Annotation>, List<XTypeElement>>()
+        annotations().forEach { annotation ->
+            val elements = elementsByAnnotation[annotation].mapNotNull { element ->
+                if (MoreElements.isType(element)) {
+                    env.wrapTypeElement(MoreElements.asType(element))
+                } else {
+                    env.delegate.messager.printMessage(
+                        Diagnostic.Kind.ERROR,
+                        "Unsupported element type: ${element.kind}",
+                        element
+                    )
+                    null
+                }
+            }
+            converted[annotation.kotlin] = elements
+        }
+        val result = delegate.process(env, converted)
+        return result.map {
+            (it as JavacElement).element
+        }.toSet()
+    }
+
+    override fun annotations(): Set<Class<out Annotation>> {
+        return delegate.annotations().mapTo(mutableSetOf()) {
+            it.java
+        }
+    }
+}
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/XRoundEnv.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XRoundEnv.kt
new file mode 100644
index 0000000..2e3c0fe
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XRoundEnv.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+import androidx.annotation.VisibleForTesting
+import androidx.room.processing.javac.JavacProcessingEnv
+import androidx.room.processing.javac.JavacRoundEnv
+import javax.annotation.processing.RoundEnvironment
+
+// only used in tests of Room
+@VisibleForTesting
+interface XRoundEnv {
+
+    val rootElements: Set<XElement>
+
+    fun getElementsAnnotatedWith(klass: Class<out Annotation>): Set<XElement>
+
+    companion object {
+        fun create(
+            processingEnv: XProcessingEnv,
+            roundEnvironment: RoundEnvironment
+        ): XRoundEnv {
+            check(processingEnv is JavacProcessingEnv)
+            return JavacRoundEnv(processingEnv, roundEnvironment)
+        }
+    }
+}
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/XType.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XType.kt
new file mode 100644
index 0000000..24b1499
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XType.kt
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+import com.squareup.javapoet.TypeName
+import kotlin.contracts.contract
+import kotlin.reflect.KClass
+
+interface XType {
+    val typeName: TypeName
+
+    fun asTypeElement(): XTypeElement
+
+    fun isAssignableFrom(other: XType): Boolean
+
+    fun isAssignableFromWithoutVariance(other: XType): Boolean {
+        return isAssignableWithoutVariance(other, this)
+    }
+
+    // TODO these is<Type> checks may need to be moved into the implementation.
+    //  It is not yet clear how we will model some types in Kotlin (e.g. primitives)
+    fun isNotByte() = !isByte()
+
+    fun isError(): Boolean
+
+    fun defaultValue(): String
+
+    fun boxed(): XType
+
+    fun asArray(): XArrayType = this as XArrayType
+
+    fun isPrimitiveInt(): Boolean {
+        return typeName == TypeName.INT
+    }
+
+    fun isBoxedInt() = typeName == TypeName.INT.box()
+
+    fun isInt() = isPrimitiveInt() || isBoxedInt()
+
+    fun isPrimitiveLong() = typeName == TypeName.LONG
+
+    fun isBoxedLong() = typeName == TypeName.LONG.box()
+
+    fun isLong() = isPrimitiveLong() || isBoxedLong()
+
+    fun isList(): Boolean = isType() && isTypeOf(List::class)
+
+    fun isVoid() = typeName == TypeName.VOID
+
+    fun isVoidObject(): Boolean = isType() && isTypeOf(Void::class)
+
+    fun isPrimitive() = typeName.isPrimitive
+
+    fun isKotlinUnit(): Boolean = isType() && isTypeOf(Unit::class)
+
+    fun isNotVoid() = !isVoid()
+
+    fun isNotError() = !isError()
+
+    fun isByte() = typeName == TypeName.BYTE
+
+    fun isNone(): Boolean
+
+    fun isNotNone() = !isNone()
+
+    fun isType(): Boolean
+
+    fun isTypeOf(other: KClass<*>): Boolean
+
+    fun isSameType(other: XType): Boolean
+
+    fun extendsBoundOrSelf(): XType = extendsBound() ?: this
+
+    fun isAssignableWithoutVariance(other: XType): Boolean {
+        return isAssignableWithoutVariance(other, this)
+    }
+
+    fun erasure(): XType
+
+    fun extendsBound(): XType?
+}
+
+fun XType.isDeclared(): Boolean {
+    contract {
+        returns(true) implies (this@isDeclared is XDeclaredType)
+    }
+    return this is XDeclaredType
+}
+
+fun XType.isArray(): Boolean {
+    contract {
+        returns(true) implies (this@isArray is XArrayType)
+    }
+    return this is XArrayType
+}
+
+fun XType.isCollection(): Boolean {
+    contract {
+        returns(true) implies (this@isCollection is XDeclaredType)
+    }
+    return isType() && (isTypeOf(List::class) || isTypeOf(Set::class))
+}
+
+fun XType.asDeclaredType() = this as XDeclaredType
+
+private fun isAssignableWithoutVariance(from: XType, to: XType): Boolean {
+    val assignable = to.isAssignableFrom(from)
+    if (assignable) {
+        return true
+    }
+    if (!from.isDeclared() || !to.isDeclared()) {
+        return false
+    }
+    val declaredFrom = from.asDeclaredType()
+    val declaredTo = to.asDeclaredType()
+    val fromTypeArgs = declaredFrom.typeArguments
+    val toTypeArgs = declaredTo.typeArguments
+    // no type arguments, we don't need extra checks
+    if (fromTypeArgs.isEmpty() || fromTypeArgs.size != toTypeArgs.size) {
+        return false
+    }
+    // check erasure version first, if it does not match, no reason to proceed
+    if (!to.erasure().isAssignableFrom(from.erasure())) {
+        return false
+    }
+    // convert from args to their upper bounds if it exists
+    val fromExtendsBounds = fromTypeArgs.map {
+        it.extendsBound()
+    }
+    // if there are no upper bound conversions, return.
+    if (fromExtendsBounds.all { it == null }) {
+        return false
+    }
+    // try to move the types of the from to their upper bounds. It does not matter for the "to"
+    // because Types.isAssignable handles it as it is valid java
+    return (fromTypeArgs.indices).all { index ->
+        isAssignableWithoutVariance(
+            from = fromExtendsBounds[index] ?: fromTypeArgs[index],
+            to = toTypeArgs[index]
+        )
+    }
+}
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/XTypeElement.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XTypeElement.kt
new file mode 100644
index 0000000..320821c
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XTypeElement.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+import com.squareup.javapoet.ClassName
+
+interface XTypeElement : XElement {
+    val qualifiedName: String
+
+    val type: XDeclaredType
+
+    val superType: XType?
+
+    val className: ClassName
+
+    fun isInterface(): Boolean
+
+    fun isKotlinObject(): Boolean
+
+    /**
+     * All fields, including private supers.
+     * Room only ever reads fields this way.
+     */
+    fun getAllFieldsIncludingPrivateSupers(): List<XVariableElement>
+
+    // only in kotlin
+    fun findPrimaryConstructor(): XExecutableElement?
+
+    /**
+     * methods declared in this type
+     *  includes all instance/static methods in this
+     */
+    fun getDeclaredMethods(): List<XExecutableElement>
+
+    /**
+     * Methods declared in this type and its parents
+     *  includes all instance/static methods in this
+     *  includes all instance/static methods in parent CLASS if they are accessible from this (e.g. not
+     *  private).
+     *  does not include static methods in parent interfaces
+     */
+    fun getAllMethods(): List<XExecutableElement>
+
+    /**
+     * Instance methods declared in this and supers
+     *  include non private instance methods
+     *  also includes non-private instance methods from supers
+     */
+    fun getAllNonPrivateInstanceMethods(): List<XExecutableElement>
+
+    fun getConstructors(): List<XExecutableElement>
+}
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/XVariableElement.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XVariableElement.kt
new file mode 100644
index 0000000..84cc29d
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/XVariableElement.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+interface XVariableElement : XElement {
+    val type: XType
+
+    fun asMemberOf(other: XDeclaredType): XType
+}
\ No newline at end of file
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/DefaultJavacType.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/DefaultJavacType.kt
new file mode 100644
index 0000000..af56602
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/DefaultJavacType.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.javac
+
+import javax.lang.model.type.TypeMirror
+
+/**
+ * Catch-all class for XType implementation when we don't need/discover a sub-type
+ */
+internal class DefaultJavacType(
+    env: JavacProcessingEnv,
+    typeMirror: TypeMirror
+) : JavacType(
+    env, typeMirror
+) {
+    override val equalityItems by lazy {
+        arrayOf(typeMirror)
+    }
+}
\ No newline at end of file
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/ElementExt.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/ElementExt.kt
new file mode 100644
index 0000000..4ff1abe
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/ElementExt.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.javac
+
+import com.google.auto.common.MoreTypes
+import javax.lang.model.element.TypeElement
+import javax.lang.model.element.VariableElement
+import javax.lang.model.type.TypeKind
+import javax.lang.model.util.ElementFilter
+import javax.lang.model.util.Elements
+
+/**
+ * gets all members including super privates. does not handle duplicate field names!!!
+ */
+// TODO handle conflicts with super: b/35568142
+fun TypeElement.getAllFieldsIncludingPrivateSupers(
+    elementUtils: Elements
+): Set<VariableElement> {
+    val myMembers = ElementFilter.fieldsIn(elementUtils.getAllMembers(this))
+        .filterIsInstance<VariableElement>()
+        .toSet()
+    if (superclass.kind != TypeKind.NONE) {
+        return myMembers + MoreTypes.asTypeElement(superclass)
+            .getAllFieldsIncludingPrivateSupers(elementUtils)
+    } else {
+        return myMembers
+    }
+}
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacAnnotationBox.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacAnnotationBox.kt
new file mode 100644
index 0000000..06e1dda
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacAnnotationBox.kt
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.javac
+
+import androidx.room.processing.XAnnotationBox
+import androidx.room.processing.XType
+import com.google.auto.common.AnnotationMirrors
+import java.lang.reflect.Proxy
+import javax.lang.model.element.AnnotationMirror
+import javax.lang.model.element.AnnotationValue
+import javax.lang.model.element.VariableElement
+import javax.lang.model.type.TypeMirror
+import javax.lang.model.util.SimpleAnnotationValueVisitor6
+
+internal interface JavacClassGetter {
+    fun getAsType(methodName: String): XType?
+    fun getAsTypeList(methodName: String): List<XType>
+    fun <T : Annotation> getAsAnnotationBox(methodName: String): XAnnotationBox<T>
+    fun <T : Annotation> getAsAnnotationBoxArray(methodName: String): Array<XAnnotationBox<T>>
+}
+
+/**
+ * Class that helps to read values from annotations. Simple types as string, int, lists can
+ * be read from [value]. If you need to read classes or another annotations from annotation use
+ * [getAsType], [getAsAnnotationBox] and [getAsAnnotationBoxArray] correspondingly.
+ */
+internal class JavacAnnotationBox<T : Annotation>(obj: Any) : XAnnotationBox<T> {
+    private val classGetter = obj as JavacClassGetter
+
+    @Suppress("UNCHECKED_CAST")
+    override val value: T = obj as T
+    override fun getAsType(methodName: String): XType? = classGetter.getAsType(methodName)
+
+    override fun getAsTypeList(methodName: String): List<XType> =
+        classGetter.getAsTypeList(methodName)
+
+    override fun <T : Annotation> getAsAnnotationBox(methodName: String): XAnnotationBox<T> {
+        return classGetter.getAsAnnotationBox(methodName)
+    }
+
+    override fun <T : Annotation> getAsAnnotationBoxArray(
+        methodName: String
+    ): Array<XAnnotationBox<T>> {
+        return classGetter.getAsAnnotationBoxArray(methodName)
+    }
+}
+
+internal fun <T : Annotation> AnnotationMirror.box(
+    env: JavacProcessingEnv,
+    cl: Class<T>
+): JavacAnnotationBox<T> {
+    if (!cl.isAnnotation) {
+        throw IllegalArgumentException("$cl is not annotation")
+    }
+    val map = cl.declaredMethods.associate { method ->
+        val value = AnnotationMirrors.getAnnotationValue(this, method.name)
+        val returnType = method.returnType
+        val defaultValue = method.defaultValue
+        val result: Any? = when {
+            returnType == Boolean::class.java -> value.getAsBoolean(defaultValue as Boolean)
+            returnType == String::class.java -> value.getAsString(defaultValue as String?)
+            returnType == Array<String>::class.java -> value.getAsStringList().toTypedArray()
+            returnType == emptyArray<Class<*>>()::class.java -> value.toListOfClassTypes(env)
+            returnType == IntArray::class.java -> value.getAsIntList().toIntArray()
+            returnType == Class::class.java -> {
+                try {
+                    value.toClassType(env)
+                } catch (notPresent: TypeNotPresentException) {
+                    null
+                }
+            }
+            returnType == Int::class.java -> value.getAsInt(defaultValue as Int?)
+            returnType.isAnnotation -> {
+                @Suppress("UNCHECKED_CAST")
+                AnnotationClassVisitor(env, returnType as Class<out Annotation>).visit(value)
+            }
+            returnType.isArray && returnType.componentType.isAnnotation -> {
+                @Suppress("UNCHECKED_CAST")
+                ListVisitor(env, returnType.componentType as Class<out Annotation>).visit(value)
+            }
+            returnType.isEnum -> {
+                @Suppress("UNCHECKED_CAST")
+                value.getAsEnum(returnType as Class<out Enum<*>>)
+            }
+            else -> throw UnsupportedOperationException("$returnType isn't supported")
+        }
+        method.name to result
+    }
+    return JavacAnnotationBox(
+        Proxy.newProxyInstance(
+            JavacClassGetter::class.java.classLoader,
+            arrayOf(cl, JavacClassGetter::class.java)
+        ) { _, method, args ->
+            when (method.name) {
+                JavacClassGetter::getAsType.name -> map[args[0]]
+                JavacClassGetter::getAsTypeList.name -> map[args[0]]
+                "getAsAnnotationBox" -> map[args[0]]
+                "getAsAnnotationBoxArray" -> map[args[0]]
+                else -> map[method.name]
+            }
+        })
+}
+
+@Suppress("DEPRECATION")
+private val ANNOTATION_VALUE_TO_INT_VISITOR = object : SimpleAnnotationValueVisitor6<Int?, Void>() {
+    override fun visitInt(i: Int, p: Void?): Int? {
+        return i
+    }
+}
+
+@Suppress("DEPRECATION")
+private val ANNOTATION_VALUE_TO_BOOLEAN_VISITOR = object :
+    SimpleAnnotationValueVisitor6<Boolean?, Void>() {
+    override fun visitBoolean(b: Boolean, p: Void?): Boolean? {
+        return b
+    }
+}
+
+@Suppress("DEPRECATION")
+private val ANNOTATION_VALUE_TO_STRING_VISITOR = object :
+    SimpleAnnotationValueVisitor6<String?, Void>() {
+    override fun visitString(s: String?, p: Void?): String? {
+        return s
+    }
+}
+
+@Suppress("DEPRECATION")
+private val ANNOTATION_VALUE_STRING_ARR_VISITOR = object :
+    SimpleAnnotationValueVisitor6<List<String>, Void>() {
+    override fun visitArray(vals: MutableList<out AnnotationValue>?, p: Void?): List<String> {
+        return vals?.mapNotNull {
+            ANNOTATION_VALUE_TO_STRING_VISITOR.visit(it)
+        } ?: emptyList()
+    }
+}
+
+@Suppress("DEPRECATION")
+private val ANNOTATION_VALUE_INT_ARR_VISITOR = object :
+    SimpleAnnotationValueVisitor6<List<Int>, Void>() {
+    override fun visitArray(vals: MutableList<out AnnotationValue>?, p: Void?): List<Int> {
+        return vals?.mapNotNull {
+            ANNOTATION_VALUE_TO_INT_VISITOR.visit(it)
+        } ?: emptyList()
+    }
+}
+
+private fun AnnotationValue.getAsInt(def: Int? = null): Int? {
+    return ANNOTATION_VALUE_TO_INT_VISITOR.visit(this) ?: def
+}
+
+private fun AnnotationValue.getAsIntList(): List<Int> {
+    return ANNOTATION_VALUE_INT_ARR_VISITOR.visit(this)
+}
+
+private fun AnnotationValue.getAsString(def: String? = null): String? {
+    return ANNOTATION_VALUE_TO_STRING_VISITOR.visit(this) ?: def
+}
+
+private fun AnnotationValue.getAsBoolean(def: Boolean): Boolean {
+    return ANNOTATION_VALUE_TO_BOOLEAN_VISITOR.visit(this) ?: def
+}
+
+private fun AnnotationValue.getAsStringList(): List<String> {
+    return ANNOTATION_VALUE_STRING_ARR_VISITOR.visit(this)
+}
+
+// code below taken from dagger2
+// compiler/src/main/java/dagger/internal/codegen/ConfigurationAnnotations.java
+@Suppress("DEPRECATION")
+private val TO_LIST_OF_TYPES = object :
+    SimpleAnnotationValueVisitor6<List<TypeMirror>, Void?>() {
+    override fun visitArray(values: MutableList<out AnnotationValue>?, p: Void?): List<TypeMirror> {
+        return values?.mapNotNull {
+            val tmp = TO_TYPE.visit(it)
+            tmp
+        } ?: emptyList()
+    }
+
+    override fun defaultAction(o: Any?, p: Void?): List<TypeMirror>? {
+        return emptyList()
+    }
+}
+
+@Suppress("DEPRECATION")
+private val TO_TYPE = object : SimpleAnnotationValueVisitor6<TypeMirror, Void>() {
+
+    override fun visitType(t: TypeMirror, p: Void?): TypeMirror {
+        return t
+    }
+
+    override fun defaultAction(o: Any?, p: Void?): TypeMirror {
+        throw TypeNotPresentException(o!!.toString(), null)
+    }
+}
+
+private fun AnnotationValue.toListOfClassTypes(env: JavacProcessingEnv): List<XType> {
+    return TO_LIST_OF_TYPES.visit(this).map {
+        env.wrap<JavacType>(it)
+    }
+}
+
+private fun AnnotationValue.toClassType(env: JavacProcessingEnv): XType? {
+    return TO_TYPE.visit(this)?.let {
+        env.wrap(it)
+    }
+}
+
+@Suppress("DEPRECATION")
+private class ListVisitor<T : Annotation>(
+    private val env: JavacProcessingEnv,
+    private val annotationClass: Class<T>
+) :
+    SimpleAnnotationValueVisitor6<Array<JavacAnnotationBox<T>>, Void?>() {
+    override fun visitArray(
+        values: MutableList<out AnnotationValue>?,
+        void: Void?
+    ): Array<JavacAnnotationBox<T>> {
+        val visitor = AnnotationClassVisitor(env, annotationClass)
+        return values?.mapNotNull { visitor.visit(it) }?.toTypedArray() ?: emptyArray()
+    }
+}
+
+@Suppress("DEPRECATION")
+private class AnnotationClassVisitor<T : Annotation>(
+    private val env: JavacProcessingEnv,
+    private val annotationClass: Class<T>
+) :
+    SimpleAnnotationValueVisitor6<JavacAnnotationBox<T>?, Void?>() {
+    override fun visitAnnotation(a: AnnotationMirror?, v: Void?) = a?.box(env, annotationClass)
+}
+
+@Suppress("UNCHECKED_CAST", "DEPRECATION")
+private fun <T : Enum<*>> AnnotationValue.getAsEnum(enumClass: Class<T>): T {
+    return object : SimpleAnnotationValueVisitor6<T, Void>() {
+        override fun visitEnumConstant(value: VariableElement?, p: Void?): T {
+            return enumClass.getDeclaredMethod("valueOf", String::class.java)
+                .invoke(null, value!!.simpleName.toString()) as T
+        }
+    }.visit(this)
+}
\ No newline at end of file
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacArrayType.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacArrayType.kt
new file mode 100644
index 0000000..42fe064
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacArrayType.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.javac
+
+import androidx.room.processing.XArrayType
+import androidx.room.processing.XType
+import javax.lang.model.type.ArrayType
+
+internal class JavacArrayType(
+    env: JavacProcessingEnv,
+    override val typeMirror: ArrayType
+) : JavacType(
+    env,
+    typeMirror
+), XArrayType {
+
+    override val equalityItems: Array<out Any?> by lazy {
+        arrayOf(typeMirror)
+    }
+    override val componentType: XType by lazy {
+        env.wrap<JavacType>(typeMirror.componentType)
+    }
+}
\ No newline at end of file
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacDeclaredType.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacDeclaredType.kt
new file mode 100644
index 0000000..835bc7d
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacDeclaredType.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.javac
+
+import androidx.room.processing.XDeclaredType
+import javax.lang.model.type.DeclaredType
+
+internal class JavacDeclaredType(
+    env: JavacProcessingEnv,
+    override val typeMirror: DeclaredType
+) : JavacType(
+    env, typeMirror
+), XDeclaredType {
+    override val equalityItems: Array<out Any?> by lazy {
+        arrayOf(typeMirror)
+    }
+
+    override val typeArguments: List<JavacType> by lazy {
+        env.wrapTypes<JavacType>(typeMirror.typeArguments)
+    }
+}
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacElement.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacElement.kt
new file mode 100644
index 0000000..f4fe020
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacElement.kt
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.javac
+
+import androidx.annotation.NonNull
+import androidx.annotation.Nullable
+import androidx.room.processing.XAnnotationBox
+import androidx.room.processing.XElement
+import androidx.room.processing.XEquality
+import androidx.room.processing.XNullability
+import com.google.auto.common.MoreElements
+import java.util.Locale
+import javax.lang.model.element.Element
+import javax.lang.model.element.Modifier
+import kotlin.reflect.KClass
+
+@Suppress("UnstableApiUsage")
+internal abstract class JavacElement(
+    protected val env: JavacProcessingEnv,
+    open val element: Element
+) : XElement, XEquality {
+
+    override val name: String
+        get() = element.simpleName.toString()
+
+    override val packageName: String
+        get() = MoreElements.getPackage(element).qualifiedName.toString()
+
+    override val nullability: XNullability
+        get() = if (element.asType().kind.isPrimitive ||
+            hasAnnotation(NonNull::class) ||
+            hasAnnotation(org.jetbrains.annotations.NotNull::class)
+        ) {
+            XNullability.NONNULL
+        } else if (hasAnnotation(Nullable::class) ||
+            hasAnnotation(org.jetbrains.annotations.Nullable::class)
+        ) {
+            XNullability.NULLABLE
+        } else {
+            XNullability.UNKNOWN
+        }
+
+    override val enclosingElement: XElement? by lazy {
+        val enclosing = element.enclosingElement
+        if (MoreElements.isType(enclosing)) {
+            env.wrapTypeElement(MoreElements.asType(enclosing))
+        } else {
+            // room only cares if it is another type as we do not model packages
+            // or modules.
+            null
+        }
+    }
+
+    override fun isPublic(): Boolean {
+        return element.modifiers.contains(Modifier.PUBLIC)
+    }
+
+    override fun isProtected(): Boolean {
+        return element.modifiers.contains(Modifier.PROTECTED)
+    }
+
+    override fun isAbstract(): Boolean {
+        return element.modifiers.contains(Modifier.ABSTRACT)
+    }
+
+    override fun isPrivate(): Boolean {
+        return element.modifiers.contains(Modifier.PRIVATE)
+    }
+
+    override fun isStatic(): Boolean {
+        return element.modifiers.contains(Modifier.STATIC)
+    }
+
+    override fun isTransient(): Boolean {
+        return element.modifiers.contains(Modifier.TRANSIENT)
+    }
+
+    override fun isFinal(): Boolean {
+        return element.modifiers.contains(Modifier.FINAL)
+    }
+
+    override fun <T : Annotation> toAnnotationBox(annotation: KClass<T>): XAnnotationBox<T>? {
+        return MoreElements
+            .getAnnotationMirror(element, annotation.java)
+            .orNull()
+            ?.box(env, annotation.java)
+    }
+
+    override fun hasAnnotation(annotation: KClass<out Annotation>): Boolean {
+        return MoreElements.isAnnotationPresent(element, annotation.java)
+    }
+
+    override fun toString(): String {
+        return element.toString()
+    }
+
+    override fun equals(other: Any?): Boolean {
+        return XEquality.equals(this, other)
+    }
+
+    override fun hashCode(): Int {
+        return XEquality.hashCode(equalityItems)
+    }
+
+    override fun kindName(): String {
+        return element.kind.name.toLowerCase(Locale.US)
+    }
+
+    override fun hasAnnotationInPackage(pkg: String): Boolean {
+        return element.annotationMirrors.any {
+            MoreElements.getPackage(it.annotationType.asElement()).toString() == pkg
+        }
+    }
+}
\ No newline at end of file
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacExecutableElement.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacExecutableElement.kt
new file mode 100644
index 0000000..675fa8e
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacExecutableElement.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.javac
+
+import androidx.room.processing.XDeclaredType
+import androidx.room.processing.XExecutableElement
+import androidx.room.processing.XExecutableType
+import androidx.room.processing.XTypeElement
+import androidx.room.processing.XVariableElement
+import androidx.room.processing.javac.kotlin.KotlinMetadataElement
+import androidx.room.processing.javac.kotlin.descriptor
+import com.google.auto.common.MoreElements
+import com.google.auto.common.MoreTypes
+import javax.lang.model.element.ElementKind
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.element.Modifier
+import javax.lang.model.element.TypeElement
+
+internal class JavacExecutableElement(
+    env: JavacProcessingEnv,
+    val containing: JavacTypeElement,
+    override val element: ExecutableElement
+) : JavacElement(
+    env,
+    element
+), XExecutableElement {
+    private val kotlinMetadata by lazy {
+        KotlinMetadataElement.createFor(element)
+    }
+
+    val descriptor by lazy {
+        element.descriptor()
+    }
+
+    private val isSuspend by lazy {
+        kotlinMetadata?.isSuspendFunction(element) == true
+    }
+
+    override val enclosingElement: XTypeElement
+        get() = super.enclosingElement as XTypeElement
+
+    override val parameters: List<JavacVariableElement> by lazy {
+        val kotlinParamNames = kotlinMetadata?.getParameterNames(element)
+        element.parameters.mapIndexed { index, variable ->
+            JavacMethodParameter(
+                env = env,
+                containing = containing,
+                element = variable,
+                kotlinName = kotlinParamNames?.getOrNull(index)
+            )
+        }
+    }
+
+    override val returnType: JavacType by lazy {
+        val asMember = env.typeUtils.asMemberOf(containing.type.typeMirror, element)
+        val asExec = MoreTypes.asExecutable(asMember)
+        env.wrap<JavacType>(asExec.returnType)
+    }
+
+    override val equalityItems: Array<out Any?> by lazy {
+        arrayOf(element, containing)
+    }
+
+    @Suppress("UnstableApiUsage")
+    private val kotlinDefaultImplClass by lazy {
+        val parent = element.enclosingElement as? TypeElement
+        val defaultImplElement = parent?.enclosedElements?.find {
+            MoreElements.isType(it) && it.simpleName.contentEquals(DEFAULT_IMPLS_CLASS_NAME)
+        } as? TypeElement
+        defaultImplElement?.let {
+            env.wrapTypeElement(it)
+        }
+    }
+
+    override fun findKotlinDefaultImpl(): XExecutableElement? {
+        fun paramsMatch(
+            ourParams: List<XVariableElement>,
+            theirParams: List<XVariableElement>
+        ): Boolean {
+            if (ourParams.size != theirParams.size - 1) {
+                return false
+            }
+            ourParams.forEachIndexed { i, variableElement ->
+                // Plus 1 to their index because their first param is a self object.
+                if (!theirParams[i + 1].type.isSameType(
+                        variableElement.type
+                    )
+                ) {
+                    return false
+                }
+            }
+            return true
+        }
+        return kotlinDefaultImplClass?.getDeclaredMethods()?.find {
+            it.name == this.name && paramsMatch(parameters, it.parameters)
+        }
+    }
+
+    override fun isSuspendFunction() = isSuspend
+
+    override val executableType: JavacExecutableType by lazy {
+        val asMemberOf = env.typeUtils.asMemberOf(containing.type.typeMirror, element)
+        JavacExecutableType(
+            env = env,
+            executableType = MoreTypes.asExecutable(asMemberOf)
+        )
+    }
+
+    override fun isJavaDefault() = element.modifiers.contains(Modifier.DEFAULT)
+
+    override fun isVarArgs(): Boolean {
+        return element.isVarArgs
+    }
+
+    override fun asMemberOf(other: XDeclaredType): XExecutableType {
+        return if (containing.type.isSameType(other)) {
+            executableType
+        } else {
+            check(other is JavacDeclaredType)
+            val asMemberOf = env.typeUtils.asMemberOf(other.typeMirror, element)
+            JavacExecutableType(
+                env = env,
+                executableType = MoreTypes.asExecutable(asMemberOf)
+            )
+        }
+    }
+
+    override fun isConstructor(): Boolean {
+        return element.kind == ElementKind.CONSTRUCTOR
+    }
+
+    companion object {
+        internal const val DEFAULT_IMPLS_CLASS_NAME = "DefaultImpls"
+    }
+}
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacExecutableType.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacExecutableType.kt
new file mode 100644
index 0000000..a321b23
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacExecutableType.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.javac
+
+import androidx.room.processing.XExecutableType
+import androidx.room.processing.XType
+import com.google.auto.common.MoreTypes
+import com.squareup.javapoet.TypeVariableName
+import javax.lang.model.type.ExecutableType
+
+internal class JavacExecutableType(
+    val env: JavacProcessingEnv,
+    val executableType: ExecutableType
+) : XExecutableType {
+    override val returnType: JavacType by lazy {
+        env.wrap<JavacType>(executableType.returnType)
+    }
+
+    override val typeVariableNames by lazy {
+        executableType.typeVariables.map {
+            TypeVariableName.get(it)
+        }
+    }
+
+    override val parameterTypes: List<JavacType> by lazy {
+        executableType.parameterTypes.map {
+            env.wrap<JavacType>(it)
+        }
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (other !is JavacExecutableType) return false
+        return executableType == other.executableType
+    }
+
+    override fun hashCode(): Int {
+        return executableType.hashCode()
+    }
+
+    override fun getSuspendFunctionReturnType(): XType {
+        // the continuation parameter is always the last parameter of a suspend function and it only
+        // has one type parameter, e.g Continuation<? super T>
+        val typeParam =
+            MoreTypes.asDeclared(executableType.parameterTypes.last()).typeArguments.first()
+        return env.wrap<JavacType>(typeParam).extendsBoundOrSelf() // reduce the type param
+    }
+
+    override fun toString(): String {
+        return executableType.toString()
+    }
+}
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacMethodParameter.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacMethodParameter.kt
new file mode 100644
index 0000000..afef7ff
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacMethodParameter.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.javac
+
+import javax.lang.model.element.VariableElement
+
+internal class JavacMethodParameter(
+    env: JavacProcessingEnv,
+    containing: JavacTypeElement,
+    element: VariableElement,
+    val kotlinName: String? = null
+) : JavacVariableElement(env, containing, element) {
+    override val name: String
+        get() = kotlinName ?: super.name
+}
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacProcessingEnv.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacProcessingEnv.kt
new file mode 100644
index 0000000..dbe582b
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacProcessingEnv.kt
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.javac
+
+import androidx.room.processing.XDeclaredType
+import androidx.room.processing.XMessager
+import androidx.room.processing.XProcessingEnv
+import androidx.room.processing.XType
+import androidx.room.processing.XTypeElement
+import com.google.auto.common.GeneratedAnnotations
+import com.google.auto.common.MoreTypes
+import java.util.Locale
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.element.TypeElement
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeMirror
+import javax.lang.model.util.Elements
+import javax.lang.model.util.Types
+
+internal class JavacProcessingEnv(
+    val delegate: ProcessingEnvironment
+) : XProcessingEnv {
+
+    val elementUtils: Elements = delegate.elementUtils
+
+    val typeUtils: Types = delegate.typeUtils
+
+    override val messager: XMessager by lazy {
+        JavacProcessingEnvMessager(delegate)
+    }
+
+    override val filer = delegate.filer
+
+    override val options: Map<String, String>
+        get() = delegate.options
+
+    override fun findTypeElement(qName: String): JavacTypeElement? {
+        val result = delegate.elementUtils.getTypeElement(qName)
+        return result?.let(this::wrapTypeElement)
+    }
+
+    override fun findType(qName: String): XType? {
+        // check for primitives first
+        PRIMITIVE_TYPES[qName]?.let {
+            return wrap(
+                typeUtils.getPrimitiveType(it)
+            )
+        }
+        return findTypeElement(qName)?.type
+    }
+
+    override fun findGeneratedAnnotation(): XTypeElement? {
+        val element = GeneratedAnnotations.generatedAnnotation(elementUtils, delegate.sourceVersion)
+        return if (element.isPresent) {
+            wrapTypeElement(element.get())
+        } else {
+            null
+        }
+    }
+
+    override fun getArrayType(type: XType): JavacArrayType {
+        check(type is JavacType) {
+            "given type must be from java, $type is not"
+        }
+        return wrap<JavacArrayType>(
+            typeUtils.getArrayType(type.typeMirror)
+        )
+    }
+
+    override fun getDeclaredType(type: XTypeElement, vararg types: XType): XDeclaredType {
+        check(type is JavacTypeElement)
+        val args = types.map {
+            check(it is JavacType)
+            it.typeMirror
+        }.toTypedArray()
+        check(types.all {
+            it is JavacType
+        })
+        return wrap<JavacDeclaredType>(
+            typeUtils.getDeclaredType(type.element, *args)
+        )
+    }
+
+    // maybe cache here ?
+    fun wrapTypeElement(element: TypeElement) = JavacTypeElement(this, element)
+
+    inline fun <reified T : JavacType> wrapTypes(types: Iterable<TypeMirror>): List<T> {
+        return types.map {
+            wrap<T>(it)
+        }
+    }
+
+    inline fun <reified T : JavacType> wrap(typeMirror: TypeMirror): T {
+        return when (typeMirror.kind) {
+            TypeKind.ARRAY -> JavacArrayType(
+                env = this,
+                typeMirror = MoreTypes.asArray(typeMirror)
+            )
+            TypeKind.DECLARED -> JavacDeclaredType(
+                env = this,
+                typeMirror = MoreTypes.asDeclared(typeMirror)
+            )
+            else -> DefaultJavacType(this, typeMirror)
+        } as T
+    }
+
+    companion object {
+        val PRIMITIVE_TYPES = TypeKind.values().filter {
+            it.isPrimitive
+        }.associateBy {
+            it.name.toLowerCase(Locale.US)
+        }
+    }
+}
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacProcessingEnvMessager.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacProcessingEnvMessager.kt
new file mode 100644
index 0000000..dbb0825
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacProcessingEnvMessager.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.javac
+
+import androidx.room.processing.XElement
+import androidx.room.processing.XMessager
+import java.io.StringWriter
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.element.Element
+import javax.lang.model.element.ElementKind
+import javax.lang.model.util.Elements
+import javax.tools.Diagnostic
+
+internal class JavacProcessingEnvMessager(
+    private val processingEnv: ProcessingEnvironment
+) : XMessager {
+    override fun printMessage(kind: Diagnostic.Kind, msg: String, element: XElement?) {
+        val javacElement = (element as? JavacElement)?.element
+        processingEnv.messager.printMessage(
+            kind,
+            if (javacElement != null && javacElement.isFromCompiledClass()) {
+                msg.appendElement(processingEnv.elementUtils, javacElement)
+            } else {
+                msg
+            },
+            javacElement
+        )
+    }
+
+    companion object {
+        /**
+         * Indicates whether an element comes from a compiled class.
+         *
+         * If this method fails to identify if the element comes from a compiled class it will
+         * default to returning false. Note that this is a poor-man's method of identifying if
+         * the java source of the element is available without depending on compiler tools.
+         */
+        private fun Element.isFromCompiledClass(): Boolean {
+            fun getClassFileString(symbol: Any): String =
+                try {
+                    symbol.javaClass.getDeclaredField("classfile").get(symbol).toString()
+                } catch (ex: NoSuchFieldException) {
+                    getClassFileString(
+                        symbol.javaClass.superclass.getDeclaredField("owner").get(symbol)
+                    )
+                }
+
+            return try {
+                getClassFileString(this).let {
+                    it.contains(".jar") || it.contains(".class")
+                }
+            } catch (ex: Throwable) {
+                false
+            }
+        }
+
+        private fun String.appendElement(elementUtils: Elements, element: Element): String {
+            return StringBuilder(this).apply {
+                append(" - ")
+                when (element.kind) {
+                    ElementKind.CLASS, ElementKind.INTERFACE, ElementKind.CONSTRUCTOR ->
+                        append(element)
+                    ElementKind.FIELD, ElementKind.METHOD, ElementKind.PARAMETER ->
+                        append("$element in ${element.enclosingElement}")
+                    else -> {
+                        // Not sure how to nicely print the element, delegate to utils then.
+                        append("In:\n")
+                        append(StringWriter().apply {
+                            elementUtils.printElements(this, element)
+                        }.toString())
+                    }
+                }
+            }.toString()
+        }
+    }
+}
\ No newline at end of file
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacRoundEnv.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacRoundEnv.kt
new file mode 100644
index 0000000..7ed95ee
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacRoundEnv.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.javac
+
+import androidx.room.processing.XElement
+import androidx.room.processing.XRoundEnv
+import com.google.auto.common.MoreElements
+import javax.annotation.processing.RoundEnvironment
+
+@Suppress("VisibleForTests", "UnstableApiUsage")
+internal class JavacRoundEnv(
+    private val env: JavacProcessingEnv,
+    val delegate: RoundEnvironment
+) : XRoundEnv {
+    override val rootElements: Set<XElement> by lazy {
+        delegate.rootElements.map {
+            check(MoreElements.isType(it))
+            env.wrapTypeElement(MoreElements.asType(it))
+        }.toSet()
+    }
+
+    // TODO this is only for tests but we may need to support more types of elements
+    override fun getElementsAnnotatedWith(klass: Class<out Annotation>): Set<XElement> {
+        val result = delegate.getElementsAnnotatedWith(klass)
+        return result.map {
+            check(MoreElements.isType(it))
+            env.wrapTypeElement(MoreElements.asType(it))
+        }.toSet()
+    }
+}
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacType.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacType.kt
new file mode 100644
index 0000000..71f094d
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacType.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.javac
+
+import androidx.room.processing.XEquality
+import androidx.room.processing.XType
+import androidx.room.processing.XTypeElement
+import androidx.room.processing.safeTypeName
+import com.google.auto.common.MoreTypes
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeMirror
+import javax.lang.model.type.WildcardType
+import javax.lang.model.util.SimpleTypeVisitor7
+import kotlin.reflect.KClass
+
+// TODO make this abstract for XEquality, not open
+internal abstract class JavacType(
+    protected val env: JavacProcessingEnv,
+    open val typeMirror: TypeMirror
+) : XType, XEquality {
+
+    override fun isError() = typeMirror.kind == TypeKind.ERROR
+
+    override val typeName by lazy {
+        typeMirror.safeTypeName()
+    }
+
+    override fun equals(other: Any?): Boolean {
+        return XEquality.equals(this, other)
+    }
+
+    override fun hashCode(): Int {
+        return XEquality.hashCode(equalityItems)
+    }
+
+    override fun defaultValue(): String {
+        return when (typeMirror.kind) {
+            TypeKind.BOOLEAN -> "false"
+            TypeKind.BYTE, TypeKind.SHORT, TypeKind.INT, TypeKind.LONG, TypeKind.CHAR -> "0"
+            TypeKind.FLOAT -> "0f"
+            TypeKind.DOUBLE -> "0.0"
+            else -> "null"
+        }
+    }
+
+    override fun boxed(): XType {
+        return if (typeMirror.kind.isPrimitive) {
+            env.wrap(
+                env.typeUtils.boxedClass(MoreTypes.asPrimitiveType(typeMirror)).asType()
+            )
+        } else {
+            this
+        }
+    }
+
+    override fun asTypeElement(): XTypeElement {
+        return env.wrapTypeElement(
+            MoreTypes.asTypeElement(typeMirror)
+        )
+    }
+
+    override fun isNone() = typeMirror.kind == TypeKind.NONE
+
+    override fun toString(): String {
+        return typeMirror.toString()
+    }
+
+    override fun extendsBound(): XType? {
+        return typeMirror.extendsBound()?.let {
+            env.wrap<JavacType>(it)
+        }
+    }
+
+    private fun TypeMirror.extendsBound(): TypeMirror? {
+        return this.accept(object : SimpleTypeVisitor7<TypeMirror?, Void?>() {
+            override fun visitWildcard(type: WildcardType, ignored: Void?): TypeMirror? {
+                return type.extendsBound ?: type.superBound
+            }
+        }, null)
+    }
+
+    override fun isAssignableFrom(other: XType): Boolean {
+        return other is JavacType && env.typeUtils.isAssignable(
+            other.typeMirror,
+            typeMirror
+        )
+    }
+
+    override fun erasure(): JavacType {
+        return env.wrap(env.typeUtils.erasure(typeMirror))
+    }
+
+    override fun isTypeOf(other: KClass<*>): Boolean {
+        return MoreTypes.isTypeOf(
+            other.java,
+            typeMirror
+        )
+    }
+
+    override fun isSameType(other: XType): Boolean {
+        return other is JavacType && env.typeUtils.isSameType(typeMirror, other.typeMirror)
+    }
+
+    override fun isType(): Boolean {
+        return MoreTypes.isType(typeMirror)
+    }
+}
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacTypeElement.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacTypeElement.kt
new file mode 100644
index 0000000..731793e
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacTypeElement.kt
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.javac
+
+import androidx.room.processing.XExecutableElement
+import androidx.room.processing.XTypeElement
+import androidx.room.processing.XVariableElement
+import androidx.room.processing.javac.kotlin.KotlinMetadataElement
+import com.google.auto.common.MoreElements
+import com.squareup.javapoet.ClassName
+import javax.lang.model.element.ElementKind
+import javax.lang.model.element.TypeElement
+import javax.lang.model.type.TypeKind
+import javax.lang.model.util.ElementFilter
+
+internal class JavacTypeElement(
+    env: JavacProcessingEnv,
+    override val element: TypeElement
+) : JavacElement(env, element), XTypeElement {
+
+    private val kotlinMetadata by lazy {
+        KotlinMetadataElement.createFor(element)
+    }
+
+    override val qualifiedName by lazy {
+        element.qualifiedName.toString()
+    }
+
+    override val className: ClassName by lazy {
+        ClassName.get(element)
+    }
+
+    override fun isInterface() = element.kind == ElementKind.INTERFACE
+
+    private val _allFieldsIncludingPrivateSupers by lazy {
+        element.getAllFieldsIncludingPrivateSupers(
+            env.elementUtils
+        ).map {
+            JavacVariableElement(
+                env = env,
+                element = it,
+                containing = this
+            )
+        }
+    }
+
+    override fun getAllFieldsIncludingPrivateSupers(): List<XVariableElement> {
+        return _allFieldsIncludingPrivateSupers
+    }
+
+    override fun isKotlinObject() = kotlinMetadata?.isObject() == true
+
+    override fun findPrimaryConstructor(): XExecutableElement? {
+        val primarySignature = kotlinMetadata?.findPrimaryConstructorSignature() ?: return null
+        return getConstructors().firstOrNull {
+            primarySignature == it.descriptor
+        }
+    }
+
+    override fun getDeclaredMethods(): List<JavacExecutableElement> {
+        return ElementFilter.methodsIn(element.enclosedElements).map {
+            JavacExecutableElement(
+                env = env,
+                containing = this,
+                element = it
+            )
+        }
+    }
+
+    override fun getAllMethods(): List<JavacExecutableElement> {
+        return ElementFilter.methodsIn(env.elementUtils.getAllMembers(element)).map {
+            JavacExecutableElement(
+                env = env,
+                containing = this,
+                element = it
+            )
+        }
+    }
+
+    override fun getAllNonPrivateInstanceMethods(): List<JavacExecutableElement> {
+        return MoreElements.getLocalAndInheritedMethods(
+            element,
+            env.typeUtils,
+            env.elementUtils
+        ).map {
+            JavacExecutableElement(
+                env = env,
+                containing = this,
+                element = it
+            )
+        }
+    }
+
+    override fun getConstructors(): List<JavacExecutableElement> {
+        return ElementFilter.constructorsIn(element.enclosedElements).map {
+            JavacExecutableElement(
+                env = env,
+                containing = this,
+                element = it
+            )
+        }
+    }
+
+    override val type: JavacDeclaredType by lazy {
+        env.wrap<JavacDeclaredType>(element.asType())
+    }
+
+    override val superType: JavacType? by lazy {
+        // javac models non-existing types as TypeKind.NONE but we prefer to make it nullable.
+        // just makes more sense and safer as we don't need to check for none.
+
+        // The result value is a JavacType instead of JavacDeclaredType to gracefully handle
+        // cases where super is an error type.
+        val superClass = element.superclass
+        if (superClass.kind == TypeKind.NONE) {
+            null
+        } else {
+            env.wrap<JavacType>(superClass)
+        }
+    }
+
+    override val equalityItems: Array<out Any?> by lazy {
+        arrayOf(element)
+    }
+}
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacVariableElement.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacVariableElement.kt
new file mode 100644
index 0000000..4c5aee9
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/JavacVariableElement.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.javac
+
+import androidx.room.processing.XDeclaredType
+import androidx.room.processing.XType
+import androidx.room.processing.XVariableElement
+import com.google.auto.common.MoreTypes
+import javax.lang.model.element.VariableElement
+
+internal open class JavacVariableElement(
+    env: JavacProcessingEnv,
+    val containing: JavacTypeElement,
+    override val element: VariableElement
+) : JavacElement(env, element), XVariableElement {
+    override val type: JavacType by lazy {
+        MoreTypes.asMemberOf(env.typeUtils, containing.type.typeMirror, element).let {
+            env.wrap<JavacType>(it)
+        }
+    }
+
+    override fun asMemberOf(other: XDeclaredType): XType {
+        return if (containing.type.isSameType(other)) {
+            type
+        } else {
+            check(other is JavacDeclaredType)
+            val asMember = MoreTypes.asMemberOf(env.typeUtils, other.typeMirror, element)
+            env.wrap<JavacType>(asMember)
+        }
+    }
+
+    override val equalityItems: Array<out Any?> by lazy {
+        arrayOf(element, containing)
+    }
+}
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/kotlin/JvmDescriptorUtils.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/kotlin/JvmDescriptorUtils.kt
new file mode 100644
index 0000000..cabec27
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/kotlin/JvmDescriptorUtils.kt
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.javac.kotlin
+
+import com.google.auto.common.MoreTypes
+import javax.lang.model.element.Element
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.element.NestingKind
+import javax.lang.model.element.QualifiedNameable
+import javax.lang.model.element.TypeElement
+import javax.lang.model.element.VariableElement
+import javax.lang.model.type.ArrayType
+import javax.lang.model.type.DeclaredType
+import javax.lang.model.type.ErrorType
+import javax.lang.model.type.ExecutableType
+import javax.lang.model.type.IntersectionType
+import javax.lang.model.type.NoType
+import javax.lang.model.type.NullType
+import javax.lang.model.type.PrimitiveType
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeMirror
+import javax.lang.model.type.TypeVariable
+import javax.lang.model.type.UnionType
+import javax.lang.model.type.WildcardType
+import javax.lang.model.util.AbstractTypeVisitor8
+
+/**
+ * Returns the method descriptor of this [ExecutableElement].
+ *
+ * For reference, see the [JVM specification, section 4.3.2](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.2)
+ */
+internal fun VariableElement.descriptor() = "$simpleName:${asType().descriptor()}"
+
+/**
+ * Returns the method descriptor of this [ExecutableElement].
+ *
+ * For reference, see the [JVM specification, section 4.3.3](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.3)
+ */
+internal fun ExecutableElement.descriptor() =
+    "$simpleName${MoreTypes.asExecutable(asType()).descriptor()}"
+
+/**
+ * Returns the name of this [TypeElement] in its "internal form".
+ *
+ * For reference, see the [JVM specification, section 4.2](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.2).
+ */
+internal val Element.internalName: String
+    get() = when (this) {
+        is TypeElement ->
+            when (nestingKind) {
+                NestingKind.TOP_LEVEL ->
+                    qualifiedName.toString().replace('.', '/')
+                NestingKind.MEMBER ->
+                    enclosingElement.internalName + "$" + simpleName
+                NestingKind.LOCAL, NestingKind.ANONYMOUS ->
+                    error("Unsupported nesting $nestingKind")
+                else ->
+                    error("Unsupported, nestingKind == null")
+            }
+        is QualifiedNameable -> qualifiedName.toString().replace('.', '/')
+        else -> simpleName.toString()
+    }
+
+@Suppress("unused")
+internal val NoType.descriptor: String
+    get() = "V"
+
+internal val DeclaredType.descriptor: String
+    get() = "L" + asElement().internalName + ";"
+
+internal val PrimitiveType.descriptor: String
+    get() = when (this.kind) {
+        TypeKind.BYTE -> "B"
+        TypeKind.CHAR -> "C"
+        TypeKind.DOUBLE -> "D"
+        TypeKind.FLOAT -> "F"
+        TypeKind.INT -> "I"
+        TypeKind.LONG -> "J"
+        TypeKind.SHORT -> "S"
+        TypeKind.BOOLEAN -> "Z"
+        else -> error("Unknown primitive type $this")
+    }
+
+internal fun TypeMirror.descriptor(): String = accept(JvmDescriptorTypeVisitor, Unit)
+
+@Suppress("unused")
+internal fun WildcardType.descriptor(): String = ""
+
+// The erasure of a type variable is the erasure of its leftmost bound. - JVM Spec Sec 4.6
+internal fun TypeVariable.descriptor(): String = this.upperBound.descriptor()
+
+// For a type variable with multiple bounds: "the erasure of a type variable is determined by
+// the first type in its bound" - JVM Spec Sec 4.4
+internal fun IntersectionType.descriptor(): String =
+    this.bounds[0].descriptor()
+
+internal fun ArrayType.descriptor(): String =
+    "[" + componentType.descriptor()
+
+internal fun ExecutableType.descriptor(): String {
+    val parameterDescriptors =
+        parameterTypes.joinToString(separator = "") { it.descriptor() }
+    val returnDescriptor = returnType.descriptor()
+    return "($parameterDescriptors)$returnDescriptor"
+}
+
+/**
+ * When applied over a type, it returns either:
+ * + a "field descriptor", for example: `Ljava/lang/Object;`
+ * + a "method descriptor", for example: `(Ljava/lang/Object;)Z`
+ *
+ * The easiest way to use this is through [TypeMirror.descriptor]
+ *
+ * For reference, see the [JVM specification, section 4.3](http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3).
+ */
+@Suppress("DEPRECATION")
+internal object JvmDescriptorTypeVisitor : AbstractTypeVisitor8<String, Unit>() {
+
+    override fun visitNoType(t: NoType, u: Unit): String = t.descriptor
+
+    override fun visitDeclared(t: DeclaredType, u: Unit): String = t.descriptor
+
+    override fun visitPrimitive(t: PrimitiveType, u: Unit): String = t.descriptor
+
+    override fun visitArray(t: ArrayType, u: Unit): String = t.descriptor()
+
+    override fun visitWildcard(t: WildcardType, u: Unit): String = t.descriptor()
+
+    override fun visitExecutable(t: ExecutableType, u: Unit): String = t.descriptor()
+
+    override fun visitTypeVariable(t: TypeVariable, u: Unit): String = t.descriptor()
+
+    override fun visitNull(t: NullType, u: Unit): String = visitUnknown(t, u)
+
+    override fun visitError(t: ErrorType, u: Unit): String = visitUnknown(t, u)
+
+    override fun visitIntersection(t: IntersectionType, u: Unit) = t.descriptor()
+
+    override fun visitUnion(t: UnionType, u: Unit) = visitUnknown(t, u)
+
+    override fun visitUnknown(t: TypeMirror, u: Unit): String = error("Unsupported type $t")
+}
\ No newline at end of file
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/kotlin/KotlinClassMetadataUtils.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/kotlin/KotlinClassMetadataUtils.kt
new file mode 100644
index 0000000..fefd8e0
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/kotlin/KotlinClassMetadataUtils.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.javac.kotlin
+
+import kotlinx.metadata.ClassName
+import kotlinx.metadata.Flag
+import kotlinx.metadata.Flags
+import kotlinx.metadata.KmClassVisitor
+import kotlinx.metadata.KmConstructorExtensionVisitor
+import kotlinx.metadata.KmConstructorVisitor
+import kotlinx.metadata.KmExtensionType
+import kotlinx.metadata.KmFunctionExtensionVisitor
+import kotlinx.metadata.KmFunctionVisitor
+import kotlinx.metadata.KmValueParameterVisitor
+import kotlinx.metadata.jvm.JvmConstructorExtensionVisitor
+import kotlinx.metadata.jvm.JvmFunctionExtensionVisitor
+import kotlinx.metadata.jvm.JvmMethodSignature
+import kotlinx.metadata.jvm.KotlinClassMetadata
+
+/**
+ * Represents the kotlin metadata of a function
+ */
+internal data class KmFunction(
+    val descriptor: String,
+    private val flags: Flags,
+    val parameters: List<KmValueParameter>
+) {
+    fun isSuspend() = Flag.Function.IS_SUSPEND(flags)
+}
+
+/**
+ * Represents the kotlin metadata of a constructor
+ */
+internal data class KmConstructor(
+    val descriptor: String,
+    private val flags: Flags,
+    val parameters: List<KmValueParameter>
+) {
+    fun isPrimary() = Flag.Constructor.IS_PRIMARY(flags)
+}
+
+/**
+ * Represents the kotlin metadata of a parameter
+ */
+internal data class KmValueParameter(val name: String, private val flags: Flags)
+
+internal fun KotlinClassMetadata.Class.readFunctions(): List<KmFunction> =
+    mutableListOf<KmFunction>().apply { accept(FunctionReader(this)) }
+
+private class FunctionReader(val result: MutableList<KmFunction>) : KmClassVisitor() {
+    override fun visitFunction(flags: Flags, name: String): KmFunctionVisitor? {
+        return object : KmFunctionVisitor() {
+
+            lateinit var descriptor: String
+            val parameters = mutableListOf<KmValueParameter>()
+
+            override fun visitValueParameter(
+                flags: Flags,
+                name: String
+            ): KmValueParameterVisitor? {
+                parameters.add(KmValueParameter(name, flags))
+                return super.visitValueParameter(flags, name)
+            }
+
+            override fun visitExtensions(type: KmExtensionType): KmFunctionExtensionVisitor? {
+                if (type != JvmFunctionExtensionVisitor.TYPE) {
+                    error("Unsupported extension type: $type")
+                }
+                return object : JvmFunctionExtensionVisitor() {
+                    override fun visit(signature: JvmMethodSignature?) {
+                        descriptor = signature!!.asString()
+                    }
+                }
+            }
+
+            override fun visitEnd() {
+                result.add(KmFunction(descriptor, flags, parameters))
+            }
+        }
+    }
+}
+
+internal fun KotlinClassMetadata.Class.readConstructors(): List<KmConstructor> =
+    mutableListOf<KmConstructor>().apply { accept(ConstructorReader(this)) }
+
+private class ConstructorReader(val result: MutableList<KmConstructor>) : KmClassVisitor() {
+    override fun visitConstructor(flags: Flags): KmConstructorVisitor? {
+        return object : KmConstructorVisitor() {
+
+            lateinit var descriptor: String
+            val parameters = mutableListOf<KmValueParameter>()
+
+            override fun visitValueParameter(
+                flags: Flags,
+                name: String
+            ): KmValueParameterVisitor? {
+                parameters.add(KmValueParameter(name, flags))
+                return super.visitValueParameter(flags, name)
+            }
+
+            override fun visitExtensions(type: KmExtensionType): KmConstructorExtensionVisitor? {
+                if (type != JvmConstructorExtensionVisitor.TYPE) {
+                    error("Unsupported extension type: $type")
+                }
+                return object : JvmConstructorExtensionVisitor() {
+                    override fun visit(signature: JvmMethodSignature?) {
+                        descriptor = signature!!.asString()
+                    }
+                }
+            }
+
+            override fun visitEnd() {
+                result.add(KmConstructor(descriptor, flags, parameters))
+            }
+        }
+    }
+}
+
+internal fun KotlinClassMetadata.Class.isObject(): Boolean = ObjectReader().let {
+    this@isObject.accept(it)
+    it.isObject
+}
+
+private class ObjectReader() : KmClassVisitor() {
+    var isObject: Boolean = false
+    override fun visit(flags: Flags, name: ClassName) {
+        isObject = Flag.Class.IS_OBJECT(flags)
+    }
+}
\ No newline at end of file
diff --git a/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/kotlin/KotlinMetadataElement.kt b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/kotlin/KotlinMetadataElement.kt
new file mode 100644
index 0000000..5e822a2
--- /dev/null
+++ b/room/compiler-xprocessing/src/main/java/androidx/room/processing/javac/kotlin/KotlinMetadataElement.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.javac.kotlin
+
+import kotlinx.metadata.jvm.KotlinClassHeader
+import kotlinx.metadata.jvm.KotlinClassMetadata
+import javax.lang.model.element.Element
+import javax.lang.model.element.ExecutableElement
+
+/**
+ * Utility class for processors that wants to run kotlin specific code.
+ */
+internal class KotlinMetadataElement(
+    val element: Element,
+    private val classMetadata: KotlinClassMetadata.Class
+) {
+
+    private val functionList: List<KmFunction> by lazy { classMetadata.readFunctions() }
+    private val constructorList: List<KmConstructor> by lazy { classMetadata.readConstructors() }
+
+    private val ExecutableElement.descriptor: String
+        get() = descriptor()
+
+    /**
+     * Returns the parameter names of the function or constructor if all have names embedded in the
+     * metadata.
+     */
+    fun getParameterNames(method: ExecutableElement): List<String>? {
+        val methodSignature = method.descriptor
+        val paramList =
+            functionList.firstOrNull { it.descriptor == methodSignature }?.parameters
+                ?: constructorList.firstOrNull { it.descriptor == methodSignature }?.parameters
+        return paramList?.map { it.name }
+    }
+
+    /**
+     * Finds the primary constructor descriptor of the class.
+     */
+    fun findPrimaryConstructorSignature() = constructorList.first { it.isPrimary() }.descriptor
+
+    /**
+     * Checks if a method is a suspend function.
+     */
+    fun isSuspendFunction(method: ExecutableElement) = functionList.firstOrNull {
+        it.descriptor == method.descriptor
+    }?.isSuspend() ?: false
+
+    fun isObject(): Boolean = classMetadata.isObject()
+
+    companion object {
+        /**
+         * Creates a [KotlinMetadataElement] for the given element if it contains Kotlin metadata,
+         * otherwise this method returns null.
+         *
+         * Usually the [element] passed must represent a class. For example, if kotlin metadata is
+         * desired for a method, then the containing class should be used as parameter.
+         */
+        fun createFor(element: Element): KotlinMetadataElement? {
+            val metadata = getMetadataAnnotation(element)?.run {
+                KotlinClassHeader(
+                    kind = kind,
+                    metadataVersion = metadataVersion,
+                    bytecodeVersion = bytecodeVersion,
+                    data1 = data1,
+                    data2 = data2,
+                    extraString = extraString,
+                    packageName = packageName,
+                    extraInt = extraInt
+                ).let {
+                    // TODO: Support more metadata kind (file facade, synthetic class, etc...)
+                    KotlinClassMetadata.read(it) as? KotlinClassMetadata.Class
+                }
+            }
+            return if (metadata != null) {
+                KotlinMetadataElement(element, metadata)
+            } else {
+                null
+            }
+        }
+
+        /**
+         * Search for Kotlin's Metadata annotation across the element's hierarchy.
+         */
+        private fun getMetadataAnnotation(element: Element?): Metadata? =
+            if (element != null) {
+                element.getAnnotation(Metadata::class.java)
+                    ?: getMetadataAnnotation(element.enclosingElement)
+            } else {
+                null
+            }
+    }
+}
\ No newline at end of file
diff --git a/room/compiler-xprocessing/src/test/java/androidx/room/processing/JavacTestProcessorTest.kt b/room/compiler-xprocessing/src/test/java/androidx/room/processing/JavacTestProcessorTest.kt
new file mode 100644
index 0000000..7a48e00
--- /dev/null
+++ b/room/compiler-xprocessing/src/test/java/androidx/room/processing/JavacTestProcessorTest.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+import androidx.room.processing.testcode.OtherAnnotation
+import androidx.room.processing.util.Source
+import com.google.common.truth.Truth.assertAbout
+import com.google.common.truth.Truth.assertThat
+import com.google.testing.compile.JavaSourcesSubjectFactory
+import org.junit.Test
+import java.util.concurrent.atomic.AtomicBoolean
+
+class JavacTestProcessorTest {
+    @Test
+    fun testGetAnnotations() {
+        val source = Source.java(
+            "foo.bar.Baz", """
+            package foo.bar;
+            import androidx.room.processing.testcode.OtherAnnotation;
+            @OtherAnnotation(value="xx")
+            class Baz {
+            }
+        """.trimIndent()
+        )
+        val invoked = AtomicBoolean(false)
+        val testProcessor = object : JavacTestProcessor() {
+            override fun doProcess(annotations: Set<XTypeElement>, roundEnv: XRoundEnv): Boolean {
+                invoked.set(true)
+                val annotatedElements = roundEnv.getElementsAnnotatedWith(
+                    OtherAnnotation::class.java
+                )
+                val targetElement = xProcessingEnv.requireTypeElement("foo.bar.Baz")
+                assertThat(
+                    annotatedElements
+                ).containsExactly(
+                    targetElement
+                )
+                return true
+            }
+
+            override fun getSupportedAnnotationTypes(): Set<String> {
+                return setOf(OtherAnnotation::class.java.canonicalName)
+            }
+        }
+        assertAbout(
+            JavaSourcesSubjectFactory.javaSources()
+        ).that(
+            listOf(source.toJFO())
+        ).processedWith(
+            testProcessor
+        ).compilesWithoutError()
+
+        assertThat(invoked.get()).isTrue()
+    }
+}
diff --git a/room/compiler-xprocessing/src/test/java/androidx/room/processing/KotlinMetadataTest.kt b/room/compiler-xprocessing/src/test/java/androidx/room/processing/KotlinMetadataTest.kt
new file mode 100644
index 0000000..8e0df4d
--- /dev/null
+++ b/room/compiler-xprocessing/src/test/java/androidx/room/processing/KotlinMetadataTest.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+import androidx.room.processing.testcode.KotlinTestClass
+import androidx.room.processing.util.Source
+import androidx.room.processing.util.getMethod
+import androidx.room.processing.util.getParameter
+import androidx.room.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
+import com.squareup.javapoet.TypeName
+import org.junit.Test
+
+class KotlinMetadataTest {
+    @Test
+    fun readWithMetadata() {
+        val source = Source.kotlin(
+            "Dummy.kt", """
+            class Dummy
+        """.trimIndent()
+        )
+        runProcessorTest(
+            sources = listOf(source)
+        ) {
+            val element = it.processingEnv.requireTypeElement(KotlinTestClass::class)
+            element.getMethod("mySuspendMethod").apply {
+                assertThat(parameters).hasSize(2)
+                assertThat(getParameter("param1").type.typeName)
+                    .isEqualTo(TypeName.get(String::class.java))
+                assertThat(isSuspendFunction()).isTrue()
+            }
+        }
+    }
+}
diff --git a/room/compiler-xprocessing/src/test/java/androidx/room/processing/MethodSpecHelperTest.kt b/room/compiler-xprocessing/src/test/java/androidx/room/processing/MethodSpecHelperTest.kt
new file mode 100644
index 0000000..049e047
--- /dev/null
+++ b/room/compiler-xprocessing/src/test/java/androidx/room/processing/MethodSpecHelperTest.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+import androidx.room.processing.util.Source
+import androidx.room.processing.util.runProcessorTest
+import com.google.auto.common.MoreTypes
+import com.google.common.truth.Truth.assertAbout
+import com.google.common.truth.Truth.assertThat
+import com.google.testing.compile.JavaSourcesSubjectFactory
+import com.squareup.javapoet.MethodSpec
+import org.junit.Test
+import javax.annotation.processing.AbstractProcessor
+import javax.annotation.processing.RoundEnvironment
+import javax.lang.model.SourceVersion
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.element.Modifier
+import javax.lang.model.element.TypeElement
+import javax.lang.model.type.DeclaredType
+import javax.lang.model.util.ElementFilter
+import javax.lang.model.util.Types
+
+class MethodSpecHelperTest {
+    @Test
+    fun overrides() {
+        // check our override impl matches javapoet
+        val source = Source.java(
+            "foo.bar.Baz", """
+            package foo.bar;
+            import androidx.room.processing.testcode.OtherAnnotation;
+
+            public class Baz {
+                public void method1() {
+                }
+
+                public void method2(int x) {
+                }
+
+                public int parameterAnnotation(@OtherAnnotation("x") int y) {
+                    return 3;
+                }
+
+                @OtherAnnotation("x")
+                public int methodAnntation(int y) {
+                    return 3;
+                }
+
+                public int varargMethod(int... y) {
+                    return 3;
+                }
+
+                protected <R> R typeArgs(R r) {
+                    return r;
+                }
+
+                protected void throwsException() throws Exception {
+                }
+            }
+        """.trimIndent()
+        )
+        // first build golden image with Java processor so we can use JavaPoet's API
+        val golden = buildMethodsViaJavaPoet(source)
+        runProcessorTest(
+            sources = listOf(source)
+        ) {
+            val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
+            element.getDeclaredMethods().forEachIndexed { index, method ->
+                val subject = MethodSpecHelper.overridingWithFinalParams(
+                    method,
+                    element.type
+                ).build().toString()
+                assertThat(subject).isEqualTo(golden[index])
+            }
+        }
+    }
+
+    private fun buildMethodsViaJavaPoet(source: Source): List<String> {
+        lateinit var result: List<String>
+        assertAbout(
+            JavaSourcesSubjectFactory.javaSources()
+        ).that(
+            listOf(source.toJFO())
+        ).processedWith(
+            object : AbstractProcessor() {
+                override fun process(
+                    annotations: MutableSet<out TypeElement>,
+                    roundEnv: RoundEnvironment
+                ): Boolean {
+                    val element = processingEnv.elementUtils.getTypeElement("foo.bar.Baz")
+                    result = ElementFilter.methodsIn(element.enclosedElements)
+                        .map {
+                            generateFromJavapoet(
+                                it,
+                                MoreTypes.asDeclared(element.asType()),
+                                processingEnv.typeUtils
+                            ).build().toString()
+                        }
+                    return true
+                }
+
+                override fun getSupportedSourceVersion(): SourceVersion {
+                    return SourceVersion.latestSupported()
+                }
+
+                override fun getSupportedAnnotationTypes(): Set<String> {
+                    return setOf("*")
+                }
+            }
+        ).compilesWithoutError()
+        return result
+    }
+
+    private fun generateFromJavapoet(
+        method: ExecutableElement,
+        owner: DeclaredType,
+        typeUtils: Types
+    ): MethodSpec.Builder {
+        return overrideWithoutAnnotations(
+            elm = method,
+            owner = owner,
+            typeUtils = typeUtils
+        )
+    }
+
+    /**
+     * Copied from DaoWriter for backwards compatibility
+     */
+    private fun overrideWithoutAnnotations(
+        elm: ExecutableElement,
+        owner: DeclaredType,
+        typeUtils: Types
+    ): MethodSpec.Builder {
+        val baseSpec = MethodSpec.overriding(elm, owner, typeUtils)
+            .build()
+
+        // make all the params final
+        val params = baseSpec.parameters.map { it.toBuilder().addModifiers(Modifier.FINAL).build() }
+
+        return MethodSpec.methodBuilder(baseSpec.name).apply {
+            addAnnotation(Override::class.java)
+            addModifiers(baseSpec.modifiers)
+            addParameters(params)
+            addTypeVariables(baseSpec.typeVariables)
+            addExceptions(baseSpec.exceptions)
+            varargs(baseSpec.varargs)
+            returns(baseSpec.returnType)
+        }
+    }
+}
diff --git a/room/compiler-xprocessing/src/test/java/androidx/room/processing/SyntheticJavacProcessor.kt b/room/compiler-xprocessing/src/test/java/androidx/room/processing/SyntheticJavacProcessor.kt
new file mode 100644
index 0000000..cfb3526
--- /dev/null
+++ b/room/compiler-xprocessing/src/test/java/androidx/room/processing/SyntheticJavacProcessor.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+import androidx.room.processing.javac.JavacProcessingEnv
+import androidx.room.processing.util.TestInvocation
+import javax.lang.model.SourceVersion
+
+class SyntheticJavacProcessor(
+    val handler: (TestInvocation) -> Unit
+) : JavacTestProcessor() {
+    private var result: Result<Unit>? = null
+
+    override fun doProcess(annotations: Set<XTypeElement>, roundEnv: XRoundEnv): Boolean {
+        result = kotlin.runCatching {
+            handler(
+                TestInvocation(
+                    processingEnv = JavacProcessingEnv(
+                        processingEnv
+                    )
+                )
+            )
+        }
+        return true
+    }
+
+    override fun getSupportedSourceVersion(): SourceVersion {
+        return SourceVersion.latest()
+    }
+
+    override fun getSupportedAnnotationTypes() = setOf("*")
+
+    fun throwIfFailed() {
+        val result = checkNotNull(result) {
+            "did not compile"
+        }
+        if (result.isFailure) {
+            throw result.exceptionOrNull()!!
+        }
+    }
+}
\ No newline at end of file
diff --git a/room/compiler-xprocessing/src/test/java/androidx/room/processing/TypeAssignmentTest.kt b/room/compiler-xprocessing/src/test/java/androidx/room/processing/TypeAssignmentTest.kt
new file mode 100644
index 0000000..5856a38
--- /dev/null
+++ b/room/compiler-xprocessing/src/test/java/androidx/room/processing/TypeAssignmentTest.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+import androidx.room.processing.util.Source
+import androidx.room.processing.util.TestInvocation
+import androidx.room.processing.util.getField
+import androidx.room.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class TypeAssignmentTest {
+    companion object {
+        private val TEST_OBJECT = Source.java(
+            "foo.bar.MyObject",
+            """
+            package foo.bar;
+            import java.util.Set;
+            import java.util.HashSet;
+            import java.util.Map;
+            class MyObject {
+                String mString;
+                Integer mInteger;
+                Set<MyObject> mSet;
+                Set<? extends MyObject> mVarianceSet;
+                HashSet<MyObject> mHashSet;
+                Map<String, ?> mUnboundedMap;
+                Map<String, String> mStringMap;
+            }
+            """.trimIndent()
+        )
+    }
+
+    @Test
+    fun basic() {
+        runTest {
+            val testObject = processingEnv.requireTypeElement("foo.bar.MyObject")
+            val string = testObject.getField("mString")
+            val integer = testObject.getField("mInteger")
+            assertThat(
+                integer.type.isAssignableFromWithoutVariance(string.type)
+            ).isFalse()
+        }
+    }
+
+    @Test
+    fun generics() {
+        runTest {
+            val testObject = processingEnv.requireTypeElement("foo.bar.MyObject")
+            val set = testObject.getField("mSet").type
+            val hashSet = testObject.getField("mHashSet").type
+            assertThat(hashSet.isAssignableFromWithoutVariance(set)).isFalse()
+            assertThat(set.isAssignableFromWithoutVariance(hashSet)).isTrue()
+        }
+    }
+
+    @Test
+    fun variance() {
+        /**
+         *  Set<User> userSet = null;
+         *  Set<? extends User> userSet2 = null;
+         *  userSet = userSet2;  // NOT OK for java but kotlin data classes hit this so we want
+         *                       // to accept it
+         */
+        runTest {
+            val testObject = processingEnv.requireTypeElement("foo.bar.MyObject")
+            val set = testObject.getField("mSet").type
+            val varianceSet = testObject.getField("mVarianceSet").type
+            assertThat(varianceSet.isAssignableFromWithoutVariance(set)).isTrue()
+            assertThat(set.isAssignableFromWithoutVariance(varianceSet)).isTrue()
+        }
+    }
+
+    @Test
+    fun unboundedVariance() {
+        runTest {
+            val testObject = processingEnv.requireTypeElement("foo.bar.MyObject")
+            val unbounded = testObject.getField("mUnboundedMap").type
+            val objectMap = testObject.getField("mStringMap").type
+            assertThat(objectMap.isAssignableFromWithoutVariance(unbounded)).isFalse()
+            assertThat(unbounded.isAssignableFromWithoutVariance(objectMap)).isTrue()
+        }
+    }
+
+    private fun runTest(handler: TestInvocation.() -> Unit) {
+        runProcessorTest(
+            sources = listOf(TEST_OBJECT),
+            handler = handler
+        )
+    }
+}
diff --git a/room/compiler-xprocessing/src/test/java/androidx/room/processing/XAnnotationBoxTest.kt b/room/compiler-xprocessing/src/test/java/androidx/room/processing/XAnnotationBoxTest.kt
new file mode 100644
index 0000000..62d76f7
--- /dev/null
+++ b/room/compiler-xprocessing/src/test/java/androidx/room/processing/XAnnotationBoxTest.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+import androidx.room.processing.testcode.MainAnnotation
+import androidx.room.processing.testcode.OtherAnnotation
+import androidx.room.processing.util.Source
+import androidx.room.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class XAnnotationBoxTest {
+    @Test
+    fun readSimpleAnotationValue() {
+        val source = Source.java(
+            "foo.bar.Baz", """
+            package foo.bar;
+            @SuppressWarnings({"warning1", "warning 2"})
+            public class Baz {
+            }
+        """.trimIndent()
+        )
+        runProcessorTest(
+            sources = listOf(source)
+        ) {
+            val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
+            val annotationBox = element.toAnnotationBox(SuppressWarnings::class)
+            assertThat(annotationBox).isNotNull()
+            assertThat(
+                annotationBox!!.value.value
+            ).isEqualTo(
+                arrayOf("warning1", "warning 2")
+            )
+        }
+    }
+
+    @Test
+    fun typeReference() {
+        val mySource = Source.java(
+            "foo.bar.Baz", """
+            package foo.bar;
+            import androidx.room.processing.testcode.MainAnnotation;
+            import androidx.room.processing.testcode.OtherAnnotation;
+            @MainAnnotation(
+                typeList = {String.class, Integer.class},
+                singleType = Long.class,
+                intMethod = 3,
+                otherAnnotationArray = {
+                    @OtherAnnotation(
+                        value = "other list 1"
+                    ),
+                    @OtherAnnotation("other list 2"),
+                },
+                singleOtherAnnotation = @OtherAnnotation("other single")
+            )
+            public class Baz {
+            }
+        """.trimIndent()
+        )
+        val targetName = "foo.bar.Baz"
+        runProcessorTest(
+            listOf(mySource)
+        ) {
+            val element = it.processingEnv.requireTypeElement(targetName)
+            element.toAnnotationBox(MainAnnotation::class)!!.let { annotation ->
+                assertThat(
+                    annotation.getAsTypeList("typeList")
+                ).containsExactly(
+                    it.processingEnv.requireType(java.lang.String::class.java.canonicalName),
+                    it.processingEnv.requireType(java.lang.Integer::class.java.canonicalName)
+                )
+                assertThat(
+                    annotation.getAsType("singleType")
+                ).isEqualTo(
+                    it.processingEnv.requireType(java.lang.Long::class.java.canonicalName)
+                )
+
+                assertThat(annotation.value.intMethod).isEqualTo(3)
+                annotation.getAsAnnotationBox<OtherAnnotation>("singleOtherAnnotation")
+                    .let { other ->
+                        assertThat(other.value.value).isEqualTo("other single")
+                    }
+                annotation.getAsAnnotationBoxArray<OtherAnnotation>("otherAnnotationArray")
+                    .let { boxArray ->
+                        assertThat(boxArray).hasLength(2)
+                        assertThat(boxArray[0].value.value).isEqualTo("other list 1")
+                        assertThat(boxArray[1].value.value).isEqualTo("other list 2")
+                    }
+            }
+        }
+    }
+}
diff --git a/room/compiler-xprocessing/src/test/java/androidx/room/processing/XArrayTypeTest.kt b/room/compiler-xprocessing/src/test/java/androidx/room/processing/XArrayTypeTest.kt
new file mode 100644
index 0000000..9544160
--- /dev/null
+++ b/room/compiler-xprocessing/src/test/java/androidx/room/processing/XArrayTypeTest.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+import androidx.room.processing.util.Source
+import androidx.room.processing.util.getField
+import androidx.room.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
+import com.squareup.javapoet.ArrayTypeName
+import com.squareup.javapoet.TypeName
+import org.junit.Test
+
+class XArrayTypeTest {
+    @Test
+    fun xArrayType() {
+        val source = Source.java(
+            "foo.bar.Baz", """
+            package foo.bar;
+            class Baz {
+                String[] param;
+            }
+        """.trimIndent()
+        )
+        runProcessorTest(
+            sources = listOf(source)
+        ) {
+            val type = it.processingEnv
+                .requireTypeElement("foo.bar.Baz")
+                .getField("param")
+                .type
+            assertThat(type.isArray()).isTrue()
+            assertThat(type.typeName).isEqualTo(
+                ArrayTypeName.of(TypeName.get(String::class.java))
+            )
+            assertThat(type.asArray().componentType.typeName).isEqualTo(
+                TypeName.get(String::class.java)
+            )
+
+            val objArray = it.processingEnv.getArrayType(
+                TypeName.OBJECT
+            )
+            assertThat(objArray.isArray()).isTrue()
+            assertThat(objArray.asArray().componentType.typeName).isEqualTo(
+                TypeName.OBJECT
+            )
+            assertThat(objArray.typeName).isEqualTo(
+                ArrayTypeName.of(TypeName.OBJECT)
+            )
+        }
+    }
+
+    @Test
+    fun notAnArray() {
+        runProcessorTest {
+            val list = it.processingEnv.requireType("java.util.List")
+            assertThat(list.isArray()).isFalse()
+        }
+    }
+}
diff --git a/room/compiler-xprocessing/src/test/java/androidx/room/processing/XElementTest.kt b/room/compiler-xprocessing/src/test/java/androidx/room/processing/XElementTest.kt
new file mode 100644
index 0000000..6cf6f2e
--- /dev/null
+++ b/room/compiler-xprocessing/src/test/java/androidx/room/processing/XElementTest.kt
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+import androidx.room.processing.testcode.OtherAnnotation
+import androidx.room.processing.util.Source
+import androidx.room.processing.util.getField
+import androidx.room.processing.util.getMethod
+import androidx.room.processing.util.getParameter
+import androidx.room.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeVariableName
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class XElementTest {
+    @Test
+    fun modifiers() {
+        runProcessorTest(
+            listOf(
+                Source.java(
+                    "foo.bar.Baz", """
+                package foo.bar;
+                public abstract class Baz {
+                    private int privateField;
+                    int packagePrivateField;
+                    protected int protectedField;
+                    public int publicField;
+                    transient int transientField;
+                    static int staticField;
+
+                    private void privateMethod() {}
+                    void packagePrivateMethod() {}
+                    public void publicMethod() {}
+                    protected void protectedMethod() {}
+                    final void finalMethod() {}
+                    abstract void abstractMethod();
+                    static void staticMethod() {}
+                }
+            """.trimIndent()
+                )
+            )
+        ) {
+            val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
+            fun XElement.readModifiers(): Set<String> {
+                val result = mutableSetOf<String>()
+                if (isPrivate()) result.add("private")
+                if (isPublic()) result.add("public")
+                if (isTransient()) result.add("transient")
+                if (isStatic()) result.add("static")
+                if (isFinal()) result.add("final")
+                if (isAbstract()) result.add("abstract")
+                if (isProtected()) result.add("protected")
+                return result
+            }
+
+            fun XElement.assertModifiers(vararg expected: String) {
+                assertThat(readModifiers()).containsExactlyElementsIn(expected)
+            }
+            element.assertModifiers("abstract", "public")
+            element.getField("privateField").assertModifiers("private")
+            element.getField("packagePrivateField").assertModifiers()
+            // we don't read isProtected, no reason. we should eventually get rid of most if not
+            // all anyways
+            element.getField("protectedField").assertModifiers("protected")
+            element.getField("publicField").assertModifiers("public")
+            element.getField("transientField").assertModifiers("transient")
+            element.getField("staticField").assertModifiers("static")
+
+            element.getMethod("privateMethod").assertModifiers("private")
+            element.getMethod("packagePrivateMethod").assertModifiers()
+            element.getMethod("publicMethod").assertModifiers("public")
+            element.getMethod("protectedMethod").assertModifiers("protected")
+            element.getMethod("finalMethod").assertModifiers("final")
+            element.getMethod("abstractMethod").assertModifiers("abstract")
+            element.getMethod("staticMethod").assertModifiers("static")
+
+            assertThat(
+                element.getMethod("privateMethod").isOverrideableIgnoringContainer()
+            ).isFalse()
+            assertThat(
+                element.getMethod("packagePrivateMethod").isOverrideableIgnoringContainer()
+            ).isTrue()
+            assertThat(element.getMethod("publicMethod").isOverrideableIgnoringContainer()).isTrue()
+            assertThat(
+                element.getMethod("protectedMethod").isOverrideableIgnoringContainer()
+            ).isTrue()
+            assertThat(element.getMethod("finalMethod").isOverrideableIgnoringContainer()).isFalse()
+            assertThat(
+                element.getMethod("abstractMethod").isOverrideableIgnoringContainer()
+            ).isTrue()
+            assertThat(
+                element.getMethod("staticMethod").isOverrideableIgnoringContainer()
+            ).isFalse()
+        }
+    }
+
+    @Test
+    fun typeParams() {
+        val genericBase = Source.java(
+            "foo.bar.Base", """
+                package foo.bar;
+                public class Base<T> {
+                    protected T returnT() {
+                        throw new RuntimeException("Stub");
+                    }
+                    public int receiveT(T param1) {
+                        return 3;
+                    }
+                    public <R> int receiveR(R param1) {
+                        return 3;
+                    }
+                    public <R> R returnR() {
+                        throw new RuntimeException("Stub");
+                    }
+                }
+            """.trimIndent()
+        )
+        val boundedChild = Source.java(
+            "foo.bar.Child", """
+                package foo.bar;
+                public class Child extends Base<String> {
+                }
+            """.trimIndent()
+        )
+        runProcessorTest(
+            listOf(genericBase, boundedChild)
+        ) {
+            fun validateElement(element: XTypeElement, tTypeName: TypeName, rTypeName: TypeName) {
+                element.getMethod("returnT").let { method ->
+                    assertThat(method.parameters).isEmpty()
+                    assertThat(method.returnType.typeName).isEqualTo(tTypeName)
+                }
+                element.getMethod("receiveT").let { method ->
+                    method.getParameter("param1").let { param ->
+                        assertThat(param.type.typeName).isEqualTo(tTypeName)
+                    }
+                    assertThat(method.returnType.typeName).isEqualTo(TypeName.INT)
+                }
+                element.getMethod("receiveR").let { method ->
+                    method.getParameter("param1").let { param ->
+                        assertThat(param.type.typeName).isEqualTo(rTypeName)
+                    }
+                    assertThat(method.returnType.typeName).isEqualTo(TypeName.INT)
+                }
+                element.getMethod("returnR").let { method ->
+                    assertThat(method.parameters).isEmpty()
+                    assertThat(method.returnType.typeName).isEqualTo(rTypeName)
+                }
+            }
+            validateElement(
+                element = it.processingEnv.requireTypeElement("foo.bar.Base"),
+                tTypeName = TypeVariableName.get("T"),
+                rTypeName = TypeVariableName.get("R")
+            )
+            validateElement(
+                element = it.processingEnv.requireTypeElement("foo.bar.Child"),
+                tTypeName = ClassName.get(String::class.java),
+                rTypeName = TypeVariableName.get("R")
+            )
+        }
+    }
+
+    @Test
+    fun annotationAvailability() {
+        val source = Source.java(
+            "foo.bar.Baz", """
+            package foo.bar;
+            import org.junit.*;
+            import org.junit.runner.*;
+            import org.junit.runners.*;
+            import androidx.room.processing.testcode.OtherAnnotation;
+
+            @RunWith(JUnit4.class)
+            class Baz {
+                @OtherAnnotation(value="xx")
+                String testField;
+
+                @org.junit.Test
+                void testMethod() {}
+            }
+        """.trimIndent()
+        )
+        runProcessorTest(
+            listOf(source)
+        ) {
+            val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
+            assertThat(element.hasAnnotation(RunWith::class)).isTrue()
+            assertThat(element.hasAnnotation(Test::class)).isFalse()
+            element.getMethod("testMethod").let { method ->
+                assertThat(method.hasAnnotation(Test::class)).isTrue()
+                assertThat(method.hasAnnotation(Override::class)).isFalse()
+                assertThat(
+                    method.hasAnnotationInPackage(
+                        "org.junit"
+                    )
+                ).isTrue()
+            }
+            element.getField("testField").let { field ->
+                assertThat(field.hasAnnotation(OtherAnnotation::class)).isTrue()
+                assertThat(field.hasAnnotation(Test::class)).isFalse()
+            }
+            assertThat(
+                element.hasAnnotationInPackage(
+                    "org.junit.runner"
+                )
+            ).isTrue()
+            assertThat(
+                element.hasAnnotationInPackage(
+                    "org.junit"
+                )
+            ).isFalse()
+            assertThat(
+                element.hasAnnotationInPackage(
+                    "foo.bar"
+                )
+            ).isFalse()
+        }
+    }
+
+    @Test
+    fun nonType() {
+        val source = Source.java(
+            "foo.bar.Baz", """
+            package foo.bar;
+            class Baz {
+            }
+        """.trimIndent()
+        )
+        runProcessorTest(
+            listOf(source)
+        ) {
+            val element = it.processingEnv.requireTypeElement("java.lang.Object")
+            // make sure we return null for not existing types
+            assertThat(element.superType).isNull()
+        }
+    }
+
+    @Test
+    fun isSomething() {
+        val subject = Source.java(
+            "foo.bar.Baz", """
+            package foo.bar;
+            class Baz {
+                int field;
+
+                void method() {}
+                static interface Inner {}
+            }
+        """.trimIndent()
+        )
+        runProcessorTest(
+            sources = listOf(subject)
+        ) {
+            val inner = ClassName.get("foo.bar", "Baz.Inner")
+            assertThat(
+                it.processingEnv.requireTypeElement(inner).isInterface()
+            ).isTrue()
+            val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
+            assertThat(element.isInterface()).isFalse()
+            assertThat(element.isAbstract()).isFalse()
+            assertThat(element.isType()).isTrue()
+            element.getField("field").let { field ->
+                assertThat(field.isType()).isFalse()
+                assertThat(field.isAbstract()).isFalse()
+                assertThat(field.isField()).isTrue()
+                assertThat(field.isMethod()).isFalse()
+            }
+            element.getMethod("method").let { method ->
+                assertThat(method.isType()).isFalse()
+                assertThat(method.isAbstract()).isFalse()
+                assertThat(method.isField()).isFalse()
+                assertThat(method.isMethod()).isTrue()
+            }
+        }
+    }
+
+    @Test
+    fun notATypeElement() {
+        val source = Source.java(
+            "foo.bar.Baz", """
+            package foo.bar;
+            class Baz {
+                public static int x;
+            }
+        """.trimIndent()
+        )
+        runProcessorTest(
+            listOf(source)
+        ) {
+            val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
+            element.getField("x").let { field ->
+                assertThat(field.isStatic()).isTrue()
+                val fail = runCatching {
+                    field.asTypeElement()
+                }
+                assertThat(fail.exceptionOrNull()).isNotNull()
+            }
+        }
+    }
+
+    @Test
+    fun nullability() {
+        val source = Source.java(
+            "foo.bar.Baz", """
+            package foo.bar;
+
+            import androidx.annotation.*;
+            import java.util.List;
+            class Baz {
+                public static int primitiveInt;
+                public static Integer boxedInt;
+                @NonNull
+                public static List<String> nonNullAnnotated;
+                @Nullable
+                public static List<String> nullableAnnotated;
+            }
+        """.trimIndent()
+        )
+        runProcessorTest(
+            sources = listOf(source)
+        ) {
+            val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
+            element.getField("primitiveInt").let { field ->
+                assertThat(field.nullability).isEqualTo(XNullability.NONNULL)
+            }
+            element.getField("boxedInt").let { field ->
+                assertThat(field.nullability).isEqualTo(XNullability.UNKNOWN)
+            }
+            element.getField("nonNullAnnotated").let { field ->
+                assertThat(field.nullability).isEqualTo(XNullability.NONNULL)
+            }
+            element.getField("nullableAnnotated").let { field ->
+                assertThat(field.nullability).isEqualTo(XNullability.NULLABLE)
+            }
+        }
+    }
+
+    @Test
+    fun toStringMatchesUnderlyingElement() {
+        runProcessorTest {
+            it.processingEnv.findTypeElement("java.util.List").let { list ->
+                assertThat(list.toString()).isEqualTo("java.util.List")
+            }
+        }
+    }
+}
diff --git a/room/compiler-xprocessing/src/test/java/androidx/room/processing/XExecutableElementTest.kt b/room/compiler-xprocessing/src/test/java/androidx/room/processing/XExecutableElementTest.kt
new file mode 100644
index 0000000..4181d68
--- /dev/null
+++ b/room/compiler-xprocessing/src/test/java/androidx/room/processing/XExecutableElementTest.kt
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+import androidx.room.processing.util.Source
+import androidx.room.processing.util.getDeclaredMethod
+import androidx.room.processing.util.getMethod
+import androidx.room.processing.util.getParameter
+import androidx.room.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
+import com.squareup.javapoet.TypeName
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class XExecutableElementTest {
+    @Test
+    fun basic() {
+        runProcessorTest(
+            listOf(
+                Source.java(
+                    "foo.bar.Baz", """
+                package foo.bar;
+                public class Baz {
+                    private void foo() {}
+                    public int bar(int param1) {
+                        return 3;
+                    }
+                }
+            """.trimIndent()
+                )
+            )
+        ) {
+            val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
+            element.getDeclaredMethod("foo").let { method ->
+                assertThat(method.isJavaDefault()).isFalse()
+                assertThat(method.isVarArgs()).isFalse()
+                assertThat(method.isOverrideableIgnoringContainer()).isFalse()
+                assertThat(method.parameters).isEmpty()
+                val returnType = method.returnType
+                assertThat(returnType.isVoid()).isTrue()
+                assertThat(returnType.defaultValue()).isEqualTo("null")
+            }
+            element.getDeclaredMethod("bar").let { method ->
+                assertThat(method.isOverrideableIgnoringContainer()).isTrue()
+                assertThat(method.parameters).hasSize(1)
+                method.getParameter("param1").let { param ->
+                    assertThat(param.type.isPrimitiveInt()).isTrue()
+                }
+                assertThat(method.returnType.isPrimitiveInt()).isTrue()
+            }
+        }
+    }
+    @Test
+    fun isVarArgs() {
+        val subject = Source.java(
+            "foo.bar.Baz", """
+            package foo.bar;
+            interface Baz {
+                void method(String... inputs);
+            }
+        """.trimIndent()
+        )
+        runProcessorTest(
+            sources = listOf(subject)
+        ) {
+            val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
+            assertThat(element.getMethod("method").isVarArgs()).isTrue()
+        }
+    }
+
+    @Test
+    fun kotlinDefaultImpl() {
+        val subject = Source.kotlin(
+            "Baz.kt", """
+            package foo.bar;
+            import java.util.List;
+            interface Baz {
+                fun noDefault()
+                fun withDefault(): Int {
+                    return 3;
+                }
+                fun nameMatch()
+                fun nameMatch(param:Int) {}
+                fun withDefaultWithParams(param1:Int, param2:String) {}
+                fun withDefaultWithTypeArgs(param1: List<String>): String {
+                    return param1.first();
+                }
+            }
+        """.trimIndent()
+        )
+        runProcessorTest(
+            sources = listOf(subject)
+        ) {
+            val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
+            element.getDeclaredMethod("noDefault").let { method ->
+                assertThat(method.findKotlinDefaultImpl()).isNull()
+            }
+            element.getDeclaredMethod("withDefault").let { method ->
+                val defaultImpl = method.findKotlinDefaultImpl()
+                assertThat(defaultImpl).isNotNull()
+                assertThat(defaultImpl!!.returnType.typeName).isEqualTo(TypeName.INT)
+                // default impl gets self as first parameter
+                assertThat(defaultImpl.parameters).hasSize(1)
+                assertThat(defaultImpl.parameters.first().type)
+                    .isEqualTo(element.type)
+            }
+            element.getDeclaredMethods().first {
+                it.name == "nameMatch" && it.parameters.isEmpty()
+            }.let { nameMatchWithoutDefault ->
+                assertThat(nameMatchWithoutDefault.findKotlinDefaultImpl()).isNull()
+            }
+
+            element.getDeclaredMethods().first {
+                it.name == "nameMatch" && it.parameters.size == 1
+            }.let { nameMatchWithoutDefault ->
+                assertThat(nameMatchWithoutDefault.findKotlinDefaultImpl()).isNotNull()
+            }
+
+            element.getDeclaredMethod("withDefaultWithParams").let { method ->
+                val defaultImpl = method.findKotlinDefaultImpl()
+                assertThat(defaultImpl).isNotNull()
+                assertThat(defaultImpl!!.parameters.drop(1).map {
+                    it.name
+                }).containsExactly("param1", "param2")
+            }
+
+            element.getDeclaredMethod("withDefaultWithTypeArgs").let { method ->
+                val defaultImpl = method.findKotlinDefaultImpl()
+                assertThat(defaultImpl).isNotNull()
+                assertThat(defaultImpl!!.parameters.drop(1).map {
+                    it.name
+                }).containsExactly("param1")
+            }
+        }
+    }
+}
diff --git a/room/compiler-xprocessing/src/test/java/androidx/room/processing/XProcessingEnvTest.kt b/room/compiler-xprocessing/src/test/java/androidx/room/processing/XProcessingEnvTest.kt
new file mode 100644
index 0000000..7305f37
--- /dev/null
+++ b/room/compiler-xprocessing/src/test/java/androidx/room/processing/XProcessingEnvTest.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+import androidx.room.processing.util.Source
+import androidx.room.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.TypeName
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class XProcessingEnvTest {
+    @Test
+    fun getElement() {
+        runProcessorTest(
+            listOf(
+                Source.java(
+                    "foo.bar.Baz", """
+                package foo.bar;
+                public class Baz {
+                }
+            """.trimIndent()
+                )
+            )
+        ) {
+            val qName = "java.util.List"
+            val className = ClassName.get("java.util", "List")
+            val klass = List::class
+            val element = it.processingEnv.requireTypeElement(qName)
+            assertThat(element).isNotNull()
+            assertThat(element.packageName).isEqualTo("java.util")
+            assertThat(element.name).isEqualTo("List")
+
+            val type = element.type
+
+            assertThat(
+                it.processingEnv.findTypeElement(qName)
+            ).isEqualTo(element)
+            assertThat(
+                it.processingEnv.findTypeElement(className)
+            ).isEqualTo(element)
+            assertThat(
+                it.processingEnv.findTypeElement(klass)
+            ).isEqualTo(element)
+
+            assertThat(
+                it.processingEnv.requireTypeElement(className)
+            ).isEqualTo(element)
+            assertThat(
+                it.processingEnv.requireTypeElement(klass)
+            ).isEqualTo(element)
+
+            assertThat(
+                it.processingEnv.findType(qName)
+            ).isEqualTo(type)
+            assertThat(
+                it.processingEnv.findType(className)
+            ).isEqualTo(type)
+            assertThat(
+                it.processingEnv.findType(klass)
+            ).isEqualTo(type)
+
+            assertThat(
+                it.processingEnv.requireType(className)
+            ).isEqualTo(type)
+            assertThat(
+                it.processingEnv.requireType(klass)
+            ).isEqualTo(type)
+            assertThat(
+                it.processingEnv.requireType(qName)
+            ).isEqualTo(type)
+        }
+    }
+
+    @Test
+    fun basic() {
+        runProcessorTest(
+            listOf(
+                Source.java(
+                    "foo.bar.Baz", """
+                package foo.bar;
+                public class Baz {
+                    private void foo() {}
+                    public int bar(int param1) {
+                        return 3;
+                    }
+                }
+            """.trimIndent()
+                )
+            )
+        ) {
+            val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
+            assertThat(element.packageName).isEqualTo("foo.bar")
+            assertThat(element.name).isEqualTo("Baz")
+            assertThat(element.asDeclaredType().typeName)
+                .isEqualTo(ClassName.get("foo.bar", "Baz"))
+            assertThat(element.getConstructors()).hasSize(1)
+            assertThat(element.getDeclaredMethods()).hasSize(2)
+            assertThat(element.kindName()).isEqualTo("class")
+            assertThat(element.isInterface()).isFalse()
+            assertThat(element.superType?.typeName).isEqualTo(TypeName.OBJECT)
+        }
+    }
+
+    @Test
+    fun getPrimitives() {
+        val source = Source.java(
+            "foo.bar.Baz", """
+            package foo.bar;
+            class Baz {
+            }
+        """.trimIndent()
+        )
+        runProcessorTest(
+            listOf(source)
+        ) { invocation ->
+            PRIMITIVE_TYPES.forEach {
+                val targetType = invocation.processingEnv.findType(it.key)
+                assertThat(targetType?.typeName).isEqualTo(it.value)
+                assertThat(targetType?.boxed()?.typeName).isEqualTo(it.value.box())
+            }
+        }
+    }
+
+    companion object {
+        val PRIMITIVE_TYPES = mapOf(
+            TypeName.BOOLEAN.toString() to TypeName.BOOLEAN,
+            TypeName.BYTE.toString() to TypeName.BYTE,
+            TypeName.SHORT.toString() to TypeName.SHORT,
+            TypeName.INT.toString() to TypeName.INT,
+            TypeName.LONG.toString() to TypeName.LONG,
+            TypeName.CHAR.toString() to TypeName.CHAR,
+            TypeName.FLOAT.toString() to TypeName.FLOAT,
+            TypeName.DOUBLE.toString() to TypeName.DOUBLE
+        )
+    }
+}
diff --git a/room/compiler-xprocessing/src/test/java/androidx/room/processing/XProcessingStepTest.kt b/room/compiler-xprocessing/src/test/java/androidx/room/processing/XProcessingStepTest.kt
new file mode 100644
index 0000000..f26a7ba
--- /dev/null
+++ b/room/compiler-xprocessing/src/test/java/androidx/room/processing/XProcessingStepTest.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+import androidx.room.processing.testcode.MainAnnotation
+import androidx.room.processing.testcode.OtherAnnotation
+import com.google.auto.common.BasicAnnotationProcessor
+import com.google.common.truth.Truth
+import com.google.testing.compile.JavaFileObjects
+import com.google.testing.compile.JavaSourcesSubjectFactory
+import org.junit.Test
+import kotlin.reflect.KClass
+
+class XProcessingStepTest {
+    @Test
+    fun xProcessingStep() {
+        val annotatedElements = mutableMapOf<KClass<out Annotation>, String>()
+        val processingStep = object : XProcessingStep {
+            override fun process(
+                env: XProcessingEnv,
+                elementsByAnnotation: Map<KClass<out Annotation>, List<XTypeElement>>
+            ): Set<XTypeElement> {
+                elementsByAnnotation[OtherAnnotation::class]?.forEach {
+                    annotatedElements[OtherAnnotation::class] = it.qualifiedName
+                }
+                elementsByAnnotation[MainAnnotation::class]?.forEach {
+                    annotatedElements[MainAnnotation::class] = it.qualifiedName
+                }
+                return emptySet()
+            }
+
+            override fun annotations(): Set<KClass<out Annotation>> {
+                return setOf(OtherAnnotation::class, MainAnnotation::class)
+            }
+        }
+        val mainProcessor = object : BasicAnnotationProcessor() {
+            override fun initSteps(): Iterable<ProcessingStep> {
+                return listOf(
+                    processingStep.asAutoCommonProcessor(
+                        XProcessingEnv.create(processingEnv)
+                    )
+                )
+            }
+        }
+        val main = JavaFileObjects.forSourceString(
+            "foo.bar.Main", """
+            package foo.bar;
+            import androidx.room.processing.testcode.*;
+            @MainAnnotation(
+                typeList = {},
+                singleType = Object.class,
+                intMethod = 3,
+                singleOtherAnnotation = @OtherAnnotation("y")
+            )
+            class Main {
+            }
+        """.trimIndent()
+        )
+        val other = JavaFileObjects.forSourceString(
+            "foo.bar.Other", """
+            package foo.bar;
+            import androidx.room.processing.testcode.*;
+            @OtherAnnotation("x")
+            class Other {
+            }
+        """.trimIndent()
+        )
+        Truth.assertAbout(
+            JavaSourcesSubjectFactory.javaSources()
+        ).that(
+            listOf(main, other)
+        ).processedWith(
+            mainProcessor
+        ).compilesWithoutError()
+        Truth.assertThat(annotatedElements).containsExactlyEntriesIn(
+            mapOf(
+                MainAnnotation::class to "foo.bar.Main",
+                OtherAnnotation::class to "foo.bar.Other"
+            )
+        )
+    }
+}
\ No newline at end of file
diff --git a/room/compiler-xprocessing/src/test/java/androidx/room/processing/XTypeTest.kt b/room/compiler-xprocessing/src/test/java/androidx/room/processing/XTypeTest.kt
new file mode 100644
index 0000000..9ef38f7
--- /dev/null
+++ b/room/compiler-xprocessing/src/test/java/androidx/room/processing/XTypeTest.kt
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing
+
+import androidx.room.processing.util.Source
+import androidx.room.processing.util.getDeclaredMethod
+import androidx.room.processing.util.getField
+import androidx.room.processing.util.getMethod
+import androidx.room.processing.util.runProcessorTest
+import androidx.room.processing.util.runProcessorTestForFailedCompilation
+import com.google.common.truth.Truth.assertThat
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeVariableName
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class XTypeTest {
+    @Test
+    fun declaredTypeArguments() {
+        val parent = Source.java(
+            "foo.bar.Parent", """
+            package foo.bar;
+            import java.io.InputStream;
+            import java.util.Set;
+            class Parent<InputStreamType extends InputStream> {
+                public void wildcardParam(Set<?> param1) {}
+            }
+        """.trimIndent()
+        )
+        runProcessorTest(
+            sources = listOf(parent)
+        ) {
+            val type = it.processingEnv.requireType("foo.bar.Parent") as XDeclaredType
+            val className = ClassName.get("foo.bar", "Parent")
+            assertThat(type.typeName).isEqualTo(
+                ParameterizedTypeName.get(
+                    className,
+                    ClassName.get("", "InputStreamType")
+                )
+            )
+            assertThat(type.isDeclared()).isTrue()
+
+            val typeArguments = type.typeArguments
+            assertThat(typeArguments).hasSize(1)
+            typeArguments.first().let { firstType ->
+                assertThat(firstType.isDeclared()).isFalse()
+                val expected = TypeVariableName.get(
+                    "InputStreamType",
+                    ClassName.get("java.io", "InputStream")
+                )
+                assertThat(firstType.typeName).isEqualTo(expected)
+            }
+
+            type.asTypeElement().getMethod("wildcardParam").let { method ->
+                val wildcardParam = method.parameters.first()
+                val extendsBoundOrSelf = wildcardParam.type.extendsBoundOrSelf()
+                assertThat(extendsBoundOrSelf.erasure())
+                    .isEqualTo(
+                        it.processingEnv.requireType("java.util.Set").erasure()
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun errorType() {
+        val missingTypeRef = Source.java(
+            "foo.bar.Baz",
+            """
+                package foo.bar;
+                public class Baz {
+                    NotExistingType badField;
+                    NotExistingType badMethod() {
+                        throw new RuntimeException("Stub");
+                    }
+                }
+            """.trimIndent()
+        )
+        runProcessorTestForFailedCompilation(
+            sources = listOf(missingTypeRef)
+        ) {
+            val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
+            element.getField("badField").let { field ->
+                assertThat(field.type.isError()).isTrue()
+                assertThat(field.type.typeName).isEqualTo(
+                    ClassName.get("", "NotExistingType")
+                )
+            }
+            element.getDeclaredMethod("badMethod").let { method ->
+                assertThat(method.returnType.isError()).isTrue()
+                assertThat(method.returnType.typeName).isEqualTo(
+                    ClassName.get("", "NotExistingType")
+                )
+            }
+        }
+    }
+
+    @Test
+    fun sameType() {
+        val subject = Source.java(
+            "foo.bar.Baz", """
+            package foo.bar;
+            interface Baz {
+                void method(String... inputs);
+            }
+        """.trimIndent()
+        )
+        runProcessorTest(
+            sources = listOf(subject)
+        ) {
+            val type = it.processingEnv.requireType("foo.bar.Baz")
+            val list = it.processingEnv.requireType("java.util.List")
+            val string = it.processingEnv.requireType("java.lang.String")
+            assertThat(type.isSameType(type)).isTrue()
+            assertThat(type.isSameType(list)).isFalse()
+            assertThat(list.isSameType(string)).isFalse()
+        }
+    }
+
+    @Test
+    fun isCollection() {
+        runProcessorTest {
+            it.processingEnv.requireType("java.util.List").let { list ->
+                assertThat(list.isCollection()).isTrue()
+            }
+            it.processingEnv.requireType("java.util.Set").let { list ->
+                assertThat(list.isCollection()).isTrue()
+            }
+            it.processingEnv.requireType("java.util.Map").let { list ->
+                assertThat(list.isCollection()).isFalse()
+            }
+        }
+    }
+
+    @Test
+    fun toStringMatchesUnderlyingElement() {
+        runProcessorTest {
+            it.processingEnv.requireType("java.lang.Integer").let { map ->
+                assertThat(map.toString()).isEqualTo("java.lang.Integer")
+            }
+        }
+    }
+
+    @Test
+    fun errorTypeForSuper() {
+        val missingTypeRef = Source.java(
+            "foo.bar.Baz",
+            """
+                package foo.bar;
+                public class Baz extends IDontExist {
+                    NotExistingType foo() {
+                        throw new RuntimeException("Stub");
+                    }
+                }
+            """.trimIndent()
+        )
+        runProcessorTestForFailedCompilation(
+            sources = listOf(missingTypeRef)
+        ) {
+            val element = it.processingEnv.requireTypeElement("foo.bar.Baz")
+            assertThat(element.superType?.isError()).isTrue()
+        }
+    }
+
+    @Test
+    fun defaultValues() {
+        runProcessorTest {
+            assertThat(
+                it.processingEnv.requireType("int").defaultValue()
+            ).isEqualTo("0")
+            assertThat(
+                it.processingEnv.requireType("java.lang.String").defaultValue()
+            ).isEqualTo("null")
+            assertThat(
+                it.processingEnv.requireType("double").defaultValue()
+            ).isEqualTo("0.0")
+            assertThat(
+                it.processingEnv.requireType("float").defaultValue()
+            ).isEqualTo("0f")
+            assertThat(
+                it.processingEnv.requireType("char").defaultValue()
+            ).isEqualTo("0")
+        }
+    }
+
+    @Test
+    fun boxed() {
+        runProcessorTest {
+            assertThat(
+                it.processingEnv.requireType("int").boxed().typeName
+            ).isEqualTo(TypeName.get(java.lang.Integer::class.java))
+            assertThat(
+                it.processingEnv.requireType("java.lang.String").boxed().typeName
+            ).isEqualTo(TypeName.get(String::class.java))
+        }
+    }
+
+    @Test
+    fun erasure() {
+        runProcessorTest {
+            val subject = it.processingEnv.getDeclaredType(
+                it.processingEnv.requireTypeElement(List::class),
+                it.processingEnv.requireType(String::class)
+            )
+            val listClassName = ClassName.get(List::class.java)
+            assertThat(subject.typeName).isEqualTo(
+                ParameterizedTypeName.get(listClassName, TypeName.get(String::class.java))
+            )
+            assertThat(subject.erasure().typeName).isEqualTo(
+                listClassName
+            )
+        }
+    }
+}
diff --git a/room/compiler-xprocessing/src/test/java/androidx/room/processing/javac/kotlin/JvmDescriptorUtilsTest.kt b/room/compiler-xprocessing/src/test/java/androidx/room/processing/javac/kotlin/JvmDescriptorUtilsTest.kt
new file mode 100644
index 0000000..4bb6c67
--- /dev/null
+++ b/room/compiler-xprocessing/src/test/java/androidx/room/processing/javac/kotlin/JvmDescriptorUtilsTest.kt
@@ -0,0 +1,369 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.javac.kotlin
+
+import com.google.auto.common.MoreElements
+import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
+import com.google.testing.compile.CompileTester
+import com.google.testing.compile.JavaFileObjects
+import com.google.testing.compile.JavaSourcesSubjectFactory
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import javax.annotation.processing.AbstractProcessor
+import javax.annotation.processing.RoundEnvironment
+import javax.lang.model.element.ElementKind.CONSTRUCTOR
+import javax.lang.model.element.ElementKind.FIELD
+import javax.lang.model.element.ElementKind.METHOD
+import javax.lang.model.element.TypeElement
+import javax.tools.JavaFileObject
+
+@RunWith(JUnit4::class)
+class JvmDescriptorUtilsTest {
+
+    private val describeAnnotation =
+        """
+        package androidx.room.test;
+
+        import java.lang.annotation.ElementType;
+        import java.lang.annotation.Target;
+
+        @Target({ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR})
+        public @interface Describe { }
+        """.toJFO("androidx.room.test.Describe")
+
+    @Test
+    fun descriptor_method_simple() {
+        singleRun(
+            """
+            package androidx.room.test;
+
+            public class DummyClass {
+                @Describe
+                public void emptyMethod() {
+                }
+            }
+            """.toJFO("androidx.room.test.DummyClass")
+        ) { descriptors ->
+            assertThat(descriptors.first())
+                .isEqualTo("emptyMethod()V")
+        }
+    }
+
+    @Test
+    fun descriptor_field() {
+        singleRun(
+            """
+            package androidx.room.test;
+
+            import java.util.List;
+
+            class DummyClass<T> {
+                @Describe
+                int field1;
+
+                @Describe
+                String field2;
+
+                @Describe
+                T field3;
+
+                @Describe
+                List<String> field4;
+            }
+            """.toJFO("androidx.room.test.DummyClass")
+        ) { descriptors ->
+            assertThat(descriptors).isEqualTo(
+                setOf(
+                    "field1:I",
+                    "field2:Ljava/lang/String;",
+                    "field3:Ljava/lang/Object;",
+                    "field4:Ljava/util/List;"
+                )
+            )
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun descriptor_method_erasured() {
+        singleRun(
+            """
+            package androidx.room.test;
+
+            import java.util.ArrayList;
+            import java.util.Collection;
+            import java.util.List;
+            import java.util.Map;
+
+            class DummyClass<T> {
+                @Describe
+                void method1(T something) { }
+
+                @Describe
+                T method2() { return null; }
+
+                @Describe
+                List<? extends String> method3() { return null; }
+
+                @Describe
+                Map<T, String> method4() { return null; }
+
+                @Describe
+                ArrayList<Map<T, String>> method5() { return null; }
+
+                @Describe
+                static <I, O extends I> O method6(I input) { return null; }
+
+                @Describe
+                static <I, O extends String> O method7(I input) { return null; }
+
+                @Describe
+                static <P extends Collection & Comparable> P method8() { return null; }
+
+                @Describe
+                static <P extends String & List<Character>> P method9() { return null; }
+            }
+            """.toJFO("androidx.room.test.DummyClass")
+        ) { descriptors ->
+            assertThat(descriptors).isEqualTo(
+                setOf(
+                    "method1(Ljava/lang/Object;)V",
+                    "method2()Ljava/lang/Object;",
+                    "method3()Ljava/util/List;",
+                    "method4()Ljava/util/Map;",
+                    "method5()Ljava/util/ArrayList;",
+                    "method6(Ljava/lang/Object;)Ljava/lang/Object;",
+                    "method7(Ljava/lang/Object;)Ljava/lang/String;",
+                    "method8()Ljava/util/Collection;",
+                    "method9()Ljava/lang/String;"
+                )
+            )
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun descriptor_method_primitiveParams() {
+        singleRun(
+            """
+            package androidx.room.test;
+
+            class DummyClass {
+                @Describe
+                void method1(boolean yesOrNo, int number) { }
+
+                @Describe
+                byte method2(char letter) { return 0; }
+
+                @Describe
+                void method3(double realNumber1, float realNummber2) { }
+
+                @Describe
+                void method4(long bigNumber, short littlerNumber) { }
+            }
+            """.toJFO("androidx.room.test.DummyClass")
+        ) { descriptors ->
+            assertThat(descriptors)
+                .isEqualTo(setOf("method1(ZI)V", "method2(C)B", "method3(DF)V", "method4(JS)V"))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun descriptor_method_classParam_javaTypes() {
+        singleRun(
+            """
+            package androidx.room.test;
+
+            import java.util.ArrayList;
+            import java.util.List;
+            import java.util.Map;
+
+            class DummyClass {
+                @Describe
+                void method1(Object something) { }
+
+                @Describe
+                Object method2() { return null; }
+
+                @Describe
+                List<String> method3(ArrayList<Integer> list) { return null; }
+
+                @Describe
+                Map<String, Object> method4() { return null; }
+            }
+            """.toJFO("androidx.room.test.DummyClass")
+        ) { descriptors ->
+            assertThat(descriptors).isEqualTo(
+                setOf(
+                    "method1(Ljava/lang/Object;)V",
+                    "method2()Ljava/lang/Object;",
+                    "method3(Ljava/util/ArrayList;)Ljava/util/List;",
+                    "method4()Ljava/util/Map;"
+                )
+            )
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun descriptor_method_classParam_testClass() {
+        val extraJfo =
+            """
+            package androidx.room.test;
+
+            class DataClass { }
+            """.toJFO("androidx.room.test.DataClass")
+
+        singleRun(
+            """
+            package androidx.room.test;
+
+            class DummyClass {
+                @Describe
+                void method1(DataClass data) { }
+
+                @Describe
+                DataClass method2() { return null; }
+            }
+            """.toJFO("androidx.room.test.DummyClass"), extraJfo
+        ) { descriptors ->
+            assertThat(descriptors).isEqualTo(
+                setOf(
+                    "method1(Landroidx/room/test/DataClass;)V",
+                    "method2()Landroidx/room/test/DataClass;"
+                )
+            )
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun descriptor_method_classParam_innerTestClass() {
+        val extraJfo =
+            """
+            package androidx.room.test;
+
+            class DataClass {
+
+                class MemberInnerData { }
+
+                static class StaticInnerData { }
+
+                enum EnumData {
+                    VALUE1, VALUE2
+                }
+            }
+            """.toJFO("androidx.room.test.DataClass")
+
+        singleRun(
+            """
+            package androidx.room.test;
+
+            class DummyClass {
+                @Describe
+                void method1(DataClass.MemberInnerData data) { }
+
+                @Describe
+                void method2(DataClass.StaticInnerData data) { }
+
+                @Describe
+                void method3(DataClass.EnumData enumData) { }
+
+                @Describe
+                DataClass.StaticInnerData method4() { return null; }
+            }
+            """.toJFO("androidx.room.test.DummyClass"), extraJfo
+        ) { descriptors ->
+            assertThat(descriptors).isEqualTo(
+                setOf(
+                    "method1(Landroidx/room/test/DataClass\$MemberInnerData;)V",
+                    "method2(Landroidx/room/test/DataClass\$StaticInnerData;)V",
+                    "method3(Landroidx/room/test/DataClass\$EnumData;)V",
+                    "method4()Landroidx/room/test/DataClass\$StaticInnerData;"
+                )
+            )
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun descriptor_method_arrayParams() {
+        val extraJfo =
+            """
+            package androidx.room.test;
+
+            class DataClass { }
+            """.toJFO("androidx.room.test.DataClass")
+
+        singleRun(
+            """
+            package androidx.room.test;
+
+            class DummyClass {
+                @Describe
+                void method1(DataClass[] data) { }
+
+                @Describe
+                DataClass[] method2() { return null; }
+
+                @Describe
+                void method3(int[] array) { }
+
+                @Describe
+                void method4(int... array) { }
+            }
+            """.toJFO("androidx.room.test.DummyClass"), extraJfo
+        ) { descriptors ->
+            assertThat(descriptors).isEqualTo(
+                setOf(
+                    "method1([Landroidx/room/test/DataClass;)V",
+                    "method2()[Landroidx/room/test/DataClass;",
+                    "method3([I)V",
+                    "method4([I)V"
+                )
+            )
+        }.compilesWithoutError()
+    }
+
+    private fun String.toJFO(qName: String): JavaFileObject =
+        JavaFileObjects.forSourceLines(qName, this)
+
+    @Suppress("UnstableApiUsage")
+    private fun singleRun(
+        vararg jfo: JavaFileObject,
+        handler: (Set<String>) -> Unit
+    ): CompileTester {
+        return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
+            .that(listOf(describeAnnotation) + jfo)
+            .processedWith(object : AbstractProcessor() {
+                override fun process(
+                    annotations: Set<TypeElement>,
+                    roundEnv: RoundEnvironment
+                ): Boolean {
+                    roundEnv.getElementsAnnotatedWith(annotations.first()).map { element ->
+                        when (element.kind) {
+                            FIELD -> MoreElements.asVariable(element).descriptor()
+                            METHOD, CONSTRUCTOR -> MoreElements.asExecutable(element).descriptor()
+                            else -> error("Unsupported element to describe.")
+                        }
+                    }.toSet().let(handler)
+                    return true
+                }
+
+                override fun getSupportedOptions(): Set<String> {
+                    return setOf("androidx.room.test.Describe")
+                }
+            })
+    }
+}
\ No newline at end of file
diff --git a/room/compiler-xprocessing/src/test/java/androidx/room/processing/javac/kotlin/KotlinMetadataElementTest.kt b/room/compiler-xprocessing/src/test/java/androidx/room/processing/javac/kotlin/KotlinMetadataElementTest.kt
new file mode 100644
index 0000000..d60932e
--- /dev/null
+++ b/room/compiler-xprocessing/src/test/java/androidx/room/processing/javac/kotlin/KotlinMetadataElementTest.kt
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.javac.kotlin
+
+import androidx.room.processing.javac.JavacProcessingEnv
+import androidx.room.processing.util.runProcessorTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.AssumptionViolatedException
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.element.TypeElement
+import javax.lang.model.util.ElementFilter
+import kotlin.reflect.KClass
+
+@RunWith(JUnit4::class)
+class KotlinMetadataElementTest {
+
+    @Test
+    fun getParameterNames() {
+        simpleRun { processingEnv ->
+            val (testClassElement, metadataElement) = getMetadataElement(
+                processingEnv,
+                TestData::class
+            )
+            assertThat(testClassElement.getDeclaredMethods()
+                .first { it.simpleName.toString() == "functionWithParams" }
+                .let { metadataElement.getParameterNames(it) }
+            ).isEqualTo(
+                listOf("param1", "yesOrNo", "number")
+            )
+        }
+    }
+
+    @Test
+    fun findPrimaryConstructorSignature() {
+        simpleRun { invocation ->
+            val (testClassElement, metadataElement) = getMetadataElement(
+                invocation,
+                TestData::class
+            )
+            assertThat(
+                testClassElement.getConstructors().map {
+                    val desc = it.descriptor()
+                    desc to (desc == metadataElement.findPrimaryConstructorSignature())
+                }
+            ).containsExactly(
+                "<init>(Ljava/lang/String;)V" to true,
+                "<init>()V" to false
+            )
+        }
+    }
+
+    @Test
+    fun isSuspendFunction() {
+        simpleRun { invocation ->
+            val (testClassElement, metadataElement) = getMetadataElement(
+                invocation,
+                TestData::class
+            )
+            assertThat(testClassElement.getDeclaredMethods().map {
+                it.simpleName.toString() to metadataElement.isSuspendFunction(it)
+            }).containsExactly(
+                "emptyFunction" to false,
+                "suspendFunction" to true,
+                "functionWithParams" to false,
+                "getConstructorParam" to false
+            )
+        }
+    }
+
+    @Test
+    fun isObject() {
+        simpleRun { invocation ->
+            val (_, objectTypeMetadata) = getMetadataElement(invocation, ObjectType::class)
+            assertThat(objectTypeMetadata.isObject()).isTrue()
+            val (_, testDataMetadata) = getMetadataElement(invocation, TestData::class)
+            assertThat(testDataMetadata.isObject()).isFalse()
+        }
+    }
+
+    private fun TypeElement.getDeclaredMethods() = ElementFilter.methodsIn(enclosedElements)
+
+    private fun TypeElement.getConstructors() = ElementFilter.constructorsIn(enclosedElements)
+
+    private fun simpleRun(
+        handler: (ProcessingEnvironment) -> Unit
+    ) {
+        runProcessorTest {
+            if (it.processingEnv !is JavacProcessingEnv) {
+                throw AssumptionViolatedException("This test only works for java/kapt compilation")
+            }
+            handler(it.processingEnv.delegate)
+        }
+    }
+
+    private fun getMetadataElement(processingEnv: ProcessingEnvironment, klass: KClass<*>) =
+        processingEnv.elementUtils.getTypeElement(klass.java.canonicalName).let {
+            it to KotlinMetadataElement.createFor(it)!!
+        }
+
+    @Suppress("unused")
+    private class TestData(val constructorParam: String) {
+
+        constructor() : this("anything")
+
+        fun emptyFunction() {}
+
+        @Suppress("RedundantSuspendModifier")
+        suspend fun suspendFunction() {
+        }
+
+        @Suppress("UNUSED_PARAMETER")
+        fun functionWithParams(param1: String, yesOrNo: Boolean, number: Int) {
+        }
+    }
+
+    object ObjectType {
+        val foo: String = ""
+    }
+}
\ No newline at end of file
diff --git a/room/compiler-xprocessing/src/test/java/androidx/room/processing/testcode/KotlinTestClass.kt b/room/compiler-xprocessing/src/test/java/androidx/room/processing/testcode/KotlinTestClass.kt
new file mode 100644
index 0000000..b3b17a7
--- /dev/null
+++ b/room/compiler-xprocessing/src/test/java/androidx/room/processing/testcode/KotlinTestClass.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("unused")
+
+package androidx.room.processing.testcode
+
+@Suppress("UNUSED_PARAMETER", "RedundantSuspendModifier")
+class KotlinTestClass {
+    suspend fun mySuspendMethod(
+        param1: String
+    ) {
+    }
+}
+
+object KotlinTestObject
diff --git a/room/compiler-xprocessing/src/test/java/androidx/room/processing/testcode/MainAnnotation.java b/room/compiler-xprocessing/src/test/java/androidx/room/processing/testcode/MainAnnotation.java
new file mode 100644
index 0000000..f587f46
--- /dev/null
+++ b/room/compiler-xprocessing/src/test/java/androidx/room/processing/testcode/MainAnnotation.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.testcode;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * used in compilation tests
+ */
+@SuppressWarnings("unused")
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface MainAnnotation {
+    Class<?>[] typeList();
+
+    Class<?> singleType();
+
+    int intMethod();
+
+    boolean boolMethodWithDefault() default true;
+
+    OtherAnnotation[] otherAnnotationArray() default {};
+
+    OtherAnnotation singleOtherAnnotation();
+}
diff --git a/room/compiler-xprocessing/src/test/java/androidx/room/processing/testcode/OtherAnnotation.java b/room/compiler-xprocessing/src/test/java/androidx/room/processing/testcode/OtherAnnotation.java
new file mode 100644
index 0000000..e1468e5
--- /dev/null
+++ b/room/compiler-xprocessing/src/test/java/androidx/room/processing/testcode/OtherAnnotation.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.testcode;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * used in compilation tests
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface OtherAnnotation {
+    String value();
+}
diff --git a/room/compiler-xprocessing/src/test/java/androidx/room/processing/util/ProcessorTestExt.kt b/room/compiler-xprocessing/src/test/java/androidx/room/processing/util/ProcessorTestExt.kt
new file mode 100644
index 0000000..2044072
--- /dev/null
+++ b/room/compiler-xprocessing/src/test/java/androidx/room/processing/util/ProcessorTestExt.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.util
+
+import androidx.room.processing.SyntheticJavacProcessor
+import com.google.common.truth.Truth
+import com.google.testing.compile.CompileTester
+import com.google.testing.compile.JavaSourcesSubjectFactory
+import com.tschuchort.compiletesting.KotlinCompilation
+
+private fun compileSources(
+    sources: List<Source>,
+    handler: (TestInvocation) -> Unit
+): Pair<SyntheticJavacProcessor, CompileTester> {
+    val syntheticJavacProcessor = SyntheticJavacProcessor(handler)
+    return syntheticJavacProcessor to Truth.assertAbout(
+        JavaSourcesSubjectFactory.javaSources()
+    ).that(
+        sources.map {
+            it.toJFO()
+        }
+    ).processedWith(
+        syntheticJavacProcessor
+    )
+}
+
+private fun compileWithKapt(
+    sources: List<Source>,
+    handler: (TestInvocation) -> Unit
+): Pair<SyntheticJavacProcessor, KotlinCompilation> {
+    val syntheticJavacProcessor = SyntheticJavacProcessor(handler)
+    val compilation = KotlinCompilation()
+    sources.forEach {
+        compilation.workingDir.resolve("sources")
+            .resolve(it.relativePath())
+            .parentFile
+            .mkdirs()
+    }
+    compilation.sources = sources.map {
+        it.toKotlinSourceFile()
+    }
+    compilation.annotationProcessors = listOf(syntheticJavacProcessor)
+    compilation.inheritClassPath = true
+    compilation.verbose = false
+
+    return syntheticJavacProcessor to compilation
+}
+
+fun runProcessorTest(
+    sources: List<Source> = emptyList(),
+    handler: (TestInvocation) -> Unit
+) {
+    @Suppress("NAME_SHADOWING")
+    val sources = if (sources.isEmpty()) {
+        // synthesize a source to trigger compilation
+        listOf(Source.java("foo.bar.SyntheticSource", """
+            package foo.bar;
+            public class SyntheticSource {}
+        """.trimIndent()))
+    } else {
+        sources
+    }
+    // we can compile w/ javac only if all code is in java
+    if (sources.all { it is Source.JavaSource }) {
+        val (syntheticJavacProcessor, compileTester) = compileSources(sources, handler)
+        compileTester.compilesWithoutError()
+        syntheticJavacProcessor.throwIfFailed()
+    }
+
+    // now run with kapt
+    val (kaptProcessor, kotlinCompilation) = compileWithKapt(sources, handler)
+    val compilationResult = kotlinCompilation.compile()
+    Truth.assertThat(compilationResult.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
+    kaptProcessor.throwIfFailed()
+}
+
+fun runProcessorTestForFailedCompilation(
+    sources: List<Source>,
+    handler: (TestInvocation) -> Unit
+) {
+    val (syntheticJavacProcessor, compileTester) = compileSources(sources, handler)
+    compileTester.failsToCompile()
+    syntheticJavacProcessor.throwIfFailed()
+
+    // now run with kapt
+    val (kaptProcessor, kotlinCompilation) = compileWithKapt(sources, handler)
+    val compilationResult = kotlinCompilation.compile()
+    Truth.assertThat(compilationResult.exitCode).isNotEqualTo(KotlinCompilation.ExitCode.OK)
+    kaptProcessor.throwIfFailed()
+}
diff --git a/room/compiler-xprocessing/src/test/java/androidx/room/processing/util/Source.kt b/room/compiler-xprocessing/src/test/java/androidx/room/processing/util/Source.kt
new file mode 100644
index 0000000..868f4ba
--- /dev/null
+++ b/room/compiler-xprocessing/src/test/java/androidx/room/processing/util/Source.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.util
+
+import com.google.testing.compile.JavaFileObjects
+import com.tschuchort.compiletesting.SourceFile
+import org.intellij.lang.annotations.Language
+import javax.tools.JavaFileObject
+
+/**
+ * Common abstraction for test sources in kotlin and java
+ */
+sealed class Source {
+    abstract fun toJFO(): JavaFileObject
+    abstract fun toKotlinSourceFile(): SourceFile
+
+    // we need this for kotlin compile testing as it doesn't create directories
+    abstract fun relativePath(): String
+
+    class JavaSource(
+        val qName: String,
+        val contents: String
+    ) : Source() {
+        override fun toJFO(): JavaFileObject {
+            return JavaFileObjects.forSourceString(
+                qName,
+                contents
+            )
+        }
+
+        override fun toKotlinSourceFile(): SourceFile {
+            return SourceFile.java(
+                relativePath(),
+                contents
+            )
+        }
+
+        override fun relativePath(): String {
+            return qName.replace(".", "/") + ".java"
+        }
+    }
+
+    class KotlinSource(
+        val filePath: String,
+        val contents: String
+    ) : Source() {
+        override fun toJFO(): JavaFileObject {
+            throw IllegalStateException("cannot include kotlin code in javac compilation")
+        }
+
+        override fun toKotlinSourceFile(): SourceFile {
+            return SourceFile.kotlin(
+                filePath,
+                contents
+            )
+        }
+
+        override fun relativePath(): String = filePath
+    }
+
+    companion object {
+        fun java(
+            qName: String,
+            @Language("java")
+            code: String
+        ): Source {
+            return JavaSource(
+                qName,
+                code
+            )
+        }
+
+        fun kotlin(
+            filePath: String,
+            @Language("kotlin")
+            code: String
+        ): Source {
+            return KotlinSource(
+                filePath,
+                code
+            )
+        }
+    }
+}
diff --git a/room/compiler-xprocessing/src/test/java/androidx/room/processing/util/TestExtensions.kt b/room/compiler-xprocessing/src/test/java/androidx/room/processing/util/TestExtensions.kt
new file mode 100644
index 0000000..22e2859
--- /dev/null
+++ b/room/compiler-xprocessing/src/test/java/androidx/room/processing/util/TestExtensions.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.util
+
+import androidx.room.processing.XExecutableElement
+import androidx.room.processing.XTypeElement
+
+fun XTypeElement.getField(name: String) = getAllFieldsIncludingPrivateSupers().first {
+    it.name == name
+}
+
+fun XTypeElement.getDeclaredMethod(name: String) = getDeclaredMethods().firstOrNull {
+    it.name == name
+} ?: throw AssertionError("cannot find method with name $name")
+
+fun XTypeElement.getMethod(name: String) = getAllMethods().firstOrNull {
+    it.name == name
+} ?: throw AssertionError("cannot find method with name $name")
+
+fun XExecutableElement.getParameter(name: String) = parameters.firstOrNull {
+    it.name == name
+} ?: throw AssertionError("cannot find parameter with name $name")
diff --git a/room/compiler-xprocessing/src/test/java/androidx/room/processing/util/TestInvocation.kt b/room/compiler-xprocessing/src/test/java/androidx/room/processing/util/TestInvocation.kt
new file mode 100644
index 0000000..ab1f69e
--- /dev/null
+++ b/room/compiler-xprocessing/src/test/java/androidx/room/processing/util/TestInvocation.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.processing.util
+
+import androidx.room.processing.XProcessingEnv
+
+class TestInvocation(
+    val processingEnv: XProcessingEnv
+)
diff --git a/settings.gradle b/settings.gradle
index 90be06a..6434bb1 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -242,6 +242,7 @@
 includeProject(":paging:paging-runtime-ktx", "paging/runtime/ktx")
 includeProject(":paging:paging-rxjava2", "paging/rxjava2")
 includeProject(":paging:paging-rxjava2-ktx", "paging/rxjava2/ktx")
+includeProject(":paging:paging-rxjava3", "paging/rxjava3")
 includeProject(":paging:paging-guava", "paging/guava")
 includeProject(":paging:samples", "paging/samples")
 includeProject(":palette:palette", "palette/palette")
@@ -263,6 +264,7 @@
 includeProject(":room:room-benchmark", "room/benchmark")
 includeProject(":room:room-common", "room/common")
 includeProject(":room:room-compiler", "room/compiler")
+includeProject(":room:room-compiler-xprocessing", "room/compiler-xprocessing")
 includeProject(":room:room-guava", "room/guava")
 includeProject(":room:room-ktx", "room/ktx")
 includeProject(":room:room-migration", "room/migration")
diff --git a/ui/ui-internal-lint-checks/src/main/java/androidx/ui/lint/ComposeIssueRegistry.kt b/ui/ui-internal-lint-checks/src/main/java/androidx/ui/lint/ComposeIssueRegistry.kt
index 178fd66..2ccc7b7 100644
--- a/ui/ui-internal-lint-checks/src/main/java/androidx/ui/lint/ComposeIssueRegistry.kt
+++ b/ui/ui-internal-lint-checks/src/main/java/androidx/ui/lint/ComposeIssueRegistry.kt
@@ -25,6 +25,9 @@
     override val minApi = CURRENT_API
     override val api = 8
     override val issues get(): List<Issue> {
-        return listOf(UnnecessaryLambdaCreationDetector.ISSUE) + AndroidXIssueRegistry.Issues
+        return listOf(
+            UnnecessaryLambdaCreationDetector.ISSUE,
+            PackageNameMigrationDetector.ISSUE
+        ) + AndroidXIssueRegistry.Issues
     }
 }
diff --git a/ui/ui-internal-lint-checks/src/main/java/androidx/ui/lint/PackageNameMigrationDetector.kt b/ui/ui-internal-lint-checks/src/main/java/androidx/ui/lint/PackageNameMigrationDetector.kt
new file mode 100644
index 0000000..1846910
--- /dev/null
+++ b/ui/ui-internal-lint-checks/src/main/java/androidx/ui/lint/PackageNameMigrationDetector.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.ui.lint
+
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import org.jetbrains.uast.UFile
+
+/**
+ * Simple lint check that prevents using old package names (map defined in
+ * [PackageNameMigrationMap]) after a library has migrated to the new name.
+ *
+ * TODO: b/160233169 remove this lint check after the migration has finished.
+ */
+class PackageNameMigrationDetector : Detector(), SourceCodeScanner {
+    override fun getApplicableUastTypes() = listOf(UFile::class.java)
+
+    override fun createUastHandler(context: JavaContext) = object : UElementHandler() {
+        override fun visitFile(node: UFile) {
+            val packageName = node.packageName
+
+            if (packageName in PackageNameMigrationMap) {
+                val newPackageName = PackageNameMigrationMap[packageName]
+                context.report(
+                    ISSUE,
+                    node,
+                    context.getLocation(node),
+                    "The package name '$packageName' has been migrated to '$newPackageName', " +
+                            "please update the package name of this file accordingly."
+                )
+            }
+        }
+    }
+    companion object {
+        private val PackageNameMigrationMap: Map<String, String> = mapOf(
+            // placeholder package name used in PackageNameMigrationDetectorTest, since the
+            // migration has not started yet
+            "androidx.ui.foo" to "androidx.compose.foo"
+        )
+
+        val ISSUE = Issue.create(
+            "PackageNameMigration",
+            "Using an old package name that has recently been migrated to androidx.compose",
+            "As part of a large migration from androidx.ui to androidx.compose, package names " +
+                    "across all libraries are being refactored. If you are seeing this Lint " +
+                    "error, you are adding new files to the old package name, once the rest of " +
+                    "the library has migrated to the new package name.",
+            Category.PERFORMANCE, 5, Severity.ERROR,
+            Implementation(
+                PackageNameMigrationDetector::class.java,
+                Scope.JAVA_FILE_SCOPE
+            )
+        )
+    }
+}
diff --git a/ui/ui-internal-lint-checks/src/test/java/androidx/ui/lint/PackageNameMigrationDetectorTest.kt b/ui/ui-internal-lint-checks/src/test/java/androidx/ui/lint/PackageNameMigrationDetectorTest.kt
new file mode 100644
index 0000000..41344a6
--- /dev/null
+++ b/ui/ui-internal-lint-checks/src/test/java/androidx/ui/lint/PackageNameMigrationDetectorTest.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.ui.lint
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+/* ktlint-disable max-line-length */
+@RunWith(JUnit4::class)
+/**
+ * Test for [PackageNameMigrationDetector]
+ *
+ * TODO: b/160233169 remove this lint check after the migration has finished.
+ */
+class PackageNameMigrationDetectorTest : LintDetectorTest() {
+    override fun getDetector(): Detector = PackageNameMigrationDetector()
+
+    override fun getIssues(): MutableList<Issue> = mutableListOf(PackageNameMigrationDetector.ISSUE)
+
+    @Test
+    fun oldPackageShouldFail() {
+        lint().files(
+            kotlin(
+                """
+                package androidx.ui.foo
+
+                fun someApi() {}
+            """
+            )
+        )
+            .run()
+            .expect(
+                """
+src/androidx/ui/foo/test.kt:1: Error: The package name 'androidx.ui.foo' has been migrated to 'androidx.compose.foo', please update the package name of this file accordingly. [PackageNameMigration]
+
+^
+1 errors, 0 warnings
+            """
+            )
+    }
+
+    @Test
+    fun newPackageShouldPass() {
+        lint().files(
+            kotlin(
+                """
+                package androidx.compose.foo
+
+                fun someApi() {}
+            """
+            )
+        )
+            .run()
+            .expectClean()
+    }
+}
+/* ktlint-enable max-line-length */
diff --git a/ui/ui-material/src/main/java/androidx/ui/material/Surface.kt b/ui/ui-material/src/main/java/androidx/ui/material/Surface.kt
index 17b34a6..7f129d4 100644
--- a/ui/ui-material/src/main/java/androidx/ui/material/Surface.kt
+++ b/ui/ui-material/src/main/java/androidx/ui/material/Surface.kt
@@ -18,10 +18,11 @@
 
 import androidx.compose.Composable
 import androidx.compose.Providers
+import androidx.ui.core.DensityAmbient
 import androidx.ui.core.Layout
 import androidx.ui.core.Modifier
 import androidx.ui.core.clip
-import androidx.ui.core.drawShadow
+import androidx.ui.core.drawLayer
 import androidx.ui.core.zIndex
 import androidx.ui.foundation.Border
 import androidx.ui.foundation.ContentColorAmbient
@@ -87,8 +88,9 @@
     elevation: Dp = 0.dp,
     content: @Composable () -> Unit
 ) {
+    val elevationPx = with(DensityAmbient.current) { elevation.toPx() }
     SurfaceLayout(
-        modifier.drawShadow(elevation = elevation, shape = shape, clip = false)
+        modifier.drawLayer(shadowElevation = elevationPx, shape = shape)
             .zIndex(elevation.value)
             .plus(if (border != null) Modifier.drawBorder(border, shape) else Modifier)
             .background(
diff --git a/ui/ui-text/src/commonMain/kotlin/androidx/ui/text/CoreText.kt b/ui/ui-text/src/commonMain/kotlin/androidx/ui/text/CoreText.kt
index 97f1d54..2967eee 100644
--- a/ui/ui-text/src/commonMain/kotlin/androidx/ui/text/CoreText.kt
+++ b/ui/ui-text/src/commonMain/kotlin/androidx/ui/text/CoreText.kt
@@ -31,6 +31,7 @@
 import androidx.ui.core.LayoutCoordinates
 import androidx.ui.core.Modifier
 import androidx.ui.core.drawBehind
+import androidx.ui.core.drawLayer
 import androidx.ui.core.globalPosition
 import androidx.ui.core.onPositioned
 import androidx.ui.core.selection.Selectable
@@ -126,7 +127,7 @@
 
     Layout(
         children = { InlineChildren(text, inlineComposables) },
-        modifier = modifier.drawBehind {
+        modifier = modifier.drawLayer().drawBehind {
             state.layoutResult?.let { layoutResult ->
                 drawCanvas { canvas, _ ->
                     state.selectionRange?.let {
diff --git a/ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/ComposeViewAdapterTest.kt b/ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/ComposeViewAdapterTest.kt
index 430fdb4..d35e3e58 100644
--- a/ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/ComposeViewAdapterTest.kt
+++ b/ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/ComposeViewAdapterTest.kt
@@ -71,9 +71,6 @@
             // Verify that valid line numbers are being recorded
             assertTrue(viewInfos.map { it.lineNumber }.all { it > 0 })
             // Verify that method names are being captured
-            assertTrue(viewInfos.map { it.methodName }.all {
-                it.startsWith("androidx.ui.tooling.")
-            })
         }
     }
 
diff --git a/ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/inspector/LayoutInspectorTreeTest.kt b/ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/inspector/LayoutInspectorTreeTest.kt
index 6ece044..9be0e70 100644
--- a/ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/inspector/LayoutInspectorTreeTest.kt
+++ b/ui/ui-tooling/src/androidTest/java/androidx/ui/tooling/inspector/LayoutInspectorTreeTest.kt
@@ -215,6 +215,7 @@
             name = "CoreText",
             fileName = "CoreText.kt",
             function = "androidx.ui.text.CoreTextKt.CoreText",
+            isRenderNode = true,
             left = 0.0.dp, top = 0.0.dp, width = 70.5.dp, height = 18.9.dp
         ) {
             parameter(name = "maxLines", type = ParameterType.Int32, value = 2147483647)
@@ -301,6 +302,7 @@
             name = "SurfaceLayout",
             fileName = "Surface.kt",
             function = "androidx.ui.material.SurfaceKt.Surface",
+            isRenderNode = true,
             left = 0.0.dp, top = 18.9.dp, width = 64.0.dp, height = 36.0.dp,
             children = listOf("Box")
         )
@@ -405,6 +407,7 @@
             name = "CoreText",
             fileName = "CoreText.kt",
             function = "androidx.ui.text.CoreTextKt.CoreText",
+            isRenderNode = true,
             left = 21.8.dp, top = 27.6.dp, width = 20.4.dp, height = 18.9.dp
         ) {
             parameter(name = "maxLines", type = ParameterType.Int32, value = 2147483647)
diff --git a/ui/ui-tooling/src/main/java/androidx/ui/tooling/inspector/LayoutInspectorTree.kt b/ui/ui-tooling/src/main/java/androidx/ui/tooling/inspector/LayoutInspectorTree.kt
index 46f9062..d85b518 100644
--- a/ui/ui-tooling/src/main/java/androidx/ui/tooling/inspector/LayoutInspectorTree.kt
+++ b/ui/ui-tooling/src/main/java/androidx/ui/tooling/inspector/LayoutInspectorTree.kt
@@ -221,7 +221,11 @@
     }
 
     private fun getRenderNode(group: Group): Long =
-        group.modifierInfo.mapNotNull { (it.extra as? OwnedLayer)?.layerId }.singleOrNull() ?: 0
+        group.modifierInfo.asSequence()
+            .map { it.extra }
+            .filterIsInstance<OwnedLayer>()
+            .map { it.layerId }
+            .firstOrNull() ?: 0
 
     private fun addParameters(parameters: List<ParameterInformation>, node: MutableInspectorNode) =
         parameters.forEach { addParameter(it, node) }
diff --git a/ui/ui-tooling/src/main/java/androidx/ui/tooling/preview/ComposeViewAdapter.kt b/ui/ui-tooling/src/main/java/androidx/ui/tooling/preview/ComposeViewAdapter.kt
index b1d0f4e9..4ffa2e2 100644
--- a/ui/ui-tooling/src/main/java/androidx/ui/tooling/preview/ComposeViewAdapter.kt
+++ b/ui/ui-tooling/src/main/java/androidx/ui/tooling/preview/ComposeViewAdapter.kt
@@ -64,7 +64,6 @@
 data class ViewInfo(
     val fileName: String,
     val lineNumber: Int,
-    val methodName: String,
     val bounds: IntBounds,
     val location: SourceLocation?,
     val children: List<ViewInfo>
@@ -77,26 +76,12 @@
     override fun toString(): String =
         """($fileName:$lineNumber,
             |bounds=(top=${bounds.top}, left=${bounds.left},
+            |location=${location?.let { "(${it.offset}L${it.length}"} ?: "<none>" }
             |bottom=${bounds.bottom}, right=${bounds.right}),
             |childrenCount=${children.size})""".trimMargin()
 }
 
 /**
- * Regular expression that matches and extracts the key information as serialized in
- * [KeySourceInfo#recordSourceKeyInfo]. The expression supports two formats for backwards
- * compatibility:
- *
- *  - fileName:lineNumber
- *  - methodName (fileName:lineNumber)
- *
- *  API <=21 does not support named regex but for documentation purposes, the named version
- *  of the regex would be:
- *  `(?<method>[\w\\.$]*?)\s?\(?(?<fileName>[\w.]+):(?<lineNumber>\d+)\)?`
- */
-private val KEY_INFO_REGEX =
-    """([\w\\.$]*?)\s?\(?([\w.]+):(\d+)\)?""".toRegex()
-
-/**
  * View adapter that renders a `@Composable`. The `@Composable` is found by
  * reading the `tools:composableName` attribute that contains the FQN. Additional attributes can
  * be used to customize the behaviour of this view:
@@ -190,14 +175,10 @@
             .filter { !it.isNullGroup() }
             .map { it.toViewInfo() }
 
-        val match = KEY_INFO_REGEX.matchEntire(key as? String ?: "")
-            ?: return ViewInfo("", -1, "", box, location, childrenViewInfo)
-
         // TODO: Use group names instead of indexing once it's supported
         return ViewInfo(
-            match.groups[2]?.value ?: "",
-            match.groups[3]?.value?.toInt() ?: -1,
-            match.groups[1]?.value ?: "",
+            location?.sourceFile ?: "",
+            location?.lineNumber ?: -1,
             box,
             location,
             childrenViewInfo