Db Writer initial code

This CL creates the database writer and prepares RoomDatabase
for actual implementation.

This is just the skeleton, database creation will happen
in another CL.

Bug: 32342709
Test: DatabaseWriter.kt
Change-Id: I7ce7b9c938c7ba196c8d0b6fc5f08d76e2001696
diff --git a/flatfoot-common/init.gradle b/flatfoot-common/init.gradle
index baee4f43..5deb735 100644
--- a/flatfoot-common/init.gradle
+++ b/flatfoot-common/init.gradle
@@ -26,7 +26,7 @@
 ext.kotlin_version = "1.0.5"
 ext.android_gradle_plugin_version = "2.2.1"
 ext.auto_common_version = "0.6"
-ext.javapoet_version = "1.7.0"
+ext.javapoet_version = "1.8.0"
 ext.compile_testing_version = "0.9"
 ext.localize_maven_version = "1.1"
 ext.support_lib_version = "25.1.0-SNAPSHOT"
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/ext/javapoet_ext.kt b/room/compiler/src/main/kotlin/com/android/support/room/ext/javapoet_ext.kt
index 4968fd1..3a4c17c 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/ext/javapoet_ext.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/ext/javapoet_ext.kt
@@ -34,6 +34,16 @@
 object SupportDbTypeNames {
     val DB: ClassName = ClassName.get("com.android.support.db", "SupportSQLiteDatabase")
     val SQLITE_STMT : ClassName = ClassName.get("com.android.support.db", "SupportSQLiteStatement")
+    val SQLITE_OPEN_HELPER : ClassName =
+            ClassName.get("com.android.support.db", "SupportSQLiteOpenHelper")
+    val SQLITE_OPEN_HELPER_CALLBACK : ClassName =
+            ClassName.get("com.android.support.db", "SupportSQLiteOpenHelper.Callback")
+    val SQLITE_OPEN_HELPER_FACTORY : ClassName =
+            ClassName.get("com.android.support.db", "SupportSQLiteOpenHelper.Factory")
+    val SQLITE_OPEN_HELPER_CONFIG : ClassName =
+            ClassName.get("com.android.support.db", "SupportSQLiteOpenHelper.Configuration")
+    val SQLITE_OPEN_HELPER_CONFIG_BUILDER : ClassName =
+            ClassName.get("com.android.support.db", "SupportSQLiteOpenHelper.Configuration.Builder")
 }
 
 object RoomTypeNames {
@@ -41,6 +51,8 @@
     val CURSOR_CONVERTER : ClassName = ClassName.get("com.android.support.room", "CursorConverter")
     val ROOM : ClassName = ClassName.get("com.android.support.room", "Room")
     val ROOM_DB : ClassName = ClassName.get("com.android.support.room", "RoomDatabase")
+    val ROOM_DB_CONFIG : ClassName = ClassName.get("com.android.support.room",
+            "DatabaseConfiguration")
     val INSERTION_ADAPTER : ClassName =
             ClassName.get("com.android.support.room", "EntityInsertionAdapter")
 }
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/processor/DatabaseProcessor.kt b/room/compiler/src/main/kotlin/com/android/support/room/processor/DatabaseProcessor.kt
index 9694a24..829a987 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/processor/DatabaseProcessor.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/processor/DatabaseProcessor.kt
@@ -24,6 +24,7 @@
 import com.google.auto.common.AnnotationMirrors
 import com.google.auto.common.MoreElements
 import com.google.auto.common.MoreTypes
+import com.squareup.javapoet.TypeName
 import javax.lang.model.element.AnnotationMirror
 import javax.lang.model.element.AnnotationValue
 import javax.lang.model.element.ElementKind
@@ -56,6 +57,11 @@
         val allMembers = context.processingEnv.elementUtils.getAllMembers(element)
         val daoMethods = allMembers.filter {
             it.hasAnyOf(Modifier.ABSTRACT) && it.kind == ElementKind.METHOD
+        }.filterNot {
+            // remove methods that belong to room
+            val containing = it.enclosingElement
+            MoreElements.isType(containing) &&
+                    TypeName.get(containing.asType()) == RoomTypeNames.ROOM_DB
         }.map {
             val executable = MoreElements.asExecutable(it)
             // TODO when we add support for non Dao return types (e.g. database), this code needs
@@ -65,6 +71,7 @@
         }
 
         return Database(element = element,
+                type = MoreElements.asType(element).asType(),
                 entities = entities,
                 daoMethods = daoMethods)
     }
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/solver/CodeGenScope.kt b/room/compiler/src/main/kotlin/com/android/support/room/solver/CodeGenScope.kt
index 37ad6ac..cdc838d 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/solver/CodeGenScope.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/solver/CodeGenScope.kt
@@ -54,4 +54,13 @@
     }
 
     fun generate() = builder().build().toString()
