• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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