Fix measurement db migration to copy table by individual column.

Also add tests for every field in every table for each migration.

Also remove Source.DEDUP_KEYS from MeasurementTables and add it to
MeasurementTablesDepricated.

Bug: 269760724

Test: atest
Change-Id: I684d6d490e9b7899a978968ad860b8d09de8592b
diff --git a/adservices/service-core/java/com/android/adservices/data/measurement/MeasurementTables.java b/adservices/service-core/java/com/android/adservices/data/measurement/MeasurementTables.java
index cc2e012..b654f50 100644
--- a/adservices/service-core/java/com/android/adservices/data/measurement/MeasurementTables.java
+++ b/adservices/service-core/java/com/android/adservices/data/measurement/MeasurementTables.java
@@ -79,7 +79,6 @@
         String PUBLISHER_TYPE = "publisher_type";
         String APP_DESTINATION = "app_destination";
         String WEB_DESTINATION = "web_destination";
-        String DEDUP_KEYS = "dedup_keys";
         String EVENT_REPORT_DEDUP_KEYS = "event_report_dedup_keys";
         String AGGREGATE_REPORT_DEDUP_KEYS = "aggregate_report_dedup_keys";
         String EVENT_TIME = "event_time";
@@ -310,7 +309,7 @@
                     + " INTEGER, "
                     + SourceContract.STATUS
                     + " INTEGER, "
-                    + SourceContract.DEDUP_KEYS
+                    + MeasurementTablesDeprecated.Source.DEDUP_KEYS
                     + " TEXT, "
                     + SourceContract.SOURCE_TYPE
                     + " TEXT, "
diff --git a/adservices/service-core/java/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV6.java b/adservices/service-core/java/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV6.java
index af4040b..5bee1a4 100644
--- a/adservices/service-core/java/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV6.java
+++ b/adservices/service-core/java/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV6.java
@@ -50,10 +50,10 @@
         String.format(
                 "ALTER TABLE %1$s RENAME COLUMN %2$s TO %3$s",
                 MeasurementTables.SourceContract.TABLE,
-                MeasurementTables.SourceContract.DEDUP_KEYS,
+                MeasurementTablesDeprecated.Source.DEDUP_KEYS,
                 MeasurementTables.SourceContract.EVENT_REPORT_DEDUP_KEYS),
         String.format(
-                "ALTER TABLE %1$s ADD %2$s INTEGER",
+                "ALTER TABLE %1$s ADD %2$s TEXT",
                 MeasurementTables.SourceContract.TABLE,
                 MeasurementTables.SourceContract.AGGREGATE_REPORT_DEDUP_KEYS),
         String.format(
diff --git a/adservices/service-core/java/com/android/adservices/data/measurement/migration/MeasurementTablesDeprecated.java b/adservices/service-core/java/com/android/adservices/data/measurement/migration/MeasurementTablesDeprecated.java
index 2a3a8c0..6be12f3 100644
--- a/adservices/service-core/java/com/android/adservices/data/measurement/migration/MeasurementTablesDeprecated.java
+++ b/adservices/service-core/java/com/android/adservices/data/measurement/migration/MeasurementTablesDeprecated.java
@@ -28,6 +28,10 @@
         String SCHEDULED_TIME = "scheduled_time";
     }
 
+    public interface Source {
+        String DEDUP_KEYS = "dedup_keys";
+    }
+
     // Private constructor to prevent instantiation.
     private MeasurementTablesDeprecated() {
     }
diff --git a/adservices/service-core/java/com/android/adservices/data/measurement/migration/MigrationHelpers.java b/adservices/service-core/java/com/android/adservices/data/measurement/migration/MigrationHelpers.java
index d3ecf93..c035184 100644
--- a/adservices/service-core/java/com/android/adservices/data/measurement/migration/MigrationHelpers.java
+++ b/adservices/service-core/java/com/android/adservices/data/measurement/migration/MigrationHelpers.java
@@ -19,6 +19,11 @@
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 
+import com.android.adservices.LogUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
 /** Helper methods for database migrations. */
 public final class MigrationHelpers {
 
@@ -31,12 +36,29 @@
         if (!isTablePresent(db, tableName)) {
             return;
         }
-        String copyTableQuery =
+        String renameTableQuery =
                 String.format("ALTER TABLE %1$s RENAME TO %2$s", tableName, backupTableName);
-        db.execSQL(copyTableQuery);
+        db.execSQL(renameTableQuery);
         db.execSQL(createTableQuery);
+        List<String> backupTableColumnNames = getTableColumnNames(db, backupTableName);
+        List<String> newTableColumnNames = getTableColumnNames(db, backupTableName);
+
+        if (!newTableColumnNames.containsAll(backupTableColumnNames)) {
+            // Not all the columns in the old table are present in the new table.  Proceeding with
+            // an empty table.
+            LogUtil.e(
+                    "Error during measurement migration (copyAndUpdateTable()).  The new "
+                            + "table does not have all of the columns from the old table.");
+            return;
+        }
+
+        String columns = String.join(",", backupTableColumnNames);
+
         String insertValuesFromBackupQuery =
-                String.format("INSERT INTO %1$s SELECT * FROM %2$s", tableName, backupTableName);
+                String.format(
+                        "INSERT INTO %1$s (%2$s) SELECT %2$s FROM %3$s",
+                        tableName, columns, backupTableName);
+
         db.execSQL(insertValuesFromBackupQuery);
         String dropBackupTable = String.format("DROP TABLE %1$s", backupTableName);
         db.execSQL(dropBackupTable);
@@ -92,4 +114,15 @@
             return cursor.getCount() != 0;
         }
     }
+
+    private static List<String> getTableColumnNames(SQLiteDatabase db, String tableName) {
+        String getTableColumnNamesQuery = String.format("PRAGMA TABLE_INFO(%1$s)", tableName);
+        ArrayList<String> tableColumnNames = new ArrayList<>();
+        try (Cursor cursor = db.rawQuery(getTableColumnNamesQuery, null)) {
+            while (cursor.moveToNext()) {
+                tableColumnNames.add(cursor.getString(cursor.getColumnIndex("name")));
+            }
+        }
+        return tableColumnNames;
+    }
 }
diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/AbstractMeasurementDbMigratorTestBase.java b/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/AbstractMeasurementDbMigratorTestBase.java
index 731010a..0be02de 100644
--- a/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/AbstractMeasurementDbMigratorTestBase.java
+++ b/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/AbstractMeasurementDbMigratorTestBase.java
@@ -20,7 +20,9 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
+import android.content.ContentValues;
 import android.content.Context;
+import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 
 import androidx.test.core.app.ApplicationProvider;
@@ -86,6 +88,27 @@
 
     abstract AbstractMeasurementDbMigrator getTestSubject();
 
+    // Create our own method instead of using DatabaseUtils.cursorRowToContentValues because that
+    // one reads every type from the cursor as a String.
+    public static ContentValues cursorRowToContentValues(Cursor cursor) {
+        String[] columns = cursor.getColumnNames();
+        ContentValues values = new ContentValues();
+        for (int i = 0; i < columns.length; i++) {
+            switch (cursor.getType(i)) {
+                case Cursor.FIELD_TYPE_FLOAT:
+                    values.put(columns[i], cursor.getDouble(i));
+                    break;
+                case Cursor.FIELD_TYPE_INTEGER:
+                    values.put(columns[i], cursor.getLong(i));
+                    break;
+                case Cursor.FIELD_TYPE_STRING:
+                default:
+                    values.put(columns[i], cursor.getString(i));
+                    break;
+            }
+        }
+        return values;
+    }
 }
 
 
diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/ContentValueFixtures.java b/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/ContentValueFixtures.java
new file mode 100644
index 0000000..6766e8c
--- /dev/null
+++ b/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/ContentValueFixtures.java
@@ -0,0 +1,644 @@
+/*
+ * Copyright (C) 2023 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.adservices.data.measurement.migration;
+
+import android.content.ContentValues;
+
+import com.android.adservices.data.measurement.MeasurementTables;
+import com.android.adservices.service.measurement.AsyncRegistration;
+import com.android.adservices.service.measurement.EventReport;
+import com.android.adservices.service.measurement.EventSurfaceType;
+import com.android.adservices.service.measurement.Source;
+import com.android.adservices.service.measurement.Trigger;
+import com.android.adservices.service.measurement.aggregation.AggregateReport;
+
+public class ContentValueFixtures {
+
+    public static class AsyncRegistrationValues {
+        public static final String ID = "async_registration_id";
+        public static final String REGISTRATION_URI = "android-app://com.registration";
+        public static final String WEB_DESTINATION = "https://com.web.destination";
+        public static final String OS_DESTINATION = "android-app://com.os.destination";
+        public static final String VERIFIED_DESTINATION = "android-app://com.verified.destination";
+        public static final String TOP_ORIGIN = "android-app://com.top.origin";
+        public static final long REDIRECT = AsyncRegistration.RedirectType.DAISY_CHAIN;
+        public static final long INPUT_EVENT = 1;
+        public static final String REGISTRANT = "android-app://com.registrant";
+        public static final long SCHEDULED_TIME = 8640000000L;
+        public static final long RETRY_COUNT = 4;
+        public static final long LAST_PROCESSING_TIME = 8650000000L;
+        public static final long TYPE = AsyncRegistration.RegistrationType.WEB_TRIGGER.ordinal();
+
+        // Added in V3.
+        public static final String ENROLLMENT_ID = "enrollment-id";
+        public static final long REDIRECT_TYPE = AsyncRegistration.RedirectType.DAISY_CHAIN;
+        public static final long REDIRECT_COUNT = 10;
+        public static final long SOURCE_TYPE = Source.SourceType.NAVIGATION.ordinal();
+        public static final long REQUEST_TIME = 8660000000L;
+        public static final long DEBUG_KEY_ALLOWED = 1;
+        public static final long AD_ID_PERMISSION = 0;
+
+        // Added in V6.
+        public static final String REGISTRATION_ID = "xna_registration_id";
+    }
+
+    public static class SourceValues {
+        public static final String ID = "source_id";
+        public static final long EVENT_ID = 123L;
+        public static final String PUBLISHER = "android-app://com.publisher";
+        public static final long PUBLISHER_TYPE = 5;
+        public static final String APP_DESTINATION = "android-app://com.destination";
+        public static final String ENROLLMENT_ID = "enrollment_id";
+        public static final long EVENT_TIME = 8640000000L;
+        public static final long EXPIRY_TIME = 8640000010L;
+        public static final long PRIORITY = 100L;
+        public static final long STATUS = Source.Status.MARKED_TO_DELETE;
+        public static final String DEDUP_KEYS = "1001";
+        public static final String SOURCE_TYPE = Source.SourceType.EVENT.toString();
+        public static final String REGISTRANT = "android-app://com.registrant";
+        public static final long ATTRIBUTION_MODE = Source.AttributionMode.FALSELY;
+        public static final long INSTALL_ATTRIBUTION_WINDOW = 841839879274L;
+        public static final long INSTALL_COOLDOWN_WINDOW = 8418398274L;
+        public static final long IS_INSTALL_ATTRIBUTED = 1;
+        public static final String FILTER_DATA = "test filter data";
+        public static final String AGGREGATE_SOURCE_V2_AND_BELOW =
+                "[{\"id\": \"campaignCounts\",\"key_piece\": \"0x159\"},"
+                        + "{\"id\": \"geoValue\",\"key_piece\": \"0x5\"}]";
+        public static final long AGGREGATE_CONTRIBUTIONS = 6;
+        public static final String WEB_DESTINATION = "https://destination.com";
+        public static final long DEBUG_KEY = 12345L;
+
+        // Added in V3.
+        public static final String AGGREGATE_SOURCE_V3 =
+                "{\"geoValue\":\"0x5\",\"campaignCounts\":\"0x159\"}";
+        public static final long DEBUG_REPORTING = 10;
+        public static final long AD_ID_PERMISSION = 11;
+        public static final long AR_DEBUG_PERMISSION = 12;
+
+        // Added in V6.
+        public static final String EVENT_REPORT_DEDUP_KEY = "1001";
+        public static final String AGGREGATE_REPORT_DEDUP_KEY = "2002";
+        public static final long EVENT_REPORT_WINDOW = 400000L;
+        public static final long AGGREGATE_REPORT_WINDOW = 500000L;
+        public static final String REGISTRATION_ID = "xna_registration_id";
+        public static final String SHARED_AGGREGATION_KEY = "shared_aggregation_key";
+        public static final long INSTALL_TIME = 8660000000L;
+    }
+
+    public static class TriggerValues {
+        public static final String ID = "trigger_id";
+        public static final String ATTRIBUTION_DESTINATION = "android-app://com.destination";
+        public static final long DESTINATION_TYPE = EventSurfaceType.WEB;
+        public static final String ENROLLMENT_ID = "enrollment_id";
+        public static final long TRIGGER_TIME = 8630000000L;
+        public static final String FILTERS_V2_AND_BELOW = "{\"id1\":[\"val11\",\"val12\"]}";
+        public static final String EVENT_TRIGGERS_V2_AND_BELOW =
+                "[{\"trigger_data\":2,\"filters\":"
+                        + FILTERS_V2_AND_BELOW
+                        + ",\"not_filters\":"
+                        + FILTERS_V2_AND_BELOW
+                        + "},"
+                        + "{\"priority\":100}]";
+        public static final long STATUS = Trigger.Status.MARKED_TO_DELETE;
+        public static final String REGISTRANT = "android-app://com.registrant";
+        public static final String AGGREGATE_TRIGGER_DATA_V2_AND_BELOW =
+                "[{\"key_piece\":\"0x400\",\"source_keys\":[\"key11\"],"
+                        + "\"filters\":"
+                        + FILTERS_V2_AND_BELOW
+                        + ",\"not_filters\":"
+                        + FILTERS_V2_AND_BELOW
+                        + "}]";
+        public static final String AGGREGATE_VALUES =
+                "{" + "\"campaignCounts\":32768," + "\"geoValue\":1664" + "}";
+        public static final long DEBUG_KEY = 27836L;
+
+        // Added in V3
+        public static final String NOT_FILTERS = "{\"id2\":[\"val21\",\"val22\"]}";
+        public static final String FILTERS_V3 = "[" + FILTERS_V2_AND_BELOW + "]";
+        public static final String EVENT_TRIGGERS_V3 =
+                "[{\"trigger_data\":2,\"filters\":"
+                        + FILTERS_V3
+                        + ",\"not_filters\":"
+                        + FILTERS_V3
+                        + "},"
+                        + "{\"priority\":100,\"trigger_data\":\"0\"}]";
+        public static final String AGGREGATE_TRIGGER_DATA_V3 =
+                "[{\"key_piece\":\"0x400\",\"source_keys\":[\"key11\"],"
+                        + "\"filters\":"
+                        + FILTERS_V3
+                        + ",\"not_filters\":"
+                        + FILTERS_V3
+                        + "}]";
+        public static final long DEBUG_REPORTING = 10;
+        public static final long AD_ID_PERMISSION = 11;
+        public static final long AR_DEBUG_PERMISSION = 12;
+
+        // Added in V6.
+        public static final String ATTRIBUTION_CONFIG = "attribution_config";
+        public static final String X_NETWORK_KEY_MAPPING = "x_network_key_mapping";
+        public static final String AGGREGATABLE_DEDUPLICATION_KEYS =
+                "aggregatable_deduplication_keys";
+    }
+
+    public static class AttributionValues {
+        public static final String ID = "attribution_id";
+        public static final String SOURCE_SITE = "android-app://com.source-site";
+        public static final String SOURCE_ORIGIN = "android-app://com.source-origin";
+        public static final String DESTINATION_SITE = "android-app://com.destination-site";
+        public static final String DESTINATION_ORIGIN = "android-app://com.destination-origin";
+        public static final String ENROLLMENT_ID = "enrollment_id";
+        public static final long TRIGGER_TIME = 8630000000L;
+        public static final String REGISTRANT = "android-app://com.registrant";
+
+        // Added in V3.
+        public static final String SOURCE_ID = "source_id";
+        public static final String TRIGGER_ID = "trigger_id";
+    }
+
+    public static class EventReportValues {
+        public static final String ID = "event_report_id";
+        public static final long SOURCE_ID_V2_AND_BELOW = 123L;
+        public static final String ENROLLMENT_ID = "enrollment_id";
+        public static final String ATTRIBUTION_DESTINATION = "android-app://com.destination";
+        public static final long REPORT_TIME = 8640000000L;
+        public static final long TRIGGER_DATA = 1;
+        public static final long TRIGGER_PRIORITY = 100L;
+        public static final long TRIGGER_DEDUP_KEY = 56;
+        public static final long TRIGGER_TIME = 8630000000L;
+        public static final long STATUS = EventReport.Status.MARKED_TO_DELETE;
+        public static final String SOURCE_TYPE = Source.SourceType.EVENT.toString();
+        public static final double RANDOMIZED_TRIGGER_RATE = 0.0000025F;
+
+        // Added in V2
+        public static final long SOURCE_DEBUG_KEY = 12345L;
+        public static final long TRIGGER_DEBUG_KEY = 34567L;
+
+        // Added in V3
+        public static final long SOURCE_EVENT_ID = SOURCE_ID_V2_AND_BELOW;
+        public static final String SOURCE_ID_V3 = "source_id";
+        public static final String TRIGGER_ID = "trigger_id";
+        public static final long DEBUG_REPORT_STATUS = 4;
+    }
+
+    public static class AggregateReportValues {
+        public static final String ID = "aggregate_report_id";
+        public static final String PUBLISHER = "android-app://com.publisher";
+        public static final String ATTRIBUTION_DESTINATION = "android-app://com.destination";
+        public static final long SOURCE_REGISTRATION_TIME = 8640000000L;
+        public static final long SCHEDULED_REPORT_TIME = 8640000200L;
+        public static final String ENROLLMENT_ID = "enrollment_id";
+        public static final String DEBUG_CLEARTEXT_PAYLOAD = "payload";
+        public static final long STATUS = AggregateReport.Status.MARKED_TO_DELETE;
+        public static final String API_VERSION = "api_version";
+
+        // Added in V2
+        public static final long SOURCE_DEBUG_KEY = 12345L;
+        public static final long TRIGGER_DEBUG_KEY = 34567L;
+
+        // Added in V3.
+        public static final String SOURCE_ID = "source_id";
+        public static final String TRIGGER_ID = "trigger_id";
+        public static final long DEBUG_REPORT_STATUS = 4;
+    }
+
+    public static class AggregateEncryptionKeyValues {
+        public static final String ID = "aggregate_encryption_key_id";
+        public static final String KEY_ID = "key_id";
+        public static final String PUBLIC_KEY = "public_key";
+        public static final long EXPIRY = 8640000000L;
+    }
+
+    public static class DebugReportValues {
+        // Added in V3.
+        public static final String ID = "debug_report_id";
+        public static final String TYPE = "source-noised";
+        public static final String BODY = "{\"source_event_id\":\"123\"}";
+        public static final String ENROLLMENT_ID = "enrollment_id";
+    }
+
+    public static ContentValues generateAsyncRegistrationContentValuesV1() {
+        ContentValues asyncRegistration = new ContentValues();
+
+        asyncRegistration.put(
+                MeasurementTables.AsyncRegistrationContract.ID, AsyncRegistrationValues.ID);
+        asyncRegistration.put(
+                MeasurementTables.AsyncRegistrationContract.REGISTRATION_URI,
+                AsyncRegistrationValues.REGISTRATION_URI);
+        asyncRegistration.put(
+                MeasurementTables.AsyncRegistrationContract.WEB_DESTINATION,
+                AsyncRegistrationValues.WEB_DESTINATION);
+        asyncRegistration.put(
+                MeasurementTables.AsyncRegistrationContract.OS_DESTINATION,
+                AsyncRegistrationValues.OS_DESTINATION);
+        asyncRegistration.put(
+                MeasurementTables.AsyncRegistrationContract.VERIFIED_DESTINATION,
+                AsyncRegistrationValues.VERIFIED_DESTINATION);
+        asyncRegistration.put(
+                MeasurementTables.AsyncRegistrationContract.TOP_ORIGIN,
+                AsyncRegistrationValues.TOP_ORIGIN);
+        asyncRegistration.put(
+                MeasurementTablesDeprecated.AsyncRegistration.REDIRECT,
+                AsyncRegistrationValues.REDIRECT);
+        asyncRegistration.put(
+                MeasurementTablesDeprecated.AsyncRegistration.INPUT_EVENT,
+                AsyncRegistrationValues.INPUT_EVENT);
+        asyncRegistration.put(
+                MeasurementTables.AsyncRegistrationContract.REGISTRANT,
+                AsyncRegistrationValues.REGISTRANT);
+        asyncRegistration.put(
+                MeasurementTablesDeprecated.AsyncRegistration.SCHEDULED_TIME,
+                AsyncRegistrationValues.SCHEDULED_TIME);
+        asyncRegistration.put(
+                MeasurementTables.AsyncRegistrationContract.RETRY_COUNT,
+                AsyncRegistrationValues.RETRY_COUNT);
+        asyncRegistration.put(
+                MeasurementTables.AsyncRegistrationContract.LAST_PROCESSING_TIME,
+                AsyncRegistrationValues.LAST_PROCESSING_TIME);
+        asyncRegistration.put(
+                MeasurementTables.AsyncRegistrationContract.TYPE, AsyncRegistrationValues.TYPE);
+
+        return asyncRegistration;
+    }
+
+    public static ContentValues generateSourceContentValuesV1() {
+        ContentValues source = new ContentValues();
+
+        source.put(MeasurementTables.SourceContract.ID, SourceValues.ID);
+        source.put(MeasurementTables.SourceContract.EVENT_ID, SourceValues.EVENT_ID);
+        source.put(MeasurementTables.SourceContract.PUBLISHER, SourceValues.PUBLISHER);
+        source.put(MeasurementTables.SourceContract.PUBLISHER_TYPE, SourceValues.PUBLISHER_TYPE);
+        source.put(MeasurementTables.SourceContract.APP_DESTINATION, SourceValues.APP_DESTINATION);
+        source.put(MeasurementTables.SourceContract.ENROLLMENT_ID, SourceValues.ENROLLMENT_ID);
+        source.put(MeasurementTables.SourceContract.EVENT_TIME, SourceValues.EVENT_TIME);
+        source.put(MeasurementTables.SourceContract.EXPIRY_TIME, SourceValues.EXPIRY_TIME);
+        source.put(MeasurementTables.SourceContract.PRIORITY, SourceValues.PRIORITY);
+        source.put(MeasurementTables.SourceContract.STATUS, SourceValues.STATUS);
+        source.put(MeasurementTablesDeprecated.Source.DEDUP_KEYS, SourceValues.DEDUP_KEYS);
+        source.put(MeasurementTables.SourceContract.SOURCE_TYPE, SourceValues.SOURCE_TYPE);
+        source.put(MeasurementTables.SourceContract.REGISTRANT, SourceValues.REGISTRANT);
+        source.put(
+                MeasurementTables.SourceContract.ATTRIBUTION_MODE, SourceValues.ATTRIBUTION_MODE);
+        source.put(
+                MeasurementTables.SourceContract.INSTALL_ATTRIBUTION_WINDOW,
+                SourceValues.INSTALL_ATTRIBUTION_WINDOW);
+        source.put(
+                MeasurementTables.SourceContract.INSTALL_COOLDOWN_WINDOW,
+                SourceValues.INSTALL_COOLDOWN_WINDOW);
+        source.put(
+                MeasurementTables.SourceContract.IS_INSTALL_ATTRIBUTED,
+                SourceValues.IS_INSTALL_ATTRIBUTED);
+        source.put(MeasurementTables.SourceContract.FILTER_DATA, SourceValues.FILTER_DATA);
+        source.put(
+                MeasurementTables.SourceContract.AGGREGATE_SOURCE,
+                SourceValues.AGGREGATE_SOURCE_V2_AND_BELOW);
+        source.put(
+                MeasurementTables.SourceContract.AGGREGATE_CONTRIBUTIONS,
+                SourceValues.AGGREGATE_CONTRIBUTIONS);
+        source.put(MeasurementTables.SourceContract.WEB_DESTINATION, SourceValues.WEB_DESTINATION);
+        source.put(MeasurementTables.SourceContract.DEBUG_KEY, SourceValues.DEBUG_KEY);
+
+        return source;
+    }
+
+    public static ContentValues generateTriggerContentValuesV1() {
+        ContentValues trigger = new ContentValues();
+
+        trigger.put(MeasurementTables.TriggerContract.ID, TriggerValues.ID);
+        trigger.put(
+                MeasurementTables.TriggerContract.ATTRIBUTION_DESTINATION,
+                TriggerValues.ATTRIBUTION_DESTINATION);
+        trigger.put(
+                MeasurementTables.TriggerContract.DESTINATION_TYPE, TriggerValues.DESTINATION_TYPE);
+        trigger.put(MeasurementTables.TriggerContract.ENROLLMENT_ID, TriggerValues.ENROLLMENT_ID);
+        trigger.put(MeasurementTables.TriggerContract.TRIGGER_TIME, TriggerValues.TRIGGER_TIME);
+        trigger.put(
+                MeasurementTables.TriggerContract.EVENT_TRIGGERS,
+                TriggerValues.EVENT_TRIGGERS_V2_AND_BELOW);
+        trigger.put(MeasurementTables.TriggerContract.STATUS, TriggerValues.STATUS);
+        trigger.put(MeasurementTables.TriggerContract.REGISTRANT, TriggerValues.REGISTRANT);
+        trigger.put(
+                MeasurementTables.TriggerContract.AGGREGATE_TRIGGER_DATA,
+                TriggerValues.AGGREGATE_TRIGGER_DATA_V2_AND_BELOW);
+        trigger.put(
+                MeasurementTables.TriggerContract.AGGREGATE_VALUES, TriggerValues.AGGREGATE_VALUES);
+        trigger.put(MeasurementTables.TriggerContract.FILTERS, TriggerValues.FILTERS_V2_AND_BELOW);
+        trigger.put(MeasurementTables.TriggerContract.DEBUG_KEY, TriggerValues.DEBUG_KEY);
+
+        return trigger;
+    }
+
+    public static ContentValues generateAttributionContentValuesV1() {
+        ContentValues attribution = new ContentValues();
+
+        attribution.put(MeasurementTables.AttributionContract.ID, AttributionValues.ID);
+        attribution.put(
+                MeasurementTables.AttributionContract.SOURCE_SITE, AttributionValues.SOURCE_SITE);
+        attribution.put(
+                MeasurementTables.AttributionContract.SOURCE_ORIGIN,
+                AttributionValues.SOURCE_ORIGIN);
+        attribution.put(
+                MeasurementTables.AttributionContract.DESTINATION_SITE,
+                AttributionValues.DESTINATION_SITE);
+        attribution.put(
+                MeasurementTables.AttributionContract.DESTINATION_ORIGIN,
+                AttributionValues.DESTINATION_ORIGIN);
+        attribution.put(
+                MeasurementTables.AttributionContract.ENROLLMENT_ID,
+                AttributionValues.ENROLLMENT_ID);
+        attribution.put(
+                MeasurementTables.AttributionContract.TRIGGER_TIME, AttributionValues.TRIGGER_TIME);
+        attribution.put(
+                MeasurementTables.AttributionContract.REGISTRANT, AttributionValues.REGISTRANT);
+
+        return attribution;
+    }
+
+    public static ContentValues generateEventReportContentValuesV1() {
+        ContentValues eventReport = new ContentValues();
+
+        eventReport.put(MeasurementTables.EventReportContract.ID, EventReportValues.ID);
+        eventReport.put(
+                MeasurementTables.EventReportContract.SOURCE_ID,
+                EventReportValues.SOURCE_ID_V2_AND_BELOW);
+        eventReport.put(
+                MeasurementTables.EventReportContract.ENROLLMENT_ID,
+                EventReportValues.ENROLLMENT_ID);
+        eventReport.put(
+                MeasurementTables.EventReportContract.ATTRIBUTION_DESTINATION,
+                EventReportValues.ATTRIBUTION_DESTINATION);
+        eventReport.put(
+                MeasurementTables.EventReportContract.REPORT_TIME, EventReportValues.REPORT_TIME);
+        eventReport.put(
+                MeasurementTables.EventReportContract.TRIGGER_DATA, EventReportValues.TRIGGER_DATA);
+        eventReport.put(
+                MeasurementTables.EventReportContract.TRIGGER_PRIORITY,
+                EventReportValues.TRIGGER_PRIORITY);
+        eventReport.put(
+                MeasurementTables.EventReportContract.TRIGGER_DEDUP_KEY,
+                EventReportValues.TRIGGER_DEDUP_KEY);
+        eventReport.put(
+                MeasurementTables.EventReportContract.TRIGGER_TIME, EventReportValues.TRIGGER_TIME);
+        eventReport.put(MeasurementTables.EventReportContract.STATUS, EventReportValues.STATUS);
+        eventReport.put(
+                MeasurementTables.EventReportContract.SOURCE_TYPE, EventReportValues.SOURCE_TYPE);
+        eventReport.put(
+                MeasurementTables.EventReportContract.RANDOMIZED_TRIGGER_RATE,
+                EventReportValues.RANDOMIZED_TRIGGER_RATE);
+
+        return eventReport;
+    }
+
+    public static ContentValues generateAggregateReportContentValuesV1() {
+        ContentValues aggregateReport = new ContentValues();
+
+        aggregateReport.put(MeasurementTables.AggregateReport.ID, AggregateReportValues.ID);
+        aggregateReport.put(
+                MeasurementTables.AggregateReport.PUBLISHER, AggregateReportValues.PUBLISHER);
+        aggregateReport.put(
+                MeasurementTables.AggregateReport.ATTRIBUTION_DESTINATION,
+                AggregateReportValues.ATTRIBUTION_DESTINATION);
+        aggregateReport.put(
+                MeasurementTables.AggregateReport.SOURCE_REGISTRATION_TIME,
+                AggregateReportValues.SOURCE_REGISTRATION_TIME);
+        aggregateReport.put(
+                MeasurementTables.AggregateReport.SCHEDULED_REPORT_TIME,
+                AggregateReportValues.SCHEDULED_REPORT_TIME);
+        aggregateReport.put(
+                MeasurementTables.AggregateReport.ENROLLMENT_ID,
+                AggregateReportValues.ENROLLMENT_ID);
+        aggregateReport.put(
+                MeasurementTables.AggregateReport.DEBUG_CLEARTEXT_PAYLOAD,
+                AggregateReportValues.DEBUG_CLEARTEXT_PAYLOAD);
+        aggregateReport.put(MeasurementTables.AggregateReport.STATUS, AggregateReportValues.STATUS);
+        aggregateReport.put(
+                MeasurementTables.AggregateReport.API_VERSION, AggregateReportValues.API_VERSION);
+
+        return aggregateReport;
+    }
+
+    public static ContentValues generateAggregateEncryptionKeyContentValuesV1() {
+        ContentValues aggregateEncryptionKey = new ContentValues();
+
+        aggregateEncryptionKey.put(
+                MeasurementTables.AggregateEncryptionKey.ID, AggregateEncryptionKeyValues.ID);
+        aggregateEncryptionKey.put(
+                MeasurementTables.AggregateEncryptionKey.KEY_ID,
+                AggregateEncryptionKeyValues.KEY_ID);
+        aggregateEncryptionKey.put(
+                MeasurementTables.AggregateEncryptionKey.PUBLIC_KEY,
+                AggregateEncryptionKeyValues.PUBLIC_KEY);
+        aggregateEncryptionKey.put(
+                MeasurementTables.AggregateEncryptionKey.EXPIRY,
+                AggregateEncryptionKeyValues.EXPIRY);
+
+        return aggregateEncryptionKey;
+    }
+
+    public static ContentValues generateAsyncRegistrationContentValuesV2() {
+        // No differences in async registration fields between V1 and V2.
+        return generateAsyncRegistrationContentValuesV1();
+    }
+
+    public static ContentValues generateSourceContentValuesV2() {
+        // No differences in source fields between V1 and V2.
+        return generateSourceContentValuesV1();
+    }
+
+    public static ContentValues generateTriggerContentValuesV2() {
+        // No differences in trigger fields between V1 and V2.
+        return generateTriggerContentValuesV1();
+    }
+
+    public static ContentValues generateAttributionContentValuesV2() {
+        // No differences in attribution fields between V1 and V2.
+        return generateAttributionContentValuesV1();
+    }
+
+    public static ContentValues generateEventReportContentValuesV2() {
+        ContentValues eventReport = generateEventReportContentValuesV1();
+
+        eventReport.put(
+                MeasurementTables.EventReportContract.SOURCE_DEBUG_KEY,
+                EventReportValues.SOURCE_DEBUG_KEY);
+        eventReport.put(
+                MeasurementTables.EventReportContract.TRIGGER_DEBUG_KEY,
+                EventReportValues.TRIGGER_DEBUG_KEY);
+
+        return eventReport;
+    }
+
+    public static ContentValues generateAggregateReportContentValuesV2() {
+        ContentValues aggregateReport = generateAggregateReportContentValuesV1();
+
+        aggregateReport.put(
+                MeasurementTables.AggregateReport.SOURCE_DEBUG_KEY,
+                AggregateReportValues.SOURCE_DEBUG_KEY);
+        aggregateReport.put(
+                MeasurementTables.AggregateReport.TRIGGER_DEBUG_KEY,
+                AggregateReportValues.TRIGGER_DEBUG_KEY);
+
+        return aggregateReport;
+    }
+
+    public static ContentValues generateAggregateEncryptionKeyContentValuesV2() {
+        // No differences in aggregate encryption key fields between V1 and V2.
+        return generateAggregateEncryptionKeyContentValuesV1();
+    }
+
+    public static ContentValues generateAsyncRegistrationContentValuesV3() {
+        ContentValues asyncRegistration = generateAsyncRegistrationContentValuesV2();
+
+        // Remove columns.
+        asyncRegistration.remove(MeasurementTablesDeprecated.AsyncRegistration.REDIRECT);
+        asyncRegistration.remove(MeasurementTablesDeprecated.AsyncRegistration.INPUT_EVENT);
+        asyncRegistration.remove(MeasurementTablesDeprecated.AsyncRegistration.SCHEDULED_TIME);
+
+        // Add columns.
+        asyncRegistration.put(
+                MeasurementTables.AsyncRegistrationContract.ENROLLMENT_ID,
+                AsyncRegistrationValues.ENROLLMENT_ID);
+        asyncRegistration.put(
+                MeasurementTables.AsyncRegistrationContract.REDIRECT_TYPE,
+                AsyncRegistrationValues.REDIRECT_TYPE);
+        asyncRegistration.put(
+                MeasurementTables.AsyncRegistrationContract.REDIRECT_COUNT,
+                AsyncRegistrationValues.REDIRECT_COUNT);
+        asyncRegistration.put(
+                MeasurementTables.AsyncRegistrationContract.SOURCE_TYPE,
+                AsyncRegistrationValues.SOURCE_TYPE);
+        asyncRegistration.put(
+                MeasurementTables.AsyncRegistrationContract.REQUEST_TIME,
+                AsyncRegistrationValues.REQUEST_TIME);
+        asyncRegistration.put(
+                MeasurementTables.AsyncRegistrationContract.DEBUG_KEY_ALLOWED,
+                AsyncRegistrationValues.DEBUG_KEY_ALLOWED);
+        asyncRegistration.put(
+                MeasurementTables.AsyncRegistrationContract.AD_ID_PERMISSION,
+                AsyncRegistrationValues.AD_ID_PERMISSION);
+
+        return asyncRegistration;
+    }
+
+    public static ContentValues generateSourceContentValuesV3() {
+        ContentValues source = generateSourceContentValuesV2();
+
+        // Update columns.
+        source.put(
+                MeasurementTables.SourceContract.AGGREGATE_SOURCE,
+                SourceValues.AGGREGATE_SOURCE_V3);
+
+        // Add columns.
+        source.put(MeasurementTables.SourceContract.DEBUG_REPORTING, SourceValues.DEBUG_REPORTING);
+        source.put(
+                MeasurementTables.SourceContract.AD_ID_PERMISSION, SourceValues.AD_ID_PERMISSION);
+        source.put(
+                MeasurementTables.SourceContract.AR_DEBUG_PERMISSION,
+                SourceValues.AR_DEBUG_PERMISSION);
+
+        return source;
+    }
+
+    public static ContentValues generateTriggerContentValuesV3() {
+        // No differences in trigger fields between V1 and V2.
+        ContentValues trigger = generateTriggerContentValuesV2();
+
+        // Update values.
+        trigger.put(
+                MeasurementTables.TriggerContract.EVENT_TRIGGERS, TriggerValues.EVENT_TRIGGERS_V3);
+        trigger.put(
+                MeasurementTables.TriggerContract.AGGREGATE_TRIGGER_DATA,
+                TriggerValues.AGGREGATE_TRIGGER_DATA_V3);
+        trigger.put(MeasurementTables.TriggerContract.FILTERS, TriggerValues.FILTERS_V3);
+
+        // Add columns.
+        trigger.put(MeasurementTables.TriggerContract.NOT_FILTERS, TriggerValues.NOT_FILTERS);
+        trigger.put(
+                MeasurementTables.TriggerContract.DEBUG_REPORTING, TriggerValues.DEBUG_REPORTING);
+        trigger.put(
+                MeasurementTables.TriggerContract.AD_ID_PERMISSION, TriggerValues.AD_ID_PERMISSION);
+        trigger.put(
+                MeasurementTables.TriggerContract.AR_DEBUG_PERMISSION,
+                TriggerValues.AR_DEBUG_PERMISSION);
+
+        return trigger;
+    }
+
+    public static ContentValues generateAttributionContentValuesV3() {
+        ContentValues attribution = generateAttributionContentValuesV2();
+
+        // Add columns.
+        attribution.put(
+                MeasurementTables.AttributionContract.SOURCE_ID, AttributionValues.SOURCE_ID);
+        attribution.put(
+                MeasurementTables.AttributionContract.TRIGGER_ID, AttributionValues.TRIGGER_ID);
+
+        return attribution;
+    }
+
+    public static ContentValues generateEventReportContentValuesV3() {
+        ContentValues eventReport = generateEventReportContentValuesV2();
+
+        // Update column.
+        eventReport.put(
+                MeasurementTables.EventReportContract.SOURCE_ID, EventReportValues.SOURCE_ID_V3);
+
+        // Add columns.
+        eventReport.put(
+                MeasurementTables.EventReportContract.SOURCE_EVENT_ID,
+                EventReportValues.SOURCE_EVENT_ID);
+        eventReport.put(
+                MeasurementTables.EventReportContract.TRIGGER_ID, EventReportValues.TRIGGER_ID);
+        eventReport.put(
+                MeasurementTables.EventReportContract.DEBUG_REPORT_STATUS,
+                EventReportValues.DEBUG_REPORT_STATUS);
+
+        return eventReport;
+    }
+
+    public static ContentValues generateAggregateReportContentValuesV3() {
+        ContentValues aggregateReport = generateAggregateReportContentValuesV2();
+
+        // Add columns.
+        aggregateReport.put(
+                MeasurementTables.AggregateReport.SOURCE_ID, AggregateReportValues.SOURCE_ID);
+        aggregateReport.put(
+                MeasurementTables.AggregateReport.TRIGGER_ID, AggregateReportValues.TRIGGER_ID);
+        aggregateReport.put(
+                MeasurementTables.AggregateReport.DEBUG_REPORT_STATUS,
+                AggregateReportValues.DEBUG_REPORT_STATUS);
+
+        return aggregateReport;
+    }
+
+    public static ContentValues generateAggregateEncryptionKeyContentValuesV3() {
+        // No differences in aggregate encryption key fields between V2 and V3.
+        return generateAggregateEncryptionKeyContentValuesV2();
+    }
+
+    public static ContentValues generateDebugReportContentValuesV3() {
+        ContentValues debugReport = new ContentValues();
+
+        debugReport.put(MeasurementTables.DebugReportContract.ID, DebugReportValues.ID);
+        debugReport.put(MeasurementTables.DebugReportContract.TYPE, DebugReportValues.TYPE);
+        debugReport.put(MeasurementTables.DebugReportContract.BODY, DebugReportValues.BODY);
+        debugReport.put(
+                MeasurementTables.DebugReportContract.ENROLLMENT_ID,
+                DebugReportValues.ENROLLMENT_ID);
+
+        return debugReport;
+    }
+}
diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV2Test.java b/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV2Test.java
index 5dc0b87..5eaf278 100644
--- a/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV2Test.java
+++ b/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV2Test.java
@@ -17,9 +17,14 @@
 package com.android.adservices.data.measurement.migration;
 
 import static com.android.adservices.data.DbTestUtil.doesTableExistAndColumnCountMatch;
+import static com.android.adservices.data.DbTestUtil.getTableColumns;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 
+import android.content.ContentValues;
+import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 
 import com.android.adservices.data.DbHelper;
@@ -52,6 +57,312 @@
                 doesTableExistAndColumnCountMatch(db, MeasurementTables.AggregateReport.TABLE, 11));
     }
 
+    @Test
+    public void performMigration_success_checkAllFields_V1toV2() {
+        // Setup
+        DbHelper dbHelper = getDbHelper(1);
+        SQLiteDatabase db = dbHelper.getWritableDatabase();
+
+        // Insert ContentValues with all fields for all tables in V1
+
+        // Async Registration
+        db.insert(
+                MeasurementTables.AsyncRegistrationContract.TABLE,
+                /* nullColumnHack */ null,
+                ContentValueFixtures.generateAsyncRegistrationContentValuesV1());
+        assertEquals(
+                getTableColumns(db, MeasurementTables.AsyncRegistrationContract.TABLE).size(),
+                ContentValueFixtures.generateAsyncRegistrationContentValuesV1().size());
+
+        // Source
+        db.insert(
+                MeasurementTables.SourceContract.TABLE,
+                /* nullColumnHack */ null,
+                ContentValueFixtures.generateSourceContentValuesV1());
+        assertEquals(
+                getTableColumns(db, MeasurementTables.SourceContract.TABLE).size(),
+                ContentValueFixtures.generateSourceContentValuesV1().size());
+
+        // Trigger
+        db.insert(
+                MeasurementTables.TriggerContract.TABLE,
+                /* nullColumnHack */ null,
+                ContentValueFixtures.generateTriggerContentValuesV1());
+        assertEquals(
+                getTableColumns(db, MeasurementTables.TriggerContract.TABLE).size(),
+                ContentValueFixtures.generateTriggerContentValuesV1().size());
+
+        // Attribution
+        assertEquals(
+                getTableColumns(db, MeasurementTables.AttributionContract.TABLE).size(),
+                ContentValueFixtures.generateAttributionContentValuesV1().size());
+        db.insert(
+                MeasurementTables.AttributionContract.TABLE,
+                /* nullColumnHack */ null,
+                ContentValueFixtures.generateAttributionContentValuesV1());
+
+        // Event Report
+        db.insert(
+                MeasurementTables.EventReportContract.TABLE,
+                /* nullColumnHack */ null,
+                ContentValueFixtures.generateEventReportContentValuesV1());
+        assertEquals(
+                getTableColumns(db, MeasurementTables.EventReportContract.TABLE).size(),
+                ContentValueFixtures.generateEventReportContentValuesV1().size());
+
+        // Aggregate Report
+        db.insert(
+                MeasurementTables.AggregateReport.TABLE,
+                /* nullColumnHack */ null,
+                ContentValueFixtures.generateAggregateReportContentValuesV1());
+        assertEquals(
+                getTableColumns(db, MeasurementTables.AggregateReport.TABLE).size(),
+                ContentValueFixtures.generateAggregateReportContentValuesV1().size());
+
+        // Aggregate Encryption Key
+        db.insert(
+                MeasurementTables.AggregateEncryptionKey.TABLE,
+                /* nullColumnHack */ null,
+                ContentValueFixtures.generateAggregateEncryptionKeyContentValuesV1());
+        assertEquals(
+                getTableColumns(db, MeasurementTables.AggregateEncryptionKey.TABLE).size(),
+                ContentValueFixtures.generateAggregateEncryptionKeyContentValuesV1().size());
+
+        // Execution
+        new MeasurementDbMigratorV2().performMigration(db, 1, 2);
+        // To mimic real onUpgrade behaviour. Without closing the db, changes don't reflect.
+        db.close();
+
+        // Verify
+        db = dbHelper.getReadableDatabase();
+
+        verifyAsyncRegistrationAllFieldsV1(db);
+        verifySourceAllFieldsV1(db);
+        verifyTriggerAllFieldsV1(db);
+        verifyAttributionAllFieldsV1(db);
+        verifyEventReportAllFieldsV1(db);
+        verifyAggregateReportAllFieldsV1(db);
+        verifyAggregateEncryptionKeyAllFieldsV1(db);
+    }
+
+    private void verifyAsyncRegistrationAllFieldsV1(SQLiteDatabase db) {
+        Cursor cursor =
+                db.query(
+                        MeasurementTables.AsyncRegistrationContract.TABLE,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        MeasurementTables.AsyncRegistrationContract.ID,
+                        null);
+
+        assertEquals(1, cursor.getCount());
+        while (cursor.moveToNext()) {
+            ContentValues asyncRegistrationV1 =
+                    ContentValueFixtures.generateAsyncRegistrationContentValuesV1();
+            ContentValues asyncRegistrationV2 = cursorRowToContentValues(cursor);
+
+            for (String column : asyncRegistrationV1.keySet()) {
+                assertEquals(asyncRegistrationV1.get(column), asyncRegistrationV2.get(column));
+            }
+
+            // No new columns were added.
+            assertEquals(asyncRegistrationV1.size(), asyncRegistrationV2.size());
+        }
+    }
+
+    private void verifySourceAllFieldsV1(SQLiteDatabase db) {
+        Cursor cursor =
+                db.query(
+                        MeasurementTables.SourceContract.TABLE,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        MeasurementTables.SourceContract.ID,
+                        null);
+
+        assertEquals(1, cursor.getCount());
+        while (cursor.moveToNext()) {
+            ContentValues sourceV1 = ContentValueFixtures.generateSourceContentValuesV1();
+            ContentValues sourceV2 = cursorRowToContentValues(cursor);
+
+            for (String column : sourceV1.keySet()) {
+                assertEquals(sourceV1.get(column), sourceV2.get(column));
+            }
+
+            // No new columns were added.
+            assertEquals(sourceV1.size(), sourceV2.size());
+        }
+    }
+
+    private void verifyTriggerAllFieldsV1(SQLiteDatabase db) {
+        Cursor cursor =
+                db.query(
+                        MeasurementTables.TriggerContract.TABLE,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        MeasurementTables.TriggerContract.ID,
+                        null);
+
+        assertEquals(1, cursor.getCount());
+        while (cursor.moveToNext()) {
+            ContentValues triggerV1 = ContentValueFixtures.generateTriggerContentValuesV1();
+            ContentValues triggerV2 = cursorRowToContentValues(cursor);
+
+            for (String column : triggerV1.keySet()) {
+                assertEquals(triggerV1.get(column), triggerV2.get(column));
+            }
+
+            // No new columns were added.
+            assertEquals(triggerV1.size(), triggerV2.size());
+        }
+    }
+
+    private void verifyAttributionAllFieldsV1(SQLiteDatabase db) {
+        Cursor cursor =
+                db.query(
+                        MeasurementTables.AttributionContract.TABLE,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        MeasurementTables.AttributionContract.ID,
+                        null);
+
+        assertEquals(1, cursor.getCount());
+        while (cursor.moveToNext()) {
+            ContentValues attributionV1 = ContentValueFixtures.generateAttributionContentValuesV1();
+            ContentValues attributionV2 = cursorRowToContentValues(cursor);
+
+            for (String column : attributionV1.keySet()) {
+                assertEquals(attributionV1.get(column), attributionV2.get(column));
+            }
+
+            // No new columns were added.
+            assertEquals(attributionV1.size(), attributionV2.size());
+        }
+    }
+
+    private void verifyEventReportAllFieldsV1(SQLiteDatabase db) {
+        Cursor cursor =
+                db.query(
+                        MeasurementTables.EventReportContract.TABLE,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        MeasurementTables.EventReportContract.ID,
+                        null);
+
+        assertEquals(1, cursor.getCount());
+        while (cursor.moveToNext()) {
+            ContentValues eventReportV1 = ContentValueFixtures.generateEventReportContentValuesV1();
+            ContentValues eventReportV2 = cursorRowToContentValues(cursor);
+
+            for (String column : eventReportV1.keySet()) {
+                assertEquals(eventReportV1.get(column), eventReportV2.get(column));
+            }
+
+            // The migration added 2 new columns ("source_debug_key" and "trigger_debug_key") to the
+            // EventReport table.  Assert those columns are not populated.
+            assertEquals(eventReportV1.size() + 2, eventReportV2.size());
+
+            assertNotEquals(
+                    -1,
+                    cursor.getColumnIndex(MeasurementTables.EventReportContract.SOURCE_DEBUG_KEY));
+            assertTrue(
+                    cursor.isNull(
+                            cursor.getColumnIndex(
+                                    MeasurementTables.EventReportContract.SOURCE_DEBUG_KEY)));
+
+            assertNotEquals(
+                    -1,
+                    cursor.getColumnIndex(MeasurementTables.EventReportContract.TRIGGER_DEBUG_KEY));
+            assertTrue(
+                    cursor.isNull(
+                            cursor.getColumnIndex(
+                                    MeasurementTables.EventReportContract.TRIGGER_DEBUG_KEY)));
+        }
+    }
+
+    private void verifyAggregateReportAllFieldsV1(SQLiteDatabase db) {
+        Cursor cursor =
+                db.query(
+                        MeasurementTables.AggregateReport.TABLE,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        MeasurementTables.AggregateReport.ID,
+                        null);
+
+        assertEquals(1, cursor.getCount());
+        while (cursor.moveToNext()) {
+            ContentValues aggregateReportV1 =
+                    ContentValueFixtures.generateAggregateReportContentValuesV1();
+            ContentValues aggregateReportV2 = cursorRowToContentValues(cursor);
+
+            for (String column : aggregateReportV1.keySet()) {
+                assertEquals(aggregateReportV1.get(column), aggregateReportV2.get(column));
+            }
+
+            // The migration added 2 new columns ("source_debug_key" and "trigger_debug_key") to the
+            // AggregateReport table.  Assert those columns are not populated.
+            assertEquals(aggregateReportV1.size() + 2, aggregateReportV2.size());
+
+            assertNotEquals(
+                    -1, cursor.getColumnIndex(MeasurementTables.AggregateReport.SOURCE_DEBUG_KEY));
+            assertTrue(
+                    cursor.isNull(
+                            cursor.getColumnIndex(
+                                    MeasurementTables.AggregateReport.SOURCE_DEBUG_KEY)));
+
+            assertNotEquals(
+                    -1, cursor.getColumnIndex(MeasurementTables.AggregateReport.TRIGGER_DEBUG_KEY));
+            assertTrue(
+                    cursor.isNull(
+                            cursor.getColumnIndex(
+                                    MeasurementTables.AggregateReport.TRIGGER_DEBUG_KEY)));
+        }
+    }
+
+    private void verifyAggregateEncryptionKeyAllFieldsV1(SQLiteDatabase db) {
+        Cursor cursor =
+                db.query(
+                        MeasurementTables.AggregateEncryptionKey.TABLE,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        MeasurementTables.AggregateEncryptionKey.ID,
+                        null);
+
+        assertEquals(1, cursor.getCount());
+        while (cursor.moveToNext()) {
+            ContentValues aggregateEncryptionKeyV1 =
+                    ContentValueFixtures.generateAggregateEncryptionKeyContentValuesV1();
+            ContentValues aggregateEncryptionKeyV2 = cursorRowToContentValues(cursor);
+
+            for (String column : aggregateEncryptionKeyV1.keySet()) {
+                assertEquals(
+                        aggregateEncryptionKeyV1.get(column), aggregateEncryptionKeyV2.get(column));
+            }
+
+            // No new columns were added.
+            assertEquals(aggregateEncryptionKeyV1.size(), aggregateEncryptionKeyV2.size());
+        }
+    }
+
     @Override
     int getTargetVersion() {
         return 2;
diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV3Test.java b/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV3Test.java
index 59ac4ff..6726aff 100644
--- a/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV3Test.java
+++ b/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV3Test.java
@@ -18,10 +18,12 @@
 
 import static com.android.adservices.data.DbTestUtil.doesIndexExist;
 import static com.android.adservices.data.DbTestUtil.doesTableExistAndColumnCountMatch;
+import static com.android.adservices.data.DbTestUtil.getTableColumns;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
@@ -219,6 +221,96 @@
         assertDbContainsAsyncRegistrationValues(db, asyncRegistrationRow);
     }
 
