Calling generated AutoMigration class in the generated code from the database writer.

Test: AutoMigrationProcessorTest.kt, AutoMigrationWriterTest.kt
Bug: 180395129
Change-Id: I4a7ac3495dd21b296d2ea291c4ec7a67cbe772f4
diff --git a/room/compiler/src/main/kotlin/androidx/room/ext/javapoet_ext.kt b/room/compiler/src/main/kotlin/androidx/room/ext/javapoet_ext.kt
index 4aedd7a..a98a1ce 100644
--- a/room/compiler/src/main/kotlin/androidx/room/ext/javapoet_ext.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/ext/javapoet_ext.kt
@@ -130,6 +130,7 @@
 }
 
 object CommonTypeNames {
+    val ARRAYS = ClassName.get("java.util", "Arrays")
     val LIST = ClassName.get("java.util", "List")
     val MAP = ClassName.get("java.util", "Map")
     val SET = ClassName.get("java.util", "Set")
diff --git a/room/compiler/src/main/kotlin/androidx/room/processor/AutoMigrationProcessor.kt b/room/compiler/src/main/kotlin/androidx/room/processor/AutoMigrationProcessor.kt
index b9d992c..00268f2 100644
--- a/room/compiler/src/main/kotlin/androidx/room/processor/AutoMigrationProcessor.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/processor/AutoMigrationProcessor.kt
@@ -115,13 +115,13 @@
     private fun getValidatedSchemaFile(version: Int): File? {
         val schemaFile = File(
             context.schemaOutFolder,
-            "${element.qualifiedName}/$version.json"
+            "${element.className.enclosingClassName()}/$version.json"
         )
         if (!schemaFile.exists()) {
             context.logger.e(
                 ProcessorErrors.autoMigrationSchemasNotFound(
                     context.schemaOutFolder.toString(),
-                    "${element.qualifiedName}/$version.json"
+                    "${element.className.enclosingClassName()}/$version.json"
                 ),
                 element
             )
diff --git a/room/compiler/src/main/kotlin/androidx/room/writer/AutoMigrationWriter.kt b/room/compiler/src/main/kotlin/androidx/room/writer/AutoMigrationWriter.kt
index c6a7b9c..0661aa1 100644
--- a/room/compiler/src/main/kotlin/androidx/room/writer/AutoMigrationWriter.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/writer/AutoMigrationWriter.kt
@@ -36,7 +36,7 @@
 class AutoMigrationWriter(
     private val dbElement: XElement,
     val autoMigrationResult: AutoMigrationResult
-) : ClassWriter(autoMigrationResult.element.className) {
+) : ClassWriter(autoMigrationResult.implTypeName) {
 
     override fun createTypeSpecBuilder(): TypeSpec.Builder {
         val builder = TypeSpec.classBuilder(autoMigrationResult.implTypeName)
@@ -98,7 +98,7 @@
                         "${it.fieldBundle.affinity} "
                 )
                 if (it.fieldBundle.isNonNull) {
-                    append("NOT NULL DEFAULT `${it.fieldBundle.defaultValue}`")
+                    append("NOT NULL DEFAULT ${it.fieldBundle.defaultValue}")
                 } else {
                     append("DEFAULT NULL")
                 }
diff --git a/room/compiler/src/main/kotlin/androidx/room/writer/DatabaseWriter.kt b/room/compiler/src/main/kotlin/androidx/room/writer/DatabaseWriter.kt
index 9902227..c88bb6a 100644
--- a/room/compiler/src/main/kotlin/androidx/room/writer/DatabaseWriter.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/writer/DatabaseWriter.kt
@@ -31,6 +31,7 @@
 import androidx.room.vo.DaoMethod
 import androidx.room.vo.Database
 import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.CodeBlock
 import com.squareup.javapoet.FieldSpec
 import com.squareup.javapoet.MethodSpec
 import com.squareup.javapoet.ParameterSpec
@@ -62,6 +63,7 @@
             addMethod(createCreateInvalidationTracker())
             addMethod(createClearAllTables())
             addMethod(createCreateTypeConvertersMap())
+            addMethod(getAutoMigrations())
         }
         addDaoImpls(builder)
         return builder
@@ -301,4 +303,20 @@
             addStatement("return $L", openHelperVar)
         }.build()
     }
+
+    private fun getAutoMigrations(): MethodSpec {
+        return MethodSpec.methodBuilder("getAutoMigrations").apply {
+            addModifiers(PROTECTED)
+            addAnnotation(Override::class.java)
+            returns(ParameterizedTypeName.get(CommonTypeNames.LIST, RoomTypeNames.MIGRATION))
+            val autoMigrationsList = database.autoMigrations.map {
+                CodeBlock.of("new $L()", it.implTypeName)
+            }
+            addStatement(
+                "return $T.asList( $L )",
+                CommonTypeNames.ARRAYS,
+                CodeBlock.join(autoMigrationsList, ",")
+            )
+        }.build()
+    }
 }