+
+    /**
+     * copies all variable indices but excludes generated code.
+     */
+    fun fork() : CodeGenScope {
+        val forked = CodeGenScope()
+        forked.tmpVarIndices.putAll(tmpVarIndices)
+        return forked
+    }
 }
\ No newline at end of file
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/vo/Database.kt b/room/compiler/src/main/kotlin/com/android/support/room/vo/Database.kt
index 670ab15..e1ee2f8 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/vo/Database.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/vo/Database.kt
@@ -16,10 +16,24 @@
 
 package com.android.support.room.vo
 
+import com.squareup.javapoet.ClassName
 import javax.lang.model.element.Element
+import javax.lang.model.type.TypeMirror
 
 /**
  * Holds information about a class annotated with Database.
  */
-data class Database(val element : Element, val entities : List<Entity>,
-                    val daoMethods : List<DaoMethod>)
\ No newline at end of file
+data class Database(val element : Element,
+                    val type : TypeMirror,
+                    val entities : List<Entity>,
+                    val daoMethods : List<DaoMethod>) {
+    val typeName by lazy { ClassName.get(type) as ClassName }
+
+    val implClassName by lazy {
+        "${typeName.simpleName()}_Impl"
+    }
+
+    val implTypeName by lazy {
+        ClassName.get(typeName.packageName(), implClassName)
+    }
+}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/writer/DatabaseWriter.kt b/room/compiler/src/main/kotlin/com/android/support/room/writer/DatabaseWriter.kt
new file mode 100644
index 0000000..9ae0510
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/writer/DatabaseWriter.kt
@@ -0,0 +1,74 @@
+/*
+ * 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 com.android.support.room.writer
+
+import com.android.support.room.ext.L
+import com.android.support.room.ext.N
+import com.android.support.room.ext.RoomTypeNames
+import com.android.support.room.ext.SupportDbTypeNames
+import com.android.support.room.solver.CodeGenScope
+import com.android.support.room.vo.Database
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.TypeSpec
+import javax.lang.model.element.Modifier
+import javax.lang.model.element.Modifier.PUBLIC
+
+/**
+ * Writes implementation of classes that were annotated with @Database.
+ */
+class DatabaseWriter(val database : Database) : ClassWriter(database.implTypeName) {
+    override fun createTypeSpec(): TypeSpec {
+        val builder = TypeSpec.classBuilder(database.implTypeName)
+        builder.apply {
+            addModifiers(PUBLIC)
+            superclass(database.typeName)
+            addMethod(createConstructor())
+            addMethod(createCreateOpenHelper())
+        }
+        return builder.build()
+    }
+
+    private fun createConstructor(): MethodSpec {
+        return MethodSpec.constructorBuilder().apply {
+            addModifiers(Modifier.PUBLIC)
+            val configParam = ParameterSpec.builder(RoomTypeNames.ROOM_DB_CONFIG,
+                    "configuration").build()
+            addParameter(configParam)
+            addStatement("super($N)", configParam)
+        }.build()
+    }
+
+    private fun createCreateOpenHelper() : MethodSpec {
+        val scope = CodeGenScope()
+        return MethodSpec.methodBuilder("createOpenHelper").apply {
+            addModifiers(Modifier.PROTECTED)
+            returns(SupportDbTypeNames.SQLITE_OPEN_HELPER)
+
+            val configParam = ParameterSpec.builder(RoomTypeNames.ROOM_DB_CONFIG,
+                    "configuration").build()
+            addParameter(configParam)
+
+            val openHelperVar = scope.getTmpVar("_helper")
+            val openHelperCode = scope.fork()
+            SQLiteOpenHelperWriter(database)
+                    .write(openHelperVar, configParam, openHelperCode)
+            addCode(openHelperCode.builder().build())
+            addStatement("return $L", openHelperVar)
+        }.build()
+    }
+}
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/writer/SQLiteOpenHelperWriter.kt b/room/compiler/src/main/kotlin/com/android/support/room/writer/SQLiteOpenHelperWriter.kt
new file mode 100644
index 0000000..30de7e7
--- /dev/null
+++ b/room/compiler/src/main/kotlin/com/android/support/room/writer/SQLiteOpenHelperWriter.kt
@@ -0,0 +1,97 @@
+/*
+ * 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 com.android.support.room.writer
+
+import com.android.support.room.ext.L
+import com.android.support.room.ext.N
+import com.android.support.room.ext.SupportDbTypeNames
+import com.android.support.room.ext.T
+import com.android.support.room.solver.CodeGenScope
+import com.android.support.room.vo.Database
+import com.squareup.javapoet.FieldSpec
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeSpec
+import javax.lang.model.element.Modifier.PUBLIC
+
+/**
+ * Create an open helper using SupportSQLiteOpenHelperFactory
+ */
+class SQLiteOpenHelperWriter(val database : Database) {
+    fun write(outVar : String, configuration : ParameterSpec, scope: CodeGenScope) {
+        scope.builder().apply {
+            val sqliteConfigVar = scope.getTmpVar("_sqliteConfig")
+            val callbackVar = scope.getTmpVar("_openCallback")
+            addStatement("final $T $L = $L",
+                    SupportDbTypeNames.SQLITE_OPEN_HELPER_CALLBACK,
+                    callbackVar, createOpenCallback())
+            // build configuration
+            addStatement(
+                    """
+                    final $T $L = $T.builder($N.context)
+                    .name($N.name)
+                    .version($N.version)
+                    .callback($L)
+                    .build()
+                    """.trimIndent(),
+                    SupportDbTypeNames.SQLITE_OPEN_HELPER_CONFIG, sqliteConfigVar,
+                    SupportDbTypeNames.SQLITE_OPEN_HELPER_CONFIG,
+                    configuration, configuration, configuration, callbackVar)
+            addStatement("final $T $N = $N.sqliteOpenHelperFactory.create($L)",
+                    SupportDbTypeNames.SQLITE_OPEN_HELPER, outVar,
+                    configuration, sqliteConfigVar)
+        }
+    }
+
+    private fun createOpenCallback() : TypeSpec {
+        return TypeSpec.anonymousClassBuilder("").apply {
+            superclass(SupportDbTypeNames.SQLITE_OPEN_HELPER_CALLBACK)
+            addMethod(createOnCreate())
+            addMethod(createOnUpgrade())
+            addMethod(createOnDowngrade())
+        }.build()
+    }
+
+    private fun createOnCreate() : MethodSpec {
+        return MethodSpec.methodBuilder("onCreate").apply {
+            addModifiers(PUBLIC)
+            addParameter(SupportDbTypeNames.DB, "_db")
+            // TODO
+        }.build()
+    }
+
+    private fun createOnUpgrade() : MethodSpec {
+        return MethodSpec.methodBuilder("onUpgrade").apply {
+            addModifiers(PUBLIC)
+            addParameter(SupportDbTypeNames.DB, "_db")
+            addParameter(TypeName.INT, "_oldVersion")
+            addParameter(TypeName.INT, "_newVersion")
+            // TODO
+        }.build()
+    }
+
+    private fun createOnDowngrade() : MethodSpec {
+        return MethodSpec.methodBuilder("onDowngrade").apply {
+            addModifiers(PUBLIC)
+            addParameter(SupportDbTypeNames.DB, "_db")
+            addParameter(TypeName.INT, "_oldVersion")
+            addParameter(TypeName.INT, "_newVersion")
+            // TODO
+        }.build()
+    }
+}
diff --git a/room/compiler/src/test/data/databasewriter/input/ComplexDatabase.java b/room/compiler/src/test/data/databasewriter/input/ComplexDatabase.java
new file mode 100644
index 0000000..0a582b7
--- /dev/null
+++ b/room/compiler/src/test/data/databasewriter/input/ComplexDatabase.java
@@ -0,0 +1,25 @@
+/*
+ * 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 foo.bar;
+import com.android.support.room.*;
+import java.util.List;
+@Database(entities = {User.class})
+abstract class ComplexDatabase extends RoomDatabase {
+    public ComplexDatabase(DatabaseConfiguration configuration) {
+        super(configuration);
+    }
+}
diff --git a/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java b/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java
new file mode 100644
index 0000000..aea0829
--- /dev/null
+++ b/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java
@@ -0,0 +1,33 @@
+package foo.bar;
+
+import com.android.support.db.SupportSQLiteDatabase;
+import com.android.support.db.SupportSQLiteOpenHelper;
+import com.android.support.db.SupportSQLiteOpenHelper.Callback;
+import com.android.support.db.SupportSQLiteOpenHelper.Configuration;
+import com.android.support.room.DatabaseConfiguration;
+
+public class ComplexDatabase_Impl extends ComplexDatabase {
+    public ComplexDatabase_Impl(DatabaseConfiguration configuration) {
+        super(configuration);
+    }
+
+    protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {
+        final SupportSQLiteOpenHelper.Callback _openCallback = new SupportSQLiteOpenHelper.Callback() {
+            public void onCreate(SupportSQLiteDatabase _db) {
+            }
+
+            public void onUpgrade(SupportSQLiteDatabase _db, int _oldVersion, int _newVersion) {
+            }
+
+            public void onDowngrade(SupportSQLiteDatabase _db, int _oldVersion, int _newVersion) {
+            }
+        };
+        final SupportSQLiteOpenHelper.Configuration _sqliteConfig = SupportSQLiteOpenHelper.Configuration.builder(configuration.context)
+                .name(configuration.name)
+                .version(configuration.version)
+                .callback(_openCallback)
+                .build();
+        final SupportSQLiteOpenHelper _helper = configuration.sqliteOpenHelperFactory.create(_sqliteConfig);
+        return _helper;
+    }
+}
\ No newline at end of file
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/processor/DatabaseProcessorTest.kt b/room/compiler/src/test/kotlin/com/android/support/room/processor/DatabaseProcessorTest.kt
index b772007..89f378a 100644
--- a/room/compiler/src/test/kotlin/com/android/support/room/processor/DatabaseProcessorTest.kt
+++ b/room/compiler/src/test/kotlin/com/android/support/room/processor/DatabaseProcessorTest.kt
@@ -37,7 +37,6 @@
         const val DATABASE_PREFIX = """
             package foo.bar;
             import com.android.support.room.*;
-            import com.android.support.db.SupportSQLiteDatabase;
             """
         val USER: JavaFileObject = JavaFileObjects.forSourceString("foo.bar.User",
                 """
@@ -86,8 +85,8 @@
         singleDb("""
             @Database(entities = {User.class})
             public abstract class MyDb extends RoomDatabase {
-                public MyDb(SupportSQLiteDatabase supportDb) {
-                    super(supportDb);
+                public MyDb(DatabaseConfiguration config) {
+                    super(config);
                 }
                 abstract UserDao userDao();
             }
@@ -102,8 +101,8 @@
         singleDb("""
             @Database(entities = {User.class, Book.class})
             public abstract class MyDb extends RoomDatabase {
-                public MyDb(SupportSQLiteDatabase supportDb) {
-                    super(supportDb);
+                public MyDb(DatabaseConfiguration config) {
+                    super(config);
                 }
                 abstract UserDao userDao();
                 abstract BookDao bookDao();
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/writer/DatabaseWriterTest.kt b/room/compiler/src/test/kotlin/com/android/support/room/writer/DatabaseWriterTest.kt
new file mode 100644
index 0000000..d3ca260
--- /dev/null
+++ b/room/compiler/src/test/kotlin/com/android/support/room/writer/DatabaseWriterTest.kt
@@ -0,0 +1,62 @@
+/*
+ * 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 com.android.support.room.writer
+
+import com.android.support.room.processor.DatabaseProcessor
+import com.android.support.room.testing.TestProcessor
+import com.google.auto.common.MoreElements
+import com.google.common.truth.Truth
+import com.google.testing.compile.CompileTester
+import com.google.testing.compile.JavaSourcesSubjectFactory
+import loadJavaCode
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import javax.tools.JavaFileObject
+
+@RunWith(JUnit4::class)
+class DatabaseWriterTest {
+
+    @Test
+    fun simpleDb() {
+        singleDb(
+                loadJavaCode("databasewriter/input/ComplexDatabase.java",
+                        "foo.bar.ComplexDatabase")
+        ).compilesWithoutError().and().generatesSources(
+                loadJavaCode("databasewriter/output/ComplexDatabase.java",
+                        "foo.bar.ComplexDatabase_Impl")
+        )
+    }
+
+    private fun singleDb(vararg jfo : JavaFileObject): CompileTester {
+        return Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
+                .that(jfo.toList() + COMMON.USER)
+                .processedWith(TestProcessor.builder()
+                        .forAnnotations(com.android.support.room.Database::class)
+                        .nextRunHandler { invocation ->
+                            val dao = invocation.roundEnv
+                                    .getElementsAnnotatedWith(
+                                            com.android.support.room.Database::class.java)
+                                    .first()
+                            val processor = DatabaseProcessor(invocation.context)
+                            val db = processor.parse(MoreElements.asType(dao))
+                            DatabaseWriter(db).write(invocation.processingEnv)
+                            true
+                        }
+                        .build())
+    }
+}
\ No newline at end of file
diff --git a/room/gradle/wrapper/gradle-wrapper.properties b/room/gradle/wrapper/gradle-wrapper.properties
index d1755f7..a5292b0 100644
--- a/room/gradle/wrapper/gradle-wrapper.properties
+++ b/room/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Thu Dec 08 18:53:02 PST 2016
+#Fri Dec 09 13:07:04 PST 2016
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.2-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-3.2-bin.zip
diff --git a/room/runtime/build.gradle b/room/runtime/build.gradle
index 76992f1..a917757 100644
--- a/room/runtime/build.gradle
+++ b/room/runtime/build.gradle
@@ -49,6 +49,7 @@
 dependencies {
     compile project(":common")
     compile project(":db")
+    compile project(":db-impl")
     compile "com.android.support:support-core-utils:$support_lib_version"
 
     testCompile "junit:junit:$junit_version"
diff --git a/room/runtime/src/main/java/com/android/support/room/DatabaseConfiguration.java b/room/runtime/src/main/java/com/android/support/room/DatabaseConfiguration.java
new file mode 100644
index 0000000..7a6431a
--- /dev/null
+++ b/room/runtime/src/main/java/com/android/support/room/DatabaseConfiguration.java
@@ -0,0 +1,116 @@
+/*
+ * 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 com.android.support.room;
+
+import android.content.Context;
+
+import com.android.support.db.SupportSQLiteOpenHelper;
+import com.android.support.db.framework.FrameworkSQLiteOpenHelperFactory;
+
+/**
+ * Configuration class for {@link RoomDatabase}.
+ */
+public class DatabaseConfiguration {
+    /**
+     * The factory to use to access the database.
+     */
+    public final SupportSQLiteOpenHelper.Factory sqliteOpenHelperFactory;
+    /**
+     * The context to you.
+     */
+    public final Context context;
+    /**
+     * The name of the database or null if it is in memory.
+     */
+    public final String name;
+    /**
+     * The version of the database.
+     */
+    public final int version;
+
+    private DatabaseConfiguration(Context context, String name, int version,
+                                  SupportSQLiteOpenHelper.Factory sqliteOpenHelperFactory) {
+        this.sqliteOpenHelperFactory = sqliteOpenHelperFactory;
+        this.context = context;
+        this.name = name;
+        this.version = version;
+    }
+
+    /**
+     * Builder for the RoomDatabase configuration.
+     */
+    public static class Builder {
+        SupportSQLiteOpenHelper.Factory mFactory;
+        Context mContext;
+        String mName;
+        int mVersion = 1;
+
+        /**
+         * Sets the database factory. If not set, it defaults to
+         * {@link FrameworkSQLiteOpenHelperFactory}.
+         *
+         * @param factory The factory to use to access the database.
+         *
+         * @return this
+         */
+        public Builder withOpenHelperFactory(SupportSQLiteOpenHelper.Factory factory) {
+            mFactory = factory;
+            return this;
+        }
+
+        /**
+         * @param name The name of the database.
+         * @return this
+         */
+        public Builder withName(String name) {
+            mName = name;
+            return this;
+        }
+
+        /**
+         * Version of the database, defaults to 1.
+         * @param version The database version to use
+         * @return this
+         */
+        public Builder withVersion(int version) {
+            mVersion = version;
+            return this;
+        }
+
+        /**
+         * Creates a Configuration builder with the given context.
+         * Most of the time, this should be an application context.
+         *
+         * @param context The context to use for the database.
+         */
+        public Builder(Context context) {
+            mContext = context;
+        }
+
+        /**
+         * Creates the {@link RoomDatabase.Configuration} object for the database.
+         *
+         * @return Configuration that can be used to create the RoomDatabase.
+         */
+        public DatabaseConfiguration build() {
+            if (mFactory == null) {
+                mFactory = new FrameworkSQLiteOpenHelperFactory();
+            }
+            return new DatabaseConfiguration(mContext, mName, mVersion, mFactory);
+        }
+    }
+}
diff --git a/room/runtime/src/main/java/com/android/support/room/RoomDatabase.java b/room/runtime/src/main/java/com/android/support/room/RoomDatabase.java
index 650d97f..83af0aa 100644
--- a/room/runtime/src/main/java/com/android/support/room/RoomDatabase.java
+++ b/room/runtime/src/main/java/com/android/support/room/RoomDatabase.java
@@ -19,6 +19,7 @@
 import android.database.Cursor;
 
 import com.android.support.db.SupportSQLiteDatabase;