+    @Test
+    public void performMigration_success_checkAllFields_V2toV3() {
+        // Setup
+        DbHelper dbHelper = getDbHelper(1);
+        SQLiteDatabase db = dbHelper.getWritableDatabase();
+
+        new MeasurementDbMigratorV2().performMigration(db, 1, 2);
+
+        // Insert ContentValues with all fields for all tables in V2
+
+        // Async Registration
+        db.insert(
+                MeasurementTables.AsyncRegistrationContract.TABLE,
+                /* nullColumnHack */ null,
+                ContentValueFixtures.generateAsyncRegistrationContentValuesV2());
+        assertEquals(
+                getTableColumns(db, MeasurementTables.AsyncRegistrationContract.TABLE).size(),
+                ContentValueFixtures.generateAsyncRegistrationContentValuesV2().size());
+
+        // Source
+        db.insert(
+                MeasurementTables.SourceContract.TABLE,
+                /* nullColumnHack */ null,
+                ContentValueFixtures.generateSourceContentValuesV2());
+        assertEquals(
+                getTableColumns(db, MeasurementTables.SourceContract.TABLE).size(),
+                ContentValueFixtures.generateSourceContentValuesV2().size());
+
+        // Trigger
+        db.insert(
+                MeasurementTables.TriggerContract.TABLE,
+                /* nullColumnHack */ null,
+                ContentValueFixtures.generateTriggerContentValuesV2());
+        assertEquals(
+                getTableColumns(db, MeasurementTables.TriggerContract.TABLE).size(),
+                ContentValueFixtures.generateTriggerContentValuesV2().size());
+
+        // Attribution
+        assertEquals(
+                getTableColumns(db, MeasurementTables.AttributionContract.TABLE).size(),
+                ContentValueFixtures.generateAttributionContentValuesV2().size());
+        db.insert(
+                MeasurementTables.AttributionContract.TABLE,
+                /* nullColumnHack */ null,
+                ContentValueFixtures.generateAttributionContentValuesV2());
+
+        // Event Report
+        db.insert(
+                MeasurementTables.EventReportContract.TABLE,
+                /* nullColumnHack */ null,
+                ContentValueFixtures.generateEventReportContentValuesV2());
+        assertEquals(
+                getTableColumns(db, MeasurementTables.EventReportContract.TABLE).size(),
+                ContentValueFixtures.generateEventReportContentValuesV2().size());
+
+        // Aggregate Report
+        db.insert(
+                MeasurementTables.AggregateReport.TABLE,
+                /* nullColumnHack */ null,
+                ContentValueFixtures.generateAggregateReportContentValuesV2());
+        assertEquals(
+                getTableColumns(db, MeasurementTables.AggregateReport.TABLE).size(),
+                ContentValueFixtures.generateAggregateReportContentValuesV2().size());
+
+        // Aggregate Encryption Key
+        db.insert(
+                MeasurementTables.AggregateEncryptionKey.TABLE,
+                /* nullColumnHack */ null,
+                ContentValueFixtures.generateAggregateEncryptionKeyContentValuesV2());
+        assertEquals(
+                getTableColumns(db, MeasurementTables.AggregateEncryptionKey.TABLE).size(),
+                ContentValueFixtures.generateAggregateEncryptionKeyContentValuesV2().size());
+
+        // Execution
+        new MeasurementDbMigratorV3().performMigration(db, 2, 3);
+        // To mimic real onUpgrade behaviour. Without closing the db, changes don't reflect.
+        db.close();
+
+        // Verify
+        db = dbHelper.getReadableDatabase();
+
+        verifyAsyncRegistrationAllFieldsV2(db);
+        verifySourceAllFieldsV2(db);
+        verifyTriggerAllFieldsV2(db);
+        verifyAttributionAllFieldsV2(db);
+        verifyEventReportAllFieldsV2(db);
+        verifyAggregateReportAllFieldsV2(db);
+        verifyAggregateEncryptionKeyAllFieldsV2(db);
+    }
+
     @Override
     int getTargetVersion() {
         return 3;
@@ -537,4 +629,325 @@
         assertEquals(MIGRATED_TRIGGER_DATA[i][3], cursor.getString(cursor.getColumnIndex(
                 MeasurementTables.TriggerContract.FILTERS)));
     }
