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());
+ }
+ }
}