diff --git a/room/compiler/src/test/data/autoMigrationWriter/output/ValidAutoMigrationWithDefault.java b/room/compiler/src/test/data/autoMigrationWriter/output/ValidAutoMigrationWithDefault.java
index b0882e0..c75033d 100644
--- a/room/compiler/src/test/data/autoMigrationWriter/output/ValidAutoMigrationWithDefault.java
+++ b/room/compiler/src/test/data/autoMigrationWriter/output/ValidAutoMigrationWithDefault.java
@@ -17,7 +17,7 @@
 
     @Override
     public void migrate(@NonNull SupportSQLiteDatabase database) {
-        database.execSQL("ALTER TABLE `Song` ADD COLUMN `artistId` INTEGER NOT NULL DEFAULT `0`");
+        database.execSQL("ALTER TABLE `Song` ADD COLUMN `artistId` INTEGER NOT NULL DEFAULT 0");
         onPostMigrate(database);
     }
 }
diff --git a/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java b/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java
index aa123f3..6b6c6d5 100644
--- a/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java
+++ b/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java
@@ -5,6 +5,7 @@
 import androidx.room.RoomOpenHelper;
 import androidx.room.RoomOpenHelper.Delegate;
 import androidx.room.RoomOpenHelper.ValidationResult;
+import androidx.room.migration.Migration;
 import androidx.room.util.DBUtil;
 import androidx.room.util.TableInfo;
 import androidx.room.util.TableInfo.Column;
@@ -19,6 +20,7 @@
 import java.lang.Override;
 import java.lang.String;
 import java.lang.SuppressWarnings;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -185,6 +187,11 @@
     }
 
     @Override