+
+    private void verifyAsyncRegistrationAllFieldsV2(SQLiteDatabase db) {
+        Cursor cursor =
+                db.query(
+                        MeasurementTables.AsyncRegistrationContract.TABLE,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        MeasurementTables.AsyncRegistrationContract.ID,
+                        null);
+
+        // The migration drops the old table and creates a new one without copying the data.  So
+        // there should be 0 entries.
+        assertEquals(0, cursor.getCount());
+    }
+
+    private void verifySourceAllFieldsV2(SQLiteDatabase db) {
+        Cursor cursor =
+                db.query(
+                        MeasurementTables.SourceContract.TABLE,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        MeasurementTables.SourceContract.ID,
+                        null);
+
+        assertEquals(1, cursor.getCount());
+        while (cursor.moveToNext()) {
+            ContentValues sourceV2 = ContentValueFixtures.generateSourceContentValuesV2();
+            ContentValues sourceV3 = cursorRowToContentValues(cursor);
+
+            for (String column : sourceV2.keySet()) {
+                if (column.equals(MeasurementTables.SourceContract.AGGREGATE_SOURCE)) {
+                    // Aggregate source is modified
+                    assertEquals(
+                            ContentValueFixtures.SourceValues.AGGREGATE_SOURCE_V3,
+                            sourceV3.get(column));
+                } else {
+                    assertEquals(sourceV2.get(column), sourceV3.get(column));
+                }
+            }
+
+            // The migration added 3 new columns ("debug_reporting", "ad_id_permission", and
+            // "ar_debug_permission") to the Source table.  Assert those columns are not populated.
+            assertEquals(sourceV2.size() + 3, sourceV3.size());
+
+            assertNotEquals(
+                    -1, cursor.getColumnIndex(MeasurementTables.SourceContract.DEBUG_REPORTING));
+            assertTrue(
+                    cursor.isNull(
+                            cursor.getColumnIndex(
+                                    MeasurementTables.SourceContract.DEBUG_REPORTING)));
+
+            assertNotEquals(
+                    -1, cursor.getColumnIndex(MeasurementTables.SourceContract.AD_ID_PERMISSION));
+            assertTrue(
+                    cursor.isNull(
+                            cursor.getColumnIndex(
+                                    MeasurementTables.SourceContract.AD_ID_PERMISSION)));
+
+            assertNotEquals(
+                    -1,
+                    cursor.getColumnIndex(MeasurementTables.SourceContract.AR_DEBUG_PERMISSION));
+            assertTrue(
+                    cursor.isNull(
+                            cursor.getColumnIndex(
+                                    MeasurementTables.SourceContract.AR_DEBUG_PERMISSION)));
+        }
+    }
+
+    private void verifyTriggerAllFieldsV2(SQLiteDatabase db) {
+        Cursor cursor =
+                db.query(
+                        MeasurementTables.TriggerContract.TABLE,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        MeasurementTables.TriggerContract.ID,
+                        null);
+
+        assertEquals(1, cursor.getCount());
+        while (cursor.moveToNext()) {
+            ContentValues triggerV2 = ContentValueFixtures.generateTriggerContentValuesV2();
+            ContentValues triggerV3 = cursorRowToContentValues(cursor);
+
+            for (String column : triggerV2.keySet()) {
+                if (column.equals(MeasurementTables.TriggerContract.EVENT_TRIGGERS)) {
+                    // Event triggers is modified.
+                    assertEquals(
+                            ContentValueFixtures.TriggerValues.EVENT_TRIGGERS_V3,
+                            triggerV3.get(column));
+                } else if (column.equals(
+                        MeasurementTables.TriggerContract.AGGREGATE_TRIGGER_DATA)) {
+                    // Aggregate trigger data is modified.
+                    assertEquals(
+                            ContentValueFixtures.TriggerValues.AGGREGATE_TRIGGER_DATA_V3,
+                            triggerV3.get(column));
+                } else if (column.equals(MeasurementTables.TriggerContract.FILTERS)) {
+                    // Filter is modified.
+                    assertEquals(
+                            ContentValueFixtures.TriggerValues.FILTERS_V3, triggerV3.get(column));
+                } else {
+                    assertEquals(triggerV2.get(column), triggerV3.get(column));
+                }
+            }
+            // The migration added 4 new columns ("not_filters", "debug_reporting",
+            // "ad_id_permission", and "ar_debug_permission") to the Trigger table.  Assert those
+            // columns are not populated.
+            assertEquals(triggerV2.size() + 4, triggerV3.size());
+
+            assertNotEquals(
+                    -1, cursor.getColumnIndex(MeasurementTables.TriggerContract.NOT_FILTERS));
+            assertTrue(
+                    cursor.isNull(
+                            cursor.getColumnIndex(MeasurementTables.TriggerContract.NOT_FILTERS)));
+
+            assertNotEquals(
+                    -1, cursor.getColumnIndex(MeasurementTables.TriggerContract.DEBUG_REPORTING));
+            assertTrue(
+                    cursor.isNull(
+                            cursor.getColumnIndex(
+                                    MeasurementTables.TriggerContract.DEBUG_REPORTING)));
+
+            assertNotEquals(
+                    -1, cursor.getColumnIndex(MeasurementTables.TriggerContract.AD_ID_PERMISSION));
+            assertTrue(
+                    cursor.isNull(
+                            cursor.getColumnIndex(
+                                    MeasurementTables.TriggerContract.AD_ID_PERMISSION)));
+
+            assertNotEquals(
+                    -1,
+                    cursor.getColumnIndex(MeasurementTables.TriggerContract.AR_DEBUG_PERMISSION));
+            assertTrue(
+                    cursor.isNull(
+                            cursor.getColumnIndex(
+                                    MeasurementTables.TriggerContract.AR_DEBUG_PERMISSION)));
+        }
+    }
+
+    private void verifyAttributionAllFieldsV2(SQLiteDatabase db) {
+        Cursor cursor =
+                db.query(
+                        MeasurementTables.AttributionContract.TABLE,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        MeasurementTables.AttributionContract.ID,
+                        null);
+
+        assertEquals(1, cursor.getCount());
+        while (cursor.moveToNext()) {
+            ContentValues attributionV2 = ContentValueFixtures.generateAttributionContentValuesV2();
+            ContentValues attributionV3 = cursorRowToContentValues(cursor);
+
+            for (String column : attributionV2.keySet()) {
+                assertEquals(attributionV2.get(column), attributionV3.get(column));
+            }
+
+            // The migration added 2 new columns ("source_id" and "trigger_id") to the Attribution
+            // table.  Assert those columns are not populated.
+            assertEquals(attributionV2.size() + 2, attributionV3.size());
+
+            assertNotEquals(
+                    -1, cursor.getColumnIndex(MeasurementTables.AttributionContract.SOURCE_ID));
+            assertTrue(
+                    cursor.isNull(
+                            cursor.getColumnIndex(
+                                    MeasurementTables.AttributionContract.SOURCE_ID)));
+
+            assertNotEquals(
+                    -1, cursor.getColumnIndex(MeasurementTables.AttributionContract.TRIGGER_ID));
+            assertTrue(
+                    cursor.isNull(
+                            cursor.getColumnIndex(
+                                    MeasurementTables.AttributionContract.TRIGGER_ID)));
+        }
+    }
+
+    private void verifyEventReportAllFieldsV2(SQLiteDatabase db) {
+        Cursor cursor =
+                db.query(
+                        MeasurementTables.EventReportContract.TABLE,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        MeasurementTables.EventReportContract.ID,
+                        null);
+
+        assertEquals(1, cursor.getCount());
+        while (cursor.moveToNext()) {
+            ContentValues eventReportV2 = ContentValueFixtures.generateEventReportContentValuesV2();
+            ContentValues eventReportV3 = cursorRowToContentValues(cursor);
+
+            for (String column : eventReportV2.keySet()) {
+                if (column.equals(MeasurementTables.EventReportContract.SOURCE_ID)) {
+                    // The migration renamed the column from "source_id" to "source_event_id"
+                    assertEquals(
+                            eventReportV2.get(MeasurementTables.EventReportContract.SOURCE_ID),
+                            eventReportV3.get(
+                                    MeasurementTables.EventReportContract.SOURCE_EVENT_ID));
+                } else {
+                    assertEquals(eventReportV2.get(column), eventReportV3.get(column));
+                }
+            }
+
+            // The migration added 3 new columns ("source_id", "trigger_id", and
+            // "debug_report_status") to the EventReport table.  Assert those columns are not
+            // populated.
+            assertEquals(eventReportV2.size() + 3, eventReportV3.size());
+
+            assertNotEquals(
+                    -1, cursor.getColumnIndex(MeasurementTables.EventReportContract.SOURCE_ID));
+            assertTrue(
+                    cursor.isNull(
+                            cursor.getColumnIndex(
+                                    MeasurementTables.EventReportContract.SOURCE_ID)));
+
+            assertNotEquals(
+                    -1, cursor.getColumnIndex(MeasurementTables.EventReportContract.TRIGGER_ID));
+            assertTrue(
+                    cursor.isNull(
+                            cursor.getColumnIndex(
+                                    MeasurementTables.EventReportContract.TRIGGER_ID)));
+
+            assertNotEquals(
+                    -1,
+                    cursor.getColumnIndex(
+                            MeasurementTables.EventReportContract.DEBUG_REPORT_STATUS));
+            assertTrue(
+                    cursor.isNull(
+                            cursor.getColumnIndex(
+                                    MeasurementTables.EventReportContract.DEBUG_REPORT_STATUS)));
+        }
+    }
+
+    private void verifyAggregateReportAllFieldsV2(SQLiteDatabase db) {
+        Cursor cursor =
+                db.query(
+                        MeasurementTables.AggregateReport.TABLE,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        MeasurementTables.AggregateReport.ID,
+                        null);
+
+        assertEquals(1, cursor.getCount());
+        while (cursor.moveToNext()) {
+            ContentValues aggregateReportV2 =
+                    ContentValueFixtures.generateAggregateReportContentValuesV2();
+            ContentValues aggregateReportV3 = cursorRowToContentValues(cursor);
+
+            for (String column : aggregateReportV2.keySet()) {
+                assertEquals(aggregateReportV2.get(column), aggregateReportV3.get(column));
+            }
+
+            // The migration added 3 new columns ("source_id", "trigger_id", and
+            // "debug_report_status") to the Aggregate Report table.  Assert those columns are not
+            // populated.
+            assertEquals(aggregateReportV2.size() + 3, aggregateReportV3.size());
+
+            assertNotEquals(-1, cursor.getColumnIndex(MeasurementTables.AggregateReport.SOURCE_ID));
+            assertTrue(
+                    cursor.isNull(
+                            cursor.getColumnIndex(MeasurementTables.AggregateReport.SOURCE_ID)));
+
+            assertNotEquals(
+                    -1, cursor.getColumnIndex(MeasurementTables.AggregateReport.TRIGGER_ID));
+            assertTrue(
+                    cursor.isNull(
+                            cursor.getColumnIndex(MeasurementTables.AggregateReport.TRIGGER_ID)));
+
+            assertNotEquals(
+                    -1,
+                    cursor.getColumnIndex(MeasurementTables.AggregateReport.DEBUG_REPORT_STATUS));
+            assertTrue(
+                    cursor.isNull(
+                            cursor.getColumnIndex(
+                                    MeasurementTables.AggregateReport.DEBUG_REPORT_STATUS)));
+        }
+    }
+
+    private void verifyAggregateEncryptionKeyAllFieldsV2(SQLiteDatabase db) {
+        Cursor cursor =
+                db.query(
+                        MeasurementTables.AggregateEncryptionKey.TABLE,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        MeasurementTables.AggregateEncryptionKey.ID,
+                        null);
+
+        assertEquals(1, cursor.getCount());
+        while (cursor.moveToNext()) {
+            ContentValues aggregateEncryptionKeyV2 =
+                    ContentValueFixtures.generateAggregateEncryptionKeyContentValuesV2();
+            ContentValues aggregateEncryptionKeyV3 = cursorRowToContentValues(cursor);
+
+            for (String column : aggregateEncryptionKeyV2.keySet()) {
+                assertEquals(
+                        aggregateEncryptionKeyV2.get(column), aggregateEncryptionKeyV3.get(column));
+            }
+
+            // No new columns were added.
+            assertEquals(aggregateEncryptionKeyV2.size(), aggregateEncryptionKeyV3.size());
+        }
+    }
 }
diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV6Test.java b/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV6Test.java
index b1aebde..0ec26d5 100644
--- a/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV6Test.java
+++ b/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV6Test.java
@@ -17,9 +17,11 @@
 package com.android.adservices.data.measurement.migration;
 
 import static com.android.adservices.data.DbTestUtil.doesTableExistAndColumnCountMatch;
+import static com.android.adservices.data.DbTestUtil.getTableColumns;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import android.content.ContentValues;
@@ -160,6 +162,107 @@
         db.close();
     }
 
+    @Test
+    public void performMigration_success_checkAllFields_V3toV6() {
+        // Setup
+        DbHelper dbHelper = getDbHelper(1);
+        SQLiteDatabase db = dbHelper.getWritableDatabase();
+
+        new MeasurementDbMigratorV2().performMigration(db, 1, 2);
+        new MeasurementDbMigratorV3().performMigration(db, 2, 3);
+
+        // Insert ContentValues with all fields for all tables in V3
+        // Async Registration
+        db.insert(
+                MeasurementTables.AsyncRegistrationContract.TABLE,
+                /* nullColumnHack */ null,
+                ContentValueFixtures.generateAsyncRegistrationContentValuesV3());
+        assertEquals(
+                getTableColumns(db, MeasurementTables.AsyncRegistrationContract.TABLE).size(),
+                ContentValueFixtures.generateAsyncRegistrationContentValuesV3().size());
+
+        // Source
+        db.insert(
+                MeasurementTables.SourceContract.TABLE,
+                /* nullColumnHack */ null,
+                ContentValueFixtures.generateSourceContentValuesV3());
+        assertEquals(
+                getTableColumns(db, MeasurementTables.SourceContract.TABLE).size(),
+                ContentValueFixtures.generateSourceContentValuesV3().size());
+
+        // Trigger
+        db.insert(
+                MeasurementTables.TriggerContract.TABLE,
+                /* nullColumnHack */ null,
+                ContentValueFixtures.generateTriggerContentValuesV3());
+        assertEquals(
+                getTableColumns(db, MeasurementTables.TriggerContract.TABLE).size(),
+                ContentValueFixtures.generateTriggerContentValuesV3().size());
+
+        // Attribution
+        assertEquals(
+                getTableColumns(db, MeasurementTables.AttributionContract.TABLE).size(),
+                ContentValueFixtures.generateAttributionContentValuesV3().size());
+        db.insert(
+                MeasurementTables.AttributionContract.TABLE,
+                /* nullColumnHack */ null,
+                ContentValueFixtures.generateAttributionContentValuesV3());
+
+        // Event Report
+        db.insert(
+                MeasurementTables.EventReportContract.TABLE,
+                /* nullColumnHack */ null,
+                ContentValueFixtures.generateEventReportContentValuesV3());
+        assertEquals(
+                getTableColumns(db, MeasurementTables.EventReportContract.TABLE).size(),
+                ContentValueFixtures.generateEventReportContentValuesV3().size());
+
+        // Aggregate Report
+        db.insert(
+                MeasurementTables.AggregateReport.TABLE,
+                /* nullColumnHack */ null,
+                ContentValueFixtures.generateAggregateReportContentValuesV3());
+        assertEquals(
+                getTableColumns(db, MeasurementTables.AggregateReport.TABLE).size(),
+                ContentValueFixtures.generateAggregateReportContentValuesV3().size());
+
+        // Aggregate Encryption Key
+        db.insert(
+                MeasurementTables.AggregateEncryptionKey.TABLE,
+                /* nullColumnHack */ null,
+                ContentValueFixtures.generateAggregateEncryptionKeyContentValuesV3());
+        assertEquals(
+                getTableColumns(db, MeasurementTables.AggregateEncryptionKey.TABLE).size(),
+                ContentValueFixtures.generateAggregateEncryptionKeyContentValuesV3().size());
+
+        // Debug Report
+        db.insert(
+                MeasurementTables.DebugReportContract.TABLE,
+                /* nullColumnHack */ null,
+                ContentValueFixtures.generateDebugReportContentValuesV3());
+        assertEquals(
+                getTableColumns(db, MeasurementTables.DebugReportContract.TABLE).size(),
+                ContentValueFixtures.generateDebugReportContentValuesV3().size());
+
+        // Execution
+        new MeasurementDbMigratorV6().performMigration(db, 3, 6);
+
+        // To mimic real onUpgrade behaviour. Without closing the db, changes don't reflect.
+        db.close();
+
+        // Verify
+        db = dbHelper.getReadableDatabase();
+
+        verifyAsyncRegistrationAllFieldsV3(db);
+        verifySourceAllFieldsV3(db);
+        verifyTriggerAllFieldsV3(db);
+        verifyAttributionAllFieldsV3(db);
+        verifyEventReportAllFieldsV3(db);
+        verifyAggregateReportAllFieldsV3(db);
+        verifyAggregateEncryptionKeyAllFieldsV3(db);
+        verifyDebugReportAllFieldsV3(db);
+    }
+
     @Override
     int getTargetVersion() {
         return 6;
@@ -365,4 +468,284 @@
                         cursor.getColumnIndex(
                                 MeasurementTables.TriggerContract.X_NETWORK_KEY_MAPPING)));
     }