+import com.android.support.db.SupportSQLiteOpenHelper;
 import com.android.support.db.SupportSQLiteStatement;
 
 /**
@@ -26,11 +27,38 @@
  */
 @SuppressWarnings({"unused", "WeakerAccess"})
 public abstract class RoomDatabase {
-    private final SupportSQLiteDatabase mDb;
+    private volatile SupportSQLiteDatabase mDatabase;
+    private final SupportSQLiteOpenHelper mOpenHelper;
 
-    public RoomDatabase(SupportSQLiteDatabase supportDb) {
-        mDb = supportDb;
+    /**
+     * Creates a RoomDatabase with the given configuration.
+     *
+     * @param configuration The configuration to setup the database.
+     */
+    public RoomDatabase(DatabaseConfiguration configuration) {
+        mOpenHelper = createOpenHelper(configuration);
     }
+
+    /**
+     * Returns the SQLite open helper used by this database.
+     *
+     * @return The SQLite open helper used by this database.
+     */
+    public SupportSQLiteOpenHelper getOpenHelper() {
+        return mOpenHelper;
+    }
+
+    /**
+     * Creates the open helper to access the database. Generated class already implements this
+     * method.
+     * Note that this method is called when the RoomDatabase is initialized.
+     *
+     * @param config The configuration of the Room database.
+     *
+     * @return A new SupportSQLiteOpenHelper to be used while connecting to the database.
+     */
+    protected abstract SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration config);
+
     // Below, there are wrapper methods for SupportSQLiteDatabase. This helps us track which
     // methods we are using and also helps unit tests to mock this class without mocking
     // all sqlite database methods.
@@ -43,7 +71,7 @@
      * @return Result of the query.
      */
     public Cursor query(String sql, String[] selectionArgs) {
-        return mDb.rawQuery(sql, selectionArgs);
+        return mOpenHelper.getWritableDatabase().rawQuery(sql, selectionArgs);
     }
 
     /**
@@ -54,27 +82,27 @@
      * @return The compiled query.
      */
     public SupportSQLiteStatement compileStatement(String sql) {
-        return mDb.compileStatement(sql);
+        return mOpenHelper.getWritableDatabase().compileStatement(sql);
     }
 
     /**
      * Wrapper for {@link SupportSQLiteDatabase#beginTransaction()}.
      */
     public void beginTransaction() {
-        mDb.beginTransaction();
+        mOpenHelper.getWritableDatabase().beginTransaction();
     }
 
     /**
      * Wrapper for {@link SupportSQLiteDatabase#endTransaction()}.
      */
     public void endTransaction() {
-        mDb.endTransaction();
+        mOpenHelper.getWritableDatabase().endTransaction();
     }
 
     /**
      * Wrapper for {@link SupportSQLiteDatabase#setTransactionSuccessful()}.
      */
     public void setTransactionSuccessful() {
-        mDb.setTransactionSuccessful();
+        mOpenHelper.getWritableDatabase().setTransactionSuccessful();
     }
 }