+    protected List<Migration> getAutoMigrations() {
+        return Arrays.asList(  );
+    }
+
+    @Override
     ComplexDao getComplexDao() {
         if (_complexDao != null) {
             return _complexDao;
diff --git a/room/integration-tests/testapp/schemas/androidx.room.integration.testapp.migration.AutoMigrationDb/1.json b/room/integration-tests/testapp/schemas/androidx.room.integration.testapp.migration.AutoMigrationDb/1.json
new file mode 100644
index 0000000..8f1c4a3
--- /dev/null
+++ b/room/integration-tests/testapp/schemas/androidx.room.integration.testapp.migration.AutoMigrationDb/1.json
@@ -0,0 +1,54 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 1,
+    "identityHash": "83c0712e9973d534e0a9b882f1d7c4ce",
+    "entities": [
+      {
+        "tableName": "Entity1",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, `value1` INTEGER NOT NULL DEFAULT 1, `value2` INTEGER NOT NULL DEFAULT 2, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "value1",
+            "columnName": "value1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          },
+          {
+            "fieldPath": "value2",
+            "columnName": "value2",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "2"
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": [],
+        "foreignKeys": []
+      }
+    ],
+    "views": [],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '83c0712e9973d534e0a9b882f1d7c4ce')"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/room/integration-tests/testapp/schemas/androidx.room.integration.testapp.migration.AutoMigrationDb/2.json b/room/integration-tests/testapp/schemas/androidx.room.integration.testapp.migration.AutoMigrationDb/2.json
new file mode 100644
index 0000000..da1b494
--- /dev/null
+++ b/room/integration-tests/testapp/schemas/androidx.room.integration.testapp.migration.AutoMigrationDb/2.json
@@ -0,0 +1,61 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 2,
+    "identityHash": "78c7261f6c50da0e69cf00d3277c1994",
+    "entities": [
+      {
+        "tableName": "Entity1",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT, `value1` INTEGER NOT NULL DEFAULT 1, `value2` INTEGER NOT NULL DEFAULT 2, `addedInV2` INTEGER NOT NULL DEFAULT 3, PRIMARY KEY(`id`))",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "value1",
+            "columnName": "value1",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "1"
+          },
+          {
+            "fieldPath": "value2",
+            "columnName": "value2",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "2"
+          },
+          {
+            "fieldPath": "addedInV2",
+            "columnName": "addedInV2",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "3"
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": [],
+        "foreignKeys": []
+      }
+    ],
+    "views": [],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '78c7261f6c50da0e69cf00d3277c1994')"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/AutoMigrationDb.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/AutoMigrationDb.java
new file mode 100644
index 0000000..5e3c4aa
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/AutoMigrationDb.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.integration.testapp.migration;
+
+
+import androidx.room.AutoMigration;
+import androidx.room.ColumnInfo;
+import androidx.room.Dao;
+import androidx.room.Database;
+import androidx.room.Entity;
+import androidx.room.Insert;
+import androidx.room.PrimaryKey;
+import androidx.room.Query;
+import androidx.room.RoomDatabase;
+import androidx.room.migration.AutoMigrationCallback;
+
+import java.util.List;
+
+@Database(
+        version = AutoMigrationDb.LATEST_VERSION,
+        entities = AutoMigrationDb.Entity1.class,
+        autoMigrations = AutoMigrationDb.SimpleAutoMigration.class,
+        exportSchema = true
+)
+public abstract class AutoMigrationDb extends RoomDatabase {
+    static final int LATEST_VERSION = 2;
+    abstract AutoMigrationDb.AutoMigrationDao dao();
+    @Entity
+    static class Entity1 {
+        public static final String TABLE_NAME = "Entity1";
+        @PrimaryKey
+        public int id;
+        public String name;
+        @ColumnInfo(defaultValue = "1")
+        public int value1;
+        @ColumnInfo(defaultValue = "2")
+        public int value2;
+        @ColumnInfo(defaultValue = "3")
+        public int addedInV2;
+    }
+
+    @Dao
+    interface AutoMigrationDao {
+        @Query("SELECT * from Entity1")
+        List<Entity1> getAllEntity1s();
+        @Insert
+        void insert(AutoMigrationDb.Entity1... entity1);
+    }
+
+    @AutoMigration(from=1, to=2)
+    interface SimpleAutoMigration extends AutoMigrationCallback {}
+}
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/AutoMigrationTest.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/AutoMigrationTest.java
new file mode 100644
index 0000000..cf53daf
--- /dev/null
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/migration/AutoMigrationTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.integration.testapp.migration;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import androidx.room.Room;
+import androidx.room.testing.MigrationTestHelper;
+import androidx.sqlite.db.SupportSQLiteDatabase;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+
+/**
+ * Test custom database migrations.
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class AutoMigrationTest {
+    private static final String TEST_DB = "auto-migration-test";
+    @Rule
+    public MigrationTestHelper helper;
+
+    // TODO: (b/181985265) Implement running AutoMigrations and validate.
+    public AutoMigrationTest() {
+        helper = new MigrationTestHelper(InstrumentationRegistry.getInstrumentation(),
+                AutoMigrationDb.class.getCanonicalName());
+    }
+
+
+    // Run this to create the very 1st version of the db.
+    public void createFirstVersion() throws IOException {
+        SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 1);
+        db.close();
+    }
+
+    @Test
+    public void addColumnToDatabaseWithOneTable() throws IOException {
+        try (SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 1)) {
+            db.execSQL("INSERT INTO Entity1 (id, name) VALUES (1, 'row1')");
+        }
+        AutoMigrationDb autoMigrationDbV2 = getLatestDb();
+
+        assertThat(autoMigrationDbV2.dao().getAllEntity1s().size(), is(1));
+    }
+
+    private AutoMigrationDb getLatestDb() {
+        AutoMigrationDb db = Room.databaseBuilder(
+                InstrumentationRegistry.getInstrumentation().getTargetContext(),
+                AutoMigrationDb.class, TEST_DB).build();
+        db.getOpenHelper().getWritableDatabase(); // trigger open
+        helper.closeWhenFinished(db);
+        return db;
+    }
+}
diff --git a/room/runtime/api/current.txt b/room/runtime/api/current.txt
index 061b0ad..d708dbf 100644
--- a/room/runtime/api/current.txt
+++ b/room/runtime/api/current.txt
@@ -51,6 +51,7 @@
     method protected abstract androidx.room.InvalidationTracker createInvalidationTracker();
     method protected abstract androidx.sqlite.db.SupportSQLiteOpenHelper createOpenHelper(androidx.room.DatabaseConfiguration!);
     method @Deprecated public void endTransaction();
+    method protected java.util.List<androidx.room.migration.Migration!> getAutoMigrations();
     method public androidx.room.InvalidationTracker getInvalidationTracker();
     method public androidx.sqlite.db.SupportSQLiteOpenHelper getOpenHelper();
     method public java.util.concurrent.Executor getQueryExecutor();
@@ -108,6 +109,7 @@
   public static class RoomDatabase.MigrationContainer {
     ctor public RoomDatabase.MigrationContainer();
     method public void addMigrations(androidx.room.migration.Migration!...);
+    method public void addMigrations(java.util.List<androidx.room.migration.Migration!>);
     method public java.util.List<androidx.room.migration.Migration!>? findMigrationPath(int, int);
   }
 
diff --git a/room/runtime/api/public_plus_experimental_current.txt b/room/runtime/api/public_plus_experimental_current.txt
index b1f6259..608cd54 100644
--- a/room/runtime/api/public_plus_experimental_current.txt
+++ b/room/runtime/api/public_plus_experimental_current.txt
@@ -54,6 +54,7 @@
     method protected abstract androidx.room.InvalidationTracker createInvalidationTracker();
     method protected abstract androidx.sqlite.db.SupportSQLiteOpenHelper createOpenHelper(androidx.room.DatabaseConfiguration!);
     method @Deprecated public void endTransaction();
+    method protected java.util.List<androidx.room.migration.Migration!> getAutoMigrations();
     method public androidx.room.InvalidationTracker getInvalidationTracker();
     method public androidx.sqlite.db.SupportSQLiteOpenHelper getOpenHelper();
     method public java.util.concurrent.Executor getQueryExecutor();
@@ -113,6 +114,7 @@
   public static class RoomDatabase.MigrationContainer {
     ctor public RoomDatabase.MigrationContainer();
     method public void addMigrations(androidx.room.migration.Migration!...);
+    method public void addMigrations(java.util.List<androidx.room.migration.Migration!>);
     method public java.util.List<androidx.room.migration.Migration!>? findMigrationPath(int, int);
   }
 
diff --git a/room/runtime/api/restricted_current.txt b/room/runtime/api/restricted_current.txt
index 9d4e534..8662b57 100644
--- a/room/runtime/api/restricted_current.txt
+++ b/room/runtime/api/restricted_current.txt
@@ -92,6 +92,7 @@
     method protected abstract androidx.room.InvalidationTracker createInvalidationTracker();
     method protected abstract androidx.sqlite.db.SupportSQLiteOpenHelper createOpenHelper(androidx.room.DatabaseConfiguration!);
     method @Deprecated public void endTransaction();
+    method protected java.util.List<androidx.room.migration.Migration!> getAutoMigrations();
     method public androidx.room.InvalidationTracker getInvalidationTracker();
     method public androidx.sqlite.db.SupportSQLiteOpenHelper getOpenHelper();
     method public java.util.concurrent.Executor getQueryExecutor();
@@ -151,6 +152,7 @@
   public static class RoomDatabase.MigrationContainer {
     ctor public RoomDatabase.MigrationContainer();
     method public void addMigrations(androidx.room.migration.Migration!...);
+    method public void addMigrations(java.util.List<androidx.room.migration.Migration!>);
     method public java.util.List<androidx.room.migration.Migration!>? findMigrationPath(int, int);
   }
 
diff --git a/room/runtime/src/main/java/androidx/room/RoomDatabase.java b/room/runtime/src/main/java/androidx/room/RoomDatabase.java
index 9b652bf..3411a26 100644
--- a/room/runtime/src/main/java/androidx/room/RoomDatabase.java
+++ b/room/runtime/src/main/java/androidx/room/RoomDatabase.java
@@ -45,6 +45,7 @@
 import java.io.File;
 import java.io.InputStream;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.BitSet;
 import java.util.Collections;
 import java.util.HashMap;
@@ -184,6 +185,10 @@
     @CallSuper
     public void init(@NonNull DatabaseConfiguration configuration) {
         mOpenHelper = createOpenHelper(configuration);
+        List<Migration> autoMigrations = getAutoMigrations();
+        if (autoMigrations.size() > 0) {
+            configuration.migrationContainer.addMigrations(autoMigrations);
+        }
 
         // Configure SqliteCopyOpenHelper if it is available:
         SQLiteCopyOpenHelper copyOpenHelper = unwrapOpenHelper(SQLiteCopyOpenHelper.class,
@@ -256,6 +261,17 @@
         }
     }
 
+    @NonNull
+    /**
+     * Returns a list of {@link Migration} of a database that have been generated using
+     * {@link AutoMigration}.
+     *
+     * @return A list of migration instances each of which is a generated autoMigration
+     */
+    protected List<Migration> getAutoMigrations() {
+        return Arrays.asList();
+    }
+
     /**
      * Unwraps (delegating) open helpers until it finds clazz, otherwise returns null.
      *
@@ -1380,6 +1396,12 @@
             }
         }
 
+        public void addMigrations(@NonNull List<Migration> migrations) {
+            for (Migration migration : migrations) {
+                addMigration(migration);
+            }
+        }
+
         private void addMigration(Migration migration) {
             final int start = migration.startVersion;
             final int end = migration.endVersion;
@@ -1390,6 +1412,7 @@
             }
             Migration existing = targetMap.get(end);
             if (existing != null) {
+                // TODO: (b/182251019) Favor user specified migration over generated automigrations
                 Log.w(Room.LOG_TAG, "Overriding migration " + existing + " with " + migration);
             }
             targetMap.put(end, migration);