+
+    private void verifyAsyncRegistrationAllFieldsV3(SQLiteDatabase db) {
+        Cursor cursor =
+                db.query(
+                        MeasurementTables.AsyncRegistrationContract.TABLE,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        MeasurementTables.AsyncRegistrationContract.ID,
+                        null);
+
+        assertEquals(1, cursor.getCount());
+        while (cursor.moveToNext()) {
+            ContentValues asyncRegistrationV3 =
+                    ContentValueFixtures.generateAsyncRegistrationContentValuesV3();
+            ContentValues asyncRegistrationV6 = cursorRowToContentValues(cursor);
+
+            for (String column : asyncRegistrationV3.keySet()) {
+                assertEquals(asyncRegistrationV3.get(column), asyncRegistrationV6.get(column));
+            }
+
+            // The migration added 1 new column ("registration_id") to the AsyncRegistration table.
+            // Assert that column was updated with a random UUID.
+            assertEquals(asyncRegistrationV3.size() + 1, asyncRegistrationV6.size());
+
+            assertTrue(
+                    asyncRegistrationV6.containsKey(
+                            MeasurementTables.AsyncRegistrationContract.REGISTRATION_ID));
+            assertNotNull(
+                    asyncRegistrationV6.get(
+                            MeasurementTables.AsyncRegistrationContract.REGISTRATION_ID));
+        }
+    }
+
+    private void verifySourceAllFieldsV3(SQLiteDatabase db) {
+        Cursor cursor =
+                db.query(
+                        MeasurementTables.SourceContract.TABLE,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        MeasurementTables.SourceContract.ID,
+                        null);
+
+        assertEquals(1, cursor.getCount());
+        while (cursor.moveToNext()) {
+            ContentValues sourceV3 = ContentValueFixtures.generateSourceContentValuesV3();
+            ContentValues sourceV6 = cursorRowToContentValues(cursor);
+
+            for (String column : sourceV3.keySet()) {
+                if (column.equals(MeasurementTablesDeprecated.Source.DEDUP_KEYS)) {
+                    // The migration renamed the column from "dedup_keys" to
+                    // "event_report_dedup_keys"
+                    assertEquals(
+                            sourceV3.get(MeasurementTablesDeprecated.Source.DEDUP_KEYS),
+                            sourceV6.get(MeasurementTables.SourceContract.EVENT_REPORT_DEDUP_KEYS));
+                } else {
+                    assertEquals(sourceV3.get(column), sourceV6.get(column));
+                }
+            }
+
+            // The migration added 6 new columns ("aggregate_report_dedup_keys",
+            // "event_report_window", "aggregate_report_window", "registration_id",
+            // "shared_aggregation_keys", and "install_time") to the Source table.
+            assertEquals(sourceV3.size() + 6, sourceV6.size());
+
+            // The "event_report_window" and "aggregate_report_window" are set with the value from
+            // the "expiry time" column.
+            assertTrue(sourceV6.containsKey(MeasurementTables.SourceContract.EVENT_REPORT_WINDOW));
+            assertEquals(
+                    sourceV3.get(MeasurementTables.SourceContract.EXPIRY_TIME),
+                    sourceV6.get(MeasurementTables.SourceContract.EVENT_REPORT_WINDOW));
+
+            assertTrue(
+                    sourceV6.containsKey(
+                            MeasurementTables.SourceContract.AGGREGATABLE_REPORT_WINDOW));
+            assertEquals(
+                    sourceV3.get(MeasurementTables.SourceContract.EXPIRY_TIME),
+                    sourceV6.get(MeasurementTables.SourceContract.AGGREGATABLE_REPORT_WINDOW));
+
+            // The "registration_id" column was updated with a random UUID.
+            assertTrue(sourceV6.containsKey(MeasurementTables.SourceContract.REGISTRATION_ID));
+            assertNotNull(sourceV6.get(MeasurementTables.SourceContract.REGISTRATION_ID));
+
+            // The "aggregate_report_dedup_keys", "shared_aggregation_keys", and "install_time"
+            // columns are not populated.
+            assertTrue(
+                    sourceV6.containsKey(
+                            MeasurementTables.SourceContract.AGGREGATE_REPORT_DEDUP_KEYS));
+            assertNull(sourceV6.get(MeasurementTables.SourceContract.AGGREGATE_REPORT_DEDUP_KEYS));
+
+            assertTrue(
+                    sourceV6.containsKey(MeasurementTables.SourceContract.SHARED_AGGREGATION_KEYS));
+            assertNull(sourceV6.get(MeasurementTables.SourceContract.SHARED_AGGREGATION_KEYS));
+
+            assertTrue(sourceV6.containsKey(MeasurementTables.SourceContract.INSTALL_TIME));
+            assertTrue(
+                    cursor.isNull(
+                            cursor.getColumnIndex(MeasurementTables.SourceContract.INSTALL_TIME)));
+        }
+    }
+
+    private void verifyTriggerAllFieldsV3(SQLiteDatabase db) {
+        Cursor cursor =
+                db.query(
+                        MeasurementTables.TriggerContract.TABLE,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        MeasurementTables.TriggerContract.ID,
+                        null);
+
+        assertEquals(1, cursor.getCount());
+        while (cursor.moveToNext()) {
+            ContentValues triggerV3 = ContentValueFixtures.generateTriggerContentValuesV3();
+            ContentValues triggerV6 = cursorRowToContentValues(cursor);
+
+            for (String column : triggerV3.keySet()) {
+                assertEquals(triggerV3.get(column), triggerV6.get(column));
+            }
+
+            // The migration added 3 new columns ("attribution_config", "x_network_key_mapping", and
+            // "aggregatable_deduplication_keys") to the Trigger table.  Assert the columns are not
+            // populated.
+            assertEquals(triggerV3.size() + 3, triggerV6.size());
+
+            assertTrue(triggerV6.containsKey(MeasurementTables.TriggerContract.ATTRIBUTION_CONFIG));
+            assertNull(triggerV6.get(MeasurementTables.TriggerContract.ATTRIBUTION_CONFIG));
+
+            assertTrue(
+                    triggerV6.containsKey(MeasurementTables.TriggerContract.X_NETWORK_KEY_MAPPING));
+            assertNull(triggerV6.get(MeasurementTables.TriggerContract.X_NETWORK_KEY_MAPPING));
+
+            assertTrue(
+                    triggerV6.containsKey(
+                            MeasurementTables.TriggerContract.AGGREGATABLE_DEDUPLICATION_KEYS));
+            assertNull(
+                    triggerV6.get(
+                            MeasurementTables.TriggerContract.AGGREGATABLE_DEDUPLICATION_KEYS));
+        }
+    }
+
+    private void verifyAttributionAllFieldsV3(SQLiteDatabase db) {
+        Cursor cursor =
+                db.query(
+                        MeasurementTables.AttributionContract.TABLE,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        MeasurementTables.AttributionContract.ID,
+                        null);
+
+        assertEquals(1, cursor.getCount());
+        while (cursor.moveToNext()) {
+            ContentValues attributionV3 = ContentValueFixtures.generateAttributionContentValuesV3();
+            ContentValues attributionV6 = cursorRowToContentValues(cursor);
+
+            for (String column : attributionV3.keySet()) {
+                assertEquals(attributionV3.get(column), attributionV6.get(column));
+            }
+
+            // No new columns were added.
+            assertEquals(attributionV3.size(), attributionV6.size());
+        }
+    }
+
+    private void verifyEventReportAllFieldsV3(SQLiteDatabase db) {
+        Cursor cursor =
+                db.query(
+                        MeasurementTables.EventReportContract.TABLE,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        MeasurementTables.EventReportContract.ID,
+                        null);
+
+        assertEquals(1, cursor.getCount());
+        while (cursor.moveToNext()) {
+            ContentValues eventReportV3 = ContentValueFixtures.generateEventReportContentValuesV3();
+            ContentValues eventReportV6 = cursorRowToContentValues(cursor);
+
+            for (String column : eventReportV3.keySet()) {
+                assertEquals(eventReportV3.get(column), eventReportV6.get(column));
+            }
+
+            // No new columns were added.
+            assertEquals(eventReportV3.size(), eventReportV3.size());
+        }
+    }
+
+    private void verifyAggregateReportAllFieldsV3(SQLiteDatabase db) {
+        Cursor cursor =
+                db.query(
+                        MeasurementTables.AggregateReport.TABLE,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        MeasurementTables.AggregateReport.ID,
+                        null);
+
+        assertEquals(1, cursor.getCount());
+        while (cursor.moveToNext()) {
+            ContentValues aggregateReportV3 =
+                    ContentValueFixtures.generateAggregateReportContentValuesV3();
+            ContentValues aggregateReportV6 = cursorRowToContentValues(cursor);
+
+            for (String column : aggregateReportV3.keySet()) {
+                assertEquals(aggregateReportV3.get(column), aggregateReportV6.get(column));
+            }
+
+            // No new columns were added.
+            assertEquals(aggregateReportV3.size(), aggregateReportV6.size());
+        }
+    }
+
+    private void verifyAggregateEncryptionKeyAllFieldsV3(SQLiteDatabase db) {
+        Cursor cursor =
+                db.query(
+                        MeasurementTables.AggregateEncryptionKey.TABLE,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        MeasurementTables.AggregateEncryptionKey.ID,
+                        null);
+
+        assertEquals(1, cursor.getCount());
+        while (cursor.moveToNext()) {
+            ContentValues aggregateEncryptionKeyV3 =
+                    ContentValueFixtures.generateAggregateEncryptionKeyContentValuesV2();
+            ContentValues aggregateEncryptionKeyV6 = cursorRowToContentValues(cursor);
+
+            for (String column : aggregateEncryptionKeyV3.keySet()) {
+                assertEquals(
+                        aggregateEncryptionKeyV3.get(column), aggregateEncryptionKeyV6.get(column));
+            }
+
+            // No new columns were added.
+            assertEquals(aggregateEncryptionKeyV3.size(), aggregateEncryptionKeyV6.size());
+        }
+    }
+
+    private void verifyDebugReportAllFieldsV3(SQLiteDatabase db) {
+        Cursor cursor =
+                db.query(
+                        MeasurementTables.DebugReportContract.TABLE,
+                        null,
+                        null,
+                        null,
+                        null,
+                        null,
+                        MeasurementTables.DebugReportContract.ID,
+                        null);
+
+        assertEquals(1, cursor.getCount());
+        while (cursor.moveToNext()) {
+            ContentValues debugReportV3 = ContentValueFixtures.generateDebugReportContentValuesV3();
+            ContentValues debugReportV6 = cursorRowToContentValues(cursor);
+
+            for (String column : debugReportV3.keySet()) {
+                assertEquals(debugReportV3.get(column), debugReportV6.get(column));
+            }
+
+            // No new columns were added.
+            assertEquals(debugReportV3.size(), debugReportV6.size());
+        }
+    }
 }