Merge changes from topic "inavlidation-tracker-runtime" into androidx-main

* changes:
  Converting `invalidation tracker` related files in `room-runtime` from Java to Kotlin.
  Initial code check-in for renaming `invalidation tracker` related files in `room-runtime` from *.java to *.kt.
diff --git a/room/benchmark/src/androidTest/java/androidx/room/benchmark/InvalidationTrackerBenchmark.kt b/room/benchmark/src/androidTest/java/androidx/room/benchmark/InvalidationTrackerBenchmark.kt
index e3552e1..2d19f32 100644
--- a/room/benchmark/src/androidTest/java/androidx/room/benchmark/InvalidationTrackerBenchmark.kt
+++ b/room/benchmark/src/androidTest/java/androidx/room/benchmark/InvalidationTrackerBenchmark.kt
@@ -67,7 +67,7 @@
             .build()
 
         val observer = object : InvalidationTracker.Observer("user") {
-            override fun onInvalidated(tables: MutableSet<String>) {}
+            override fun onInvalidated(tables: Set<String>) {}
         }
         db.invalidationTracker.addObserver(observer)
 
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/InvalidationTrackerExt.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/InvalidationTrackerExt.kt
index 22c113d..f254556 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/InvalidationTrackerExt.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/InvalidationTrackerExt.kt
@@ -20,24 +20,12 @@
 import java.util.concurrent.TimeUnit
 
 /**
- * Makes refresh runnable accessible in tests
- */
-val InvalidationTracker.refreshRunnable: Runnable
-    get() = this.mRefreshRunnable
-
-/**
- * True if invalidation tracker is pending a refresh event to get database changes.
- */
-val InvalidationTracker.pendingRefresh
-    get() = this.mPendingRefresh.get()
-
-/**
  * Polls [InvalidationTracker] until it sets its pending refresh flag to true.
  */
 suspend fun InvalidationTracker.awaitPendingRefresh() {
     withTimeout(TimeUnit.SECONDS.toMillis(10)) {
         while (true) {
-            if (pendingRefresh) return@withTimeout
+            if (pendingRefresh.get()) return@withTimeout
             delay(50)
         }
     }
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/PagingSourceTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/PagingSourceTest.kt
index bf212a9..0457592 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/PagingSourceTest.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/PagingSourceTest.kt
@@ -36,8 +36,6 @@
 import androidx.room.Room
 import androidx.room.RoomDatabase
 import androidx.room.awaitPendingRefresh
-import androidx.room.pendingRefresh
-import androidx.room.refreshRunnable
 import androidx.sqlite.db.SimpleSQLiteQuery
 import androidx.sqlite.db.SupportSQLiteQuery
 import androidx.test.core.app.ApplicationProvider
@@ -467,7 +465,7 @@
                     toIndex = 20 + CONFIG.initialLoadSize
                 )
             )
-            assertThat(db.invalidationTracker.pendingRefresh).isFalse()
+            assertThat(db.invalidationTracker.pendingRefresh.get()).isFalse()
             // now do some changes in the database but don't let change notifications go through
             // to the data source. it should not crash :)
             queryExecutor.filterFunction = { runnable ->
@@ -521,7 +519,7 @@
             // Runs the original invalidationTracker.refreshRunnable.
             // Note that the second initial load's call to mRefreshRunnable resets the flag to
             // false, so this mRefreshRunnable will not detect changes in the table anymore.
-            assertThat(db.invalidationTracker.pendingRefresh).isFalse()
+            assertThat(db.invalidationTracker.pendingRefresh.get()).isFalse()
             queryExecutor.executeAll()
 
             itemStore.awaitInitialLoad()
@@ -556,7 +554,7 @@
         val blockingObserver = object : InvalidationTracker.Observer("PagingEntity") {
             // make sure observer blocks the time longer than the timeout of waiting for
             // paging source invalidation, so that we can assert new generation failure later
-            override fun onInvalidated(tables: MutableSet<String>) {
+            override fun onInvalidated(tables: Set<String>) {
                 Thread.sleep(3_500)
             }
         }
@@ -576,7 +574,7 @@
                 // should load starting from initial Key = 20
                 initialItems
             )
-            assertThat(db.invalidationTracker.pendingRefresh).isFalse()
+            assertThat(db.invalidationTracker.pendingRefresh.get()).isFalse()
 
             db.dao.deleteItems(
                 items.subList(0, 60).map { it.id }
@@ -643,7 +641,7 @@
                     toIndex = CONFIG.initialLoadSize
                 )
             )
