1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.adservices.data.measurement; 18 19 import android.content.ContentValues; 20 import android.database.sqlite.SQLiteDatabase; 21 import android.database.sqlite.SQLiteException; 22 import android.net.Uri; 23 24 import com.android.adservices.common.AdServicesExtendedMockitoTestCase; 25 import com.android.adservices.common.DbTestUtil; 26 import com.android.adservices.service.FlagsConstants; 27 import com.android.adservices.service.FlagsFactory; 28 import com.android.adservices.service.measurement.Attribution; 29 import com.android.adservices.service.measurement.CountUniqueReport; 30 import com.android.adservices.service.measurement.EventReport; 31 import com.android.adservices.service.measurement.KeyValueData; 32 import com.android.adservices.service.measurement.Source; 33 import com.android.adservices.service.measurement.Trigger; 34 import com.android.adservices.service.measurement.aggregation.AggregateEncryptionKey; 35 import com.android.adservices.service.measurement.aggregation.AggregateReport; 36 import com.android.adservices.service.measurement.registration.AsyncRegistration; 37 import com.android.adservices.service.measurement.reporting.DebugReport; 38 import com.android.adservices.service.measurement.util.UnsignedLong; 39 import com.android.modules.utils.testing.ExtendedMockitoRule.SpyStatic; 40 41 import org.json.JSONArray; 42 import org.json.JSONException; 43 import org.json.JSONObject; 44 import org.junit.After; 45 import org.junit.Assert; 46 import org.junit.Before; 47 import org.junit.Test; 48 49 import java.io.IOException; 50 import java.io.InputStream; 51 import java.nio.charset.StandardCharsets; 52 import java.util.ArrayList; 53 import java.util.Arrays; 54 import java.util.Collection; 55 import java.util.HashMap; 56 import java.util.List; 57 import java.util.Map; 58 import java.util.Objects; 59 import java.util.Optional; 60 import java.util.concurrent.TimeUnit; 61 import java.util.stream.Collectors; 62 63 /** 64 * Abstract class for parameterized tests that check the database state of measurement tables 65 * against expected output after an action is run. 66 * 67 * <p>Consider @RunWith(Parameterized.class) 68 */ 69 @SpyStatic(FlagsFactory.class) 70 public abstract class AbstractDbIntegrationTest extends AdServicesExtendedMockitoTestCase { 71 72 private static final String PH_FLAGS_OVERRIDE_KEY = "phflags_override"; 73 74 protected final DbState mInput; 75 protected final DbState mOutput; 76 protected final Map<String, String> mFlagsMap; 77 78 @Before before()79 public void before() { 80 SQLiteDatabase db = DbTestUtil.getMeasurementDbHelperForTest().getWritableDatabase(); 81 emptyTables(db); 82 seedTables(db, mInput); 83 } 84 85 @After after()86 public void after() { 87 SQLiteDatabase db = DbTestUtil.getMeasurementDbHelperForTest().getWritableDatabase(); 88 emptyTables(db); 89 } 90 AbstractDbIntegrationTest(DbState input, DbState output, Map<String, String> flagsMap)91 public AbstractDbIntegrationTest(DbState input, DbState output, Map<String, String> flagsMap) { 92 mInput = input; 93 mOutput = output; 94 mFlagsMap = flagsMap; 95 setCommonFlags(); 96 } 97 98 /** Runs the action we want to test. */ runActionToTest()99 protected abstract void runActionToTest() throws DatastoreException; 100 101 @Test runTest()102 public void runTest() throws Exception { 103 setupFlags(); 104 runActionToTest(); 105 SQLiteDatabase readerDb = DbTestUtil.getMeasurementDbHelperForTest().getReadableDatabase(); 106 DbState dbState = new DbState(readerDb); 107 mOutput.sortAll(); 108 dbState.sortAll(); 109 110 Assert.assertTrue("Event report mismatch", 111 areEqual(mOutput.mEventReportList, dbState.mEventReportList)); 112 // Custom matching for AggregateReport due to non-deterministic reporting random number 113 // TODO: Remove custom matching using DI for Random class 114 Assert.assertEquals( 115 "AggregateReport size mismatch", 116 mOutput.mAggregateReportList.size(), 117 dbState.mAggregateReportList.size()); 118 for (int i = 0; i < mOutput.mAggregateReportList.size(); i++) { 119 Assert.assertTrue( 120 "AggregateReport Mismatch", 121 fuzzyCompareAggregateReport( 122 mOutput.mAggregateReportList.get(i), 123 dbState.mAggregateReportList.get(i))); 124 } 125 Assert.assertTrue("AggregateEncryptionKey mismatch", 126 areEqual(mOutput.mAggregateEncryptionKeyList, dbState.mAggregateEncryptionKeyList)); 127 Assert.assertTrue("Attribution mismatch", 128 areEqual(mOutput.mAttrRateLimitList, dbState.mAttrRateLimitList)); 129 Assert.assertTrue("Source mismatch", 130 areEqual(mOutput.mSourceList, dbState.mSourceList)); 131 Assert.assertTrue("SourceDestination mismatch", 132 areEqual(mOutput.mSourceDestinationList, dbState.mSourceDestinationList)); 133 Assert.assertTrue("Trigger mismatch", 134 areEqual(mOutput.mTriggerList, dbState.mTriggerList)); 135 Assert.assertTrue( 136 "Async Registration mismatch", 137 areEqual(mOutput.mAsyncRegistrationList, dbState.mAsyncRegistrationList)); 138 for (int i = 0; i < mOutput.mDebugReportList.size(); i++) { 139 Assert.assertTrue( 140 "DebugReport Mismatch", 141 areDebugReportContentsSimilar( 142 mOutput.mDebugReportList.get(i), dbState.mDebugReportList.get(i))); 143 } 144 for (int i = 0; i < mOutput.mKeyValueDataList.size(); i++) { 145 Assert.assertTrue( 146 "KeyValueData Mismatch", 147 areEqualKeyValueData( 148 mOutput.mKeyValueDataList.get(i), dbState.mKeyValueDataList.get(i))); 149 } 150 } 151 areDebugReportContentsSimilar( DebugReport debugReport, DebugReport debugReport1)152 private boolean areDebugReportContentsSimilar( 153 DebugReport debugReport, DebugReport debugReport1) { 154 // TODO (b/300109438) Investigate DebugReport::equals 155 return Objects.equals(debugReport.getType(), debugReport1.getType()) 156 && Objects.equals( 157 debugReport.getBody().toString(), debugReport1.getBody().toString()) 158 && Objects.equals(debugReport.getEnrollmentId(), debugReport1.getEnrollmentId()) 159 && Objects.equals( 160 debugReport.getRegistrationOrigin(), debugReport1.getRegistrationOrigin()) 161 && Objects.equals(debugReport.getReferenceId(), debugReport1.getReferenceId()); 162 } 163 areEqualKeyValueData(KeyValueData keyValueData, KeyValueData keyValueData1)164 private boolean areEqualKeyValueData(KeyValueData keyValueData, KeyValueData keyValueData1) { 165 return Objects.equals(keyValueData.getKey(), keyValueData1.getKey()) 166 && Objects.equals(keyValueData.getDataType(), keyValueData1.getDataType()) 167 && Objects.equals(keyValueData.getValue(), keyValueData1.getValue()); 168 } 169 fuzzyCompareAggregateReport( AggregateReport aggregateReport, AggregateReport aggregateReport1)170 private boolean fuzzyCompareAggregateReport( 171 AggregateReport aggregateReport, AggregateReport aggregateReport1) { 172 return Objects.equals(aggregateReport.getPublisher(), aggregateReport1.getPublisher()) 173 && Objects.equals( 174 aggregateReport.getAttributionDestination(), 175 aggregateReport1.getAttributionDestination()) 176 && Objects.equals( 177 aggregateReport.getSourceRegistrationTime(), 178 aggregateReport1.getSourceRegistrationTime()) 179 && Math.abs( 180 aggregateReport.getScheduledReportTime() 181 - aggregateReport1.getScheduledReportTime()) 182 <= TimeUnit.HOURS.toMillis(1) 183 && Objects.equals( 184 aggregateReport.getEnrollmentId(), aggregateReport1.getEnrollmentId()) 185 && Objects.equals( 186 aggregateReport.getDebugCleartextPayload(), 187 aggregateReport1.getDebugCleartextPayload()) 188 && Objects.equals( 189 aggregateReport.getAggregateAttributionData(), 190 aggregateReport1.getAggregateAttributionData()) 191 && aggregateReport.getStatus() == aggregateReport1.getStatus() 192 && Objects.equals(aggregateReport.getApiVersion(), aggregateReport1.getApiVersion()) 193 && Objects.equals( 194 aggregateReport.getSourceDebugKey(), aggregateReport1.getSourceDebugKey()) 195 && Objects.equals( 196 aggregateReport.getTriggerDebugKey(), aggregateReport1.getTriggerDebugKey()) 197 && Objects.equals( 198 aggregateReport.getRegistrationOrigin(), 199 aggregateReport1.getRegistrationOrigin()); 200 } 201 202 /** 203 * Generic function type that handles JSON objects, 204 * used to type lambdas provided to {@link getTestCasesFrom} 205 * to prepare test-specific data. 206 */ 207 @FunctionalInterface 208 public interface CheckedJsonFunction { apply(JSONObject jsonObject)209 Object apply(JSONObject jsonObject) throws JSONException; 210 } 211 212 /** 213 * Builds and returns test cases from a JSON InputStream 214 * to be used by JUnit parameterized tests. 215 * 216 * @return A collection of Object arrays, each with 217 * {@code [DbState input, DbState output, (any) additionalData, String name]} 218 */ getTestCasesFrom(InputStream inputStream, CheckedJsonFunction prepareAdditionalData)219 public static Collection<Object[]> getTestCasesFrom(InputStream inputStream, 220 CheckedJsonFunction prepareAdditionalData) throws IOException, JSONException { 221 int size = inputStream.available(); 222 byte[] buffer = new byte[size]; 223 inputStream.read(buffer); 224 inputStream.close(); 225 String json = new String(buffer, StandardCharsets.UTF_8); 226 if (json.length() <= 10) { 227 throw new IOException("json length less than 11 characters."); 228 } 229 JSONObject obj = new JSONObject(json.replaceAll("\\.test(?=[\"\\/ ])", ".com")); 230 JSONArray testArray = obj.getJSONArray("testCases"); 231 232 List<Object[]> testCases = new ArrayList<>(); 233 for (int i = 0; i < testArray.length(); i++) { 234 JSONObject testObj = testArray.getJSONObject(i); 235 addTestCase(testObj, testCases, prepareAdditionalData); 236 } 237 return testCases; 238 } 239 getTestCasesFromMultipleStreams( List<InputStream> inputStreams, CheckedJsonFunction prepareAdditionalData)240 public static Collection<Object[]> getTestCasesFromMultipleStreams( 241 List<InputStream> inputStreams, CheckedJsonFunction prepareAdditionalData) 242 throws IOException, JSONException { 243 List<Object[]> testCases = new ArrayList<>(); 244 for (InputStream inputStream : inputStreams) { 245 int size = inputStream.available(); 246 byte[] buffer = new byte[size]; 247 inputStream.read(buffer); 248 inputStream.close(); 249 String json = new String(buffer, StandardCharsets.UTF_8); 250 JSONObject testObj = new JSONObject(json); 251 addTestCase(testObj, testCases, prepareAdditionalData); 252 } 253 return testCases; 254 } 255 addTestCase(JSONObject testObj, List<Object[]> testCases, CheckedJsonFunction prepareAdditionalData)256 private static void addTestCase(JSONObject testObj, List<Object[]> testCases, 257 CheckedJsonFunction prepareAdditionalData) throws JSONException { 258 String name = testObj.getString("name"); 259 JSONObject input = testObj.getJSONObject("input"); 260 JSONObject output = testObj.getJSONObject("output"); 261 DbState inputState = new DbState(input); 262 DbState outputState = new DbState(output); 263 Map<String, String> flagsMap = getFlagsMap(testObj); 264 if (prepareAdditionalData != null) { 265 testCases.add( 266 new Object[]{ 267 inputState, 268 outputState, 269 flagsMap, 270 prepareAdditionalData.apply(testObj), 271 name }); 272 } else { 273 testCases.add( 274 new Object[]{ 275 inputState, 276 outputState, 277 flagsMap, 278 name }); 279 } 280 } 281 282 /** Set flags that are common for all tests. */ setCommonFlags()283 private void setCommonFlags() { 284 // Make null agg report generation deterministic. 285 mFlagsMap.putIfAbsent( 286 FlagsConstants.KEY_MEASUREMENT_NULL_AGG_REPORT_RATE_EXCL_SOURCE_REGISTRATION_TIME, 287 "0.0"); 288 mFlagsMap.putIfAbsent( 289 FlagsConstants.KEY_MEASUREMENT_NULL_AGG_REPORT_RATE_INCL_SOURCE_REGISTRATION_TIME, 290 "0.0"); 291 // Limit the number of aggregate keys because histograms are padded with many 0 valued 292 // buckets. This helps keep the test file shorter. 293 mFlagsMap.putIfAbsent( 294 FlagsConstants.KEY_MEASUREMENT_MAX_AGGREGATE_KEYS_PER_SOURCE_REGISTRATION, "5"); 295 } 296 getFlagsMap(JSONObject testObj)297 private static Map<String, String> getFlagsMap(JSONObject testObj) throws JSONException { 298 Map<String, String> flagsMap = new HashMap<>(); 299 if (testObj.isNull(PH_FLAGS_OVERRIDE_KEY)) { 300 return flagsMap; 301 } 302 JSONObject phFlagsObject = testObj.optJSONObject(PH_FLAGS_OVERRIDE_KEY); 303 for (String key : phFlagsObject.keySet()) { 304 flagsMap.put(key, phFlagsObject.optString(key)); 305 } 306 return flagsMap; 307 } 308 309 /** 310 * Compares two lists of the same measurement record type. 311 * (Caller enforces the element types.) 312 */ areEqual(List<T> list1, List<T> list2)313 public static <T> boolean areEqual(List<T> list1, List<T> list2) { 314 return Arrays.equals(list1.toArray(), list2.toArray()); 315 } 316 317 /** 318 * Empties measurement database tables, 319 * used for test cleanup. 320 */ emptyTables(SQLiteDatabase db)321 private static void emptyTables(SQLiteDatabase db) { 322 db.delete(MeasurementTables.SourceContract.TABLE, null, null); 323 db.delete(MeasurementTables.SourceDestination.TABLE, null, null); 324 db.delete(MeasurementTables.TriggerContract.TABLE, null, null); 325 db.delete(MeasurementTables.EventReportContract.TABLE, null, null); 326 db.delete(MeasurementTables.AttributionContract.TABLE, null, null); 327 db.delete(MeasurementTables.AggregateReport.TABLE, null, null); 328 db.delete(MeasurementTables.AggregateEncryptionKey.TABLE, null, null); 329 db.delete(MeasurementTables.AsyncRegistrationContract.TABLE, null, null); 330 db.delete(MeasurementTables.DebugReportContract.TABLE, null, null); 331 db.delete(MeasurementTables.KeyValueDataContract.TABLE, null, null); 332 } 333 334 /** 335 * Seeds measurement database tables for testing. 336 */ seedTables(SQLiteDatabase db, DbState input)337 private static void seedTables(SQLiteDatabase db, DbState input) throws SQLiteException { 338 for (Source source : input.mSourceList) { 339 insertToDb(source, db); 340 } 341 for (SourceDestination sourceDest : input.mSourceDestinationList) { 342 insertToDb(sourceDest, db); 343 } 344 for (Trigger trigger : input.mTriggerList) { 345 insertToDb(trigger, db); 346 } 347 for (EventReport eventReport : input.mEventReportList) { 348 insertToDb(eventReport, db); 349 } 350 for (com.android.adservices.service.measurement.Attribution attr : 351 input.mAttrRateLimitList) { 352 insertToDb(attr, db); 353 } 354 for (AggregateReport aggregateReport : input.mAggregateReportList) { 355 insertToDb(aggregateReport, db); 356 } 357 for (AggregateEncryptionKey key : input.mAggregateEncryptionKeyList) { 358 insertToDb(key, db); 359 } 360 for (AsyncRegistration registration : input.mAsyncRegistrationList) { 361 insertToDb(registration, db); 362 } 363 for (DebugReport debugReport : input.mDebugReportList) { 364 insertToDb(debugReport, db); 365 } 366 for (KeyValueData keyValueData : input.mKeyValueDataList) { 367 insertToDb(keyValueData, db); 368 } 369 } 370 371 /** 372 * Inserts a Source record into the given database. 373 */ insertToDb(Source source, SQLiteDatabase db)374 public static void insertToDb(Source source, SQLiteDatabase db) throws SQLiteException { 375 ContentValues values = new ContentValues(); 376 values.put(MeasurementTables.SourceContract.ID, source.getId()); 377 values.put(MeasurementTables.SourceContract.EVENT_ID, 378 getNullableUnsignedLong(source.getEventId())); 379 values.put(MeasurementTables.SourceContract.SOURCE_TYPE, source.getSourceType().toString()); 380 values.put(MeasurementTables.SourceContract.PUBLISHER, 381 source.getPublisher().toString()); 382 values.put(MeasurementTables.SourceContract.PUBLISHER_TYPE, 383 source.getPublisherType()); 384 values.put(MeasurementTables.SourceContract.AGGREGATE_SOURCE, source.getAggregateSource()); 385 values.put(MeasurementTables.SourceContract.ENROLLMENT_ID, source.getEnrollmentId()); 386 values.put(MeasurementTables.SourceContract.STATUS, source.getStatus()); 387 values.put(MeasurementTables.SourceContract.EVENT_TIME, source.getEventTime()); 388 values.put(MeasurementTables.SourceContract.EXPIRY_TIME, source.getExpiryTime()); 389 values.put(MeasurementTables.SourceContract.EVENT_REPORT_WINDOW, 390 source.getEventReportWindow()); 391 values.put(MeasurementTables.SourceContract.AGGREGATABLE_REPORT_WINDOW, 392 source.getAggregatableReportWindow()); 393 values.put(MeasurementTables.SourceContract.PRIORITY, source.getPriority()); 394 values.put(MeasurementTables.SourceContract.REGISTRANT, source.getRegistrant().toString()); 395 values.put(MeasurementTables.SourceContract.INSTALL_ATTRIBUTION_WINDOW, 396 source.getInstallAttributionWindow()); 397 values.put(MeasurementTables.SourceContract.INSTALL_COOLDOWN_WINDOW, 398 source.getInstallCooldownWindow()); 399 values.put(MeasurementTables.SourceContract.IS_INSTALL_ATTRIBUTED, 400 source.isInstallAttributed() ? 1 : 0); 401 values.put(MeasurementTables.SourceContract.ATTRIBUTION_MODE, source.getAttributionMode()); 402 values.put( 403 MeasurementTables.SourceContract.AGGREGATE_CONTRIBUTIONS, 404 source.getAggregateContributions()); 405 values.put(MeasurementTables.SourceContract.FILTER_DATA, source.getFilterDataString()); 406 values.put(MeasurementTables.SourceContract.DEBUG_REPORTING, source.isDebugReporting()); 407 values.put(MeasurementTables.SourceContract.INSTALL_TIME, source.getInstallTime()); 408 values.put(MeasurementTables.SourceContract.REGISTRATION_ID, source.getRegistrationId()); 409 values.put( 410 MeasurementTables.SourceContract.SHARED_AGGREGATION_KEYS, 411 source.getSharedAggregationKeys()); 412 values.put( 413 MeasurementTables.SourceContract.SHARED_FILTER_DATA_KEYS, 414 source.getSharedFilterDataKeys()); 415 values.put( 416 MeasurementTables.SourceContract.REGISTRATION_ORIGIN, 417 source.getRegistrationOrigin().toString()); 418 values.put( 419 MeasurementTables.SourceContract.AGGREGATE_REPORT_DEDUP_KEYS, 420 listToCommaSeparatedString(source.getAggregateReportDedupKeys())); 421 // Flex API 422 values.put( 423 MeasurementTables.SourceContract.TRIGGER_SPECS, 424 source.getTriggerSpecsString()); 425 values.put( 426 MeasurementTables.SourceContract.EVENT_ATTRIBUTION_STATUS, 427 source.getEventAttributionStatus()); 428 values.put( 429 MeasurementTables.SourceContract.AGGREGATE_DEBUG_REPORTING, 430 source.getAggregateDebugReportingString()); 431 long row = db.insert(MeasurementTables.SourceContract.TABLE, null, values); 432 if (row == -1) { 433 throw new SQLiteException("Source insertion failed"); 434 } 435 } 436 listToCommaSeparatedString(List<UnsignedLong> list)437 private static String listToCommaSeparatedString(List<UnsignedLong> list) { 438 return list.stream().map(UnsignedLong::toString).collect(Collectors.joining(",")); 439 } 440 441 /** 442 * Inserts a SourceDestination into the given database. 443 */ insertToDb(SourceDestination sourceDest, SQLiteDatabase db)444 public static void insertToDb(SourceDestination sourceDest, SQLiteDatabase db) 445 throws SQLiteException { 446 ContentValues values = new ContentValues(); 447 values.put(MeasurementTables.SourceDestination.SOURCE_ID, sourceDest.getSourceId()); 448 values.put(MeasurementTables.SourceDestination.DESTINATION, sourceDest.getDestination()); 449 values.put(MeasurementTables.SourceDestination.DESTINATION_TYPE, 450 sourceDest.getDestinationType()); 451 long row = db.insert(MeasurementTables.SourceDestination.TABLE, null, values); 452 if (row == -1) { 453 throw new SQLiteException("SourceDestination insertion failed"); 454 } 455 } 456 457 /** Inserts a Trigger record into the given database. */ insertToDb(Trigger trigger, SQLiteDatabase db)458 public static void insertToDb(Trigger trigger, SQLiteDatabase db) throws SQLiteException { 459 ContentValues values = new ContentValues(); 460 values.put(MeasurementTables.TriggerContract.ID, trigger.getId()); 461 values.put(MeasurementTables.TriggerContract.ATTRIBUTION_DESTINATION, 462 trigger.getAttributionDestination().toString()); 463 values.put(MeasurementTables.TriggerContract.DESTINATION_TYPE, 464 trigger.getDestinationType()); 465 values.put( 466 MeasurementTables.TriggerContract.AGGREGATE_TRIGGER_DATA, 467 trigger.getAggregateTriggerData()); 468 values.put( 469 MeasurementTables.TriggerContract.AGGREGATE_VALUES, 470 trigger.getAggregateValuesString()); 471 values.put(MeasurementTables.TriggerContract.ENROLLMENT_ID, trigger.getEnrollmentId()); 472 values.put(MeasurementTables.TriggerContract.STATUS, trigger.getStatus()); 473 values.put(MeasurementTables.TriggerContract.TRIGGER_TIME, trigger.getTriggerTime()); 474 values.put(MeasurementTables.TriggerContract.EVENT_TRIGGERS, trigger.getEventTriggers()); 475 values.put(MeasurementTables.TriggerContract.REGISTRANT, 476 trigger.getRegistrant().toString()); 477 values.put(MeasurementTables.TriggerContract.FILTERS, trigger.getFilters()); 478 values.put(MeasurementTables.TriggerContract.NOT_FILTERS, trigger.getNotFilters()); 479 values.put( 480 MeasurementTables.TriggerContract.ATTRIBUTION_CONFIG, 481 trigger.getAttributionConfig()); 482 values.put( 483 MeasurementTables.TriggerContract.X_NETWORK_KEY_MAPPING, 484 trigger.getAdtechKeyMapping()); 485 values.put( 486 MeasurementTables.TriggerContract.REGISTRATION_ORIGIN, 487 trigger.getRegistrationOrigin().toString()); 488 values.put( 489 MeasurementTables.TriggerContract.AGGREGATABLE_SOURCE_REGISTRATION_TIME_CONFIG, 490 trigger.getAggregatableSourceRegistrationTimeConfig().name()); 491 values.put( 492 MeasurementTables.TriggerContract.AGGREGATE_DEBUG_REPORTING, 493 trigger.getAggregateDebugReportingString()); 494 long row = db.insert(MeasurementTables.TriggerContract.TABLE, null, values); 495 if (row == -1) { 496 throw new SQLiteException("Trigger insertion failed"); 497 } 498 } 499 500 /** Inserts an EventReport record into the given database. */ insertToDb(EventReport report, SQLiteDatabase db)501 public static void insertToDb(EventReport report, SQLiteDatabase db) throws SQLiteException { 502 ContentValues values = new ContentValues(); 503 values.put(MeasurementTables.EventReportContract.ID, report.getId()); 504 values.put( 505 MeasurementTables.EventReportContract.SOURCE_EVENT_ID, 506 report.getSourceEventId().getValue()); 507 values.put(MeasurementTables.EventReportContract.ENROLLMENT_ID, report.getEnrollmentId()); 508 values.put(MeasurementTables.EventReportContract.ATTRIBUTION_DESTINATION, 509 String.join(" ", report.getAttributionDestinations() 510 .stream() 511 .map(Uri::toString) 512 .collect(Collectors.toList()))); 513 values.put(MeasurementTables.EventReportContract.REPORT_TIME, report.getReportTime()); 514 values.put(MeasurementTables.EventReportContract.TRIGGER_DATA, 515 report.getTriggerData().getValue()); 516 values.put(MeasurementTables.EventReportContract.TRIGGER_PRIORITY, 517 report.getTriggerPriority()); 518 values.put(MeasurementTables.EventReportContract.TRIGGER_DEDUP_KEY, 519 getNullableUnsignedLong(report.getTriggerDedupKey())); 520 values.put(MeasurementTables.EventReportContract.TRIGGER_TIME, report.getTriggerTime()); 521 values.put(MeasurementTables.EventReportContract.STATUS, report.getStatus()); 522 values.put(MeasurementTables.EventReportContract.SOURCE_TYPE, 523 report.getSourceType().toString()); 524 values.put(MeasurementTables.EventReportContract.RANDOMIZED_TRIGGER_RATE, 525 report.getRandomizedTriggerRate()); 526 values.put(MeasurementTables.EventReportContract.SOURCE_ID, report.getSourceId()); 527 values.put(MeasurementTables.EventReportContract.TRIGGER_ID, report.getTriggerId()); 528 values.put( 529 MeasurementTables.EventReportContract.REGISTRATION_ORIGIN, 530 report.getRegistrationOrigin().toString()); 531 long row = db.insert(MeasurementTables.EventReportContract.TABLE, null, values); 532 if (row == -1) { 533 throw new SQLiteException("EventReport insertion failed"); 534 } 535 } 536 537 /** Inserts an Attribution record into the given database. */ insertToDb(Attribution attribution, SQLiteDatabase db)538 public static void insertToDb(Attribution attribution, SQLiteDatabase db) 539 throws SQLiteException { 540 ContentValues values = new ContentValues(); 541 values.put(MeasurementTables.AttributionContract.ID, attribution.getId()); 542 values.put(MeasurementTables.AttributionContract.SCOPE, attribution.getScope()); 543 values.put(MeasurementTables.AttributionContract.SOURCE_SITE, attribution.getSourceSite()); 544 values.put( 545 MeasurementTables.AttributionContract.SOURCE_ORIGIN, attribution.getSourceOrigin()); 546 values.put(MeasurementTables.AttributionContract.DESTINATION_SITE, 547 attribution.getDestinationSite()); 548 values.put(MeasurementTables.AttributionContract.DESTINATION_ORIGIN, 549 attribution.getDestinationOrigin()); 550 values.put(MeasurementTables.AttributionContract.ENROLLMENT_ID, 551 attribution.getEnrollmentId()); 552 values.put(MeasurementTables.AttributionContract.TRIGGER_TIME, 553 attribution.getTriggerTime()); 554 values.put(MeasurementTables.AttributionContract.REGISTRANT, 555 attribution.getRegistrant()); 556 values.put(MeasurementTables.AttributionContract.SOURCE_ID, attribution.getSourceId()); 557 values.put(MeasurementTables.AttributionContract.TRIGGER_ID, attribution.getTriggerId()); 558 values.put(MeasurementTables.AttributionContract.REPORT_ID, attribution.getReportId()); 559 long row = db.insert(MeasurementTables.AttributionContract.TABLE, null, values); 560 if (row == -1) { 561 throw new SQLiteException("Attribution insertion failed"); 562 } 563 } 564 565 /** Inserts an AggregateReport record into the given database. */ insertToDb(AggregateReport aggregateReport, SQLiteDatabase db)566 public static void insertToDb(AggregateReport aggregateReport, SQLiteDatabase db) 567 throws SQLiteException { 568 ContentValues values = new ContentValues(); 569 values.put(MeasurementTables.AggregateReport.ID, aggregateReport.getId()); 570 values.put( 571 MeasurementTables.AggregateReport.PUBLISHER, 572 aggregateReport.getPublisher().toString()); 573 values.put( 574 MeasurementTables.AggregateReport.ATTRIBUTION_DESTINATION, 575 aggregateReport.getAttributionDestination().toString()); 576 values.put( 577 MeasurementTables.AggregateReport.SOURCE_REGISTRATION_TIME, 578 aggregateReport.getSourceRegistrationTime()); 579 values.put( 580 MeasurementTables.AggregateReport.SCHEDULED_REPORT_TIME, 581 aggregateReport.getScheduledReportTime()); 582 values.put( 583 MeasurementTables.AggregateReport.ENROLLMENT_ID, 584 aggregateReport.getEnrollmentId()); 585 values.put( 586 MeasurementTables.AggregateReport.DEBUG_CLEARTEXT_PAYLOAD, 587 aggregateReport.getDebugCleartextPayload()); 588 values.put( 589 MeasurementTables.AggregateReport.DEBUG_REPORT_STATUS, 590 aggregateReport.getDebugReportStatus()); 591 values.put(MeasurementTables.AggregateReport.STATUS, aggregateReport.getStatus()); 592 values.put(MeasurementTables.AggregateReport.API_VERSION, aggregateReport.getApiVersion()); 593 values.put(MeasurementTables.AggregateReport.SOURCE_ID, aggregateReport.getSourceId()); 594 values.put(MeasurementTables.AggregateReport.TRIGGER_ID, aggregateReport.getTriggerId()); 595 values.put( 596 MeasurementTables.AggregateReport.REGISTRATION_ORIGIN, 597 aggregateReport.getRegistrationOrigin().toString()); 598 values.put( 599 MeasurementTables.AggregateReport.AGGREGATION_COORDINATOR_ORIGIN, 600 aggregateReport.getAggregationCoordinatorOrigin().toString()); 601 values.put( 602 MeasurementTables.AggregateReport.IS_FAKE_REPORT, aggregateReport.isFakeReport()); 603 values.put( 604 MeasurementTables.AggregateReport.DEDUP_KEY, 605 aggregateReport.getDedupKey() != null 606 ? aggregateReport.getDedupKey().getValue() 607 : null); 608 values.put(MeasurementTables.AggregateReport.API, aggregateReport.getApi()); 609 long row = db.insert(MeasurementTables.AggregateReport.TABLE, null, values); 610 if (row == -1) { 611 throw new SQLiteException("AggregateReport insertion failed"); 612 } 613 } 614 615 /** Inserts an AggregateEncryptionKey record into the given database. */ insertToDb(AggregateEncryptionKey aggregateEncryptionKey, SQLiteDatabase db)616 public static void insertToDb(AggregateEncryptionKey aggregateEncryptionKey, SQLiteDatabase db) 617 throws SQLiteException { 618 ContentValues values = new ContentValues(); 619 values.put(MeasurementTables.AggregateEncryptionKey.ID, aggregateEncryptionKey.getId()); 620 values.put(MeasurementTables.AggregateEncryptionKey.KEY_ID, 621 aggregateEncryptionKey.getKeyId()); 622 values.put(MeasurementTables.AggregateEncryptionKey.PUBLIC_KEY, 623 aggregateEncryptionKey.getPublicKey()); 624 values.put(MeasurementTables.AggregateEncryptionKey.EXPIRY, 625 aggregateEncryptionKey.getExpiry()); 626 values.put( 627 MeasurementTables.AggregateEncryptionKey.AGGREGATION_COORDINATOR_ORIGIN, 628 aggregateEncryptionKey.getAggregationCoordinatorOrigin().toString()); 629 long row = db.insert(MeasurementTables.AggregateEncryptionKey.TABLE, null, values); 630 if (row == -1) { 631 throw new SQLiteException("AggregateEncryptionKey insertion failed."); 632 } 633 } 634 635 /** Inserts a Count Unique Report into the given database. */ insertToDb(CountUniqueReport countUniqueReport, SQLiteDatabase db)636 public static void insertToDb(CountUniqueReport countUniqueReport, SQLiteDatabase db) 637 throws SQLiteException { 638 ContentValues values = new ContentValues(); 639 values.put( 640 MeasurementTables.CountUniqueReportingContract.REPORT_ID, 641 countUniqueReport.getReportId()); 642 values.put( 643 MeasurementTables.CountUniqueReportingContract.DEBUG_KEY, 644 countUniqueReport.getDebugKey()); 645 values.put( 646 MeasurementTables.CountUniqueReportingContract.API_VERSION, 647 countUniqueReport.getApiVersion()); 648 values.put( 649 MeasurementTables.CountUniqueReportingContract.STATUS, 650 countUniqueReport.getStatus()); 651 values.put( 652 MeasurementTables.CountUniqueReportingContract.PAYLOAD, 653 countUniqueReport.getPayload()); 654 values.put( 655 MeasurementTables.CountUniqueReportingContract.REPORTING_ORIGIN, 656 countUniqueReport.getReportingOrigin().toString()); 657 values.put( 658 MeasurementTables.CountUniqueReportingContract.SCHEDULED_REPORT_TIME, 659 countUniqueReport.getScheduledReportTime()); 660 long row = db.insert(MeasurementTables.CountUniqueReportingContract.TABLE, null, values); 661 if (row == -1) { 662 throw new SQLiteException("CountUniqueReport insertion failed."); 663 } 664 } 665 666 /** Inserts an AsyncRegistration record into the given database. */ insertToDb(AsyncRegistration asyncRegistration, SQLiteDatabase db)667 private static void insertToDb(AsyncRegistration asyncRegistration, SQLiteDatabase db) 668 throws SQLiteException { 669 ContentValues values = new ContentValues(); 670 values.put(MeasurementTables.AsyncRegistrationContract.ID, asyncRegistration.getId()); 671 values.put( 672 MeasurementTables.AsyncRegistrationContract.REGISTRATION_URI, 673 asyncRegistration.getRegistrationUri().toString()); 674 values.put( 675 MeasurementTables.AsyncRegistrationContract.WEB_DESTINATION, 676 getNullableUriString(asyncRegistration.getWebDestination())); 677 values.put( 678 MeasurementTables.AsyncRegistrationContract.VERIFIED_DESTINATION, 679 getNullableUriString(asyncRegistration.getVerifiedDestination())); 680 values.put( 681 MeasurementTables.AsyncRegistrationContract.OS_DESTINATION, 682 getNullableUriString(asyncRegistration.getOsDestination())); 683 values.put( 684 MeasurementTables.AsyncRegistrationContract.REGISTRANT, 685 getNullableUriString(asyncRegistration.getRegistrant())); 686 values.put( 687 MeasurementTables.AsyncRegistrationContract.TOP_ORIGIN, 688 getNullableUriString(asyncRegistration.getTopOrigin())); 689 values.put( 690 MeasurementTables.AsyncRegistrationContract.SOURCE_TYPE, 691 asyncRegistration.getSourceType() == null 692 ? null 693 : asyncRegistration.getSourceType().ordinal()); 694 values.put( 695 MeasurementTables.AsyncRegistrationContract.REQUEST_TIME, 696 asyncRegistration.getRequestTime()); 697 values.put( 698 MeasurementTables.AsyncRegistrationContract.RETRY_COUNT, 699 asyncRegistration.getRetryCount()); 700 values.put( 701 MeasurementTables.AsyncRegistrationContract.TYPE, 702 asyncRegistration.getType().ordinal()); 703 values.put( 704 MeasurementTables.AsyncRegistrationContract.DEBUG_KEY_ALLOWED, 705 asyncRegistration.getDebugKeyAllowed()); 706 values.put( 707 MeasurementTables.AsyncRegistrationContract.AD_ID_PERMISSION, 708 asyncRegistration.hasAdIdPermission()); 709 values.put( 710 MeasurementTables.AsyncRegistrationContract.REGISTRATION_ID, 711 asyncRegistration.getRegistrationId()); 712 values.put( 713 MeasurementTables.AsyncRegistrationContract.PLATFORM_AD_ID, 714 asyncRegistration.getPlatformAdId()); 715 values.put( 716 MeasurementTables.AsyncRegistrationContract.REDIRECT_BEHAVIOR, 717 asyncRegistration.getRedirectBehavior().name()); 718 long rowId = 719 db.insert( 720 MeasurementTables.AsyncRegistrationContract.TABLE, 721 /* nullColumnHack= */ null, 722 values); 723 if (rowId == -1) { 724 throw new SQLiteException("Async Registration insertion failed."); 725 } 726 } 727 insertToDb(KeyValueData keyValueData, SQLiteDatabase db)728 public static void insertToDb(KeyValueData keyValueData, SQLiteDatabase db) 729 throws SQLiteException { 730 ContentValues values = new ContentValues(); 731 values.put(MeasurementTables.KeyValueDataContract.KEY, keyValueData.getKey()); 732 values.put(MeasurementTables.KeyValueDataContract.VALUE, keyValueData.getValue()); 733 values.put( 734 MeasurementTables.KeyValueDataContract.DATA_TYPE, 735 keyValueData.getDataType().toString()); 736 long rowId = 737 db.insert( 738 MeasurementTables.KeyValueDataContract.TABLE, 739 /* nullColumnHack= */ null, 740 values); 741 if (rowId == -1) { 742 throw new SQLiteException("KeyValueData insertion failed."); 743 } 744 } 745 insertToDb(DebugReport debugReport, SQLiteDatabase db)746 public static void insertToDb(DebugReport debugReport, SQLiteDatabase db) 747 throws SQLiteException { 748 ContentValues values = new ContentValues(); 749 values.put(MeasurementTables.DebugReportContract.ID, debugReport.getId()); 750 values.put(MeasurementTables.DebugReportContract.BODY, debugReport.getBody().toString()); 751 values.put(MeasurementTables.DebugReportContract.TYPE, debugReport.getType()); 752 values.put( 753 MeasurementTables.DebugReportContract.REGISTRANT, 754 getNullableUriString(debugReport.getRegistrant())); 755 long rowId = 756 db.insert( 757 MeasurementTables.DebugReportContract.TABLE, 758 /* nullColumnHack= */ null, 759 values); 760 if (rowId == -1) { 761 throw new SQLiteException("DebugReport insertion failed."); 762 } 763 } 764 getNullableUnsignedLong(UnsignedLong ulong)765 private static Long getNullableUnsignedLong(UnsignedLong ulong) { 766 return Optional.ofNullable(ulong).map(UnsignedLong::getValue).orElse(null); 767 } 768 getNullableUriString(Uri uri)769 private static String getNullableUriString(Uri uri) { 770 return Optional.ofNullable(uri).map(Uri::toString).orElse(null); 771 } 772 setupFlags()773 private void setupFlags() { 774 mocker.mockGetFlags(mFakeFlags); 775 mFlagsMap.entrySet().forEach(entry -> flags.setFlag(entry.getKey(), entry.getValue())); 776 } 777 } 778