-            assertThat(db.invalidationTracker.pendingRefresh).isFalse()
+            assertThat(db.invalidationTracker.pendingRefresh.get()).isFalse()
             // now do some changes in the database but don't let change notifications go through
             // to the data source. it should not crash :)
             queryExecutor.filterFunction = { runnable ->
@@ -696,7 +694,7 @@
             // Runs the original invalidationTracker.refreshRunnable.
             // Note that the second initial load's call to mRefreshRunnable resets the flag to
             // false, so this mRefreshRunnable will not detect changes in the table anymore.
-            assertThat(db.invalidationTracker.pendingRefresh).isFalse()
+            assertThat(db.invalidationTracker.pendingRefresh.get()).isFalse()
             queryExecutor.executeAll()
 
             itemStore.awaitInitialLoad()
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/SyncTriggersConcurrencyTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/SyncTriggersConcurrencyTest.kt
index 43da129..0586d72 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/SyncTriggersConcurrencyTest.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/SyncTriggersConcurrencyTest.kt
@@ -142,7 +142,7 @@
 
         val latch = CountDownLatch(expectedInvalidationCount)
 
-        override fun onInvalidated(tables: MutableSet<String>) {
+        override fun onInvalidated(tables: Set<String>) {
             latch.countDown()
         }
     }
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/InvalidationTrackerTrojan.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/InvalidationTrackerTrojan.java
index 13a5469..c57639a 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/InvalidationTrackerTrojan.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/InvalidationTrackerTrojan.java
@@ -21,7 +21,7 @@
  */
 public class InvalidationTrackerTrojan {
     public static int countObservers(InvalidationTracker tracker) {
-        return tracker.mObserverMap.size();
+        return tracker.observerMap.size();
     }
 
     private InvalidationTrackerTrojan() {
diff --git a/room/room-common/build.gradle b/room/room-common/build.gradle
index bf73c08..2f7b61b 100644
--- a/room/room-common/build.gradle
+++ b/room/room-common/build.gradle
@@ -22,7 +22,7 @@
 }
 
 dependencies {
-    api("androidx.annotation:annotation:1.1.0")
+    api("androidx.annotation:annotation:1.3.0")
     api(libs.kotlinStdlibJdk8)
     testImplementation(libs.junit)
     testImplementation(libs.mockitoCore)
diff --git a/room/room-ktx/src/androidTest/java/androidx/room/CoroutineRoomCancellationTest.kt b/room/room-ktx/src/androidTest/java/androidx/room/CoroutineRoomCancellationTest.kt
index 00f0cb4..445b404 100644
--- a/room/room-ktx/src/androidTest/java/androidx/room/CoroutineRoomCancellationTest.kt
+++ b/room/room-ktx/src/androidTest/java/androidx/room/CoroutineRoomCancellationTest.kt
@@ -179,7 +179,7 @@
         }
     }
 
-    private class TestInvalidationTracker(db: RoomDatabase) : InvalidationTracker(db) {
+    private class TestInvalidationTracker(db: RoomDatabase) : InvalidationTracker(db, "") {
         val observers = mutableListOf<Observer>()
 
         override fun addObserver(observer: Observer) {
diff --git a/room/room-ktx/src/main/java/androidx/room/CoroutinesRoom.kt b/room/room-ktx/src/main/java/androidx/room/CoroutinesRoom.kt
index 27dd4d8..8d0b2a8 100644
--- a/room/room-ktx/src/main/java/androidx/room/CoroutinesRoom.kt
+++ b/room/room-ktx/src/main/java/androidx/room/CoroutinesRoom.kt
@@ -111,7 +111,7 @@
                 // Observer channel receives signals from the invalidation tracker to emit queries.
                 val observerChannel = Channel<Unit>(Channel.CONFLATED)
                 val observer = object : InvalidationTracker.Observer(tableNames) {
-                    override fun onInvalidated(tables: MutableSet<String>) {
+                    override fun onInvalidated(tables: Set<String>) {
                         observerChannel.trySend(Unit)
                     }
                 }
diff --git a/room/room-paging/src/androidTest/kotlin/androidx/room/InvalidationTrackerExtRoomPaging.kt b/room/room-paging/src/androidTest/kotlin/androidx/room/InvalidationTrackerExtRoomPaging.kt
index 6ba8c2a..6d83c13 100644
--- a/room/room-paging/src/androidTest/kotlin/androidx/room/InvalidationTrackerExtRoomPaging.kt
+++ b/room/room-paging/src/androidTest/kotlin/androidx/room/InvalidationTrackerExtRoomPaging.kt
@@ -23,14 +23,14 @@
  * Makes refresh runnable accessible in tests. Used for LimitOffsetPagingSource unit tests that
  * needs to block InvalidationTracker's invalidation notification
  */
-val InvalidationTracker.refreshRunnable: Runnable
-    get() = this.mRefreshRunnable
+val InvalidationTracker.refreshRunnableForTest: Runnable
+    get() = this.refreshRunnable
 
 /**
  * True if invalidation tracker is pending a refresh event to get database changes.
  */
-val InvalidationTracker.pendingRefresh
-    get() = this.mPendingRefresh.get()
+val InvalidationTracker.pendingRefreshForTest
+    get() = this.pendingRefresh.get()
 
 /**
  * Polls [InvalidationTracker] until it sets its pending refresh flag to true.
@@ -38,7 +38,7 @@
 suspend fun InvalidationTracker.awaitPendingRefresh() {
     withTimeout(TimeUnit.SECONDS.toMillis(3)) {
         while (true) {
-            if (pendingRefresh) return@withTimeout
+            if (pendingRefreshForTest) return@withTimeout
             delay(50)
         }
     }
diff --git a/room/room-paging/src/androidTest/kotlin/androidx/room/paging/LimitOffsetPagingSourceTest.kt b/room/room-paging/src/androidTest/kotlin/androidx/room/paging/LimitOffsetPagingSourceTest.kt
index e4991c8..a8795ae 100644
--- a/room/room-paging/src/androidTest/kotlin/androidx/room/paging/LimitOffsetPagingSourceTest.kt
+++ b/room/room-paging/src/androidTest/kotlin/androidx/room/paging/LimitOffsetPagingSourceTest.kt
@@ -27,7 +27,6 @@
 import androidx.room.RoomDatabase
 import androidx.room.RoomSQLiteQuery
 import androidx.room.awaitPendingRefresh
-import androidx.room.refreshRunnable
 import androidx.room.util.getColumnIndexOrThrow
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
diff --git a/room/room-paging/src/main/java/androidx/room/paging/util/ThreadSafeInvalidationObserver.kt b/room/room-paging/src/main/java/androidx/room/paging/util/ThreadSafeInvalidationObserver.kt
index 90b9fb7..86c7a76 100644
--- a/room/room-paging/src/main/java/androidx/room/paging/util/ThreadSafeInvalidationObserver.kt
+++ b/room/room-paging/src/main/java/androidx/room/paging/util/ThreadSafeInvalidationObserver.kt
@@ -20,14 +20,15 @@
 import androidx.room.RoomDatabase
 import java.util.concurrent.atomic.AtomicBoolean
 
+@Suppress("UNCHECKED_CAST")
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 class ThreadSafeInvalidationObserver(
     tables: Array<out String>,
     val onInvalidated: () -> Unit,
-) : InvalidationTracker.Observer(tables) {
+) : InvalidationTracker.Observer(tables = tables as Array<String>) {
     private val registered: AtomicBoolean = AtomicBoolean(false)
 
-    override fun onInvalidated(tables: MutableSet<String>) {
+    override fun onInvalidated(tables: Set<String>) {
         onInvalidated()
     }
 
diff --git a/room/room-runtime/api/current.ignore b/room/room-runtime/api/current.ignore
new file mode 100644
index 0000000..4652540
--- /dev/null
+++ b/room/room-runtime/api/current.ignore
@@ -0,0 +1,5 @@
+// Baseline format: 1.0
+ChangedType: androidx.room.Room#databaseBuilder(android.content.Context, Class<T>, String):
+    Method androidx.room.Room.databaseBuilder has changed return type from androidx.room.RoomDatabase.Builder<T!> to androidx.room.RoomDatabase.Builder<T>
+ChangedType: androidx.room.Room#inMemoryDatabaseBuilder(android.content.Context, Class<T>):
+    Method androidx.room.Room.inMemoryDatabaseBuilder has changed return type from androidx.room.RoomDatabase.Builder<T!> to androidx.room.RoomDatabase.Builder<T>
diff --git a/room/room-runtime/api/current.txt b/room/room-runtime/api/current.txt
index 2d84411..019422f 100644
--- a/room/room-runtime/api/current.txt
+++ b/room/room-runtime/api/current.txt
@@ -25,24 +25,34 @@
   }
 
   public class InvalidationTracker {
-    method @WorkerThread public void addObserver(androidx.room.InvalidationTracker.Observer);
+    method @WorkerThread public void addObserver(androidx.room.InvalidationTracker.Observer observer);
     method public void refreshVersionsAsync();
-    method @WorkerThread public void removeObserver(androidx.room.InvalidationTracker.Observer);
+    method @WorkerThread public void removeObserver(androidx.room.InvalidationTracker.Observer observer);
+    field public static final androidx.room.InvalidationTracker.Companion Companion;
+  }
+
+  public static final class InvalidationTracker.Companion {
   }
 
   public abstract static class InvalidationTracker.Observer {
-    ctor protected InvalidationTracker.Observer(String, java.lang.String!...);
-    ctor public InvalidationTracker.Observer(String![]);
-    method public abstract void onInvalidated(java.util.Set<java.lang.String!>);
+    ctor public InvalidationTracker.Observer(String![] tables);
+    ctor protected InvalidationTracker.Observer(String firstTable, java.lang.String... rest);
+    method public abstract void onInvalidated(java.util.Set<java.lang.String> tables);
   }
 
   public class Room {
     ctor @Deprecated public Room();
-    method public static <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T!> databaseBuilder(android.content.Context, Class<T!>, String);
-    method public static <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T!> inMemoryDatabaseBuilder(android.content.Context, Class<T!>);
+    method public static final <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T> databaseBuilder(android.content.Context context, Class<T> klass, String? name);
+    method public static final <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T> inMemoryDatabaseBuilder(android.content.Context context, Class<T> klass);
+    field public static final androidx.room.Room.Companion Companion;
     field public static final String MASTER_TABLE_NAME = "room_master_table";
   }
 
+  public static final class Room.Companion {
+    method public <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T> databaseBuilder(android.content.Context context, Class<T> klass, String? name);
+    method public <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T> inMemoryDatabaseBuilder(android.content.Context context, Class<T> klass);
+  }
+
   public abstract class RoomDatabase {
     ctor public RoomDatabase();
     method @Deprecated public void beginTransaction();
diff --git a/room/room-runtime/api/public_plus_experimental_current.txt b/room/room-runtime/api/public_plus_experimental_current.txt
index 3debf0f..695f17f 100644
--- a/room/room-runtime/api/public_plus_experimental_current.txt
+++ b/room/room-runtime/api/public_plus_experimental_current.txt
@@ -28,29 +28,39 @@
   }
 
   public class InvalidationTracker {
-    method @WorkerThread public void addObserver(androidx.room.InvalidationTracker.Observer);
+    method @WorkerThread public void addObserver(androidx.room.InvalidationTracker.Observer observer);
     method public void refreshVersionsAsync();
-    method @WorkerThread public void removeObserver(androidx.room.InvalidationTracker.Observer);
+    method @WorkerThread public void removeObserver(androidx.room.InvalidationTracker.Observer observer);
+    field public static final androidx.room.InvalidationTracker.Companion Companion;
+  }
+
+  public static final class InvalidationTracker.Companion {
   }
 
   public abstract static class InvalidationTracker.Observer {
-    ctor protected InvalidationTracker.Observer(String, java.lang.String!...);
-    ctor public InvalidationTracker.Observer(String![]);
-    method public abstract void onInvalidated(java.util.Set<java.lang.String!>);
+    ctor public InvalidationTracker.Observer(String![] tables);
+    ctor protected InvalidationTracker.Observer(String firstTable, java.lang.String... rest);
+    method public abstract void onInvalidated(java.util.Set<java.lang.String> tables);
   }
 
-  @androidx.room.ExperimentalRoomApi public class MultiInstanceInvalidationService extends android.app.Service {
+  @androidx.room.ExperimentalRoomApi public final class MultiInstanceInvalidationService extends android.app.Service {
     ctor public MultiInstanceInvalidationService();
-    method public android.os.IBinder? onBind(android.content.Intent);
+    method public android.os.IBinder onBind(android.content.Intent intent);
   }
 
   public class Room {
     ctor @Deprecated public Room();
-    method public static <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T!> databaseBuilder(android.content.Context, Class<T!>, String);
-    method public static <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T!> inMemoryDatabaseBuilder(android.content.Context, Class<T!>);
+    method public static final <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T> databaseBuilder(android.content.Context context, Class<T> klass, String? name);
+    method public static final <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T> inMemoryDatabaseBuilder(android.content.Context context, Class<T> klass);
+    field public static final androidx.room.Room.Companion Companion;
     field public static final String MASTER_TABLE_NAME = "room_master_table";
   }
 
+  public static final class Room.Companion {
+    method public <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T> databaseBuilder(android.content.Context context, Class<T> klass, String? name);
+    method public <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T> inMemoryDatabaseBuilder(android.content.Context context, Class<T> klass);
+  }
+
   public abstract class RoomDatabase {
     ctor public RoomDatabase();
     method @Deprecated public void beginTransaction();
diff --git a/room/room-runtime/api/restricted_current.ignore b/room/room-runtime/api/restricted_current.ignore
index 757a1db..8bb79fc 100644
--- a/room/room-runtime/api/restricted_current.ignore
+++ b/room/room-runtime/api/restricted_current.ignore
@@ -1,4 +1,12 @@
 // Baseline format: 1.0
+ChangedType: androidx.room.InvalidationTracker#createLiveData(String[], boolean, java.util.concurrent.Callable<T>):
+    Method androidx.room.InvalidationTracker.createLiveData has changed return type from androidx.lifecycle.LiveData<T!> to androidx.lifecycle.LiveData<T>
+ChangedType: androidx.room.InvalidationTracker#createLiveData(String[], java.util.concurrent.Callable<T>):
+    Method androidx.room.InvalidationTracker.createLiveData has changed return type from androidx.lifecycle.LiveData<T!> to androidx.lifecycle.LiveData<T>
+ChangedType: androidx.room.Room#databaseBuilder(android.content.Context, Class<T>, String):
+    Method androidx.room.Room.databaseBuilder has changed return type from androidx.room.RoomDatabase.Builder<T!> to androidx.room.RoomDatabase.Builder<T>
+ChangedType: androidx.room.Room#inMemoryDatabaseBuilder(android.content.Context, Class<T>):
+    Method androidx.room.Room.inMemoryDatabaseBuilder has changed return type from androidx.room.RoomDatabase.Builder<T!> to androidx.room.RoomDatabase.Builder<T>
 ChangedType: androidx.room.util.FtsTableInfo#columns:
     Field androidx.room.util.FtsTableInfo.columns has changed type from java.util.Set<java.lang.String!> to java.util.Set<java.lang.String>
 ChangedType: androidx.room.util.FtsTableInfo#options:
diff --git a/room/room-runtime/api/restricted_current.txt b/room/room-runtime/api/restricted_current.txt
index 4900922..0ec9fe0 100644
--- a/room/room-runtime/api/restricted_current.txt
+++ b/room/room-runtime/api/restricted_current.txt
@@ -57,30 +57,40 @@
   }
 
   public class InvalidationTracker {
-    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public InvalidationTracker(androidx.room.RoomDatabase!, java.lang.String!...);
-    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public InvalidationTracker(androidx.room.RoomDatabase!, java.util.Map<java.lang.String!,java.lang.String!>!, java.util.Map<java.lang.String!,java.util.Set<java.lang.String!>!>!, java.lang.String!...);
-    method @WorkerThread public void addObserver(androidx.room.InvalidationTracker.Observer);
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void addWeakObserver(androidx.room.InvalidationTracker.Observer!);
-    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T> androidx.lifecycle.LiveData<T!>! createLiveData(String![]!, java.util.concurrent.Callable<T!>!);
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T> androidx.lifecycle.LiveData<T!>! createLiveData(String![]!, boolean, java.util.concurrent.Callable<T!>!);
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public InvalidationTracker(androidx.room.RoomDatabase database, java.util.Map<java.lang.String,java.lang.String> shadowTablesMap, java.util.Map<java.lang.String,java.util.Set<java.lang.String>> viewTables, java.lang.String... tableNames);
+    ctor @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public InvalidationTracker(androidx.room.RoomDatabase database, java.lang.String... tableNames);
+    method @WorkerThread public void addObserver(androidx.room.InvalidationTracker.Observer observer);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void addWeakObserver(androidx.room.InvalidationTracker.Observer observer);
+    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T> androidx.lifecycle.LiveData<T> createLiveData(String![] tableNames, java.util.concurrent.Callable<T> computeFunction);
+    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public <T> androidx.lifecycle.LiveData<T> createLiveData(String![] tableNames, boolean inTransaction, java.util.concurrent.Callable<T> computeFunction);
     method public void refreshVersionsAsync();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @WorkerThread public void refreshVersionsSync();
-    method @WorkerThread public void removeObserver(androidx.room.InvalidationTracker.Observer);
+    method @WorkerThread public void removeObserver(androidx.room.InvalidationTracker.Observer observer);
+    field public static final androidx.room.InvalidationTracker.Companion Companion;
+  }
+
+  public static final class InvalidationTracker.Companion {
   }
 
   public abstract static class InvalidationTracker.Observer {
-    ctor protected InvalidationTracker.Observer(String, java.lang.String!...);
-    ctor public InvalidationTracker.Observer(String![]);
-    method public abstract void onInvalidated(java.util.Set<java.lang.String!>);
+    ctor public InvalidationTracker.Observer(String![] tables);
+    ctor protected InvalidationTracker.Observer(String firstTable, java.lang.String... rest);
+    method public abstract void onInvalidated(java.util.Set<java.lang.String> tables);
   }
 
   public class Room {
     ctor @Deprecated public Room();
-    method public static <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T!> databaseBuilder(android.content.Context, Class<T!>, String);
-    method public static <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T!> inMemoryDatabaseBuilder(android.content.Context, Class<T!>);
+    method public static final <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T> databaseBuilder(android.content.Context context, Class<T> klass, String? name);
+    method public static final <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T> inMemoryDatabaseBuilder(android.content.Context context, Class<T> klass);
+    field public static final androidx.room.Room.Companion Companion;
     field public static final String MASTER_TABLE_NAME = "room_master_table";
   }
 
+  public static final class Room.Companion {
+    method public <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T> databaseBuilder(android.content.Context context, Class<T> klass, String? name);
+    method public <T extends androidx.room.RoomDatabase> androidx.room.RoomDatabase.Builder<T> inMemoryDatabaseBuilder(android.content.Context context, Class<T> klass);
+  }
+
   public abstract class RoomDatabase {
     ctor public RoomDatabase();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void assertNotMainThread();
diff --git a/room/room-runtime/src/main/java/androidx/room/AutoCloser.java b/room/room-runtime/src/main/java/androidx/room/AutoCloser.java
index ba157ec..eb67727 100644
--- a/room/room-runtime/src/main/java/androidx/room/AutoCloser.java
+++ b/room/room-runtime/src/main/java/androidx/room/AutoCloser.java
@@ -24,6 +24,7 @@
 import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
 import androidx.arch.core.util.Function;
 import androidx.room.util.SneakyThrow;
@@ -40,8 +41,12 @@
  * SupportSqliteDatabase.
  *
  * It is important to ensure that the ref count is incremented when using a returned database.
+ *
+ * @hide
  */
-final class AutoCloser {
+// TODO: (b/218894771) Make internal once RoomDatabase is converted to Kotlin.
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public final class AutoCloser {
 
     @Nullable
     private SupportSQLiteOpenHelper mDelegateOpenHelper = null;
@@ -305,7 +310,7 @@
      *
      * @param onAutoClose the callback to run
      */
-    public void setAutoCloseCallback(Runnable onAutoClose) {
+    public void setAutoCloseCallback(@NonNull Runnable onAutoClose) {
         mOnAutoCloseCallback = onAutoClose;
     }
 }
diff --git a/room/room-runtime/src/main/java/androidx/room/InvalidationLiveDataContainer.java b/room/room-runtime/src/main/java/androidx/room/InvalidationLiveDataContainer.java
deleted file mode 100644
index e1e8155..0000000
--- a/room/room-runtime/src/main/java/androidx/room/InvalidationLiveDataContainer.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.room;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.lifecycle.LiveData;
-
-import java.util.Collections;
-import java.util.IdentityHashMap;
-import java.util.Set;
-import java.util.concurrent.Callable;
-
-/**
- * A helper class that maintains {@link RoomTrackingLiveData} instances for an
- * {@link InvalidationTracker}.
- * <p>
- * We keep a strong reference to active LiveData instances to avoid garbage collection in case
- * developer does not hold onto the returned LiveData.
- */
-class InvalidationLiveDataContainer {
-    @SuppressWarnings("WeakerAccess")
-    @VisibleForTesting
-    final Set<LiveData> mLiveDataSet = Collections.newSetFromMap(
-            new IdentityHashMap<LiveData, Boolean>()
-    );
-    private final RoomDatabase mDatabase;
-
-    InvalidationLiveDataContainer(RoomDatabase database) {
-        mDatabase = database;
-    }
-
-    <T> LiveData<T> create(String[] tableNames, boolean inTransaction,
-            Callable<T> computeFunction) {
-        return new RoomTrackingLiveData<>(mDatabase, this, inTransaction, computeFunction,
-                tableNames);
-    }
-
-    void onActive(LiveData liveData) {
-        mLiveDataSet.add(liveData);
-    }
-
-    void onInactive(LiveData liveData) {
-        mLiveDataSet.remove(liveData);
-    }
-}
diff --git a/room/room-runtime/src/main/java/androidx/room/InvalidationLiveDataContainer.kt b/room/room-runtime/src/main/java/androidx/room/InvalidationLiveDataContainer.kt
new file mode 100644
index 0000000..2a51d0c
--- /dev/null
+++ b/room/room-runtime/src/main/java/androidx/room/InvalidationLiveDataContainer.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room
+
+import androidx.lifecycle.LiveData
+import java.util.Collections
+import java.util.IdentityHashMap
+import java.util.concurrent.Callable
+
+/**
+ * A helper class that maintains [RoomTrackingLiveData] instances for an
+ * [InvalidationTracker].
+ *
+ * We keep a strong reference to active LiveData instances to avoid garbage collection in case
+ * developer does not hold onto the returned LiveData.
+ */
+internal class InvalidationLiveDataContainer(private val database: RoomDatabase) {
+    internal val liveDataSet: MutableSet<LiveData<*>> = Collections.newSetFromMap(IdentityHashMap())
+
+    fun <T> create(
+        tableNames: Array<String>,
+        inTransaction: Boolean,
+        computeFunction: Callable<T>
+    ): LiveData<T> {
+        return RoomTrackingLiveData(
+            database,
+            this,
+            inTransaction,
+            computeFunction,
+            tableNames
+        )
+    }
+
+    fun onActive(liveData: LiveData<*>) {
+        liveDataSet.add(liveData)
+    }
+
+    fun onInactive(liveData: LiveData<*>) {
+        liveDataSet.remove(liveData)
+    }
+}
diff --git a/room/room-runtime/src/main/java/androidx/room/InvalidationTracker.java b/room/room-runtime/src/main/java/androidx/room/InvalidationTracker.java
deleted file mode 100644
index 3a0c5ca..0000000
--- a/room/room-runtime/src/main/java/androidx/room/InvalidationTracker.java
+++ /dev/null
@@ -1,900 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.room;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteException;
-import android.os.Build;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.VisibleForTesting;
-import androidx.annotation.WorkerThread;
-import androidx.arch.core.internal.SafeIterableMap;
-import androidx.lifecycle.LiveData;
-import androidx.sqlite.db.SimpleSQLiteQuery;
-import androidx.sqlite.db.SupportSQLiteDatabase;
-import androidx.sqlite.db.SupportSQLiteStatement;
-
-import java.lang.ref.WeakReference;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.locks.Lock;
-
-/**
- * InvalidationTracker keeps a list of tables modified by queries and notifies its callbacks about
- * these tables.
- */
-// Some details on how the InvalidationTracker works:
-// * An in memory table is created with (table_id, invalidated) table_id is a hardcoded int from
-// initialization, while invalidated is a boolean bit to indicate if the table has been invalidated.
-// * ObservedTableTracker tracks list of tables we should be watching (e.g. adding triggers for).
-// * Before each beginTransaction, RoomDatabase invokes InvalidationTracker to sync trigger states.
-// * After each endTransaction, RoomDatabase invokes InvalidationTracker to refresh invalidated
-// tables.
-// * Each update (write operation) on one of the observed tables triggers an update into the
-// memory table table, flipping the invalidated flag ON.
-// * When multi-instance invalidation is turned on, MultiInstanceInvalidationClient will be created.
-// It works as an Observer, and notifies other instances of table invalidation.
-public class InvalidationTracker {
-
-    private static final String[] TRIGGERS = new String[]{"UPDATE", "DELETE", "INSERT"};
-
-    private static final String UPDATE_TABLE_NAME = "room_table_modification_log";
-
-    private static final String TABLE_ID_COLUMN_NAME = "table_id";
-
-    private static final String INVALIDATED_COLUMN_NAME = "invalidated";
-
-    private static final String CREATE_TRACKING_TABLE_SQL = "CREATE TEMP TABLE " + UPDATE_TABLE_NAME
-            + "(" + TABLE_ID_COLUMN_NAME + " INTEGER PRIMARY KEY, "
-            + INVALIDATED_COLUMN_NAME + " INTEGER NOT NULL DEFAULT 0)";
-
-    @VisibleForTesting
-    static final String RESET_UPDATED_TABLES_SQL = "UPDATE " + UPDATE_TABLE_NAME
-            + " SET " + INVALIDATED_COLUMN_NAME + " = 0 WHERE " + INVALIDATED_COLUMN_NAME + " = 1 ";
-
-    @VisibleForTesting
-    static final String SELECT_UPDATED_TABLES_SQL = "SELECT * FROM " + UPDATE_TABLE_NAME
-            + " WHERE " + INVALIDATED_COLUMN_NAME + " = 1;";
-
-    @NonNull
-    final HashMap<String, Integer> mTableIdLookup;
-    final String[] mTableNames;
-
-    @NonNull
-    private Map<String, Set<String>> mViewTables;
-
-    @Nullable
-    AutoCloser mAutoCloser = null;
-
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    final RoomDatabase mDatabase;
-
-    AtomicBoolean mPendingRefresh = new AtomicBoolean(false);
-
-    private volatile boolean mInitialized = false;
-
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    volatile SupportSQLiteStatement mCleanupStatement;
-
-    private final ObservedTableTracker mObservedTableTracker;
-
-    private final InvalidationLiveDataContainer mInvalidationLiveDataContainer;
-
-    // should be accessed with synchronization only.
-    @VisibleForTesting
-    @SuppressLint("RestrictedApi")
-    final SafeIterableMap<Observer, ObserverWrapper> mObserverMap = new SafeIterableMap<>();
-
-    private MultiInstanceInvalidationClient mMultiInstanceInvalidationClient;
-
-    private final Object mSyncTriggersLock = new Object();
-
-    /**
-     * Used by the generated code.
-     *
-     * @hide
-     */
-    @SuppressWarnings("WeakerAccess")
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
-    public InvalidationTracker(RoomDatabase database, String... tableNames) {
-        this(database, new HashMap<String, String>(), Collections.<String, Set<String>>emptyMap(),
-                tableNames);
-    }
-
-    /**
-     * Used by the generated code.
-     *
-     * @hide
-     */
-    @SuppressWarnings("WeakerAccess")
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
-    public InvalidationTracker(RoomDatabase database, Map<String, String> shadowTablesMap,
-            Map<String, Set<String>> viewTables, String... tableNames) {
-        mDatabase = database;
-        mObservedTableTracker = new ObservedTableTracker(tableNames.length);
-        mTableIdLookup = new HashMap<>();
-        mViewTables = viewTables;
-        mInvalidationLiveDataContainer = new InvalidationLiveDataContainer(mDatabase);
-        final int size = tableNames.length;
-        mTableNames = new String[size];
-        for (int id = 0; id < size; id++) {
-            final String tableName = tableNames[id].toLowerCase(Locale.US);
-            mTableIdLookup.put(tableName, id);
-            String shadowTableName = shadowTablesMap.get(tableNames[id]);
-            if (shadowTableName != null) {
-                mTableNames[id] = shadowTableName.toLowerCase(Locale.US);
-            } else {
-                mTableNames[id] = tableName;
-            }
-        }
-        // Adjust table id lookup for those tables whose shadow table is another already mapped
-        // table (e.g. external content fts tables).
-        for (Map.Entry<String, String> shadowTableEntry : shadowTablesMap.entrySet()) {
-            String shadowTableName = shadowTableEntry.getValue().toLowerCase(Locale.US);
-            if (mTableIdLookup.containsKey(shadowTableName)) {
-                String tableName = shadowTableEntry.getKey().toLowerCase(Locale.US);
-                mTableIdLookup.put(tableName, mTableIdLookup.get(shadowTableName));
-            }
-        }
-    }
-
-    /**
-     * Sets the auto closer for this invalidation tracker so that the invalidation tracker can
-     * ensure that the database is not closed if there are pending invalidations that haven't yet
-     * been flushed.
-     *
-     * This also adds a callback to the autocloser to ensure that the InvalidationTracker is in
-     * an ok state once the table is invalidated.
-     *
-     * This must be called before the database is used.
-     *
-     * @param autoCloser the autocloser associated with the db
-     */
-    void setAutoCloser(AutoCloser autoCloser) {
-        this.mAutoCloser = autoCloser;
-        mAutoCloser.setAutoCloseCallback(this::onAutoCloseCallback);
-    }
-
-    /**
-     * Internal method to initialize table tracking.
-     * <p>
-     * You should never call this method, it is called by the generated code.
-     */
-    void internalInit(SupportSQLiteDatabase database) {
-        synchronized (this) {
-            if (mInitialized) {
-                Log.e(Room.LOG_TAG, "Invalidation tracker is initialized twice :/.");
-                return;
-            }
-
-            // These actions are not in a transaction because temp_store is not allowed to be
-            // performed on a transaction, and recursive_triggers is not affected by transactions.
-            database.execSQL("PRAGMA temp_store = MEMORY;");
-            database.execSQL("PRAGMA recursive_triggers='ON';");
-            database.execSQL(CREATE_TRACKING_TABLE_SQL);
-            syncTriggers(database);
-            mCleanupStatement = database.compileStatement(RESET_UPDATED_TABLES_SQL);
-            mInitialized = true;
-        }
-    }
-
-    void onAutoCloseCallback() {
-        synchronized (this) {
-            mInitialized = false;
-            mObservedTableTracker.resetTriggerState();
-        }
-    }
-
-    void startMultiInstanceInvalidation(Context context, String name, Intent serviceIntent) {
-        mMultiInstanceInvalidationClient = new MultiInstanceInvalidationClient(context, name,
-                serviceIntent, this, mDatabase.getQueryExecutor());
-    }
-
-    void stopMultiInstanceInvalidation() {
-        if (mMultiInstanceInvalidationClient != null) {
-            mMultiInstanceInvalidationClient.stop();
-            mMultiInstanceInvalidationClient = null;
-        }
-    }
-
-    private static void appendTriggerName(StringBuilder builder, String tableName,
-            String triggerType) {
-        builder.append("`")
-                .append("room_table_modification_trigger_")
-                .append(tableName)
-                .append("_")
-                .append(triggerType)
-                .append("`");
-    }
-
-    private void stopTrackingTable(SupportSQLiteDatabase writableDb, int tableId) {
-        final String tableName = mTableNames[tableId];
-        StringBuilder stringBuilder = new StringBuilder();
-        for (String trigger : TRIGGERS) {
-            stringBuilder.setLength(0);
-            stringBuilder.append("DROP TRIGGER IF EXISTS ");
-            appendTriggerName(stringBuilder, tableName, trigger);
-            writableDb.execSQL(stringBuilder.toString());
-        }
-    }
-
-    private void startTrackingTable(SupportSQLiteDatabase writableDb, int tableId) {
-        writableDb.execSQL(
-                "INSERT OR IGNORE INTO " + UPDATE_TABLE_NAME + " VALUES(" + tableId + ", 0)");
-        final String tableName = mTableNames[tableId];
-        StringBuilder stringBuilder = new StringBuilder();
-        for (String trigger : TRIGGERS) {
-            stringBuilder.setLength(0);
-            stringBuilder.append("CREATE TEMP TRIGGER IF NOT EXISTS ");
-            appendTriggerName(stringBuilder, tableName, trigger);
-            stringBuilder.append(" AFTER ")
-                    .append(trigger)
-                    .append(" ON `")
-                    .append(tableName)
-                    .append("` BEGIN UPDATE ")
-                    .append(UPDATE_TABLE_NAME)
-                    .append(" SET ").append(INVALIDATED_COLUMN_NAME).append(" = 1")
-                    .append(" WHERE ").append(TABLE_ID_COLUMN_NAME).append(" = ").append(tableId)
-                    .append(" AND ").append(INVALIDATED_COLUMN_NAME).append(" = 0")
-                    .append("; END");
-            writableDb.execSQL(stringBuilder.toString());
-        }
-    }
-
-    /**
-     * Adds the given observer to the observers list and it will be notified if any table it
-     * observes changes.
-     * <p>
-     * Database changes are pulled on another thread so in some race conditions, the observer might
-     * be invoked for changes that were done before it is added.
-     * <p>
-     * If the observer already exists, this is a no-op call.
-     * <p>
-     * If one of the tables in the Observer does not exist in the database, this method throws an
-     * {@link IllegalArgumentException}.
-     * <p>
-     * This method should be called on a background/worker thread as it performs database
-     * operations.
-     *
-     * @param observer The observer which listens the database for changes.
-     */
-    @SuppressLint("RestrictedApi")
-    @WorkerThread
-    public void addObserver(@NonNull Observer observer) {
-        final String[] tableNames = resolveViews(observer.mTables);
-        int[] tableIds = new int[tableNames.length];
-        final int size = tableNames.length;
-
-        for (int i = 0; i < size; i++) {
-            Integer tableId = mTableIdLookup.get(tableNames[i].toLowerCase(Locale.US));
-            if (tableId == null) {
-                throw new IllegalArgumentException("There is no table with name " + tableNames[i]);
-            }
-            tableIds[i] = tableId;
-        }
-        ObserverWrapper wrapper = new ObserverWrapper(observer, tableIds, tableNames);
-        ObserverWrapper currentObserver;
-        synchronized (mObserverMap) {
-            currentObserver = mObserverMap.putIfAbsent(observer, wrapper);
-        }
-        if (currentObserver == null && mObservedTableTracker.onAdded(tableIds)) {
-            syncTriggers();
-        }
-    }
-
-    private String[] validateAndResolveTableNames(String[] tableNames) {
-        String[] resolved = resolveViews(tableNames);
-        for (String tableName : resolved) {
-            if (!mTableIdLookup.containsKey(tableName.toLowerCase(Locale.US))) {
-                throw new IllegalArgumentException("There is no table with name " + tableName);
-            }
-        }
-        return resolved;
-    }
-
-    /**
-     * Resolves the list of tables and views into a list of unique tables that are underlying them.
-     *
-     * @param names The names of tables or views.
-     * @return The names of the underlying tables.
-     */
-    private String[] resolveViews(String[] names) {
-        Set<String> tables = new HashSet<>();
-        for (String name : names) {
-            final String lowercase = name.toLowerCase(Locale.US);
-            if (mViewTables.containsKey(lowercase)) {
-                tables.addAll(mViewTables.get(lowercase));
-            } else {
-                tables.add(name);
-            }
-        }
-        return tables.toArray(new String[tables.size()]);
-    }
-
-    private static void beginTransactionInternal(SupportSQLiteDatabase database) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
-                && database.isWriteAheadLoggingEnabled()) {
-            database.beginTransactionNonExclusive();
-        } else {
-            database.beginTransaction();
-        }
-    }
-
-    /**
-     * Adds an observer but keeps a weak reference back to it.
-     * <p>
-     * Note that you cannot remove this observer once added. It will be automatically removed
-     * when the observer is GC'ed.
-     *
-     * @param observer The observer to which InvalidationTracker will keep a weak reference.
-     * @hide
-     */
-    @SuppressWarnings("unused")
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
-    public void addWeakObserver(Observer observer) {
-        addObserver(new WeakObserver(this, observer));
-    }
-
-    /**
-     * Removes the observer from the observers list.
-     * <p>
-     * This method should be called on a background/worker thread as it performs database
-     * operations.
-     *
-     * @param observer The observer to remove.
-     */
-    @SuppressLint("RestrictedApi")
-    @SuppressWarnings("WeakerAccess")
-    @WorkerThread
-    public void removeObserver(@NonNull final Observer observer) {
-        ObserverWrapper wrapper;
-        synchronized (mObserverMap) {
-            wrapper = mObserverMap.remove(observer);
-        }
-        if (wrapper != null && mObservedTableTracker.onRemoved(wrapper.mTableIds)) {
-            syncTriggers();
-        }
-    }
-
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
-    boolean ensureInitialization() {
-        if (!mDatabase.isOpen()) {
-            return false;
-        }
-        if (!mInitialized) {
-            // trigger initialization
-            mDatabase.getOpenHelper().getWritableDatabase();
-        }
-        if (!mInitialized) {
-            Log.e(Room.LOG_TAG, "database is not initialized even though it is open");
-            return false;
-        }
-        return true;
-    }
-
-    @VisibleForTesting
-    Runnable mRefreshRunnable = new Runnable() {
-        @Override
-        public void run() {
-            final Lock closeLock = mDatabase.getCloseLock();
-            Set<Integer> invalidatedTableIds = null;
-            closeLock.lock();
-            try {
-
-                if (!ensureInitialization()) {
-                    return;
-                }
-
-                if (!mPendingRefresh.compareAndSet(true, false)) {
-                    // no pending refresh
-                    return;
-                }
-
-                if (mDatabase.inTransaction()) {
-                    // current thread is in a transaction. when it ends, it will invoke
-                    // refreshRunnable again. mPendingRefresh is left as false on purpose
-                    // so that the last transaction can flip it on again.
-                    return;
-                }
-
-                // This transaction has to be on the underlying DB rather than the RoomDatabase
-                // in order to avoid a recursive loop after endTransaction.
-                SupportSQLiteDatabase db = mDatabase.getOpenHelper().getWritableDatabase();
-                db.beginTransactionNonExclusive();
-                try {
-                    invalidatedTableIds = checkUpdatedTable();
-                    db.setTransactionSuccessful();
-                } finally {
-                    db.endTransaction();
-                }
-            } catch (IllegalStateException | SQLiteException exception) {
-                // may happen if db is closed. just log.
-                Log.e(Room.LOG_TAG, "Cannot run invalidation tracker. Is the db closed?",
-                        exception);
-            } finally {
-                closeLock.unlock();
-
-                if (mAutoCloser != null) {
-                    mAutoCloser.decrementCountAndScheduleClose();
-                }
-            }
-            if (invalidatedTableIds != null && !invalidatedTableIds.isEmpty()) {
-                synchronized (mObserverMap) {
-                    for (Map.Entry<Observer, ObserverWrapper> entry : mObserverMap) {
-                        entry.getValue().notifyByTableInvalidStatus(invalidatedTableIds);
-                    }
-                }
-            }
-        }
-
-        private Set<Integer> checkUpdatedTable() {
-            HashSet<Integer> invalidatedTableIds = new HashSet<>();
-            Cursor cursor = mDatabase.query(new SimpleSQLiteQuery(SELECT_UPDATED_TABLES_SQL));
-            //noinspection TryFinallyCanBeTryWithResources
-            try {
-                while (cursor.moveToNext()) {
-                    final int tableId = cursor.getInt(0);
-                    invalidatedTableIds.add(tableId);
-                }
-            } finally {
-                cursor.close();
-            }
-            if (!invalidatedTableIds.isEmpty()) {
-                mCleanupStatement.executeUpdateDelete();
-            }
-            return invalidatedTableIds;
-        }
-    };
-
-    /**
-     * Enqueues a task to refresh the list of updated tables.
-     * <p>
-     * This method is automatically called when {@link RoomDatabase#endTransaction()} is called but
-     * if you have another connection to the database or directly use {@link
-     * SupportSQLiteDatabase}, you may need to call this manually.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public void refreshVersionsAsync() {
-        // TODO we should consider doing this sync instead of async.
-        if (mPendingRefresh.compareAndSet(false, true)) {
-            if (mAutoCloser != null) {
-                // refreshVersionsAsync is called with the ref count incremented from
-                // RoomDatabase, so the db can't be closed here, but we need to be sure that our
-                // db isn't closed until refresh is completed. This increment call must be
-                // matched with a corresponding call in mRefreshRunnable.
-                mAutoCloser.incrementCountAndEnsureDbIsOpen();
-            }
-            mDatabase.getQueryExecutor().execute(mRefreshRunnable);
-        }
-    }
-
-    /**
-     * Check versions for tables, and run observers synchronously if tables have been updated.
-     *
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
-    @WorkerThread
-    public void refreshVersionsSync() {
-        if (mAutoCloser != null) {
-            // This increment call must be matched with a corresponding call in mRefreshRunnable.
-            mAutoCloser.incrementCountAndEnsureDbIsOpen();
-        }
-        syncTriggers();
-        mRefreshRunnable.run();
-    }
-
-    /**
-     * Notifies all the registered {@link Observer}s of table changes.
-     * <p>
-     * This can be used for notifying invalidation that cannot be detected by this
-     * {@link InvalidationTracker}, for example, invalidation from another process.
-     *
-     * @param tables The invalidated tables.
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY)
-    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
-    public void notifyObserversByTableNames(String... tables) {
-        synchronized (mObserverMap) {
-            for (Map.Entry<Observer, ObserverWrapper> entry : mObserverMap) {
-                if (!entry.getKey().isRemote()) {
-                    entry.getValue().notifyByTableNames(tables);
-                }
-            }
-        }
-    }
-
-    void syncTriggers(SupportSQLiteDatabase database) {
-        if (database.inTransaction()) {
-            // we won't run this inside another transaction.
-            return;
-        }
-        try {
-            Lock closeLock = mDatabase.getCloseLock();
-            closeLock.lock();
-            try {
-                // Serialize adding and removing table trackers, this is specifically important
-                // to avoid missing invalidation before a transaction starts but there are
-                // pending (possibly concurrent) observer changes.
-                synchronized (mSyncTriggersLock) {
-                    final int[] tablesToSync = mObservedTableTracker.getTablesToSync();
-                    if (tablesToSync == null) {
-                        return;
-                    }
-                    final int limit = tablesToSync.length;
-                    beginTransactionInternal(database);
-                    try {
-                        for (int tableId = 0; tableId < limit; tableId++) {
-                            switch (tablesToSync[tableId]) {
-                                case ObservedTableTracker.ADD:
-                                    startTrackingTable(database, tableId);
-                                    break;
-                                case ObservedTableTracker.REMOVE:
-                                    stopTrackingTable(database, tableId);
-                                    break;
-                            }
-                        }
-                        database.setTransactionSuccessful();
-                    } finally {
-                        database.endTransaction();
-                    }
-                }
-            } finally {
-                closeLock.unlock();
-            }
-        } catch (IllegalStateException | SQLiteException exception) {
-            // may happen if db is closed. just log.
-            Log.e(Room.LOG_TAG, "Cannot run invalidation tracker. Is the db closed?",
-                    exception);
-        }
-    }
-
-    /**
-     * Called by RoomDatabase before each beginTransaction call.
-     * <p>
-     * It is important that pending trigger changes are applied to the database before any query
-     * runs. Otherwise, we may miss some changes.
-     * <p>
-     * This api should eventually be public.
-     */
-    void syncTriggers() {
-        if (!mDatabase.isOpen()) {
-            return;
-        }
-        syncTriggers(mDatabase.getOpenHelper().getWritableDatabase());
-    }
-
-    /**
-     * Creates a LiveData that computes the given function once and for every other invalidation
-     * of the database.
-     * <p>
-     * Holds a strong reference to the created LiveData as long as it is active.
-     *
-     * @deprecated Use {@link #createLiveData(String[], boolean, Callable)}
-     *
-     * @param computeFunction The function that calculates the value
-     * @param tableNames      The list of tables to observe
-     * @param <T>             The return type
-     * @return A new LiveData that computes the given function when the given list of tables
-     * invalidates.
-     * @hide
-     */
-    @Deprecated
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
-    public <T> LiveData<T> createLiveData(String[] tableNames, Callable<T> computeFunction) {
-        return createLiveData(tableNames, false, computeFunction);
-    }
-
-    /**
-     * Creates a LiveData that computes the given function once and for every other invalidation
-     * of the database.
-     * <p>
-     * Holds a strong reference to the created LiveData as long as it is active.
-     *
-     * @param tableNames      The list of tables to observe
-     * @param inTransaction   True if the computeFunction will be done in a transaction, false
-     *                        otherwise.
-     * @param computeFunction The function that calculates the value
-     * @param <T>             The return type
-     * @return A new LiveData that computes the given function when the given list of tables
-     * invalidates.
-     * @hide
-     */
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
-    public <T> LiveData<T> createLiveData(String[] tableNames, boolean inTransaction,
-            Callable<T> computeFunction) {
-        return mInvalidationLiveDataContainer.create(
-                validateAndResolveTableNames(tableNames), inTransaction, computeFunction);
-    }
-
-    /**
-     * Wraps an observer and keeps the table information.
-     * <p>
-     * Internally table ids are used which may change from database to database so the table
-     * related information is kept here rather than in the Observer.
-     */
-    @SuppressWarnings("WeakerAccess")
-    static class ObserverWrapper {
-        final int[] mTableIds;
-        private final String[] mTableNames;
-        final Observer mObserver;
-        private final Set<String> mSingleTableSet;
-
-        ObserverWrapper(Observer observer, int[] tableIds, String[] tableNames) {
-            mObserver = observer;
-            mTableIds = tableIds;
-            mTableNames = tableNames;
-            if (tableIds.length == 1) {
-                HashSet<String> set = new HashSet<>();
-                set.add(mTableNames[0]);
-                mSingleTableSet = Collections.unmodifiableSet(set);
-            } else {
-                mSingleTableSet = null;
-            }
-        }
-
-        /**
-         * Notifies the underlying {@link #mObserver} if any of the observed tables are invalidated
-         * based on the given invalid status set.
-         *
-         * @param invalidatedTablesIds The table ids of the tables that are invalidated.
-         */
-        void notifyByTableInvalidStatus(Set<Integer> invalidatedTablesIds) {
-            Set<String> invalidatedTables = null;
-            final int size = mTableIds.length;
-            for (int index = 0; index < size; index++) {
-                final int tableId = mTableIds[index];
-                if (invalidatedTablesIds.contains(tableId)) {
-                    if (size == 1) {
-                        // Optimization for a single-table observer
-                        invalidatedTables = mSingleTableSet;
-                    } else {
-                        if (invalidatedTables == null) {
-                            invalidatedTables = new HashSet<>(size);
-                        }
-                        invalidatedTables.add(mTableNames[index]);
-                    }
-                }
-            }
-            if (invalidatedTables != null) {
-                mObserver.onInvalidated(invalidatedTables);
-            }
-        }
-
-        /**
-         * Notifies the underlying {@link #mObserver} if it observes any of the specified
-         * {@code tables}.
-         *
-         * @param tables The invalidated table names.
-         */
-        void notifyByTableNames(String[] tables) {
-            Set<String> invalidatedTables = null;
-            if (mTableNames.length == 1) {
-                for (String table : tables) {
-                    if (table.equalsIgnoreCase(mTableNames[0])) {
-                        // Optimization for a single-table observer
-                        invalidatedTables = mSingleTableSet;
-                        break;
-                    }
-                }
-            } else {
-                HashSet<String> set = new HashSet<>();
-                for (String table : tables) {
-                    for (String ourTable : mTableNames) {
-                        if (ourTable.equalsIgnoreCase(table)) {
-                            set.add(ourTable);
-                            break;
-                        }
-                    }
-                }
-                if (set.size() > 0) {
-                    invalidatedTables = set;
-                }
-            }
-            if (invalidatedTables != null) {
-                mObserver.onInvalidated(invalidatedTables);
-            }
-        }
-    }
-
-    /**
-     * An observer that can listen for changes in the database.
-     */
-    public abstract static class Observer {
-        final String[] mTables;
-
-        /**
-         * Observes the given list of tables and views.
-         *
-         * @param firstTable The name of the table or view.
-         * @param rest       More names of tables or views.
-         */
-        @SuppressWarnings("unused")
-        protected Observer(@NonNull String firstTable, String... rest) {
-            mTables = Arrays.copyOf(rest, rest.length + 1);
-            mTables[rest.length] = firstTable;
-        }
-
-        /**
-         * Observes the given list of tables and views.
-         *
-         * @param tables The list of tables or views to observe for changes.
-         */
-        public Observer(@NonNull String[] tables) {
-            // copy tables in case user modifies them afterwards
-            mTables = Arrays.copyOf(tables, tables.length);
-        }
-
-        /**
-         * Called when one of the observed tables is invalidated in the database.
-         *
-         * @param tables A set of invalidated tables. This is useful when the observer targets
-         *               multiple tables and you want to know which table is invalidated. This will
-         *               be names of underlying tables when you are observing views.
-         */
-        public abstract void onInvalidated(@NonNull Set<String> tables);
-
-        boolean isRemote() {
-            return false;
-        }
-    }
-
-    /**
-     * Keeps a list of tables we should observe. Invalidation tracker lazily syncs this list w/
-     * triggers in the database.
-     * <p>
-     * This class is thread safe
-     */
-    static class ObservedTableTracker {
-        static final int NO_OP = 0; // don't change trigger state for this table
-        static final int ADD = 1; // add triggers for this table
-        static final int REMOVE = 2; // remove triggers for this table
-
-        // number of observers per table
-        final long[] mTableObservers;
-        // trigger state for each table at last sync
-        // this field is updated when syncAndGet is called.
-        final boolean[] mTriggerStates;
-        // when sync is called, this field is returned. It includes actions as ADD, REMOVE, NO_OP
-        final int[] mTriggerStateChanges;
-
-        boolean mNeedsSync;
-
-        ObservedTableTracker(int tableCount) {
-            mTableObservers = new long[tableCount];
-            mTriggerStates = new boolean[tableCount];
-            mTriggerStateChanges = new int[tableCount];
-            Arrays.fill(mTableObservers, 0);
-            Arrays.fill(mTriggerStates, false);
-        }
-
-        /**
-         * @return true if # of triggers is affected.
-         */
-        boolean onAdded(int... tableIds) {
-            boolean needTriggerSync = false;
-            synchronized (this) {
-                for (int tableId : tableIds) {
-                    final long prevObserverCount = mTableObservers[tableId];
-                    mTableObservers[tableId] = prevObserverCount + 1;
-                    if (prevObserverCount == 0) {
-                        mNeedsSync = true;
-                        needTriggerSync = true;
-                    }
-                }
-            }
-            return needTriggerSync;
-        }
-
-        /**
-         * @return true if # of triggers is affected.
-         */
-        boolean onRemoved(int... tableIds) {
-            boolean needTriggerSync = false;
-            synchronized (this) {
-                for (int tableId : tableIds) {
-                    final long prevObserverCount = mTableObservers[tableId];
-                    mTableObservers[tableId] = prevObserverCount - 1;
-                    if (prevObserverCount == 1) {
-                        mNeedsSync = true;
-                        needTriggerSync = true;
-                    }
-                }
-            }
-            return needTriggerSync;
-        }
-
-        /**
-         * If we are re-opening the db we'll need to add all the triggers that we need so change
-         * the current state to false for all.
-         */
-        void resetTriggerState() {
-            synchronized (this) {
-                Arrays.fill(mTriggerStates, false);
-                mNeedsSync = true;
-            }
-        }
-
-        /**
-         * If this returns non-null there are no pending sync operations.
-         *
-         * @return int[] An int array where the index for each tableId has the action for that
-         * table.
-         */
-        @Nullable
-        int[] getTablesToSync() {
-            synchronized (this) {
-                if (!mNeedsSync) {
-                    return null;
-                }
-                final int tableCount = mTableObservers.length;
-                for (int i = 0; i < tableCount; i++) {
-                    final boolean newState = mTableObservers[i] > 0;
-                    if (newState != mTriggerStates[i]) {
-                        mTriggerStateChanges[i] = newState ? ADD : REMOVE;
-                    } else {
-                        mTriggerStateChanges[i] = NO_OP;
-                    }
-                    mTriggerStates[i] = newState;
-                }
-                mNeedsSync = false;
-                return mTriggerStateChanges.clone();
-            }
-        }
-    }
-
-    /**
-     * An Observer wrapper that keeps a weak reference to the given object.
-     * <p>
-     * This class will automatically unsubscribe when the wrapped observer goes out of memory.
-     */
-    static class WeakObserver extends Observer {
-        final InvalidationTracker mTracker;
-        final WeakReference<Observer> mDelegateRef;
-
-        WeakObserver(InvalidationTracker tracker, Observer delegate) {
-            super(delegate.mTables);
-            mTracker = tracker;
-            mDelegateRef = new WeakReference<>(delegate);
-        }
-
-        @Override
-        public void onInvalidated(@NonNull Set<String> tables) {
-            final Observer observer = mDelegateRef.get();
-            if (observer == null) {
-                mTracker.removeObserver(this);
-            } else {
-                observer.onInvalidated(tables);
-            }
-        }
-    }
-}
diff --git a/room/room-runtime/src/main/java/androidx/room/InvalidationTracker.kt b/room/room-runtime/src/main/java/androidx/room/InvalidationTracker.kt
new file mode 100644
index 0000000..7352a17
--- /dev/null
+++ b/room/room-runtime/src/main/java/androidx/room/InvalidationTracker.kt
@@ -0,0 +1,878 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.room
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.content.Intent
+import android.database.sqlite.SQLiteException
+import android.os.Build
+import android.util.Log
+import androidx.annotation.GuardedBy
+import androidx.annotation.RestrictTo
+import androidx.annotation.VisibleForTesting
+import androidx.annotation.WorkerThread
+import androidx.arch.core.internal.SafeIterableMap
+import androidx.lifecycle.LiveData
+import androidx.room.Room.Companion.LOG_TAG
+import androidx.room.util.useCursor
+import androidx.sqlite.db.SimpleSQLiteQuery
+import androidx.sqlite.db.SupportSQLiteDatabase
+import androidx.sqlite.db.SupportSQLiteStatement
+import java.lang.ref.WeakReference
+import java.util.Arrays
+import java.util.Locale
+import java.util.concurrent.Callable
+import java.util.concurrent.atomic.AtomicBoolean
+
+/**
+ * InvalidationTracker keeps a list of tables modified by queries and notifies its callbacks about
+ * these tables.
+ */
+// Some details on how the InvalidationTracker works:
+// * An in memory table is created with (table_id, invalidated) table_id is a hardcoded int from
+// initialization, while invalidated is a boolean bit to indicate if the table has been invalidated.
+// * ObservedTableTracker tracks list of tables we should be watching (e.g. adding triggers for).
+// * Before each beginTransaction, RoomDatabase invokes InvalidationTracker to sync trigger states.
+// * After each endTransaction, RoomDatabase invokes InvalidationTracker to refresh invalidated
+// tables.
+// * Each update (write operation) on one of the observed tables triggers an update into the
+// memory table table, flipping the invalidated flag ON.
+// * When multi-instance invalidation is turned on, MultiInstanceInvalidationClient will be created.
+// It works as an Observer, and notifies other instances of table invalidation.
+
+open class InvalidationTracker @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) constructor(
+    @get:RestrictTo(RestrictTo.Scope.LIBRARY)
+    @field:RestrictTo(RestrictTo.Scope.LIBRARY)
+    protected val database: RoomDatabase,
+    private val shadowTablesMap: Map<String, String>,
+    private val viewTables: Map<String, @JvmSuppressWildcards Set<String>>,
+    vararg tableNames: String
+) {
+    @JvmField
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    val tableIdLookup: Map<String, Int>
+
+    @JvmField
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    val tablesNames: Array<String>
+
+    private var autoCloser: AutoCloser? = null
+
+    @JvmField
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    val pendingRefresh = AtomicBoolean(false)
+
+    @Volatile
+    private var initialized = false
+
+    @Volatile
+    @get:RestrictTo(RestrictTo.Scope.LIBRARY)
+    @set:RestrictTo(RestrictTo.Scope.LIBRARY)
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    var cleanupStatement: SupportSQLiteStatement? = null
+
+    private val observedTableTracker: ObservedTableTracker = ObservedTableTracker(tableNames.size)
+
+    private val invalidationLiveDataContainer: InvalidationLiveDataContainer =
+        InvalidationLiveDataContainer(database)
+
+    @GuardedBy("observerMap")
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @JvmField
+    protected val observerMap = SafeIterableMap<Observer, ObserverWrapper>()
+
+    private var multiInstanceInvalidationClient: MultiInstanceInvalidationClient? = null
+
+    private val syncTriggersLock = Any()
+
+    private val trackerLock = Any()
+
+    init {
+        tableIdLookup = mutableMapOf()
+        tablesNames = Array(tableNames.size) { id ->
+            val tableName = tableNames[id].lowercase(Locale.US)
+            tableIdLookup[tableName] = id
+            val shadowTableName = shadowTablesMap[tableNames[id]]?.lowercase(Locale.US)
+            shadowTableName ?: tableName
+        }
+
+        // Adjust table id lookup for those tables whose shadow table is another already mapped
+        // table (e.g. external content fts tables).
+        shadowTablesMap.forEach { entry ->
+            val shadowTableName = entry.value.lowercase(Locale.US)
+            if (tableIdLookup.containsKey(shadowTableName)) {
+                val tableName = entry.key.lowercase(Locale.US)
+                tableIdLookup[tableName] = tableIdLookup.getValue(shadowTableName)
+            }
+        }
+    }
+
+    /**
+     * Used by the generated code.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+    constructor(database: RoomDatabase, vararg tableNames: String) :
+        this(
+            database = database,
+            shadowTablesMap = emptyMap(),
+            viewTables = emptyMap(),
+            tableNames = tableNames
+        )
+
+    /**
+     * Sets the auto closer for this invalidation tracker so that the invalidation tracker can
+     * ensure that the database is not closed if there are pending invalidations that haven't yet
+     * been flushed.
+     *
+     * This also adds a callback to the autocloser to ensure that the InvalidationTracker is in
+     * an ok state once the table is invalidated.
+     *
+     * This must be called before the database is used.
+     *
+     * @param autoCloser the autocloser associated with the db
+     */
+    // TODO (b/218894771): Make internal when RoomDatabase is converted to Kotlin
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    fun setAutoCloser(autoCloser: AutoCloser) {
+        this.autoCloser = autoCloser
+        autoCloser.setAutoCloseCallback(::onAutoCloseCallback)
+    }
+
+    /**
+     * Internal method to initialize table tracking.
+     *
+     * You should never call this method, it is called by the generated code.
+     */
+    // TODO (b/218894771): Make internal when RoomDatabase is converted to Kotlin
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    fun internalInit(database: SupportSQLiteDatabase) {
+        synchronized(trackerLock) {
+            if (initialized) {
+                Log.e(LOG_TAG, "Invalidation tracker is initialized twice :/.")
+                return
+            }
+
+            // These actions are not in a transaction because temp_store is not allowed to be
+            // performed on a transaction, and recursive_triggers is not affected by transactions.
+            database.execSQL("PRAGMA temp_store = MEMORY;")
+            database.execSQL("PRAGMA recursive_triggers='ON';")
+            database.execSQL(CREATE_TRACKING_TABLE_SQL)
+            syncTriggers(database)
+            cleanupStatement = database.compileStatement(RESET_UPDATED_TABLES_SQL)
+            initialized = true
+        }
+    }
+
+    // TODO: Close CleanupStatement
+    internal fun onAutoCloseCallback() {
+        synchronized(trackerLock) {
+            initialized = false
+            observedTableTracker.resetTriggerState()
+        }
+    }
+
+    // TODO (b/218894771): Make internal when RoomDatabase is converted to Kotlin
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    fun startMultiInstanceInvalidation(context: Context, name: String, serviceIntent: Intent) {
+        multiInstanceInvalidationClient = MultiInstanceInvalidationClient(
+            context = context,
+            name = name,
+            serviceIntent = serviceIntent,
+            invalidationTracker = this,
+            executor = database.queryExecutor
+        )
+    }
+
+    // TODO (b/218894771): Make internal when RoomDatabase is converted to Kotlin
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    fun stopMultiInstanceInvalidation() {
+        multiInstanceInvalidationClient?.stop()
+        multiInstanceInvalidationClient = null
+    }
+
+    private fun stopTrackingTable(db: SupportSQLiteDatabase, tableId: Int) {
+        val tableName = tablesNames[tableId]
+        for (trigger in TRIGGERS) {
+            val sql = buildString {
+                append("DROP TRIGGER IF EXISTS ")
+                append(getTriggerName(tableName, trigger))
+            }
+            db.execSQL(sql)
+        }
+    }
+
+    private fun startTrackingTable(db: SupportSQLiteDatabase, tableId: Int) {
+        db.execSQL(
+            "INSERT OR IGNORE INTO $UPDATE_TABLE_NAME VALUES($tableId, 0)"
+        )
+        val tableName = tablesNames[tableId]
+        for (trigger in TRIGGERS) {
+            val sql = buildString {
+                append("CREATE TEMP TRIGGER IF NOT EXISTS ")
+                append(getTriggerName(tableName, trigger))
+                append(" AFTER ")
+                append(trigger)
+                append(" ON `")
+                append(tableName)
+                append("` BEGIN UPDATE ")
+                append(UPDATE_TABLE_NAME)
+                append(" SET ").append(INVALIDATED_COLUMN_NAME)
+                append(" = 1")
+                append(" WHERE ").append(TABLE_ID_COLUMN_NAME)
+                append(" = ").append(tableId)
+                append(" AND ").append(INVALIDATED_COLUMN_NAME)
+                append(" = 0")
+                append("; END")
+            }
+            db.execSQL(sql)
+        }
+    }
+
+    /**
+     * Adds the given observer to the observers list and it will be notified if any table it
+     * observes changes.
+     *
+     * Database changes are pulled on another thread so in some race conditions, the observer might
+     * be invoked for changes that were done before it is added.
+     *
+     * If the observer already exists, this is a no-op call.
+     *
+     * If one of the tables in the Observer does not exist in the database, this method throws an
+     * [IllegalArgumentException].
+     *
+     * This method should be called on a background/worker thread as it performs database
+     * operations.
+     *
+     * @param observer The observer which listens the database for changes.
+     */
+    @SuppressLint("RestrictedApi")
+    @WorkerThread
+    open fun addObserver(observer: Observer) {
+        val tableNames = resolveViews(observer.tables)
+        val tableIds = tableNames.map { tableName ->
+            tableIdLookup[tableName.lowercase(Locale.US)]
+                ?: throw IllegalArgumentException("There is no table with name $tableName")
+        }.toIntArray()
+
+        val wrapper = ObserverWrapper(
+            observer = observer,
+            tableIds = tableIds,
+            tableNames = tableNames
+        )
+
+        val currentObserver = synchronized(observerMap) {
+            observerMap.putIfAbsent(observer, wrapper)
+        }
+        if (currentObserver == null && observedTableTracker.onAdded(*tableIds)) {
+            syncTriggers()
+        }
+    }
+
+    private fun validateAndResolveTableNames(tableNames: Array<String>): Array<String> {
+        val resolved = resolveViews(tableNames)
+        resolved.forEach { tableName ->
+            require(tableIdLookup.containsKey(tableName.lowercase(Locale.US))) {
+                "There is no table with name $tableName"
+            }
+        }
+        return resolved
+    }
+
+    /**
+     * Resolves the list of tables and views into a list of unique tables that are underlying them.
+     *
+     * @param names The names of tables or views.
+     * @return The names of the underlying tables.
+     */
+    private fun resolveViews(names: Array<String>): Array<String> {
+        return buildSet {
+            names.forEach { name ->
+                if (viewTables.containsKey(name.lowercase(Locale.US))) {
+                    addAll(viewTables[name.lowercase(Locale.US)]!!)
+                } else {
+                    add(name)
+                }
+            }
+        }.toTypedArray()
+    }
+
+    /**
+     * Adds an observer but keeps a weak reference back to it.
+     *
+     * Note that you cannot remove this observer once added. It will be automatically removed
+     * when the observer is GC'ed.
+     *
+     * @param observer The observer to which InvalidationTracker will keep a weak reference.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+    open fun addWeakObserver(observer: Observer) {
+        addObserver(WeakObserver(this, observer))
+    }
+
+    /**
+     * Removes the observer from the observers list.
+     *
+     * This method should be called on a background/worker thread as it performs database
+     * operations.
+     *
+     * @param observer The observer to remove.
+     */
+    @SuppressLint("RestrictedApi")
+    @WorkerThread
+    open fun removeObserver(observer: Observer) {
+        val wrapper = synchronized(observerMap) {
+            observerMap.remove(observer)
+        }
+        if (wrapper != null && observedTableTracker.onRemoved(tableIds = wrapper.tableIds)) {
+            syncTriggers()
+        }
+    }
+
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    protected fun ensureInitialization(): Boolean {
+        if (!database.isOpen) {
+            return false
+        }
+        if (!initialized) {
+            // trigger initialization
+            database.openHelper.writableDatabase
+        }
+        if (!initialized) {
+            Log.e(LOG_TAG, "database is not initialized even though it is open")
+            return false
+        }
+        return true
+    }
+
+    @VisibleForTesting
+    @JvmField
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    val refreshRunnable: Runnable = object : Runnable {
+        override fun run() {
+            val closeLock = database.closeLock
+            closeLock.lock()
+            val invalidatedTableIds: Set<Int> =
+                try {
+                    if (!ensureInitialization()) {
+                        return
+                    }
+                    if (!pendingRefresh.compareAndSet(true, false)) {
+                        // no pending refresh
+                        return
+                    }
+                    if (database.inTransaction()) {
+                        // current thread is in a transaction. when it ends, it will invoke
+                        // refreshRunnable again. pendingRefresh is left as false on purpose
+                        // so that the last transaction can flip it on again.
+                        return
+                    }
+
+                    // This transaction has to be on the underlying DB rather than the RoomDatabase
+                    // in order to avoid a recursive loop after endTransaction.
+                    val db = database.openHelper.writableDatabase
+                    db.beginTransactionNonExclusive()
+                    val invalidatedTableIds: Set<Int>
+                    try {
+                        invalidatedTableIds = checkUpdatedTable()
+                        db.setTransactionSuccessful()
+                    } finally {
+                        db.endTransaction()
+                    }
+                    invalidatedTableIds
+                } catch (ex: IllegalStateException) {
+                    // may happen if db is closed. just log.
+                    Log.e(
+                        LOG_TAG, "Cannot run invalidation tracker. Is the db closed?",
+                        ex
+                    )
+                    emptySet()
+                } catch (ex: SQLiteException) {
+                    Log.e(
+                        LOG_TAG, "Cannot run invalidation tracker. Is the db closed?",
+                        ex
+                    )
+                    emptySet()
+                } finally {
+                    closeLock.unlock()
+                    autoCloser?.decrementCountAndScheduleClose()
+                }
+
+            if (invalidatedTableIds.isNotEmpty()) {
+                synchronized(observerMap) {
+                    observerMap.forEach {
+                        it.value.notifyByTableInvalidStatus(invalidatedTableIds)
+                    }
+                }
+            }
+        }
+
+        private fun checkUpdatedTable(): Set<Int> {
+            val invalidatedTableIds = buildSet {
+                database.query(SimpleSQLiteQuery(SELECT_UPDATED_TABLES_SQL)).useCursor { cursor ->
+                    while (cursor.moveToNext()) {
+                        add(cursor.getInt(0))
+                    }
+                }
+            }
+            if (invalidatedTableIds.isNotEmpty()) {
+                checkNotNull(cleanupStatement)
+                val statement = cleanupStatement
+                requireNotNull(statement)
+                statement.executeUpdateDelete()
+            }
+            return invalidatedTableIds
+        }
+    }
+
+    /**
+     * Enqueues a task to refresh the list of updated tables.
+     *
+     * This method is automatically called when [RoomDatabase.endTransaction] is called but
+     * if you have another connection to the database or directly use [ ], you may need to call this
+     * manually.
+     */
+    open fun refreshVersionsAsync() {
+        // TODO we should consider doing this sync instead of async.
+        if (pendingRefresh.compareAndSet(false, true)) {
+            // refreshVersionsAsync is called with the ref count incremented from
+            // RoomDatabase, so the db can't be closed here, but we need to be sure that our
+            // db isn't closed until refresh is completed. This increment call must be
+            // matched with a corresponding call in refreshRunnable.
+            autoCloser?.incrementCountAndEnsureDbIsOpen()
+            database.queryExecutor.execute(refreshRunnable)
+        }
+    }
+
+    /**
+     * Check versions for tables, and run observers synchronously if tables have been updated.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+    @WorkerThread
+    open fun refreshVersionsSync() {
+        // This increment call must be matched with a corresponding call in refreshRunnable.
+        autoCloser?.incrementCountAndEnsureDbIsOpen()
+        syncTriggers()
+        refreshRunnable.run()
+    }
+
+    /**
+     * Notifies all the registered [Observer]s of table changes.
+     *
+     * This can be used for notifying invalidation that cannot be detected by this
+     * [InvalidationTracker], for example, invalidation from another process.
+     *
+     * @param tables The invalidated tables.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+    fun notifyObserversByTableNames(vararg tables: String) {
+        synchronized(observerMap) {
+            observerMap.forEach { (observer, wrapper) ->
+                if (!observer.isRemote) {
+                    wrapper.notifyByTableNames(tables)
+                }
+            }
+        }
+    }
+
+    // TODO (b/218894771): Make internal when RoomDatabase is converted to Kotlin
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    protected fun syncTriggers(database: SupportSQLiteDatabase) {
+        if (database.inTransaction()) {
+            // we won't run this inside another transaction.
+            return
+        }
+        try {
+            val closeLock = this.database.closeLock
+            closeLock.lock()
+            try {
+                // Serialize adding and removing table trackers, this is specifically important
+                // to avoid missing invalidation before a transaction starts but there are
+                // pending (possibly concurrent) observer changes.
+                synchronized(syncTriggersLock) {
+                    val tablesToSync = observedTableTracker.getTablesToSync() ?: return
+                    beginTransactionInternal(database)
+                    try {
+                        tablesToSync.forEachIndexed { tableId, syncState ->
+                            when (syncState) {
+                                ObservedTableTracker.ADD ->
+                                    startTrackingTable(database, tableId)
+                                ObservedTableTracker.REMOVE ->
+                                    stopTrackingTable(database, tableId)
+                            }
+                        }
+                        database.setTransactionSuccessful()
+                    } finally {
+                        database.endTransaction()
+                    }
+                }
+            } finally {
+                closeLock.unlock()
+            }
+        } catch (ex: IllegalStateException) {
+            // may happen if db is closed. just log.
+            Log.e(LOG_TAG, "Cannot run invalidation tracker. Is the db closed?", ex)
+        } catch (ex: SQLiteException) {
+            Log.e(LOG_TAG, "Cannot run invalidation tracker. Is the db closed?", ex)
+        }
+    }
+
+    /**
+     * Called by RoomDatabase before each beginTransaction call.
+     *
+     * It is important that pending trigger changes are applied to the database before any query
+     * runs. Otherwise, we may miss some changes.
+     *
+     * This api should eventually be public.
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    fun syncTriggers() {
+        if (!database.isOpen) {
+            return
+        }
+        syncTriggers(database.openHelper.writableDatabase)
+    }
+
+    /**
+     * Creates a LiveData that computes the given function once and for every other invalidation
+     * of the database.
+     *
+     * Holds a strong reference to the created LiveData as long as it is active.
+     *
+     * @param computeFunction The function that calculates the value
+     * @param tableNames      The list of tables to observe
+     * @param T             The return type
+     * @return A new LiveData that computes the given function when the given list of tables
+     * invalidates.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+    @Deprecated("Use [createLiveData(String[], boolean, Callable)]")
+    open fun <T> createLiveData(
+        tableNames: Array<String>,
+        computeFunction: Callable<T>
+    ): LiveData<T> {
+        return createLiveData(tableNames, false, computeFunction)
+    }
+
+    /**
+     * Creates a LiveData that computes the given function once and for every other invalidation
+     * of the database.
+     *
+     * Holds a strong reference to the created LiveData as long as it is active.
+     *
+     * @param tableNames      The list of tables to observe
+     * @param inTransaction   True if the computeFunction will be done in a transaction, false
+     * otherwise.
+     * @param computeFunction The function that calculates the value
+     * @param T             The return type
+     * @return A new LiveData that computes the given function when the given list of tables
+     * invalidates.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+    open fun <T> createLiveData(
+        tableNames: Array<String>,
+        inTransaction: Boolean,
+        computeFunction: Callable<T>
+    ): LiveData<T> {
+        return invalidationLiveDataContainer.create(
+            validateAndResolveTableNames(tableNames), inTransaction, computeFunction
+        )
+    }
+
+    /**
+     * Wraps an observer and keeps the table information.
+     *
+     * Internally table ids are used which may change from database to database so the table
+     * related information is kept here rather than in the Observer.
+     *
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    protected class ObserverWrapper(
+        @get:RestrictTo(RestrictTo.Scope.LIBRARY)
+        val observer: Observer,
+        @get:RestrictTo(RestrictTo.Scope.LIBRARY)
+        val tableIds: IntArray,
+        private val tableNames: Array<String>
+    ) {
+        private val singleTableSet = if (tableNames.isNotEmpty()) {
+            setOf(tableNames[0])
+        } else {
+            emptySet()
+        }
+
+        init {
+            check(tableIds.size == tableNames.size)
+        }
+
+        /**
+         * Notifies the underlying [.mObserver] if any of the observed tables are invalidated
+         * based on the given invalid status set.
+         *
+         * @param invalidatedTablesIds The table ids of the tables that are invalidated.
+         */
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        fun notifyByTableInvalidStatus(invalidatedTablesIds: Set<Int?>) {
+            val invalidatedTables = when (tableIds.size) {
+                0 -> emptySet()
+                1 -> if (invalidatedTablesIds.contains(tableIds[0])) {
+                    singleTableSet // Optimization for a single-table observer
+                } else {
+                    emptySet()
+                }
+                else -> buildSet {
+                    tableIds.forEachIndexed { idx, tableId ->
+                        if (invalidatedTablesIds.contains(tableId)) {
+                            add(tableNames[idx])
+                        }
+                    }
+                }
+            }
+
+            if (invalidatedTables.isNotEmpty()) {
+                observer.onInvalidated(invalidatedTables)
+            }
+        }
+
+        /**
+         * Notifies the underlying [.mObserver] if it observes any of the specified
+         * `tables`.
+         *
+         * @param tables The invalidated table names.
+         */
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        fun notifyByTableNames(tables: Array<out String>) {
+            val invalidatedTables = when (tableNames.size) {
+                0 -> emptySet()
+                1 -> if (tables.any { it.equals(tableNames[0], ignoreCase = true) }) {
+                    singleTableSet // Optimization for a single-table observer
+                } else {
+                    emptySet()
+                }
+                else -> buildSet {
+                    tables.forEach { table ->
+                        tableNames.forEach ourTablesLoop@{ ourTable ->
+                            if (ourTable.equals(table, ignoreCase = true)) {
+                                add(ourTable)
+                                return@ourTablesLoop
+                            }
+                        }
+                    }
+                }
+            }
+            if (invalidatedTables.isNotEmpty()) {
+                observer.onInvalidated(invalidatedTables)
+            }
+        }
+    }
+
+    /**
+     * An observer that can listen for changes in the database.
+     */
+    abstract class Observer(@get:RestrictTo(RestrictTo.Scope.LIBRARY) val tables: Array<String>) {
+        /**
+         * Observes the given list of tables and views.
+         *
+         * @param firstTable The name of the table or view.
+         * @param rest       More names of tables or views.
+         */
+        protected constructor(firstTable: String, vararg rest: String) : this(
+            buildList {
+                addAll(rest)
+                add(firstTable)
+            }.toTypedArray()
+        )
+
+        /**
+         * Called when one of the observed tables is invalidated in the database.
+         *
+         * @param tables A set of invalidated tables. This is useful when the observer targets
+         * multiple tables and you want to know which table is invalidated. This will
+         * be names of underlying tables when you are observing views.
+         */
+        abstract fun onInvalidated(tables: Set<String>)
+
+        @get:RestrictTo(RestrictTo.Scope.LIBRARY)
+        open val isRemote: Boolean
+            get() = false
+    }
+
+    /**
+     * Keeps a list of tables we should observe. Invalidation tracker lazily syncs this list w/
+     * triggers in the database.
+     *
+     * This class is thread safe
+     */
+    internal class ObservedTableTracker(tableCount: Int) {
+        // number of observers per table
+        val tableObservers = LongArray(tableCount)
+
+        // trigger state for each table at last sync
+        // this field is updated when syncAndGet is called.
+        private val triggerStates = BooleanArray(tableCount)
+
+        // when sync is called, this field is returned. It includes actions as ADD, REMOVE, NO_OP
+        private val triggerStateChanges = IntArray(tableCount)
+
+        var needsSync = false
+
+        /**
+         * @return true if # of triggers is affected.
+         */
+        fun onAdded(vararg tableIds: Int): Boolean {
+            var needTriggerSync = false
+            synchronized(this) {
+                tableIds.forEach { tableId ->
+                    val prevObserverCount = tableObservers[tableId]
+                    tableObservers[tableId] = prevObserverCount + 1
+                    if (prevObserverCount == 0L) {
+                        needsSync = true
+                        needTriggerSync = true
+                    }
+                }
+            }
+            return needTriggerSync
+        }
+
+        /**
+         * @return true if # of triggers is affected.
+         */
+        fun onRemoved(vararg tableIds: Int): Boolean {
+            var needTriggerSync = false
+            synchronized(this) {
+                tableIds.forEach { tableId ->
+                    val prevObserverCount = tableObservers[tableId]
+                    tableObservers[tableId] = prevObserverCount - 1
+                    if (prevObserverCount == 1L) {
+                        needsSync = true
+                        needTriggerSync = true
+                    }
+                }
+            }
+            return needTriggerSync
+        }
+
+        /**
+         * If we are re-opening the db we'll need to add all the triggers that we need so change
+         * the current state to false for all.
+         */
+        fun resetTriggerState() {
+            synchronized(this) {
+                Arrays.fill(triggerStates, false)
+                needsSync = true
+            }
+        }
+
+        /**
+         * If this returns non-null, you must call onSyncCompleted.
+         *
+         * @return int[] An int array where the index for each tableId has the action for that
+         * table.
+         */
+        @VisibleForTesting
+        @JvmName("getTablesToSync")
+        fun getTablesToSync(): IntArray? {
+            synchronized(this) {
+                if (!needsSync) {
+                    return null
+                }
+                tableObservers.forEachIndexed { i, observerCount ->
+                    val newState = observerCount > 0
+                    if (newState != triggerStates[i]) {
+                        triggerStateChanges[i] = if (newState) ADD else REMOVE
+                    } else {
+                        triggerStateChanges[i] = NO_OP
+                    }
+                    triggerStates[i] = newState
+                }
+                needsSync = false
+                return triggerStateChanges.clone()
+            }
+        }
+
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        companion object {
+            const val NO_OP = 0 // don't change trigger state for this table
+            const val ADD = 1 // add triggers for this table
+            const val REMOVE = 2 // remove triggers for this table
+        }
+    }
+
+    /**
+     * An Observer wrapper that keeps a weak reference to the given object.
+     *
+     * This class will automatically unsubscribe when the wrapped observer goes out of memory.
+     */
+    internal class WeakObserver(
+        val tracker: InvalidationTracker,
+        delegate: Observer
+    ) : Observer(delegate.tables) {
+        val delegateRef: WeakReference<Observer> = WeakReference(delegate)
+        override fun onInvalidated(tables: Set<String>) {
+            val observer = delegateRef.get()
+            if (observer == null) {
+                tracker.removeObserver(this)
+            } else {
+                observer.onInvalidated(tables)
+            }
+        }
+    }
+
+    companion object {
+        private val TRIGGERS = arrayOf("UPDATE", "DELETE", "INSERT")
+        private const val UPDATE_TABLE_NAME = "room_table_modification_log"
+        private const val TABLE_ID_COLUMN_NAME = "table_id"
+        private const val INVALIDATED_COLUMN_NAME = "invalidated"
+        private const val CREATE_TRACKING_TABLE_SQL =
+            "CREATE TEMP TABLE $UPDATE_TABLE_NAME ($TABLE_ID_COLUMN_NAME INTEGER PRIMARY KEY, " +
+                "$INVALIDATED_COLUMN_NAME INTEGER NOT NULL DEFAULT 0)"
+
+        @VisibleForTesting
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        const val RESET_UPDATED_TABLES_SQL =
+            "UPDATE $UPDATE_TABLE_NAME SET $INVALIDATED_COLUMN_NAME = 0 " +
+                "WHERE $INVALIDATED_COLUMN_NAME = 1"
+
+        @VisibleForTesting
+        @RestrictTo(RestrictTo.Scope.LIBRARY)
+        const val SELECT_UPDATED_TABLES_SQL =
+            "SELECT * FROM $UPDATE_TABLE_NAME WHERE $INVALIDATED_COLUMN_NAME = 1;"
+
+        internal fun getTriggerName(
+            tableName: String,
+            triggerType: String
+        ) = "`room_table_modification_trigger_${tableName}_$triggerType`"
+
+        internal fun beginTransactionInternal(database: SupportSQLiteDatabase) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN &&
+                database.isWriteAheadLoggingEnabled
+            ) {
+                database.beginTransactionNonExclusive()
+            } else {
+                database.beginTransaction()
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/room/room-runtime/src/main/java/androidx/room/MultiInstanceInvalidationClient.java b/room/room-runtime/src/main/java/androidx/room/MultiInstanceInvalidationClient.java
deleted file mode 100644
index cd847acc..0000000
--- a/room/room-runtime/src/main/java/androidx/room/MultiInstanceInvalidationClient.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.room;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import java.util.Set;
-import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * Handles all the communication from {@link RoomDatabase} and {@link InvalidationTracker} to
- * {@link MultiInstanceInvalidationService}.
- */
-class MultiInstanceInvalidationClient {
-
-    /**
-     * The application context.
-     */
-    // synthetic access
-    @SuppressWarnings("WeakerAccess")
-    final Context mAppContext;
-
-    /**
-     * The name of the database file.
-     */
-    // synthetic access
-    @SuppressWarnings("WeakerAccess")
-    final String mName;
-
-    /**
-     * The client ID assigned by {@link MultiInstanceInvalidationService}.
-     */
-    // synthetic access
-    @SuppressWarnings("WeakerAccess")
-    int mClientId;
-
-    // synthetic access
-    @SuppressWarnings("WeakerAccess")
-    final InvalidationTracker mInvalidationTracker;
-
-    // synthetic access
-    @SuppressWarnings("WeakerAccess")
-    final InvalidationTracker.Observer mObserver;
-
-    // synthetic access
-    @SuppressWarnings("WeakerAccess")
-    @Nullable
-    IMultiInstanceInvalidationService mService;
-
-    // synthetic access
-    @SuppressWarnings("WeakerAccess")
-    final Executor mExecutor;
-
-    // synthetic access
-    @SuppressWarnings("WeakerAccess")
-    final IMultiInstanceInvalidationCallback mCallback =
-            new IMultiInstanceInvalidationCallback.Stub() {
-                @Override
-                public void onInvalidation(final String[] tables) {
-                    mExecutor.execute(new Runnable() {
-                        @Override
-                        public void run() {
-                            mInvalidationTracker.notifyObserversByTableNames(tables);
-                        }
-                    });
-                }
-            };
-
-    // synthetic access
-    @SuppressWarnings("WeakerAccess")
-    final AtomicBoolean mStopped = new AtomicBoolean(false);
-
-    // synthetic access
-    @SuppressWarnings("WeakerAccess")
-    final ServiceConnection mServiceConnection = new ServiceConnection() {
-
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            mService = IMultiInstanceInvalidationService.Stub.asInterface(service);
-            mExecutor.execute(mSetUpRunnable);
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            mExecutor.execute(mRemoveObserverRunnable);
-            mService = null;
-        }
-
-    };
-
-    // synthetic access
-    @SuppressWarnings("WeakerAccess")
-    final Runnable mSetUpRunnable = new Runnable() {
-        @Override
-        public void run() {
-            try {
-                final IMultiInstanceInvalidationService service = mService;
-                if (service != null) {
-                    mClientId = service.registerCallback(mCallback, mName);
-                    mInvalidationTracker.addObserver(mObserver);
-                }
-            } catch (RemoteException e) {
-                Log.w(Room.LOG_TAG, "Cannot register multi-instance invalidation callback", e);
-            }
-        }
-    };
-
-    // synthetic access
-    @SuppressWarnings("WeakerAccess")
-    final Runnable mRemoveObserverRunnable = new Runnable() {
-        @Override
-        public void run() {
-            mInvalidationTracker.removeObserver(mObserver);
-        }
-    };
-
-    /**
-     * @param context             The Context to be used for binding
-     *                            {@link IMultiInstanceInvalidationService}.
-     * @param name                The name of the database file.
-     * @param serviceIntent       The {@link Intent} used for binding
-     *                            {@link IMultiInstanceInvalidationService}.
-     * @param invalidationTracker The {@link InvalidationTracker}
-     * @param executor            The background executor.
-     */
-    MultiInstanceInvalidationClient(Context context, String name, Intent serviceIntent,
-            InvalidationTracker invalidationTracker, Executor executor) {
-        mAppContext = context.getApplicationContext();
-        mName = name;
-        mInvalidationTracker = invalidationTracker;
-        mExecutor = executor;
-        // Use all tables names for observer.
-        final Set<String> tableNames = invalidationTracker.mTableIdLookup.keySet();
-        mObserver = new InvalidationTracker.Observer(tableNames.toArray(new String[0])) {
-            @Override
-            public void onInvalidated(@NonNull Set<String> tables) {
-                if (mStopped.get()) {
-                    return;
-                }
-                try {
-                    final IMultiInstanceInvalidationService service = mService;
-                    if (service != null) {
-                        service.broadcastInvalidation(mClientId, tables.toArray(new String[0]));
-                    }
-                } catch (RemoteException e) {
-                    Log.w(Room.LOG_TAG, "Cannot broadcast invalidation", e);
-                }
-            }
-
-            @Override
-            boolean isRemote() {
-                return true;
-            }
-        };
-        mAppContext.bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
-    }
-
-    void stop() {
-        if (mStopped.compareAndSet(false, true)) {
-            mInvalidationTracker.removeObserver(mObserver);
-            try {
-                final IMultiInstanceInvalidationService service = mService;
-                if (service != null) {
-                    service.unregisterCallback(mCallback, mClientId);
-                }
-            } catch (RemoteException e) {
-                Log.w(Room.LOG_TAG, "Cannot unregister multi-instance invalidation callback", e);
-            }
-            mAppContext.unbindService(mServiceConnection);
-        }
-    }
-}
diff --git a/room/room-runtime/src/main/java/androidx/room/MultiInstanceInvalidationClient.kt b/room/room-runtime/src/main/java/androidx/room/MultiInstanceInvalidationClient.kt
new file mode 100644
index 0000000..bd52304
--- /dev/null
+++ b/room/room-runtime/src/main/java/androidx/room/MultiInstanceInvalidationClient.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.room
+
+import android.content.Intent
+import android.content.ServiceConnection
+import android.content.ComponentName
+import android.content.Context
+import android.os.IBinder
+import android.os.RemoteException
+import android.util.Log
+import androidx.room.Room.Companion.LOG_TAG
+import java.util.concurrent.Executor
+import java.util.concurrent.atomic.AtomicBoolean
+
+/**
+ * Handles all the communication from [RoomDatabase] and [InvalidationTracker] to
+ * [MultiInstanceInvalidationService].
+ *
+ * @param context             The Context to be used for binding
+ * [IMultiInstanceInvalidationService].
+ * @param name                The name of the database file.
+ * @param serviceIntent       The [Intent] used for binding
+ * [IMultiInstanceInvalidationService].
+ * @param invalidationTracker The [InvalidationTracker]
+ * @param executor            The background executor.
+ */
+internal class MultiInstanceInvalidationClient(
+    context: Context,
+    val name: String,
+    serviceIntent: Intent,
+    val invalidationTracker: InvalidationTracker,
+    val executor: Executor
+) {
+    private val appContext = context.applicationContext
+
+    /**
+     * The client ID assigned by [MultiInstanceInvalidationService].
+     */
+    var clientId = 0
+    lateinit var observer: InvalidationTracker.Observer
+    var service: IMultiInstanceInvalidationService? = null
+
+    val callback: IMultiInstanceInvalidationCallback =
+        object : IMultiInstanceInvalidationCallback.Stub() {
+            override fun onInvalidation(tables: Array<String>) {
+                executor.execute { invalidationTracker.notifyObserversByTableNames(*tables) }
+            }
+        }
+
+    val stopped = AtomicBoolean(false)
+
+    val serviceConnection: ServiceConnection = object : ServiceConnection {
+        override fun onServiceConnected(name: ComponentName, service: IBinder) {
+            this@MultiInstanceInvalidationClient.service =
+                IMultiInstanceInvalidationService.Stub.asInterface(service)
+            executor.execute(setUpRunnable)
+        }
+
+        override fun onServiceDisconnected(name: ComponentName) {
+            executor.execute(removeObserverRunnable)
+            service = null
+        }
+    }
+
+    val setUpRunnable = Runnable {
+        try {
+            service?.let {
+                clientId = it.registerCallback(callback, name)
+                invalidationTracker.addObserver(observer)
+            }
+        } catch (e: RemoteException) {
+            Log.w(LOG_TAG, "Cannot register multi-instance invalidation callback", e)
+        }
+    }
+
+    val removeObserverRunnable = Runnable { invalidationTracker.removeObserver(observer) }
+
+    init {
+        // Use all tables names for observer.
+        val tableNames: Set<String> = invalidationTracker.tableIdLookup.keys
+        observer = object : InvalidationTracker.Observer(tableNames.toTypedArray()) {
+            override fun onInvalidated(tables: Set<String>) {
+                if (stopped.get()) {
+                    return
+                }
+
+                try {
+                    service?.broadcastInvalidation(clientId, tables.toTypedArray())
+                } catch (e: RemoteException) {
+                    Log.w(LOG_TAG, "Cannot broadcast invalidation", e)
+                }
+            }
+
+            override val isRemote: Boolean
+                get() = true
+        }
+        appContext.bindService(
+            serviceIntent,
+            serviceConnection,
+            Context.BIND_AUTO_CREATE
+        )
+    }
+
+    fun stop() {
+        if (stopped.compareAndSet(false, true)) {
+            invalidationTracker.removeObserver(observer)
+            try {
+                service?.unregisterCallback(callback, clientId)
+            } catch (e: RemoteException) {
+                Log.w(LOG_TAG, "Cannot unregister multi-instance invalidation callback", e)
+            }
+            appContext.unbindService(serviceConnection)
+        }
+    }
+}
\ No newline at end of file
diff --git a/room/room-runtime/src/main/java/androidx/room/MultiInstanceInvalidationService.java b/room/room-runtime/src/main/java/androidx/room/MultiInstanceInvalidationService.java
deleted file mode 100644
index 0c82f93..0000000
--- a/room/room-runtime/src/main/java/androidx/room/MultiInstanceInvalidationService.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.room;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import java.util.HashMap;
-
-/**
- * A {@link Service} for remote invalidation among multiple {@link InvalidationTracker} instances.
- * This service runs in the main app process. All the instances of {@link InvalidationTracker}
- * (potentially in other processes) has to connect to this service.
- *
- * <p>The intent to launch it can be specified by
- * {@link RoomDatabase.Builder#setMultiInstanceInvalidationServiceIntent}, although the service is
- * defined in the manifest by default so there should be no need to override it in a normal
- * situation.
- */
-@ExperimentalRoomApi
-public class MultiInstanceInvalidationService extends Service {
-
-    // synthetic access
-    @SuppressWarnings("WeakerAccess")
-    int mMaxClientId = 0;
-
-    // synthetic access
-    @SuppressWarnings("WeakerAccess")
-    final HashMap<Integer, String> mClientNames = new HashMap<>();
-
-    // synthetic access
-    @SuppressWarnings("WeakerAccess")
-    final RemoteCallbackList<IMultiInstanceInvalidationCallback> mCallbackList =
-            new RemoteCallbackList<IMultiInstanceInvalidationCallback>() {
-                @Override
-                public void onCallbackDied(IMultiInstanceInvalidationCallback callback,
-                        Object cookie) {
-                    mClientNames.remove((int) cookie);
-                }
-            };
-
-    private final IMultiInstanceInvalidationService.Stub mBinder =
-            new IMultiInstanceInvalidationService.Stub() {
-
-                // Assigns a client ID to the client.
-                @Override
-                public int registerCallback(IMultiInstanceInvalidationCallback callback,
-                        String name) {
-                    if (name == null) {
-                        return 0;
-                    }
-                    synchronized (mCallbackList) {
-                        int clientId = ++mMaxClientId;
-                        // Use the client ID as the RemoteCallbackList cookie.
-                        if (mCallbackList.register(callback, clientId)) {
-                            mClientNames.put(clientId, name);
-                            return clientId;
-                        } else {
-                            --mMaxClientId;
-                            return 0;
-                        }
-                    }
-                }
-
-                // Explicitly removes the client.
-                // The client can die without calling this. In that case, mCallbackList
-                // .onCallbackDied() can take care of removal.
-                @Override
-                public void unregisterCallback(IMultiInstanceInvalidationCallback callback,
-                        int clientId) {
-                    synchronized (mCallbackList) {
-                        mCallbackList.unregister(callback);
-                        mClientNames.remove(clientId);
-                    }
-                }
-
-                // Broadcasts table invalidation to other instances of the same database file.
-                // The broadcast is not sent to the caller itself.
-                @Override
-                public void broadcastInvalidation(int clientId, String[] tables) {
-                    synchronized (mCallbackList) {
-                        String name = mClientNames.get(clientId);
-                        if (name == null) {
-                            Log.w(Room.LOG_TAG, "Remote invalidation client ID not registered");
-                            return;
-                        }
-                        int count = mCallbackList.beginBroadcast();
-                        try {
-                            for (int i = 0; i < count; i++) {
-                                int targetClientId = (int) mCallbackList.getBroadcastCookie(i);
-                                String targetName = mClientNames.get(targetClientId);
-                                if (clientId == targetClientId // This is the caller itself.
-                                        || !name.equals(targetName)) { // Not the same file.
-                                    continue;
-                                }
-                                try {
-                                    IMultiInstanceInvalidationCallback callback =
-                                            mCallbackList.getBroadcastItem(i);
-                                    callback.onInvalidation(tables);
-                                } catch (RemoteException e) {
-                                    Log.w(Room.LOG_TAG, "Error invoking a remote callback", e);
-                                }
-                            }
-                        } finally {
-                            mCallbackList.finishBroadcast();
-                        }
-                    }
-                }
-            };
-
-    @Nullable
-    @Override
-    public IBinder onBind(@NonNull Intent intent) {
-        return mBinder;
-    }
-}
diff --git a/room/room-runtime/src/main/java/androidx/room/MultiInstanceInvalidationService.kt b/room/room-runtime/src/main/java/androidx/room/MultiInstanceInvalidationService.kt
new file mode 100644
index 0000000..9b8e836
--- /dev/null
+++ b/room/room-runtime/src/main/java/androidx/room/MultiInstanceInvalidationService.kt
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.room
+
+import android.app.Service
+import android.os.RemoteCallbackList
+import android.content.Intent
+import android.os.IBinder
+import android.os.RemoteException
+import android.util.Log
+import androidx.room.Room.Companion.LOG_TAG
+
+/**
+ * A [Service] for remote invalidation among multiple [InvalidationTracker] instances.
+ * This service runs in the main app process. All the instances of [InvalidationTracker]
+ * (potentially in other processes) has to connect to this service.
+ *
+ * The intent to launch it can be specified by
+ * [RoomDatabase.Builder.setMultiInstanceInvalidationServiceIntent], although the service is
+ * defined in the manifest by default so there should be no need to override it in a normal
+ * situation.
+ */
+@ExperimentalRoomApi
+class MultiInstanceInvalidationService : Service() {
+    internal var maxClientId = 0
+    internal val clientNames = mutableMapOf<Int, String>()
+
+    internal val callbackList: RemoteCallbackList<IMultiInstanceInvalidationCallback> =
+        object : RemoteCallbackList<IMultiInstanceInvalidationCallback>() {
+            override fun onCallbackDied(
+                callback: IMultiInstanceInvalidationCallback,
+                cookie: Any
+            ) {
+                clientNames.remove(cookie as Int)
+            }
+        }
+
+    private val binder: IMultiInstanceInvalidationService.Stub =
+        object : IMultiInstanceInvalidationService.Stub() {
+            // Assigns a client ID to the client.
+            override fun registerCallback(
+                callback: IMultiInstanceInvalidationCallback,
+                name: String?
+            ): Int {
+                if (name == null) {
+                    return 0
+                }
+                synchronized(callbackList) {
+                    val clientId = ++maxClientId
+                    // Use the client ID as the RemoteCallbackList cookie.
+                    return if (callbackList.register(callback, clientId)) {
+                        clientNames[clientId] = name
+                        clientId
+                    } else {
+                        --maxClientId
+                        0
+                    }
+                }
+            }
+
+            // Explicitly removes the client.
+            // The client can die without calling this. In that case, callbackList
+            // .onCallbackDied() can take care of removal.
+            override fun unregisterCallback(
+                callback: IMultiInstanceInvalidationCallback,
+                clientId: Int
+            ) {
+                synchronized(callbackList) {
+                    callbackList.unregister(callback)
+                    clientNames.remove(clientId)
+                }
+            }
+
+            // Broadcasts table invalidation to other instances of the same database file.
+            // The broadcast is not sent to the caller itself.
+            override fun broadcastInvalidation(clientId: Int, tables: Array<String>) {
+                synchronized(callbackList) {
+                    val name = clientNames[clientId]
+                    if (name == null) {
+                        Log.w(LOG_TAG, "Remote invalidation client ID not registered")
+                        return
+                    }
+                    val count = callbackList.beginBroadcast()
+                    try {
+                        for (i in 0 until count) {
+                            val targetClientId = callbackList.getBroadcastCookie(i) as Int
+                            val targetName = clientNames[targetClientId]
+                            if (clientId == targetClientId || name != targetName) {
+                                // Skip if this is the caller itself or broadcast is for another
+                                // database.
+                                continue
+                            }
+                            try {
+                                callbackList.getBroadcastItem(i).onInvalidation(tables)
+                            } catch (e: RemoteException) {
+                                Log.w(LOG_TAG, "Error invoking a remote callback", e)
+                            }
+                        }
+                    } finally {
+                        callbackList.finishBroadcast()
+                    }
+                }
+            }
+        }
+
+    override fun onBind(intent: Intent): IBinder {
+        return binder
+    }
+}
\ No newline at end of file
diff --git a/room/room-runtime/src/main/java/androidx/room/Room.java b/room/room-runtime/src/main/java/androidx/room/Room.java
deleted file mode 100644
index d252099..0000000
--- a/room/room-runtime/src/main/java/androidx/room/Room.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.room;
-
-import android.content.Context;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-
-/**
- * Utility class for Room.
- */
-@SuppressWarnings("unused")
-public class Room {
-    static final String LOG_TAG = "ROOM";
-    /**
-     * The master table where room keeps its metadata information.
-     */
-    public static final String MASTER_TABLE_NAME = RoomMasterTable.TABLE_NAME;
-    private static final String CURSOR_CONV_SUFFIX = "_CursorConverter";
-
-    /**
-     * Creates a RoomDatabase.Builder for a persistent database. Once a database is built, you
-     * should keep a reference to it and re-use it.
-     *
-     * @param context The context for the database. This is usually the Application context.
-     * @param klass   The abstract class which is annotated with {@link Database} and extends
-     *                {@link RoomDatabase}.
-     * @param name    The name of the database file.
-     * @param <T>     The type of the database class.
-     * @return A {@code RoomDatabaseBuilder<T>} which you can use to create the database.
-     */
-    @SuppressWarnings("WeakerAccess")
-    @NonNull
-    public static <T extends RoomDatabase> RoomDatabase.Builder<T> databaseBuilder(
-            @NonNull Context context, @NonNull Class<T> klass, @NonNull String name) {
-        //noinspection ConstantConditions
-        if (name == null || name.trim().length() == 0) {
-            throw new IllegalArgumentException("Cannot build a database with null or empty name."
-                    + " If you are trying to create an in memory database, use Room"
-                    + ".inMemoryDatabaseBuilder");
-        }
-        return new RoomDatabase.Builder<>(context, klass, name);
-    }
-
-    /**
-     * Creates a RoomDatabase.Builder for an in memory database. Information stored in an in memory
-     * database disappears when the process is killed.
-     * Once a database is built, you should keep a reference to it and re-use it.
-     *
-     * @param context The context for the database. This is usually the Application context.
-     * @param klass   The abstract class which is annotated with {@link Database} and extends
-     *                {@link RoomDatabase}.
-     * @param <T>     The type of the database class.
-     * @return A {@code RoomDatabaseBuilder<T>} which you can use to create the database.
-     */
-    @NonNull
-    public static <T extends RoomDatabase> RoomDatabase.Builder<T> inMemoryDatabaseBuilder(
-            @NonNull Context context, @NonNull Class<T> klass) {
-        return new RoomDatabase.Builder<>(context, klass, null);
-    }
-
-    @SuppressWarnings({"TypeParameterUnusedInFormals", "ClassNewInstance"})
-    @NonNull
-    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    public static <T, C> T getGeneratedImplementation(@NonNull Class<C> klass,
-            @NonNull String suffix) {
-        final String fullPackage = klass.getPackage().getName();
-        String name = klass.getCanonicalName();
-        final String postPackageName = fullPackage.isEmpty()
-                ? name
-                : name.substring(fullPackage.length() + 1);
-        final String implName = postPackageName.replace('.', '_') + suffix;
-        //noinspection TryWithIdenticalCatches
-        try {
-
-            final String fullClassName = fullPackage.isEmpty()
-                    ? implName
-                    : fullPackage + "." + implName;
-            @SuppressWarnings("unchecked")
-            final Class<T> aClass = (Class<T>) Class.forName(
-                    fullClassName, true, klass.getClassLoader());
-            return aClass.newInstance();
-        } catch (ClassNotFoundException e) {
-            throw new RuntimeException("cannot find implementation for "
-                    + klass.getCanonicalName() + ". " + implName + " does not exist");
-        } catch (IllegalAccessException e) {
-            throw new RuntimeException("Cannot access the constructor"
-                    + klass.getCanonicalName());
-        } catch (InstantiationException e) {
-            throw new RuntimeException("Failed to create an instance of "
-                    + klass.getCanonicalName());
-        }
-    }
-
-    /** @deprecated This type should not be instantiated as it contains only static methods. */
-    @Deprecated
-    @SuppressWarnings("PrivateConstructorForUtilityClass")
-    public Room() {
-    }
-}
diff --git a/room/room-runtime/src/main/java/androidx/room/Room.kt b/room/room-runtime/src/main/java/androidx/room/Room.kt
new file mode 100644
index 0000000..dd72c02
--- /dev/null
+++ b/room/room-runtime/src/main/java/androidx/room/Room.kt
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.room
+
+import android.content.Context
+import androidx.annotation.RestrictTo
+
+/**
+ * Utility functions for Room.
+ */
+// TODO (b/225972678) remove class and make top-level functions in Room 3.0.
+open class Room {
+    /** This type should not be instantiated as it contains only static methods. */
+    @Deprecated("This type should not be instantiated as it contains only static methods. ")
+    @SuppressWarnings("PrivateConstructorForUtilityClass")
+    constructor()
+
+    companion object {
+        internal const val LOG_TAG = "ROOM"
+
+        /**
+         * The master table where room keeps its metadata information.
+         */
+        const val MASTER_TABLE_NAME = RoomMasterTable.TABLE_NAME
+        private const val CURSOR_CONV_SUFFIX = "_CursorConverter"
+
+        @Suppress("UNCHECKED_CAST")
+        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+        @JvmStatic
+        fun <T, C> getGeneratedImplementation(
+            klass: Class<C>,
+            suffix: String
+        ): T {
+            val fullPackage = klass.getPackage()!!.name
+            val name: String = klass.canonicalName!!
+            val postPackageName =
+                if (fullPackage.isEmpty()) name else name.substring(fullPackage.length + 1)
+            val implName = postPackageName.replace('.', '_') + suffix
+            return try {
+                val fullClassName = if (fullPackage.isEmpty()) {
+                    implName
+                } else {
+                    "$fullPackage.$implName"
+                }
+                val aClass = Class.forName(
+                    fullClassName, true, klass.classLoader
+                ) as Class<T>
+                aClass.newInstance()
+            } catch (e: ClassNotFoundException) {
+                throw RuntimeException(
+                    "Cannot find implementation for ${klass.canonicalName}. $implName does not " +
+                        "exist"
+                )
+            } catch (e: IllegalAccessException) {
+                throw RuntimeException(
+                    "Cannot access the constructor $klass.canonicalName"
+                )
+            } catch (e: InstantiationException) {
+                throw RuntimeException(
+                    "Failed to create an instance of $klass.canonicalName"
+                )
+            }
+        }
+
+        /**
+         * Creates a RoomDatabase.Builder for an in memory database. Information stored in an in memory
+         * database disappears when the process is killed.
+         * Once a database is built, you should keep a reference to it and re-use it.
+         *
+         * @param context The context for the database. This is usually the Application context.
+         * @param klass   The abstract class which is annotated with [Database] and extends
+         * [RoomDatabase].
+         * @param T     The type of the database class.
+         * @return A `RoomDatabaseBuilder<T>` which you can use to create the database.
+         */
+        @JvmStatic
+        fun <T : RoomDatabase> inMemoryDatabaseBuilder(
+            context: Context,
+            klass: Class<T>
+        ): RoomDatabase.Builder<T> {
+            return RoomDatabase.Builder(context, klass, null)
+        }
+
+        /**
+         * Creates a RoomDatabase.Builder for a persistent database. Once a database is built, you
+         * should keep a reference to it and re-use it.
+         *
+         * @param context The context for the database. This is usually the Application context.
+         * @param klass   The abstract class which is annotated with [Database] and extends
+         * [RoomDatabase].
+         * @param name    The name of the database file.
+         * @param T     The type of the database class.
+         * @return A `RoomDatabaseBuilder<T>` which you can use to create the database.
+         */
+        @JvmStatic
+        fun <T : RoomDatabase> databaseBuilder(
+            context: Context,
+            klass: Class<T>,
+            name: String?
+        ): RoomDatabase.Builder<T> {
+            require(!name.isNullOrBlank()) {
+                "Cannot build a database with null or empty name." +
+                    " If you are trying to create an in memory database, use Room" +
+                    ".inMemoryDatabaseBuilder"
+            }
+            return RoomDatabase.Builder(context, klass, name)
+        }
+    }
+}
\ No newline at end of file
diff --git a/room/room-runtime/src/main/java/androidx/room/RoomTrackingLiveData.java b/room/room-runtime/src/main/java/androidx/room/RoomTrackingLiveData.java
deleted file mode 100644
index 8df1014..0000000
--- a/room/room-runtime/src/main/java/androidx/room/RoomTrackingLiveData.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.room;
-
-
-import android.annotation.SuppressLint;
-
-import androidx.annotation.MainThread;
-import androidx.annotation.NonNull;
-import androidx.annotation.WorkerThread;
-import androidx.arch.core.executor.ArchTaskExecutor;
-import androidx.lifecycle.LiveData;
-
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * A LiveData implementation that closely works with {@link InvalidationTracker} to implement
- * database drive {@link androidx.lifecycle.LiveData} queries that are strongly hold as long
- * as they are active.
- * <p>
- * We need this extra handling for {@link androidx.lifecycle.LiveData} because when they are
- * observed forever, there is no {@link androidx.lifecycle.Lifecycle} that will keep them in
- * memory but they should stay. We cannot add-remove observer in {@link LiveData#onActive()},
- * {@link LiveData#onInactive()} because that would mean missing changes in between or doing an
- * extra query on every UI rotation.
- * <p>
- * This {@link LiveData} keeps a weak observer to the {@link InvalidationTracker} but it is hold
- * strongly by the {@link InvalidationTracker} as long as it is active.
- */
-class RoomTrackingLiveData<T> extends LiveData<T> {
-    @SuppressWarnings("WeakerAccess")
-    final RoomDatabase mDatabase;
-
-    @SuppressWarnings("WeakerAccess")
-    final boolean mInTransaction;
-
-    @SuppressWarnings("WeakerAccess")
-    final Callable<T> mComputeFunction;
-
-    private final InvalidationLiveDataContainer mContainer;
-
-    @SuppressWarnings("WeakerAccess")
-    final InvalidationTracker.Observer mObserver;
-
-    @SuppressWarnings("WeakerAccess")
-    final AtomicBoolean mInvalid = new AtomicBoolean(true);
-
-    @SuppressWarnings("WeakerAccess")
-    final AtomicBoolean mComputing = new AtomicBoolean(false);
-
-    @SuppressWarnings("WeakerAccess")
-    final AtomicBoolean mRegisteredObserver = new AtomicBoolean(false);
-
-    @SuppressWarnings("WeakerAccess")
-    final Runnable mRefreshRunnable = new Runnable() {
-        @WorkerThread
-        @Override
-        public void run() {
-            if (mRegisteredObserver.compareAndSet(false, true)) {
-                mDatabase.getInvalidationTracker().addWeakObserver(mObserver);
-            }
-            boolean computed;
-            do {
-                computed = false;
-                // compute can happen only in 1 thread but no reason to lock others.
-                if (mComputing.compareAndSet(false, true)) {
-                    // as long as it is invalid, keep computing.
-                    try {
-                        T value = null;
-                        while (mInvalid.compareAndSet(true, false)) {
-                            computed = true;
-                            try {
-                                value = mComputeFunction.call();
-                            } catch (Exception e) {
-                                throw new RuntimeException("Exception while computing database"
-                                        + " live data.", e);
-                            }
-                        }
-                        if (computed) {
-                            postValue(value);
-                        }
-                    } finally {
-                        // release compute lock
-                        mComputing.set(false);
-                    }
-                }
-                // check invalid after releasing compute lock to avoid the following scenario.
-                // Thread A runs compute()
-                // Thread A checks invalid, it is false
-                // Main thread sets invalid to true
-                // Thread B runs, fails to acquire compute lock and skips
-                // Thread A releases compute lock
-                // We've left invalid in set state. The check below recovers.
-            } while (computed && mInvalid.get());
-        }
-    };
-
-    @SuppressWarnings("WeakerAccess")
-    final Runnable mInvalidationRunnable = new Runnable() {
-        @MainThread
-        @Override
-        public void run() {
-            boolean isActive = hasActiveObservers();
-            if (mInvalid.compareAndSet(false, true)) {
-                if (isActive) {
-                    getQueryExecutor().execute(mRefreshRunnable);
-                }
-            }
-        }
-    };
-    @SuppressLint("RestrictedApi")
-    RoomTrackingLiveData(
-            RoomDatabase database,
-            InvalidationLiveDataContainer container,
-            boolean inTransaction,
-            Callable<T> computeFunction,
-            String[] tableNames) {
-        mDatabase = database;
-        mInTransaction = inTransaction;
-        mComputeFunction = computeFunction;
-        mContainer = container;
-        mObserver = new InvalidationTracker.Observer(tableNames) {
-            @Override
-            public void onInvalidated(@NonNull Set<String> tables) {
-                ArchTaskExecutor.getInstance().executeOnMainThread(mInvalidationRunnable);
-            }
-        };
-    }
-
-    @Override
-    protected void onActive() {
-        super.onActive();
-        mContainer.onActive(this);
-        getQueryExecutor().execute(mRefreshRunnable);
-    }
-
-    @Override
-    protected void onInactive() {
-        super.onInactive();
-        mContainer.onInactive(this);
-    }
-
-    Executor getQueryExecutor() {
-        if (mInTransaction) {
-            return mDatabase.getTransactionExecutor();
-        } else {
-            return mDatabase.getQueryExecutor();
-        }
-    }
-}
diff --git a/room/room-runtime/src/main/java/androidx/room/RoomTrackingLiveData.kt b/room/room-runtime/src/main/java/androidx/room/RoomTrackingLiveData.kt
new file mode 100644
index 0000000..a2785b8
--- /dev/null
+++ b/room/room-runtime/src/main/java/androidx/room/RoomTrackingLiveData.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.room
+
+import android.annotation.SuppressLint
+import androidx.arch.core.executor.ArchTaskExecutor
+import androidx.lifecycle.LiveData
+import java.lang.Exception
+import java.lang.RuntimeException
+import java.util.concurrent.Callable
+import java.util.concurrent.Executor
+import java.util.concurrent.atomic.AtomicBoolean
+
+/**
+ * A LiveData implementation that closely works with [InvalidationTracker] to implement
+ * database drive [androidx.lifecycle.LiveData] queries that are strongly hold as long
+ * as they are active.
+ *
+ * We need this extra handling for [androidx.lifecycle.LiveData] because when they are
+ * observed forever, there is no [androidx.lifecycle.Lifecycle] that will keep them in
+ * memory but they should stay. We cannot add-remove observer in [LiveData.onActive],
+ * [LiveData.onInactive] because that would mean missing changes in between or doing an
+ * extra query on every UI rotation.
+ *
+ * This [LiveData] keeps a weak observer to the [InvalidationTracker] but it is hold
+ * strongly by the [InvalidationTracker] as long as it is active.
+ */
+@SuppressLint("RestrictedApi")
+internal class RoomTrackingLiveData<T> (
+    val database: RoomDatabase,
+    private val container: InvalidationLiveDataContainer,
+    val inTransaction: Boolean,
+    val computeFunction: Callable<T>,
+    tableNames: Array<String>
+) : LiveData<T>() {
+    val observer: InvalidationTracker.Observer = object : InvalidationTracker.Observer(tableNames) {
+        override fun onInvalidated(tables: Set<String>) {
+            ArchTaskExecutor.getInstance().executeOnMainThread(invalidationRunnable)
+        }
+    }
+    val invalid = AtomicBoolean(true)
+    val computing = AtomicBoolean(false)
+    val registeredObserver = AtomicBoolean(false)
+    val refreshRunnable = Runnable {
+        if (registeredObserver.compareAndSet(false, true)) {
+            database.invalidationTracker.addWeakObserver(observer)
+        }
+        var computed: Boolean
+        do {
+            computed = false
+            // compute can happen only in 1 thread but no reason to lock others.
+            if (computing.compareAndSet(false, true)) {
+                // as long as it is invalid, keep computing.
+                try {
+                    var value: T? = null
+                    while (invalid.compareAndSet(true, false)) {
+                        computed = true
+                        try {
+                            value = computeFunction.call()
+                        } catch (e: Exception) {
+                            throw RuntimeException(
+                                "Exception while computing database live data.",
+                                e
+                            )
+                        }
+                    }
+                    if (computed) {
+                        postValue(value)
+                    }
+                } finally {
+                    // release compute lock
+                    computing.set(false)
+                }
+            }
+            // check invalid after releasing compute lock to avoid the following scenario.
+            // Thread A runs compute()
+            // Thread A checks invalid, it is false
+            // Main thread sets invalid to true
+            // Thread B runs, fails to acquire compute lock and skips
+            // Thread A releases compute lock
+            // We've left invalid in set state. The check below recovers.
+        } while (computed && invalid.get())
+    }
+
+    val invalidationRunnable = Runnable {
+        val isActive = hasActiveObservers()
+        if (invalid.compareAndSet(false, true)) {
+            if (isActive) {
+                queryExecutor.execute(refreshRunnable)
+            }
+        }
+    }
+
+    @Suppress("UNCHECKED_CAST")
+    override fun onActive() {
+        super.onActive()
+        container.onActive(this as LiveData<Any>)
+        queryExecutor.execute(refreshRunnable)
+    }
+
+    @Suppress("UNCHECKED_CAST")
+    override fun onInactive() {
+        super.onInactive()
+        container.onInactive(this as LiveData<Any>)
+    }
+
+    val queryExecutor: Executor
+        get() = if (inTransaction) {
+            database.transactionExecutor
+        } else {
+            database.queryExecutor
+        }
+}
\ No newline at end of file
diff --git a/room/room-runtime/src/test/java/androidx/room/BuilderTest.java b/room/room-runtime/src/test/java/androidx/room/BuilderTest.java
index 63ce98d..9683a39 100644
--- a/room/room-runtime/src/test/java/androidx/room/BuilderTest.java
+++ b/room/room-runtime/src/test/java/androidx/room/BuilderTest.java
@@ -48,18 +48,6 @@
 @RunWith(JUnit4.class)
 public class BuilderTest {
     @Test(expected = IllegalArgumentException.class)
-    public void nullContext() {
-        //noinspection ConstantConditions
-        Room.databaseBuilder(null, RoomDatabase.class, "bla").build();
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void nullContext2() {
-        //noinspection ConstantConditions
-        Room.inMemoryDatabaseBuilder(null, RoomDatabase.class).build();
-    }
-
-    @Test(expected = IllegalArgumentException.class)
     public void nullName() {
         //noinspection ConstantConditions
         Room.databaseBuilder(mock(Context.class), RoomDatabase.class, null).build();
diff --git a/room/room-runtime/src/test/java/androidx/room/BuilderTest_TestDatabase_Impl.java b/room/room-runtime/src/test/java/androidx/room/BuilderTest_TestDatabase_Impl.java
index d796bdc..b95d27d 100644
--- a/room/room-runtime/src/test/java/androidx/room/BuilderTest_TestDatabase_Impl.java
+++ b/room/room-runtime/src/test/java/androidx/room/BuilderTest_TestDatabase_Impl.java
@@ -18,6 +18,8 @@
 
 import androidx.sqlite.db.SupportSQLiteOpenHelper;
 
+import org.mockito.Mockito;
+
 public class BuilderTest_TestDatabase_Impl extends BuilderTest.TestDatabase {
     DatabaseConfiguration mConfig;
     @Override
@@ -28,12 +30,12 @@
 
     @Override
     protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration config) {
-        return null;
+        return Mockito.mock(SupportSQLiteOpenHelper.class);
     }
 
     @Override
     protected InvalidationTracker createInvalidationTracker() {
-        return null;
+        return Mockito.mock(InvalidationTracker.class);
     }
 
     @Override
diff --git a/room/room-runtime/src/test/java/androidx/room/InvalidationLiveDataContainerTest.kt b/room/room-runtime/src/test/java/androidx/room/InvalidationLiveDataContainerTest.kt
index 6034b67..550821f 100644
--- a/room/room-runtime/src/test/java/androidx/room/InvalidationLiveDataContainerTest.kt
+++ b/room/room-runtime/src/test/java/androidx/room/InvalidationLiveDataContainerTest.kt
@@ -40,9 +40,9 @@
     @Test
     fun add() {
         val liveData = createLiveData()
-        assertThat(container.mLiveDataSet, `is`(emptySet()))
+        assertThat(container.liveDataSet, `is`(emptySet()))
         container.onActive(liveData)
-        assertThat(container.mLiveDataSet, `is`(setOf(liveData)))
+        assertThat(container.liveDataSet, `is`(setOf(liveData)))
     }
 
     @Test
@@ -50,7 +50,7 @@
         val liveData = createLiveData()
         container.onActive(liveData)
         container.onActive(liveData)
-        assertThat(container.mLiveDataSet, `is`(setOf(liveData)))
+        assertThat(container.liveDataSet, `is`(setOf(liveData)))
     }
 
     @Test
@@ -58,7 +58,7 @@
         val liveData = createLiveData()
         container.onActive(liveData)
         container.onInactive(liveData)
-        assertThat(container.mLiveDataSet, `is`(emptySet()))
+        assertThat(container.liveDataSet, `is`(emptySet()))
     }
 
     @Test
@@ -67,33 +67,33 @@
         container.onActive(liveData)
         container.onInactive(liveData)
         container.onInactive(liveData)
-        assertThat(container.mLiveDataSet, `is`(emptySet()))
+        assertThat(container.liveDataSet, `is`(emptySet()))
     }
 
     @Test
     fun addRemoveMultiple() {
         val ld1 = createLiveData()
         val ld2 = createLiveData()
-        assertThat(container.mLiveDataSet, `is`(emptySet()))
+        assertThat(container.liveDataSet, `is`(emptySet()))
         container.onActive(ld1)
         container.onActive(ld2)
-        assertThat(container.mLiveDataSet, `is`(setOf(ld1, ld2)))
+        assertThat(container.liveDataSet, `is`(setOf(ld1, ld2)))
         container.onInactive(ld1)
-        assertThat(container.mLiveDataSet, `is`(setOf(ld2)))
+        assertThat(container.liveDataSet, `is`(setOf(ld2)))
         container.onInactive(ld1) // intentional
-        assertThat(container.mLiveDataSet, `is`(setOf(ld2)))
+        assertThat(container.liveDataSet, `is`(setOf(ld2)))
         container.onActive(ld1)
-        assertThat(container.mLiveDataSet, `is`(setOf(ld1, ld2)))
+        assertThat(container.liveDataSet, `is`(setOf(ld1, ld2)))
         container.onActive(ld1) // intentional
-        assertThat(container.mLiveDataSet, `is`(setOf(ld1, ld2)))
+        assertThat(container.liveDataSet, `is`(setOf(ld1, ld2)))
         container.onInactive(ld2)
-        assertThat(container.mLiveDataSet, `is`(setOf(ld1)))
+        assertThat(container.liveDataSet, `is`(setOf(ld1)))
         container.onInactive(ld1)
-        assertThat(container.mLiveDataSet, `is`(emptySet()))
+        assertThat(container.liveDataSet, `is`(emptySet()))
         container.onActive(ld1)
-        assertThat(container.mLiveDataSet, `is`(setOf(ld1)))
+        assertThat(container.liveDataSet, `is`(setOf(ld1)))
         container.onActive(ld2)
-        assertThat(container.mLiveDataSet, `is`(setOf(ld1, ld2)))
+        assertThat(container.liveDataSet, `is`(setOf(ld1, ld2)))
     }
 
     private fun createLiveData(): LiveData<Any> {
@@ -101,7 +101,7 @@
             arrayOf("a", "b"),
             false,
             createComputeFunction<Any>()
-        ) as LiveData
+        )
     }
 
     private fun <T> createComputeFunction(): Callable<T> {
diff --git a/room/room-runtime/src/test/java/androidx/room/InvalidationTrackerTest.java b/room/room-runtime/src/test/java/androidx/room/InvalidationTrackerTest.java
index 07a7c95..b5bd363 100644
--- a/room/room-runtime/src/test/java/androidx/room/InvalidationTrackerTest.java
+++ b/room/room-runtime/src/test/java/androidx/room/InvalidationTrackerTest.java
@@ -126,22 +126,22 @@
 
     @Test
     public void tableIds() {
-        assertThat(mTracker.mTableIdLookup.size(), is(5));
-        assertThat(mTracker.mTableIdLookup.get("a"), is(0));
-        assertThat(mTracker.mTableIdLookup.get("b"), is(1));
-        assertThat(mTracker.mTableIdLookup.get("i"), is(2));
-        assertThat(mTracker.mTableIdLookup.get("c"), is(3)); // fts
-        assertThat(mTracker.mTableIdLookup.get("d"), is(0)); // external content fts
+        assertThat(mTracker.tableIdLookup.size(), is(5));
+        assertThat(mTracker.tableIdLookup.get("a"), is(0));
+        assertThat(mTracker.tableIdLookup.get("b"), is(1));
+        assertThat(mTracker.tableIdLookup.get("i"), is(2));
+        assertThat(mTracker.tableIdLookup.get("c"), is(3)); // fts
+        assertThat(mTracker.tableIdLookup.get("d"), is(0)); // external content fts
     }
 
     @Test
     public void tableNames() {
-        assertThat(mTracker.mTableNames.length, is(5));
-        assertThat(mTracker.mTableNames[0], is("a"));
-        assertThat(mTracker.mTableNames[1], is("b"));
-        assertThat(mTracker.mTableNames[2], is("i"));
-        assertThat(mTracker.mTableNames[3], is("c_content")); // fts
-        assertThat(mTracker.mTableNames[4], is("a")); // external content fts
+        assertThat(mTracker.tablesNames.length, is(5));
+        assertThat(mTracker.tablesNames[0], is("a"));
+        assertThat(mTracker.tablesNames[1], is("b"));
+        assertThat(mTracker.tablesNames[2], is("i"));
+        assertThat(mTracker.tablesNames[3], is("c_content")); // fts
+        assertThat(mTracker.tablesNames[4], is("a")); // external content fts
     }
 
     @Test
@@ -170,11 +170,11 @@
     public void addRemoveObserver() throws Exception {
         InvalidationTracker.Observer observer = new LatchObserver(1, "a");
         mTracker.addObserver(observer);
-        assertThat(mTracker.mObserverMap.size(), is(1));
+        assertThat(mTracker.observerMap.size(), is(1));
         mTracker.removeObserver(new LatchObserver(1, "a"));
-        assertThat(mTracker.mObserverMap.size(), is(1));
+        assertThat(mTracker.observerMap.size(), is(1));
         mTracker.removeObserver(observer);
-        assertThat(mTracker.mObserverMap.size(), is(0));
+        assertThat(mTracker.observerMap.size(), is(0));
     }
 
     private void drainTasks() throws InterruptedException {
@@ -198,12 +198,12 @@
                 .thenReturn(mock(Cursor.class));
         mTracker.refreshVersionsAsync();
         mTracker.refreshVersionsAsync();
-        verify(mTaskExecutorRule.getTaskExecutor()).executeOnDiskIO(mTracker.mRefreshRunnable);
+        verify(mTaskExecutorRule.getTaskExecutor()).executeOnDiskIO(mTracker.refreshRunnable);
         drainTasks();
 
         reset(mTaskExecutorRule.getTaskExecutor());
         mTracker.refreshVersionsAsync();
-        verify(mTaskExecutorRule.getTaskExecutor()).executeOnDiskIO(mTracker.mRefreshRunnable);
+        verify(mTaskExecutorRule.getTaskExecutor()).executeOnDiskIO(mTracker.refreshRunnable);
     }
 
     @Test
@@ -268,7 +268,7 @@
         doReturn(false).when(mRoomDatabase).isOpen();
         doThrow(new IllegalStateException("foo")).when(mOpenHelper).getWritableDatabase();
         mTracker.addObserver(new LatchObserver(1, "a", "b"));
-        mTracker.mRefreshRunnable.run();
+        mTracker.refreshRunnable.run();
     }
 
     @Test
@@ -449,12 +449,12 @@
         setInvalidatedTables(3, 1);
         mTracker.addObserver(new LatchObserver(1, "a", "b"));
         mTracker.syncTriggers();
-        mTracker.mRefreshRunnable.run();
+        mTracker.refreshRunnable.run();
         doThrow(new SQLiteException("foo")).when(mRoomDatabase).query(
                 Mockito.eq(InvalidationTracker.SELECT_UPDATED_TABLES_SQL),
                 any(Object[].class));
-        mTracker.mPendingRefresh.set(true);
-        mTracker.mRefreshRunnable.run();
+        mTracker.pendingRefresh.set(true);
+        mTracker.refreshRunnable.run();
     }
 
     /**
diff --git a/sqlite/integration-tests/inspection-room-testapp/src/androidTest/java/androidx/sqlite/inspection/RoomInvalidationHookTest.kt b/sqlite/integration-tests/inspection-room-testapp/src/androidTest/java/androidx/sqlite/inspection/RoomInvalidationHookTest.kt
index 18c179c..6c745b8 100644
--- a/sqlite/integration-tests/inspection-room-testapp/src/androidTest/java/androidx/sqlite/inspection/RoomInvalidationHookTest.kt
+++ b/sqlite/integration-tests/inspection-room-testapp/src/androidTest/java/androidx/sqlite/inspection/RoomInvalidationHookTest.kt
@@ -93,7 +93,7 @@
         )
         val invalidatedTables = CompletableDeferred<List<String>>()
         db.invalidationTracker.addObserver(object : InvalidationTracker.Observer("TestEntity") {
-            override fun onInvalidated(tables: MutableSet<String>) {
+            override fun onInvalidated(tables: Set<String>) {
                 invalidatedTables.complete(tables.toList())
             }
         })