1 /* 2 * Copyright (C) 2022 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 static com.android.adservices.data.measurement.MeasurementTables.ALL_MSMT_TABLES; 20 import static com.android.adservices.data.measurement.MeasurementTables.AggregatableDebugReportBudgetTrackerContract; 21 import static com.android.adservices.data.measurement.MeasurementTables.AppReportHistoryContract; 22 import static com.android.adservices.data.measurement.MeasurementTables.AsyncRegistrationContract; 23 import static com.android.adservices.data.measurement.MeasurementTables.AttributionContract; 24 import static com.android.adservices.data.measurement.MeasurementTables.EventReportContract; 25 import static com.android.adservices.data.measurement.MeasurementTables.KeyValueDataContract; 26 import static com.android.adservices.data.measurement.MeasurementTables.MSMT_TABLE_PREFIX; 27 import static com.android.adservices.data.measurement.MeasurementTables.SourceContract; 28 import static com.android.adservices.data.measurement.MeasurementTables.TriggerContract; 29 import static com.android.adservices.data.measurement.MeasurementTables.XnaIgnoredSourcesContract; 30 import static com.android.adservices.service.Flags.MEASUREMENT_DB_SIZE_LIMIT; 31 import static com.android.adservices.service.Flags.MEASUREMENT_MAX_AGGREGATE_REPORTS_PER_SOURCE; 32 import static com.android.adservices.service.Flags.MEASUREMENT_MAX_EVENT_REPORTS_PER_DESTINATION; 33 import static com.android.adservices.service.Flags.MEASUREMENT_MAX_REPORTING_REGISTER_SOURCE_EXPIRATION_IN_SECONDS; 34 import static com.android.adservices.service.Flags.MEASUREMENT_MIN_REPORTING_ORIGIN_UPDATE_WINDOW; 35 import static com.android.adservices.service.Flags.MEASUREMENT_RATE_LIMIT_WINDOW_MILLISECONDS; 36 import static com.android.adservices.service.Flags.MEASUREMENT_REPORTING_JOB_SERVICE_BATCH_WINDOW_MILLIS; 37 import static com.android.adservices.service.measurement.SourceFixture.ValidSourceParams.SHARED_AGGREGATE_KEYS; 38 import static com.android.adservices.service.measurement.SourceFixture.ValidSourceParams.SOURCE_EVENT_TIME; 39 40 import static com.google.common.truth.Truth.assertThat; 41 42 import static org.junit.Assert.assertEquals; 43 import static org.junit.Assert.assertFalse; 44 import static org.junit.Assert.assertNotEquals; 45 import static org.junit.Assert.assertNotNull; 46 import static org.junit.Assert.assertNull; 47 import static org.junit.Assert.assertThrows; 48 import static org.junit.Assert.assertTrue; 49 import static org.junit.Assert.fail; 50 import static org.mockito.Mockito.doReturn; 51 import static org.mockito.Mockito.mock; 52 import static org.mockito.Mockito.spy; 53 import static org.mockito.Mockito.when; 54 55 import static java.util.concurrent.TimeUnit.DAYS; 56 57 import android.adservices.measurement.DeletionRequest; 58 import android.content.ContentValues; 59 import android.content.Context; 60 import android.database.Cursor; 61 import android.database.DatabaseUtils; 62 import android.database.sqlite.SQLiteDatabase; 63 import android.net.Uri; 64 import android.util.Pair; 65 66 import androidx.annotation.NonNull; 67 import androidx.test.core.app.ApplicationProvider; 68 69 import com.android.adservices.common.WebUtil; 70 import com.android.adservices.data.measurement.MeasurementTables.DebugReportContract; 71 import com.android.adservices.mockito.AdServicesExtendedMockitoRule; 72 import com.android.adservices.service.FakeFlagsFactory; 73 import com.android.adservices.service.Flags; 74 import com.android.adservices.service.FlagsFactory; 75 import com.android.adservices.service.measurement.AggregatableNamedBudgets; 76 import com.android.adservices.service.measurement.AsyncRegistrationFixture; 77 import com.android.adservices.service.measurement.AsyncRegistrationFixture.ValidAsyncRegistrationParams; 78 import com.android.adservices.service.measurement.AttributedTrigger; 79 import com.android.adservices.service.measurement.Attribution; 80 import com.android.adservices.service.measurement.EventReport; 81 import com.android.adservices.service.measurement.EventReportFixture; 82 import com.android.adservices.service.measurement.EventSurfaceType; 83 import com.android.adservices.service.measurement.EventTrigger; 84 import com.android.adservices.service.measurement.KeyValueData; 85 import com.android.adservices.service.measurement.KeyValueData.DataType; 86 import com.android.adservices.service.measurement.Source; 87 import com.android.adservices.service.measurement.SourceFixture; 88 import com.android.adservices.service.measurement.Trigger; 89 import com.android.adservices.service.measurement.TriggerFixture; 90 import com.android.adservices.service.measurement.TriggerSpecs; 91 import com.android.adservices.service.measurement.aggregation.AggregateDebugReportRecord; 92 import com.android.adservices.service.measurement.aggregation.AggregateEncryptionKey; 93 import com.android.adservices.service.measurement.aggregation.AggregateReport; 94 import com.android.adservices.service.measurement.aggregation.AggregateReportFixture; 95 import com.android.adservices.service.measurement.noising.SourceNoiseHandler; 96 import com.android.adservices.service.measurement.registration.AsyncRegistration; 97 import com.android.adservices.service.measurement.reporting.AggregateDebugReportApi; 98 import com.android.adservices.service.measurement.reporting.DebugReport; 99 import com.android.adservices.service.measurement.reporting.EventReportWindowCalcDelegate; 100 import com.android.adservices.service.measurement.util.UnsignedLong; 101 import com.android.adservices.shared.errorlogging.AdServicesErrorLogger; 102 import com.android.dx.mockito.inline.extended.ExtendedMockito; 103 104 import com.google.common.collect.ImmutableList; 105 import com.google.common.collect.ImmutableMultiset; 106 import com.google.common.truth.Truth; 107 108 import org.json.JSONException; 109 import org.junit.After; 110 import org.junit.Before; 111 import org.junit.Rule; 112 import org.junit.Test; 113 import org.junit.runner.RunWith; 114 import org.mockito.Mockito; 115 import org.mockito.internal.util.collections.Sets; 116 import org.mockito.junit.MockitoJUnitRunner; 117 import org.mockito.quality.Strictness; 118 119 import java.time.Instant; 120 import java.util.ArrayList; 121 import java.util.Arrays; 122 import java.util.Collection; 123 import java.util.Collections; 124 import java.util.Comparator; 125 import java.util.HashSet; 126 import java.util.List; 127 import java.util.Map; 128 import java.util.Objects; 129 import java.util.Optional; 130 import java.util.Set; 131 import java.util.UUID; 132 import java.util.concurrent.TimeUnit; 133 import java.util.concurrent.atomic.AtomicLong; 134 import java.util.function.Consumer; 135 import java.util.function.Function; 136 import java.util.stream.Collectors; 137 import java.util.stream.IntStream; 138 import java.util.stream.Stream; 139 140 @RunWith(MockitoJUnitRunner.class) 141 public class MeasurementDaoTest { 142 protected static final Context sContext = ApplicationProvider.getApplicationContext(); 143 private static final Uri APP_TWO_SOURCES = Uri.parse("android-app://com.example1.two-sources"); 144 private static final Uri APP_ONE_SOURCE = Uri.parse("android-app://com.example2.one-source"); 145 private static final String DEFAULT_ENROLLMENT_ID = "enrollment-id"; 146 private static final String ENROLLMENT_ID1 = "enrollment-id1"; 147 private static final Uri APP_TWO_PUBLISHER = 148 Uri.parse("android-app://com.publisher2.two-sources"); 149 private static final Uri APP_ONE_PUBLISHER = 150 Uri.parse("android-app://com.publisher1.one-source"); 151 private static final Uri APP_NO_PUBLISHER = 152 Uri.parse("android-app://com.publisher3.no-sources"); 153 private static final Uri APP_BROWSER = Uri.parse("android-app://com.example1.browser"); 154 private static final Uri WEB_ONE_DESTINATION = WebUtil.validUri("https://example1.test"); 155 private static final Uri WEB_ONE_DESTINATION_DIFFERENT_SUBDOMAIN = 156 WebUtil.validUri("https://store.example1.test"); 157 private static final Uri WEB_ONE_DESTINATION_DIFFERENT_SUBDOMAIN_2 = 158 WebUtil.validUri("https://foo.example1.test"); 159 private static final Uri WEB_TWO_DESTINATION = WebUtil.validUri("https://example2.test"); 160 private static final Uri WEB_THREE_DESTINATION = WebUtil.validUri("https://example3.test"); 161 private static final Uri WEB_TWO_DESTINATION_WITH_PATH = 162 WebUtil.validUri("https://www.example2.test/ad/foo"); 163 private static final Uri APP_ONE_DESTINATION = 164 Uri.parse("android-app://com.example1.one-trigger"); 165 private static final Uri APP_TWO_DESTINATION = 166 Uri.parse("android-app://com.example1.two-triggers"); 167 private static final Uri APP_THREE_DESTINATION = 168 Uri.parse("android-app://com.example1.three-triggers"); 169 private static final Uri APP_THREE_DESTINATION_PATH1 = 170 Uri.parse("android-app://com.example1.three-triggers/path1"); 171 private static final Uri APP_THREE_DESTINATION_PATH2 = 172 Uri.parse("android-app://com.example1.three-triggers/path2"); 173 private static final Uri APP_NO_TRIGGERS = Uri.parse("android-app://com.example1.no-triggers"); 174 private static final Uri INSTALLED_PACKAGE = Uri.parse("android-app://com.example.installed"); 175 private static final Uri WEB_PUBLISHER_ONE = WebUtil.validUri("https://not.example.test"); 176 private static final Uri WEB_PUBLISHER_TWO = WebUtil.validUri("https://notexample.test"); 177 // Differs from WEB_PUBLISHER_ONE by scheme. 178 private static final Uri WEB_PUBLISHER_THREE = WebUtil.validUri("http://not.example.test"); 179 private static final Uri APP_DESTINATION = Uri.parse("android-app://com.destination.example"); 180 private static final Uri REGISTRATION_ORIGIN = 181 WebUtil.validUri("https://subdomain.example.test"); 182 183 private static final Uri REGISTRANT = Uri.parse("android-app://com.example.abc"); 184 private static final Uri INSTALLED_REGISTRANT = Uri.parse("android-app://installed-registrant"); 185 private static final Uri NOT_INSTALLED_REGISTRANT = 186 Uri.parse("android-app://not-installed-registrant"); 187 188 private static final long INSERTION_TIME = 1617297798; 189 private static final long COOLDOWN_WINDOW = TimeUnit.HOURS.toMillis(2); 190 private static final long ATTRIBUTION_SCOPE_LIMIT = 3L; 191 private static final long MAX_EVENT_STATES = 1000L; 192 private static final String REGISTRATION_ID2 = "R2"; 193 194 // Fake ID count for initializing triggers. 195 private int mValueId = 1; 196 private Flags mFlags; 197 private DatastoreManager mDatastoreManager; 198 public static final Uri REGISTRATION_ORIGIN_2 = 199 WebUtil.validUri("https://subdomain_2.example.test"); 200 public static final Uri REGISTRATION_ORIGIN_3 = 201 WebUtil.validUri("https://subdomain_3.example.test"); 202 public static final Uri REGISTRATION_ORIGIN_4 = 203 WebUtil.validUri("https://subdomain_4.example.test"); 204 public static final Uri REGISTRATION_ORIGIN_5 = 205 WebUtil.validUri("https://subdomain_5.example.test"); 206 207 public static final Uri TOP_LEVEL_REGISTRANT_1 = Uri.parse("android-app://com.example1.sample"); 208 public static final Uri TOP_LEVEL_REGISTRANT_2 = Uri.parse("android-app://com.example2.sample"); 209 210 @Rule 211 public final AdServicesExtendedMockitoRule adServicesExtendedMockitoRule = 212 new AdServicesExtendedMockitoRule.Builder(this) 213 .spyStatic(FlagsFactory.class) 214 .spyStatic(MeasurementDbHelper.class) 215 .setStrictness(Strictness.WARN) 216 .build(); 217 218 @Before before()219 public void before() { 220 ExtendedMockito.doReturn(FakeFlagsFactory.getFlagsForTest()).when(FlagsFactory::getFlags); 221 mFlags = FakeFlagsFactory.getFlagsForTest(); 222 mDatastoreManager = 223 new SQLDatastoreManager( 224 MeasurementDbHelper.getInstance(), 225 Mockito.mock(AdServicesErrorLogger.class)); 226 } 227 228 @After cleanup()229 public void cleanup() { 230 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 231 for (String table : ALL_MSMT_TABLES) { 232 db.delete(table, null, null); 233 } 234 } 235 236 @Test testInsertSource()237 public void testInsertSource() { 238 Source validSource = 239 SourceFixture.getValidSourceBuilder() 240 .setEventReportWindows("{'start_time': 1, 'end_times': ['3600', '7200']}") 241 .setStatus(Source.Status.MARKED_TO_DELETE) 242 .setTriggerDataMatching(Source.TriggerDataMatching.EXACT) 243 .setTriggerData(Set.of(new UnsignedLong(23L), new UnsignedLong(1L))) 244 .build(); 245 mDatastoreManager.runInTransaction((dao) -> dao.insertSource(validSource)); 246 247 String sourceId = getFirstSourceIdFromDatastore(); 248 Source source = 249 mDatastoreManager 250 .runInTransactionWithResult( 251 measurementDao -> measurementDao.getSource(sourceId)) 252 .get(); 253 254 assertNotNull(source); 255 assertNotNull(source.getId()); 256 assertNull(source.getAppDestinations()); 257 assertNull(source.getWebDestinations()); 258 assertEquals(validSource.getEnrollmentId(), source.getEnrollmentId()); 259 assertEquals(validSource.getRegistrant(), source.getRegistrant()); 260 assertEquals(validSource.getEventTime(), source.getEventTime()); 261 assertEquals(validSource.getExpiryTime(), source.getExpiryTime()); 262 assertEquals(validSource.getStatus(), source.getStatus()); 263 assertEquals(validSource.getEventReportWindow(), source.getEventReportWindow()); 264 assertEquals( 265 validSource.getAggregatableReportWindow(), source.getAggregatableReportWindow()); 266 assertEquals(validSource.getPriority(), source.getPriority()); 267 assertEquals(validSource.getSourceType(), source.getSourceType()); 268 assertEquals( 269 validSource.getInstallAttributionWindow(), source.getInstallAttributionWindow()); 270 assertEquals(validSource.getInstallCooldownWindow(), source.getInstallCooldownWindow()); 271 assertEquals(validSource.getAttributionMode(), source.getAttributionMode()); 272 assertEquals( 273 validSource.getReinstallReattributionWindow(), 274 source.getReinstallReattributionWindow()); 275 assertEquals(validSource.getAggregateSource(), source.getAggregateSource()); 276 assertEquals(validSource.getFilterDataString(), source.getFilterDataString()); 277 assertEquals(validSource.getSharedFilterDataKeys(), source.getSharedFilterDataKeys()); 278 assertEquals(validSource.getAggregateContributions(), source.getAggregateContributions()); 279 assertEquals(validSource.isDebugReporting(), source.isDebugReporting()); 280 assertEquals(validSource.getSharedAggregationKeys(), source.getSharedAggregationKeys()); 281 assertEquals(validSource.getRegistrationId(), source.getRegistrationId()); 282 assertEquals(validSource.getInstallTime(), source.getInstallTime()); 283 assertEquals(validSource.getPlatformAdId(), source.getPlatformAdId()); 284 assertEquals(validSource.getDebugAdId(), source.getDebugAdId()); 285 assertEquals(validSource.getRegistrationOrigin(), source.getRegistrationOrigin()); 286 assertEquals( 287 validSource.hasCoarseEventReportDestinations(), 288 source.hasCoarseEventReportDestinations()); 289 assertEquals(validSource.getTriggerDataMatching(), source.getTriggerDataMatching()); 290 assertEquals(validSource.getTriggerData(), source.getTriggerData()); 291 assertEquals(validSource.getEventReportWindows(), source.getEventReportWindows()); 292 assertEquals(SourceFixture.ValidSourceParams.SHARED_DEBUG_KEY, source.getSharedDebugKey()); 293 assertEquals(0L, source.getDestinationLimitPriority()); 294 295 // Assert destinations were inserted into the source destination table. 296 297 Pair<List<Uri>, List<Uri>> destinations = 298 mDatastoreManager 299 .runInTransactionWithResult( 300 measurementDao -> 301 measurementDao.getSourceDestinations(source.getId())) 302 .get(); 303 assertTrue( 304 ImmutableMultiset.copyOf(validSource.getAppDestinations()) 305 .equals(ImmutableMultiset.copyOf(destinations.first))); 306 assertTrue( 307 ImmutableMultiset.copyOf(validSource.getWebDestinations()) 308 .equals(ImmutableMultiset.copyOf(destinations.second))); 309 } 310 311 @Test testInsertSource_flexibleEventReport_equal()312 public void testInsertSource_flexibleEventReport_equal() throws JSONException { 313 Source validSource = SourceFixture.getValidSourceWithFlexEventReport(); 314 mDatastoreManager.runInTransaction((dao) -> dao.insertSource(validSource)); 315 316 String sourceId = getFirstSourceIdFromDatastore(); 317 Source source = 318 mDatastoreManager 319 .runInTransactionWithResult( 320 measurementDao -> measurementDao.getSource(sourceId)) 321 .get(); 322 source.buildTriggerSpecs(); 323 324 assertNotNull(source); 325 assertNotNull(source.getId()); 326 assertNull(source.getAppDestinations()); 327 assertNull(source.getWebDestinations()); 328 assertEquals(validSource.getEnrollmentId(), source.getEnrollmentId()); 329 assertEquals(validSource.getRegistrant(), source.getRegistrant()); 330 assertEquals(validSource.getEventTime(), source.getEventTime()); 331 assertEquals(validSource.getExpiryTime(), source.getExpiryTime()); 332 assertEquals(validSource.getEventReportWindow(), source.getEventReportWindow()); 333 assertEquals( 334 validSource.getAggregatableReportWindow(), source.getAggregatableReportWindow()); 335 assertEquals(validSource.getPriority(), source.getPriority()); 336 assertEquals(validSource.getSourceType(), source.getSourceType()); 337 assertEquals( 338 validSource.getInstallAttributionWindow(), source.getInstallAttributionWindow()); 339 assertEquals(validSource.getInstallCooldownWindow(), source.getInstallCooldownWindow()); 340 assertEquals(validSource.getAttributionMode(), source.getAttributionMode()); 341 assertEquals(validSource.getAggregateSource(), source.getAggregateSource()); 342 assertEquals(validSource.getFilterDataString(), source.getFilterDataString()); 343 assertEquals(validSource.getAggregateContributions(), source.getAggregateContributions()); 344 assertEquals(validSource.isDebugReporting(), source.isDebugReporting()); 345 assertEquals(validSource.getSharedAggregationKeys(), source.getSharedAggregationKeys()); 346 assertEquals(validSource.getRegistrationId(), source.getRegistrationId()); 347 assertEquals(validSource.getInstallTime(), source.getInstallTime()); 348 assertEquals(validSource.getPlatformAdId(), source.getPlatformAdId()); 349 assertEquals(validSource.getDebugAdId(), source.getDebugAdId()); 350 assertEquals(validSource.getRegistrationOrigin(), source.getRegistrationOrigin()); 351 assertEquals( 352 validSource.getTriggerSpecs().getMaxReports(), 353 source.getMaxEventLevelReports().intValue()); 354 assertEquals( 355 validSource.getTriggerSpecs().encodeToJson(), 356 source.getTriggerSpecsString()); 357 assertNull(source.getEventAttributionStatus()); 358 assertEquals( 359 validSource.getTriggerSpecs().encodePrivacyParametersToJsonString(), 360 source.getPrivacyParameters()); 361 assertEquals(validSource.getTriggerSpecs(), source.getTriggerSpecs()); 362 363 // Assert destinations were inserted into the source destination table. 364 Pair<List<Uri>, List<Uri>> destinations = 365 mDatastoreManager 366 .runInTransactionWithResult( 367 measurementDao -> 368 measurementDao.getSourceDestinations(source.getId())) 369 .get(); 370 assertTrue( 371 ImmutableMultiset.copyOf(validSource.getAppDestinations()) 372 .equals(ImmutableMultiset.copyOf(destinations.first))); 373 assertTrue( 374 ImmutableMultiset.copyOf(validSource.getWebDestinations()) 375 .equals(ImmutableMultiset.copyOf(destinations.second))); 376 } 377 378 @Test testInsertSource_reachedDbSizeLimitOnEdgeCase_doNotInsert()379 public void testInsertSource_reachedDbSizeLimitOnEdgeCase_doNotInsert() { 380 insertSourceReachingDbSizeLimit(/* dbSize= */ 100L, /* dbSizeMaxLimit= */ 100L); 381 } 382 383 @Test testInsertSource_reachedDbSizeLimitUpperEdgeCase_doNotInsert()384 public void testInsertSource_reachedDbSizeLimitUpperEdgeCase_doNotInsert() { 385 insertSourceReachingDbSizeLimit(/* dbSize= */ 101L, /* dbSizeMaxLimit= */ 100L); 386 } 387 388 @Test testInsertSource_attributionScopeEnabled_success()389 public void testInsertSource_attributionScopeEnabled_success() { 390 mFlags = mock(Flags.class); 391 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 392 doReturn(true).when(mFlags).getMeasurementEnableAttributionScope(); 393 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mFlags).getMeasurementDbSizeLimit(); 394 395 Source validSource = 396 insertSourceForAttributionScope( 397 List.of("1", "2", "3"), 398 ATTRIBUTION_SCOPE_LIMIT, 399 MAX_EVENT_STATES, 400 SOURCE_EVENT_TIME, 401 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 402 List.of(APP_ONE_DESTINATION)); 403 Source source = 404 mDatastoreManager 405 .runInTransactionWithResult( 406 measurementDao -> measurementDao.getSource(validSource.getId())) 407 .get(); 408 assertThat(source.getAttributionScopeLimit()) 409 .isEqualTo(validSource.getAttributionScopeLimit()); 410 assertThat(source.getMaxEventStates()).isEqualTo(validSource.getMaxEventStates()); 411 List<String> attributionScopes = 412 mDatastoreManager 413 .runInTransactionWithResult( 414 measurementDao -> 415 measurementDao.getSourceAttributionScopes(source.getId())) 416 .get(); 417 assertThat(attributionScopes).containsExactlyElementsIn(validSource.getAttributionScopes()); 418 } 419 420 @Test 421 public void testInsertSource_attributionScopeDisabled_doesNotInsertAttributionScopeRelatedData()422 testInsertSource_attributionScopeDisabled_doesNotInsertAttributionScopeRelatedData() { 423 mFlags = mock(Flags.class); 424 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 425 doReturn(false).when(mFlags).getMeasurementEnableAttributionScope(); 426 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mFlags).getMeasurementDbSizeLimit(); 427 428 Source validSource = 429 insertSourceForAttributionScope( 430 List.of("1", "2", "3"), 431 ATTRIBUTION_SCOPE_LIMIT, 432 MAX_EVENT_STATES, 433 SOURCE_EVENT_TIME, 434 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 435 List.of(APP_ONE_DESTINATION)); 436 Source source = 437 mDatastoreManager 438 .runInTransactionWithResult( 439 measurementDao -> measurementDao.getSource(validSource.getId())) 440 .get(); 441 442 assertThat(source.getAttributionScopeLimit()).isEqualTo(null); 443 assertThat(source.getMaxEventStates()).isEqualTo(null); 444 List<String> attributionScopes = 445 mDatastoreManager 446 .runInTransactionWithResult( 447 measurementDao -> 448 measurementDao.getSourceAttributionScopes(source.getId())) 449 .get(); 450 assertThat(attributionScopes).isEmpty(); 451 } 452 453 @Test testInsertSource_aggregateDebugReportingEnabled_success()454 public void testInsertSource_aggregateDebugReportingEnabled_success() { 455 mFlags = mock(Flags.class); 456 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 457 doReturn(true).when(mFlags).getMeasurementEnableAggregateDebugReporting(); 458 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mFlags).getMeasurementDbSizeLimit(); 459 460 Source validSource = 461 SourceFixture.getValidSourceBuilder() 462 .setEventReportWindows("{'start_time': 1, 'end_times': ['3600', '7200']}") 463 .setStatus(Source.Status.MARKED_TO_DELETE) 464 .setTriggerDataMatching(Source.TriggerDataMatching.EXACT) 465 .setTriggerData(Set.of(new UnsignedLong(23L), new UnsignedLong(1L))) 466 .build(); 467 mDatastoreManager.runInTransaction((dao) -> dao.insertSource(validSource)); 468 469 String sourceId = getFirstSourceIdFromDatastore(); 470 Source source = 471 mDatastoreManager 472 .runInTransactionWithResult( 473 measurementDao -> measurementDao.getSource(sourceId)) 474 .get(); 475 476 assertThat(source).isNotNull(); 477 assertThat(source.getId()).isNotNull(); 478 assertThat(source.getAppDestinations()).isNull(); 479 assertThat(source.getWebDestinations()).isNull(); 480 assertThat(validSource.getEnrollmentId()).isEqualTo(source.getEnrollmentId()); 481 assertThat(validSource.getRegistrant()).isEqualTo(source.getRegistrant()); 482 assertThat(validSource.getEventTime()).isEqualTo(source.getEventTime()); 483 assertThat(validSource.getExpiryTime()).isEqualTo(source.getExpiryTime()); 484 assertThat(validSource.getStatus()).isEqualTo(source.getStatus()); 485 assertThat(validSource.getEventReportWindow()).isEqualTo(source.getEventReportWindow()); 486 assertThat(validSource.getAggregatableReportWindow()) 487 .isEqualTo(source.getAggregatableReportWindow()); 488 assertThat(validSource.getPriority()).isEqualTo(source.getPriority()); 489 assertThat(validSource.getSourceType()).isEqualTo(source.getSourceType()); 490 assertThat(validSource.getInstallAttributionWindow()) 491 .isEqualTo(source.getInstallAttributionWindow()); 492 assertThat(validSource.getInstallCooldownWindow()) 493 .isEqualTo(source.getInstallCooldownWindow()); 494 assertThat(validSource.getAttributionMode()).isEqualTo(source.getAttributionMode()); 495 assertThat(validSource.getReinstallReattributionWindow()) 496 .isEqualTo(source.getReinstallReattributionWindow()); 497 assertThat(validSource.getAggregateSource()).isEqualTo(source.getAggregateSource()); 498 assertThat(validSource.getFilterDataString()).isEqualTo(source.getFilterDataString()); 499 assertThat(validSource.getSharedFilterDataKeys()) 500 .isEqualTo(source.getSharedFilterDataKeys()); 501 assertThat(validSource.getAggregateContributions()) 502 .isEqualTo(source.getAggregateContributions()); 503 assertThat(validSource.isDebugReporting()).isEqualTo(source.isDebugReporting()); 504 assertThat(validSource.getSharedAggregationKeys()) 505 .isEqualTo(source.getSharedAggregationKeys()); 506 assertThat(validSource.getRegistrationId()).isEqualTo(source.getRegistrationId()); 507 assertThat(validSource.getInstallTime()).isEqualTo(source.getInstallTime()); 508 assertThat(validSource.getPlatformAdId()).isEqualTo(source.getPlatformAdId()); 509 assertThat(validSource.getDebugAdId()).isEqualTo(source.getDebugAdId()); 510 assertThat(validSource.getRegistrationOrigin()).isEqualTo(source.getRegistrationOrigin()); 511 assertThat(validSource.hasCoarseEventReportDestinations()) 512 .isEqualTo(source.hasCoarseEventReportDestinations()); 513 assertThat(validSource.getTriggerDataMatching()).isEqualTo(source.getTriggerDataMatching()); 514 assertThat(validSource.getTriggerData()).isEqualTo(source.getTriggerData()); 515 assertThat(validSource.getEventReportWindows()).isEqualTo(source.getEventReportWindows()); 516 assertThat(SourceFixture.ValidSourceParams.SHARED_DEBUG_KEY) 517 .isEqualTo(source.getSharedDebugKey()); 518 assertThat(source.getDestinationLimitPriority()).isEqualTo(0L); 519 assertThat(validSource.getAggregateDebugReportingString()) 520 .isEqualTo(source.getAggregateDebugReportingString()); 521 assertThat(validSource.getAggregateDebugReportContributions()) 522 .isEqualTo(source.getAggregateDebugReportContributions()); 523 524 // Assert destinations were inserted into the source destination table. 525 526 Pair<List<Uri>, List<Uri>> destinations = 527 mDatastoreManager 528 .runInTransactionWithResult( 529 measurementDao -> 530 measurementDao.getSourceDestinations(source.getId())) 531 .get(); 532 assertThat( 533 ImmutableMultiset.copyOf(validSource.getAppDestinations()) 534 .equals(ImmutableMultiset.copyOf(destinations.first))) 535 .isTrue(); 536 assertThat( 537 ImmutableMultiset.copyOf(validSource.getWebDestinations()) 538 .equals(ImmutableMultiset.copyOf(destinations.second))) 539 .isTrue(); 540 } 541 542 @Test testInsertSource_aggregateDebugReportingDisabled_relatedDataNotInserted()543 public void testInsertSource_aggregateDebugReportingDisabled_relatedDataNotInserted() { 544 mFlags = mock(Flags.class); 545 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 546 doReturn(false).when(mFlags).getMeasurementEnableAggregateDebugReporting(); 547 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mFlags).getMeasurementDbSizeLimit(); 548 549 Source validSource = 550 SourceFixture.getValidSourceBuilder() 551 .setEventReportWindows("{'start_time': 1, 'end_times': ['3600', '7200']}") 552 .setStatus(Source.Status.MARKED_TO_DELETE) 553 .setTriggerDataMatching(Source.TriggerDataMatching.EXACT) 554 .setTriggerData(Set.of(new UnsignedLong(23L), new UnsignedLong(1L))) 555 .build(); 556 mDatastoreManager.runInTransaction((dao) -> dao.insertSource(validSource)); 557 558 String sourceId = getFirstSourceIdFromDatastore(); 559 Source source = 560 mDatastoreManager 561 .runInTransactionWithResult( 562 measurementDao -> measurementDao.getSource(sourceId)) 563 .get(); 564 565 assertThat(source).isNotNull(); 566 assertThat(source.getId()).isNotNull(); 567 assertThat(source.getAppDestinations()).isNull(); 568 assertThat(source.getWebDestinations()).isNull(); 569 assertThat(validSource.getEnrollmentId()).isEqualTo(source.getEnrollmentId()); 570 assertThat(validSource.getRegistrant()).isEqualTo(source.getRegistrant()); 571 assertThat(validSource.getEventTime()).isEqualTo(source.getEventTime()); 572 assertThat(validSource.getExpiryTime()).isEqualTo(source.getExpiryTime()); 573 assertThat(validSource.getStatus()).isEqualTo(source.getStatus()); 574 assertThat(validSource.getEventReportWindow()).isEqualTo(source.getEventReportWindow()); 575 assertThat(validSource.getAggregatableReportWindow()) 576 .isEqualTo(source.getAggregatableReportWindow()); 577 assertThat(validSource.getPriority()).isEqualTo(source.getPriority()); 578 assertThat(validSource.getSourceType()).isEqualTo(source.getSourceType()); 579 assertThat(validSource.getInstallAttributionWindow()) 580 .isEqualTo(source.getInstallAttributionWindow()); 581 assertThat(validSource.getInstallCooldownWindow()) 582 .isEqualTo(source.getInstallCooldownWindow()); 583 assertThat(validSource.getAttributionMode()).isEqualTo(source.getAttributionMode()); 584 assertThat(validSource.getReinstallReattributionWindow()) 585 .isEqualTo(source.getReinstallReattributionWindow()); 586 assertThat(validSource.getAggregateSource()).isEqualTo(source.getAggregateSource()); 587 assertThat(validSource.getFilterDataString()).isEqualTo(source.getFilterDataString()); 588 assertThat(validSource.getSharedFilterDataKeys()) 589 .isEqualTo(source.getSharedFilterDataKeys()); 590 assertThat(validSource.getAggregateContributions()) 591 .isEqualTo(source.getAggregateContributions()); 592 assertThat(validSource.isDebugReporting()).isEqualTo(source.isDebugReporting()); 593 assertThat(validSource.getSharedAggregationKeys()) 594 .isEqualTo(source.getSharedAggregationKeys()); 595 assertThat(validSource.getRegistrationId()).isEqualTo(source.getRegistrationId()); 596 assertThat(validSource.getInstallTime()).isEqualTo(source.getInstallTime()); 597 assertThat(validSource.getPlatformAdId()).isEqualTo(source.getPlatformAdId()); 598 assertThat(validSource.getDebugAdId()).isEqualTo(source.getDebugAdId()); 599 assertThat(validSource.getRegistrationOrigin()).isEqualTo(source.getRegistrationOrigin()); 600 assertThat(validSource.hasCoarseEventReportDestinations()) 601 .isEqualTo(source.hasCoarseEventReportDestinations()); 602 assertThat(validSource.getTriggerDataMatching()).isEqualTo(source.getTriggerDataMatching()); 603 assertThat(validSource.getTriggerData()).isEqualTo(source.getTriggerData()); 604 assertThat(validSource.getEventReportWindows()).isEqualTo(source.getEventReportWindows()); 605 assertThat(SourceFixture.ValidSourceParams.SHARED_DEBUG_KEY) 606 .isEqualTo(source.getSharedDebugKey()); 607 assertThat(source.getDestinationLimitPriority()).isEqualTo(0L); 608 assertThat(source.getAggregateDebugReportingString()).isEqualTo(null); 609 assertThat(source.getAggregateDebugReportContributions()).isEqualTo(0L); 610 611 // Assert destinations were inserted into the source destination table. 612 613 Pair<List<Uri>, List<Uri>> destinations = 614 mDatastoreManager 615 .runInTransactionWithResult( 616 measurementDao -> 617 measurementDao.getSourceDestinations(source.getId())) 618 .get(); 619 assertThat( 620 ImmutableMultiset.copyOf(validSource.getAppDestinations()) 621 .equals(ImmutableMultiset.copyOf(destinations.first))) 622 .isTrue(); 623 assertThat( 624 ImmutableMultiset.copyOf(validSource.getWebDestinations()) 625 .equals(ImmutableMultiset.copyOf(destinations.second))) 626 .isTrue(); 627 } 628 629 @Test testInsertSource_aggregatableNamedBudgetsEnabled_success()630 public void testInsertSource_aggregatableNamedBudgetsEnabled_success() { 631 mFlags = mock(Flags.class); 632 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 633 when(mFlags.getMeasurementEnableAggregatableNamedBudgets()).thenReturn(true); 634 when(mFlags.getMeasurementDbSizeLimit()).thenReturn(MEASUREMENT_DB_SIZE_LIMIT); 635 636 AggregatableNamedBudgets aggregatableNamedBudgets = new AggregatableNamedBudgets(); 637 aggregatableNamedBudgets.createContributionBudget("budget1", 400); 638 aggregatableNamedBudgets.setContribution("budget1", 150); 639 aggregatableNamedBudgets.createContributionBudget("budget2", 750); 640 aggregatableNamedBudgets.setContribution("budget2", 320); 641 Source validSource = 642 insertSourceForAggregatableNamedBudgets( 643 aggregatableNamedBudgets, 644 SOURCE_EVENT_TIME, 645 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 646 List.of(APP_ONE_DESTINATION)); 647 648 AggregatableNamedBudgets.BudgetAndContribution daoBudgetAndContribution1 = 649 mDatastoreManager 650 .runInTransactionWithResult( 651 measurementDao -> 652 measurementDao 653 .getSourceAggregatableNamedBudgetAndContribution( 654 validSource.getId(), "budget1")) 655 .get(); 656 assertThat(daoBudgetAndContribution1.getAggregateContribution()) 657 .isEqualTo( 658 validSource 659 .getAggregatableNamedBudgets() 660 .maybeGetContribution("budget1") 661 .get()); 662 assertThat(daoBudgetAndContribution1.getBudget()) 663 .isEqualTo( 664 validSource.getAggregatableNamedBudgets().maybeGetBudget("budget1").get()); 665 AggregatableNamedBudgets.BudgetAndContribution daoBudgetAndContribution2 = 666 mDatastoreManager 667 .runInTransactionWithResult( 668 measurementDao -> 669 measurementDao 670 .getSourceAggregatableNamedBudgetAndContribution( 671 validSource.getId(), "budget2")) 672 .get(); 673 assertThat(daoBudgetAndContribution2.getAggregateContribution()) 674 .isEqualTo( 675 validSource 676 .getAggregatableNamedBudgets() 677 .maybeGetContribution("budget2") 678 .get()); 679 assertThat(daoBudgetAndContribution2.getBudget()) 680 .isEqualTo( 681 validSource.getAggregatableNamedBudgets().maybeGetBudget("budget2").get()); 682 } 683 684 @Test testInsertSource_aggregatableNamedBudgetsDisabled_relatedDataNotInserted()685 public void testInsertSource_aggregatableNamedBudgetsDisabled_relatedDataNotInserted() { 686 mFlags = mock(Flags.class); 687 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 688 when(mFlags.getMeasurementEnableAggregatableNamedBudgets()).thenReturn(false); 689 when(mFlags.getMeasurementDbSizeLimit()).thenReturn(MEASUREMENT_DB_SIZE_LIMIT); 690 691 AggregatableNamedBudgets aggregatableNamedBudgets = new AggregatableNamedBudgets(); 692 aggregatableNamedBudgets.createContributionBudget("budget3", 470); 693 aggregatableNamedBudgets.setContribution("budget3", 153); 694 Source validSource = 695 insertSourceForAggregatableNamedBudgets( 696 aggregatableNamedBudgets, 697 SOURCE_EVENT_TIME, 698 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 699 List.of(APP_ONE_DESTINATION)); 700 701 Optional<AggregatableNamedBudgets.BudgetAndContribution> daoBudgetAndContribution = 702 mDatastoreManager.runInTransactionWithResult( 703 measurementDao -> 704 measurementDao.getSourceAggregatableNamedBudgetAndContribution( 705 validSource.getId(), "budget3")); 706 assertThat(daoBudgetAndContribution).isEmpty(); 707 } 708 insertSourceReachingDbSizeLimit(long dbSize, long dbSizeMaxLimit)709 private void insertSourceReachingDbSizeLimit(long dbSize, long dbSizeMaxLimit) { 710 final Source validSource = SourceFixture.getValidSource(); 711 712 // Mocking that the DB file has a size of 100 bytes 713 final MeasurementDbHelper spyMeasurementDbHelper = spy(MeasurementDbHelper.getInstance()); 714 ExtendedMockito.doReturn(spyMeasurementDbHelper).when(MeasurementDbHelper::getInstance); 715 ExtendedMockito.doReturn(dbSize).when(spyMeasurementDbHelper).getDbFileSize(); 716 717 // Mocking that the flags return a max limit size of 100 bytes 718 Flags mockFlags = Mockito.mock(Flags.class); 719 ExtendedMockito.doReturn(mockFlags).when(FlagsFactory::getFlags); 720 ExtendedMockito.doReturn(dbSizeMaxLimit).when(mockFlags).getMeasurementDbSizeLimit(); 721 722 mDatastoreManager.runInTransaction((dao) -> dao.insertSource(validSource)); 723 724 try (Cursor sourceCursor = 725 MeasurementDbHelper.getInstance() 726 .getReadableDatabase() 727 .query(SourceContract.TABLE, null, null, null, null, null, null)) { 728 assertFalse(sourceCursor.moveToNext()); 729 } 730 } 731 732 @Test testInsertTrigger()733 public void testInsertTrigger() { 734 Trigger validTrigger = TriggerFixture.getValidTrigger(); 735 mDatastoreManager.runInTransaction((dao) -> dao.insertTrigger(validTrigger)); 736 737 try (Cursor triggerCursor = 738 MeasurementDbHelper.getInstance() 739 .getReadableDatabase() 740 .query(TriggerContract.TABLE, null, null, null, null, null, null)) { 741 assertTrue(triggerCursor.moveToNext()); 742 Trigger trigger = SqliteObjectMapper.constructTriggerFromCursor(triggerCursor); 743 assertNotNull(trigger); 744 assertNotNull(trigger.getId()); 745 assertEquals( 746 validTrigger.getAttributionDestination(), trigger.getAttributionDestination()); 747 assertEquals(validTrigger.getDestinationType(), trigger.getDestinationType()); 748 assertEquals(validTrigger.getEnrollmentId(), trigger.getEnrollmentId()); 749 assertEquals(validTrigger.getRegistrant(), trigger.getRegistrant()); 750 assertEquals(validTrigger.getTriggerTime(), trigger.getTriggerTime()); 751 assertEquals(validTrigger.getEventTriggers(), trigger.getEventTriggers()); 752 assertEquals(validTrigger.getAttributionConfig(), trigger.getAttributionConfig()); 753 assertEquals(validTrigger.getAdtechKeyMapping(), trigger.getAdtechKeyMapping()); 754 assertEquals(validTrigger.getPlatformAdId(), trigger.getPlatformAdId()); 755 assertEquals(validTrigger.getDebugAdId(), trigger.getDebugAdId()); 756 assertEquals(validTrigger.getRegistrationOrigin(), trigger.getRegistrationOrigin()); 757 assertEquals( 758 validTrigger.getAggregationCoordinatorOrigin(), 759 trigger.getAggregationCoordinatorOrigin()); 760 assertEquals( 761 validTrigger.getAggregatableSourceRegistrationTimeConfig(), 762 trigger.getAggregatableSourceRegistrationTimeConfig()); 763 assertEquals(validTrigger.getTriggerContextId(), trigger.getTriggerContextId()); 764 assertEquals( 765 validTrigger.getAttributionScopesString(), 766 trigger.getAttributionScopesString()); 767 assertEquals( 768 validTrigger.getAggregatableFilteringIdMaxBytes(), 769 trigger.getAggregatableFilteringIdMaxBytes()); 770 assertThat(validTrigger.getAggregateDebugReportingString()) 771 .isEqualTo(trigger.getAggregateDebugReportingString()); 772 } 773 } 774 775 @Test testInsertTrigger_reachedDbSizeLimitOnEdgeCase_doNotInsert()776 public void testInsertTrigger_reachedDbSizeLimitOnEdgeCase_doNotInsert() { 777 insertTriggerReachingDbSizeLimit(/* dbSize= */ 100L, /* dbSizeMaxLimit= */ 100L); 778 } 779 780 @Test testInsertTrigger_reachedDbSizeLimitUpperEdgeCase_doNotInsert()781 public void testInsertTrigger_reachedDbSizeLimitUpperEdgeCase_doNotInsert() { 782 insertTriggerReachingDbSizeLimit(/* dbSize= */ 101L, /* dbSizeMaxLimit= */ 100L); 783 } 784 insertTriggerReachingDbSizeLimit(long dbSize, long dbSizeMaxLimit)785 private void insertTriggerReachingDbSizeLimit(long dbSize, long dbSizeMaxLimit) { 786 final Trigger validTrigger = TriggerFixture.getValidTrigger(); 787 788 // Mocking that the DB file has a size of 100 bytes 789 final MeasurementDbHelper spyMeasurementDbHelper = spy(MeasurementDbHelper.getInstance()); 790 ExtendedMockito.doReturn(spyMeasurementDbHelper).when(MeasurementDbHelper::getInstance); 791 ExtendedMockito.doReturn(dbSize).when(spyMeasurementDbHelper).getDbFileSize(); 792 793 // Mocking that the flags return a max limit size of 100 bytes 794 Flags mockFlags = Mockito.mock(Flags.class); 795 ExtendedMockito.doReturn(mockFlags).when(FlagsFactory::getFlags); 796 ExtendedMockito.doReturn(dbSizeMaxLimit).when(mockFlags).getMeasurementDbSizeLimit(); 797 798 mDatastoreManager.runInTransaction((dao) -> dao.insertTrigger(validTrigger)); 799 800 try (Cursor sourceCursor = 801 MeasurementDbHelper.getInstance() 802 .getReadableDatabase() 803 .query(TriggerContract.TABLE, null, null, null, null, null, null)) { 804 assertFalse(sourceCursor.moveToNext()); 805 } 806 } 807 808 @Test testGetNumSourcesPerPublisher_publisherTypeApp()809 public void testGetNumSourcesPerPublisher_publisherTypeApp() { 810 setupSourceAndTriggerData(); 811 mDatastoreManager.runInTransaction( 812 measurementDao -> { 813 assertEquals( 814 2, 815 measurementDao.getNumSourcesPerPublisher( 816 APP_TWO_PUBLISHER, EventSurfaceType.APP)); 817 }); 818 mDatastoreManager.runInTransaction( 819 measurementDao -> { 820 assertEquals( 821 1, 822 measurementDao.getNumSourcesPerPublisher( 823 APP_ONE_PUBLISHER, EventSurfaceType.APP)); 824 }); 825 mDatastoreManager.runInTransaction( 826 measurementDao -> { 827 assertEquals( 828 0, 829 measurementDao.getNumSourcesPerPublisher( 830 APP_NO_PUBLISHER, EventSurfaceType.APP)); 831 }); 832 } 833 834 @Test testGetNumSourcesPerPublisher_publisherTypeWeb()835 public void testGetNumSourcesPerPublisher_publisherTypeWeb() { 836 setupSourceDataForPublisherTypeWeb(); 837 mDatastoreManager.runInTransaction( 838 measurementDao -> { 839 assertEquals( 840 1, 841 measurementDao.getNumSourcesPerPublisher( 842 WEB_PUBLISHER_ONE, EventSurfaceType.WEB)); 843 }); 844 mDatastoreManager.runInTransaction( 845 measurementDao -> { 846 assertEquals( 847 2, 848 measurementDao.getNumSourcesPerPublisher( 849 WEB_PUBLISHER_TWO, EventSurfaceType.WEB)); 850 }); 851 mDatastoreManager.runInTransaction( 852 measurementDao -> { 853 assertEquals( 854 1, 855 measurementDao.getNumSourcesPerPublisher( 856 WEB_PUBLISHER_THREE, EventSurfaceType.WEB)); 857 }); 858 } 859 860 @Test testGetUninstalledAppNamesContainingData_withDataInSource()861 public void testGetUninstalledAppNamesContainingData_withDataInSource() { 862 // Setup records in source storage 863 final List<Uri> appsInstalled = 864 List.of(buildRegistrant("foo"), buildRegistrant("bar"), buildRegistrant("baz")); 865 final List<Uri> appsNotInstalled = List.of(buildRegistrant("qux"), buildRegistrant("quux")); 866 insertSourceForPackageName( 867 Stream.concat(appsInstalled.stream(), appsNotInstalled.stream()) 868 .toArray(Uri[]::new)); 869 870 // Execution 871 final Optional<List<Uri>> uninstalledAppNames = 872 mDatastoreManager.runInTransactionWithResult( 873 measurementDao -> 874 measurementDao.getUninstalledAppNamesHavingMeasurementData( 875 appsInstalled)); 876 877 // Validation, apps not installed should be returned 878 assertNotNull(uninstalledAppNames); 879 assertTrue(uninstalledAppNames.isPresent()); 880 assertEquals(2, uninstalledAppNames.get().size()); 881 Set<Uri> result = new HashSet<>(uninstalledAppNames.get()); 882 assertTrue(result.contains(buildRegistrant("qux"))); 883 assertTrue(result.contains(buildRegistrant("quux"))); 884 } 885 886 @Test testGetUninstalledAppNamesContainingData_withDataInTrigger()887 public void testGetUninstalledAppNamesContainingData_withDataInTrigger() { 888 // Setup records in trigger storage 889 final List<Uri> appsInstalled = 890 List.of(buildRegistrant("foo"), buildRegistrant("bar"), buildRegistrant("baz")); 891 final List<Uri> appsNotInstalled = List.of(buildRegistrant("qux"), buildRegistrant("quux")); 892 insertTriggerForPackageName( 893 Stream.concat(appsInstalled.stream(), appsNotInstalled.stream()) 894 .toArray(Uri[]::new)); 895 896 // Execution 897 final Optional<List<Uri>> uninstalledAppNames = 898 mDatastoreManager.runInTransactionWithResult( 899 measurementDao -> 900 measurementDao.getUninstalledAppNamesHavingMeasurementData( 901 appsInstalled)); 902 903 // Validation, apps not installed should be returned 904 assertNotNull(uninstalledAppNames); 905 assertTrue(uninstalledAppNames.isPresent()); 906 assertEquals(2, uninstalledAppNames.get().size()); 907 Set<Uri> result = new HashSet<>(uninstalledAppNames.get()); 908 assertTrue(result.contains(buildRegistrant("qux"))); 909 assertTrue(result.contains(buildRegistrant("quux"))); 910 } 911 912 @Test testGetUninstalledAppNamesContainingData_withDataInTriggerAndSource()913 public void testGetUninstalledAppNamesContainingData_withDataInTriggerAndSource() { 914 // Setup records in source and trigger storage 915 final List<Uri> appsInstalled = 916 List.of(buildRegistrant("foo"), buildRegistrant("bar"), buildRegistrant("baz")); 917 final List<Uri> appsNotInstalled = List.of(buildRegistrant("qux"), buildRegistrant("quux")); 918 final Uri[] appNames = 919 Stream.concat(appsInstalled.stream(), appsNotInstalled.stream()) 920 .toArray(Uri[]::new); 921 insertSourceForPackageName(appNames); 922 insertTriggerForPackageName(appNames); 923 924 // Execution 925 final Optional<List<Uri>> uninstalledAppNames = 926 mDatastoreManager.runInTransactionWithResult( 927 measurementDao -> 928 measurementDao.getUninstalledAppNamesHavingMeasurementData( 929 appsInstalled)); 930 931 // Validation, apps not installed should be returned 932 assertNotNull(uninstalledAppNames); 933 assertTrue(uninstalledAppNames.isPresent()); 934 assertEquals(2, uninstalledAppNames.get().size()); 935 Set<Uri> result = new HashSet<>(uninstalledAppNames.get()); 936 assertTrue(result.contains(buildRegistrant("qux"))); 937 assertTrue(result.contains(buildRegistrant("quux"))); 938 } 939 940 @Test testGetUninstalledAppNamesContainingData_withDataInAsync()941 public void testGetUninstalledAppNamesContainingData_withDataInAsync() { 942 // Setup records in source storage 943 final List<Uri> appsInstalled = 944 List.of(buildRegistrant("foo"), buildRegistrant("bar"), buildRegistrant("baz")); 945 final List<Uri> appsNotInstalled = List.of(buildRegistrant("qux"), buildRegistrant("quux")); 946 insertAsyncRecordForPackageName( 947 Stream.concat(appsInstalled.stream(), appsNotInstalled.stream()) 948 .toArray(Uri[]::new)); 949 950 // Execution 951 final Optional<List<Uri>> uninstalledAppNames = 952 mDatastoreManager.runInTransactionWithResult( 953 measurementDao -> 954 measurementDao.getUninstalledAppNamesHavingMeasurementData( 955 appsInstalled)); 956 957 // Validation, apps not installed should be returned 958 assertNotNull(uninstalledAppNames); 959 assertTrue(uninstalledAppNames.isPresent()); 960 assertEquals(2, uninstalledAppNames.get().size()); 961 Set<Uri> result = new HashSet<>(uninstalledAppNames.get()); 962 assertTrue(result.contains(buildRegistrant("qux"))); 963 assertTrue(result.contains(buildRegistrant("quux"))); 964 } 965 966 @Test testGetUninstalledAppNamesContainingData_withAnyDataAndNoAppsUninstalled()967 public void testGetUninstalledAppNamesContainingData_withAnyDataAndNoAppsUninstalled() { 968 // Setup records in source storage (any storage), all these apps are still installed 969 final List<Uri> appsInstalled = 970 List.of( 971 buildRegistrant("foo"), 972 buildRegistrant("bar"), 973 buildRegistrant("baz"), 974 buildRegistrant("qux"), 975 buildRegistrant("quux")); 976 insertSourceForPackageName(appsInstalled.stream().toArray(Uri[]::new)); 977 978 // Execution 979 final Optional<List<Uri>> uninstalledAppNames = 980 mDatastoreManager.runInTransactionWithResult( 981 measurementDao -> 982 measurementDao.getUninstalledAppNamesHavingMeasurementData( 983 appsInstalled)); 984 985 // Validation, apps not installed should be returned 986 assertNotNull(uninstalledAppNames); 987 assertTrue(uninstalledAppNames.isPresent()); 988 assertTrue(uninstalledAppNames.get().isEmpty()); 989 assertEquals(0, uninstalledAppNames.get().size()); 990 } 991 992 @Test testGetUninstalledAppNamesContainingData_withNoData()993 public void testGetUninstalledAppNamesContainingData_withNoData() { 994 // Setup records, these apps don't have source nor trigger data 995 final List<Uri> appsInstalled = 996 List.of( 997 buildRegistrant("foo"), 998 buildRegistrant("bar"), 999 buildRegistrant("baz"), 1000 buildRegistrant("qux"), 1001 buildRegistrant("quux")); 1002 1003 // Execution 1004 final Optional<List<Uri>> uninstalledAppNames = 1005 mDatastoreManager.runInTransactionWithResult( 1006 measurementDao -> 1007 measurementDao.getUninstalledAppNamesHavingMeasurementData( 1008 appsInstalled)); 1009 1010 // Validation, apps not installed should be returned 1011 assertNotNull(uninstalledAppNames); 1012 assertTrue(uninstalledAppNames.isPresent()); 1013 assertTrue(uninstalledAppNames.get().isEmpty()); 1014 assertEquals(0, uninstalledAppNames.get().size()); 1015 } 1016 1017 @Test testCountDistinctReportingOriginPerPublisherXDestinationInAttribution_atWindow()1018 public void testCountDistinctReportingOriginPerPublisherXDestinationInAttribution_atWindow() { 1019 Uri sourceSite = Uri.parse("android-app://publisher.app"); 1020 Uri appDestination = Uri.parse("android-app://destination.app"); 1021 String registrant = "android-app://registrant.app"; 1022 List<Attribution> attributionsWithAppDestinations = 1023 getAttributionsWithDifferentReportingOrigins( 1024 4, appDestination, 5000000001L, sourceSite, registrant); 1025 for (Attribution attribution : attributionsWithAppDestinations) { 1026 insertAttribution(attribution); 1027 } 1028 Uri excludedRegistrationOrigin = WebUtil.validUri("https://subdomain0.example.test"); 1029 mDatastoreManager.runInTransaction( 1030 measurementDao -> { 1031 assertEquals( 1032 Integer.valueOf(3), 1033 measurementDao 1034 .countDistinctReportingOriginsPerPublisherXDestInAttribution( 1035 sourceSite, 1036 appDestination, 1037 excludedRegistrationOrigin, 1038 5000000000L, 1039 6000000000L)); 1040 }); 1041 } 1042 1043 @Test testCountDistinctReportingOriginsPerPublisherXDestInAttribution_beyondWindow()1044 public void testCountDistinctReportingOriginsPerPublisherXDestInAttribution_beyondWindow() { 1045 Uri sourceSite = Uri.parse("android-app://publisher.app"); 1046 Uri appDestination = Uri.parse("android-app://destination.app"); 1047 String registrant = "android-app://registrant.app"; 1048 List<Attribution> attributionsWithAppDestinations = 1049 getAttributionsWithDifferentReportingOrigins( 1050 4, appDestination, 5000000000L, sourceSite, registrant); 1051 for (Attribution attribution : attributionsWithAppDestinations) { 1052 insertAttribution(attribution); 1053 } 1054 Uri excludedReportingOrigin = WebUtil.validUri("https://subdomain.example0.test"); 1055 mDatastoreManager.runInTransaction( 1056 measurementDao -> { 1057 assertEquals( 1058 Integer.valueOf(0), 1059 measurementDao 1060 .countDistinctReportingOriginsPerPublisherXDestInAttribution( 1061 sourceSite, 1062 appDestination, 1063 excludedReportingOrigin, 1064 5000000000L, 1065 6000000000L)); 1066 }); 1067 } 1068 1069 @Test testInsertDebugReport()1070 public void testInsertDebugReport() { 1071 DebugReport debugReport = createDebugReport(); 1072 mDatastoreManager.runInTransaction((dao) -> dao.insertDebugReport(debugReport)); 1073 1074 try (Cursor cursor = 1075 MeasurementDbHelper.getInstance() 1076 .getReadableDatabase() 1077 .query(DebugReportContract.TABLE, null, null, null, null, null, null)) { 1078 assertTrue(cursor.moveToNext()); 1079 DebugReport report = SqliteObjectMapper.constructDebugReportFromCursor(cursor); 1080 assertNotNull(report); 1081 assertNotNull(report.getId()); 1082 assertEquals(debugReport.getType(), report.getType()); 1083 assertEquals(debugReport.getBody().toString(), report.getBody().toString()); 1084 assertEquals(debugReport.getEnrollmentId(), report.getEnrollmentId()); 1085 assertEquals(debugReport.getRegistrationOrigin(), report.getRegistrationOrigin()); 1086 assertEquals(debugReport.getReferenceId(), report.getReferenceId()); 1087 assertEquals(debugReport.getInsertionTime(), report.getInsertionTime()); 1088 assertEquals(debugReport.getRegistrant(), report.getRegistrant()); 1089 } 1090 } 1091 1092 @Test singleAppTrigger_triggersPerDestination_returnsOne()1093 public void singleAppTrigger_triggersPerDestination_returnsOne() { 1094 List<Trigger> triggerList = new ArrayList<>(); 1095 triggerList.add(createAppTrigger(APP_ONE_DESTINATION, APP_ONE_DESTINATION)); 1096 addTriggersToDatabase(triggerList); 1097 1098 mDatastoreManager.runInTransaction( 1099 measurementDao -> 1100 assertThat( 1101 measurementDao.getNumTriggersPerDestination( 1102 APP_ONE_DESTINATION, EventSurfaceType.APP)) 1103 .isEqualTo(1)); 1104 } 1105 1106 @Test multipleAppTriggers_similarUris_triggersPerDestination()1107 public void multipleAppTriggers_similarUris_triggersPerDestination() { 1108 List<Trigger> triggerList = new ArrayList<>(); 1109 triggerList.add(createAppTrigger(APP_TWO_DESTINATION, APP_TWO_DESTINATION)); 1110 triggerList.add(createAppTrigger(APP_TWO_DESTINATION, APP_TWO_DESTINATION)); 1111 addTriggersToDatabase(triggerList); 1112 1113 mDatastoreManager.runInTransaction( 1114 measurementDao -> 1115 assertThat( 1116 measurementDao.getNumTriggersPerDestination( 1117 APP_TWO_DESTINATION, EventSurfaceType.APP)) 1118 .isEqualTo(2)); 1119 } 1120 1121 @Test noAppTriggers_triggersPerDestination_returnsNone()1122 public void noAppTriggers_triggersPerDestination_returnsNone() { 1123 mDatastoreManager.runInTransaction( 1124 measurementDao -> 1125 assertThat( 1126 measurementDao.getNumTriggersPerDestination( 1127 APP_NO_TRIGGERS, EventSurfaceType.APP)) 1128 .isEqualTo(0)); 1129 } 1130 1131 @Test multipleAppTriggers_differentPaths_returnsAllMatching()1132 public void multipleAppTriggers_differentPaths_returnsAllMatching() { 1133 List<Trigger> triggerList = new ArrayList<>(); 1134 triggerList.add(createAppTrigger(APP_THREE_DESTINATION, APP_THREE_DESTINATION)); 1135 triggerList.add(createAppTrigger(APP_THREE_DESTINATION, APP_THREE_DESTINATION_PATH1)); 1136 triggerList.add(createAppTrigger(APP_THREE_DESTINATION, APP_THREE_DESTINATION_PATH2)); 1137 addTriggersToDatabase(triggerList); 1138 1139 mDatastoreManager.runInTransaction( 1140 measurementDao -> { 1141 assertThat( 1142 measurementDao.getNumTriggersPerDestination( 1143 APP_THREE_DESTINATION, EventSurfaceType.APP)) 1144 .isEqualTo(3); 1145 // Try the same thing, but use the app uri with path to find number of triggers. 1146 assertThat( 1147 measurementDao.getNumTriggersPerDestination( 1148 APP_THREE_DESTINATION_PATH1, EventSurfaceType.APP)) 1149 .isEqualTo(3); 1150 assertThat( 1151 measurementDao.getNumTriggersPerDestination( 1152 APP_THREE_DESTINATION_PATH2, EventSurfaceType.APP)) 1153 .isEqualTo(3); 1154 Uri unseenAppThreePath = 1155 Uri.parse("android-app://com.example1.three-triggers/path3"); 1156 assertThat( 1157 measurementDao.getNumTriggersPerDestination( 1158 unseenAppThreePath, EventSurfaceType.APP)) 1159 .isEqualTo(3); 1160 }); 1161 } 1162 1163 @Test singleWebTrigger_triggersPerDestination_returnsOne()1164 public void singleWebTrigger_triggersPerDestination_returnsOne() { 1165 List<Trigger> triggerList = new ArrayList<>(); 1166 triggerList.add(createWebTrigger(WEB_ONE_DESTINATION)); 1167 addTriggersToDatabase(triggerList); 1168 1169 mDatastoreManager.runInTransaction( 1170 measurementDao -> { 1171 assertThat( 1172 measurementDao.getNumTriggersPerDestination( 1173 WEB_ONE_DESTINATION, EventSurfaceType.WEB)) 1174 .isEqualTo(1); 1175 }); 1176 } 1177 1178 @Test webTriggerMultipleSubDomains_triggersPerDestination_returnsAllMatching()1179 public void webTriggerMultipleSubDomains_triggersPerDestination_returnsAllMatching() { 1180 List<Trigger> triggerList = new ArrayList<>(); 1181 triggerList.add(createWebTrigger(WEB_ONE_DESTINATION)); 1182 triggerList.add(createWebTrigger(WEB_ONE_DESTINATION_DIFFERENT_SUBDOMAIN)); 1183 triggerList.add(createWebTrigger(WEB_ONE_DESTINATION_DIFFERENT_SUBDOMAIN_2)); 1184 addTriggersToDatabase(triggerList); 1185 1186 mDatastoreManager.runInTransaction( 1187 measurementDao -> { 1188 assertThat( 1189 measurementDao.getNumTriggersPerDestination( 1190 WEB_ONE_DESTINATION, EventSurfaceType.WEB)) 1191 .isEqualTo(3); 1192 assertThat( 1193 measurementDao.getNumTriggersPerDestination( 1194 WEB_ONE_DESTINATION_DIFFERENT_SUBDOMAIN, 1195 EventSurfaceType.WEB)) 1196 .isEqualTo(3); 1197 assertThat( 1198 measurementDao.getNumTriggersPerDestination( 1199 WEB_ONE_DESTINATION_DIFFERENT_SUBDOMAIN_2, 1200 EventSurfaceType.WEB)) 1201 .isEqualTo(3); 1202 assertThat( 1203 measurementDao.getNumTriggersPerDestination( 1204 WebUtil.validUri("https://new-subdomain.example1.test"), 1205 EventSurfaceType.WEB)) 1206 .isEqualTo(3); 1207 assertThat( 1208 measurementDao.getNumTriggersPerDestination( 1209 WebUtil.validUri("https://example1.test"), 1210 EventSurfaceType.WEB)) 1211 .isEqualTo(3); 1212 }); 1213 } 1214 1215 @Test webTriggerWithoutSubdomains_triggersPerDestination_returnsAllMatching()1216 public void webTriggerWithoutSubdomains_triggersPerDestination_returnsAllMatching() { 1217 List<Trigger> triggerList = new ArrayList<>(); 1218 Uri webDestinationWithoutSubdomain = WebUtil.validUri("https://example1.test"); 1219 Uri webDestinationWithoutSubdomainPath1 = WebUtil.validUri("https://example1.test/path1"); 1220 Uri webDestinationWithoutSubdomainPath2 = WebUtil.validUri("https://example1.test/path2"); 1221 Uri webDestinationWithoutSubdomainPath3 = WebUtil.validUri("https://example1.test/path3"); 1222 triggerList.add(createWebTrigger(webDestinationWithoutSubdomain)); 1223 triggerList.add(createWebTrigger(webDestinationWithoutSubdomainPath1)); 1224 triggerList.add(createWebTrigger(webDestinationWithoutSubdomainPath2)); 1225 addTriggersToDatabase(triggerList); 1226 1227 mDatastoreManager.runInTransaction( 1228 measurementDao -> { 1229 assertThat( 1230 measurementDao.getNumTriggersPerDestination( 1231 webDestinationWithoutSubdomain, EventSurfaceType.WEB)) 1232 .isEqualTo(3); 1233 assertThat( 1234 measurementDao.getNumTriggersPerDestination( 1235 webDestinationWithoutSubdomainPath1, 1236 EventSurfaceType.WEB)) 1237 .isEqualTo(3); 1238 assertThat( 1239 measurementDao.getNumTriggersPerDestination( 1240 webDestinationWithoutSubdomainPath2, 1241 EventSurfaceType.WEB)) 1242 .isEqualTo(3); 1243 assertThat( 1244 measurementDao.getNumTriggersPerDestination( 1245 webDestinationWithoutSubdomainPath3, 1246 EventSurfaceType.WEB)) 1247 .isEqualTo(3); 1248 }); 1249 } 1250 1251 @Test webTriggerDifferentPaths_triggersPerDestination_returnsAllMatching()1252 public void webTriggerDifferentPaths_triggersPerDestination_returnsAllMatching() { 1253 List<Trigger> triggerList = new ArrayList<>(); 1254 triggerList.add(createWebTrigger(WEB_TWO_DESTINATION)); 1255 triggerList.add(createWebTrigger(WEB_TWO_DESTINATION_WITH_PATH)); 1256 addTriggersToDatabase(triggerList); 1257 1258 mDatastoreManager.runInTransaction( 1259 measurementDao -> { 1260 assertThat( 1261 measurementDao.getNumTriggersPerDestination( 1262 WEB_TWO_DESTINATION, EventSurfaceType.WEB)) 1263 .isEqualTo(2); 1264 assertThat( 1265 measurementDao.getNumTriggersPerDestination( 1266 WEB_TWO_DESTINATION_WITH_PATH, EventSurfaceType.WEB)) 1267 .isEqualTo(2); 1268 }); 1269 } 1270 1271 @Test noMathingWebTriggers_triggersPerDestination_returnsZero()1272 public void noMathingWebTriggers_triggersPerDestination_returnsZero() { 1273 List<Trigger> triggerList = new ArrayList<>(); 1274 triggerList.add(createWebTrigger(WEB_ONE_DESTINATION)); 1275 addTriggersToDatabase(triggerList); 1276 1277 mDatastoreManager.runInTransaction( 1278 measurementDao -> { 1279 Uri differentScheme = WebUtil.validUri("http://www.example1.test"); 1280 assertThat( 1281 measurementDao.getNumTriggersPerDestination( 1282 differentScheme, EventSurfaceType.WEB)) 1283 .isEqualTo(0); 1284 1285 Uri notMatchingUrl2 = WebUtil.validUri("https://www.not-example1.test"); 1286 assertThat( 1287 measurementDao.getNumTriggersPerDestination( 1288 notMatchingUrl2, EventSurfaceType.WEB)) 1289 .isEqualTo(0); 1290 1291 Uri notMatchingUrl = WebUtil.validUri("https://www.not-example-1.test"); 1292 assertThat( 1293 measurementDao.getNumTriggersPerDestination( 1294 notMatchingUrl, EventSurfaceType.WEB)) 1295 .isEqualTo(0); 1296 }); 1297 } 1298 1299 @Test testCountDistinctReportingOriginsPerPublisherXDestinationInAttribution_appDest()1300 public void testCountDistinctReportingOriginsPerPublisherXDestinationInAttribution_appDest() { 1301 Uri sourceSite = Uri.parse("android-app://publisher.app"); 1302 Uri webDestination = WebUtil.validUri("https://web-destination.test"); 1303 Uri appDestination = Uri.parse("android-app://destination.app"); 1304 String registrant = "android-app://registrant.app"; 1305 List<Attribution> attributionsWithAppDestinations1 = 1306 getAttributionsWithDifferentReportingOrigins( 1307 4, appDestination, 5000000000L, sourceSite, registrant); 1308 List<Attribution> attributionsWithAppDestinations2 = 1309 getAttributionsWithDifferentReportingOrigins( 1310 2, appDestination, 5000000000L, sourceSite, registrant); 1311 List<Attribution> attributionsWithWebDestinations = 1312 getAttributionsWithDifferentReportingOrigins( 1313 2, webDestination, 5500000000L, sourceSite, registrant); 1314 List<Attribution> attributionsOutOfWindow = 1315 getAttributionsWithDifferentReportingOrigins( 1316 10, appDestination, 50000000000L, sourceSite, registrant); 1317 for (Attribution attribution : attributionsWithAppDestinations1) { 1318 insertAttribution(attribution); 1319 } 1320 for (Attribution attribution : attributionsWithAppDestinations2) { 1321 insertAttribution(attribution); 1322 } 1323 for (Attribution attribution : attributionsWithWebDestinations) { 1324 insertAttribution(attribution); 1325 } 1326 for (Attribution attribution : attributionsOutOfWindow) { 1327 insertAttribution(attribution); 1328 } 1329 Uri excludedReportingOrigin = WebUtil.validUri("https://subdomain0.example.test"); 1330 mDatastoreManager.runInTransaction( 1331 measurementDao -> { 1332 assertEquals( 1333 Integer.valueOf(3), 1334 measurementDao 1335 .countDistinctReportingOriginsPerPublisherXDestInAttribution( 1336 sourceSite, 1337 appDestination, 1338 excludedReportingOrigin, 1339 4000000000L, 1340 6000000000L)); 1341 }); 1342 } 1343 1344 @Test testCountDistinctReportingOriginsPerPublisherXDestinationInAttribution_webDest()1345 public void testCountDistinctReportingOriginsPerPublisherXDestinationInAttribution_webDest() { 1346 Uri sourceSite = Uri.parse("android-app://publisher.app"); 1347 Uri webDestination = WebUtil.validUri("https://web-destination.test"); 1348 Uri appDestination = Uri.parse("android-app://destination.app"); 1349 String registrant = "android-app://registrant.app"; 1350 List<Attribution> attributionsWithAppDestinations = 1351 getAttributionsWithDifferentReportingOrigins( 1352 2, appDestination, 5000000000L, sourceSite, registrant); 1353 List<Attribution> attributionsWithWebDestinations1 = 1354 getAttributionsWithDifferentReportingOrigins( 1355 4, webDestination, 5000000000L, sourceSite, registrant); 1356 List<Attribution> attributionsWithWebDestinations2 = 1357 getAttributionsWithDifferentReportingOrigins( 1358 2, webDestination, 5500000000L, sourceSite, registrant); 1359 List<Attribution> attributionsOutOfWindow = 1360 getAttributionsWithDifferentReportingOrigins( 1361 10, webDestination, 50000000000L, sourceSite, registrant); 1362 for (Attribution attribution : attributionsWithAppDestinations) { 1363 insertAttribution(attribution); 1364 } 1365 for (Attribution attribution : attributionsWithWebDestinations1) { 1366 insertAttribution(attribution); 1367 } 1368 for (Attribution attribution : attributionsWithWebDestinations2) { 1369 insertAttribution(attribution); 1370 } 1371 for (Attribution attribution : attributionsOutOfWindow) { 1372 insertAttribution(attribution); 1373 } 1374 Uri excludedReportingOrigin = WebUtil.validUri("https://subdomain3.example.test"); 1375 mDatastoreManager.runInTransaction( 1376 measurementDao -> { 1377 assertEquals( 1378 Integer.valueOf(3), 1379 measurementDao 1380 .countDistinctReportingOriginsPerPublisherXDestInAttribution( 1381 sourceSite, 1382 webDestination, 1383 excludedReportingOrigin, 1384 4000000000L, 1385 6000000000L)); 1386 }); 1387 } 1388 1389 @Test testCountDistinctDestinations_atWindow()1390 public void testCountDistinctDestinations_atWindow() { 1391 Uri publisher = Uri.parse("android-app://publisher.app"); 1392 List<Source> activeSourcesWithAppAndWebDestinations = 1393 getSourcesWithDifferentDestinations( 1394 4, 1395 true, 1396 true, 1397 4500000001L, 1398 publisher, 1399 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1400 Source.Status.ACTIVE); 1401 for (Source source : activeSourcesWithAppAndWebDestinations) { 1402 insertSource(source); 1403 } 1404 List<Uri> excludedDestinations = 1405 List.of(WebUtil.validUri("https://web-destination-2.test")); 1406 mDatastoreManager.runInTransaction( 1407 measurementDao -> { 1408 assertEquals( 1409 Integer.valueOf(3), 1410 measurementDao 1411 .countDistinctDestPerPubXEnrollmentInUnexpiredSourceInWindow( 1412 publisher, 1413 EventSurfaceType.APP, 1414 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1415 excludedDestinations, 1416 EventSurfaceType.WEB, 1417 4500000000L, 1418 6000000000L)); 1419 }); 1420 mDatastoreManager.runInTransaction( 1421 measurementDao -> { 1422 assertEquals( 1423 Integer.valueOf(3), 1424 measurementDao 1425 .countDistinctDestinationsPerPubXEnrollmentInUnexpiredSource( 1426 publisher, 1427 EventSurfaceType.APP, 1428 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1429 excludedDestinations, 1430 EventSurfaceType.WEB, 1431 6000000000L)); 1432 }); 1433 mDatastoreManager.runInTransaction( 1434 measurementDao -> { 1435 assertEquals( 1436 Integer.valueOf(3), 1437 measurementDao 1438 .countDistinctDestinationsPerPublisherPerRateLimitWindow( 1439 publisher, 1440 EventSurfaceType.APP, 1441 excludedDestinations, 1442 EventSurfaceType.WEB, 1443 4500000000L, 1444 6000000000L)); 1445 }); 1446 } 1447 1448 @Test testCountDistinctDestinations_expiredSource()1449 public void testCountDistinctDestinations_expiredSource() { 1450 Uri publisher = Uri.parse("android-app://publisher.app"); 1451 List<Source> activeSourcesWithAppAndWebDestinations = 1452 getSourcesWithDifferentDestinations( 1453 4, 1454 true, 1455 true, 1456 4500000001L, 1457 publisher, 1458 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1459 Source.Status.ACTIVE); 1460 List<Source> expiredSourcesWithAppAndWebDestinations = 1461 getSourcesWithDifferentDestinations( 1462 6, 1463 true, 1464 true, 1465 4500000001L, 1466 6000000000L, 1467 publisher, 1468 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1469 Source.Status.ACTIVE, 1470 REGISTRATION_ORIGIN); 1471 for (Source source : activeSourcesWithAppAndWebDestinations) { 1472 insertSource(source); 1473 } 1474 for (Source source : expiredSourcesWithAppAndWebDestinations) { 1475 insertSource(source); 1476 } 1477 List<Uri> excludedDestinations = 1478 List.of(WebUtil.validUri("https://web-destination-2.test")); 1479 mDatastoreManager.runInTransaction( 1480 measurementDao -> { 1481 assertEquals( 1482 Integer.valueOf(3), 1483 measurementDao 1484 .countDistinctDestPerPubXEnrollmentInUnexpiredSourceInWindow( 1485 publisher, 1486 EventSurfaceType.APP, 1487 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1488 excludedDestinations, 1489 EventSurfaceType.WEB, 1490 4500000000L, 1491 6000000000L)); 1492 }); 1493 mDatastoreManager.runInTransaction( 1494 measurementDao -> { 1495 assertEquals( 1496 Integer.valueOf(3), 1497 measurementDao 1498 .countDistinctDestinationsPerPubXEnrollmentInUnexpiredSource( 1499 publisher, 1500 EventSurfaceType.APP, 1501 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1502 excludedDestinations, 1503 EventSurfaceType.WEB, 1504 6000000000L)); 1505 }); 1506 mDatastoreManager.runInTransaction( 1507 measurementDao -> { 1508 assertEquals( 1509 Integer.valueOf(3), 1510 measurementDao 1511 .countDistinctDestinationsPerPublisherPerRateLimitWindow( 1512 publisher, 1513 EventSurfaceType.APP, 1514 excludedDestinations, 1515 EventSurfaceType.WEB, 1516 4500000000L, 1517 6000000000L)); 1518 }); 1519 } 1520 1521 @Test testCountDistinctDestinations_beyondWindow()1522 public void testCountDistinctDestinations_beyondWindow() { 1523 Uri publisher = Uri.parse("android-app://publisher.app"); 1524 List<Source> activeSourcesWithAppAndWebDestinations = 1525 getSourcesWithDifferentDestinations( 1526 4, 1527 true, 1528 true, 1529 4500000000L, 1530 publisher, 1531 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1532 Source.Status.ACTIVE); 1533 for (Source source : activeSourcesWithAppAndWebDestinations) { 1534 insertSource(source); 1535 } 1536 List<Uri> excludedDestinations = 1537 List.of(WebUtil.validUri("https://web-destination-2.test")); 1538 mDatastoreManager.runInTransaction( 1539 measurementDao -> { 1540 assertEquals( 1541 Integer.valueOf(0), 1542 measurementDao 1543 .countDistinctDestPerPubXEnrollmentInUnexpiredSourceInWindow( 1544 publisher, 1545 EventSurfaceType.APP, 1546 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1547 excludedDestinations, 1548 EventSurfaceType.WEB, 1549 4500000000L, 1550 6000000000L)); 1551 }); 1552 mDatastoreManager.runInTransaction( 1553 measurementDao -> { 1554 assertEquals( 1555 Integer.valueOf(3), 1556 measurementDao 1557 .countDistinctDestinationsPerPubXEnrollmentInUnexpiredSource( 1558 publisher, 1559 EventSurfaceType.APP, 1560 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1561 excludedDestinations, 1562 EventSurfaceType.WEB, 1563 6000000000L)); 1564 }); 1565 mDatastoreManager.runInTransaction( 1566 measurementDao -> { 1567 assertEquals( 1568 Integer.valueOf(0), 1569 measurementDao 1570 .countDistinctDestinationsPerPublisherPerRateLimitWindow( 1571 publisher, 1572 EventSurfaceType.APP, 1573 excludedDestinations, 1574 EventSurfaceType.WEB, 1575 4500000000L, 1576 6000000000L)); 1577 }); 1578 } 1579 1580 @Test testCountDistinctDestinations_appPublisher()1581 public void testCountDistinctDestinations_appPublisher() { 1582 Uri publisher = Uri.parse("android-app://publisher.app"); 1583 List<Source> activeSourcesWithAppAndWebDestinations = 1584 getSourcesWithDifferentDestinations( 1585 4, 1586 true, 1587 true, 1588 4500000000L, 1589 publisher, 1590 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1591 Source.Status.ACTIVE); 1592 List<Source> activeSourcesWithAppDestinations = 1593 getSourcesWithDifferentDestinations( 1594 2, 1595 true, 1596 false, 1597 5000000000L, 1598 publisher, 1599 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1600 Source.Status.ACTIVE); 1601 List<Source> activeSourcesWithWebDestinations = 1602 getSourcesWithDifferentDestinations( 1603 2, 1604 false, 1605 true, 1606 5500000000L, 1607 publisher, 1608 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1609 Source.Status.ACTIVE); 1610 List<Source> activeSourcesOutOfWindow = 1611 getSourcesWithDifferentDestinations( 1612 10, 1613 true, 1614 true, 1615 50000000000L, 1616 publisher, 1617 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1618 Source.Status.ACTIVE); 1619 List<Source> ignoredSources = 1620 getSourcesWithDifferentDestinations( 1621 6, 1622 true, 1623 true, 1624 5000000000L, 1625 publisher, 1626 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1627 Source.Status.IGNORED); 1628 for (Source source : activeSourcesWithAppAndWebDestinations) { 1629 insertSource(source); 1630 } 1631 for (Source source : activeSourcesWithAppDestinations) { 1632 insertSource(source); 1633 } 1634 for (Source source : activeSourcesWithWebDestinations) { 1635 insertSource(source); 1636 } 1637 for (Source source : activeSourcesOutOfWindow) { 1638 insertSource(source); 1639 } 1640 for (Source source : ignoredSources) { 1641 insertSource(source); 1642 } 1643 List<Uri> excludedDestinations = 1644 List.of(WebUtil.validUri("https://web-destination-2.test")); 1645 mDatastoreManager.runInTransaction( 1646 measurementDao -> { 1647 assertEquals( 1648 Integer.valueOf(5), 1649 measurementDao 1650 .countDistinctDestPerPubXEnrollmentInUnexpiredSourceInWindow( 1651 publisher, 1652 EventSurfaceType.APP, 1653 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1654 excludedDestinations, 1655 EventSurfaceType.WEB, 1656 4000000000L, 1657 6000000000L)); 1658 }); 1659 mDatastoreManager.runInTransaction( 1660 measurementDao -> { 1661 assertEquals( 1662 Integer.valueOf(9), 1663 measurementDao 1664 .countDistinctDestinationsPerPubXEnrollmentInUnexpiredSource( 1665 publisher, 1666 EventSurfaceType.APP, 1667 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1668 excludedDestinations, 1669 EventSurfaceType.WEB, 1670 6000000000L)); 1671 }); 1672 mDatastoreManager.runInTransaction( 1673 measurementDao -> { 1674 assertEquals( 1675 Integer.valueOf(5), 1676 measurementDao 1677 .countDistinctDestinationsPerPublisherPerRateLimitWindow( 1678 publisher, 1679 EventSurfaceType.APP, 1680 excludedDestinations, 1681 EventSurfaceType.WEB, 1682 4000000000L, 1683 6000000000L)); 1684 }); 1685 } 1686 1687 // (Testing countDistinctDestinationsPerPublisherInActiveSource) 1688 @Test testCountDistinctDestinations_appPublisher_differentEnrollment()1689 public void testCountDistinctDestinations_appPublisher_differentEnrollment() { 1690 Uri publisher = Uri.parse("android-app://publisher.app"); 1691 List<Source> activeSourcesWithAppAndWebDestinations = 1692 getSourcesWithDifferentDestinations( 1693 4, 1694 true, 1695 true, 1696 4500000000L, 1697 publisher, 1698 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1699 Source.Status.ACTIVE); 1700 List<Source> activeSourcesWithAppDestinations = 1701 getSourcesWithDifferentDestinations( 1702 2, 1703 true, 1704 false, 1705 5000000000L, 1706 publisher, 1707 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1708 Source.Status.ACTIVE); 1709 List<Source> activeSourcesWithWebDestinations = 1710 getSourcesWithDifferentDestinations( 1711 2, 1712 false, 1713 true, 1714 5500000000L, 1715 publisher, 1716 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1717 Source.Status.ACTIVE); 1718 List<Source> activeSourcesOutOfWindow = 1719 getSourcesWithDifferentDestinations( 1720 10, 1721 true, 1722 true, 1723 50000000000L, 1724 publisher, 1725 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1726 Source.Status.ACTIVE); 1727 List<Source> ignoredSources = 1728 getSourcesWithDifferentDestinations( 1729 6, 1730 true, 1731 true, 1732 5000000000L, 1733 publisher, 1734 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1735 Source.Status.IGNORED); 1736 for (Source source : activeSourcesWithAppAndWebDestinations) { 1737 insertSource(source); 1738 } 1739 for (Source source : activeSourcesWithAppDestinations) { 1740 insertSource(source); 1741 } 1742 for (Source source : activeSourcesWithWebDestinations) { 1743 insertSource(source); 1744 } 1745 for (Source source : activeSourcesOutOfWindow) { 1746 insertSource(source); 1747 } 1748 for (Source source : ignoredSources) { 1749 insertSource(source); 1750 } 1751 List<Uri> excludedDestinations = 1752 List.of(WebUtil.validUri("https://web-destination-2.test")); 1753 mDatastoreManager.runInTransaction( 1754 measurementDao -> { 1755 assertEquals( 1756 Integer.valueOf(0), 1757 measurementDao 1758 .countDistinctDestPerPubXEnrollmentInUnexpiredSourceInWindow( 1759 publisher, 1760 EventSurfaceType.APP, 1761 "unmatched-enrollment-id", 1762 excludedDestinations, 1763 EventSurfaceType.WEB, 1764 4000000000L, 1765 6000000000L)); 1766 }); 1767 mDatastoreManager.runInTransaction( 1768 measurementDao -> { 1769 assertEquals( 1770 Integer.valueOf(0), 1771 measurementDao 1772 .countDistinctDestinationsPerPubXEnrollmentInUnexpiredSource( 1773 publisher, 1774 EventSurfaceType.APP, 1775 "unmatched-enrollment-id", 1776 excludedDestinations, 1777 EventSurfaceType.WEB, 1778 6000000000L)); 1779 }); 1780 mDatastoreManager.runInTransaction( 1781 measurementDao -> { 1782 assertEquals( 1783 Integer.valueOf(5), 1784 measurementDao 1785 .countDistinctDestinationsPerPublisherPerRateLimitWindow( 1786 publisher, 1787 EventSurfaceType.APP, 1788 excludedDestinations, 1789 EventSurfaceType.WEB, 1790 4000000000L, 1791 6000000000L)); 1792 }); 1793 } 1794 1795 @Test testCountDistinctDestinations_webPublisher_exactMatch()1796 public void testCountDistinctDestinations_webPublisher_exactMatch() { 1797 Uri publisher = WebUtil.validUri("https://publisher.test"); 1798 List<Source> activeSourcesWithAppAndWebDestinations = 1799 getSourcesWithDifferentDestinations( 1800 4, 1801 true, 1802 true, 1803 4500000000L, 1804 publisher, 1805 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1806 Source.Status.ACTIVE); 1807 List<Source> activeSourcesWithAppDestinations = 1808 getSourcesWithDifferentDestinations( 1809 2, 1810 true, 1811 false, 1812 5000000000L, 1813 publisher, 1814 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1815 Source.Status.ACTIVE); 1816 List<Source> activeSourcesWithWebDestinations = 1817 getSourcesWithDifferentDestinations( 1818 2, 1819 false, 1820 true, 1821 5500000000L, 1822 publisher, 1823 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1824 Source.Status.ACTIVE); 1825 List<Source> activeSourcesOutOfWindow = 1826 getSourcesWithDifferentDestinations( 1827 10, 1828 true, 1829 true, 1830 50000000000L, 1831 publisher, 1832 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1833 Source.Status.ACTIVE); 1834 List<Source> ignoredSources = 1835 getSourcesWithDifferentDestinations( 1836 6, 1837 true, 1838 true, 1839 5000000000L, 1840 publisher, 1841 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1842 Source.Status.IGNORED); 1843 for (Source source : activeSourcesWithAppAndWebDestinations) { 1844 insertSource(source); 1845 } 1846 for (Source source : activeSourcesWithAppDestinations) { 1847 insertSource(source); 1848 } 1849 for (Source source : activeSourcesWithWebDestinations) { 1850 insertSource(source); 1851 } 1852 for (Source source : activeSourcesOutOfWindow) { 1853 insertSource(source); 1854 } 1855 for (Source source : ignoredSources) { 1856 insertSource(source); 1857 } 1858 List<Uri> excludedDestinations = 1859 List.of(WebUtil.validUri("https://web-destination-2.test")); 1860 mDatastoreManager.runInTransaction( 1861 measurementDao -> { 1862 assertEquals( 1863 Integer.valueOf(5), 1864 measurementDao 1865 .countDistinctDestPerPubXEnrollmentInUnexpiredSourceInWindow( 1866 publisher, 1867 EventSurfaceType.WEB, 1868 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1869 excludedDestinations, 1870 EventSurfaceType.WEB, 1871 4000000000L, 1872 6000000000L)); 1873 }); 1874 mDatastoreManager.runInTransaction( 1875 measurementDao -> { 1876 assertEquals( 1877 Integer.valueOf(9), 1878 measurementDao 1879 .countDistinctDestinationsPerPubXEnrollmentInUnexpiredSource( 1880 publisher, 1881 EventSurfaceType.WEB, 1882 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1883 excludedDestinations, 1884 EventSurfaceType.WEB, 1885 6000000000L)); 1886 }); 1887 mDatastoreManager.runInTransaction( 1888 measurementDao -> { 1889 assertEquals( 1890 Integer.valueOf(5), 1891 measurementDao 1892 .countDistinctDestinationsPerPublisherPerRateLimitWindow( 1893 publisher, 1894 EventSurfaceType.WEB, 1895 excludedDestinations, 1896 EventSurfaceType.WEB, 1897 4000000000L, 1898 6000000000L)); 1899 }); 1900 } 1901 1902 @Test testCountDistinctDestinations_webPublisher_doesNotMatchDomainAsSuffix()1903 public void testCountDistinctDestinations_webPublisher_doesNotMatchDomainAsSuffix() { 1904 Uri publisher = WebUtil.validUri("https://publisher.test"); 1905 Uri publisherAsSuffix = WebUtil.validUri("https://prefix-publisher.test"); 1906 List<Source> activeSourcesWithAppAndWebDestinations = 1907 getSourcesWithDifferentDestinations( 1908 8, 1909 true, 1910 true, 1911 4500000000L, 1912 publisherAsSuffix, 1913 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1914 Source.Status.ACTIVE); 1915 List<Source> ignoredSourcesWithAppAndWebDestinations = 1916 getSourcesWithDifferentDestinations( 1917 4, 1918 true, 1919 true, 1920 4500000000L, 1921 publisher, 1922 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1923 Source.Status.IGNORED); 1924 List<Source> activeSourcesWithAppDestinations = 1925 getSourcesWithDifferentDestinations( 1926 2, 1927 true, 1928 false, 1929 5000000000L, 1930 publisher, 1931 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1932 Source.Status.ACTIVE); 1933 List<Source> activeSourcesWithWebDestinations = 1934 getSourcesWithDifferentDestinations( 1935 2, 1936 false, 1937 true, 1938 5500000000L, 1939 publisher, 1940 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1941 Source.Status.ACTIVE); 1942 List<Source> activeSourcesOutOfWindow = 1943 getSourcesWithDifferentDestinations( 1944 10, 1945 true, 1946 true, 1947 50000000000L, 1948 publisherAsSuffix, 1949 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1950 Source.Status.ACTIVE); 1951 List<Source> markedAsDeletedSources = 1952 getSourcesWithDifferentDestinations( 1953 5, 1954 true, 1955 true, 1956 5000000000L, 1957 publisher, 1958 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1959 Source.Status.MARKED_TO_DELETE); 1960 for (Source source : activeSourcesWithAppAndWebDestinations) { 1961 insertSource(source); 1962 } 1963 for (Source source : ignoredSourcesWithAppAndWebDestinations) { 1964 insertSource(source); 1965 } 1966 for (Source source : activeSourcesWithAppDestinations) { 1967 insertSource(source); 1968 } 1969 for (Source source : activeSourcesWithWebDestinations) { 1970 insertSource(source); 1971 } 1972 for (Source source : activeSourcesOutOfWindow) { 1973 insertSource(source); 1974 } 1975 for (Source source : markedAsDeletedSources) { 1976 insertSource(source); 1977 } 1978 List<Uri> excludedDestinations = 1979 List.of(WebUtil.validUri("https://web-destination-2.test")); 1980 mDatastoreManager.runInTransaction( 1981 measurementDao -> { 1982 assertEquals( 1983 Integer.valueOf(4), 1984 measurementDao 1985 .countDistinctDestPerPubXEnrollmentInUnexpiredSourceInWindow( 1986 publisher, 1987 EventSurfaceType.WEB, 1988 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1989 excludedDestinations, 1990 EventSurfaceType.WEB, 1991 4000000000L, 1992 6000000000L)); 1993 }); 1994 mDatastoreManager.runInTransaction( 1995 measurementDao -> { 1996 assertEquals( 1997 Integer.valueOf(3), 1998 measurementDao 1999 .countDistinctDestinationsPerPubXEnrollmentInUnexpiredSource( 2000 publisher, 2001 EventSurfaceType.WEB, 2002 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2003 excludedDestinations, 2004 EventSurfaceType.WEB, 2005 6000000000L)); 2006 }); 2007 mDatastoreManager.runInTransaction( 2008 measurementDao -> 2009 assertEquals( 2010 Integer.valueOf(4), 2011 measurementDao 2012 .countDistinctDestinationsPerPublisherPerRateLimitWindow( 2013 publisher, 2014 EventSurfaceType.WEB, 2015 excludedDestinations, 2016 EventSurfaceType.WEB, 2017 4000000000L, 2018 6000000000L))); 2019 } 2020 2021 @Test testCountDistinctDestinations_webPublisher_doesNotMatchDifferentScheme()2022 public void testCountDistinctDestinations_webPublisher_doesNotMatchDifferentScheme() { 2023 Uri publisher = WebUtil.validUri("https://publisher.test"); 2024 Uri publisherWithDifferentScheme = WebUtil.validUri("http://publisher.test"); 2025 List<Source> activeSourcesWithAppAndWebDestinations = 2026 getSourcesWithDifferentDestinations( 2027 4, 2028 true, 2029 true, 2030 4500000000L, 2031 publisherWithDifferentScheme, 2032 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2033 Source.Status.ACTIVE); 2034 List<Source> activeSourcesWithAppDestinations = 2035 getSourcesWithDifferentDestinations( 2036 2, 2037 true, 2038 false, 2039 5000000000L, 2040 publisher, 2041 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2042 Source.Status.ACTIVE); 2043 List<Source> activeSourcesWithWebDestinations = 2044 getSourcesWithDifferentDestinations( 2045 2, 2046 false, 2047 true, 2048 5500000000L, 2049 publisher, 2050 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2051 Source.Status.ACTIVE); 2052 List<Source> activeSourcesOutOfWindow = 2053 getSourcesWithDifferentDestinations( 2054 10, 2055 true, 2056 true, 2057 50000000000L, 2058 publisher, 2059 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2060 Source.Status.ACTIVE); 2061 List<Source> ignoredSources = 2062 getSourcesWithDifferentDestinations( 2063 6, 2064 true, 2065 true, 2066 5000000000L, 2067 publisher, 2068 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2069 Source.Status.IGNORED); 2070 for (Source source : activeSourcesWithAppAndWebDestinations) { 2071 insertSource(source); 2072 } 2073 for (Source source : activeSourcesWithAppDestinations) { 2074 insertSource(source); 2075 } 2076 for (Source source : activeSourcesWithWebDestinations) { 2077 insertSource(source); 2078 } 2079 for (Source source : activeSourcesOutOfWindow) { 2080 insertSource(source); 2081 } 2082 for (Source source : ignoredSources) { 2083 insertSource(source); 2084 } 2085 List<Uri> excludedDestinations = 2086 List.of(WebUtil.validUri("https://web-destination-2.test")); 2087 mDatastoreManager.runInTransaction( 2088 measurementDao -> { 2089 assertEquals( 2090 Integer.valueOf(5), 2091 measurementDao 2092 .countDistinctDestPerPubXEnrollmentInUnexpiredSourceInWindow( 2093 publisher, 2094 EventSurfaceType.WEB, 2095 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2096 excludedDestinations, 2097 EventSurfaceType.WEB, 2098 4000000000L, 2099 6000000000L)); 2100 }); 2101 mDatastoreManager.runInTransaction( 2102 measurementDao -> { 2103 assertEquals( 2104 Integer.valueOf(9), 2105 measurementDao 2106 .countDistinctDestinationsPerPubXEnrollmentInUnexpiredSource( 2107 publisher, 2108 EventSurfaceType.WEB, 2109 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2110 excludedDestinations, 2111 EventSurfaceType.WEB, 2112 6000000000L)); 2113 }); 2114 mDatastoreManager.runInTransaction( 2115 measurementDao -> { 2116 assertEquals( 2117 Integer.valueOf(5), 2118 measurementDao 2119 .countDistinctDestinationsPerPublisherPerRateLimitWindow( 2120 publisher, 2121 EventSurfaceType.WEB, 2122 excludedDestinations, 2123 EventSurfaceType.WEB, 2124 4000000000L, 2125 6000000000L)); 2126 }); 2127 } 2128 2129 @Test testCountDistinctDestinations_webPublisher_multipleDestinations()2130 public void testCountDistinctDestinations_webPublisher_multipleDestinations() { 2131 Uri publisher = WebUtil.validUri("https://publisher.test"); 2132 // One source with multiple destinations 2133 Source activeSourceWithAppAndWebDestinations = 2134 getSourceWithDifferentDestinations( 2135 3, 2136 true, 2137 true, 2138 4500000000L, 2139 publisher, 2140 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2141 Source.Status.ACTIVE); 2142 List<Source> activeSourcesWithAppDestinations = 2143 getSourcesWithDifferentDestinations( 2144 2, 2145 true, 2146 false, 2147 5000000000L, 2148 publisher, 2149 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2150 Source.Status.ACTIVE); 2151 List<Source> activeSourcesWithWebDestinations = 2152 getSourcesWithDifferentDestinations( 2153 1, 2154 false, 2155 true, 2156 5500000000L, 2157 publisher, 2158 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2159 Source.Status.ACTIVE); 2160 List<Source> activeSourcesOutOfWindow = 2161 getSourcesWithDifferentDestinations( 2162 10, 2163 true, 2164 true, 2165 50000000000L, 2166 publisher, 2167 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2168 Source.Status.ACTIVE); 2169 List<Source> ignoredSources = 2170 getSourcesWithDifferentDestinations( 2171 4, 2172 true, 2173 true, 2174 5000000000L, 2175 publisher, 2176 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2177 Source.Status.IGNORED); 2178 insertSource(activeSourceWithAppAndWebDestinations); 2179 for (Source source : activeSourcesWithAppDestinations) { 2180 insertSource(source); 2181 } 2182 for (Source source : activeSourcesWithWebDestinations) { 2183 insertSource(source); 2184 } 2185 for (Source source : activeSourcesOutOfWindow) { 2186 insertSource(source); 2187 } 2188 for (Source source : ignoredSources) { 2189 insertSource(source); 2190 } 2191 List<Uri> excludedDestinations = 2192 List.of( 2193 WebUtil.validUri("https://web-destination-1.test"), 2194 WebUtil.validUri("https://web-destination-2.test")); 2195 mDatastoreManager.runInTransaction( 2196 measurementDao -> { 2197 assertEquals( 2198 Integer.valueOf(2), 2199 measurementDao 2200 .countDistinctDestPerPubXEnrollmentInUnexpiredSourceInWindow( 2201 publisher, 2202 EventSurfaceType.WEB, 2203 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2204 excludedDestinations, 2205 EventSurfaceType.WEB, 2206 4000000000L, 2207 6000000000L)); 2208 }); 2209 mDatastoreManager.runInTransaction( 2210 measurementDao -> { 2211 assertEquals( 2212 Integer.valueOf(8), 2213 measurementDao 2214 .countDistinctDestinationsPerPubXEnrollmentInUnexpiredSource( 2215 publisher, 2216 EventSurfaceType.WEB, 2217 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2218 excludedDestinations, 2219 EventSurfaceType.WEB, 2220 6000000000L)); 2221 }); 2222 mDatastoreManager.runInTransaction( 2223 measurementDao -> { 2224 assertEquals( 2225 Integer.valueOf(2), 2226 measurementDao 2227 .countDistinctDestinationsPerPublisherPerRateLimitWindow( 2228 publisher, 2229 EventSurfaceType.WEB, 2230 excludedDestinations, 2231 EventSurfaceType.WEB, 2232 4000000000L, 2233 6000000000L)); 2234 }); 2235 } 2236 2237 // Tests countSourcesPerPublisherXEnrollmentExcludingRegistrationOriginSinceTime 2238 @Test testCountSourcesExclRegOrigin_forSameOrigin_returnsZero()2239 public void testCountSourcesExclRegOrigin_forSameOrigin_returnsZero() { 2240 // Positive case. For same registration origin we always pass the 1 origin 2241 // per site limit and return 0 2242 Uri appPublisher = Uri.parse("android-app://publisher.app"); 2243 List<Source> sourcesMoreThanOneDayOld = 2244 getSourcesWithDifferentDestinations( 2245 5, 2246 true, 2247 true, 2248 System.currentTimeMillis() - DAYS.toMillis(2), 2249 appPublisher, 2250 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2251 Source.Status.ACTIVE, 2252 REGISTRATION_ORIGIN); 2253 2254 List<Source> sourcesRecent = 2255 getSourcesWithDifferentDestinations( 2256 5, 2257 true, 2258 true, 2259 System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(2), 2260 appPublisher, 2261 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2262 Source.Status.ACTIVE, 2263 REGISTRATION_ORIGIN); 2264 2265 for (Source source : sourcesMoreThanOneDayOld) { 2266 insertSource(source); 2267 } 2268 for (Source source : sourcesRecent) { 2269 insertSource(source); 2270 } 2271 mDatastoreManager.runInTransaction( 2272 measurementDao -> { 2273 assertEquals( 2274 Integer.valueOf(0), 2275 measurementDao 2276 .countDistinctRegOriginPerPublisherXEnrollmentExclRegOrigin( 2277 REGISTRATION_ORIGIN, 2278 appPublisher, 2279 EventSurfaceType.APP, 2280 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2281 System.currentTimeMillis(), 2282 MEASUREMENT_MIN_REPORTING_ORIGIN_UPDATE_WINDOW)); 2283 }); 2284 } 2285 2286 @Test testCountSourcesExclRegOrigin_forDifferentAppPublisher_returnsZero()2287 public void testCountSourcesExclRegOrigin_forDifferentAppPublisher_returnsZero() { 2288 // Positive case. For different publisher we always pass the 1 origin 2289 // per site limit and return 0 2290 Uri appPublisher = Uri.parse("android-app://publisher.app"); 2291 Uri appPublisher2 = Uri.parse("android-app://publisher2.app"); 2292 List<Source> sources = 2293 getSourcesWithDifferentDestinations( 2294 5, 2295 true, 2296 true, 2297 System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(2), 2298 appPublisher, 2299 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2300 Source.Status.ACTIVE, 2301 REGISTRATION_ORIGIN); 2302 for (Source source : sources) { 2303 insertSource(source); 2304 } 2305 mDatastoreManager.runInTransaction( 2306 measurementDao -> { 2307 assertEquals( 2308 Integer.valueOf(0), 2309 measurementDao 2310 .countDistinctRegOriginPerPublisherXEnrollmentExclRegOrigin( 2311 REGISTRATION_ORIGIN_2, 2312 appPublisher2, 2313 EventSurfaceType.APP, 2314 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2315 System.currentTimeMillis(), 2316 MEASUREMENT_MIN_REPORTING_ORIGIN_UPDATE_WINDOW)); 2317 }); 2318 } 2319 2320 @Test testCountSourcesExclRegOrigin_forDifferentWebPublisher_returnsZero()2321 public void testCountSourcesExclRegOrigin_forDifferentWebPublisher_returnsZero() { 2322 // Positive case. For different publisher we always pass the 1 origin 2323 // per site limit and return 0 2324 Uri publisher = WebUtil.validUri("https://publisher.test"); 2325 Uri publisher2 = WebUtil.validUri("https://publisher2.test"); 2326 List<Source> sources = 2327 getSourcesWithDifferentDestinations( 2328 5, 2329 true, 2330 true, 2331 System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(2), 2332 publisher, 2333 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2334 Source.Status.ACTIVE, 2335 REGISTRATION_ORIGIN); 2336 for (Source source : sources) { 2337 insertSource(source); 2338 } 2339 mDatastoreManager.runInTransaction( 2340 measurementDao -> { 2341 assertEquals( 2342 Integer.valueOf(0), 2343 measurementDao 2344 .countDistinctRegOriginPerPublisherXEnrollmentExclRegOrigin( 2345 REGISTRATION_ORIGIN_2, 2346 publisher2, 2347 EventSurfaceType.WEB, 2348 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2349 System.currentTimeMillis(), 2350 MEASUREMENT_MIN_REPORTING_ORIGIN_UPDATE_WINDOW)); 2351 }); 2352 } 2353 2354 @Test testCountSourcesExclRegOrigin_forDifferentEnrollment_returnsZero()2355 public void testCountSourcesExclRegOrigin_forDifferentEnrollment_returnsZero() { 2356 // Positive case. For different enrollment (aka reporting site) 2357 // we always pass the 1 origin per site limit and return 0 2358 String differentEnrollment = "new-enrollment"; 2359 Uri differentSite = WebUtil.validUri("https://subdomain.different-site.test"); 2360 Uri appPublisher = Uri.parse("android-app://publisher.app"); 2361 List<Source> sources = 2362 getSourcesWithDifferentDestinations( 2363 5, 2364 true, 2365 true, 2366 System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(2), 2367 appPublisher, 2368 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2369 Source.Status.ACTIVE, 2370 REGISTRATION_ORIGIN); 2371 for (Source source : sources) { 2372 insertSource(source); 2373 } 2374 mDatastoreManager.runInTransaction( 2375 measurementDao -> { 2376 assertEquals( 2377 Integer.valueOf(0), 2378 measurementDao 2379 .countDistinctRegOriginPerPublisherXEnrollmentExclRegOrigin( 2380 differentSite, 2381 appPublisher, 2382 EventSurfaceType.APP, 2383 differentEnrollment, 2384 System.currentTimeMillis(), 2385 MEASUREMENT_MIN_REPORTING_ORIGIN_UPDATE_WINDOW)); 2386 }); 2387 } 2388 2389 @Test testCountSourcesExclRegOrigin_forDifferentOriginMoreThanTimeWindow_returnsZero()2390 public void testCountSourcesExclRegOrigin_forDifferentOriginMoreThanTimeWindow_returnsZero() { 2391 // Positive case. For different origin with same enrollment 2392 // more than time window of 1 day we always pass the 1 origin per site 2393 // limit and return 0 2394 Uri appPublisher = Uri.parse("android-app://publisher.app"); 2395 List<Source> sources = 2396 getSourcesWithDifferentDestinations( 2397 5, 2398 true, 2399 true, 2400 System.currentTimeMillis() - DAYS.toMillis(2), 2401 appPublisher, 2402 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2403 Source.Status.ACTIVE, 2404 REGISTRATION_ORIGIN); 2405 for (Source source : sources) { 2406 insertSource(source); 2407 } 2408 mDatastoreManager.runInTransaction( 2409 measurementDao -> { 2410 assertEquals( 2411 Integer.valueOf(0), 2412 measurementDao 2413 .countDistinctRegOriginPerPublisherXEnrollmentExclRegOrigin( 2414 REGISTRATION_ORIGIN_2, 2415 appPublisher, 2416 EventSurfaceType.APP, 2417 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2418 System.currentTimeMillis(), 2419 MEASUREMENT_MIN_REPORTING_ORIGIN_UPDATE_WINDOW)); 2420 }); 2421 } 2422 2423 // Tests countSourcesPerPublisherXEnrollmentExcludingRegistrationOriginSinceTime 2424 @Test testCountSources_forDifferentOriginWithinTimeWindow_returnsNumOfSources()2425 public void testCountSources_forDifferentOriginWithinTimeWindow_returnsNumOfSources() { 2426 // Negative case. For different origin with same enrollment 2427 // we always fail the 1 origin per site limit and return 1 2428 Uri appPublisher = Uri.parse("android-app://publisher.app"); 2429 List<Source> sources = 2430 getSourcesWithDifferentDestinations( 2431 5, 2432 true, 2433 true, 2434 System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(2), 2435 appPublisher, 2436 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2437 Source.Status.ACTIVE, 2438 REGISTRATION_ORIGIN); 2439 for (Source source : sources) { 2440 insertSource(source); 2441 } 2442 mDatastoreManager.runInTransaction( 2443 measurementDao -> { 2444 assertEquals( 2445 Integer.valueOf(1), 2446 measurementDao 2447 .countDistinctRegOriginPerPublisherXEnrollmentExclRegOrigin( 2448 REGISTRATION_ORIGIN_2, 2449 appPublisher, 2450 EventSurfaceType.APP, 2451 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2452 System.currentTimeMillis(), 2453 MEASUREMENT_MIN_REPORTING_ORIGIN_UPDATE_WINDOW)); 2454 }); 2455 } 2456 2457 @Test testCountDistinctRegistrationOriginPerPublisherXDestinationInSource_atWindow()2458 public void testCountDistinctRegistrationOriginPerPublisherXDestinationInSource_atWindow() { 2459 Uri publisher = Uri.parse("android-app://publisher.app"); 2460 List<Uri> webDestinations = List.of(WebUtil.validUri("https://web-destination.test")); 2461 List<Uri> appDestinations = List.of(Uri.parse("android-app://destination.app")); 2462 List<Source> activeSourcesWithAppAndWebDestinations = 2463 getSourcesWithDifferentRegistrationOrigins( 2464 2, 2465 appDestinations, 2466 webDestinations, 2467 4500000001L, 2468 publisher, 2469 Source.Status.ACTIVE); 2470 for (Source source : activeSourcesWithAppAndWebDestinations) { 2471 insertSource(source); 2472 } 2473 Uri excludedRegistrationOrigin = WebUtil.validUri("https://subdomain1.example.test"); 2474 mDatastoreManager.runInTransaction( 2475 measurementDao -> { 2476 assertEquals( 2477 Integer.valueOf(1), 2478 measurementDao 2479 .countDistinctReportingOriginsPerPublisherXDestinationInSource( 2480 publisher, 2481 EventSurfaceType.APP, 2482 appDestinations, 2483 excludedRegistrationOrigin, 2484 4500000000L, 2485 6000000000L)); 2486 }); 2487 } 2488 2489 @Test testCountDistinctReportingOriginsPerPublisherXDestinationInSource_beyondWindow()2490 public void testCountDistinctReportingOriginsPerPublisherXDestinationInSource_beyondWindow() { 2491 Uri publisher = Uri.parse("android-app://publisher.app"); 2492 List<Uri> webDestinations = List.of(WebUtil.validUri("https://web-destination.test")); 2493 List<Uri> appDestinations = List.of(Uri.parse("android-app://destination.app")); 2494 List<Source> activeSourcesWithAppAndWebDestinations = 2495 getSourcesWithDifferentRegistrationOrigins( 2496 2, 2497 appDestinations, 2498 webDestinations, 2499 4500000000L, 2500 publisher, 2501 Source.Status.ACTIVE); 2502 for (Source source : activeSourcesWithAppAndWebDestinations) { 2503 insertSource(source); 2504 } 2505 Uri excludedReportingOrigin = WebUtil.validUri("https://subdomain1.example.test"); 2506 mDatastoreManager.runInTransaction( 2507 measurementDao -> { 2508 assertEquals( 2509 Integer.valueOf(0), 2510 measurementDao 2511 .countDistinctReportingOriginsPerPublisherXDestinationInSource( 2512 publisher, 2513 EventSurfaceType.APP, 2514 appDestinations, 2515 excludedReportingOrigin, 2516 4500000000L, 2517 6000000000L)); 2518 }); 2519 } 2520 2521 @Test testCountDistinctReportingOriginsPerPublisherXDestinationInSource_expiredSource()2522 public void testCountDistinctReportingOriginsPerPublisherXDestinationInSource_expiredSource() { 2523 Uri publisher = Uri.parse("android-app://publisher.app"); 2524 List<Uri> webDestinations = List.of(WebUtil.validUri("https://web-destination.test")); 2525 List<Uri> appDestinations = List.of(Uri.parse("android-app://destination.app")); 2526 List<Source> activeSourcesWithAppAndWebDestinations = 2527 getSourcesWithDifferentRegistrationOrigins( 2528 2, 2529 appDestinations, 2530 webDestinations, 2531 4500000001L, 2532 publisher, 2533 Source.Status.ACTIVE); 2534 List<Source> expiredSourcesWithAppAndWebDestinations = 2535 getSourcesWithDifferentRegistrationOrigins( 2536 4, 2537 appDestinations, 2538 webDestinations, 2539 4500000001L, 2540 6000000000L, 2541 publisher, 2542 Source.Status.ACTIVE); 2543 for (Source source : activeSourcesWithAppAndWebDestinations) { 2544 insertSource(source); 2545 } 2546 for (Source source : expiredSourcesWithAppAndWebDestinations) { 2547 insertSource(source); 2548 } 2549 Uri excludedReportingOrigin = WebUtil.validUri("https://subdomain1.example.test"); 2550 mDatastoreManager.runInTransaction( 2551 measurementDao -> { 2552 assertEquals( 2553 Integer.valueOf(3), 2554 measurementDao 2555 .countDistinctReportingOriginsPerPublisherXDestinationInSource( 2556 publisher, 2557 EventSurfaceType.APP, 2558 appDestinations, 2559 excludedReportingOrigin, 2560 4500000000L, 2561 6000000000L)); 2562 }); 2563 } 2564 2565 @Test testCountDistinctReportingOriginsPerPublisherXDestinationInSource_appDestination()2566 public void testCountDistinctReportingOriginsPerPublisherXDestinationInSource_appDestination() { 2567 Uri publisher = Uri.parse("android-app://publisher.app"); 2568 List<Uri> webDestinations = List.of(WebUtil.validUri("https://web-destination.test")); 2569 List<Uri> appDestinations = List.of(Uri.parse("android-app://destination.app")); 2570 List<Source> activeSourcesWithAppAndWebDestinations = 2571 getSourcesWithDifferentRegistrationOrigins( 2572 2, 2573 appDestinations, 2574 webDestinations, 2575 4500000000L, 2576 publisher, 2577 Source.Status.ACTIVE); 2578 List<Source> activeSourcesWithAppDestinations = 2579 getSourcesWithDifferentRegistrationOrigins( 2580 2, appDestinations, null, 5000000000L, publisher, Source.Status.ACTIVE); 2581 List<Source> activeSourcesWithWebDestinations = 2582 getSourcesWithDifferentRegistrationOrigins( 2583 2, null, webDestinations, 5500000000L, publisher, Source.Status.ACTIVE); 2584 List<Source> activeSourcesOutOfWindow = 2585 getSourcesWithDifferentRegistrationOrigins( 2586 10, 2587 appDestinations, 2588 webDestinations, 2589 50000000000L, 2590 publisher, 2591 Source.Status.ACTIVE); 2592 List<Source> ignoredSources = 2593 getSourcesWithDifferentRegistrationOrigins( 2594 3, 2595 appDestinations, 2596 webDestinations, 2597 5000000000L, 2598 publisher, 2599 Source.Status.IGNORED); 2600 for (Source source : activeSourcesWithAppAndWebDestinations) { 2601 insertSource(source); 2602 } 2603 for (Source source : activeSourcesWithAppDestinations) { 2604 insertSource(source); 2605 } 2606 for (Source source : activeSourcesWithWebDestinations) { 2607 insertSource(source); 2608 } 2609 for (Source source : activeSourcesOutOfWindow) { 2610 insertSource(source); 2611 } 2612 for (Source source : ignoredSources) { 2613 insertSource(source); 2614 } 2615 String excludedEnrollmentId = "enrollment-id-1"; 2616 Uri excludedReportingOrigin = WebUtil.validUri("https://subdomain1.example.test"); 2617 mDatastoreManager.runInTransaction( 2618 measurementDao -> { 2619 assertEquals( 2620 Integer.valueOf(2), 2621 measurementDao 2622 .countDistinctReportingOriginsPerPublisherXDestinationInSource( 2623 publisher, 2624 EventSurfaceType.APP, 2625 appDestinations, 2626 excludedReportingOrigin, 2627 4000000000L, 2628 6000000000L)); 2629 }); 2630 } 2631 2632 @Test testCountDistinctReportingOriginsPerPublisherXDestinationInSource_webDestination()2633 public void testCountDistinctReportingOriginsPerPublisherXDestinationInSource_webDestination() { 2634 Uri publisher = Uri.parse("android-app://publisher.app"); 2635 List<Uri> webDestinations = List.of(WebUtil.validUri("https://web-destination.test")); 2636 List<Uri> appDestinations = List.of(Uri.parse("android-app://destination.app")); 2637 List<Source> activeSourcesWithAppAndWebDestinations = 2638 getSourcesWithDifferentRegistrationOrigins( 2639 2, 2640 appDestinations, 2641 webDestinations, 2642 4500000000L, 2643 publisher, 2644 Source.Status.ACTIVE); 2645 List<Source> activeSourcesWithAppDestinations = 2646 getSourcesWithDifferentRegistrationOrigins( 2647 2, appDestinations, null, 5000000000L, publisher, Source.Status.ACTIVE); 2648 List<Source> activeSourcesWithWebDestinations = 2649 getSourcesWithDifferentRegistrationOrigins( 2650 2, null, webDestinations, 5500000000L, publisher, Source.Status.ACTIVE); 2651 List<Source> activeSourcesOutOfWindow = 2652 getSourcesWithDifferentRegistrationOrigins( 2653 10, 2654 appDestinations, 2655 webDestinations, 2656 50000000000L, 2657 publisher, 2658 Source.Status.ACTIVE); 2659 List<Source> ignoredSources = 2660 getSourcesWithDifferentRegistrationOrigins( 2661 3, 2662 appDestinations, 2663 webDestinations, 2664 5000000000L, 2665 publisher, 2666 Source.Status.IGNORED); 2667 for (Source source : activeSourcesWithAppAndWebDestinations) { 2668 insertSource(source); 2669 } 2670 for (Source source : activeSourcesWithAppDestinations) { 2671 insertSource(source); 2672 } 2673 for (Source source : activeSourcesWithWebDestinations) { 2674 insertSource(source); 2675 } 2676 for (Source source : activeSourcesOutOfWindow) { 2677 insertSource(source); 2678 } 2679 for (Source source : ignoredSources) { 2680 insertSource(source); 2681 } 2682 String excludedEnrollmentId = "enrollment-id-22"; 2683 Uri excludedReportingOrigin = WebUtil.validUri("https://subdomain22.example.test"); 2684 mDatastoreManager.runInTransaction( 2685 measurementDao -> { 2686 assertEquals( 2687 Integer.valueOf(3), 2688 measurementDao 2689 .countDistinctReportingOriginsPerPublisherXDestinationInSource( 2690 publisher, 2691 EventSurfaceType.WEB, 2692 webDestinations, 2693 excludedReportingOrigin, 2694 4000000000L, 2695 6000000000L)); 2696 }); 2697 } 2698 2699 // countDistinctEnrollmentsPerPublisherXDestinationInSource 2700 @Test countDistinctReportingOriginsPerPublisher_webDestination_multipleDestinations()2701 public void countDistinctReportingOriginsPerPublisher_webDestination_multipleDestinations() { 2702 Uri publisher = Uri.parse("android-app://publisher.app"); 2703 List<Uri> webDestinations1 = List.of(WebUtil.validUri("https://web-destination-1.test")); 2704 List<Uri> webDestinations2 = 2705 List.of( 2706 WebUtil.validUri("https://web-destination-1.test"), 2707 WebUtil.validUri("https://web-destination-2.test")); 2708 List<Uri> appDestinations = List.of(Uri.parse("android-app://destination.app")); 2709 List<Source> activeSourcesWithAppAndWebDestinations = 2710 getSourcesWithDifferentRegistrationOrigins( 2711 3, 2712 appDestinations, 2713 webDestinations1, 2714 4500000000L, 2715 publisher, 2716 Source.Status.ACTIVE); 2717 List<Source> activeSourcesWithAppDestinations = 2718 getSourcesWithDifferentRegistrationOrigins( 2719 2, appDestinations, null, 5000000000L, publisher, Source.Status.ACTIVE); 2720 List<Source> activeSourcesWithWebDestinations = 2721 getSourcesWithDifferentRegistrationOrigins( 2722 2, null, webDestinations2, 5500000000L, publisher, Source.Status.ACTIVE); 2723 List<Source> activeSourcesOutOfWindow = 2724 getSourcesWithDifferentRegistrationOrigins( 2725 10, 2726 appDestinations, 2727 webDestinations2, 2728 50000000000L, 2729 publisher, 2730 Source.Status.ACTIVE); 2731 List<Source> ignoredSources = 2732 getSourcesWithDifferentRegistrationOrigins( 2733 2, 2734 appDestinations, 2735 webDestinations1, 2736 5000000000L, 2737 publisher, 2738 Source.Status.IGNORED); 2739 for (Source source : activeSourcesWithAppAndWebDestinations) { 2740 insertSource(source); 2741 } 2742 for (Source source : activeSourcesWithAppDestinations) { 2743 insertSource(source); 2744 } 2745 for (Source source : activeSourcesWithWebDestinations) { 2746 insertSource(source); 2747 } 2748 for (Source source : activeSourcesOutOfWindow) { 2749 insertSource(source); 2750 } 2751 for (Source source : ignoredSources) { 2752 insertSource(source); 2753 } 2754 String excludedEnrollmentId = "enrollment-id-1"; 2755 Uri excludedReportingOrigin = WebUtil.validUri("https://subdomain1.example.test"); 2756 mDatastoreManager.runInTransaction( 2757 measurementDao -> { 2758 assertEquals( 2759 Integer.valueOf(2), 2760 measurementDao 2761 .countDistinctReportingOriginsPerPublisherXDestinationInSource( 2762 publisher, 2763 EventSurfaceType.WEB, 2764 webDestinations2, 2765 excludedReportingOrigin, 2766 4000000000L, 2767 6000000000L)); 2768 }); 2769 } 2770 2771 @Test testInstallAttribution_selectHighestPriority()2772 public void testInstallAttribution_selectHighestPriority() { 2773 long currentTimestamp = System.currentTimeMillis(); 2774 2775 insertSource( 2776 createSourceForIATest( 2777 "IA1", currentTimestamp, 100, -1, false, DEFAULT_ENROLLMENT_ID) 2778 .setInstallCooldownWindow(COOLDOWN_WINDOW) 2779 .build(), 2780 "IA1"); 2781 insertSource( 2782 createSourceForIATest("IA2", currentTimestamp, 50, -1, false, DEFAULT_ENROLLMENT_ID) 2783 .setInstallCooldownWindow(COOLDOWN_WINDOW) 2784 .build(), 2785 "IA2"); 2786 // Should select id IA1 because it has higher priority 2787 assertTrue( 2788 mDatastoreManager.runInTransaction( 2789 measurementDao -> { 2790 measurementDao.doInstallAttribution( 2791 INSTALLED_PACKAGE, currentTimestamp); 2792 })); 2793 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 2794 assertTrue(getInstallAttributionStatus("IA1", db)); 2795 assertFalse(getInstallAttributionStatus("IA2", db)); 2796 removeSources(Arrays.asList("IA1", "IA2"), db); 2797 } 2798 2799 @Test testInstallAttribution_selectLatest()2800 public void testInstallAttribution_selectLatest() { 2801 long currentTimestamp = System.currentTimeMillis(); 2802 insertSource( 2803 createSourceForIATest("IA1", currentTimestamp, -1, 10, false, DEFAULT_ENROLLMENT_ID) 2804 .setInstallCooldownWindow(COOLDOWN_WINDOW) 2805 .build(), 2806 "IA1"); 2807 insertSource( 2808 createSourceForIATest("IA2", currentTimestamp, -1, 5, false, DEFAULT_ENROLLMENT_ID) 2809 .setInstallCooldownWindow(COOLDOWN_WINDOW) 2810 .build(), 2811 "IA2"); 2812 // Should select id=IA2 as it is latest 2813 assertTrue( 2814 mDatastoreManager.runInTransaction( 2815 measurementDao -> { 2816 measurementDao.doInstallAttribution( 2817 INSTALLED_PACKAGE, currentTimestamp); 2818 })); 2819 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 2820 assertFalse(getInstallAttributionStatus("IA1", db)); 2821 assertTrue(getInstallAttributionStatus("IA2", db)); 2822 2823 removeSources(Arrays.asList("IA1", "IA2"), db); 2824 } 2825 2826 @Test installAttribution_noCooldownWindow_ignoredToBeMarked()2827 public void installAttribution_noCooldownWindow_ignoredToBeMarked() { 2828 long currentTimestamp = System.currentTimeMillis(); 2829 insertSource( 2830 createSourceForIATest("IA1", currentTimestamp, -1, 10, false, DEFAULT_ENROLLMENT_ID) 2831 .setInstallCooldownWindow(0) 2832 .build(), 2833 "IA1"); 2834 // Should select id=IA2 as it is latest 2835 assertTrue( 2836 mDatastoreManager.runInTransaction( 2837 measurementDao -> { 2838 measurementDao.doInstallAttribution( 2839 INSTALLED_PACKAGE, currentTimestamp); 2840 })); 2841 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 2842 assertFalse(getInstallAttributionStatus("IA1", db)); 2843 2844 removeSources(Arrays.asList("IA1", "IA2"), db); 2845 } 2846 2847 @Test testInstallAttribution_ignoreNewerSources()2848 public void testInstallAttribution_ignoreNewerSources() { 2849 long currentTimestamp = System.currentTimeMillis(); 2850 insertSource( 2851 createSourceForIATest("IA1", currentTimestamp, -1, 10, false, DEFAULT_ENROLLMENT_ID) 2852 .setInstallCooldownWindow(COOLDOWN_WINDOW) 2853 .build(), 2854 "IA1"); 2855 insertSource( 2856 createSourceForIATest("IA2", currentTimestamp, -1, 5, false, DEFAULT_ENROLLMENT_ID) 2857 .setInstallCooldownWindow(COOLDOWN_WINDOW) 2858 .build(), 2859 "IA2"); 2860 // Should select id=IA1 as it is the only valid choice. 2861 // id=IA2 is newer than the evenTimestamp of install event. 2862 assertTrue( 2863 mDatastoreManager.runInTransaction( 2864 measurementDao -> { 2865 measurementDao.doInstallAttribution( 2866 INSTALLED_PACKAGE, currentTimestamp - DAYS.toMillis(7)); 2867 })); 2868 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 2869 assertTrue(getInstallAttributionStatus("IA1", db)); 2870 assertFalse(getInstallAttributionStatus("IA2", db)); 2871 removeSources(Arrays.asList("IA1", "IA2"), db); 2872 } 2873 2874 @Test testInstallAttribution_noValidSource()2875 public void testInstallAttribution_noValidSource() { 2876 long currentTimestamp = System.currentTimeMillis(); 2877 insertSource( 2878 createSourceForIATest("IA1", currentTimestamp, 10, 10, true, DEFAULT_ENROLLMENT_ID) 2879 .build(), 2880 "IA1"); 2881 insertSource( 2882 createSourceForIATest("IA2", currentTimestamp, 10, 11, true, DEFAULT_ENROLLMENT_ID) 2883 .build(), 2884 "IA2"); 2885 // Should not update any sources. 2886 assertTrue( 2887 mDatastoreManager.runInTransaction( 2888 measurementDao -> 2889 measurementDao.doInstallAttribution( 2890 INSTALLED_PACKAGE, currentTimestamp))); 2891 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 2892 assertFalse(getInstallAttributionStatus("IA1", db)); 2893 assertFalse(getInstallAttributionStatus("IA2", db)); 2894 removeSources(Arrays.asList("IA1", "IA2"), db); 2895 } 2896 2897 @Test installAttribution_install_installTimeEqualsEventTime()2898 public void installAttribution_install_installTimeEqualsEventTime() { 2899 long currentTimestamp = System.currentTimeMillis(); 2900 insertSource( 2901 createSourceForIATest("IA1", currentTimestamp, -1, 10, false, DEFAULT_ENROLLMENT_ID) 2902 .setInstallCooldownWindow(COOLDOWN_WINDOW) 2903 .build(), 2904 "IA1"); 2905 assertTrue( 2906 mDatastoreManager.runInTransaction( 2907 measurementDao -> { 2908 measurementDao.doInstallAttribution( 2909 INSTALLED_PACKAGE, currentTimestamp - DAYS.toMillis(7)); 2910 })); 2911 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 2912 assertEquals( 2913 currentTimestamp - DAYS.toMillis(7), 2914 getInstallAttributionInstallTime("IA1", db).longValue()); 2915 removeSources(Arrays.asList("IA1"), db); 2916 } 2917 2918 @Test 2919 public void testInstallAttribution_reinstallReattributionEnabled_skipsAttributionForReinstall()2920 testInstallAttribution_reinstallReattributionEnabled_skipsAttributionForReinstall() { 2921 mFlags = mock(Flags.class); 2922 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 2923 doReturn(true).when(mFlags).getMeasurementEnableReinstallReattribution(); 2924 long currentTimestamp = System.currentTimeMillis(); 2925 long reinstallWindow = DAYS.toMillis(50); 2926 insertSource( 2927 createSourceForIATest( 2928 /* id= */ "IA1", 2929 currentTimestamp, 2930 /* priority= */ -1, 2931 /* eventTimePastDays= */ 10, 2932 /* expiredIAWindow= */ false, 2933 DEFAULT_ENROLLMENT_ID) 2934 .setReinstallReattributionWindow(reinstallWindow) 2935 .setInstallCooldownWindow(COOLDOWN_WINDOW) 2936 .setInstallAttributed(true) 2937 .build(), 2938 /* sourceId= */ "IA1"); 2939 2940 insertSource( 2941 createSourceForIATest( 2942 /* id= */ "IA2", 2943 currentTimestamp, 2944 /* priority= */ -1, 2945 /* eventTimePastDays= */ 5, 2946 /* expiredIAWindow= */ false, 2947 DEFAULT_ENROLLMENT_ID) 2948 .setReinstallReattributionWindow(reinstallWindow) 2949 .setInstallCooldownWindow(COOLDOWN_WINDOW) 2950 .build(), 2951 /* sourceId= */ "IA2"); 2952 insertSource( 2953 createSourceForIATest( 2954 /* id= */ "IA3", 2955 currentTimestamp, 2956 /* priority= */ -1, 2957 /* eventTimePastDays= */ 10, 2958 /* expiredIAWindow= */ false, 2959 ENROLLMENT_ID1, 2960 REGISTRATION_ORIGIN_2) 2961 .setReinstallReattributionWindow(reinstallWindow) 2962 .setInstallCooldownWindow(COOLDOWN_WINDOW) 2963 .build(), 2964 /* sourceId= */ "IA3"); 2965 insertSource( 2966 createSourceForIATest( 2967 /* id= */ "IA4", 2968 currentTimestamp, 2969 /* priority= */ -1, 2970 /* eventTimePastDays= */ 5, 2971 /* expiredIAWindow= */ false, 2972 ENROLLMENT_ID1, 2973 REGISTRATION_ORIGIN_2) 2974 .setReinstallReattributionWindow(reinstallWindow) 2975 .setInstallCooldownWindow(COOLDOWN_WINDOW) 2976 .build(), 2977 /* sourceId= */ "IA4"); 2978 assertTrue( 2979 mDatastoreManager.runInTransaction( 2980 measurementDao -> { 2981 measurementDao.insertOrUpdateAppReportHistory( 2982 INSTALLED_PACKAGE, REGISTRATION_ORIGIN, currentTimestamp); 2983 })); 2984 2985 assertTrue( 2986 mDatastoreManager.runInTransaction( 2987 measurementDao -> { 2988 measurementDao.doInstallAttribution( 2989 INSTALLED_PACKAGE, currentTimestamp); 2990 })); 2991 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 2992 assertTrue(getInstallAttributionStatus(/* sourceDbId= */ "IA1", db)); 2993 assertFalse(getInstallAttributionStatus(/* sourceDbId= */ "IA2", db)); 2994 assertFalse(getInstallAttributionStatus(/* sourceDbId= */ "IA3", db)); 2995 assertTrue(getInstallAttributionStatus(/* sourceDbId= */ "IA4", db)); 2996 2997 removeSources(Arrays.asList("IA1", "IA2", "IA3", "IA4"), db); 2998 } 2999 3000 @Test testInstallAttribution_reinstallReattributionDisabled_doesNotSkipReinstall()3001 public void testInstallAttribution_reinstallReattributionDisabled_doesNotSkipReinstall() { 3002 mFlags = mock(Flags.class); 3003 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 3004 doReturn(false).when(mFlags).getMeasurementEnableReinstallReattribution(); 3005 long currentTimestamp = System.currentTimeMillis(); 3006 insertSource( 3007 createSourceForIATest( 3008 /* id= */ "IA1", 3009 currentTimestamp, 3010 /* priority= */ -1, 3011 /* eventTimePastDays= */ 10, 3012 /* expiredIAWindow= */ false, 3013 DEFAULT_ENROLLMENT_ID) 3014 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3015 .setInstallAttributed(true) 3016 .build(), 3017 "IA1"); 3018 insertSource( 3019 createSourceForIATest( 3020 /* id= */ "IA2", 3021 currentTimestamp, 3022 /* priority= */ -1, 3023 /* eventTimePastDays= */ 5, 3024 /* expiredIAWindow= */ false, 3025 DEFAULT_ENROLLMENT_ID) 3026 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3027 .build(), 3028 "IA2"); 3029 insertSource( 3030 createSourceForIATest( 3031 /* id= */ "IA3", 3032 currentTimestamp, 3033 /* priority= */ -1, 3034 /* eventTimePastDays= */ 10, 3035 /* expiredIAWindow= */ false, 3036 ENROLLMENT_ID1, 3037 REGISTRATION_ORIGIN_2) 3038 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3039 .build(), 3040 "IA3"); 3041 insertSource( 3042 createSourceForIATest( 3043 /* id= */ "IA4", 3044 currentTimestamp, 3045 /* priority= */ -1, 3046 /* eventTimePastDays= */ 5, 3047 /* expiredIAWindow= */ false, 3048 ENROLLMENT_ID1, 3049 REGISTRATION_ORIGIN_2) 3050 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3051 .build(), 3052 "IA4"); 3053 assertTrue( 3054 mDatastoreManager.runInTransaction( 3055 measurementDao -> { 3056 measurementDao.insertOrUpdateAppReportHistory( 3057 INSTALLED_PACKAGE, REGISTRATION_ORIGIN, currentTimestamp); 3058 })); 3059 // Should select id=IA2 as it is latest 3060 assertTrue( 3061 mDatastoreManager.runInTransaction( 3062 measurementDao -> { 3063 measurementDao.doInstallAttribution( 3064 INSTALLED_PACKAGE, currentTimestamp); 3065 })); 3066 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 3067 assertTrue(getInstallAttributionStatus(/* sourceDbId= */ "IA1", db)); 3068 assertTrue(getInstallAttributionStatus(/* sourceDbId= */ "IA2", db)); 3069 assertFalse(getInstallAttributionStatus(/* sourceDbId= */ "IA3", db)); 3070 assertTrue(getInstallAttributionStatus(/* sourceDbId= */ "IA4", db)); 3071 3072 removeSources(Arrays.asList("IA1", "IA2", "IA3", "IA4"), db); 3073 } 3074 3075 @Test 3076 public void testInstallAttribution_reinstallReattributionEnabledNoWindow_doesNotSkipReinstall()3077 testInstallAttribution_reinstallReattributionEnabledNoWindow_doesNotSkipReinstall() { 3078 mFlags = mock(Flags.class); 3079 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 3080 doReturn(true).when(mFlags).getMeasurementEnableReinstallReattribution(); 3081 long currentTimestamp = System.currentTimeMillis(); 3082 insertSource( 3083 createSourceForIATest( 3084 /* id= */ "IA1", 3085 currentTimestamp, 3086 /* priority= */ -1, 3087 /* eventTimePastDays= */ 10, 3088 /* expiredIAWindow= */ false, 3089 DEFAULT_ENROLLMENT_ID) 3090 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3091 .setReinstallReattributionWindow(0L) 3092 .setInstallAttributed(true) 3093 .build(), 3094 "IA1"); 3095 insertSource( 3096 createSourceForIATest( 3097 /* id= */ "IA2", 3098 currentTimestamp, 3099 /* priority= */ -1, 3100 /* eventTimePastDays= */ 5, 3101 /* expiredIAWindow= */ false, 3102 DEFAULT_ENROLLMENT_ID) 3103 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3104 .setReinstallReattributionWindow(0L) 3105 .build(), 3106 "IA2"); 3107 insertSource( 3108 createSourceForIATest( 3109 /* id= */ "IA3", 3110 currentTimestamp, 3111 /* priority= */ -1, 3112 /* eventTimePastDays= */ 10, 3113 /* expiredIAWindow= */ false, 3114 ENROLLMENT_ID1, 3115 REGISTRATION_ORIGIN_2) 3116 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3117 .setReinstallReattributionWindow(0L) 3118 .build(), 3119 "IA3"); 3120 insertSource( 3121 createSourceForIATest( 3122 /* id= */ "IA4", 3123 currentTimestamp, 3124 /* priority= */ -1, 3125 /* eventTimePastDays= */ 5, 3126 /* expiredIAWindow= */ false, 3127 ENROLLMENT_ID1, 3128 REGISTRATION_ORIGIN_2) 3129 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3130 .setReinstallReattributionWindow(0L) 3131 .build(), 3132 "IA4"); 3133 assertTrue( 3134 mDatastoreManager.runInTransaction( 3135 measurementDao -> { 3136 measurementDao.insertOrUpdateAppReportHistory( 3137 INSTALLED_PACKAGE, REGISTRATION_ORIGIN, currentTimestamp); 3138 })); 3139 // Should select id=IA2 as it is latest 3140 assertTrue( 3141 mDatastoreManager.runInTransaction( 3142 measurementDao -> { 3143 measurementDao.doInstallAttribution( 3144 INSTALLED_PACKAGE, currentTimestamp); 3145 })); 3146 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 3147 assertTrue(getInstallAttributionStatus(/* sourceDbId= */ "IA1", db)); 3148 assertTrue(getInstallAttributionStatus(/* sourceDbId= */ "IA2", db)); 3149 assertFalse(getInstallAttributionStatus(/* sourceDbId= */ "IA3", db)); 3150 assertTrue(getInstallAttributionStatus(/* sourceDbId= */ "IA4", db)); 3151 3152 removeSources(Arrays.asList("IA1", "IA2", "IA3", "IA4"), db); 3153 } 3154 3155 @Test testInstallAttribution_reinstallReattributionEnabledNoReinstall_doesNotSkip()3156 public void testInstallAttribution_reinstallReattributionEnabledNoReinstall_doesNotSkip() { 3157 mFlags = mock(Flags.class); 3158 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 3159 doReturn(true).when(mFlags).getMeasurementEnableReinstallReattribution(); 3160 long currentTimestamp = System.currentTimeMillis(); 3161 insertSource( 3162 createSourceForIATest( 3163 /* id= */ "IA1", 3164 currentTimestamp, 3165 /* priority= */ -1, 3166 /* eventTimePastDays= */ 10, 3167 /* expiredIAWindow= */ false, 3168 DEFAULT_ENROLLMENT_ID) 3169 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3170 .setReinstallReattributionWindow(0L) 3171 .build(), 3172 "IA1"); 3173 insertSource( 3174 createSourceForIATest( 3175 /* id= */ "IA2", 3176 currentTimestamp, 3177 /* priority= */ -1, 3178 /* eventTimePastDays= */ 5, 3179 /* expiredIAWindow= */ false, 3180 DEFAULT_ENROLLMENT_ID) 3181 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3182 .setReinstallReattributionWindow(0L) 3183 .build(), 3184 "IA2"); 3185 3186 assertTrue( 3187 mDatastoreManager.runInTransaction( 3188 measurementDao -> { 3189 measurementDao.insertOrUpdateAppReportHistory( 3190 INSTALLED_PACKAGE, REGISTRATION_ORIGIN, currentTimestamp); 3191 })); 3192 // Should select id=IA2 as it is latest 3193 assertTrue( 3194 mDatastoreManager.runInTransaction( 3195 measurementDao -> { 3196 measurementDao.doInstallAttribution( 3197 INSTALLED_PACKAGE, currentTimestamp); 3198 })); 3199 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 3200 assertFalse(getInstallAttributionStatus(/* sourceDbId= */ "IA1", db)); 3201 assertTrue(getInstallAttributionStatus(/* sourceDbId= */ "IA2", db)); 3202 3203 removeSources(Arrays.asList("IA1", "IA2"), db); 3204 } 3205 3206 @Test doInstallAttribution_noValidSourceStatus_IgnoresSources()3207 public void doInstallAttribution_noValidSourceStatus_IgnoresSources() { 3208 long currentTimestamp = System.currentTimeMillis(); 3209 Source source = 3210 createSourceForIATest( 3211 "IA1", currentTimestamp, 100, -1, false, DEFAULT_ENROLLMENT_ID) 3212 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3213 .build(); 3214 3215 // Execution 3216 // Active source should get install attributed 3217 source.setStatus(Source.Status.ACTIVE); 3218 insertSource(source, source.getId()); 3219 assertTrue( 3220 mDatastoreManager.runInTransaction( 3221 measurementDao -> 3222 measurementDao.doInstallAttribution( 3223 INSTALLED_PACKAGE, currentTimestamp))); 3224 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 3225 assertTrue(getInstallAttributionStatus("IA1", db)); 3226 removeSources(Collections.singletonList("IA1"), db); 3227 3228 // Active source should not get install attributed 3229 source.setStatus(Source.Status.IGNORED); 3230 insertSource(source, source.getId()); 3231 assertTrue( 3232 mDatastoreManager.runInTransaction( 3233 measurementDao -> 3234 measurementDao.doInstallAttribution( 3235 INSTALLED_PACKAGE, currentTimestamp))); 3236 assertFalse(getInstallAttributionStatus("IA1", db)); 3237 removeSources(Collections.singletonList("IA1"), db); 3238 3239 // MARKED_TO_DELETE source should not get install attributed 3240 source.setStatus(Source.Status.MARKED_TO_DELETE); 3241 insertSource(source, source.getId()); 3242 assertTrue( 3243 mDatastoreManager.runInTransaction( 3244 measurementDao -> 3245 measurementDao.doInstallAttribution( 3246 INSTALLED_PACKAGE, currentTimestamp))); 3247 assertFalse(getInstallAttributionStatus("IA1", db)); 3248 removeSources(Collections.singletonList("IA1"), db); 3249 } 3250 3251 @Test 3252 public void doInstallAttribution_withSourcesAcrossEnrollments_marksOneInstallFromEachRegOrigin()3253 doInstallAttribution_withSourcesAcrossEnrollments_marksOneInstallFromEachRegOrigin() { 3254 long currentTimestamp = System.currentTimeMillis(); 3255 3256 // Enrollment1: Choose IA2 because that's newer and still occurred before install 3257 insertSource( 3258 createSourceForIATest( 3259 "IA1", 3260 currentTimestamp, 3261 -1, 3262 10, 3263 false, 3264 DEFAULT_ENROLLMENT_ID + "_1") 3265 .setRegistrationOrigin(WebUtil.validUri("https://subdomain.example1.test")) 3266 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3267 .build(), 3268 "IA1"); 3269 insertSource( 3270 createSourceForIATest( 3271 "IA2", currentTimestamp, -1, 9, false, DEFAULT_ENROLLMENT_ID + "_1") 3272 .setRegistrationOrigin(WebUtil.validUri("https://subdomain.example1.test")) 3273 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3274 .build(), 3275 "IA2"); 3276 3277 // Enrollment2: Choose IA4 because IA3's install attribution window has expired 3278 insertSource( 3279 createSourceForIATest( 3280 "IA3", currentTimestamp, -1, 10, true, DEFAULT_ENROLLMENT_ID + "_2") 3281 .setRegistrationOrigin(WebUtil.validUri("https://subdomain.example2.test")) 3282 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3283 .build(), 3284 "IA3"); 3285 insertSource( 3286 createSourceForIATest( 3287 "IA4", currentTimestamp, -1, 9, false, DEFAULT_ENROLLMENT_ID + "_2") 3288 .setRegistrationOrigin(WebUtil.validUri("https://subdomain.example2.test")) 3289 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3290 .build(), 3291 "IA4"); 3292 3293 // Enrollment3: Choose IA5 because IA6 was registered after install event 3294 insertSource( 3295 createSourceForIATest( 3296 "IA5", 3297 currentTimestamp, 3298 -1, 3299 10, 3300 false, 3301 DEFAULT_ENROLLMENT_ID + "_3") 3302 .setRegistrationOrigin(WebUtil.validUri("https://subdomain.example3.test")) 3303 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3304 .build(), 3305 "IA5"); 3306 insertSource( 3307 createSourceForIATest( 3308 "IA6", currentTimestamp, -1, 5, false, DEFAULT_ENROLLMENT_ID + "_3") 3309 .setRegistrationOrigin(WebUtil.validUri("https://subdomain.example3.test")) 3310 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3311 .build(), 3312 "IA6"); 3313 3314 // Enrollment4: Choose IA8 due to higher priority 3315 insertSource( 3316 createSourceForIATest( 3317 "IA7", currentTimestamp, 5, 10, false, DEFAULT_ENROLLMENT_ID + "_4") 3318 .setRegistrationOrigin(WebUtil.validUri("https://subdomain.example4.test")) 3319 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3320 .build(), 3321 "IA7"); 3322 insertSource( 3323 createSourceForIATest( 3324 "IA8", 3325 currentTimestamp, 3326 10, 3327 10, 3328 false, 3329 DEFAULT_ENROLLMENT_ID + "_4") 3330 .setRegistrationOrigin(WebUtil.validUri("https://subdomain.example4.test")) 3331 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3332 .build(), 3333 "IA8"); 3334 3335 // Enrollment5: Choose none because both sources are ineligible 3336 // Expired install attribution window 3337 insertSource( 3338 createSourceForIATest( 3339 "IA9", currentTimestamp, 5, 31, true, DEFAULT_ENROLLMENT_ID + "_5") 3340 .setRegistrationOrigin(WebUtil.validUri("https://subdomain.example5.test")) 3341 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3342 .build(), 3343 "IA9"); 3344 // Registered after install attribution 3345 insertSource( 3346 createSourceForIATest( 3347 "IA10", 3348 currentTimestamp, 3349 10, 3350 3, 3351 false, 3352 DEFAULT_ENROLLMENT_ID + "_5") 3353 .setRegistrationOrigin(WebUtil.validUri("https://subdomain.example5.test")) 3354 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3355 .build(), 3356 "IA10"); 3357 3358 assertTrue( 3359 mDatastoreManager.runInTransaction( 3360 measurementDao -> { 3361 measurementDao.doInstallAttribution( 3362 INSTALLED_PACKAGE, currentTimestamp - DAYS.toMillis(7)); 3363 })); 3364 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 3365 assertTrue(getInstallAttributionStatus("IA2", db)); 3366 assertTrue(getInstallAttributionStatus("IA4", db)); 3367 assertTrue(getInstallAttributionStatus("IA5", db)); 3368 assertTrue(getInstallAttributionStatus("IA8", db)); 3369 3370 assertFalse(getInstallAttributionStatus("IA1", db)); 3371 assertFalse(getInstallAttributionStatus("IA3", db)); 3372 assertFalse(getInstallAttributionStatus("IA6", db)); 3373 assertFalse(getInstallAttributionStatus("IA7", db)); 3374 assertFalse(getInstallAttributionStatus("IA9", db)); 3375 assertFalse(getInstallAttributionStatus("IA10", db)); 3376 3377 removeSources( 3378 Arrays.asList( 3379 "IA1", "IA2", "IA3", "IA4", "IA5", "IA6", "IA7", "IA8", "IA8", "IA10"), 3380 db); 3381 } 3382 3383 @Test deleteFlexEventReportsAndAttributions_success()3384 public void deleteFlexEventReportsAndAttributions_success() throws JSONException { 3385 // Setup - Creates the following - 3386 // source - S1, S2 3387 // trigger - T1, T2, T3 3388 // event reports - (E13, E23) (E11_1, E11_2, E11_3) (E21) (E22_1, E22_2) 3389 // attributions 3390 // (ATT11_00 (aggregate scope), ATT11_01 (aggregate scope)) 3391 // (ATT11_1, ATT11_2, ATT11_3) (ATT21) (ATT22_1, ATT22_2) 3392 prepareDataForFlexEventReportAndAttributionDeletion(); 3393 3394 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 3395 3396 // Assert attributions present 3397 assertNotNull(getAttribution("ATT11_1", db)); 3398 assertNotNull(getAttribution("ATT11_2", db)); 3399 assertNotNull(getAttribution("ATT11_3", db)); 3400 assertNotNull(getAttribution("ATT21", db)); 3401 assertNotNull(getAttribution("ATT22_1", db)); 3402 assertNotNull(getAttribution("ATT22_2", db)); 3403 3404 mDatastoreManager.runInTransaction( 3405 measurementDao -> { 3406 // Assert sources and triggers present 3407 assertNotNull(measurementDao.getSource("S1")); 3408 assertNotNull(measurementDao.getSource("S2")); 3409 assertNotNull(measurementDao.getTrigger("T1")); 3410 assertNotNull(measurementDao.getTrigger("T2")); 3411 3412 // Validate presence of unmatched event reports 3413 measurementDao.getEventReport("E13"); 3414 measurementDao.getEventReport("E23"); 3415 3416 // Event report group 1 3417 // Validate event reports present 3418 EventReport e111 = measurementDao.getEventReport("E11_1"); 3419 EventReport e112 = measurementDao.getEventReport("E11_2"); 3420 3421 // Deletion 3422 measurementDao.deleteFlexEventReportsAndAttributions( 3423 List.of(e111, e112)); 3424 3425 // Validate event report deletion 3426 assertThrows( 3427 DatastoreException.class, 3428 () -> { 3429 measurementDao.getEventReport("E11_1"); 3430 }); 3431 assertThrows( 3432 DatastoreException.class, 3433 () -> { 3434 measurementDao.getEventReport("E11_2"); 3435 }); 3436 assertNotNull(measurementDao.getEventReport("E11_3")); 3437 3438 // Event report group 2 3439 // Validate event reports present 3440 EventReport e21 = measurementDao.getEventReport("E21"); 3441 3442 // Deletion 3443 measurementDao.deleteFlexEventReportsAndAttributions( 3444 List.of(e21)); 3445 3446 // Validate event report deletion 3447 assertThrows( 3448 DatastoreException.class, 3449 () -> { 3450 measurementDao.getEventReport("E21"); 3451 }); 3452 3453 // Event report group 3 3454 // Validate event reports present (retrieval doesn't throw) 3455 measurementDao.getEventReport("E22_1"); 3456 EventReport e222 = measurementDao.getEventReport("E22_2"); 3457 3458 // Deletion 3459 measurementDao.deleteFlexEventReportsAndAttributions( 3460 List.of(e222)); 3461 3462 // Validate event report deletion 3463 assertThrows( 3464 DatastoreException.class, 3465 () -> { 3466 measurementDao.getEventReport("E22_2"); 3467 }); 3468 assertNotNull(measurementDao.getEventReport("E22_1")); 3469 3470 // Validate sources and triggers present 3471 assertNotNull(measurementDao.getSource("S1")); 3472 assertNotNull(measurementDao.getSource("S2")); 3473 assertNotNull(measurementDao.getTrigger("T1")); 3474 assertNotNull(measurementDao.getTrigger("T2")); 3475 3476 // Validate presence of unmatched event reports 3477 measurementDao.getEventReport("E13"); 3478 measurementDao.getEventReport("E23"); 3479 }); 3480 3481 // Validate attribution deletion 3482 assertNotNull(getAttribution("ATT11_00", db)); 3483 assertNotNull(getAttribution("ATT11_01", db)); 3484 assertNull(getAttribution("ATT11_1", db)); 3485 assertNull(getAttribution("ATT11_2", db)); 3486 assertNotNull(getAttribution("ATT11_3", db)); 3487 assertNull(getAttribution("ATT21", db)); 3488 // Attribution deletion order within the group associated with an event report is generally 3489 // by insertion order, although it's not guaranteed. We deleted event report E22_2 but the 3490 // first limited associated attribution returned is ATT22_1. 3491 assertNull(getAttribution("ATT22_1", db)); 3492 assertNotNull(getAttribution("ATT22_2", db)); 3493 } 3494 3495 @Test deleteSources_providedIds_deletesMatchingSourcesAndRelatedData()3496 public void deleteSources_providedIds_deletesMatchingSourcesAndRelatedData() 3497 throws JSONException { 3498 // Setup - Creates the following - 3499 // source - S1, S2, S3, S4 3500 // trigger - T1, T2, T3, T4 3501 // event reports - E11, E12, E21, E22, E23, E33, E44 3502 // aggregate reports - AR11, AR12, AR21, AR34 3503 // attributions - ATT11, ATT12, ATT21, ATT22, ATT33, ATT44 3504 prepareDataForSourceAndTriggerDeletion(); 3505 3506 // Execution 3507 mDatastoreManager.runInTransaction( 3508 measurementDao -> { 3509 measurementDao.deleteSources(List.of("S1", "S2")); 3510 3511 assertThrows( 3512 DatastoreException.class, 3513 () -> { 3514 measurementDao.getSource("S1"); 3515 }); 3516 assertThrows( 3517 DatastoreException.class, 3518 () -> { 3519 measurementDao.getSource("S2"); 3520 }); 3521 3522 assertNotNull(measurementDao.getSource("S3")); 3523 assertNotNull(measurementDao.getSource("S4")); 3524 assertNotNull(measurementDao.getTrigger("T1")); 3525 assertNotNull(measurementDao.getTrigger("T2")); 3526 assertNotNull(measurementDao.getTrigger("T3")); 3527 assertNotNull(measurementDao.getTrigger("T4")); 3528 3529 assertThrows( 3530 DatastoreException.class, 3531 () -> { 3532 measurementDao.getEventReport("E11"); 3533 }); 3534 assertThrows( 3535 DatastoreException.class, 3536 () -> { 3537 measurementDao.getEventReport("E12"); 3538 }); 3539 assertThrows( 3540 DatastoreException.class, 3541 () -> { 3542 measurementDao.getEventReport("E21"); 3543 }); 3544 assertThrows( 3545 DatastoreException.class, 3546 () -> { 3547 measurementDao.getEventReport("E22"); 3548 }); 3549 assertThrows( 3550 DatastoreException.class, 3551 () -> { 3552 measurementDao.getEventReport("E23"); 3553 }); 3554 assertNotNull(measurementDao.getEventReport("E33")); 3555 assertNotNull(measurementDao.getEventReport("E44")); 3556 3557 assertThrows( 3558 DatastoreException.class, 3559 () -> { 3560 measurementDao.getAggregateReport("AR11"); 3561 }); 3562 assertThrows( 3563 DatastoreException.class, 3564 () -> { 3565 measurementDao.getAggregateReport("AR12"); 3566 }); 3567 assertThrows( 3568 DatastoreException.class, 3569 () -> { 3570 measurementDao.getAggregateReport("AR21"); 3571 }); 3572 assertNotNull(measurementDao.getAggregateReport("AR34")); 3573 }); 3574 3575 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 3576 assertEquals(2, DatabaseUtils.queryNumEntries(db, AttributionContract.TABLE)); 3577 } 3578 3579 @Test deleteSource_providedId_deletesMatchingXnaIgnoredSource()3580 public void deleteSource_providedId_deletesMatchingXnaIgnoredSource() { 3581 // Setup 3582 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 3583 3584 Source s1 = 3585 SourceFixture.getMinimalValidSourceBuilder() 3586 .setEventId(new UnsignedLong(1L)) 3587 .setId("S1") 3588 .setEnrollmentId("1") 3589 .build(); 3590 3591 ContentValues sourceValues = new ContentValues(); 3592 sourceValues.put(SourceContract.ID, s1.getId()); 3593 sourceValues.put(SourceContract.EVENT_ID, s1.getEventId().getValue()); 3594 3595 ContentValues xnaIgnoredSourceValues = new ContentValues(); 3596 xnaIgnoredSourceValues.put(XnaIgnoredSourcesContract.SOURCE_ID, s1.getId()); 3597 xnaIgnoredSourceValues.put(XnaIgnoredSourcesContract.ENROLLMENT_ID, s1.getEnrollmentId()); 3598 3599 // Execution 3600 db.insert(SourceContract.TABLE, null, sourceValues); 3601 db.insert(XnaIgnoredSourcesContract.TABLE, null, xnaIgnoredSourceValues); 3602 3603 // Assertion 3604 assertEquals(1, DatabaseUtils.queryNumEntries(db, SourceContract.TABLE)); 3605 assertEquals(1, DatabaseUtils.queryNumEntries(db, XnaIgnoredSourcesContract.TABLE)); 3606 3607 // Execution 3608 removeSources(Collections.singletonList(s1.getId()), db); 3609 3610 // Assertion 3611 assertEquals(0, DatabaseUtils.queryNumEntries(db, SourceContract.TABLE)); 3612 assertEquals(0, DatabaseUtils.queryNumEntries(db, XnaIgnoredSourcesContract.TABLE)); 3613 } 3614 3615 @Test deleteTriggers_providedIds_deletesMatchingTriggersAndRelatedData()3616 public void deleteTriggers_providedIds_deletesMatchingTriggersAndRelatedData() 3617 throws JSONException { 3618 // Setup - Creates the following - 3619 // source - S1, S2, S3, S4 3620 // trigger - T1, T2, T3, T4 3621 // event reports - E11, E12, E21, E22, E23, E33, E44 3622 // aggregate reports - AR11, AR12, AR21, AR34 3623 // attributions - ATT11, ATT12, ATT21, ATT22, ATT33, ATT44 3624 prepareDataForSourceAndTriggerDeletion(); 3625 3626 // Execution 3627 mDatastoreManager.runInTransaction( 3628 measurementDao -> { 3629 measurementDao.deleteTriggers(List.of("T1", "T2")); 3630 3631 assertNotNull(measurementDao.getSource("S1")); 3632 assertNotNull(measurementDao.getSource("S2")); 3633 assertNotNull(measurementDao.getSource("S3")); 3634 assertNotNull(measurementDao.getSource("S4")); 3635 assertThrows(DatastoreException.class, () -> measurementDao.getTrigger("T1")); 3636 assertThrows(DatastoreException.class, () -> measurementDao.getTrigger("T2")); 3637 assertNotNull(measurementDao.getTrigger("T3")); 3638 assertNotNull(measurementDao.getTrigger("T4")); 3639 3640 assertThrows( 3641 DatastoreException.class, () -> measurementDao.getEventReport("E11")); 3642 assertThrows( 3643 DatastoreException.class, () -> measurementDao.getEventReport("E12")); 3644 assertThrows( 3645 DatastoreException.class, () -> measurementDao.getEventReport("E21")); 3646 assertThrows( 3647 DatastoreException.class, () -> measurementDao.getEventReport("E22")); 3648 assertNotNull(measurementDao.getEventReport("E23")); 3649 assertNotNull(measurementDao.getEventReport("E33")); 3650 assertNotNull(measurementDao.getEventReport("E44")); 3651 3652 assertThrows( 3653 DatastoreException.class, 3654 () -> measurementDao.getAggregateReport("AR11")); 3655 assertThrows( 3656 DatastoreException.class, 3657 () -> measurementDao.getAggregateReport("AR12")); 3658 assertThrows( 3659 DatastoreException.class, 3660 () -> measurementDao.getAggregateReport("AR21")); 3661 3662 assertNotNull(measurementDao.getAggregateReport("AR34")); 3663 }); 3664 3665 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 3666 assertEquals(2, DatabaseUtils.queryNumEntries(db, AttributionContract.TABLE)); 3667 } 3668 3669 // Setup - Creates the following - 3670 // source - S1, S2 3671 // trigger - T1, T2 3672 // event reports - E11_1, E11_2, E11_3, E21, E22_1, E22_2 3673 // attributions - ATT11_1, ATT11_2, ATT11_3, ATT21, ATT22_1, ATT22_2 prepareDataForFlexEventReportAndAttributionDeletion()3674 private void prepareDataForFlexEventReportAndAttributionDeletion() throws JSONException { 3675 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 3676 Source s1 = 3677 SourceFixture.getMinimalValidSourceBuilder() 3678 .setEventId(new UnsignedLong(1L)) 3679 .setId("S1") 3680 .build(); 3681 Source s2 = 3682 SourceFixture.getMinimalValidSourceBuilder() 3683 .setEventId(new UnsignedLong(2L)) 3684 .setId("S2") 3685 .build(); 3686 Trigger t1 = 3687 TriggerFixture.getValidTriggerBuilder() 3688 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 3689 .setId("T1") 3690 .build(); 3691 Trigger t2 = 3692 TriggerFixture.getValidTriggerBuilder() 3693 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 3694 .setId("T2") 3695 .build(); 3696 Trigger t3 = 3697 TriggerFixture.getValidTriggerBuilder() 3698 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 3699 .setId("T2") 3700 .build(); 3701 EventReport e111 = createEventReportForSourceAndTrigger("E11_1", s1, t1); 3702 EventReport e112 = createEventReportForSourceAndTrigger("E11_2", s1, t1); 3703 EventReport e113 = createEventReportForSourceAndTrigger("E11_3", s1, t1); 3704 EventReport e21 = createEventReportForSourceAndTrigger("E21", s2, t1); 3705 EventReport e221 = createEventReportForSourceAndTrigger("E22_1", s2, t2); 3706 EventReport e222 = createEventReportForSourceAndTrigger("E22_2", s2, t2); 3707 EventReport e13 = createEventReportForSourceAndTrigger("E13", s1, t3); 3708 EventReport e23 = createEventReportForSourceAndTrigger("E23", s2, t3); 3709 Attribution att1100 = 3710 createAttribution( 3711 "ATT11_00", 3712 Attribution.Scope.AGGREGATE, 3713 s1.getId(), 3714 t1.getId()); 3715 Attribution att1101 = 3716 createAttribution( 3717 "ATT11_01", 3718 Attribution.Scope.AGGREGATE, 3719 s1.getId(), 3720 t1.getId()); 3721 Attribution att111 = 3722 createAttribution( 3723 "ATT11_1", s1.getId(), t1.getId()); 3724 Attribution att112 = 3725 createAttribution( 3726 "ATT11_2", s1.getId(), t1.getId()); 3727 Attribution att113 = 3728 createAttribution( 3729 "ATT11_3", s1.getId(), t1.getId()); 3730 Attribution att21 = 3731 createAttribution( 3732 "ATT21", s2.getId(), t1.getId()); 3733 Attribution att221 = 3734 createAttribution( 3735 "ATT22_1", s2.getId(), t2.getId()); 3736 Attribution att222 = 3737 createAttribution( 3738 "ATT22_2", s2.getId(), t2.getId()); 3739 3740 insertSource(s1, s1.getId()); 3741 insertSource(s2, s2.getId()); 3742 AbstractDbIntegrationTest.insertToDb(t1, db); 3743 AbstractDbIntegrationTest.insertToDb(t2, db); 3744 AbstractDbIntegrationTest.insertToDb(e111, db); 3745 AbstractDbIntegrationTest.insertToDb(e112, db); 3746 AbstractDbIntegrationTest.insertToDb(e113, db); 3747 AbstractDbIntegrationTest.insertToDb(e21, db); 3748 AbstractDbIntegrationTest.insertToDb(e221, db); 3749 AbstractDbIntegrationTest.insertToDb(e222, db); 3750 AbstractDbIntegrationTest.insertToDb(e13, db); 3751 AbstractDbIntegrationTest.insertToDb(e23, db); 3752 AbstractDbIntegrationTest.insertToDb(att1100, db); 3753 AbstractDbIntegrationTest.insertToDb(att1101, db); 3754 AbstractDbIntegrationTest.insertToDb(att111, db); 3755 AbstractDbIntegrationTest.insertToDb(att112, db); 3756 AbstractDbIntegrationTest.insertToDb(att113, db); 3757 AbstractDbIntegrationTest.insertToDb(att21, db); 3758 AbstractDbIntegrationTest.insertToDb(att221, db); 3759 AbstractDbIntegrationTest.insertToDb(att222, db); 3760 } 3761 3762 // Setup - Creates the following - 3763 // source - S1, S2, S3, S4 3764 // trigger - T1, T2, T3, T4 3765 // event reports - E11, E12, E21, E22, E23, E33, E44 3766 // aggregate reports - AR11, AR12, AR21, AR34 3767 // attributions - ATT11, ATT12, ATT21, ATT22, ATT33, ATT44 prepareDataForSourceAndTriggerDeletion()3768 private void prepareDataForSourceAndTriggerDeletion() throws JSONException { 3769 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 3770 Source s1 = 3771 SourceFixture.getMinimalValidSourceBuilder() 3772 .setEventId(new UnsignedLong(1L)) 3773 .setId("S1") 3774 .build(); // deleted 3775 Source s2 = 3776 SourceFixture.getMinimalValidSourceBuilder() 3777 .setEventId(new UnsignedLong(2L)) 3778 .setId("S2") 3779 .build(); // deleted 3780 Source s3 = 3781 SourceFixture.getMinimalValidSourceBuilder() 3782 .setEventId(new UnsignedLong(3L)) 3783 .setId("S3") 3784 .build(); 3785 Source s4 = 3786 SourceFixture.getMinimalValidSourceBuilder() 3787 .setEventId(new UnsignedLong(4L)) 3788 .setId("S4") 3789 .build(); 3790 Trigger t1 = 3791 TriggerFixture.getValidTriggerBuilder() 3792 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 3793 .setId("T1") 3794 .build(); 3795 Trigger t2 = 3796 TriggerFixture.getValidTriggerBuilder() 3797 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 3798 .setId("T2") 3799 .build(); 3800 Trigger t3 = 3801 TriggerFixture.getValidTriggerBuilder() 3802 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 3803 .setId("T3") 3804 .build(); 3805 Trigger t4 = 3806 TriggerFixture.getValidTriggerBuilder() 3807 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 3808 .setId("T4") 3809 .build(); 3810 EventReport e11 = createEventReportForSourceAndTrigger("E11", s1, t1); 3811 EventReport e12 = createEventReportForSourceAndTrigger("E12", s1, t2); 3812 EventReport e21 = createEventReportForSourceAndTrigger("E21", s2, t1); 3813 EventReport e22 = createEventReportForSourceAndTrigger("E22", s2, t2); 3814 EventReport e23 = createEventReportForSourceAndTrigger("E23", s2, t3); 3815 EventReport e33 = createEventReportForSourceAndTrigger("E33", s3, t3); 3816 EventReport e44 = createEventReportForSourceAndTrigger("E44", s4, t4); 3817 AggregateReport ar11 = createAggregateReportForSourceAndTrigger("AR11", s1, t1); 3818 AggregateReport ar12 = createAggregateReportForSourceAndTrigger("AR12", s1, t2); 3819 AggregateReport ar21 = createAggregateReportForSourceAndTrigger("AR21", s2, t1); 3820 AggregateReport ar34 = createAggregateReportForSourceAndTrigger("AR34", s3, t4); 3821 Attribution att11 = 3822 createAttribution( 3823 "ATT11", s1.getId(), t1.getId()); // deleted 3824 Attribution att12 = 3825 createAttribution( 3826 "ATT12", s1.getId(), t2.getId()); // deleted 3827 Attribution att21 = 3828 createAttribution( 3829 "ATT21", s2.getId(), t1.getId()); // deleted 3830 Attribution att22 = 3831 createAttribution( 3832 "ATT22", s2.getId(), t2.getId()); // deleted 3833 Attribution att33 = 3834 createAttribution("ATT33", s3.getId(), t3.getId()); 3835 Attribution att44 = 3836 createAttribution("ATT44", s4.getId(), t4.getId()); 3837 3838 insertSource(s1, s1.getId()); 3839 insertSource(s2, s2.getId()); 3840 insertSource(s3, s3.getId()); 3841 insertSource(s4, s4.getId()); 3842 3843 AbstractDbIntegrationTest.insertToDb(t1, db); 3844 AbstractDbIntegrationTest.insertToDb(t2, db); 3845 AbstractDbIntegrationTest.insertToDb(t3, db); 3846 AbstractDbIntegrationTest.insertToDb(t4, db); 3847 3848 AbstractDbIntegrationTest.insertToDb(e11, db); 3849 AbstractDbIntegrationTest.insertToDb(e12, db); 3850 AbstractDbIntegrationTest.insertToDb(e21, db); 3851 AbstractDbIntegrationTest.insertToDb(e22, db); 3852 AbstractDbIntegrationTest.insertToDb(e23, db); 3853 AbstractDbIntegrationTest.insertToDb(e33, db); 3854 AbstractDbIntegrationTest.insertToDb(e44, db); 3855 3856 AbstractDbIntegrationTest.insertToDb(ar11, db); 3857 AbstractDbIntegrationTest.insertToDb(ar12, db); 3858 AbstractDbIntegrationTest.insertToDb(ar21, db); 3859 AbstractDbIntegrationTest.insertToDb(ar34, db); 3860 3861 AbstractDbIntegrationTest.insertToDb(att11, db); 3862 AbstractDbIntegrationTest.insertToDb(att12, db); 3863 AbstractDbIntegrationTest.insertToDb(att21, db); 3864 AbstractDbIntegrationTest.insertToDb(att22, db); 3865 AbstractDbIntegrationTest.insertToDb(att33, db); 3866 AbstractDbIntegrationTest.insertToDb(att44, db); 3867 } 3868 3869 @Test testUndoInstallAttribution_noMarkedSource()3870 public void testUndoInstallAttribution_noMarkedSource() { 3871 long currentTimestamp = System.currentTimeMillis(); 3872 Source source = 3873 createSourceForIATest("IA1", currentTimestamp, 10, 10, false, DEFAULT_ENROLLMENT_ID) 3874 .build(); 3875 source.setInstallAttributed(true); 3876 insertSource(source, source.getId()); 3877 assertTrue( 3878 mDatastoreManager.runInTransaction( 3879 measurementDao -> 3880 measurementDao.undoInstallAttribution(INSTALLED_PACKAGE))); 3881 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 3882 // Should set installAttributed = false for id=IA1 3883 assertFalse(getInstallAttributionStatus("IA1", db)); 3884 } 3885 3886 @Test undoInstallAttribution_uninstall_nullInstallTime()3887 public void undoInstallAttribution_uninstall_nullInstallTime() { 3888 long currentTimestamp = System.currentTimeMillis(); 3889 Source source = 3890 createSourceForIATest("IA1", currentTimestamp, 10, 10, false, DEFAULT_ENROLLMENT_ID) 3891 .build(); 3892 source.setInstallAttributed(true); 3893 insertSource(source, source.getId()); 3894 assertTrue( 3895 mDatastoreManager.runInTransaction( 3896 measurementDao -> 3897 measurementDao.undoInstallAttribution(INSTALLED_PACKAGE))); 3898 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 3899 // Should set installTime = null for id=IA1 3900 assertNull(getInstallAttributionInstallTime("IA1", db)); 3901 } 3902 3903 @Test getSourceDestinations_returnsExpected()3904 public void getSourceDestinations_returnsExpected() { 3905 // Insert two sources with some intersection of destinations 3906 // and assert all destination queries work. 3907 3908 // First source 3909 List<Uri> webDestinations1 = 3910 List.of( 3911 Uri.parse("https://first-place.test"), 3912 Uri.parse("https://second-place.test"), 3913 Uri.parse("https://third-place.test")); 3914 List<Uri> appDestinations1 = List.of(Uri.parse("android-app://test.first-place")); 3915 Source source1 = 3916 SourceFixture.getMinimalValidSourceBuilder() 3917 .setId("1") 3918 .setAppDestinations(appDestinations1) 3919 .setWebDestinations(webDestinations1) 3920 .build(); 3921 insertSource(source1, source1.getId()); 3922 3923 // Second source 3924 List<Uri> webDestinations2 = 3925 List.of( 3926 Uri.parse("https://not-first-place.test"), 3927 Uri.parse("https://not-second-place.test"), 3928 Uri.parse("https://third-place.test")); 3929 List<Uri> appDestinations2 = List.of(Uri.parse("android-app://test.not-first-place")); 3930 Source source2 = 3931 SourceFixture.getMinimalValidSourceBuilder() 3932 .setId("2") 3933 .setAppDestinations(appDestinations2) 3934 .setWebDestinations(webDestinations2) 3935 .build(); 3936 insertSource(source2, source2.getId()); 3937 3938 Pair<List<Uri>, List<Uri>> result1 = 3939 mDatastoreManager 3940 .runInTransactionWithResult( 3941 measurementDao -> 3942 measurementDao.getSourceDestinations(source1.getId())) 3943 .get(); 3944 // Assert first app destinations 3945 assertTrue( 3946 ImmutableMultiset.copyOf(source1.getAppDestinations()) 3947 .equals(ImmutableMultiset.copyOf(result1.first))); 3948 // Assert first web destinations 3949 assertTrue( 3950 ImmutableMultiset.copyOf(source1.getWebDestinations()) 3951 .equals(ImmutableMultiset.copyOf(result1.second))); 3952 3953 Pair<List<Uri>, List<Uri>> result2 = 3954 mDatastoreManager 3955 .runInTransactionWithResult( 3956 measurementDao -> 3957 measurementDao.getSourceDestinations(source2.getId())) 3958 .get(); 3959 // Assert second app destinations 3960 assertTrue( 3961 ImmutableMultiset.copyOf(source2.getAppDestinations()) 3962 .equals(ImmutableMultiset.copyOf(result2.first))); 3963 // Assert second web destinations 3964 assertTrue( 3965 ImmutableMultiset.copyOf(source2.getWebDestinations()) 3966 .equals(ImmutableMultiset.copyOf(result2.second))); 3967 } 3968 3969 @Test getNumAggregateReportsPerSource_returnsExpected()3970 public void getNumAggregateReportsPerSource_returnsExpected() { 3971 List<Source> sources = 3972 Arrays.asList( 3973 SourceFixture.getMinimalValidSourceBuilder() 3974 .setEventId(new UnsignedLong(1L)) 3975 .setId("source1") 3976 .build(), 3977 SourceFixture.getMinimalValidSourceBuilder() 3978 .setEventId(new UnsignedLong(2L)) 3979 .setId("source2") 3980 .build(), 3981 SourceFixture.getMinimalValidSourceBuilder() 3982 .setEventId(new UnsignedLong(3L)) 3983 .setId("source3") 3984 .build()); 3985 List<AggregateReport> reports = 3986 Arrays.asList( 3987 generateMockAggregateReport( 3988 WebUtil.validUrl("https://destination-1.test"), 3989 1, 3990 "source1", 3991 AggregateReportFixture.ValidAggregateReportParams.API), 3992 generateMockAggregateReport( 3993 WebUtil.validUrl("https://destination-1.test"), 3994 2, 3995 "source1", 3996 AggregateReportFixture.ValidAggregateReportParams.API), 3997 generateMockAggregateReport( 3998 WebUtil.validUrl("https://destination-2.test"), 3999 3, 4000 "source2", 4001 AggregateReportFixture.ValidAggregateReportParams.API), 4002 generateMockAggregateReport( 4003 WebUtil.validUrl("https://destination-1.test"), 4004 4, 4005 "source3", 4006 AggregateDebugReportApi.AGGREGATE_DEBUG_REPORT_API), 4007 generateMockAggregateReport( 4008 WebUtil.validUrl("https://destination-1.test"), 4009 5, 4010 "source3", 4011 AggregateDebugReportApi.AGGREGATE_DEBUG_REPORT_API), 4012 generateMockAggregateReport( 4013 WebUtil.validUrl("https://destination-2.test"), 4014 6, 4015 "source3", 4016 AggregateDebugReportApi.AGGREGATE_DEBUG_REPORT_API)); 4017 4018 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 4019 Objects.requireNonNull(db); 4020 sources.forEach(source -> insertSource(source, source.getId())); 4021 Consumer<AggregateReport> aggregateReportConsumer = 4022 aggregateReport -> { 4023 ContentValues values = new ContentValues(); 4024 values.put(MeasurementTables.AggregateReport.ID, aggregateReport.getId()); 4025 values.put( 4026 MeasurementTables.AggregateReport.SOURCE_ID, 4027 aggregateReport.getSourceId()); 4028 values.put( 4029 MeasurementTables.AggregateReport.ATTRIBUTION_DESTINATION, 4030 aggregateReport.getAttributionDestination().toString()); 4031 values.put(MeasurementTables.AggregateReport.API, aggregateReport.getApi()); 4032 db.insert(MeasurementTables.AggregateReport.TABLE, null, values); 4033 }; 4034 reports.forEach(aggregateReportConsumer); 4035 4036 mDatastoreManager.runInTransaction( 4037 measurementDao -> { 4038 assertThat( 4039 measurementDao.countNumAggregateReportsPerSource( 4040 "source1", 4041 AggregateReportFixture.ValidAggregateReportParams.API)) 4042 .isEqualTo(2); 4043 assertThat( 4044 measurementDao.countNumAggregateReportsPerSource( 4045 "source2", 4046 AggregateReportFixture.ValidAggregateReportParams.API)) 4047 .isEqualTo(1); 4048 assertThat( 4049 measurementDao.countNumAggregateReportsPerSource( 4050 "source3", 4051 AggregateReportFixture.ValidAggregateReportParams.API)) 4052 .isEqualTo(0); 4053 assertThat( 4054 measurementDao.countNumAggregateReportsPerSource( 4055 "source1", 4056 AggregateDebugReportApi.AGGREGATE_DEBUG_REPORT_API)) 4057 .isEqualTo(0); 4058 assertThat( 4059 measurementDao.countNumAggregateReportsPerSource( 4060 "source2", 4061 AggregateDebugReportApi.AGGREGATE_DEBUG_REPORT_API)) 4062 .isEqualTo(0); 4063 assertThat( 4064 measurementDao.countNumAggregateReportsPerSource( 4065 "source3", 4066 AggregateDebugReportApi.AGGREGATE_DEBUG_REPORT_API)) 4067 .isEqualTo(3); 4068 }); 4069 } 4070 4071 @Test getNumAggregateReportsPerDestination_returnsExpected()4072 public void getNumAggregateReportsPerDestination_returnsExpected() { 4073 List<AggregateReport> reportsWithPlainDestination = 4074 Arrays.asList( 4075 generateMockAggregateReport( 4076 WebUtil.validUrl("https://destination-1.test"), 1)); 4077 List<AggregateReport> reportsWithPlainAndSubDomainDestination = 4078 Arrays.asList( 4079 generateMockAggregateReport( 4080 WebUtil.validUrl("https://destination-2.test"), 2), 4081 generateMockAggregateReport( 4082 WebUtil.validUrl("https://subdomain.destination-2.test"), 3)); 4083 List<AggregateReport> reportsWithPlainAndPathDestination = 4084 Arrays.asList( 4085 generateMockAggregateReport( 4086 WebUtil.validUrl("https://subdomain.destination-3.test"), 4), 4087 generateMockAggregateReport( 4088 WebUtil.validUrl("https://subdomain.destination-3.test/abcd"), 5)); 4089 List<AggregateReport> reportsWithAll3Types = 4090 Arrays.asList( 4091 generateMockAggregateReport( 4092 WebUtil.validUrl("https://destination-4.test"), 6), 4093 generateMockAggregateReport( 4094 WebUtil.validUrl("https://subdomain.destination-4.test"), 7), 4095 generateMockAggregateReport( 4096 WebUtil.validUrl("https://subdomain.destination-4.test/abcd"), 8)); 4097 List<AggregateReport> reportsWithAndroidAppDestination = 4098 Arrays.asList(generateMockAggregateReport("android-app://destination-5.app", 9)); 4099 4100 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 4101 Objects.requireNonNull(db); 4102 Stream.of( 4103 reportsWithPlainDestination, 4104 reportsWithPlainAndSubDomainDestination, 4105 reportsWithPlainAndPathDestination, 4106 reportsWithAll3Types, 4107 reportsWithAndroidAppDestination) 4108 .flatMap(Collection::stream) 4109 .forEach( 4110 aggregateReport -> { 4111 ContentValues values = new ContentValues(); 4112 values.put( 4113 MeasurementTables.AggregateReport.ID, aggregateReport.getId()); 4114 values.put( 4115 MeasurementTables.AggregateReport.ATTRIBUTION_DESTINATION, 4116 aggregateReport.getAttributionDestination().toString()); 4117 values.put( 4118 MeasurementTables.AggregateReport.IS_FAKE_REPORT, 4119 aggregateReport.isFakeReport()); 4120 db.insert(MeasurementTables.AggregateReport.TABLE, null, values); 4121 }); 4122 4123 List<String> attributionDestinations1 = createWebDestinationVariants(1); 4124 List<String> attributionDestinations2 = createWebDestinationVariants(2); 4125 List<String> attributionDestinations3 = createWebDestinationVariants(3); 4126 List<String> attributionDestinations4 = createWebDestinationVariants(4); 4127 List<String> attributionDestinations5 = createAppDestinationVariants(5); 4128 4129 // expected query return values for attribution destination variants 4130 List<Integer> destination1ExpectedCounts = Arrays.asList(1, 1, 1, 1, 0); 4131 List<Integer> destination2ExpectedCounts = Arrays.asList(2, 2, 2, 2, 0); 4132 List<Integer> destination3ExpectedCounts = Arrays.asList(2, 2, 2, 2, 0); 4133 List<Integer> destination4ExpectedCounts = Arrays.asList(3, 3, 3, 3, 0); 4134 List<Integer> destination5ExpectedCounts = Arrays.asList(0, 0, 1, 1, 0); 4135 assertAggregateReportCount( 4136 attributionDestinations1, EventSurfaceType.WEB, destination1ExpectedCounts); 4137 assertAggregateReportCount( 4138 attributionDestinations2, EventSurfaceType.WEB, destination2ExpectedCounts); 4139 assertAggregateReportCount( 4140 attributionDestinations3, EventSurfaceType.WEB, destination3ExpectedCounts); 4141 assertAggregateReportCount( 4142 attributionDestinations4, EventSurfaceType.WEB, destination4ExpectedCounts); 4143 assertAggregateReportCount( 4144 attributionDestinations5, EventSurfaceType.APP, destination5ExpectedCounts); 4145 } 4146 4147 @Test getAggregateReportById_fakeReport()4148 public void getAggregateReportById_fakeReport() { 4149 AggregateReport ar11 = 4150 AggregateReportFixture.getValidAggregateReportBuilder() 4151 .setId("11") 4152 .setIsFakeReport(true) 4153 .build(); 4154 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 4155 AbstractDbIntegrationTest.insertToDb(ar11, db); 4156 4157 Optional<AggregateReport> resOpt = 4158 mDatastoreManager.runInTransactionWithResult((dao) -> dao.getAggregateReport("11")); 4159 assertTrue(resOpt.isPresent()); 4160 AggregateReport res = resOpt.get(); 4161 assertTrue(res.isFakeReport()); 4162 } 4163 4164 @Test getNumEventReportsPerDestination_returnsExpected()4165 public void getNumEventReportsPerDestination_returnsExpected() { 4166 List<EventReport> reportsWithPlainDestination = 4167 Arrays.asList( 4168 generateMockEventReport(WebUtil.validUrl("https://destination-1.test"), 1)); 4169 List<EventReport> reportsWithPlainAndSubDomainDestination = 4170 Arrays.asList( 4171 generateMockEventReport(WebUtil.validUrl("https://destination-2.test"), 2), 4172 generateMockEventReport( 4173 WebUtil.validUrl("https://subdomain.destination-2.test"), 3)); 4174 List<EventReport> reportsWithPlainAndPathDestination = 4175 Arrays.asList( 4176 generateMockEventReport( 4177 WebUtil.validUrl("https://subdomain.destination-3.test"), 4), 4178 generateMockEventReport( 4179 WebUtil.validUrl("https://subdomain.destination-3.test/abcd"), 5)); 4180 List<EventReport> reportsWithAll3Types = 4181 Arrays.asList( 4182 generateMockEventReport(WebUtil.validUrl("https://destination-4.test"), 6), 4183 generateMockEventReport( 4184 WebUtil.validUrl("https://subdomain.destination-4.test"), 7), 4185 generateMockEventReport( 4186 WebUtil.validUrl("https://subdomain.destination-4.test/abcd"), 8)); 4187 List<EventReport> reportsWithAndroidAppDestination = 4188 Arrays.asList(generateMockEventReport("android-app://destination-5.app", 9)); 4189 4190 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 4191 Objects.requireNonNull(db); 4192 Stream.of( 4193 reportsWithPlainDestination, 4194 reportsWithPlainAndSubDomainDestination, 4195 reportsWithPlainAndPathDestination, 4196 reportsWithAll3Types, 4197 reportsWithAndroidAppDestination) 4198 .flatMap(Collection::stream) 4199 .forEach( 4200 eventReport -> { 4201 ContentValues values = new ContentValues(); 4202 values.put(EventReportContract.ID, eventReport.getId()); 4203 values.put( 4204 EventReportContract.ATTRIBUTION_DESTINATION, 4205 eventReport.getAttributionDestinations().get(0).toString()); 4206 db.insert(EventReportContract.TABLE, null, values); 4207 }); 4208 4209 List<String> attributionDestinations1 = createWebDestinationVariants(1); 4210 List<String> attributionDestinations2 = createWebDestinationVariants(2); 4211 List<String> attributionDestinations3 = createWebDestinationVariants(3); 4212 List<String> attributionDestinations4 = createWebDestinationVariants(4); 4213 List<String> attributionDestinations5 = createAppDestinationVariants(5); 4214 4215 // expected query return values for attribution destination variants 4216 List<Integer> destination1ExpectedCounts = Arrays.asList(1, 1, 1, 1, 0); 4217 List<Integer> destination2ExpectedCounts = Arrays.asList(2, 2, 2, 2, 0); 4218 List<Integer> destination3ExpectedCounts = Arrays.asList(2, 2, 2, 2, 0); 4219 List<Integer> destination4ExpectedCounts = Arrays.asList(3, 3, 3, 3, 0); 4220 List<Integer> destination5ExpectedCounts = Arrays.asList(0, 0, 1, 1, 0); 4221 assertEventReportCount( 4222 attributionDestinations1, EventSurfaceType.WEB, destination1ExpectedCounts); 4223 assertEventReportCount( 4224 attributionDestinations2, EventSurfaceType.WEB, destination2ExpectedCounts); 4225 assertEventReportCount( 4226 attributionDestinations3, EventSurfaceType.WEB, destination3ExpectedCounts); 4227 assertEventReportCount( 4228 attributionDestinations4, EventSurfaceType.WEB, destination4ExpectedCounts); 4229 assertEventReportCount( 4230 attributionDestinations5, EventSurfaceType.APP, destination5ExpectedCounts); 4231 } 4232 4233 @Test testGetSourceEventReports()4234 public void testGetSourceEventReports() { 4235 List<Source> sourceList = 4236 Arrays.asList( 4237 SourceFixture.getMinimalValidSourceBuilder() 4238 .setId("1") 4239 .setEventId(new UnsignedLong(3L)) 4240 .setEnrollmentId("1") 4241 .build(), 4242 SourceFixture.getMinimalValidSourceBuilder() 4243 .setId("2") 4244 .setEventId(new UnsignedLong(4L)) 4245 .setEnrollmentId("1") 4246 .build(), 4247 // Should always be ignored 4248 SourceFixture.getMinimalValidSourceBuilder() 4249 .setId("3") 4250 .setEventId(new UnsignedLong(4L)) 4251 .setEnrollmentId("2") 4252 .build(), 4253 SourceFixture.getMinimalValidSourceBuilder() 4254 .setId("15") 4255 .setEventId(new UnsignedLong(15L)) 4256 .setEnrollmentId("2") 4257 .build(), 4258 SourceFixture.getMinimalValidSourceBuilder() 4259 .setId("16") 4260 .setEventId(new UnsignedLong(16L)) 4261 .setEnrollmentId("2") 4262 .build(), 4263 SourceFixture.getMinimalValidSourceBuilder() 4264 .setId("20") 4265 .setEventId(new UnsignedLong(20L)) 4266 .setEnrollmentId("2") 4267 .build()); 4268 4269 List<Trigger> triggers = 4270 Arrays.asList( 4271 TriggerFixture.getValidTriggerBuilder() 4272 .setId("101") 4273 .setEnrollmentId("2") 4274 .build(), 4275 TriggerFixture.getValidTriggerBuilder() 4276 .setId("102") 4277 .setEnrollmentId("2") 4278 .build(), 4279 TriggerFixture.getValidTriggerBuilder() 4280 .setId("201") 4281 .setEnrollmentId("2") 4282 .build(), 4283 TriggerFixture.getValidTriggerBuilder() 4284 .setId("202") 4285 .setEnrollmentId("2") 4286 .build(), 4287 TriggerFixture.getValidTriggerBuilder() 4288 .setId("1001") 4289 .setEnrollmentId("2") 4290 .build()); 4291 4292 // Should match with source 1 4293 List<EventReport> reportList1 = new ArrayList<>(); 4294 reportList1.add( 4295 new EventReport.Builder() 4296 .setId("1") 4297 .setSourceEventId(new UnsignedLong(3L)) 4298 .setEnrollmentId("1") 4299 .setAttributionDestinations(sourceList.get(0).getAppDestinations()) 4300 .setSourceType(sourceList.get(0).getSourceType()) 4301 .setSourceId("1") 4302 .setTriggerId("101") 4303 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4304 .build()); 4305 reportList1.add( 4306 new EventReport.Builder() 4307 .setId("7") 4308 .setSourceEventId(new UnsignedLong(3L)) 4309 .setEnrollmentId("1") 4310 .setAttributionDestinations(List.of(APP_DESTINATION)) 4311 .setSourceType(sourceList.get(0).getSourceType()) 4312 .setSourceId("1") 4313 .setTriggerId("102") 4314 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4315 .build()); 4316 4317 // Should match with source 2 4318 List<EventReport> reportList2 = new ArrayList<>(); 4319 reportList2.add( 4320 new EventReport.Builder() 4321 .setId("3") 4322 .setSourceEventId(new UnsignedLong(4L)) 4323 .setEnrollmentId("1") 4324 .setAttributionDestinations(sourceList.get(1).getAppDestinations()) 4325 .setSourceType(sourceList.get(1).getSourceType()) 4326 .setSourceId("2") 4327 .setTriggerId("201") 4328 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4329 .build()); 4330 reportList2.add( 4331 new EventReport.Builder() 4332 .setId("8") 4333 .setSourceEventId(new UnsignedLong(4L)) 4334 .setEnrollmentId("1") 4335 .setAttributionDestinations(sourceList.get(1).getAppDestinations()) 4336 .setSourceType(sourceList.get(1).getSourceType()) 4337 .setSourceId("2") 4338 .setTriggerId("202") 4339 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4340 .build()); 4341 4342 List<EventReport> reportList3 = new ArrayList<>(); 4343 // Should not match with any source 4344 reportList3.add( 4345 new EventReport.Builder() 4346 .setId("2") 4347 .setSourceEventId(new UnsignedLong(5L)) 4348 .setEnrollmentId("1") 4349 .setSourceType(Source.SourceType.EVENT) 4350 .setAttributionDestinations(List.of(APP_DESTINATION)) 4351 .setSourceId("15") 4352 .setTriggerId("1001") 4353 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4354 .build()); 4355 reportList3.add( 4356 new EventReport.Builder() 4357 .setId("4") 4358 .setSourceEventId(new UnsignedLong(6L)) 4359 .setEnrollmentId("1") 4360 .setSourceType(Source.SourceType.EVENT) 4361 .setAttributionDestinations(List.of(APP_DESTINATION)) 4362 .setSourceId("16") 4363 .setTriggerId("1001") 4364 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4365 .setTriggerValue(100L) 4366 .build()); 4367 reportList3.add( 4368 new EventReport.Builder() 4369 .setId("5") 4370 .setSourceEventId(new UnsignedLong(1L)) 4371 .setEnrollmentId("1") 4372 .setSourceType(Source.SourceType.EVENT) 4373 .setAttributionDestinations(List.of(APP_DESTINATION)) 4374 .setSourceId("15") 4375 .setTriggerId("1001") 4376 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4377 .setTriggerValue(120L) 4378 .build()); 4379 reportList3.add( 4380 new EventReport.Builder() 4381 .setId("6") 4382 .setSourceEventId(new UnsignedLong(2L)) 4383 .setEnrollmentId("1") 4384 .setSourceType(Source.SourceType.EVENT) 4385 .setAttributionDestinations(List.of(APP_DESTINATION)) 4386 .setSourceId("20") 4387 .setTriggerId("1001") 4388 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4389 .setTriggerValue(200L) 4390 .build()); 4391 4392 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 4393 Objects.requireNonNull(db); 4394 sourceList.forEach(source -> insertSource(source, source.getId())); 4395 triggers.forEach(trigger -> AbstractDbIntegrationTest.insertToDb(trigger, db)); 4396 4397 Stream.of(reportList1, reportList2, reportList3) 4398 .flatMap(Collection::stream) 4399 .forEach( 4400 (eventReport -> { 4401 mDatastoreManager.runInTransaction( 4402 (dao) -> dao.insertEventReport(eventReport)); 4403 })); 4404 4405 assertEquals( 4406 reportList1, 4407 mDatastoreManager 4408 .runInTransactionWithResult( 4409 measurementDao -> 4410 measurementDao.getSourceEventReports(sourceList.get(0))) 4411 .orElseThrow()); 4412 4413 assertEquals( 4414 reportList2, 4415 mDatastoreManager 4416 .runInTransactionWithResult( 4417 measurementDao -> 4418 measurementDao.getSourceEventReports(sourceList.get(1))) 4419 .orElseThrow()); 4420 } 4421 4422 @Test getSourceEventReports_sourcesWithSameEventId_haveSeparateEventReportsMatch()4423 public void getSourceEventReports_sourcesWithSameEventId_haveSeparateEventReportsMatch() { 4424 List<Source> sourceList = 4425 Arrays.asList( 4426 SourceFixture.getMinimalValidSourceBuilder() 4427 .setId("1") 4428 .setEventId(new UnsignedLong(1L)) 4429 .setEnrollmentId("1") 4430 .build(), 4431 SourceFixture.getMinimalValidSourceBuilder() 4432 .setId("2") 4433 .setEventId(new UnsignedLong(1L)) 4434 .setEnrollmentId("1") 4435 .build(), 4436 SourceFixture.getMinimalValidSourceBuilder() 4437 .setId("3") 4438 .setEventId(new UnsignedLong(2L)) 4439 .setEnrollmentId("2") 4440 .build(), 4441 SourceFixture.getMinimalValidSourceBuilder() 4442 .setId("4") 4443 .setEventId(new UnsignedLong(2L)) 4444 .setEnrollmentId("2") 4445 .build()); 4446 4447 List<Trigger> triggers = 4448 Arrays.asList( 4449 TriggerFixture.getValidTriggerBuilder() 4450 .setId("101") 4451 .setEnrollmentId("2") 4452 .build(), 4453 TriggerFixture.getValidTriggerBuilder() 4454 .setId("102") 4455 .setEnrollmentId("2") 4456 .build()); 4457 4458 // Should match with source 1 4459 List<EventReport> reportList1 = new ArrayList<>(); 4460 reportList1.add( 4461 new EventReport.Builder() 4462 .setId("1") 4463 .setSourceEventId(new UnsignedLong(1L)) 4464 .setEnrollmentId("1") 4465 .setAttributionDestinations(sourceList.get(0).getAppDestinations()) 4466 .setSourceType(sourceList.get(0).getSourceType()) 4467 .setSourceId("1") 4468 .setTriggerId("101") 4469 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4470 .build()); 4471 reportList1.add( 4472 new EventReport.Builder() 4473 .setId("2") 4474 .setSourceEventId(new UnsignedLong(1L)) 4475 .setEnrollmentId("1") 4476 .setAttributionDestinations(List.of(APP_DESTINATION)) 4477 .setSourceType(sourceList.get(0).getSourceType()) 4478 .setSourceId("1") 4479 .setTriggerId("102") 4480 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4481 .build()); 4482 4483 // Should match with source 2 4484 List<EventReport> reportList2 = new ArrayList<>(); 4485 reportList2.add( 4486 new EventReport.Builder() 4487 .setId("3") 4488 .setSourceEventId(new UnsignedLong(2L)) 4489 .setEnrollmentId("1") 4490 .setAttributionDestinations(sourceList.get(1).getAppDestinations()) 4491 .setSourceType(sourceList.get(1).getSourceType()) 4492 .setSourceId("2") 4493 .setTriggerId("101") 4494 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4495 .build()); 4496 reportList2.add( 4497 new EventReport.Builder() 4498 .setId("4") 4499 .setSourceEventId(new UnsignedLong(2L)) 4500 .setEnrollmentId("1") 4501 .setAttributionDestinations(sourceList.get(1).getAppDestinations()) 4502 .setSourceType(sourceList.get(1).getSourceType()) 4503 .setSourceId("2") 4504 .setTriggerId("102") 4505 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4506 .build()); 4507 4508 // Match with source3 4509 List<EventReport> reportList3 = new ArrayList<>(); 4510 reportList3.add( 4511 new EventReport.Builder() 4512 .setId("5") 4513 .setSourceEventId(new UnsignedLong(2L)) 4514 .setEnrollmentId("2") 4515 .setSourceType(Source.SourceType.EVENT) 4516 .setAttributionDestinations(List.of(APP_DESTINATION)) 4517 .setSourceId("3") 4518 .setTriggerId("101") 4519 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4520 .build()); 4521 reportList3.add( 4522 new EventReport.Builder() 4523 .setId("6") 4524 .setSourceEventId(new UnsignedLong(2L)) 4525 .setEnrollmentId("2") 4526 .setSourceType(Source.SourceType.EVENT) 4527 .setAttributionDestinations(List.of(APP_DESTINATION)) 4528 .setSourceId("3") 4529 .setTriggerId("102") 4530 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4531 .build()); 4532 4533 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 4534 Objects.requireNonNull(db); 4535 sourceList.forEach(source -> insertSource(source, source.getId())); 4536 triggers.forEach(trigger -> AbstractDbIntegrationTest.insertToDb(trigger, db)); 4537 4538 Stream.of(reportList1, reportList2, reportList3) 4539 .flatMap(Collection::stream) 4540 .forEach( 4541 (eventReport -> { 4542 mDatastoreManager.runInTransaction( 4543 (dao) -> dao.insertEventReport(eventReport)); 4544 })); 4545 4546 assertEquals( 4547 reportList1, 4548 mDatastoreManager 4549 .runInTransactionWithResult( 4550 measurementDao -> 4551 measurementDao.getSourceEventReports(sourceList.get(0))) 4552 .orElseThrow()); 4553 4554 assertEquals( 4555 reportList2, 4556 mDatastoreManager 4557 .runInTransactionWithResult( 4558 measurementDao -> 4559 measurementDao.getSourceEventReports(sourceList.get(1))) 4560 .orElseThrow()); 4561 4562 assertEquals( 4563 reportList3, 4564 mDatastoreManager 4565 .runInTransactionWithResult( 4566 measurementDao -> 4567 measurementDao.getSourceEventReports(sourceList.get(2))) 4568 .orElseThrow()); 4569 } 4570 4571 @Test testUpdateSourceStatus()4572 public void testUpdateSourceStatus() { 4573 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 4574 Objects.requireNonNull(db); 4575 4576 List<Source> sourceList = new ArrayList<>(); 4577 sourceList.add(SourceFixture.getMinimalValidSourceBuilder().setId("1").build()); 4578 sourceList.add(SourceFixture.getMinimalValidSourceBuilder().setId("2").build()); 4579 sourceList.add(SourceFixture.getMinimalValidSourceBuilder().setId("3").build()); 4580 sourceList.forEach( 4581 source -> { 4582 ContentValues values = new ContentValues(); 4583 values.put(SourceContract.ID, source.getId()); 4584 values.put(SourceContract.STATUS, 1); 4585 db.insert(SourceContract.TABLE, null, values); 4586 }); 4587 4588 // Multiple Elements 4589 assertTrue( 4590 mDatastoreManager.runInTransaction( 4591 measurementDao -> 4592 measurementDao.updateSourceStatus( 4593 List.of("1", "2", "3"), Source.Status.IGNORED))); 4594 4595 // Single Element 4596 assertTrue( 4597 mDatastoreManager.runInTransaction( 4598 measurementDao -> 4599 measurementDao.updateSourceStatus( 4600 List.of("1", "2"), Source.Status.IGNORED))); 4601 } 4602 4603 @Test updateSourceAttributedTriggers_baseline_equal()4604 public void updateSourceAttributedTriggers_baseline_equal() throws JSONException { 4605 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 4606 Objects.requireNonNull(db); 4607 4608 List<Source> sourceList = new ArrayList<>(); 4609 sourceList.add( 4610 SourceFixture.getValidFullSourceBuilderWithFlexEventReportValueSum() 4611 .setId("1") 4612 .build()); 4613 sourceList.add( 4614 SourceFixture.getValidFullSourceBuilderWithFlexEventReportValueSum() 4615 .setId("2") 4616 .build()); 4617 sourceList.add( 4618 SourceFixture.getValidFullSourceBuilderWithFlexEventReportValueSum() 4619 .setId("3") 4620 .build()); 4621 mDatastoreManager.runInTransaction( 4622 (dao) -> { 4623 for (Source source : sourceList) { 4624 dao.insertSource(source); 4625 } 4626 }); 4627 List<EventReport> eventReportList = new ArrayList<>(); 4628 eventReportList.add( 4629 EventReportFixture.getBaseEventReportBuild() 4630 .setTriggerData(new UnsignedLong(1L)) 4631 .setTriggerPriority(3L) 4632 .setTriggerValue(5L) 4633 .setReportTime(10000L) 4634 .build()); 4635 Source originalSource = sourceList.get(0); 4636 insertAttributedTrigger(originalSource.getTriggerSpecs(), eventReportList.get(0)); 4637 Optional<Source> newSource = 4638 mDatastoreManager.runInTransactionWithResult( 4639 measurementDao -> measurementDao.getSource(originalSource.getId())); 4640 assertTrue(newSource.isPresent()); 4641 4642 assertNotEquals(newSource.get(), originalSource); 4643 newSource.get().buildTriggerSpecs(); 4644 assertEquals(0, newSource.get().getTriggerSpecs().getAttributedTriggers().size()); 4645 4646 mDatastoreManager.runInTransaction( 4647 measurementDao -> 4648 measurementDao.updateSourceAttributedTriggers( 4649 originalSource.getId(), 4650 originalSource.attributedTriggersToJsonFlexApi())); 4651 4652 mDatastoreManager.runInTransaction( 4653 measurementDao -> { 4654 assertEquals( 4655 originalSource.attributedTriggersToJsonFlexApi(), 4656 measurementDao 4657 .getSource(originalSource.getId()) 4658 .getEventAttributionStatus()); 4659 }); 4660 } 4661 4662 @Test testGetMatchingActiveSources()4663 public void testGetMatchingActiveSources() { 4664 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 4665 Objects.requireNonNull(db); 4666 String enrollmentId = "enrollment-id"; 4667 Uri appDestination = Uri.parse("android-app://com.example.abc"); 4668 Uri webDestination = WebUtil.validUri("https://example.test"); 4669 Uri webDestinationWithSubdomain = WebUtil.validUri("https://xyz.example.test"); 4670 Source sApp1 = 4671 SourceFixture.getMinimalValidSourceBuilder() 4672 .setId("1") 4673 .setEventTime(10) 4674 .setExpiryTime(20) 4675 .setAppDestinations(List.of(appDestination)) 4676 .setEnrollmentId(enrollmentId) 4677 .build(); 4678 Source sApp2 = 4679 SourceFixture.getMinimalValidSourceBuilder() 4680 .setId("2") 4681 .setEventTime(10) 4682 .setExpiryTime(50) 4683 .setAppDestinations(List.of(appDestination)) 4684 .setEnrollmentId(enrollmentId) 4685 .build(); 4686 Source sApp3 = 4687 SourceFixture.getMinimalValidSourceBuilder() 4688 .setId("3") 4689 .setEventTime(20) 4690 .setExpiryTime(50) 4691 .setAppDestinations(List.of(appDestination)) 4692 .setEnrollmentId(enrollmentId) 4693 .build(); 4694 Source sApp4 = 4695 SourceFixture.getMinimalValidSourceBuilder() 4696 .setId("4") 4697 .setEventTime(30) 4698 .setExpiryTime(50) 4699 .setAppDestinations(List.of(appDestination)) 4700 .setEnrollmentId(enrollmentId) 4701 .build(); 4702 Source sWeb5 = 4703 SourceFixture.getMinimalValidSourceBuilder() 4704 .setId("5") 4705 .setEventTime(10) 4706 .setExpiryTime(20) 4707 .setWebDestinations(List.of(webDestination)) 4708 .setEnrollmentId(enrollmentId) 4709 .build(); 4710 Source sWeb6 = 4711 SourceFixture.getMinimalValidSourceBuilder() 4712 .setId("6") 4713 .setEventTime(10) 4714 .setExpiryTime(50) 4715 .setWebDestinations(List.of(webDestination)) 4716 .setEnrollmentId(enrollmentId) 4717 .build(); 4718 Source sAppWeb7 = 4719 SourceFixture.getMinimalValidSourceBuilder() 4720 .setId("7") 4721 .setEventTime(10) 4722 .setExpiryTime(20) 4723 .setAppDestinations(List.of(appDestination)) 4724 .setWebDestinations(List.of(webDestination)) 4725 .setEnrollmentId(enrollmentId) 4726 .build(); 4727 4728 List<Source> sources = Arrays.asList(sApp1, sApp2, sApp3, sApp4, sWeb5, sWeb6, sAppWeb7); 4729 sources.forEach(source -> insertInDb(db, source)); 4730 4731 Function<Trigger, List<Source>> runFunc = 4732 trigger -> { 4733 List<Source> result = 4734 mDatastoreManager 4735 .runInTransactionWithResult( 4736 measurementDao -> 4737 measurementDao.getMatchingActiveSources( 4738 trigger)) 4739 .orElseThrow(); 4740 result.sort(Comparator.comparing(Source::getId)); 4741 return result; 4742 }; 4743 4744 // Trigger Time > sApp1's eventTime and < sApp1's expiryTime 4745 // Trigger Time > sApp2's eventTime and < sApp2's expiryTime 4746 // Trigger Time < sApp3's eventTime 4747 // Trigger Time < sApp4's eventTime 4748 // sApp5 and sApp6 don't have app destination 4749 // Trigger Time > sAppWeb7's eventTime and < sAppWeb7's expiryTime 4750 // Expected: Match with sApp1, sApp2, sAppWeb7 4751 Trigger trigger1MatchSource1And2 = 4752 TriggerFixture.getValidTriggerBuilder() 4753 .setTriggerTime(12) 4754 .setEnrollmentId(enrollmentId) 4755 .setAttributionDestination(appDestination) 4756 .setDestinationType(EventSurfaceType.APP) 4757 .build(); 4758 List<Source> result1 = runFunc.apply(trigger1MatchSource1And2); 4759 assertEquals(3, result1.size()); 4760 assertEquals(sApp1.getId(), result1.get(0).getId()); 4761 assertEquals(sApp2.getId(), result1.get(1).getId()); 4762 assertEquals(sAppWeb7.getId(), result1.get(2).getId()); 4763 4764 // Trigger Time > sApp1's eventTime and = sApp1's expiryTime 4765 // Trigger Time > sApp2's eventTime and < sApp2's expiryTime 4766 // Trigger Time = sApp3's eventTime 4767 // Trigger Time < sApp4's eventTime 4768 // sApp5 and sApp6 don't have app destination 4769 // Trigger Time > sAppWeb7's eventTime and = sAppWeb7's expiryTime 4770 // Expected: Match with sApp2, sApp3 4771 Trigger trigger2MatchSource127 = 4772 TriggerFixture.getValidTriggerBuilder() 4773 .setTriggerTime(20) 4774 .setEnrollmentId(enrollmentId) 4775 .setAttributionDestination(appDestination) 4776 .setDestinationType(EventSurfaceType.APP) 4777 .build(); 4778 4779 List<Source> result2 = runFunc.apply(trigger2MatchSource127); 4780 assertEquals(2, result2.size()); 4781 assertEquals(sApp2.getId(), result2.get(0).getId()); 4782 assertEquals(sApp3.getId(), result2.get(1).getId()); 4783 4784 // Trigger Time > sApp1's expiryTime 4785 // Trigger Time > sApp2's eventTime and < sApp2's expiryTime 4786 // Trigger Time > sApp3's eventTime and < sApp3's expiryTime 4787 // Trigger Time < sApp4's eventTime 4788 // sApp5 and sApp6 don't have app destination 4789 // Trigger Time > sAppWeb7's expiryTime 4790 // Expected: Match with sApp2, sApp3 4791 Trigger trigger3MatchSource237 = 4792 TriggerFixture.getValidTriggerBuilder() 4793 .setTriggerTime(21) 4794 .setEnrollmentId(enrollmentId) 4795 .setAttributionDestination(appDestination) 4796 .setDestinationType(EventSurfaceType.APP) 4797 .build(); 4798 4799 List<Source> result3 = runFunc.apply(trigger3MatchSource237); 4800 assertEquals(2, result3.size()); 4801 assertEquals(sApp2.getId(), result3.get(0).getId()); 4802 assertEquals(sApp3.getId(), result3.get(1).getId()); 4803 4804 // Trigger Time > sApp1's expiryTime 4805 // Trigger Time > sApp2's eventTime and < sApp2's expiryTime 4806 // Trigger Time > sApp3's eventTime and < sApp3's expiryTime 4807 // Trigger Time > sApp4's eventTime and < sApp4's expiryTime 4808 // sApp5 and sApp6 don't have app destination 4809 // Trigger Time > sAppWeb7's expiryTime 4810 // Expected: Match with sApp2, sApp3 and sApp4 4811 Trigger trigger4MatchSource1And2And3 = 4812 TriggerFixture.getValidTriggerBuilder() 4813 .setTriggerTime(31) 4814 .setEnrollmentId(enrollmentId) 4815 .setAttributionDestination(appDestination) 4816 .setDestinationType(EventSurfaceType.APP) 4817 .build(); 4818 4819 List<Source> result4 = runFunc.apply(trigger4MatchSource1And2And3); 4820 assertEquals(3, result4.size()); 4821 assertEquals(sApp2.getId(), result4.get(0).getId()); 4822 assertEquals(sApp3.getId(), result4.get(1).getId()); 4823 assertEquals(sApp4.getId(), result4.get(2).getId()); 4824 4825 // sApp1, sApp2, sApp3, sApp4 don't have web destination 4826 // Trigger Time > sWeb5's eventTime and < sApp5's expiryTime 4827 // Trigger Time > sWeb6's eventTime and < sApp6's expiryTime 4828 // Trigger Time > sAppWeb7's eventTime and < sAppWeb7's expiryTime 4829 // Expected: Match with sApp5, sApp6, sAppWeb7 4830 Trigger trigger5MatchSource567 = 4831 TriggerFixture.getValidTriggerBuilder() 4832 .setTriggerTime(12) 4833 .setEnrollmentId(enrollmentId) 4834 .setAttributionDestination(webDestination) 4835 .setDestinationType(EventSurfaceType.WEB) 4836 .build(); 4837 List<Source> result5 = runFunc.apply(trigger5MatchSource567); 4838 assertEquals(3, result1.size()); 4839 assertEquals(sWeb5.getId(), result5.get(0).getId()); 4840 assertEquals(sWeb6.getId(), result5.get(1).getId()); 4841 assertEquals(sAppWeb7.getId(), result5.get(2).getId()); 4842 4843 // sApp1, sApp2, sApp3, sApp4 don't have web destination 4844 // Trigger Time > sWeb5's expiryTime 4845 // Trigger Time > sWeb6's eventTime and < sApp6's expiryTime 4846 // Trigger Time > sWeb7's expiryTime 4847 // Expected: Match with sApp6 only 4848 Trigger trigger6MatchSource67 = 4849 TriggerFixture.getValidTriggerBuilder() 4850 .setTriggerTime(21) 4851 .setEnrollmentId(enrollmentId) 4852 .setAttributionDestination(webDestinationWithSubdomain) 4853 .setDestinationType(EventSurfaceType.WEB) 4854 .build(); 4855 4856 List<Source> result6 = runFunc.apply(trigger6MatchSource67); 4857 assertEquals(1, result6.size()); 4858 assertEquals(sWeb6.getId(), result6.get(0).getId()); 4859 4860 // Trigger with different subdomain than source 4861 // Expected: No Match found 4862 Trigger triggerDifferentRegistrationOrigin = 4863 TriggerFixture.getValidTriggerBuilder() 4864 .setTriggerTime(12) 4865 .setEnrollmentId(enrollmentId) 4866 .setAttributionDestination(appDestination) 4867 .setDestinationType(EventSurfaceType.APP) 4868 .setRegistrationOrigin( 4869 WebUtil.validUri("https://subdomain-different.example.test")) 4870 .build(); 4871 4872 List<Source> result7 = runFunc.apply(triggerDifferentRegistrationOrigin); 4873 assertTrue(result7.isEmpty()); 4874 4875 // Trigger with different domain than source 4876 // Expected: No Match found 4877 Trigger triggerDifferentDomainOrigin = 4878 TriggerFixture.getValidTriggerBuilder() 4879 .setTriggerTime(12) 4880 .setEnrollmentId(enrollmentId) 4881 .setAttributionDestination(appDestination) 4882 .setDestinationType(EventSurfaceType.APP) 4883 .setRegistrationOrigin( 4884 WebUtil.validUri("https://subdomain.example-different.test")) 4885 .build(); 4886 4887 List<Source> result8 = runFunc.apply(triggerDifferentDomainOrigin); 4888 assertTrue(result8.isEmpty()); 4889 4890 // Trigger with different port than source 4891 // Expected: No Match found 4892 Trigger triggerDifferentPort = 4893 TriggerFixture.getValidTriggerBuilder() 4894 .setTriggerTime(12) 4895 .setEnrollmentId(enrollmentId) 4896 .setAttributionDestination(appDestination) 4897 .setDestinationType(EventSurfaceType.APP) 4898 .setRegistrationOrigin( 4899 WebUtil.validUri("https://subdomain.example.test:8083")) 4900 .build(); 4901 4902 List<Source> result9 = runFunc.apply(triggerDifferentPort); 4903 assertTrue(result9.isEmpty()); 4904 4905 // Enrollment id for trigger and source not same 4906 // Registration Origin for trigger and source same 4907 // Expected: Match with sApp1, sApp2, sAppWeb7 4908 Trigger triggerDifferentEnrollmentSameRegistration = 4909 TriggerFixture.getValidTriggerBuilder() 4910 .setTriggerTime(12) 4911 .setEnrollmentId("different-enrollment-id") 4912 .setAttributionDestination(appDestination) 4913 .setDestinationType(EventSurfaceType.APP) 4914 .build(); 4915 List<Source> result10 = runFunc.apply(triggerDifferentEnrollmentSameRegistration); 4916 assertEquals(3, result10.size()); 4917 assertEquals(sApp1.getId(), result10.get(0).getId()); 4918 assertEquals(sApp2.getId(), result10.get(1).getId()); 4919 assertEquals(sAppWeb7.getId(), result10.get(2).getId()); 4920 } 4921 4922 @Test testGetMatchingActiveSources_multipleDestinations()4923 public void testGetMatchingActiveSources_multipleDestinations() { 4924 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 4925 String enrollmentId = "enrollment-id"; 4926 Uri webDestination1 = WebUtil.validUri("https://example.test"); 4927 Uri webDestination1WithSubdomain = WebUtil.validUri("https://xyz.example.test"); 4928 Uri webDestination2 = WebUtil.validUri("https://example2.test"); 4929 Source sWeb1 = 4930 SourceFixture.getMinimalValidSourceBuilder() 4931 .setId("1") 4932 .setEventTime(10) 4933 .setExpiryTime(20) 4934 .setWebDestinations(List.of(webDestination1)) 4935 .setEnrollmentId(enrollmentId) 4936 .build(); 4937 Source sWeb2 = 4938 SourceFixture.getMinimalValidSourceBuilder() 4939 .setId("2") 4940 .setEventTime(10) 4941 .setExpiryTime(50) 4942 .setWebDestinations(List.of(webDestination1, webDestination2)) 4943 .setEnrollmentId(enrollmentId) 4944 .build(); 4945 Source sAppWeb3 = 4946 SourceFixture.getMinimalValidSourceBuilder() 4947 .setId("3") 4948 .setEventTime(10) 4949 .setExpiryTime(20) 4950 .setWebDestinations(List.of(webDestination1)) 4951 .setEnrollmentId(enrollmentId) 4952 .build(); 4953 4954 List<Source> sources = Arrays.asList(sWeb1, sWeb2, sAppWeb3); 4955 sources.forEach(source -> insertInDb(db, source)); 4956 4957 Function<Trigger, List<Source>> getMatchingSources = 4958 trigger -> { 4959 List<Source> result = 4960 mDatastoreManager 4961 .runInTransactionWithResult( 4962 measurementDao -> 4963 measurementDao.getMatchingActiveSources( 4964 trigger)) 4965 .orElseThrow(); 4966 result.sort(Comparator.comparing(Source::getId)); 4967 return result; 4968 }; 4969 4970 Trigger triggerMatchSourceWeb2 = 4971 TriggerFixture.getValidTriggerBuilder() 4972 .setTriggerTime(21) 4973 .setEnrollmentId(enrollmentId) 4974 .setAttributionDestination(webDestination1WithSubdomain) 4975 .setDestinationType(EventSurfaceType.WEB) 4976 .build(); 4977 4978 List<Source> result = getMatchingSources.apply(triggerMatchSourceWeb2); 4979 assertEquals(1, result.size()); 4980 assertEquals(sWeb2.getId(), result.get(0).getId()); 4981 } 4982 4983 @Test testGetMatchingActiveSources_attributionScopeEnabled_populateScopes()4984 public void testGetMatchingActiveSources_attributionScopeEnabled_populateScopes() { 4985 mFlags = mock(Flags.class); 4986 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 4987 doReturn(true).when(mFlags).getMeasurementEnableAttributionScope(); 4988 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mFlags).getMeasurementDbSizeLimit(); 4989 4990 // S0: attribution scopes -> [], destinations -> [D1, D2] 4991 Source source0 = 4992 insertSourceForAttributionScope( 4993 /* attributionScopes= */ null, 4994 /* attributionScopeLimit= */ null, 4995 /* maxEventStates= */ null, 4996 SOURCE_EVENT_TIME, 4997 List.of(WEB_ONE_DESTINATION), 4998 List.of(APP_ONE_DESTINATION)); 4999 // S1: attribution scopes -> ["1", "2"], destinations -> [D1] 5000 Source source1 = 5001 insertSourceForAttributionScope( 5002 List.of("1", "2"), 5003 ATTRIBUTION_SCOPE_LIMIT, 5004 MAX_EVENT_STATES, 5005 SOURCE_EVENT_TIME + 1, 5006 null, 5007 List.of(APP_ONE_DESTINATION)); 5008 // S2: attribution scopes -> ["2", "3"], destinations -> [D2] 5009 Source source2 = 5010 insertSourceForAttributionScope( 5011 List.of("2", "3"), 5012 ATTRIBUTION_SCOPE_LIMIT, 5013 MAX_EVENT_STATES, 5014 SOURCE_EVENT_TIME + 2, 5015 List.of(WEB_ONE_DESTINATION), 5016 null); 5017 5018 Trigger trigger0 = 5019 TriggerFixture.getValidTriggerBuilder() 5020 .setTriggerTime(SOURCE_EVENT_TIME + 3) 5021 .setAttributionDestination(APP_ONE_DESTINATION) 5022 .setDestinationType(EventSurfaceType.APP) 5023 .build(); 5024 List<Source> matchingSources0 = getMatchingSources(trigger0); 5025 assertThat(matchingSources0.size()).isEqualTo(2); 5026 List<String> matchingSourceIds0 = 5027 matchingSources0.stream().map(Source::getId).collect(Collectors.toList()); 5028 List<List<String>> matchingSourceAttributionScopes0 = 5029 matchingSources0.stream() 5030 .map(Source::getAttributionScopes) 5031 .collect(Collectors.toList()); 5032 assertThat(matchingSourceIds0).containsExactly(source0.getId(), source1.getId()); 5033 // Source attribution scopes won't be populated if trigger doesn't have attribution scope. 5034 assertThat(matchingSourceAttributionScopes0).containsExactly(null, null); 5035 5036 Trigger trigger1 = 5037 TriggerFixture.getValidTriggerBuilder() 5038 .setTriggerTime(SOURCE_EVENT_TIME + 4) 5039 .setAttributionScopesString("1") 5040 .setAttributionDestination(APP_ONE_DESTINATION) 5041 .setDestinationType(EventSurfaceType.APP) 5042 .build(); 5043 List<Source> matchingSources1 = getMatchingSources(trigger1); 5044 List<String> matchingSourceIds1 = 5045 matchingSources1.stream().map(Source::getId).collect(Collectors.toList()); 5046 List<List<String>> matchingSourceAttributionScopes1 = 5047 matchingSources1.stream() 5048 .map(Source::getAttributionScopes) 5049 .collect(Collectors.toList()); 5050 assertThat(matchingSourceIds1).containsExactly(source0.getId(), source1.getId()); 5051 assertThat(matchingSourceAttributionScopes1) 5052 .containsExactly(source0.getAttributionScopes(), source1.getAttributionScopes()); 5053 5054 Trigger trigger2 = 5055 TriggerFixture.getValidTriggerBuilder() 5056 .setTriggerTime(SOURCE_EVENT_TIME + 5) 5057 .setAttributionScopesString("2") 5058 .setAttributionDestination(WEB_ONE_DESTINATION) 5059 .setDestinationType(EventSurfaceType.WEB) 5060 .build(); 5061 List<Source> matchingSources2 = getMatchingSources(trigger2); 5062 List<String> matchingSourceIds2 = 5063 matchingSources2.stream().map(Source::getId).collect(Collectors.toList()); 5064 List<List<String>> matchingSourceAttributionScopes2 = 5065 matchingSources2.stream() 5066 .map(Source::getAttributionScopes) 5067 .collect(Collectors.toList()); 5068 assertThat(matchingSourceIds2).containsExactly(source0.getId(), source2.getId()); 5069 assertThat(matchingSourceAttributionScopes2) 5070 .containsExactly(source0.getAttributionScopes(), source2.getAttributionScopes()); 5071 } 5072 5073 @Test testGetAttributionScopesForRegistration()5074 public void testGetAttributionScopesForRegistration() { 5075 mFlags = mock(Flags.class); 5076 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 5077 doReturn(true).when(mFlags).getMeasurementEnableAttributionScope(); 5078 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mFlags).getMeasurementDbSizeLimit(); 5079 5080 insertSourceForAttributionScope( 5081 List.of("1"), 5082 ATTRIBUTION_SCOPE_LIMIT, 5083 MAX_EVENT_STATES, 5084 SOURCE_EVENT_TIME, 5085 List.of(WEB_ONE_DESTINATION), 5086 List.of(APP_ONE_DESTINATION), 5087 SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN, 5088 SourceFixture.ValidSourceParams.REGISTRATION_ID, 5089 Source.SourceType.NAVIGATION, 5090 Source.Status.ACTIVE); 5091 insertSourceForAttributionScope( 5092 List.of("2"), 5093 ATTRIBUTION_SCOPE_LIMIT, 5094 MAX_EVENT_STATES, 5095 SOURCE_EVENT_TIME, 5096 List.of(WEB_ONE_DESTINATION), 5097 List.of(APP_ONE_DESTINATION), 5098 SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN, 5099 SourceFixture.ValidSourceParams.REGISTRATION_ID, 5100 Source.SourceType.NAVIGATION, 5101 Source.Status.ACTIVE); 5102 insertSourceForAttributionScope( 5103 List.of("3"), 5104 ATTRIBUTION_SCOPE_LIMIT, 5105 MAX_EVENT_STATES, 5106 SOURCE_EVENT_TIME, 5107 List.of(WEB_ONE_DESTINATION), 5108 List.of(APP_ONE_DESTINATION), 5109 REGISTRATION_ORIGIN_2, 5110 REGISTRATION_ID2, 5111 Source.SourceType.NAVIGATION, 5112 Source.Status.ACTIVE); 5113 // Ignored source, attribution scopes ignored. 5114 insertSourceForAttributionScope( 5115 List.of("4"), 5116 ATTRIBUTION_SCOPE_LIMIT, 5117 MAX_EVENT_STATES, 5118 SOURCE_EVENT_TIME, 5119 List.of(WEB_ONE_DESTINATION), 5120 List.of(APP_ONE_DESTINATION), 5121 SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN, 5122 SourceFixture.ValidSourceParams.REGISTRATION_ID, 5123 Source.SourceType.NAVIGATION, 5124 Source.Status.IGNORED); 5125 5126 // Execution 5127 mDatastoreManager.runInTransaction( 5128 (dao) -> { 5129 assertThat( 5130 dao.getAttributionScopesForRegistration( 5131 SourceFixture.ValidSourceParams.REGISTRATION_ID, 5132 SourceFixture.ValidSourceParams 5133 .REGISTRATION_ORIGIN 5134 .toString()) 5135 .get()) 5136 .containsExactly("1", "2"); 5137 assertThat( 5138 dao.getAttributionScopesForRegistration( 5139 REGISTRATION_ID2, 5140 REGISTRATION_ORIGIN_2.toString()) 5141 .get()) 5142 .containsExactly("3"); 5143 assertThat( 5144 dao.getAttributionScopesForRegistration( 5145 SourceFixture.ValidSourceParams.REGISTRATION_ID, 5146 SourceFixture.ValidSourceParams 5147 .REGISTRATION_ORIGIN 5148 .toString()) 5149 .get()) 5150 .containsExactly("1", "2"); 5151 assertThat( 5152 dao.getAttributionScopesForRegistration( 5153 REGISTRATION_ID2, 5154 REGISTRATION_ORIGIN_2.toString()) 5155 .get()) 5156 .containsExactly("3"); 5157 assertThat( 5158 dao.getAttributionScopesForRegistration( 5159 SourceFixture.ValidSourceParams.REGISTRATION_ID, 5160 REGISTRATION_ORIGIN_2.toString()) 5161 .isEmpty()) 5162 .isTrue(); 5163 }); 5164 } 5165 5166 @Test testGetMatchingActiveDelayedSources()5167 public void testGetMatchingActiveDelayedSources() { 5168 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 5169 Objects.requireNonNull(db); 5170 String enrollmentId = "enrollment-id"; 5171 Uri appDestination = Uri.parse("android-app://com.example.abc"); 5172 Uri webDestination = WebUtil.validUri("https://example.test"); 5173 Source sApp1 = 5174 SourceFixture.getMinimalValidSourceBuilder() 5175 .setId("1") 5176 .setEventTime(10) 5177 .setExpiryTime(20) 5178 .setAppDestinations(List.of(appDestination)) 5179 .setEnrollmentId(enrollmentId) 5180 .build(); 5181 Source sApp2 = 5182 SourceFixture.getMinimalValidSourceBuilder() 5183 .setId("2") 5184 .setEventTime(140) 5185 .setExpiryTime(200) 5186 .setAppDestinations(List.of(appDestination)) 5187 .setEnrollmentId(enrollmentId) 5188 .build(); 5189 Source sApp3 = 5190 SourceFixture.getMinimalValidSourceBuilder() 5191 .setId("3") 5192 .setEventTime(20) 5193 .setExpiryTime(50) 5194 .setAppDestinations(List.of(appDestination)) 5195 .setEnrollmentId(enrollmentId) 5196 .build(); 5197 Source sApp4 = 5198 SourceFixture.getMinimalValidSourceBuilder() 5199 .setId("4") 5200 .setEventTime(16) 5201 .setExpiryTime(50) 5202 .setAppDestinations(List.of(appDestination)) 5203 .setEnrollmentId(enrollmentId) 5204 .build(); 5205 Source sWeb5 = 5206 SourceFixture.getMinimalValidSourceBuilder() 5207 .setId("5") 5208 .setEventTime(13) 5209 .setExpiryTime(20) 5210 .setWebDestinations(List.of(webDestination)) 5211 .setEnrollmentId(enrollmentId) 5212 .build(); 5213 Source sWeb6 = 5214 SourceFixture.getMinimalValidSourceBuilder() 5215 .setId("6") 5216 .setEventTime(14) 5217 .setExpiryTime(50) 5218 .setWebDestinations(List.of(webDestination)) 5219 .setEnrollmentId(enrollmentId) 5220 .build(); 5221 Source sAppWeb7 = 5222 SourceFixture.getMinimalValidSourceBuilder() 5223 .setId("7") 5224 .setEventTime(10) 5225 .setExpiryTime(20) 5226 .setAppDestinations(List.of(appDestination)) 5227 .setWebDestinations(List.of(webDestination)) 5228 .setEnrollmentId(enrollmentId) 5229 .build(); 5230 Source sAppWeb8 = 5231 SourceFixture.getMinimalValidSourceBuilder() 5232 .setId("8") 5233 .setEventTime(15) 5234 .setExpiryTime(25) 5235 .setAppDestinations(List.of(appDestination)) 5236 .setWebDestinations(List.of(webDestination)) 5237 .setEnrollmentId(enrollmentId) 5238 .build(); 5239 5240 List<Source> sources = 5241 Arrays.asList(sApp1, sApp2, sApp3, sApp4, sWeb5, sWeb6, sAppWeb7, sAppWeb8); 5242 sources.forEach(source -> insertInDb(db, source)); 5243 5244 Function<Trigger, Optional<Source>> runFunc = 5245 trigger -> { 5246 Optional<Source> result = 5247 mDatastoreManager 5248 .runInTransactionWithResult( 5249 measurementDao -> 5250 measurementDao 5251 .getNearestDelayedMatchingActiveSource( 5252 trigger)) 5253 .orElseThrow(); 5254 return result; 5255 }; 5256 5257 // sApp1's eventTime <= Trigger Time 5258 // Trigger Time + MAX_DELAYED_SOURCE_REGISTRATION_WINDOW > sApp2's eventTime 5259 // Trigger Time < sApp3's eventTime <= Trigger Time + MAX_DELAYED_SOURCE_REGISTRATION_WINDOW 5260 // Trigger Time < sApp4's eventTime <= Trigger Time + MAX_DELAYED_SOURCE_REGISTRATION_WINDOW 5261 // sWeb5 and sWeb6 don't have app destination 5262 // sAppWeb7's eventTime <= Trigger Time 5263 // Trigger Time < sAppWeb8's eventTime <= Trigger Time + 5264 // MAX_DELAYED_SOURCE_REGISTRATION_WINDOW 5265 // Expected: Match with sAppWeb8 5266 Trigger trigger1MatchSource8 = 5267 TriggerFixture.getValidTriggerBuilder() 5268 .setTriggerTime(12) 5269 .setEnrollmentId(enrollmentId) 5270 .setAttributionDestination(appDestination) 5271 .setDestinationType(EventSurfaceType.APP) 5272 .build(); 5273 Optional<Source> result1 = runFunc.apply(trigger1MatchSource8); 5274 assertEquals(sAppWeb8.getId(), result1.get().getId()); 5275 5276 // sApp1's eventTime <= Trigger Time 5277 // Trigger Time + MAX_DELAYED_SOURCE_REGISTRATION_WINDOW > sApp2's eventTime 5278 // Trigger Time < sApp3's eventTime <= Trigger Time + MAX_DELAYED_SOURCE_REGISTRATION_WINDOW 5279 // Trigger Time < sApp4's eventTime <= Trigger Time + MAX_DELAYED_SOURCE_REGISTRATION_WINDOW 5280 // sWeb5 and sWeb6 don't have app destination 5281 // sAppWeb7's eventTime <= Trigger Time 5282 // sAppWeb8's eventTime <= Trigger Time 5283 // Expected: Match with sApp4 5284 Trigger trigger2MatchSource4 = 5285 TriggerFixture.getValidTriggerBuilder() 5286 .setTriggerTime(15) 5287 .setEnrollmentId(enrollmentId) 5288 .setAttributionDestination(appDestination) 5289 .setDestinationType(EventSurfaceType.APP) 5290 .build(); 5291 Optional<Source> result2 = runFunc.apply(trigger2MatchSource4); 5292 assertEquals(sApp4.getId(), result2.get().getId()); 5293 5294 // sApp1's eventTime <= Trigger Time 5295 // sApp2's eventTime <= Trigger Time 5296 // sApp3's eventTime <= Trigger Time 5297 // sApp4's eventTime <= Trigger Time 5298 // sWeb5 and sWeb6 don't have app destination 5299 // sAppWeb7's eventTime <= Trigger Time 5300 // sAppWeb8's eventTime <= Trigger Time 5301 // Expected: no match 5302 Trigger trigger3NoMatchingSource = 5303 TriggerFixture.getValidTriggerBuilder() 5304 .setTriggerTime(150) 5305 .setEnrollmentId(enrollmentId) 5306 .setAttributionDestination(appDestination) 5307 .setDestinationType(EventSurfaceType.APP) 5308 .build(); 5309 Optional<Source> result3 = runFunc.apply(trigger3NoMatchingSource); 5310 assertFalse(result3.isPresent()); 5311 } 5312 5313 @Test testInsertAggregateEncryptionKey()5314 public void testInsertAggregateEncryptionKey() { 5315 String keyId = "38b1d571-f924-4dc0-abe1-e2bac9b6a6be"; 5316 String publicKey = "/amqBgfDOvHAIuatDyoHxhfHaMoYA4BDxZxwtWBRQhc="; 5317 long expiry = 1653620135831L; 5318 Uri aggregationOrigin = WebUtil.validUri("https://a.test"); 5319 5320 mDatastoreManager.runInTransaction( 5321 (dao) -> 5322 dao.insertAggregateEncryptionKey( 5323 new AggregateEncryptionKey.Builder() 5324 .setKeyId(keyId) 5325 .setPublicKey(publicKey) 5326 .setExpiry(expiry) 5327 .setAggregationCoordinatorOrigin(aggregationOrigin) 5328 .build())); 5329 5330 try (Cursor cursor = 5331 MeasurementDbHelper.getInstance() 5332 .getReadableDatabase() 5333 .query( 5334 MeasurementTables.AggregateEncryptionKey.TABLE, 5335 null, 5336 null, 5337 null, 5338 null, 5339 null, 5340 null)) { 5341 assertTrue(cursor.moveToNext()); 5342 AggregateEncryptionKey aggregateEncryptionKey = 5343 SqliteObjectMapper.constructAggregateEncryptionKeyFromCursor(cursor); 5344 assertNotNull(aggregateEncryptionKey); 5345 assertNotNull(aggregateEncryptionKey.getId()); 5346 assertEquals(keyId, aggregateEncryptionKey.getKeyId()); 5347 assertEquals(publicKey, aggregateEncryptionKey.getPublicKey()); 5348 assertEquals(expiry, aggregateEncryptionKey.getExpiry()); 5349 assertEquals( 5350 aggregationOrigin, aggregateEncryptionKey.getAggregationCoordinatorOrigin()); 5351 } 5352 } 5353 5354 @Test testInsertAggregateReport()5355 public void testInsertAggregateReport() { 5356 AggregateReport validAggregateReport = AggregateReportFixture.getValidAggregateReport(); 5357 mDatastoreManager.runInTransaction( 5358 (dao) -> dao.insertAggregateReport(validAggregateReport)); 5359 5360 try (Cursor cursor = 5361 MeasurementDbHelper.getInstance() 5362 .getReadableDatabase() 5363 .query( 5364 MeasurementTables.AggregateReport.TABLE, 5365 null, 5366 null, 5367 null, 5368 null, 5369 null, 5370 null)) { 5371 assertTrue(cursor.moveToNext()); 5372 AggregateReport aggregateReport = SqliteObjectMapper.constructAggregateReport(cursor); 5373 assertNotNull(aggregateReport); 5374 assertNotNull(aggregateReport.getId()); 5375 assertEquals(validAggregateReport, aggregateReport); 5376 } 5377 } 5378 5379 @Test testInsertAggregateReport_withNullSourceRegistrationTime()5380 public void testInsertAggregateReport_withNullSourceRegistrationTime() { 5381 AggregateReport.Builder builder = AggregateReportFixture.getValidAggregateReportBuilder(); 5382 builder.setSourceRegistrationTime(null); 5383 AggregateReport aggregateReportWithNullSourceRegistrationTime = builder.build(); 5384 mDatastoreManager.runInTransaction( 5385 (dao) -> dao.insertAggregateReport(aggregateReportWithNullSourceRegistrationTime)); 5386 5387 try (Cursor cursor = 5388 MeasurementDbHelper.getInstance() 5389 .getReadableDatabase() 5390 .query( 5391 MeasurementTables.AggregateReport.TABLE, 5392 null, 5393 null, 5394 null, 5395 null, 5396 null, 5397 null)) { 5398 assertTrue(cursor.moveToNext()); 5399 AggregateReport aggregateReport = SqliteObjectMapper.constructAggregateReport(cursor); 5400 assertNotNull(aggregateReport); 5401 assertNotNull(aggregateReport.getId()); 5402 assertEquals(aggregateReportWithNullSourceRegistrationTime, aggregateReport); 5403 } 5404 } 5405 5406 @Test testExistsSourcesWithSameDestination()5407 public void testExistsSourcesWithSameDestination() { 5408 long currentTime = System.currentTimeMillis(); 5409 Uri destination1 = Uri.parse("android-app://com.destination1"); 5410 Uri destination2 = Uri.parse("android-app://com.destination2"); 5411 Uri destination3 = Uri.parse("android-app://com.destination3"); 5412 Uri destination4 = Uri.parse("android-app://com.destination4"); 5413 Uri destination5 = Uri.parse("android-app://com.destination5"); 5414 5415 // Return true. 5416 Source s1 = 5417 SourceFixture.getMinimalValidSourceBuilder() 5418 .setId("S1") 5419 .setStatus(0) 5420 .setExpiryTime(currentTime + 1000L) 5421 .setAppDestinations(List.of(destination1)) 5422 .build(); 5423 5424 // Inactive source. Return false. 5425 Source s2 = 5426 SourceFixture.getMinimalValidSourceBuilder() 5427 .setId("S1") 5428 .setStatus(1) 5429 .setExpiryTime(currentTime + 1000L) 5430 .setAppDestinations(List.of(destination2)) 5431 .build(); 5432 5433 // Expired source. Return false. 5434 Source s3 = 5435 SourceFixture.getMinimalValidSourceBuilder() 5436 .setId("S3") 5437 .setStatus(0) 5438 .setExpiryTime(currentTime - 1000L) 5439 .setAppDestinations(List.of(destination3)) 5440 .build(); 5441 5442 // Web source. Return false. 5443 Source s4 = 5444 SourceFixture.getMinimalValidSourceBuilder() 5445 .setId("S4") 5446 .setStatus(0) 5447 .setExpiryTime(currentTime + 1000L) 5448 .setWebDestinations(List.of(destination4)) 5449 .build(); 5450 5451 insertSource(s1, "S1"); 5452 insertSource(s2, "S2"); 5453 insertSource(s3, "S3"); 5454 insertSource(s4, "S4"); 5455 5456 Optional<Boolean> result1 = 5457 mDatastoreManager.runInTransactionWithResult( 5458 (dao) -> dao.existsActiveSourcesWithDestination(destination1, currentTime)); 5459 5460 Optional<Boolean> result2 = 5461 mDatastoreManager.runInTransactionWithResult( 5462 (dao) -> dao.existsActiveSourcesWithDestination(destination2, currentTime)); 5463 5464 Optional<Boolean> result3 = 5465 mDatastoreManager.runInTransactionWithResult( 5466 (dao) -> dao.existsActiveSourcesWithDestination(destination3, currentTime)); 5467 5468 Optional<Boolean> result4 = 5469 mDatastoreManager.runInTransactionWithResult( 5470 (dao) -> dao.existsActiveSourcesWithDestination(destination4, currentTime)); 5471 5472 // No source with app destination 5. Return false. 5473 Optional<Boolean> result5 = 5474 mDatastoreManager.runInTransactionWithResult( 5475 (dao) -> dao.existsActiveSourcesWithDestination(destination5, currentTime)); 5476 5477 assertThat(result1.get()).isTrue(); 5478 assertThat(result2.get()).isFalse(); 5479 assertThat(result3.get()).isFalse(); 5480 assertThat(result4.get()).isFalse(); 5481 assertThat(result5.get()).isFalse(); 5482 } 5483 5484 @Test testInsertAggregateReport_withTriggerTime()5485 public void testInsertAggregateReport_withTriggerTime() { 5486 AggregateReport.Builder builder = AggregateReportFixture.getValidAggregateReportBuilder(); 5487 builder.setTriggerTime(1L); 5488 AggregateReport aggregateReportWithTriggerTime = builder.build(); 5489 mDatastoreManager.runInTransaction( 5490 (dao) -> dao.insertAggregateReport(aggregateReportWithTriggerTime)); 5491 5492 try (Cursor cursor = 5493 MeasurementDbHelper.getInstance() 5494 .getReadableDatabase() 5495 .query( 5496 MeasurementTables.AggregateReport.TABLE, 5497 null, 5498 null, 5499 null, 5500 null, 5501 null, 5502 null)) { 5503 assertTrue(cursor.moveToNext()); 5504 AggregateReport aggregateReport = SqliteObjectMapper.constructAggregateReport(cursor); 5505 assertNotNull(aggregateReport); 5506 assertNotNull(aggregateReport.getId()); 5507 assertEquals(aggregateReportWithTriggerTime, aggregateReport); 5508 } 5509 } 5510 5511 @Test testInsertAggregateReport_withAggregatableFilteringIdMaxBytes()5512 public void testInsertAggregateReport_withAggregatableFilteringIdMaxBytes() { 5513 AggregateReport.Builder builder = AggregateReportFixture.getValidAggregateReportBuilder(); 5514 builder.setAggregatableFilteringIdMaxBytes(2); 5515 AggregateReport aggregateReportWithAggregatableFilteringIdMaxBytes = builder.build(); 5516 mDatastoreManager.runInTransaction( 5517 (dao) -> 5518 dao.insertAggregateReport( 5519 aggregateReportWithAggregatableFilteringIdMaxBytes)); 5520 5521 try (Cursor cursor = 5522 MeasurementDbHelper.getInstance() 5523 .getReadableDatabase() 5524 .query( 5525 MeasurementTables.AggregateReport.TABLE, 5526 null, 5527 null, 5528 null, 5529 null, 5530 null, 5531 null)) { 5532 assertTrue(cursor.moveToNext()); 5533 AggregateReport aggregateReport = SqliteObjectMapper.constructAggregateReport(cursor); 5534 assertNotNull(aggregateReport); 5535 assertNotNull(aggregateReport.getId()); 5536 assertEquals(aggregateReportWithAggregatableFilteringIdMaxBytes, aggregateReport); 5537 } 5538 } 5539 5540 @Test testDeleteAllMeasurementDataWithEmptyList()5541 public void testDeleteAllMeasurementDataWithEmptyList() { 5542 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 5543 5544 Source source = SourceFixture.getMinimalValidSourceBuilder().setId("S1").build(); 5545 ContentValues sourceValue = new ContentValues(); 5546 sourceValue.put("_id", source.getId()); 5547 db.insert(SourceContract.TABLE, null, sourceValue); 5548 5549 Trigger trigger = TriggerFixture.getValidTriggerBuilder().setId("T1").build(); 5550 ContentValues triggerValue = new ContentValues(); 5551 triggerValue.put("_id", trigger.getId()); 5552 db.insert(TriggerContract.TABLE, null, triggerValue); 5553 5554 EventReport eventReport = new EventReport.Builder().setId("E1").build(); 5555 ContentValues eventReportValue = new ContentValues(); 5556 eventReportValue.put("_id", eventReport.getId()); 5557 db.insert(EventReportContract.TABLE, null, eventReportValue); 5558 5559 AggregateReport aggregateReport = new AggregateReport.Builder().setId("A1").build(); 5560 ContentValues aggregateReportValue = new ContentValues(); 5561 aggregateReportValue.put("_id", aggregateReport.getId()); 5562 db.insert(MeasurementTables.AggregateReport.TABLE, null, aggregateReportValue); 5563 5564 ContentValues rateLimitValue = new ContentValues(); 5565 rateLimitValue.put(AttributionContract.ID, "ARL1"); 5566 rateLimitValue.put(AttributionContract.SOURCE_SITE, "sourceSite"); 5567 rateLimitValue.put(AttributionContract.SOURCE_ORIGIN, "sourceOrigin"); 5568 rateLimitValue.put(AttributionContract.DESTINATION_SITE, "destinationSite"); 5569 rateLimitValue.put(AttributionContract.TRIGGER_TIME, 5L); 5570 rateLimitValue.put(AttributionContract.REGISTRANT, "registrant"); 5571 rateLimitValue.put(AttributionContract.ENROLLMENT_ID, "enrollmentId"); 5572 5573 db.insert(AttributionContract.TABLE, null, rateLimitValue); 5574 5575 AggregateEncryptionKey key = 5576 new AggregateEncryptionKey.Builder() 5577 .setId("K1") 5578 .setKeyId("keyId") 5579 .setPublicKey("publicKey") 5580 .setExpiry(1) 5581 .setAggregationCoordinatorOrigin(Uri.parse("https://1.test")) 5582 .build(); 5583 ContentValues keyValues = new ContentValues(); 5584 keyValues.put("_id", key.getId()); 5585 5586 mDatastoreManager.runInTransaction( 5587 (dao) -> dao.deleteAllMeasurementData(Collections.emptyList())); 5588 5589 for (String table : ALL_MSMT_TABLES) { 5590 assertThat( 5591 db.query( 5592 /* table */ table, 5593 /* columns */ null, 5594 /* selection */ null, 5595 /* selectionArgs */ null, 5596 /* groupBy */ null, 5597 /* having */ null, 5598 /* orderedBy */ null) 5599 .getCount()) 5600 .isEqualTo(0); 5601 } 5602 } 5603 5604 @Test testDeleteAllMeasurementDataWithNonEmptyList()5605 public void testDeleteAllMeasurementDataWithNonEmptyList() { 5606 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 5607 5608 Source source = SourceFixture.getMinimalValidSourceBuilder().setId("S1").build(); 5609 ContentValues sourceValue = new ContentValues(); 5610 sourceValue.put("_id", source.getId()); 5611 db.insert(SourceContract.TABLE, null, sourceValue); 5612 5613 Trigger trigger = TriggerFixture.getValidTriggerBuilder().setId("T1").build(); 5614 ContentValues triggerValue = new ContentValues(); 5615 triggerValue.put("_id", trigger.getId()); 5616 db.insert(TriggerContract.TABLE, null, triggerValue); 5617 5618 EventReport eventReport = new EventReport.Builder().setId("E1").build(); 5619 ContentValues eventReportValue = new ContentValues(); 5620 eventReportValue.put("_id", eventReport.getId()); 5621 db.insert(EventReportContract.TABLE, null, eventReportValue); 5622 5623 AggregateReport aggregateReport = new AggregateReport.Builder().setId("A1").build(); 5624 ContentValues aggregateReportValue = new ContentValues(); 5625 aggregateReportValue.put("_id", aggregateReport.getId()); 5626 db.insert(MeasurementTables.AggregateReport.TABLE, null, aggregateReportValue); 5627 5628 ContentValues rateLimitValue = new ContentValues(); 5629 rateLimitValue.put(AttributionContract.ID, "ARL1"); 5630 rateLimitValue.put(AttributionContract.SOURCE_SITE, "sourceSite"); 5631 rateLimitValue.put(AttributionContract.SOURCE_ORIGIN, "sourceOrigin"); 5632 rateLimitValue.put(AttributionContract.DESTINATION_SITE, "destinationSite"); 5633 rateLimitValue.put(AttributionContract.TRIGGER_TIME, 5L); 5634 rateLimitValue.put(AttributionContract.REGISTRANT, "registrant"); 5635 rateLimitValue.put(AttributionContract.ENROLLMENT_ID, "enrollmentId"); 5636 db.insert(AttributionContract.TABLE, null, rateLimitValue); 5637 5638 AggregateEncryptionKey key = 5639 new AggregateEncryptionKey.Builder() 5640 .setId("K1") 5641 .setKeyId("keyId") 5642 .setPublicKey("publicKey") 5643 .setExpiry(1) 5644 .setAggregationCoordinatorOrigin(Uri.parse("https://1.test")) 5645 .build(); 5646 ContentValues keyValues = new ContentValues(); 5647 keyValues.put("_id", key.getId()); 5648 5649 List<String> excludedTables = List.of(SourceContract.TABLE); 5650 5651 mDatastoreManager.runInTransaction((dao) -> dao.deleteAllMeasurementData(excludedTables)); 5652 5653 for (String table : ALL_MSMT_TABLES) { 5654 if (!excludedTables.contains(table)) { 5655 assertThat( 5656 db.query( 5657 /* table */ table, 5658 /* columns */ null, 5659 /* selection */ null, 5660 /* selectionArgs */ null, 5661 /* groupBy */ null, 5662 /* having */ null, 5663 /* orderedBy */ null) 5664 .getCount()) 5665 .isEqualTo(0); 5666 } else { 5667 assertThat( 5668 db.query( 5669 /* table */ table, 5670 /* columns */ null, 5671 /* selection */ null, 5672 /* selectionArgs */ null, 5673 /* groupBy */ null, 5674 /* having */ null, 5675 /* orderedBy */ null) 5676 .getCount()) 5677 .isNotEqualTo(0); 5678 } 5679 } 5680 } 5681 5682 /** Test that the variable ALL_MSMT_TABLES actually has all the measurement related tables. */ 5683 @Test testAllMsmtTables()5684 public void testAllMsmtTables() { 5685 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 5686 Cursor cursor = 5687 db.query( 5688 "sqlite_master", 5689 /* columns */ null, 5690 /* selection */ "type = ? AND name like ?", 5691 /* selectionArgs*/ new String[] {"table", MSMT_TABLE_PREFIX + "%"}, 5692 /* groupBy */ null, 5693 /* having */ null, 5694 /* orderBy */ null); 5695 5696 List<String> tableNames = new ArrayList<>(); 5697 while (cursor.moveToNext()) { 5698 String tableName = cursor.getString(cursor.getColumnIndex("name")); 5699 tableNames.add(tableName); 5700 } 5701 assertThat(tableNames.size()).isEqualTo(ALL_MSMT_TABLES.length); 5702 for (String tableName : tableNames) { 5703 assertThat(ALL_MSMT_TABLES).asList().contains(tableName); 5704 } 5705 } 5706 5707 @Test insertAttributionRateLimit()5708 public void insertAttributionRateLimit() { 5709 // Setup 5710 Source source = SourceFixture.getValidSource(); 5711 Trigger trigger = 5712 TriggerFixture.getValidTriggerBuilder() 5713 .setTriggerTime(source.getEventTime() + TimeUnit.HOURS.toMillis(1)) 5714 .build(); 5715 Attribution attribution = 5716 new Attribution.Builder() 5717 .setScope(Attribution.Scope.AGGREGATE) 5718 .setEnrollmentId(source.getEnrollmentId()) 5719 .setDestinationOrigin(source.getWebDestinations().get(0).toString()) 5720 .setDestinationSite(source.getAppDestinations().get(0).toString()) 5721 .setSourceOrigin(source.getPublisher().toString()) 5722 .setSourceSite(source.getPublisher().toString()) 5723 .setRegistrant(source.getRegistrant().toString()) 5724 .setTriggerTime(trigger.getTriggerTime()) 5725 .setRegistrationOrigin(trigger.getRegistrationOrigin()) 5726 .setReportId(UUID.randomUUID().toString()) 5727 .build(); 5728 5729 // Execution 5730 mDatastoreManager.runInTransaction( 5731 (dao) -> { 5732 dao.insertAttribution(attribution); 5733 }); 5734 5735 // Assertion 5736 try (Cursor cursor = 5737 MeasurementDbHelper.getInstance() 5738 .getReadableDatabase() 5739 .query(AttributionContract.TABLE, null, null, null, null, null, null)) { 5740 assertTrue(cursor.moveToNext()); 5741 assertEquals( 5742 attribution.getScope(), 5743 cursor.getInt(cursor.getColumnIndex(AttributionContract.SCOPE))); 5744 assertEquals( 5745 attribution.getEnrollmentId(), 5746 cursor.getString(cursor.getColumnIndex(AttributionContract.ENROLLMENT_ID))); 5747 assertEquals( 5748 attribution.getDestinationOrigin(), 5749 cursor.getString( 5750 cursor.getColumnIndex(AttributionContract.DESTINATION_ORIGIN))); 5751 assertEquals( 5752 attribution.getDestinationSite(), 5753 cursor.getString(cursor.getColumnIndex(AttributionContract.DESTINATION_SITE))); 5754 assertEquals( 5755 attribution.getSourceOrigin(), 5756 cursor.getString(cursor.getColumnIndex(AttributionContract.SOURCE_ORIGIN))); 5757 assertEquals( 5758 attribution.getSourceSite(), 5759 cursor.getString(cursor.getColumnIndex(AttributionContract.SOURCE_SITE))); 5760 assertEquals( 5761 attribution.getRegistrant(), 5762 cursor.getString(cursor.getColumnIndex(AttributionContract.REGISTRANT))); 5763 assertEquals( 5764 attribution.getTriggerTime(), 5765 cursor.getLong(cursor.getColumnIndex(AttributionContract.TRIGGER_TIME))); 5766 assertEquals( 5767 attribution.getRegistrationOrigin(), 5768 Uri.parse( 5769 cursor.getString( 5770 cursor.getColumnIndex( 5771 AttributionContract.REGISTRATION_ORIGIN)))); 5772 assertEquals( 5773 attribution.getReportId(), 5774 cursor.getString(cursor.getColumnIndex(AttributionContract.REPORT_ID))); 5775 } 5776 } 5777 5778 @Test getAttributionsPerRateLimitWindow_atTimeWindowScoped_countsAttribution()5779 public void getAttributionsPerRateLimitWindow_atTimeWindowScoped_countsAttribution() { 5780 // Setup 5781 Source source = SourceFixture.getValidSource(); 5782 Trigger trigger = 5783 TriggerFixture.getValidTriggerBuilder() 5784 .setTriggerTime(source.getEventTime() + TimeUnit.HOURS.toMillis(1)) 5785 .build(); 5786 5787 Attribution eventAttribution = 5788 getAttributionBuilder(source, trigger).setScope(Attribution.Scope.EVENT).build(); 5789 5790 Attribution aggregateAttribution = 5791 getAttributionBuilder(source, trigger) 5792 .setScope(Attribution.Scope.AGGREGATE) 5793 .build(); 5794 5795 // Execution 5796 mDatastoreManager.runInTransaction( 5797 (dao) -> { 5798 dao.insertAttribution(eventAttribution); 5799 dao.insertAttribution(aggregateAttribution); 5800 }); 5801 5802 // Assertion 5803 AtomicLong eventAttributionsCount = new AtomicLong(); 5804 AtomicLong aggregateAttributionsCount = new AtomicLong(); 5805 mDatastoreManager.runInTransaction( 5806 (dao) -> { 5807 eventAttributionsCount.set( 5808 dao.getAttributionsPerRateLimitWindow( 5809 Attribution.Scope.EVENT, source, trigger)); 5810 aggregateAttributionsCount.set( 5811 dao.getAttributionsPerRateLimitWindow( 5812 Attribution.Scope.AGGREGATE, source, trigger)); 5813 }); 5814 5815 assertEquals(1L, eventAttributionsCount.get()); 5816 assertEquals(1L, aggregateAttributionsCount.get()); 5817 } 5818 5819 @Test getAttributionsPerRateLimitWindow_beyondTimeWindowScoped_countsAttribution()5820 public void getAttributionsPerRateLimitWindow_beyondTimeWindowScoped_countsAttribution() { 5821 // Setup 5822 Source source = SourceFixture.getValidSource(); 5823 Trigger trigger = 5824 TriggerFixture.getValidTriggerBuilder() 5825 .setTriggerTime(source.getEventTime() + TimeUnit.HOURS.toMillis(1)) 5826 .build(); 5827 5828 Attribution eventAttribution = 5829 getAttributionBuilder(source, trigger) 5830 .setTriggerTime( 5831 trigger.getTriggerTime() 5832 - MEASUREMENT_RATE_LIMIT_WINDOW_MILLISECONDS) 5833 .setScope(Attribution.Scope.EVENT) 5834 .build(); 5835 5836 Attribution aggregateAttribution = 5837 getAttributionBuilder(source, trigger) 5838 .setTriggerTime( 5839 trigger.getTriggerTime() 5840 - MEASUREMENT_RATE_LIMIT_WINDOW_MILLISECONDS) 5841 .setScope(Attribution.Scope.AGGREGATE) 5842 .build(); 5843 5844 // Execution 5845 mDatastoreManager.runInTransaction( 5846 (dao) -> { 5847 dao.insertAttribution(eventAttribution); 5848 dao.insertAttribution(aggregateAttribution); 5849 }); 5850 5851 // Assertion 5852 AtomicLong eventAttributionsCount = new AtomicLong(); 5853 AtomicLong aggregateAttributionsCount = new AtomicLong(); 5854 mDatastoreManager.runInTransaction( 5855 (dao) -> { 5856 eventAttributionsCount.set( 5857 dao.getAttributionsPerRateLimitWindow( 5858 Attribution.Scope.EVENT, source, trigger)); 5859 aggregateAttributionsCount.set( 5860 dao.getAttributionsPerRateLimitWindow( 5861 Attribution.Scope.AGGREGATE, source, trigger)); 5862 }); 5863 5864 assertEquals(0L, eventAttributionsCount.get()); 5865 assertEquals(0L, aggregateAttributionsCount.get()); 5866 } 5867 5868 @Test testTransactionRollbackForRuntimeException()5869 public void testTransactionRollbackForRuntimeException() { 5870 assertThrows( 5871 IllegalArgumentException.class, 5872 () -> 5873 mDatastoreManager.runInTransaction( 5874 (dao) -> { 5875 dao.insertSource(SourceFixture.getValidSource()); 5876 // build() call throws IllegalArgumentException 5877 Trigger trigger = new Trigger.Builder().build(); 5878 dao.insertTrigger(trigger); 5879 })); 5880 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 5881 Objects.requireNonNull(db); 5882 // There should be no insertions 5883 assertEquals( 5884 0, db.query(SourceContract.TABLE, null, null, null, null, null, null).getCount()); 5885 assertEquals( 5886 0, db.query(TriggerContract.TABLE, null, null, null, null, null, null).getCount()); 5887 } 5888 5889 @Test testDeleteEventReportAndAttribution()5890 public void testDeleteEventReportAndAttribution() throws JSONException { 5891 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 5892 Source s1 = 5893 SourceFixture.getMinimalValidSourceBuilder() 5894 .setEventId(new UnsignedLong(1L)) 5895 .setId("S1") 5896 .build(); 5897 Trigger t1 = 5898 TriggerFixture.getValidTriggerBuilder() 5899 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 5900 .setId("T1") 5901 .build(); 5902 Trigger t2 = 5903 TriggerFixture.getValidTriggerBuilder() 5904 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 5905 .setId("T2") 5906 .build(); 5907 EventReport e11 = createEventReportForSourceAndTrigger("E11", s1, t1); 5908 EventReport e12 = createEventReportForSourceAndTrigger("E12", s1, t2); 5909 5910 Attribution aggregateAttribution11 = 5911 createAttribution( 5912 "ATT11_aggregate", 5913 Attribution.Scope.AGGREGATE, 5914 s1.getId(), 5915 t1.getId(), 5916 "E11"); 5917 Attribution aggregateAttribution12 = 5918 createAttribution( 5919 "ATT12_aggregate", 5920 Attribution.Scope.AGGREGATE, 5921 s1.getId(), 5922 t2.getId(), 5923 "E12"); 5924 Attribution eventAttribution11 = 5925 createAttribution( 5926 "ATT11_event", 5927 Attribution.Scope.EVENT, 5928 s1.getId(), 5929 t1.getId(), 5930 "E11"); 5931 Attribution eventAttribution12 = 5932 createAttribution( 5933 "ATT12_event", 5934 Attribution.Scope.EVENT, 5935 s1.getId(), 5936 t2.getId(), 5937 "E12"); 5938 5939 insertSource(s1, s1.getId()); 5940 AbstractDbIntegrationTest.insertToDb(t1, db); 5941 AbstractDbIntegrationTest.insertToDb(t2, db); 5942 AbstractDbIntegrationTest.insertToDb(e11, db); 5943 AbstractDbIntegrationTest.insertToDb(e12, db); 5944 AbstractDbIntegrationTest.insertToDb(aggregateAttribution11, db); 5945 AbstractDbIntegrationTest.insertToDb(aggregateAttribution12, db); 5946 AbstractDbIntegrationTest.insertToDb(eventAttribution11, db); 5947 AbstractDbIntegrationTest.insertToDb(eventAttribution12, db); 5948 5949 // Assert attributions present 5950 assertNotNull(getAttribution("ATT11_aggregate", db)); 5951 assertNotNull(getAttribution("ATT12_aggregate", db)); 5952 assertNotNull(getAttribution("ATT11_event", db)); 5953 assertNotNull(getAttribution("ATT12_event", db)); 5954 5955 mDatastoreManager.runInTransaction( 5956 measurementDao -> { 5957 // Assert sources and triggers present 5958 assertNotNull(measurementDao.getSource("S1")); 5959 assertNotNull(measurementDao.getTrigger("T1")); 5960 assertNotNull(measurementDao.getTrigger("T2")); 5961 5962 // Validate presence of event reports 5963 measurementDao.getEventReport("E11"); 5964 measurementDao.getEventReport("E12"); 5965 5966 // Deletion 5967 measurementDao.deleteEventReportAndAttribution(e11); 5968 5969 // Validate event report deletion 5970 assertThrows( 5971 DatastoreException.class, 5972 () -> { 5973 measurementDao.getEventReport("E11"); 5974 }); 5975 assertNotNull(measurementDao.getEventReport("E12")); 5976 5977 // Validate sources and triggers present 5978 assertNotNull(measurementDao.getSource("S1")); 5979 assertNotNull(measurementDao.getTrigger("T1")); 5980 assertNotNull(measurementDao.getTrigger("T2")); 5981 }); 5982 5983 // Validate attribution deletion 5984 assertNotNull(getAttribution("ATT11_aggregate", db)); 5985 assertNotNull(getAttribution("ATT12_aggregate", db)); 5986 assertNull(getAttribution("ATT11_event", db)); 5987 assertNotNull(getAttribution("ATT12_event", db)); 5988 } 5989 5990 @Test testDeleteDebugReport()5991 public void testDeleteDebugReport() { 5992 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 5993 DebugReport debugReport = createDebugReport(); 5994 5995 ContentValues values = new ContentValues(); 5996 values.put(DebugReportContract.ID, debugReport.getId()); 5997 values.put(DebugReportContract.TYPE, debugReport.getType()); 5998 values.put(DebugReportContract.BODY, debugReport.getBody().toString()); 5999 values.put(DebugReportContract.ENROLLMENT_ID, debugReport.getEnrollmentId()); 6000 values.put( 6001 DebugReportContract.REGISTRATION_ORIGIN, 6002 debugReport.getRegistrationOrigin().toString()); 6003 db.insert(DebugReportContract.TABLE, null, values); 6004 6005 long count = 6006 DatabaseUtils.queryNumEntries(db, DebugReportContract.TABLE, /* selection */ null); 6007 assertEquals(1, count); 6008 6009 assertTrue( 6010 mDatastoreManager.runInTransaction( 6011 measurementDao -> measurementDao.deleteDebugReport(debugReport.getId()))); 6012 6013 count = DatabaseUtils.queryNumEntries(db, DebugReportContract.TABLE, /* selection */ null); 6014 assertEquals(0, count); 6015 } 6016 6017 @Test testDeleteDebugReports_byRegistrantAndRange()6018 public void testDeleteDebugReports_byRegistrantAndRange() { 6019 final String registrantMatching = "foo"; 6020 final long insertionTimeWithinRange = 1701206853050L; 6021 final long insertionTimeNotWithinRange = 1701206853000L; 6022 DebugReport debugReportMatchingRegistrantAndWithinRange = 6023 createDebugReport( 6024 /* id= */ "1", 6025 buildRegistrant(registrantMatching), 6026 insertionTimeWithinRange); 6027 6028 DebugReport debugReportNotMatchingRegistrantAndWithinRange = 6029 createDebugReport(/* id= */ "2", buildRegistrant("bar"), insertionTimeWithinRange); 6030 6031 DebugReport debugReportMatchingRegistrantAndNotWithinRange = 6032 createDebugReport( 6033 /* id= */ "3", 6034 buildRegistrant(registrantMatching), 6035 insertionTimeNotWithinRange); 6036 6037 mDatastoreManager.runInTransaction( 6038 (dao) -> { 6039 dao.insertDebugReport(debugReportMatchingRegistrantAndWithinRange); 6040 dao.insertDebugReport(debugReportNotMatchingRegistrantAndWithinRange); 6041 dao.insertDebugReport(debugReportMatchingRegistrantAndNotWithinRange); 6042 }); 6043 6044 mDatastoreManager.runInTransaction( 6045 (dao) -> 6046 dao.deleteDebugReports( 6047 buildRegistrant(registrantMatching), 6048 /* start= */ Instant.ofEpochMilli(insertionTimeWithinRange - 1), 6049 /* end= */ Instant.ofEpochMilli(insertionTimeWithinRange + 1))); 6050 6051 Set<String> ids = new HashSet<>(); 6052 try (Cursor cursor = 6053 MeasurementDbHelper.getInstance() 6054 .getReadableDatabase() 6055 .query(DebugReportContract.TABLE, null, null, null, null, null, null)) { 6056 while (cursor.moveToNext()) { 6057 ids.add(cursor.getString(cursor.getColumnIndexOrThrow(DebugReportContract.ID))); 6058 } 6059 } 6060 assertEquals(2, ids.size()); 6061 assertTrue(ids.contains("2")); 6062 assertTrue(ids.contains("3")); 6063 } 6064 6065 @Test testGetDebugReportIds()6066 public void testGetDebugReportIds() { 6067 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 6068 DebugReport debugReport = createDebugReport(); 6069 6070 ContentValues values = new ContentValues(); 6071 values.put(DebugReportContract.ID, debugReport.getId()); 6072 values.put(DebugReportContract.TYPE, debugReport.getType()); 6073 values.put(DebugReportContract.BODY, debugReport.getBody().toString()); 6074 values.put(DebugReportContract.ENROLLMENT_ID, debugReport.getEnrollmentId()); 6075 values.put( 6076 DebugReportContract.REGISTRATION_ORIGIN, 6077 debugReport.getRegistrationOrigin().toString()); 6078 db.insert(DebugReportContract.TABLE, null, values); 6079 6080 long count = 6081 DatabaseUtils.queryNumEntries(db, DebugReportContract.TABLE, /* selection */ null); 6082 assertEquals(1, count); 6083 6084 assertTrue( 6085 mDatastoreManager.runInTransaction( 6086 measurementDao -> 6087 assertEquals( 6088 List.of(debugReport.getId()), 6089 measurementDao.getDebugReportIds()))); 6090 } 6091 6092 @Test testGetDebugReportIdsWithRetryLimit()6093 public void testGetDebugReportIdsWithRetryLimit() { 6094 // Mocking that the flags return a Max Retry of 1 6095 Flags mockFlags = Mockito.mock(Flags.class); 6096 ExtendedMockito.doReturn(mockFlags).when(FlagsFactory::getFlags); 6097 ExtendedMockito.doReturn(1).when(mockFlags).getMeasurementReportingRetryLimit(); 6098 ExtendedMockito.doReturn(true).when(mockFlags).getMeasurementReportingRetryLimitEnabled(); 6099 6100 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 6101 DebugReport debugReport = createDebugReport(); 6102 6103 ContentValues values = new ContentValues(); 6104 values.put(DebugReportContract.ID, debugReport.getId()); 6105 values.put(DebugReportContract.TYPE, debugReport.getType()); 6106 values.put(DebugReportContract.BODY, debugReport.getBody().toString()); 6107 values.put(DebugReportContract.ENROLLMENT_ID, debugReport.getEnrollmentId()); 6108 values.put( 6109 DebugReportContract.REGISTRATION_ORIGIN, 6110 debugReport.getRegistrationOrigin().toString()); 6111 db.insert(DebugReportContract.TABLE, null, values); 6112 6113 long count = 6114 DatabaseUtils.queryNumEntries(db, DebugReportContract.TABLE, /* selection */ null); 6115 assertEquals(1, count); 6116 6117 assertTrue( 6118 mDatastoreManager.runInTransaction( 6119 measurementDao -> 6120 assertEquals( 6121 List.of(debugReport.getId()), 6122 measurementDao.getDebugReportIds()))); 6123 assertTrue( 6124 mDatastoreManager.runInTransaction( 6125 measurementDao -> { 6126 // Adds records to KeyValueData table for Retry Count. 6127 measurementDao.incrementAndGetReportingRetryCount( 6128 debugReport.getId(), DataType.DEBUG_REPORT_RETRY_COUNT); 6129 assertTrue(measurementDao.getDebugReportIds().isEmpty()); 6130 })); 6131 } 6132 6133 @Test testDeleteExpiredRecordsForAsyncRegistrations()6134 public void testDeleteExpiredRecordsForAsyncRegistrations() { 6135 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 6136 6137 List<AsyncRegistration> asyncRegistrationList = new ArrayList<>(); 6138 int retryLimit = Flags.MEASUREMENT_MAX_RETRIES_PER_REGISTRATION_REQUEST; 6139 6140 // Will be deleted by request time 6141 asyncRegistrationList.add( 6142 new AsyncRegistration.Builder() 6143 .setId("1") 6144 .setOsDestination(Uri.parse("android-app://installed-app-destination")) 6145 .setRegistrant(INSTALLED_REGISTRANT) 6146 .setTopOrigin(INSTALLED_REGISTRANT) 6147 .setAdIdPermission(false) 6148 .setType(AsyncRegistration.RegistrationType.APP_SOURCE) 6149 .setRequestTime(1) 6150 .setRetryCount(retryLimit - 1L) 6151 .setRegistrationId(UUID.randomUUID().toString()) 6152 .build()); 6153 6154 // Will be deleted by either request time or retry limit 6155 asyncRegistrationList.add( 6156 new AsyncRegistration.Builder() 6157 .setId("2") 6158 .setOsDestination(Uri.parse("android-app://installed-app-destination")) 6159 .setRegistrant(INSTALLED_REGISTRANT) 6160 .setTopOrigin(INSTALLED_REGISTRANT) 6161 .setAdIdPermission(false) 6162 .setType(AsyncRegistration.RegistrationType.APP_SOURCE) 6163 .setRequestTime(1) 6164 .setRetryCount(retryLimit) 6165 .setRegistrationId(UUID.randomUUID().toString()) 6166 .build()); 6167 6168 // Will not be deleted 6169 asyncRegistrationList.add( 6170 new AsyncRegistration.Builder() 6171 .setId("3") 6172 .setOsDestination(Uri.parse("android-app://not-installed-app-destination")) 6173 .setRegistrant(INSTALLED_REGISTRANT) 6174 .setTopOrigin(INSTALLED_REGISTRANT) 6175 .setAdIdPermission(false) 6176 .setType(AsyncRegistration.RegistrationType.APP_SOURCE) 6177 .setRequestTime(Long.MAX_VALUE) 6178 .setRetryCount(retryLimit - 1L) 6179 .setRegistrationId(UUID.randomUUID().toString()) 6180 .build()); 6181 6182 // Will be deleted due to retry limit 6183 asyncRegistrationList.add( 6184 new AsyncRegistration.Builder() 6185 .setId("4") 6186 .setOsDestination(Uri.parse("android-app://not-installed-app-destination")) 6187 .setRegistrant(INSTALLED_REGISTRANT) 6188 .setTopOrigin(INSTALLED_REGISTRANT) 6189 .setAdIdPermission(false) 6190 .setType(AsyncRegistration.RegistrationType.APP_SOURCE) 6191 .setRequestTime(Long.MAX_VALUE) 6192 .setRetryCount(retryLimit) 6193 .setRegistrationId(UUID.randomUUID().toString()) 6194 .build()); 6195 6196 asyncRegistrationList.forEach( 6197 asyncRegistration -> { 6198 ContentValues values = new ContentValues(); 6199 values.put(AsyncRegistrationContract.ID, asyncRegistration.getId()); 6200 values.put( 6201 AsyncRegistrationContract.REGISTRANT, 6202 asyncRegistration.getRegistrant().toString()); 6203 values.put( 6204 AsyncRegistrationContract.TOP_ORIGIN, 6205 asyncRegistration.getTopOrigin().toString()); 6206 values.put( 6207 AsyncRegistrationContract.OS_DESTINATION, 6208 asyncRegistration.getOsDestination().toString()); 6209 values.put( 6210 AsyncRegistrationContract.AD_ID_PERMISSION, 6211 asyncRegistration.getDebugKeyAllowed()); 6212 values.put( 6213 AsyncRegistrationContract.TYPE, asyncRegistration.getType().toString()); 6214 values.put( 6215 AsyncRegistrationContract.REQUEST_TIME, 6216 asyncRegistration.getRequestTime()); 6217 values.put( 6218 AsyncRegistrationContract.RETRY_COUNT, 6219 asyncRegistration.getRetryCount()); 6220 values.put( 6221 AsyncRegistrationContract.REGISTRATION_ID, 6222 asyncRegistration.getRegistrationId()); 6223 db.insert(AsyncRegistrationContract.TABLE, /* nullColumnHack */ null, values); 6224 }); 6225 6226 long count = 6227 DatabaseUtils.queryNumEntries( 6228 db, AsyncRegistrationContract.TABLE, /* selection */ null); 6229 assertEquals(4, count); 6230 6231 long earliestValidInsertion = System.currentTimeMillis() - 2; 6232 6233 assertTrue( 6234 mDatastoreManager.runInTransaction( 6235 measurementDao -> 6236 measurementDao.deleteExpiredRecords( 6237 earliestValidInsertion, 6238 retryLimit, 6239 /* earliestValidAppReportInsertion */ null, 6240 /* earliestValidAggregateDebugReportInsertion */ 0))); 6241 6242 count = 6243 DatabaseUtils.queryNumEntries( 6244 db, AsyncRegistrationContract.TABLE, /* selection */ null); 6245 assertEquals(1, count); 6246 6247 Cursor cursor = 6248 db.query( 6249 AsyncRegistrationContract.TABLE, 6250 /* columns */ null, 6251 /* selection */ null, 6252 /* selectionArgs */ null, 6253 /* groupBy */ null, 6254 /* having */ null, 6255 /* orderBy */ null); 6256 6257 Set<String> ids = new HashSet<>(Arrays.asList("3")); 6258 List<AsyncRegistration> asyncRegistrations = new ArrayList<>(); 6259 while (cursor.moveToNext()) { 6260 AsyncRegistration asyncRegistration = 6261 SqliteObjectMapper.constructAsyncRegistration(cursor); 6262 asyncRegistrations.add(asyncRegistration); 6263 } 6264 for (AsyncRegistration asyncRegistration : asyncRegistrations) { 6265 assertTrue(ids.contains(asyncRegistration.getId())); 6266 } 6267 } 6268 6269 @Test deleteExpiredRecords_registrationRedirectCount()6270 public void deleteExpiredRecords_registrationRedirectCount() { 6271 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 6272 List<Pair<String, String>> regIdCounts = 6273 List.of( 6274 new Pair<>("reg1", "1"), 6275 new Pair<>("reg2", "2"), 6276 new Pair<>("reg3", "3"), 6277 new Pair<>("reg4", "4")); 6278 for (Pair<String, String> regIdCount : regIdCounts) { 6279 ContentValues contentValues = new ContentValues(); 6280 contentValues.put( 6281 KeyValueDataContract.DATA_TYPE, 6282 KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT.toString()); 6283 contentValues.put(KeyValueDataContract.KEY, regIdCount.first); 6284 contentValues.put(KeyValueDataContract.VALUE, regIdCount.second); 6285 db.insert(KeyValueDataContract.TABLE, null, contentValues); 6286 } 6287 AsyncRegistration asyncRegistration1 = 6288 AsyncRegistrationFixture.getValidAsyncRegistrationBuilder() 6289 .setRegistrationId("reg1") 6290 .setRequestTime(System.currentTimeMillis() + 60000) // Avoid deletion 6291 .build(); 6292 AsyncRegistration asyncRegistration2 = 6293 AsyncRegistrationFixture.getValidAsyncRegistrationBuilder() 6294 .setRegistrationId("reg2") 6295 .setRequestTime(System.currentTimeMillis() + 60000) // Avoid deletion 6296 .build(); 6297 List<AsyncRegistration> asyncRegistrations = 6298 List.of(asyncRegistration1, asyncRegistration2); 6299 asyncRegistrations.forEach( 6300 asyncRegistration -> 6301 mDatastoreManager.runInTransaction( 6302 dao -> dao.insertAsyncRegistration(asyncRegistration))); 6303 6304 long earliestValidInsertion = System.currentTimeMillis() - 60000; 6305 int retryLimit = Flags.MEASUREMENT_MAX_RETRIES_PER_REGISTRATION_REQUEST; 6306 assertTrue( 6307 mDatastoreManager.runInTransaction( 6308 (dao) -> 6309 dao.deleteExpiredRecords( 6310 earliestValidInsertion, 6311 retryLimit, 6312 /* earliestValidAggregateDebugReportInsertion */ null, 6313 /* earliestValidAggregateDebugReportInsertion */ 0))); 6314 6315 Cursor cursor = 6316 db.query( 6317 KeyValueDataContract.TABLE, 6318 null, 6319 null, 6320 null, 6321 null, 6322 null, 6323 KeyValueDataContract.KEY); 6324 assertEquals(2, cursor.getCount()); 6325 cursor.moveToNext(); 6326 assertEquals( 6327 KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT.toString(), 6328 cursor.getString(cursor.getColumnIndex(KeyValueDataContract.DATA_TYPE))); 6329 assertEquals("reg1", cursor.getString(cursor.getColumnIndex(KeyValueDataContract.KEY))); 6330 assertEquals("1", cursor.getString(cursor.getColumnIndex(KeyValueDataContract.VALUE))); 6331 cursor.moveToNext(); 6332 assertEquals( 6333 KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT.toString(), 6334 cursor.getString(cursor.getColumnIndex(KeyValueDataContract.DATA_TYPE))); 6335 assertEquals("reg2", cursor.getString(cursor.getColumnIndex(KeyValueDataContract.KEY))); 6336 assertEquals("2", cursor.getString(cursor.getColumnIndex(KeyValueDataContract.VALUE))); 6337 cursor.close(); 6338 } 6339 6340 @Test deleteExpiredRecords_skipDeliveredEventReportsOutsideWindow()6341 public void deleteExpiredRecords_skipDeliveredEventReportsOutsideWindow() { 6342 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 6343 ContentValues sourceValid = new ContentValues(); 6344 sourceValid.put(SourceContract.ID, "s1"); 6345 sourceValid.put(SourceContract.EVENT_TIME, System.currentTimeMillis()); 6346 6347 ContentValues sourceExpired = new ContentValues(); 6348 sourceExpired.put(SourceContract.ID, "s2"); 6349 sourceExpired.put( 6350 SourceContract.EVENT_TIME, System.currentTimeMillis() - DAYS.toMillis(20)); 6351 6352 ContentValues triggerValid = new ContentValues(); 6353 triggerValid.put(TriggerContract.ID, "t1"); 6354 triggerValid.put(TriggerContract.TRIGGER_TIME, System.currentTimeMillis()); 6355 6356 ContentValues triggerExpired = new ContentValues(); 6357 triggerExpired.put(TriggerContract.ID, "t2"); 6358 triggerExpired.put( 6359 TriggerContract.TRIGGER_TIME, System.currentTimeMillis() - DAYS.toMillis(20)); 6360 6361 db.insert(SourceContract.TABLE, null, sourceValid); 6362 db.insert(SourceContract.TABLE, null, sourceExpired); 6363 db.insert(TriggerContract.TABLE, null, triggerValid); 6364 db.insert(TriggerContract.TABLE, null, triggerExpired); 6365 6366 ContentValues eventReport_NotDelivered_WithinWindow = new ContentValues(); 6367 eventReport_NotDelivered_WithinWindow.put(EventReportContract.ID, "e1"); 6368 eventReport_NotDelivered_WithinWindow.put( 6369 EventReportContract.REPORT_TIME, System.currentTimeMillis()); 6370 eventReport_NotDelivered_WithinWindow.put( 6371 EventReportContract.STATUS, EventReport.Status.PENDING); 6372 eventReport_NotDelivered_WithinWindow.put( 6373 EventReportContract.SOURCE_ID, sourceValid.getAsString(SourceContract.ID)); 6374 eventReport_NotDelivered_WithinWindow.put( 6375 EventReportContract.TRIGGER_ID, triggerValid.getAsString(TriggerContract.ID)); 6376 db.insert(EventReportContract.TABLE, null, eventReport_NotDelivered_WithinWindow); 6377 6378 ContentValues eventReport_Delivered_WithinWindow = new ContentValues(); 6379 eventReport_Delivered_WithinWindow.put(EventReportContract.ID, "e2"); 6380 eventReport_Delivered_WithinWindow.put( 6381 EventReportContract.REPORT_TIME, System.currentTimeMillis()); 6382 eventReport_Delivered_WithinWindow.put( 6383 EventReportContract.STATUS, EventReport.Status.DELIVERED); 6384 eventReport_Delivered_WithinWindow.put( 6385 EventReportContract.SOURCE_ID, sourceValid.getAsString(SourceContract.ID)); 6386 eventReport_Delivered_WithinWindow.put( 6387 EventReportContract.TRIGGER_ID, triggerValid.getAsString(TriggerContract.ID)); 6388 db.insert(EventReportContract.TABLE, null, eventReport_Delivered_WithinWindow); 6389 6390 ContentValues eventReport_Delivered_OutsideWindow = new ContentValues(); 6391 eventReport_Delivered_OutsideWindow.put(EventReportContract.ID, "e3"); 6392 eventReport_Delivered_OutsideWindow.put( 6393 EventReportContract.REPORT_TIME, System.currentTimeMillis() - DAYS.toMillis(20)); 6394 eventReport_Delivered_OutsideWindow.put( 6395 EventReportContract.STATUS, EventReport.Status.DELIVERED); 6396 eventReport_Delivered_OutsideWindow.put( 6397 EventReportContract.SOURCE_ID, sourceValid.getAsString(SourceContract.ID)); 6398 eventReport_Delivered_OutsideWindow.put( 6399 EventReportContract.TRIGGER_ID, triggerValid.getAsString(TriggerContract.ID)); 6400 db.insert(EventReportContract.TABLE, null, eventReport_Delivered_OutsideWindow); 6401 6402 ContentValues eventReport_NotDelivered_OutsideWindow = new ContentValues(); 6403 eventReport_NotDelivered_OutsideWindow.put(EventReportContract.ID, "e4"); 6404 eventReport_NotDelivered_OutsideWindow.put( 6405 EventReportContract.REPORT_TIME, System.currentTimeMillis() - DAYS.toMillis(20)); 6406 eventReport_NotDelivered_OutsideWindow.put( 6407 EventReportContract.STATUS, EventReport.Status.PENDING); 6408 eventReport_NotDelivered_OutsideWindow.put( 6409 EventReportContract.SOURCE_ID, sourceValid.getAsString(SourceContract.ID)); 6410 eventReport_NotDelivered_OutsideWindow.put( 6411 EventReportContract.TRIGGER_ID, triggerValid.getAsString(TriggerContract.ID)); 6412 db.insert(EventReportContract.TABLE, null, eventReport_NotDelivered_OutsideWindow); 6413 6414 ContentValues eventReport_expiredSource = new ContentValues(); 6415 eventReport_expiredSource.put(EventReportContract.ID, "e5"); 6416 eventReport_expiredSource.put(EventReportContract.REPORT_TIME, System.currentTimeMillis()); 6417 eventReport_expiredSource.put(EventReportContract.STATUS, EventReport.Status.PENDING); 6418 eventReport_expiredSource.put( 6419 EventReportContract.SOURCE_ID, sourceExpired.getAsString(SourceContract.ID)); 6420 eventReport_expiredSource.put( 6421 EventReportContract.TRIGGER_ID, triggerValid.getAsString(TriggerContract.ID)); 6422 db.insert(EventReportContract.TABLE, null, eventReport_expiredSource); 6423 6424 ContentValues eventReport_expiredTrigger = new ContentValues(); 6425 eventReport_expiredTrigger.put(EventReportContract.ID, "e6"); 6426 eventReport_expiredTrigger.put(EventReportContract.REPORT_TIME, System.currentTimeMillis()); 6427 eventReport_expiredTrigger.put(EventReportContract.STATUS, EventReport.Status.PENDING); 6428 eventReport_expiredTrigger.put( 6429 EventReportContract.SOURCE_ID, sourceValid.getAsString(SourceContract.ID)); 6430 eventReport_expiredTrigger.put( 6431 EventReportContract.TRIGGER_ID, triggerExpired.getAsString(TriggerContract.ID)); 6432 db.insert(EventReportContract.TABLE, null, eventReport_expiredTrigger); 6433 6434 long earliestValidInsertion = System.currentTimeMillis() - DAYS.toMillis(10); 6435 int retryLimit = Flags.MEASUREMENT_MAX_RETRIES_PER_REGISTRATION_REQUEST; 6436 mDatastoreManager.runInTransaction( 6437 measurementDao -> 6438 measurementDao.deleteExpiredRecords( 6439 earliestValidInsertion, 6440 retryLimit, 6441 /* earliestValidAppReportInsertion */ null, 6442 /* earliestValidAggregateDebugReportInsertion */ 0)); 6443 6444 List<ContentValues> deletedReports = 6445 List.of(eventReport_expiredSource, eventReport_expiredTrigger); 6446 6447 List<ContentValues> notDeletedReports = 6448 List.of( 6449 eventReport_Delivered_OutsideWindow, 6450 eventReport_Delivered_WithinWindow, 6451 eventReport_NotDelivered_OutsideWindow, 6452 eventReport_NotDelivered_WithinWindow); 6453 6454 assertEquals( 6455 notDeletedReports.size(), 6456 DatabaseUtils.longForQuery( 6457 db, 6458 "SELECT COUNT(" 6459 + EventReportContract.ID 6460 + ") FROM " 6461 + EventReportContract.TABLE 6462 + " WHERE " 6463 + EventReportContract.ID 6464 + " IN (" 6465 + notDeletedReports.stream() 6466 .map( 6467 (eR) -> { 6468 return DatabaseUtils.sqlEscapeString( 6469 eR.getAsString(EventReportContract.ID)); 6470 }) 6471 .collect(Collectors.joining(",")) 6472 + ")", 6473 null)); 6474 6475 assertEquals( 6476 0, 6477 DatabaseUtils.longForQuery( 6478 db, 6479 "SELECT COUNT(" 6480 + EventReportContract.ID 6481 + ") FROM " 6482 + EventReportContract.TABLE 6483 + " WHERE " 6484 + EventReportContract.ID 6485 + " IN (" 6486 + deletedReports.stream() 6487 .map( 6488 (eR) -> { 6489 return DatabaseUtils.sqlEscapeString( 6490 eR.getAsString(EventReportContract.ID)); 6491 }) 6492 .collect(Collectors.joining(",")) 6493 + ")", 6494 null)); 6495 } 6496 6497 @Test deleteExpiredRecords_VerboseDebugReportsWhileLimitingRetries()6498 public void deleteExpiredRecords_VerboseDebugReportsWhileLimitingRetries() { 6499 // Mocking that the flags return a Max Retry of 1 6500 Flags mockFlags = Mockito.mock(Flags.class); 6501 ExtendedMockito.doReturn(mockFlags).when(FlagsFactory::getFlags); 6502 ExtendedMockito.doReturn(1).when(mockFlags).getMeasurementReportingRetryLimit(); 6503 ExtendedMockito.doReturn(true).when(mockFlags).getMeasurementReportingRetryLimitEnabled(); 6504 6505 SQLiteDatabase db = MeasurementDbHelper.getInstance().getReadableDatabase(); 6506 6507 DebugReport debugReport1 = 6508 new DebugReport.Builder() 6509 .setId("reportId1") 6510 .setType("trigger-event-deduplicated") 6511 .setBody( 6512 " {\n" 6513 + " \"attribution_destination\":" 6514 + " \"https://destination.example\",\n" 6515 + " \"source_event_id\": \"45623\"\n" 6516 + " }") 6517 .setEnrollmentId("1") 6518 .setRegistrationOrigin(REGISTRATION_ORIGIN) 6519 .build(); 6520 6521 DebugReport debugReport2 = 6522 new DebugReport.Builder() 6523 .setId("reportId2") 6524 .setType("trigger-event-deduplicated") 6525 .setBody( 6526 " {\n" 6527 + " \"attribution_destination\":" 6528 + " \"https://destination.example\",\n" 6529 + " \"source_event_id\": \"45623\"\n" 6530 + " }") 6531 .setEnrollmentId("1") 6532 .setRegistrationOrigin(REGISTRATION_ORIGIN) 6533 .build(); 6534 6535 mDatastoreManager.runInTransaction((dao) -> dao.insertDebugReport(debugReport1)); 6536 mDatastoreManager.runInTransaction((dao) -> dao.insertDebugReport(debugReport2)); 6537 6538 mDatastoreManager.runInTransaction( 6539 dao -> 6540 dao.deleteExpiredRecords( 6541 /* earliestValidInsertion */ 0, 6542 /* registrationRetryLimit */ 0, 6543 /* earliestValidAppReportInsertion */ null, 6544 /* earliestValidAggregateDebugReportInsertion */ 0)); 6545 assertEquals( 6546 2, 6547 DatabaseUtils.longForQuery( 6548 db, 6549 "SELECT COUNT(" 6550 + DebugReportContract.ID 6551 + ") FROM " 6552 + DebugReportContract.TABLE, 6553 null)); 6554 // Increment Attempt Record 1 6555 mDatastoreManager.runInTransaction( 6556 (dao) -> 6557 dao.incrementAndGetReportingRetryCount( 6558 debugReport1.getId(), DataType.DEBUG_REPORT_RETRY_COUNT)); 6559 // Delete Expired (Record 1) 6560 mDatastoreManager.runInTransaction( 6561 dao -> 6562 dao.deleteExpiredRecords( 6563 /* earliestValidInsertion */ 0, 6564 /* registrationRetryLimit */ 0, 6565 /* earliestValidAppReportInsertion */ null, 6566 /* earliestValidAggregateDebugReportInsertion */ 0)); 6567 6568 // Assert Record 2 remains. 6569 assertEquals( 6570 1, 6571 DatabaseUtils.longForQuery( 6572 db, 6573 "SELECT COUNT(" 6574 + DebugReportContract.ID 6575 + ") FROM " 6576 + DebugReportContract.TABLE 6577 + " WHERE " 6578 + DebugReportContract.ID 6579 + " = ?", 6580 new String[] {debugReport2.getId()})); 6581 6582 // Assert Record 1 Removed 6583 assertEquals( 6584 0, 6585 DatabaseUtils.longForQuery( 6586 db, 6587 "SELECT COUNT(" 6588 + DebugReportContract.ID 6589 + ") FROM " 6590 + DebugReportContract.TABLE 6591 + " WHERE " 6592 + DebugReportContract.ID 6593 + " = ?", 6594 new String[] {debugReport1.getId()})); 6595 } 6596 6597 @Test deleteExpiredRecords_VerboseDebugReportsWhileNotLimitingRetries()6598 public void deleteExpiredRecords_VerboseDebugReportsWhileNotLimitingRetries() { 6599 // Mocking that the retry Limiting Disable, but has limit number, 6600 Flags mockFlags = Mockito.mock(Flags.class); 6601 ExtendedMockito.doReturn(mockFlags).when(FlagsFactory::getFlags); 6602 ExtendedMockito.doReturn(1).when(mockFlags).getMeasurementReportingRetryLimit(); 6603 ExtendedMockito.doReturn(false).when(mockFlags).getMeasurementReportingRetryLimitEnabled(); 6604 6605 SQLiteDatabase db = MeasurementDbHelper.getInstance().getReadableDatabase(); 6606 6607 DebugReport debugReport1 = 6608 new DebugReport.Builder() 6609 .setId("reportId1") 6610 .setType("trigger-event-deduplicated") 6611 .setBody( 6612 " {\n" 6613 + " \"attribution_destination\":" 6614 + " \"https://destination.example\",\n" 6615 + " \"source_event_id\": \"45623\"\n" 6616 + " }") 6617 .setEnrollmentId("1") 6618 .setRegistrationOrigin(REGISTRATION_ORIGIN) 6619 .setInsertionTime(System.currentTimeMillis() + 60000L) 6620 .build(); 6621 6622 DebugReport debugReport2 = 6623 new DebugReport.Builder() 6624 .setId("reportId2") 6625 .setType("trigger-event-deduplicated") 6626 .setBody( 6627 " {\n" 6628 + " \"attribution_destination\":" 6629 + " \"https://destination.example\",\n" 6630 + " \"source_event_id\": \"45623\"\n" 6631 + " }") 6632 .setEnrollmentId("1") 6633 .setRegistrationOrigin(REGISTRATION_ORIGIN) 6634 .setInsertionTime(System.currentTimeMillis() - 60000L) 6635 .build(); 6636 // Insert 6637 mDatastoreManager.runInTransaction((dao) -> dao.insertDebugReport(debugReport1)); 6638 mDatastoreManager.runInTransaction((dao) -> dao.insertDebugReport(debugReport2)); 6639 6640 // Increment Attempt 6641 mDatastoreManager.runInTransaction( 6642 (dao) -> 6643 dao.incrementAndGetReportingRetryCount( 6644 debugReport1.getId(), DataType.DEBUG_REPORT_RETRY_COUNT)); 6645 // Delete Expired 6646 long earliestValidInsertion = System.currentTimeMillis(); 6647 6648 assertTrue( 6649 mDatastoreManager.runInTransaction( 6650 measurementDao -> 6651 measurementDao.deleteExpiredRecords( 6652 earliestValidInsertion, 6653 /* earliestValidAppReportInsertion */ 0, 6654 /* earliestValidAppReportInsertion */ null, 6655 /* earliestValidAggregateDebugReportInsertion */ 0))); 6656 6657 // Assert Record 1 remains because not expired and Retry Limiting Off. 6658 assertEquals( 6659 1, 6660 DatabaseUtils.longForQuery( 6661 db, 6662 "SELECT COUNT(" 6663 + DebugReportContract.ID 6664 + ") FROM " 6665 + DebugReportContract.TABLE 6666 + " WHERE " 6667 + DebugReportContract.ID 6668 + " = ?", 6669 new String[] {debugReport1.getId()})); 6670 6671 // Assert Record 2 Removed because expired. 6672 assertEquals( 6673 0, 6674 DatabaseUtils.longForQuery( 6675 db, 6676 "SELECT COUNT(" 6677 + DebugReportContract.ID 6678 + ") FROM " 6679 + DebugReportContract.TABLE 6680 + " WHERE " 6681 + DebugReportContract.ID 6682 + " = ?", 6683 new String[] {debugReport2.getId()})); 6684 } 6685 6686 @Test deleteExpiredRecords_RetryKeyValueData()6687 public void deleteExpiredRecords_RetryKeyValueData() { 6688 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 6689 // Non-stale join record 6690 DebugReport debugReport = createDebugReport(); 6691 mDatastoreManager.runInTransaction((dao) -> dao.insertDebugReport(debugReport)); 6692 6693 // Should Remain 6694 ContentValues nonStaleValues = new ContentValues(); 6695 nonStaleValues.put( 6696 KeyValueDataContract.DATA_TYPE, DataType.DEBUG_REPORT_RETRY_COUNT.toString()); 6697 nonStaleValues.put(KeyValueDataContract.KEY, debugReport.getId()); 6698 nonStaleValues.put(KeyValueDataContract.VALUE, "1"); 6699 db.insert(KeyValueDataContract.TABLE, null, nonStaleValues); 6700 6701 // Should Delete 6702 ContentValues staleValues = new ContentValues(); 6703 staleValues.put( 6704 KeyValueDataContract.DATA_TYPE, DataType.DEBUG_REPORT_RETRY_COUNT.toString()); 6705 staleValues.put(KeyValueDataContract.KEY, "stale-key"); 6706 staleValues.put(KeyValueDataContract.VALUE, "1"); 6707 db.insert(KeyValueDataContract.TABLE, null, staleValues); 6708 6709 mDatastoreManager.runInTransaction( 6710 dao -> 6711 dao.deleteExpiredRecords( 6712 /* earliestValidInsertion */ 0, 6713 /* registrationRetryLimit */ 0, 6714 /* earliestValidAppReportInsertion */ null, 6715 /* earliestValidAggregateDebugReportInsertion */ 0)); 6716 6717 // Assert Non-Stale record remains. 6718 assertEquals( 6719 1, 6720 DatabaseUtils.longForQuery( 6721 db, 6722 "SELECT COUNT(" 6723 + KeyValueDataContract.KEY 6724 + ") FROM " 6725 + KeyValueDataContract.TABLE 6726 + " WHERE " 6727 + KeyValueDataContract.KEY 6728 + " = ?", 6729 new String[] {nonStaleValues.getAsString(KeyValueDataContract.KEY)})); 6730 6731 // Assert Stale Record Removed 6732 assertEquals( 6733 0, 6734 DatabaseUtils.longForQuery( 6735 db, 6736 "SELECT COUNT(" 6737 + KeyValueDataContract.KEY 6738 + ") FROM " 6739 + KeyValueDataContract.TABLE 6740 + " WHERE " 6741 + KeyValueDataContract.KEY 6742 + " = ?", 6743 new String[] {staleValues.getAsString(KeyValueDataContract.KEY)})); 6744 } 6745 6746 @Test deleteExpiredRecords_reinstallAttributionEnabled_deletesExpiredAppInstallHistory()6747 public void deleteExpiredRecords_reinstallAttributionEnabled_deletesExpiredAppInstallHistory() { 6748 Flags mockFlags = Mockito.mock(Flags.class); 6749 ExtendedMockito.doReturn(mockFlags).when(FlagsFactory::getFlags); 6750 ExtendedMockito.doReturn(true).when(mockFlags).getMeasurementEnableReinstallReattribution(); 6751 6752 SQLiteDatabase db = MeasurementDbHelper.getInstance().getReadableDatabase(); 6753 long now = System.currentTimeMillis(); 6754 mDatastoreManager.runInTransaction( 6755 (dao) -> 6756 dao.insertOrUpdateAppReportHistory( 6757 INSTALLED_PACKAGE, 6758 REGISTRATION_ORIGIN, 6759 /* lastReportDeliveredTimestamp= */ now 6760 - TimeUnit.DAYS.toMillis(1))); 6761 mDatastoreManager.runInTransaction( 6762 (dao) -> 6763 dao.insertOrUpdateAppReportHistory( 6764 INSTALLED_PACKAGE, 6765 REGISTRATION_ORIGIN_2, 6766 /* lastReportDeliveredTimestamp= */ now 6767 - TimeUnit.DAYS.toMillis(3))); 6768 6769 long earliestValidInsertion = now - TimeUnit.DAYS.toMillis(2); 6770 assertTrue( 6771 mDatastoreManager.runInTransaction( 6772 measurementDao -> 6773 measurementDao.deleteExpiredRecords( 6774 earliestValidInsertion, 6775 /* registrationRetryLimit= */ 0, 6776 earliestValidInsertion, 6777 /* earliestValidAggregateDebugReportInsertion */ 0))); 6778 // Assert Record 1 remains because not expired and Retry Limiting Off. 6779 assertEquals( 6780 1, 6781 DatabaseUtils.longForQuery( 6782 db, 6783 "SELECT COUNT(" 6784 + AppReportHistoryContract.REGISTRATION_ORIGIN 6785 + ") FROM " 6786 + AppReportHistoryContract.TABLE 6787 + " WHERE " 6788 + AppReportHistoryContract.REGISTRATION_ORIGIN 6789 + " = ?", 6790 new String[] {REGISTRATION_ORIGIN.toString()})); 6791 6792 // Assert Record 2 Removed because expired. 6793 assertEquals( 6794 0, 6795 DatabaseUtils.longForQuery( 6796 db, 6797 "SELECT COUNT(" 6798 + AppReportHistoryContract.REGISTRATION_ORIGIN 6799 + ") FROM " 6800 + AppReportHistoryContract.TABLE 6801 + " WHERE " 6802 + AppReportHistoryContract.REGISTRATION_ORIGIN 6803 + " = ?", 6804 new String[] {REGISTRATION_ORIGIN_2.toString()})); 6805 } 6806 6807 @Test deleteExpiredRecords_withAdrBudgetTrackerRecords_deletesOlderRecords()6808 public void deleteExpiredRecords_withAdrBudgetTrackerRecords_deletesOlderRecords() { 6809 // Setup 6810 Flags mockFlags = Mockito.mock(Flags.class); 6811 ExtendedMockito.doReturn(mockFlags).when(FlagsFactory::getFlags); 6812 ExtendedMockito.doReturn(true) 6813 .when(mockFlags) 6814 .getMeasurementEnableAggregateDebugReporting(); 6815 long currentTime = System.currentTimeMillis(); 6816 6817 // record1 - 6 hour old report record 6818 AggregateDebugReportRecord record1 = 6819 new AggregateDebugReportRecord.Builder( 6820 currentTime - TimeUnit.HOURS.toMillis(6), 6821 Uri.parse("android-app://com.example.abc"), 6822 APP_ONE_PUBLISHER, 6823 REGISTRATION_ORIGIN, 6824 1) 6825 .build(); 6826 // record2 - 1 day 1 millisecond old report record 6827 AggregateDebugReportRecord record2 = 6828 new AggregateDebugReportRecord.Builder( 6829 currentTime - DAYS.toMillis(1) - 1, 6830 Uri.parse("android-app://com.example.def"), 6831 APP_TWO_PUBLISHER, 6832 REGISTRATION_ORIGIN_2, 6833 10) 6834 .build(); 6835 // record3 - 10 days old report record 6836 AggregateDebugReportRecord record3 = 6837 new AggregateDebugReportRecord.Builder( 6838 currentTime - DAYS.toMillis(10), 6839 Uri.parse("android-app://com.example.ghi"), 6840 APP_ONE_PUBLISHER, 6841 REGISTRATION_ORIGIN_3, 6842 100) 6843 .build(); 6844 // record4 - 1 minute old report record 6845 AggregateDebugReportRecord record4 = 6846 new AggregateDebugReportRecord.Builder( 6847 currentTime - TimeUnit.MINUTES.toMillis(1), 6848 Uri.parse("android-app://com.example.abc"), 6849 APP_TWO_SOURCES, 6850 REGISTRATION_ORIGIN_4, 6851 1000) 6852 .build(); 6853 List<AggregateDebugReportRecord> records = 6854 Arrays.asList(record1, record2, record3, record4); 6855 records.forEach( 6856 record -> 6857 mDatastoreManager.runInTransaction( 6858 dao -> dao.insertAggregateDebugReportRecord(record))); 6859 6860 // Execution - delete records older than 1 day - should retain record1 & record4 6861 mDatastoreManager.runInTransaction( 6862 dao -> 6863 dao.deleteExpiredRecords( 6864 /* earliestValidInsertion */ 0L, 6865 /* registrationRetryLimit */ 0, 6866 /* earliestValidAppReportInsertion */ null, 6867 currentTime - DAYS.toMillis(1))); 6868 6869 // Assertion 6870 assertThat( 6871 DatabaseUtils.queryNumEntries( 6872 MeasurementDbHelper.getInstance().getReadableDatabase(), 6873 AggregatableDebugReportBudgetTrackerContract.TABLE)) 6874 .isEqualTo(2); 6875 try (Cursor recordCursor = 6876 MeasurementDbHelper.getInstance() 6877 .getReadableDatabase() 6878 .query( 6879 AggregatableDebugReportBudgetTrackerContract.TABLE, 6880 new String[] { 6881 AggregatableDebugReportBudgetTrackerContract.REGISTRATION_ORIGIN 6882 }, 6883 null, 6884 null, 6885 null, 6886 null, 6887 AggregatableDebugReportBudgetTrackerContract.REGISTRATION_ORIGIN)) { 6888 assertThat(recordCursor.getCount()).isEqualTo(2); 6889 assertThat(recordCursor.moveToNext()).isTrue(); 6890 assertThat( 6891 recordCursor.getString( 6892 recordCursor.getColumnIndex( 6893 AggregatableDebugReportBudgetTrackerContract 6894 .REGISTRATION_ORIGIN))) 6895 .isEqualTo(REGISTRATION_ORIGIN.toString()); 6896 assertThat(recordCursor.moveToNext()).isTrue(); 6897 assertThat( 6898 recordCursor.getString( 6899 recordCursor.getColumnIndex( 6900 AggregatableDebugReportBudgetTrackerContract 6901 .REGISTRATION_ORIGIN))) 6902 .isEqualTo(REGISTRATION_ORIGIN_4.toString()); 6903 } 6904 } 6905 6906 @Test deleteExpiredRecords_withAdrRecords_deletesRecordsDueToSourceTriggerDeletion()6907 public void deleteExpiredRecords_withAdrRecords_deletesRecordsDueToSourceTriggerDeletion() { 6908 // Setup 6909 Flags mockFlags = Mockito.mock(Flags.class); 6910 ExtendedMockito.doReturn(mockFlags).when(FlagsFactory::getFlags); 6911 ExtendedMockito.doReturn(true) 6912 .when(mockFlags) 6913 .getMeasurementEnableAggregateDebugReporting(); 6914 long currentTime = System.currentTimeMillis(); 6915 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 6916 6917 // 30 days old 6918 Source s1 = 6919 SourceFixture.getValidSourceBuilder() 6920 .setId("S1") 6921 .setEventTime(currentTime - DAYS.toMillis(30)) 6922 .build(); 6923 Trigger t1 = 6924 TriggerFixture.getValidTriggerBuilder() 6925 .setId("T1") 6926 .setTriggerTime(currentTime - DAYS.toMillis(30)) 6927 .build(); 6928 AbstractDbIntegrationTest.insertToDb(s1, db); 6929 AbstractDbIntegrationTest.insertToDb(t1, db); 6930 6931 // 1 day old 6932 Source s2 = 6933 SourceFixture.getValidSourceBuilder() 6934 .setId("S2") 6935 .setEventTime(currentTime - DAYS.toMillis(1)) 6936 .build(); 6937 Trigger t2 = 6938 TriggerFixture.getValidTriggerBuilder() 6939 .setId("T2") 6940 .setTriggerTime(currentTime - DAYS.toMillis(1)) 6941 .build(); 6942 AbstractDbIntegrationTest.insertToDb(s2, db); 6943 AbstractDbIntegrationTest.insertToDb(t2, db); 6944 6945 // deleted - record1 - 6 hour old report record but source and trigger are 30 days old 6946 AggregateDebugReportRecord record1 = 6947 new AggregateDebugReportRecord.Builder( 6948 currentTime - TimeUnit.HOURS.toMillis(6), 6949 Uri.parse("android-app://com.example.abc"), 6950 APP_ONE_PUBLISHER, 6951 REGISTRATION_ORIGIN, 6952 1) 6953 .setSourceId(s1.getId()) 6954 .setTriggerId(t1.getId()) 6955 .build(); 6956 // deleted - record2 - 6 hour old report record but source is 30 days old 6957 AggregateDebugReportRecord record2 = 6958 new AggregateDebugReportRecord.Builder( 6959 currentTime - TimeUnit.HOURS.toMillis(6), 6960 Uri.parse("android-app://com.example.def"), 6961 APP_TWO_PUBLISHER, 6962 REGISTRATION_ORIGIN_2, 6963 10) 6964 .setSourceId(s1.getId()) 6965 .setTriggerId(null) 6966 .build(); 6967 // deleted - record3 - 6 hour old report record but trigger is 30 days old 6968 AggregateDebugReportRecord record3 = 6969 new AggregateDebugReportRecord.Builder( 6970 currentTime - TimeUnit.HOURS.toMillis(6), 6971 Uri.parse("android-app://com.example.ghi"), 6972 APP_ONE_PUBLISHER, 6973 REGISTRATION_ORIGIN_3, 6974 100) 6975 .setSourceId(null) 6976 .setTriggerId(t1.getId()) 6977 .build(); 6978 // retained - record4 - 6 hour old report record and source is 1 day old 6979 AggregateDebugReportRecord record4 = 6980 new AggregateDebugReportRecord.Builder( 6981 currentTime - TimeUnit.HOURS.toMillis(6), 6982 Uri.parse("android-app://com.example.jkl"), 6983 APP_TWO_SOURCES, 6984 REGISTRATION_ORIGIN_4, 6985 1000) 6986 .setSourceId(s2.getId()) 6987 .setTriggerId(null) 6988 .build(); 6989 // retained - record4 - 6 hour old report record and trigger is 1 day old 6990 AggregateDebugReportRecord record5 = 6991 new AggregateDebugReportRecord.Builder( 6992 currentTime - TimeUnit.HOURS.toMillis(6), 6993 Uri.parse("android-app://com.example.mno"), 6994 APP_TWO_SOURCES, 6995 REGISTRATION_ORIGIN_5, 6996 1000) 6997 .setSourceId(null) 6998 .setTriggerId(t2.getId()) 6999 .build(); 7000 List<AggregateDebugReportRecord> records = 7001 Arrays.asList(record1, record2, record3, record4, record5); 7002 records.forEach( 7003 record -> 7004 mDatastoreManager.runInTransaction( 7005 dao -> dao.insertAggregateDebugReportRecord(record))); 7006 7007 // Execution - delete records older than 10 days - should retain record4 & record5 7008 mDatastoreManager.runInTransaction( 7009 dao -> 7010 dao.deleteExpiredRecords( 7011 /* earliestValidInsertion */ currentTime - DAYS.toMillis(10), 7012 /* registrationRetryLimit */ 0, 7013 /* earliestValidAppReportInsertion */ null, 7014 /* earliestValidAggregateDebugReportInsertion */ 0)); 7015 7016 // Assertion 7017 assertThat( 7018 DatabaseUtils.queryNumEntries( 7019 MeasurementDbHelper.getInstance().getReadableDatabase(), 7020 AggregatableDebugReportBudgetTrackerContract.TABLE)) 7021 .isEqualTo(2); 7022 try (Cursor recordCursor = 7023 MeasurementDbHelper.getInstance() 7024 .getReadableDatabase() 7025 .query( 7026 AggregatableDebugReportBudgetTrackerContract.TABLE, 7027 new String[] { 7028 AggregatableDebugReportBudgetTrackerContract.REGISTRATION_ORIGIN 7029 }, 7030 null, 7031 null, 7032 null, 7033 null, 7034 AggregatableDebugReportBudgetTrackerContract.REGISTRATION_ORIGIN)) { 7035 assertThat(recordCursor.getCount()).isEqualTo(2); 7036 assertThat(recordCursor.moveToNext()).isTrue(); 7037 assertThat( 7038 recordCursor.getString( 7039 recordCursor.getColumnIndex( 7040 AggregatableDebugReportBudgetTrackerContract 7041 .REGISTRATION_ORIGIN))) 7042 .isEqualTo(REGISTRATION_ORIGIN_4.toString()); 7043 assertThat(recordCursor.moveToNext()).isTrue(); 7044 assertThat( 7045 recordCursor.getString( 7046 recordCursor.getColumnIndex( 7047 AggregatableDebugReportBudgetTrackerContract 7048 .REGISTRATION_ORIGIN))) 7049 .isEqualTo(REGISTRATION_ORIGIN_5.toString()); 7050 } 7051 } 7052 7053 @Test getRegistrationRedirectCount_keyMissing()7054 public void getRegistrationRedirectCount_keyMissing() { 7055 Optional<KeyValueData> optKeyValueData = 7056 mDatastoreManager.runInTransactionWithResult( 7057 (dao) -> 7058 dao.getKeyValueData( 7059 "missing_random_id", DataType.REGISTRATION_REDIRECT_COUNT)); 7060 assertTrue(optKeyValueData.isPresent()); 7061 KeyValueData keyValueData = optKeyValueData.get(); 7062 assertEquals(KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT, keyValueData.getDataType()); 7063 assertEquals("missing_random_id", keyValueData.getKey()); 7064 assertNull(keyValueData.getValue()); 7065 assertEquals(1, keyValueData.getRegistrationRedirectCount()); 7066 } 7067 7068 @Test getRegistrationRedirectCount_keyExists()7069 public void getRegistrationRedirectCount_keyExists() { 7070 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 7071 ContentValues contentValues = new ContentValues(); 7072 contentValues.put( 7073 KeyValueDataContract.DATA_TYPE, 7074 KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT.toString()); 7075 contentValues.put(KeyValueDataContract.KEY, "random_id"); 7076 contentValues.put(KeyValueDataContract.VALUE, "2"); 7077 db.insert(KeyValueDataContract.TABLE, null, contentValues); 7078 Optional<KeyValueData> optKeyValueData = 7079 mDatastoreManager.runInTransactionWithResult( 7080 (dao) -> 7081 dao.getKeyValueData( 7082 "random_id", 7083 KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT)); 7084 assertTrue(optKeyValueData.isPresent()); 7085 KeyValueData keyValueData = optKeyValueData.get(); 7086 assertEquals(KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT, keyValueData.getDataType()); 7087 assertEquals("random_id", keyValueData.getKey()); 7088 assertEquals("2", keyValueData.getValue()); 7089 assertEquals(2, keyValueData.getRegistrationRedirectCount()); 7090 } 7091 7092 @Test updateRegistrationRedirectCount_keyMissing()7093 public void updateRegistrationRedirectCount_keyMissing() { 7094 KeyValueData keyValueData = 7095 new KeyValueData.Builder() 7096 .setDataType(KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT) 7097 .setKey("key_1") 7098 .setValue("4") 7099 .build(); 7100 mDatastoreManager.runInTransaction((dao) -> dao.insertOrUpdateKeyValueData(keyValueData)); 7101 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 7102 Cursor cursor = db.query(KeyValueDataContract.TABLE, null, null, null, null, null, null); 7103 assertEquals(1, cursor.getCount()); 7104 cursor.moveToNext(); 7105 assertEquals( 7106 KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT.toString(), 7107 cursor.getString(cursor.getColumnIndex(KeyValueDataContract.DATA_TYPE))); 7108 assertEquals("key_1", cursor.getString(cursor.getColumnIndex(KeyValueDataContract.KEY))); 7109 assertEquals("4", cursor.getString(cursor.getColumnIndex(KeyValueDataContract.VALUE))); 7110 cursor.close(); 7111 } 7112 7113 @Test updateRegistrationRedirectCount_keyExists()7114 public void updateRegistrationRedirectCount_keyExists() { 7115 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 7116 ContentValues contentValues = new ContentValues(); 7117 contentValues.put( 7118 KeyValueDataContract.DATA_TYPE, 7119 KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT.toString()); 7120 contentValues.put(KeyValueDataContract.KEY, "key_1"); 7121 contentValues.put(KeyValueDataContract.VALUE, "2"); 7122 db.insert(KeyValueDataContract.TABLE, null, contentValues); 7123 7124 KeyValueData keyValueData = 7125 new KeyValueData.Builder() 7126 .setDataType(KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT) 7127 .setKey("key_1") 7128 .setValue("4") 7129 .build(); 7130 mDatastoreManager.runInTransaction((dao) -> dao.insertOrUpdateKeyValueData(keyValueData)); 7131 7132 Cursor cursor = db.query(KeyValueDataContract.TABLE, null, null, null, null, null, null); 7133 assertEquals(1, cursor.getCount()); 7134 cursor.moveToNext(); 7135 assertEquals( 7136 KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT.toString(), 7137 cursor.getString(cursor.getColumnIndex(KeyValueDataContract.DATA_TYPE))); 7138 assertEquals("key_1", cursor.getString(cursor.getColumnIndex(KeyValueDataContract.KEY))); 7139 assertEquals("4", cursor.getString(cursor.getColumnIndex(KeyValueDataContract.VALUE))); 7140 cursor.close(); 7141 } 7142 7143 @Test keyValueDataTable_PrimaryKeyConstraint()7144 public void keyValueDataTable_PrimaryKeyConstraint() { 7145 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 7146 ContentValues contentValues1 = new ContentValues(); 7147 contentValues1.put( 7148 KeyValueDataContract.DATA_TYPE, 7149 KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT.toString()); 7150 contentValues1.put(KeyValueDataContract.KEY, "key_1"); 7151 contentValues1.put(KeyValueDataContract.VALUE, "2"); 7152 7153 assertNotEquals(-1, db.insert(KeyValueDataContract.TABLE, null, contentValues1)); 7154 7155 // Should fail because we are using <DataType, Key> as primary key 7156 assertEquals(-1, db.insert(KeyValueDataContract.TABLE, null, contentValues1)); 7157 7158 ContentValues contentValues2 = new ContentValues(); 7159 contentValues2.put( 7160 KeyValueDataContract.DATA_TYPE, 7161 KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT.toString()); 7162 contentValues2.put(KeyValueDataContract.KEY, "key_2"); 7163 contentValues2.put(KeyValueDataContract.VALUE, "2"); 7164 7165 assertNotEquals(-1, db.insert(KeyValueDataContract.TABLE, null, contentValues2)); 7166 } 7167 7168 @Test 7169 public void fetchSourceIdsForLowestPriorityDest_appDestEmptyExclusions_delLowPriorityDestination()7170 fetchSourceIdsForLowestPriorityDest_appDestEmptyExclusions_delLowPriorityDestination() { 7171 // Setup 7172 mFlags = mock(Flags.class); 7173 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 7174 doReturn(true).when(mFlags).getMeasurementEnableSourceDestinationLimitPriority(); 7175 long baseEventTime = System.currentTimeMillis(); 7176 long commonExpiryTime = baseEventTime + DAYS.toMillis(30); 7177 insertSource( 7178 createSourceBuilder() 7179 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app1"))) 7180 .setWebDestinations(List.of(Uri.parse("https://web1.example.com"))) 7181 .setEventTime(baseEventTime) 7182 .setExpiryTime(commonExpiryTime) 7183 .setDestinationLimitPriority(10) 7184 .build(), 7185 "s11"); 7186 insertSource( 7187 createSourceBuilder() 7188 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app2"))) 7189 .setWebDestinations(List.of(Uri.parse("https://web2.example.com"))) 7190 .setEventTime(baseEventTime + DAYS.toMillis(1)) 7191 .setExpiryTime(commonExpiryTime) 7192 .setDestinationLimitPriority(20) 7193 .build(), 7194 "s21"); 7195 insertSource( 7196 createSourceBuilder() 7197 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app3"))) 7198 .setWebDestinations(List.of(Uri.parse("https://web3.example.com"))) 7199 .setEventTime(baseEventTime + DAYS.toMillis(2)) 7200 .setExpiryTime(commonExpiryTime) 7201 .setDestinationLimitPriority(30) 7202 .build(), 7203 "s31"); 7204 insertSource( 7205 createSourceBuilder() 7206 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app1"))) 7207 .setWebDestinations(List.of(Uri.parse("https://web1.example.com"))) 7208 .setEventTime(baseEventTime + DAYS.toMillis(3)) 7209 .setExpiryTime(commonExpiryTime) 7210 .setDestinationLimitPriority(10) 7211 .build(), 7212 "s12"); 7213 insertSource( 7214 createSourceBuilder() 7215 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app2"))) 7216 .setWebDestinations(List.of(Uri.parse("https://web2.example.com"))) 7217 .setEventTime(baseEventTime + DAYS.toMillis(4)) 7218 .setExpiryTime(commonExpiryTime) 7219 .setDestinationLimitPriority(20) 7220 .build(), 7221 "s22"); 7222 7223 // Execute 7224 // com.example.app1 has the least priority of all as 10 7225 mDatastoreManager.runInTransaction( 7226 (dao) -> { 7227 Pair<Long, List<String>> destinationPriorityAndSourcesTodelete = 7228 dao.fetchSourceIdsForLowestPriorityDestinationXEnrollmentXPublisher( 7229 SourceFixture.ValidSourceParams.PUBLISHER, 7230 EventSurfaceType.APP, 7231 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 7232 Collections.emptyList(), 7233 EventSurfaceType.APP, 7234 baseEventTime + DAYS.toMillis(10) // request time 7235 ); 7236 7237 assertEquals(10L, (long) destinationPriorityAndSourcesTodelete.first); 7238 assertEquals( 7239 Sets.newSet("s11", "s12"), 7240 new HashSet<>(destinationPriorityAndSourcesTodelete.second)); 7241 }); 7242 } 7243 7244 @Test 7245 public void fetchSourceIdsForLowPriorityDest_webDestEmptyExclusions_delLowPriorityDestinations()7246 fetchSourceIdsForLowPriorityDest_webDestEmptyExclusions_delLowPriorityDestinations() { 7247 // Setup 7248 mFlags = mock(Flags.class); 7249 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 7250 doReturn(true).when(mFlags).getMeasurementEnableSourceDestinationLimitPriority(); 7251 long baseEventTime = System.currentTimeMillis(); 7252 long commonExpiryTime = baseEventTime + DAYS.toMillis(30); 7253 insertSource( 7254 createSourceBuilder() 7255 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app1"))) 7256 .setWebDestinations(List.of(Uri.parse("https://web1.example.com"))) 7257 .setEventTime(baseEventTime) 7258 .setExpiryTime(commonExpiryTime) 7259 .setDestinationLimitPriority(10) 7260 .build(), 7261 "s11"); 7262 insertSource( 7263 createSourceBuilder() 7264 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app2"))) 7265 .setWebDestinations(List.of(Uri.parse("https://web2.example.com"))) 7266 .setEventTime(baseEventTime + DAYS.toMillis(1)) 7267 .setExpiryTime(commonExpiryTime) 7268 .setDestinationLimitPriority(20) 7269 .build(), 7270 "s21"); 7271 insertSource( 7272 createSourceBuilder() 7273 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app3"))) 7274 .setWebDestinations(List.of(Uri.parse("https://web3.example.com"))) 7275 .setEventTime(baseEventTime + DAYS.toMillis(2)) 7276 .setExpiryTime(commonExpiryTime) 7277 .setDestinationLimitPriority(30) 7278 .build(), 7279 "s31"); 7280 insertSource( 7281 createSourceBuilder() 7282 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app1"))) 7283 .setWebDestinations(List.of(Uri.parse("https://web1.example.com"))) 7284 .setEventTime(baseEventTime + DAYS.toMillis(3)) 7285 .setExpiryTime(commonExpiryTime) 7286 .setDestinationLimitPriority(40) 7287 .build(), 7288 "s12"); 7289 insertSource( 7290 createSourceBuilder() 7291 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app2"))) 7292 .setWebDestinations(List.of(Uri.parse("https://web2.example.com"))) 7293 .setEventTime(baseEventTime + DAYS.toMillis(4)) 7294 .setExpiryTime(commonExpiryTime) 7295 .setDestinationLimitPriority(20) 7296 .build(), 7297 "s22"); 7298 7299 // Execute 7300 // web1.example.com has priority 10 with s11 and priority 40 with s12, the higher one will 7301 // be considered, i.e. 40. web2.example.com" has priority as 20 through both s21 and s22, 7302 // its associated sources will be deleted instead. 7303 mDatastoreManager.runInTransaction( 7304 (dao) -> { 7305 Pair<Long, List<String>> destinationPriorityAndSourcesToDelete = 7306 dao.fetchSourceIdsForLowestPriorityDestinationXEnrollmentXPublisher( 7307 SourceFixture.ValidSourceParams.PUBLISHER, 7308 EventSurfaceType.APP, 7309 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 7310 Collections.emptyList(), 7311 EventSurfaceType.WEB, 7312 baseEventTime + DAYS.toMillis(10) // request time 7313 ); 7314 7315 assertEquals(20L, (long) destinationPriorityAndSourcesToDelete.first); 7316 assertEquals( 7317 Sets.newSet("s21", "s22"), 7318 new HashSet<>(destinationPriorityAndSourcesToDelete.second)); 7319 }); 7320 } 7321 7322 @Test fetchSourceIdsForLowPriorityDest_appDestEmptyExclusions_delLruDestinations()7323 public void fetchSourceIdsForLowPriorityDest_appDestEmptyExclusions_delLruDestinations() { 7324 // Setup 7325 long baseEventTime = System.currentTimeMillis(); 7326 insert5SourcesForLruDestDeletion(baseEventTime); 7327 7328 // Execute 7329 // com.example.app3 would be the least recently used destination, as 1 & 2 are used 7330 // afterwards 7331 mDatastoreManager.runInTransaction( 7332 (dao) -> { 7333 Pair<Long, List<String>> destinationPriorityAndSourcesToDelete = 7334 dao.fetchSourceIdsForLowestPriorityDestinationXEnrollmentXPublisher( 7335 SourceFixture.ValidSourceParams.PUBLISHER, 7336 EventSurfaceType.APP, 7337 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 7338 Collections.emptyList(), 7339 EventSurfaceType.APP, 7340 baseEventTime + DAYS.toMillis(10) // request time 7341 ); 7342 7343 assertEquals(0, (long) destinationPriorityAndSourcesToDelete.first); 7344 assertEquals(List.of("s31"), destinationPriorityAndSourcesToDelete.second); 7345 }); 7346 } 7347 7348 @Test 7349 public void fetchSourceIdsForLowPriorityDest_appDestWebPubEmptyExclusions_delLowPrioDestSource()7350 fetchSourceIdsForLowPriorityDest_appDestWebPubEmptyExclusions_delLowPrioDestSource() { 7351 // Setup 7352 long baseEventTime = System.currentTimeMillis(); 7353 long commonExpiryTime = baseEventTime + DAYS.toMillis(30); 7354 insertSource( 7355 createSourceBuilder() 7356 .setPublisher(Uri.parse("https://web.example.com")) 7357 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app1"))) 7358 .setWebDestinations(List.of(Uri.parse("https://web1.example.com"))) 7359 .setEventTime(baseEventTime) 7360 .setExpiryTime(commonExpiryTime) 7361 .build(), 7362 "s11"); 7363 insertSource( 7364 createSourceBuilder() 7365 .setPublisher(Uri.parse("https://web.example.com")) 7366 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app2"))) 7367 .setWebDestinations(List.of(Uri.parse("https://web2.example.com"))) 7368 .setEventTime(baseEventTime + DAYS.toMillis(1)) 7369 .setExpiryTime(commonExpiryTime) 7370 .build(), 7371 "s21"); 7372 insertSource( 7373 createSourceBuilder() 7374 .setPublisher(Uri.parse("https://web.example.com")) 7375 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app3"))) 7376 .setWebDestinations(List.of(Uri.parse("https://web3.example.com"))) 7377 .setEventTime(baseEventTime + DAYS.toMillis(2)) 7378 .setExpiryTime(commonExpiryTime) 7379 .build(), 7380 "s31"); 7381 insertSource( 7382 createSourceBuilder() 7383 .setPublisher(Uri.parse("https://web.example.com")) 7384 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app1"))) 7385 .setWebDestinations(List.of(Uri.parse("https://web1.example.com"))) 7386 .setEventTime(baseEventTime + DAYS.toMillis(3)) 7387 .setExpiryTime(commonExpiryTime) 7388 .build(), 7389 "s12"); 7390 insertSource( 7391 createSourceBuilder() 7392 .setPublisher(Uri.parse("https://web.example.com")) 7393 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app2"))) 7394 .setWebDestinations(List.of(Uri.parse("https://web2.example.com"))) 7395 .setEventTime(baseEventTime + DAYS.toMillis(4)) 7396 .setExpiryTime(commonExpiryTime) 7397 .build(), 7398 "s22"); 7399 7400 // Execute 7401 // com.example.app3 would be the least recently used destination, as 1 & 2 are used 7402 // afterwards 7403 mDatastoreManager.runInTransaction( 7404 (dao) -> { 7405 Pair<Long, List<String>> destinationPriorityAndSourcesToDelete = 7406 dao.fetchSourceIdsForLowestPriorityDestinationXEnrollmentXPublisher( 7407 Uri.parse("https://web.example.com"), 7408 EventSurfaceType.WEB, 7409 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 7410 Collections.emptyList(), 7411 EventSurfaceType.APP, 7412 baseEventTime + DAYS.toMillis(10) // request time 7413 ); 7414 7415 assertEquals(0L, (long) destinationPriorityAndSourcesToDelete.first); 7416 assertEquals(List.of("s31"), destinationPriorityAndSourcesToDelete.second); 7417 }); 7418 } 7419 7420 @Test 7421 public void fetchSourceIdsForLowPriorityDest_diffEnrollments_delOldDestSourceForChosenEnrollment()7422 fetchSourceIdsForLowPriorityDest_diffEnrollments_delOldDestSourceForChosenEnrollment() { 7423 // Setup 7424 long baseEventTime = System.currentTimeMillis(); 7425 long commonExpiryTime = baseEventTime + DAYS.toMillis(30); 7426 insertSource( 7427 createSourceBuilder() 7428 .setEnrollmentId("enrollment1") 7429 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app1"))) 7430 .setEventTime(baseEventTime) 7431 .setExpiryTime(commonExpiryTime) 7432 .build(), 7433 "s11"); 7434 insertSource( 7435 createSourceBuilder() 7436 .setEnrollmentId("enrollment1") 7437 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app2"))) 7438 .setEventTime(baseEventTime + DAYS.toMillis(1)) 7439 .setExpiryTime(commonExpiryTime) 7440 .build(), 7441 "s21"); 7442 insertSource( 7443 createSourceBuilder() 7444 .setEnrollmentId("enrollment1") 7445 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app3"))) 7446 .setEventTime(baseEventTime + DAYS.toMillis(2)) 7447 .setExpiryTime(commonExpiryTime) 7448 .build(), 7449 "s31"); 7450 insertSource( 7451 createSourceBuilder() 7452 .setEnrollmentId("enrollment2") 7453 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app1"))) 7454 .setEventTime(baseEventTime + DAYS.toMillis(3)) 7455 .setExpiryTime(commonExpiryTime) 7456 .build(), 7457 "s12"); 7458 insertSource( 7459 createSourceBuilder() 7460 .setEnrollmentId("enrollment2") 7461 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app2"))) 7462 .setEventTime(baseEventTime + DAYS.toMillis(4)) 7463 .setExpiryTime(commonExpiryTime) 7464 .build(), 7465 "s22"); 7466 7467 // Execute 7468 // com.example.app1 would be the least recently used destination for enrollment2, that will 7469 // be deleted 7470 mDatastoreManager.runInTransaction( 7471 (dao) -> { 7472 Pair<Long, List<String>> destinationPriorityAndSourcesToDelete = 7473 dao.fetchSourceIdsForLowestPriorityDestinationXEnrollmentXPublisher( 7474 SourceFixture.ValidSourceParams.PUBLISHER, 7475 EventSurfaceType.APP, 7476 "enrollment2", 7477 Collections.emptyList(), 7478 EventSurfaceType.APP, 7479 baseEventTime + DAYS.toMillis(10) // request time 7480 ); 7481 7482 assertEquals(0L, (long) destinationPriorityAndSourcesToDelete.first); 7483 assertEquals(List.of("s12"), destinationPriorityAndSourcesToDelete.second); 7484 }); 7485 } 7486 7487 @Test 7488 public void fetchSourceIdsForLowPriorityDest_appDestExcludeLruSource_deletes2ndLruDestSources()7489 fetchSourceIdsForLowPriorityDest_appDestExcludeLruSource_deletes2ndLruDestSources() { 7490 // Setup 7491 long baseEventTime = System.currentTimeMillis(); 7492 insert5SourcesForLruDestDeletion(System.currentTimeMillis()); 7493 7494 // Execute 7495 // com.example.app1 would be the second least recently used destination, as 2 is used 7496 // afterwards and 3 is ignored to be deleted. 7497 mDatastoreManager.runInTransaction( 7498 (dao) -> { 7499 Pair<Long, List<String>> destinationPriorityAndSourcesToDelete = 7500 dao.fetchSourceIdsForLowestPriorityDestinationXEnrollmentXPublisher( 7501 SourceFixture.ValidSourceParams.PUBLISHER, 7502 EventSurfaceType.APP, 7503 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 7504 List.of(Uri.parse("android-app://com.example.app3")), 7505 EventSurfaceType.APP, 7506 baseEventTime + DAYS.toMillis(10) // request time 7507 ); 7508 7509 assertEquals(0L, (long) destinationPriorityAndSourcesToDelete.first); 7510 assertEquals( 7511 List.of("s11", "s12"), destinationPriorityAndSourcesToDelete.second); 7512 }); 7513 } 7514 insert5SourcesForLruDestDeletion(long baseEventTime)7515 private void insert5SourcesForLruDestDeletion(long baseEventTime) { 7516 long commonExpiryTime = baseEventTime + DAYS.toMillis(30); 7517 insertSource( 7518 createSourceBuilder() 7519 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app1"))) 7520 .setWebDestinations(List.of(Uri.parse("https://web1.example.com"))) 7521 .setEventTime(baseEventTime) 7522 .setExpiryTime(commonExpiryTime) 7523 .build(), 7524 "s11"); 7525 insertSource( 7526 createSourceBuilder() 7527 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app2"))) 7528 .setWebDestinations(List.of(Uri.parse("https://web2.example.com"))) 7529 .setEventTime(baseEventTime + DAYS.toMillis(1)) 7530 .setExpiryTime(commonExpiryTime) 7531 .build(), 7532 "s21"); 7533 insertSource( 7534 createSourceBuilder() 7535 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app3"))) 7536 .setWebDestinations(List.of(Uri.parse("https://web3.example.com"))) 7537 .setEventTime(baseEventTime + DAYS.toMillis(2)) 7538 .setExpiryTime(commonExpiryTime) 7539 .build(), 7540 "s31"); 7541 insertSource( 7542 createSourceBuilder() 7543 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app1"))) 7544 .setWebDestinations(List.of(Uri.parse("https://web1.example.com"))) 7545 .setEventTime(baseEventTime + DAYS.toMillis(3)) 7546 .setExpiryTime(commonExpiryTime) 7547 .build(), 7548 "s12"); 7549 insertSource( 7550 createSourceBuilder() 7551 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app2"))) 7552 .setWebDestinations(List.of(Uri.parse("https://web2.example.com"))) 7553 .setEventTime(baseEventTime + DAYS.toMillis(4)) 7554 .setExpiryTime(commonExpiryTime) 7555 .build(), 7556 "s22"); 7557 } 7558 7559 @Test deletePendingAggregateReportsAndAttributionsForSources_success()7560 public void deletePendingAggregateReportsAndAttributionsForSources_success() { 7561 // Setup 7562 long baseTime = System.currentTimeMillis(); 7563 // Sources 7564 insertSource(SourceFixture.getValidSource(), "S1"); 7565 insertSource(SourceFixture.getValidSource(), "S2"); 7566 insertSource(SourceFixture.getValidSource(), "S3"); 7567 insertSource(SourceFixture.getValidSource(), "S4"); 7568 7569 mDatastoreManager.runInTransaction( 7570 (dao) -> { 7571 // Aggregate reports 7572 // Should get deleted 7573 AggregateReport agg1 = 7574 AggregateReportFixture.getValidAggregateReportBuilder() 7575 .setId("Agg1") 7576 .setSourceId("S1") 7577 .setScheduledReportTime(baseTime + TimeUnit.HOURS.toMillis(1)) 7578 .setStatus(AggregateReport.Status.PENDING) 7579 .build(); 7580 dao.insertAggregateReport(agg1); 7581 dao.insertAttribution( 7582 createAttribution( 7583 "Att1", Attribution.Scope.AGGREGATE, "S1", null, agg1.getId())); 7584 7585 // Should not get deleted because S2 is not provided 7586 AggregateReport agg2 = 7587 AggregateReportFixture.getValidAggregateReportBuilder() 7588 .setId("Agg2") 7589 .setSourceId("S2") 7590 .setScheduledReportTime(baseTime + TimeUnit.HOURS.toMillis(1)) 7591 .setStatus(AggregateReport.Status.PENDING) 7592 .build(); 7593 dao.insertAggregateReport(agg2); 7594 dao.insertAttribution( 7595 createAttribution( 7596 "Att2", Attribution.Scope.AGGREGATE, "S2", null, agg2.getId())); 7597 7598 // Infeasible case, but it should not get deleted because its status is 7599 // DELIVERED 7600 AggregateReport agg3 = 7601 AggregateReportFixture.getValidAggregateReportBuilder() 7602 .setId("Agg3") 7603 .setSourceId("S3") 7604 .setScheduledReportTime(baseTime + TimeUnit.HOURS.toMillis(1)) 7605 .setStatus(AggregateReport.Status.DELIVERED) 7606 .build(); 7607 dao.insertAggregateReport(agg3); 7608 dao.insertAttribution( 7609 createAttribution( 7610 "Att3", Attribution.Scope.AGGREGATE, "S3", null, agg3.getId())); 7611 7612 // Execution 7613 dao.deletePendingAggregateReportsAndAttributionsForSources(List.of("S1", "S3")); 7614 7615 // Assertion 7616 assertThrows(DatastoreException.class, () -> dao.getAggregateReport("Agg1")); 7617 assertEquals(agg2, dao.getAggregateReport("Agg2")); 7618 assertEquals(agg3, dao.getAggregateReport("Agg3")); 7619 }); 7620 7621 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 7622 assertEquals(2, DatabaseUtils.queryNumEntries(db, AttributionContract.TABLE)); 7623 Set<String> reportIds = new HashSet<>(); 7624 try (Cursor cursor = 7625 db.rawQuery( 7626 "SELECT " 7627 + AttributionContract.REPORT_ID 7628 + " FROM " 7629 + AttributionContract.TABLE, 7630 null)) { 7631 while (cursor.moveToNext()) { 7632 reportIds.add(cursor.getString(0)); 7633 } 7634 } 7635 assertEquals(Set.of("Agg2", "Agg3"), reportIds); 7636 } 7637 7638 @Test fetchMatchingSourcesUninstall_outsideReportLifetime_deleteSources()7639 public void fetchMatchingSourcesUninstall_outsideReportLifetime_deleteSources() 7640 throws JSONException { 7641 // Setup 7642 mFlags = mock(Flags.class); 7643 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 7644 doReturn(true).when(mFlags).getMeasurementEnableMinReportLifespanForUninstall(); 7645 doReturn(TimeUnit.DAYS.toSeconds(1)) 7646 .when(mFlags) 7647 .getMeasurementMinReportLifespanForUninstallSeconds(); 7648 7649 long currentTime = System.currentTimeMillis(); 7650 long baseEventTime = currentTime - DAYS.toMillis(3); 7651 long expiryTime = baseEventTime + DAYS.toMillis(30); 7652 7653 List<Source> sources = 7654 Arrays.asList( 7655 SourceFixture.getMinimalValidSourceBuilder() 7656 .setEventId(new UnsignedLong(1L)) 7657 .setId("source1") 7658 .setEventTime(baseEventTime) 7659 .setExpiryTime(expiryTime) 7660 .build()); 7661 7662 // All trigger times more than 24 hours ago. 7663 List<Trigger> triggers = 7664 Arrays.asList( 7665 TriggerFixture.getValidTriggerBuilder() 7666 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 7667 .setId("trigger1") 7668 .setTriggerTime( 7669 currentTime - DAYS.toMillis(1) - TimeUnit.HOURS.toMillis(1)) 7670 .build(), 7671 TriggerFixture.getValidTriggerBuilder() 7672 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 7673 .setId("trigger2") 7674 .setTriggerTime(currentTime - DAYS.toMillis(2)) 7675 .build(), 7676 TriggerFixture.getValidTriggerBuilder() 7677 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 7678 .setId("trigger3") 7679 .setTriggerTime(currentTime - DAYS.toMillis(3)) 7680 .build()); 7681 7682 EventReport report0 = 7683 createEventReportForSourceAndTriggerForUninstall( 7684 "report0", sources.get(0), triggers.get(0)); 7685 EventReport report1 = 7686 createEventReportForSourceAndTriggerForUninstall( 7687 "report1", sources.get(0), triggers.get(1)); 7688 7689 AggregateReport aggregateReport0 = 7690 createAggregateReportForSourceAndTrigger( 7691 "areport0", sources.get(0), triggers.get(2)); 7692 7693 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 7694 sources.forEach(source -> insertSource(source, source.getId())); 7695 triggers.forEach(trigger -> AbstractDbIntegrationTest.insertToDb(trigger, db)); 7696 7697 mDatastoreManager.runInTransaction( 7698 (dao) -> { 7699 dao.insertEventReport(report0); 7700 dao.insertEventReport(report1); 7701 dao.insertAggregateReport(aggregateReport0); 7702 }); 7703 7704 // Execution 7705 mDatastoreManager.runInTransaction( 7706 dao -> { 7707 Pair<List<String>, List<String>> actualSources = 7708 dao.fetchMatchingSourcesUninstall( 7709 SourceFixture.ValidSourceParams.REGISTRANT, currentTime); 7710 // Source is deleted 7711 Truth.assertThat(actualSources.first.size()).isEqualTo(1); 7712 Truth.assertThat(actualSources.second.size()).isEqualTo(0); 7713 }); 7714 } 7715 7716 @Test fetchMatchingSourcesUninstall_withinReportLifetime_ignoreSources()7717 public void fetchMatchingSourcesUninstall_withinReportLifetime_ignoreSources() 7718 throws JSONException { 7719 // Setup 7720 mFlags = mock(Flags.class); 7721 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 7722 doReturn(true).when(mFlags).getMeasurementEnableMinReportLifespanForUninstall(); 7723 doReturn(TimeUnit.DAYS.toSeconds(1)) 7724 .when(mFlags) 7725 .getMeasurementMinReportLifespanForUninstallSeconds(); 7726 7727 long currentTime = System.currentTimeMillis(); 7728 long baseEventTime = currentTime - DAYS.toMillis(3); 7729 long expiryTime = baseEventTime + DAYS.toMillis(30); 7730 7731 List<Source> sources = 7732 Arrays.asList( 7733 SourceFixture.getMinimalValidSourceBuilder() 7734 .setEventId(new UnsignedLong(1L)) 7735 .setId("source1") 7736 .setEventTime(baseEventTime) 7737 .setExpiryTime(expiryTime) 7738 .build()); 7739 7740 // All trigger times more than 24 hours ago except trigger1. 7741 List<Trigger> triggers = 7742 Arrays.asList( 7743 TriggerFixture.getValidTriggerBuilder() 7744 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 7745 .setId("trigger1") 7746 .setTriggerTime(currentTime) 7747 .build(), 7748 TriggerFixture.getValidTriggerBuilder() 7749 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 7750 .setId("trigger2") 7751 .setTriggerTime(currentTime - DAYS.toMillis(3)) 7752 .build(), 7753 TriggerFixture.getValidTriggerBuilder() 7754 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 7755 .setId("trigger3") 7756 .setTriggerTime(currentTime - DAYS.toMillis(3)) 7757 .build()); 7758 7759 EventReport report0 = 7760 createEventReportForSourceAndTriggerForUninstall( 7761 "report0", sources.get(0), triggers.get(0)); 7762 EventReport report1 = 7763 createEventReportForSourceAndTriggerForUninstall( 7764 "report1", sources.get(0), triggers.get(1)); 7765 7766 AggregateReport aggregateReport0 = 7767 createAggregateReportForSourceAndTrigger( 7768 "areport0", sources.get(0), triggers.get(2)); 7769 7770 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 7771 sources.forEach(source -> insertSource(source, source.getId())); 7772 triggers.forEach(trigger -> AbstractDbIntegrationTest.insertToDb(trigger, db)); 7773 7774 mDatastoreManager.runInTransaction( 7775 (dao) -> { 7776 dao.insertEventReport(report0); 7777 dao.insertEventReport(report1); 7778 dao.insertAggregateReport(aggregateReport0); 7779 }); 7780 7781 // Execution 7782 mDatastoreManager.runInTransaction( 7783 dao -> { 7784 Pair<List<String>, List<String>> actualSources = 7785 dao.fetchMatchingSourcesUninstall( 7786 SourceFixture.ValidSourceParams.REGISTRANT, currentTime); 7787 // Source is ignored 7788 Truth.assertThat(actualSources.first.size()).isEqualTo(0); 7789 Truth.assertThat(actualSources.second.size()).isEqualTo(1); 7790 }); 7791 } 7792 7793 @Test fetchMatchingSourcesUninstall_deleteAndIgnoreSources()7794 public void fetchMatchingSourcesUninstall_deleteAndIgnoreSources() throws JSONException { 7795 // Setup 7796 mFlags = mock(Flags.class); 7797 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 7798 doReturn(true).when(mFlags).getMeasurementEnableMinReportLifespanForUninstall(); 7799 doReturn(TimeUnit.DAYS.toSeconds(1)) 7800 .when(mFlags) 7801 .getMeasurementMinReportLifespanForUninstallSeconds(); 7802 7803 long currentTime = System.currentTimeMillis(); 7804 long baseEventTime = currentTime - DAYS.toMillis(3); 7805 long expiryTime = baseEventTime + DAYS.toMillis(30); 7806 7807 List<Source> sources = 7808 Arrays.asList( 7809 SourceFixture.getMinimalValidSourceBuilder() 7810 .setEventId(new UnsignedLong(1L)) 7811 .setId("source1") 7812 .setEventTime(baseEventTime) 7813 .setExpiryTime(expiryTime) 7814 .build(), 7815 SourceFixture.getMinimalValidSourceBuilder() 7816 .setEventId(new UnsignedLong(1L)) 7817 .setId("source2") 7818 .setEventTime(baseEventTime) 7819 .setExpiryTime(expiryTime) 7820 .build(), 7821 SourceFixture.getMinimalValidSourceBuilder() 7822 .setEventId(new UnsignedLong(1L)) 7823 .setId("source3") 7824 .setEventTime(baseEventTime) 7825 .setExpiryTime(expiryTime) 7826 .build()); 7827 7828 // All trigger times more than 24 hours ago except trigger1. 7829 List<Trigger> triggers = 7830 Arrays.asList( 7831 TriggerFixture.getValidTriggerBuilder() 7832 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 7833 .setId("trigger1") 7834 .setTriggerTime(currentTime) 7835 .build(), 7836 TriggerFixture.getValidTriggerBuilder() 7837 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 7838 .setId("trigger2") 7839 .setTriggerTime(currentTime - DAYS.toMillis(3)) 7840 .build(), 7841 TriggerFixture.getValidTriggerBuilder() 7842 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 7843 .setId("trigger3") 7844 .setTriggerTime(currentTime - DAYS.toMillis(2)) 7845 .build()); 7846 7847 EventReport report0 = 7848 createEventReportForSourceAndTriggerForUninstall( 7849 "report0", sources.get(0), triggers.get(0)); 7850 EventReport report1 = 7851 createEventReportForSourceAndTriggerForUninstall( 7852 "report1", sources.get(1), triggers.get(1)); 7853 7854 AggregateReport aggregateReport0 = 7855 createAggregateReportForSourceAndTrigger( 7856 "areport0", sources.get(2), triggers.get(2)); 7857 7858 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 7859 sources.forEach(source -> insertSource(source, source.getId())); 7860 triggers.forEach(trigger -> AbstractDbIntegrationTest.insertToDb(trigger, db)); 7861 7862 mDatastoreManager.runInTransaction( 7863 (dao) -> { 7864 dao.insertEventReport(report0); 7865 dao.insertEventReport(report1); 7866 dao.insertAggregateReport(aggregateReport0); 7867 }); 7868 7869 // Execution 7870 mDatastoreManager.runInTransaction( 7871 dao -> { 7872 Pair<List<String>, List<String>> actualSources = 7873 dao.fetchMatchingSourcesUninstall( 7874 SourceFixture.ValidSourceParams.REGISTRANT, currentTime); 7875 // Source is ignored 7876 Truth.assertThat(actualSources.first.size()).isEqualTo(2); 7877 Truth.assertThat(actualSources.second.size()).isEqualTo(1); 7878 }); 7879 } 7880 7881 @Test fetchMatchingTriggersUninstall_outsideReportLifetime_deleteTriggers()7882 public void fetchMatchingTriggersUninstall_outsideReportLifetime_deleteTriggers() 7883 throws JSONException { 7884 // Setup 7885 mFlags = mock(Flags.class); 7886 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 7887 doReturn(true).when(mFlags).getMeasurementEnableMinReportLifespanForUninstall(); 7888 doReturn(TimeUnit.DAYS.toSeconds(1)) 7889 .when(mFlags) 7890 .getMeasurementMinReportLifespanForUninstallSeconds(); 7891 7892 long currentTime = System.currentTimeMillis(); 7893 long baseEventTime = currentTime - DAYS.toMillis(3); 7894 long expiryTime = baseEventTime + DAYS.toMillis(30); 7895 7896 List<Source> sources = 7897 Arrays.asList( 7898 SourceFixture.getMinimalValidSourceBuilder() 7899 .setEventId(new UnsignedLong(1L)) 7900 .setId("source1") 7901 .setEventTime(baseEventTime) 7902 .setExpiryTime(expiryTime) 7903 .build()); 7904 7905 List<Trigger> triggers = 7906 Arrays.asList( 7907 TriggerFixture.getValidTriggerBuilder() 7908 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 7909 .setId("trigger1") 7910 .setTriggerTime( 7911 currentTime - DAYS.toMillis(1) - TimeUnit.HOURS.toMillis(1)) 7912 .build(), 7913 TriggerFixture.getValidTriggerBuilder() 7914 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 7915 .setId("trigger2") 7916 .setTriggerTime(currentTime - DAYS.toMillis(2)) 7917 .build(), 7918 TriggerFixture.getValidTriggerBuilder() 7919 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 7920 .setId("trigger3") 7921 .setTriggerTime(currentTime - DAYS.toMillis(3)) 7922 .build()); 7923 7924 EventReport report0 = 7925 createEventReportForSourceAndTriggerForUninstall( 7926 "report0", sources.get(0), triggers.get(0)); 7927 EventReport report1 = 7928 createEventReportForSourceAndTriggerForUninstall( 7929 "report1", sources.get(0), triggers.get(1)); 7930 7931 AggregateReport aggregateReport0 = 7932 createAggregateReportForSourceAndTrigger( 7933 "areport0", sources.get(0), triggers.get(2)); 7934 7935 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 7936 sources.forEach(source -> insertSource(source, source.getId())); 7937 triggers.forEach(trigger -> AbstractDbIntegrationTest.insertToDb(trigger, db)); 7938 7939 mDatastoreManager.runInTransaction( 7940 (dao) -> { 7941 dao.insertEventReport(report0); 7942 dao.insertEventReport(report1); 7943 dao.insertAggregateReport(aggregateReport0); 7944 }); 7945 7946 // Execution 7947 mDatastoreManager.runInTransaction( 7948 dao -> { 7949 Pair<List<String>, List<String>> actualTriggers = 7950 dao.fetchMatchingTriggersUninstall( 7951 TriggerFixture.ValidTriggerParams.REGISTRANT, currentTime); 7952 // Triggers are deleted 7953 Truth.assertThat(actualTriggers.first.size()).isEqualTo(3); 7954 Truth.assertThat(actualTriggers.second.size()).isEqualTo(0); 7955 }); 7956 } 7957 7958 @Test fetchMatchingTriggersUninstall_withinReportLifetime_ignoreTriggers()7959 public void fetchMatchingTriggersUninstall_withinReportLifetime_ignoreTriggers() 7960 throws JSONException { 7961 // Setup 7962 mFlags = mock(Flags.class); 7963 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 7964 doReturn(true).when(mFlags).getMeasurementEnableMinReportLifespanForUninstall(); 7965 doReturn(TimeUnit.DAYS.toSeconds(1)) 7966 .when(mFlags) 7967 .getMeasurementMinReportLifespanForUninstallSeconds(); 7968 7969 long currentTime = System.currentTimeMillis(); 7970 long baseEventTime = currentTime - DAYS.toMillis(3); 7971 long expiryTime = baseEventTime + DAYS.toMillis(30); 7972 7973 List<Source> sources = 7974 Arrays.asList( 7975 SourceFixture.getMinimalValidSourceBuilder() 7976 .setEventId(new UnsignedLong(1L)) 7977 .setId("source1") 7978 .setEventTime(baseEventTime) 7979 .setExpiryTime(expiryTime) 7980 .build()); 7981 7982 List<Trigger> triggers = 7983 Arrays.asList( 7984 TriggerFixture.getValidTriggerBuilder() 7985 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 7986 .setId("trigger1") 7987 .setTriggerTime(currentTime - TimeUnit.HOURS.toMillis(23)) 7988 .build(), 7989 TriggerFixture.getValidTriggerBuilder() 7990 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 7991 .setId("trigger2") 7992 .setTriggerTime(currentTime - TimeUnit.HOURS.toMillis(1)) 7993 .build(), 7994 TriggerFixture.getValidTriggerBuilder() 7995 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 7996 .setId("trigger3") 7997 .setTriggerTime(currentTime - TimeUnit.HOURS.toMillis(12)) 7998 .build()); 7999 8000 EventReport report0 = 8001 createEventReportForSourceAndTriggerForUninstall( 8002 "report0", sources.get(0), triggers.get(0)); 8003 EventReport report1 = 8004 createEventReportForSourceAndTriggerForUninstall( 8005 "report1", sources.get(0), triggers.get(1)); 8006 8007 AggregateReport aggregateReport0 = 8008 createAggregateReportForSourceAndTrigger( 8009 "areport0", sources.get(0), triggers.get(2)); 8010 8011 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 8012 sources.forEach(source -> insertSource(source, source.getId())); 8013 triggers.forEach(trigger -> AbstractDbIntegrationTest.insertToDb(trigger, db)); 8014 8015 mDatastoreManager.runInTransaction( 8016 (dao) -> { 8017 dao.insertEventReport(report0); 8018 dao.insertEventReport(report1); 8019 dao.insertAggregateReport(aggregateReport0); 8020 }); 8021 8022 // Execution 8023 mDatastoreManager.runInTransaction( 8024 dao -> { 8025 Pair<List<String>, List<String>> actualTriggers = 8026 dao.fetchMatchingTriggersUninstall( 8027 TriggerFixture.ValidTriggerParams.REGISTRANT, currentTime); 8028 // Triggers are ignored 8029 Truth.assertThat(actualTriggers.first.size()).isEqualTo(0); 8030 Truth.assertThat(actualTriggers.second.size()).isEqualTo(3); 8031 }); 8032 } 8033 8034 @Test fetchMatchingTriggersUninstall_deleteAndIgnoreTriggers()8035 public void fetchMatchingTriggersUninstall_deleteAndIgnoreTriggers() throws JSONException { 8036 // Setup 8037 mFlags = mock(Flags.class); 8038 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 8039 doReturn(true).when(mFlags).getMeasurementEnableMinReportLifespanForUninstall(); 8040 doReturn(TimeUnit.DAYS.toSeconds(1)) 8041 .when(mFlags) 8042 .getMeasurementMinReportLifespanForUninstallSeconds(); 8043 8044 long currentTime = System.currentTimeMillis(); 8045 long baseEventTime = currentTime - DAYS.toMillis(3); 8046 long expiryTime = baseEventTime + DAYS.toMillis(30); 8047 8048 List<Source> sources = 8049 Arrays.asList( 8050 SourceFixture.getMinimalValidSourceBuilder() 8051 .setEventId(new UnsignedLong(1L)) 8052 .setId("source1") 8053 .setEventTime(baseEventTime) 8054 .setExpiryTime(expiryTime) 8055 .build(), 8056 SourceFixture.getMinimalValidSourceBuilder() 8057 .setEventId(new UnsignedLong(1L)) 8058 .setId("source2") 8059 .setEventTime(baseEventTime) 8060 .setExpiryTime(expiryTime) 8061 .build(), 8062 SourceFixture.getMinimalValidSourceBuilder() 8063 .setEventId(new UnsignedLong(1L)) 8064 .setId("source3") 8065 .setEventTime(baseEventTime) 8066 .setExpiryTime(expiryTime) 8067 .build()); 8068 8069 List<Trigger> triggers = 8070 Arrays.asList( 8071 TriggerFixture.getValidTriggerBuilder() 8072 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 8073 .setId("trigger1") 8074 .setTriggerTime(currentTime) 8075 .build(), 8076 TriggerFixture.getValidTriggerBuilder() 8077 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 8078 .setId("trigger2") 8079 .setTriggerTime(currentTime - DAYS.toMillis(2)) 8080 .build(), 8081 TriggerFixture.getValidTriggerBuilder() 8082 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 8083 .setId("trigger3") 8084 .setTriggerTime(currentTime - DAYS.toMillis(3)) 8085 .build()); 8086 8087 EventReport report0 = 8088 createEventReportForSourceAndTriggerForUninstall( 8089 "report0", sources.get(0), triggers.get(0)); 8090 EventReport report1 = 8091 createEventReportForSourceAndTriggerForUninstall( 8092 "report1", sources.get(1), triggers.get(1)); 8093 8094 AggregateReport aggregateReport0 = 8095 createAggregateReportForSourceAndTrigger( 8096 "areport0", sources.get(2), triggers.get(2)); 8097 8098 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 8099 sources.forEach(source -> insertSource(source, source.getId())); 8100 triggers.forEach(trigger -> AbstractDbIntegrationTest.insertToDb(trigger, db)); 8101 8102 mDatastoreManager.runInTransaction( 8103 (dao) -> { 8104 dao.insertEventReport(report0); 8105 dao.insertEventReport(report1); 8106 dao.insertAggregateReport(aggregateReport0); 8107 }); 8108 8109 // Execution 8110 mDatastoreManager.runInTransaction( 8111 dao -> { 8112 Pair<List<String>, List<String>> actualTriggers = 8113 dao.fetchMatchingTriggersUninstall( 8114 TriggerFixture.ValidTriggerParams.REGISTRANT, currentTime); 8115 // Triggers are deleted 8116 Truth.assertThat(actualTriggers.first.size()).isEqualTo(2); 8117 Truth.assertThat(actualTriggers.second.size()).isEqualTo(1); 8118 }); 8119 } 8120 8121 @Test deletePendingFakeEventReportsForSources_success()8122 public void deletePendingFakeEventReportsForSources_success() { 8123 // Setup 8124 long baseTime = SOURCE_EVENT_TIME; 8125 // Sources 8126 insertSource(SourceFixture.getValidSource(), "S1"); 8127 insertSource(SourceFixture.getValidSource(), "S2"); 8128 insertSource(SourceFixture.getValidSource(), "S3"); 8129 insertSource(SourceFixture.getValidSource(), "S4"); 8130 8131 mDatastoreManager.runInTransaction( 8132 (dao) -> { 8133 // Event reports 8134 // Should get deleted 8135 EventReport eventReport1 = 8136 EventReportFixture.getBaseEventReportBuild() 8137 .setId("Event1") 8138 .setSourceId("S1") 8139 .setReportTime(baseTime + TimeUnit.HOURS.toMillis(1)) 8140 // trigger time > source event time => fake report 8141 .setTriggerTime(baseTime + TimeUnit.HOURS.toMillis(1) + 1L) 8142 .setStatus(EventReport.Status.PENDING) 8143 .setTriggerId(null) 8144 .build(); 8145 dao.insertEventReport(eventReport1); 8146 dao.insertAttribution( 8147 createAttribution( 8148 "Att1", 8149 Attribution.Scope.EVENT, 8150 "S1", 8151 null, 8152 eventReport1.getId())); 8153 8154 // Should not get deleted because S2 is not provided 8155 EventReport eventReport2 = 8156 EventReportFixture.getBaseEventReportBuild() 8157 .setId("Event2") 8158 .setSourceId("S2") 8159 .setReportTime(baseTime + TimeUnit.HOURS.toMillis(1)) 8160 // trigger time > source event time => fake report 8161 .setTriggerTime( 8162 baseTime 8163 + TimeUnit.HOURS.toMillis(1) 8164 + TimeUnit.MINUTES.toMillis(1)) 8165 .setStatus(EventReport.Status.PENDING) 8166 .setTriggerId(null) 8167 .build(); 8168 dao.insertEventReport(eventReport2); 8169 dao.insertAttribution( 8170 createAttribution( 8171 "Att2", 8172 Attribution.Scope.EVENT, 8173 "S2", 8174 null, 8175 eventReport2.getId())); 8176 8177 // Should not get deleted because it's a real report 8178 EventReport eventReport3 = 8179 EventReportFixture.getBaseEventReportBuild() 8180 .setId("Event3") 8181 .setSourceId("S1") 8182 .setReportTime(baseTime + TimeUnit.HOURS.toMillis(1)) 8183 // trigger time < source event time => real report 8184 .setTriggerTime(baseTime - 1L) 8185 .setStatus(EventReport.Status.PENDING) 8186 .setTriggerId(null) 8187 .build(); 8188 dao.insertEventReport(eventReport3); 8189 dao.insertAttribution( 8190 createAttribution( 8191 "Att3", 8192 Attribution.Scope.EVENT, 8193 "S1", 8194 null, 8195 eventReport3.getId())); 8196 8197 // Infeasible case, but it should not get deleted because its status is 8198 // DELIVERED 8199 EventReport eventReport4 = 8200 EventReportFixture.getBaseEventReportBuild() 8201 .setId("Event4") 8202 .setSourceId("S3") 8203 .setReportTime(baseTime + TimeUnit.HOURS.toMillis(1)) 8204 // trigger time > source event time => fake report 8205 .setTriggerTime( 8206 baseTime 8207 + TimeUnit.HOURS.toMillis(1) 8208 + TimeUnit.SECONDS.toMillis(1)) 8209 .setStatus(EventReport.Status.DELIVERED) 8210 .setTriggerId(null) 8211 .build(); 8212 dao.insertEventReport(eventReport4); 8213 dao.insertAttribution( 8214 createAttribution( 8215 "Att4", 8216 Attribution.Scope.EVENT, 8217 "S3", 8218 null, 8219 eventReport4.getId())); 8220 8221 // Execution 8222 dao.deleteFutureFakeEventReportsForSources(List.of("S1", "S3"), baseTime); 8223 8224 // Assertion 8225 assertThrows(DatastoreException.class, () -> dao.getEventReport("Event1")); 8226 assertEquals(eventReport2, dao.getEventReport("Event2")); 8227 assertEquals(eventReport3, dao.getEventReport("Event3")); 8228 assertEquals(eventReport4, dao.getEventReport("Event4")); 8229 }); 8230 8231 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 8232 assertEquals(4, DatabaseUtils.queryNumEntries(db, AttributionContract.TABLE)); 8233 Set<String> reportIds = new HashSet<>(); 8234 try (Cursor cursor = 8235 db.rawQuery( 8236 "SELECT " 8237 + AttributionContract.REPORT_ID 8238 + " FROM " 8239 + AttributionContract.TABLE, 8240 null)) { 8241 while (cursor.moveToNext()) { 8242 reportIds.add(cursor.getString(0)); 8243 } 8244 } 8245 assertEquals(Set.of("Event1", "Event2", "Event3", "Event4"), reportIds); 8246 } 8247 getSourceWithDifferentDestinations( int numDestinations, boolean hasAppDestinations, boolean hasWebDestinations, long eventTime, Uri publisher, String enrollmentId, @Source.Status int sourceStatus)8248 private static Source getSourceWithDifferentDestinations( 8249 int numDestinations, 8250 boolean hasAppDestinations, 8251 boolean hasWebDestinations, 8252 long eventTime, 8253 Uri publisher, 8254 String enrollmentId, 8255 @Source.Status int sourceStatus) { 8256 List<Uri> appDestinations = null; 8257 List<Uri> webDestinations = null; 8258 if (hasAppDestinations) { 8259 appDestinations = new ArrayList<>(); 8260 appDestinations.add(Uri.parse("android-app://com.app-destination")); 8261 } 8262 if (hasWebDestinations) { 8263 webDestinations = new ArrayList<>(); 8264 for (int i = 0; i < numDestinations; i++) { 8265 webDestinations.add( 8266 Uri.parse("https://web-destination-" + String.valueOf(i) + ".com")); 8267 } 8268 } 8269 long expiryTime = 8270 eventTime 8271 + TimeUnit.SECONDS.toMillis( 8272 MEASUREMENT_MAX_REPORTING_REGISTER_SOURCE_EXPIRATION_IN_SECONDS); 8273 return new Source.Builder() 8274 .setEventId(new UnsignedLong(0L)) 8275 .setEventTime(eventTime) 8276 .setExpiryTime(expiryTime) 8277 .setPublisher(publisher) 8278 .setAppDestinations(appDestinations) 8279 .setWebDestinations(webDestinations) 8280 .setEnrollmentId(enrollmentId) 8281 .setRegistrant(SourceFixture.ValidSourceParams.REGISTRANT) 8282 .setStatus(sourceStatus) 8283 .setRegistrationOrigin(REGISTRATION_ORIGIN) 8284 .build(); 8285 } 8286 getSourcesWithDifferentDestinations( int numSources, boolean hasAppDestinations, boolean hasWebDestinations, long eventTime, Uri publisher, String enrollmentId, @Source.Status int sourceStatus, Uri registrationOrigin)8287 private static List<Source> getSourcesWithDifferentDestinations( 8288 int numSources, 8289 boolean hasAppDestinations, 8290 boolean hasWebDestinations, 8291 long eventTime, 8292 Uri publisher, 8293 String enrollmentId, 8294 @Source.Status int sourceStatus, 8295 Uri registrationOrigin) { 8296 long expiryTime = 8297 eventTime 8298 + TimeUnit.SECONDS.toMillis( 8299 MEASUREMENT_MAX_REPORTING_REGISTER_SOURCE_EXPIRATION_IN_SECONDS); 8300 return getSourcesWithDifferentDestinations( 8301 numSources, 8302 hasAppDestinations, 8303 hasWebDestinations, 8304 eventTime, 8305 expiryTime, 8306 publisher, 8307 enrollmentId, 8308 sourceStatus, 8309 registrationOrigin); 8310 } 8311 getSourcesWithDifferentDestinations( int numSources, boolean hasAppDestinations, boolean hasWebDestinations, long eventTime, Uri publisher, String enrollmentId, @Source.Status int sourceStatus)8312 private static List<Source> getSourcesWithDifferentDestinations( 8313 int numSources, 8314 boolean hasAppDestinations, 8315 boolean hasWebDestinations, 8316 long eventTime, 8317 Uri publisher, 8318 String enrollmentId, 8319 @Source.Status int sourceStatus) { 8320 long expiryTime = 8321 eventTime 8322 + TimeUnit.SECONDS.toMillis( 8323 MEASUREMENT_MAX_REPORTING_REGISTER_SOURCE_EXPIRATION_IN_SECONDS); 8324 return getSourcesWithDifferentDestinations( 8325 numSources, 8326 hasAppDestinations, 8327 hasWebDestinations, 8328 eventTime, 8329 expiryTime, 8330 publisher, 8331 enrollmentId, 8332 sourceStatus, 8333 REGISTRATION_ORIGIN); 8334 } 8335 getSourcesWithDifferentDestinations( int numSources, boolean hasAppDestinations, boolean hasWebDestinations, long eventTime, long expiryTime, Uri publisher, String enrollmentId, @Source.Status int sourceStatus, Uri registrationOrigin)8336 private static List<Source> getSourcesWithDifferentDestinations( 8337 int numSources, 8338 boolean hasAppDestinations, 8339 boolean hasWebDestinations, 8340 long eventTime, 8341 long expiryTime, 8342 Uri publisher, 8343 String enrollmentId, 8344 @Source.Status int sourceStatus, 8345 Uri registrationOrigin) { 8346 List<Source> sources = new ArrayList<>(); 8347 for (int i = 0; i < numSources; i++) { 8348 Source.Builder sourceBuilder = 8349 new Source.Builder() 8350 .setEventId(new UnsignedLong(0L)) 8351 .setEventTime(eventTime) 8352 .setExpiryTime(expiryTime) 8353 .setPublisher(publisher) 8354 .setEnrollmentId(enrollmentId) 8355 .setRegistrant(SourceFixture.ValidSourceParams.REGISTRANT) 8356 .setStatus(sourceStatus) 8357 .setRegistrationOrigin(registrationOrigin); 8358 if (hasAppDestinations) { 8359 sourceBuilder.setAppDestinations( 8360 List.of(Uri.parse("android-app://app-destination-" + String.valueOf(i)))); 8361 } 8362 if (hasWebDestinations) { 8363 sourceBuilder.setWebDestinations( 8364 List.of( 8365 Uri.parse( 8366 "https://web-destination-" + String.valueOf(i) + ".com"))); 8367 } 8368 sources.add(sourceBuilder.build()); 8369 } 8370 return sources; 8371 } 8372 getSourcesWithDifferentRegistrationOrigins( int numSources, List<Uri> appDestinations, List<Uri> webDestinations, long eventTime, Uri publisher, @Source.Status int sourceStatus)8373 private static List<Source> getSourcesWithDifferentRegistrationOrigins( 8374 int numSources, 8375 List<Uri> appDestinations, 8376 List<Uri> webDestinations, 8377 long eventTime, 8378 Uri publisher, 8379 @Source.Status int sourceStatus) { 8380 long expiryTime = 8381 eventTime 8382 + TimeUnit.SECONDS.toMillis( 8383 MEASUREMENT_MAX_REPORTING_REGISTER_SOURCE_EXPIRATION_IN_SECONDS); 8384 return getSourcesWithDifferentRegistrationOrigins( 8385 numSources, 8386 appDestinations, 8387 webDestinations, 8388 eventTime, 8389 expiryTime, 8390 publisher, 8391 sourceStatus); 8392 } 8393 getSourcesWithDifferentRegistrationOrigins( int numSources, List<Uri> appDestinations, List<Uri> webDestinations, long eventTime, long expiryTime, Uri publisher, @Source.Status int sourceStatus)8394 private static List<Source> getSourcesWithDifferentRegistrationOrigins( 8395 int numSources, 8396 List<Uri> appDestinations, 8397 List<Uri> webDestinations, 8398 long eventTime, 8399 long expiryTime, 8400 Uri publisher, 8401 @Source.Status int sourceStatus) { 8402 List<Source> sources = new ArrayList<>(); 8403 for (int i = 0; i < numSources; i++) { 8404 Source.Builder sourceBuilder = 8405 new Source.Builder() 8406 .setEventId(new UnsignedLong(0L)) 8407 .setEventTime(eventTime) 8408 .setExpiryTime(expiryTime) 8409 .setPublisher(publisher) 8410 .setRegistrant(SourceFixture.ValidSourceParams.REGISTRANT) 8411 .setStatus(sourceStatus) 8412 .setAppDestinations(getNullableUriList(appDestinations)) 8413 .setWebDestinations(getNullableUriList(webDestinations)) 8414 .setEnrollmentId("enrollment-id") 8415 .setRegistrationOrigin( 8416 WebUtil.validUri("https://subdomain" + i + ".example.test")); 8417 sources.add(sourceBuilder.build()); 8418 } 8419 return sources; 8420 } 8421 getAttributionsWithDifferentReportingOrigins( int numAttributions, Uri destinationSite, long triggerTime, Uri sourceSite, String registrant)8422 private static List<Attribution> getAttributionsWithDifferentReportingOrigins( 8423 int numAttributions, 8424 Uri destinationSite, 8425 long triggerTime, 8426 Uri sourceSite, 8427 String registrant) { 8428 List<Attribution> attributions = new ArrayList<>(); 8429 for (int i = 0; i < numAttributions; i++) { 8430 Attribution.Builder attributionBuilder = 8431 new Attribution.Builder() 8432 .setTriggerTime(triggerTime) 8433 .setSourceSite(sourceSite.toString()) 8434 .setSourceOrigin(sourceSite.toString()) 8435 .setDestinationSite(destinationSite.toString()) 8436 .setDestinationOrigin(destinationSite.toString()) 8437 .setEnrollmentId("enrollment-id") 8438 .setRegistrationOrigin( 8439 WebUtil.validUri("https://subdomain" + i + ".example.test")) 8440 .setRegistrant(registrant); 8441 attributions.add(attributionBuilder.build()); 8442 } 8443 return attributions; 8444 } 8445 insertAttribution(Attribution attribution)8446 private static void insertAttribution(Attribution attribution) { 8447 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 8448 ContentValues values = new ContentValues(); 8449 values.put(AttributionContract.ID, UUID.randomUUID().toString()); 8450 values.put(AttributionContract.SOURCE_SITE, attribution.getSourceSite()); 8451 values.put(AttributionContract.DESTINATION_SITE, attribution.getDestinationSite()); 8452 values.put(AttributionContract.ENROLLMENT_ID, attribution.getEnrollmentId()); 8453 values.put(AttributionContract.TRIGGER_TIME, attribution.getTriggerTime()); 8454 values.put(AttributionContract.SOURCE_ID, attribution.getSourceId()); 8455 values.put(AttributionContract.TRIGGER_ID, attribution.getTriggerId()); 8456 values.put( 8457 AttributionContract.REGISTRATION_ORIGIN, 8458 attribution.getRegistrationOrigin().toString()); 8459 long row = db.insert("msmt_attribution", null, values); 8460 assertNotEquals("Attribution insertion failed", -1, row); 8461 } 8462 createAttribution( String attributionId, String sourceId, String triggerId)8463 private static Attribution createAttribution( 8464 String attributionId, 8465 String sourceId, 8466 String triggerId) { 8467 return createAttribution(attributionId, Attribution.Scope.EVENT, sourceId, triggerId); 8468 } 8469 createAttribution( String attributionId, @Attribution.Scope int scope, String sourceId, String triggerId)8470 private static Attribution createAttribution( 8471 String attributionId, 8472 @Attribution.Scope int scope, 8473 String sourceId, 8474 String triggerId) { 8475 return createAttribution(attributionId, scope, sourceId, triggerId, null); 8476 } 8477 createAttribution( String attributionId, @Attribution.Scope int scope, String sourceId, String triggerId, String reportId)8478 private static Attribution createAttribution( 8479 String attributionId, 8480 @Attribution.Scope int scope, 8481 String sourceId, 8482 String triggerId, 8483 String reportId) { 8484 return new Attribution.Builder() 8485 .setId(attributionId) 8486 .setScope(scope) 8487 .setTriggerTime(0L) 8488 .setSourceSite("android-app://source.app") 8489 .setSourceOrigin("android-app://source.app") 8490 .setDestinationSite("android-app://destination.app") 8491 .setDestinationOrigin("android-app://destination.app") 8492 .setEnrollmentId("enrollment-id-") 8493 .setRegistrant("android-app://registrant.app") 8494 .setSourceId(sourceId) 8495 .setTriggerId(triggerId) 8496 .setRegistrationOrigin(REGISTRATION_ORIGIN) 8497 .setReportId(reportId) 8498 .build(); 8499 } 8500 insertSource(Source source)8501 private static void insertSource(Source source) { 8502 insertSource(source, UUID.randomUUID().toString()); 8503 } 8504 8505 // This is needed because MeasurementDao::insertSource inserts a default value for status. insertSource(Source source, String sourceId)8506 private static void insertSource(Source source, String sourceId) { 8507 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 8508 ContentValues values = new ContentValues(); 8509 values.put(SourceContract.ID, sourceId); 8510 if (source.getEventId() != null) { 8511 values.put(SourceContract.EVENT_ID, source.getEventId().getValue()); 8512 } 8513 values.put(SourceContract.PUBLISHER, source.getPublisher().toString()); 8514 values.put(SourceContract.PUBLISHER_TYPE, source.getPublisherType()); 8515 values.put(SourceContract.ENROLLMENT_ID, source.getEnrollmentId()); 8516 values.put(SourceContract.EVENT_TIME, source.getEventTime()); 8517 values.put(SourceContract.EXPIRY_TIME, source.getExpiryTime()); 8518 values.put(SourceContract.PRIORITY, source.getPriority()); 8519 values.put(SourceContract.STATUS, source.getStatus()); 8520 values.put(SourceContract.SOURCE_TYPE, source.getSourceType().toString()); 8521 values.put(SourceContract.REGISTRANT, source.getRegistrant().toString()); 8522 values.put(SourceContract.INSTALL_ATTRIBUTION_WINDOW, source.getInstallAttributionWindow()); 8523 values.put(SourceContract.INSTALL_COOLDOWN_WINDOW, source.getInstallCooldownWindow()); 8524 values.put(SourceContract.ATTRIBUTION_MODE, source.getAttributionMode()); 8525 values.put(SourceContract.AGGREGATE_SOURCE, source.getAggregateSource()); 8526 values.put(SourceContract.FILTER_DATA, source.getFilterDataString()); 8527 values.put(SourceContract.SHARED_FILTER_DATA_KEYS, source.getSharedFilterDataKeys()); 8528 values.put(SourceContract.AGGREGATE_CONTRIBUTIONS, source.getAggregateContributions()); 8529 values.put(SourceContract.DEBUG_REPORTING, source.isDebugReporting()); 8530 values.put(SourceContract.INSTALL_TIME, source.getInstallTime()); 8531 values.put(SourceContract.REGISTRATION_ID, source.getRegistrationId()); 8532 values.put(SourceContract.SHARED_AGGREGATION_KEYS, source.getSharedAggregationKeys()); 8533 values.put(SourceContract.REGISTRATION_ORIGIN, source.getRegistrationOrigin().toString()); 8534 values.put(SourceContract.DESTINATION_LIMIT_PRIORITY, source.getDestinationLimitPriority()); 8535 values.put(SourceContract.IS_INSTALL_ATTRIBUTED, source.isInstallAttributed()); 8536 values.put( 8537 SourceContract.AGGREGATE_DEBUG_REPORT_CONTRIBUTIONS, 8538 source.getAggregateDebugReportContributions()); 8539 values.put( 8540 SourceContract.REINSTALL_REATTRIBUTION_WINDOW, 8541 source.getReinstallReattributionWindow()); 8542 long row = db.insert(SourceContract.TABLE, null, values); 8543 assertNotEquals("Source insertion failed", -1, row); 8544 8545 maybeInsertSourceDestinations(db, source, sourceId); 8546 } 8547 getNullableUriString(List<Uri> uriList)8548 private static String getNullableUriString(List<Uri> uriList) { 8549 return Optional.ofNullable(uriList).map(uris -> uris.get(0).toString()).orElse(null); 8550 } 8551 8552 /** Test that the AsyncRegistration is inserted correctly. */ 8553 @Test testInsertAsyncRegistration()8554 public void testInsertAsyncRegistration() { 8555 AsyncRegistration validAsyncRegistration = 8556 AsyncRegistrationFixture.getValidAsyncRegistration(); 8557 String validAsyncRegistrationId = validAsyncRegistration.getId(); 8558 8559 mDatastoreManager.runInTransaction( 8560 (dao) -> dao.insertAsyncRegistration(validAsyncRegistration)); 8561 8562 try (Cursor cursor = 8563 MeasurementDbHelper.getInstance() 8564 .getReadableDatabase() 8565 .query( 8566 AsyncRegistrationContract.TABLE, 8567 null, 8568 AsyncRegistrationContract.ID + " = ? ", 8569 new String[] {validAsyncRegistrationId}, 8570 null, 8571 null, 8572 null)) { 8573 8574 assertTrue(cursor.moveToNext()); 8575 AsyncRegistration asyncRegistration = 8576 SqliteObjectMapper.constructAsyncRegistration(cursor); 8577 assertNotNull(asyncRegistration); 8578 assertNotNull(asyncRegistration.getId()); 8579 assertEquals(asyncRegistration.getId(), validAsyncRegistration.getId()); 8580 assertNotNull(asyncRegistration.getRegistrationUri()); 8581 assertNotNull(asyncRegistration.getTopOrigin()); 8582 assertEquals(asyncRegistration.getTopOrigin(), validAsyncRegistration.getTopOrigin()); 8583 assertNotNull(asyncRegistration.getRegistrant()); 8584 assertEquals(asyncRegistration.getRegistrant(), validAsyncRegistration.getRegistrant()); 8585 assertNotNull(asyncRegistration.getSourceType()); 8586 assertEquals(asyncRegistration.getSourceType(), validAsyncRegistration.getSourceType()); 8587 assertEquals( 8588 asyncRegistration.getDebugKeyAllowed(), 8589 validAsyncRegistration.getDebugKeyAllowed()); 8590 assertEquals(asyncRegistration.getRetryCount(), validAsyncRegistration.getRetryCount()); 8591 assertEquals( 8592 asyncRegistration.getRequestTime(), validAsyncRegistration.getRequestTime()); 8593 assertNotNull(asyncRegistration.getOsDestination()); 8594 assertEquals( 8595 asyncRegistration.getOsDestination(), 8596 validAsyncRegistration.getOsDestination()); 8597 assertNotNull(asyncRegistration.getRegistrationUri()); 8598 assertEquals( 8599 asyncRegistration.getRegistrationUri(), 8600 validAsyncRegistration.getRegistrationUri()); 8601 assertEquals( 8602 asyncRegistration.getDebugKeyAllowed(), 8603 validAsyncRegistration.getDebugKeyAllowed()); 8604 assertEquals( 8605 asyncRegistration.getPlatformAdId(), validAsyncRegistration.getPlatformAdId()); 8606 assertEquals(asyncRegistration.getPostBody(), validAsyncRegistration.getPostBody()); 8607 assertEquals( 8608 asyncRegistration.getRedirectBehavior(), 8609 validAsyncRegistration.getRedirectBehavior()); 8610 } 8611 } 8612 8613 /** Test that records in AsyncRegistration queue are fetched properly. */ 8614 @Test testFetchNextQueuedAsyncRegistration_validRetryLimit()8615 public void testFetchNextQueuedAsyncRegistration_validRetryLimit() { 8616 AsyncRegistration asyncRegistration = AsyncRegistrationFixture.getValidAsyncRegistration(); 8617 String asyncRegistrationId = asyncRegistration.getId(); 8618 8619 mDatastoreManager.runInTransaction((dao) -> dao.insertAsyncRegistration(asyncRegistration)); 8620 mDatastoreManager.runInTransaction( 8621 (dao) -> { 8622 AsyncRegistration fetchedAsyncRegistration = 8623 dao.fetchNextQueuedAsyncRegistration((short) 1, new HashSet<>()); 8624 assertNotNull(fetchedAsyncRegistration); 8625 assertEquals(fetchedAsyncRegistration.getId(), asyncRegistrationId); 8626 fetchedAsyncRegistration.incrementRetryCount(); 8627 dao.updateRetryCount(fetchedAsyncRegistration); 8628 }); 8629 8630 mDatastoreManager.runInTransaction( 8631 (dao) -> { 8632 AsyncRegistration fetchedAsyncRegistration = 8633 dao.fetchNextQueuedAsyncRegistration((short) 1, new HashSet<>()); 8634 assertNull(fetchedAsyncRegistration); 8635 }); 8636 } 8637 8638 /** Test that records in AsyncRegistration queue are fetched properly. */ 8639 @Test testFetchNextQueuedAsyncRegistration_excludeByOrigin()8640 public void testFetchNextQueuedAsyncRegistration_excludeByOrigin() { 8641 Uri origin1 = Uri.parse("https://adtech1.test"); 8642 Uri origin2 = Uri.parse("https://adtech2.test"); 8643 Uri regUri1 = origin1.buildUpon().appendPath("/hello").build(); 8644 Uri regUri2 = origin2; 8645 AsyncRegistration asyncRegistration1 = 8646 AsyncRegistrationFixture.getValidAsyncRegistrationBuilder() 8647 .setRegistrationUri(regUri1) 8648 .build(); 8649 AsyncRegistration asyncRegistration2 = 8650 AsyncRegistrationFixture.getValidAsyncRegistrationBuilder() 8651 .setRegistrationUri(regUri2) 8652 .build(); 8653 8654 mDatastoreManager.runInTransaction( 8655 (dao) -> { 8656 dao.insertAsyncRegistration(asyncRegistration1); 8657 dao.insertAsyncRegistration(asyncRegistration2); 8658 }); 8659 // Should fetch none 8660 Set<Uri> excludedOrigins1 = Set.of(origin1, origin2); 8661 Optional<AsyncRegistration> optAsyncRegistration = 8662 mDatastoreManager.runInTransactionWithResult( 8663 (dao) -> dao.fetchNextQueuedAsyncRegistration((short) 4, excludedOrigins1)); 8664 assertTrue(optAsyncRegistration.isEmpty()); 8665 8666 // Should fetch only origin1 8667 Set<Uri> excludedOrigins2 = Set.of(origin2); 8668 optAsyncRegistration = 8669 mDatastoreManager.runInTransactionWithResult( 8670 (dao) -> dao.fetchNextQueuedAsyncRegistration((short) 4, excludedOrigins2)); 8671 assertTrue(optAsyncRegistration.isPresent()); 8672 assertEquals(regUri1, optAsyncRegistration.get().getRegistrationUri()); 8673 8674 // Should fetch only origin2 8675 Set<Uri> excludedOrigins3 = Set.of(origin1); 8676 optAsyncRegistration = 8677 mDatastoreManager.runInTransactionWithResult( 8678 (dao) -> dao.fetchNextQueuedAsyncRegistration((short) 4, excludedOrigins3)); 8679 assertTrue(optAsyncRegistration.isPresent()); 8680 assertEquals(regUri2, optAsyncRegistration.get().getRegistrationUri()); 8681 } 8682 8683 /** Test that AsyncRegistration is deleted correctly. */ 8684 @Test testDeleteAsyncRegistration()8685 public void testDeleteAsyncRegistration() { 8686 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 8687 AsyncRegistration asyncRegistration = AsyncRegistrationFixture.getValidAsyncRegistration(); 8688 String asyncRegistrationID = asyncRegistration.getId(); 8689 8690 mDatastoreManager.runInTransaction((dao) -> dao.insertAsyncRegistration(asyncRegistration)); 8691 try (Cursor cursor = 8692 MeasurementDbHelper.getInstance() 8693 .getReadableDatabase() 8694 .query( 8695 AsyncRegistrationContract.TABLE, 8696 null, 8697 AsyncRegistrationContract.ID + " = ? ", 8698 new String[] {asyncRegistration.getId().toString()}, 8699 null, 8700 null, 8701 null)) { 8702 assertTrue(cursor.moveToNext()); 8703 AsyncRegistration updateAsyncRegistration = 8704 SqliteObjectMapper.constructAsyncRegistration(cursor); 8705 assertNotNull(updateAsyncRegistration); 8706 } 8707 mDatastoreManager.runInTransaction( 8708 (dao) -> dao.deleteAsyncRegistration(asyncRegistration.getId())); 8709 8710 db.query( 8711 /* table */ AsyncRegistrationContract.TABLE, 8712 /* columns */ null, 8713 /* selection */ AsyncRegistrationContract.ID + " = ? ", 8714 /* selectionArgs */ new String[] {asyncRegistrationID.toString()}, 8715 /* groupBy */ null, 8716 /* having */ null, 8717 /* orderedBy */ null); 8718 8719 assertThat( 8720 db.query( 8721 /* table */ AsyncRegistrationContract.TABLE, 8722 /* columns */ null, 8723 /* selection */ AsyncRegistrationContract.ID + " = ? ", 8724 /* selectionArgs */ new String[] { 8725 asyncRegistrationID.toString() 8726 }, 8727 /* groupBy */ null, 8728 /* having */ null, 8729 /* orderedBy */ null) 8730 .getCount()) 8731 .isEqualTo(0); 8732 } 8733 8734 @Test testDeleteAsyncRegistration_missingRecord()8735 public void testDeleteAsyncRegistration_missingRecord() { 8736 mDatastoreManager.runInTransaction( 8737 (dao) -> 8738 assertThrows( 8739 "Async Registration already deleted", 8740 DatastoreException.class, 8741 () -> dao.deleteAsyncRegistration("missingAsyncRegId"))); 8742 } 8743 8744 @Test deleteAsyncRegistrations_success()8745 public void deleteAsyncRegistrations_success() { 8746 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 8747 AsyncRegistration ar1 = 8748 new AsyncRegistration.Builder() 8749 .setId("1") 8750 .setRegistrant(Uri.parse("android-app://installed-registrant1")) 8751 .setTopOrigin(Uri.parse("android-app://installed-registrant1")) 8752 .setAdIdPermission(false) 8753 .setType(AsyncRegistration.RegistrationType.APP_SOURCE) 8754 .setRequestTime(1) 8755 .setRegistrationId(ValidAsyncRegistrationParams.REGISTRATION_ID) 8756 .build(); 8757 8758 AsyncRegistration ar2 = 8759 new AsyncRegistration.Builder() 8760 .setId("2") 8761 .setRegistrant(Uri.parse("android-app://installed-registrant2")) 8762 .setTopOrigin(Uri.parse("android-app://installed-registrant2")) 8763 .setAdIdPermission(false) 8764 .setType(AsyncRegistration.RegistrationType.APP_SOURCE) 8765 .setRequestTime(Long.MAX_VALUE) 8766 .setRegistrationId(ValidAsyncRegistrationParams.REGISTRATION_ID) 8767 .build(); 8768 8769 AsyncRegistration ar3 = 8770 new AsyncRegistration.Builder() 8771 .setId("3") 8772 .setRegistrant(Uri.parse("android-app://installed-registrant3")) 8773 .setTopOrigin(Uri.parse("android-app://installed-registrant3")) 8774 .setAdIdPermission(false) 8775 .setType(AsyncRegistration.RegistrationType.APP_SOURCE) 8776 .setRequestTime(Long.MAX_VALUE) 8777 .setRegistrationId(ValidAsyncRegistrationParams.REGISTRATION_ID) 8778 .build(); 8779 8780 List<AsyncRegistration> asyncRegistrationList = List.of(ar1, ar2, ar3); 8781 asyncRegistrationList.forEach( 8782 asyncRegistration -> { 8783 ContentValues values = new ContentValues(); 8784 values.put(AsyncRegistrationContract.ID, asyncRegistration.getId()); 8785 values.put( 8786 AsyncRegistrationContract.REQUEST_TIME, 8787 asyncRegistration.getRequestTime()); 8788 values.put( 8789 AsyncRegistrationContract.REGISTRANT, 8790 asyncRegistration.getRegistrant().toString()); 8791 values.put( 8792 AsyncRegistrationContract.TOP_ORIGIN, 8793 asyncRegistration.getTopOrigin().toString()); 8794 values.put( 8795 AsyncRegistrationContract.REGISTRATION_ID, 8796 asyncRegistration.getRegistrationId()); 8797 db.insert(AsyncRegistrationContract.TABLE, /* nullColumnHack */ null, values); 8798 }); 8799 8800 mDatastoreManager.runInTransaction( 8801 (dao) -> dao.deleteAsyncRegistrations(List.of("1", "3"))); 8802 8803 assertThat( 8804 db.query( 8805 /* table */ AsyncRegistrationContract.TABLE, 8806 /* columns */ null, 8807 /* selection */ null, 8808 /* selectionArgs */ null, 8809 /* groupBy */ null, 8810 /* having */ null, 8811 /* orderedBy */ null) 8812 .getCount()) 8813 .isEqualTo(1); 8814 8815 assertThat( 8816 db.query( 8817 /* table */ AsyncRegistrationContract.TABLE, 8818 /* columns */ null, 8819 /* selection */ AsyncRegistrationContract.ID + " = ? ", 8820 /* selectionArgs */ new String[] {"2"}, 8821 /* groupBy */ null, 8822 /* having */ null, 8823 /* orderedBy */ null) 8824 .getCount()) 8825 .isEqualTo(1); 8826 } 8827 8828 /** Test that retry count in AsyncRegistration is updated correctly. */ 8829 @Test testUpdateAsyncRegistrationRetryCount()8830 public void testUpdateAsyncRegistrationRetryCount() { 8831 AsyncRegistration asyncRegistration = AsyncRegistrationFixture.getValidAsyncRegistration(); 8832 String asyncRegistrationId = asyncRegistration.getId(); 8833 long originalRetryCount = asyncRegistration.getRetryCount(); 8834 8835 mDatastoreManager.runInTransaction((dao) -> dao.insertAsyncRegistration(asyncRegistration)); 8836 mDatastoreManager.runInTransaction( 8837 (dao) -> { 8838 asyncRegistration.incrementRetryCount(); 8839 dao.updateRetryCount(asyncRegistration); 8840 }); 8841 8842 try (Cursor cursor = 8843 MeasurementDbHelper.getInstance() 8844 .getReadableDatabase() 8845 .query( 8846 AsyncRegistrationContract.TABLE, 8847 null, 8848 AsyncRegistrationContract.ID + " = ? ", 8849 new String[] {asyncRegistrationId}, 8850 null, 8851 null, 8852 null)) { 8853 assertTrue(cursor.moveToNext()); 8854 AsyncRegistration updateAsyncRegistration = 8855 SqliteObjectMapper.constructAsyncRegistration(cursor); 8856 assertNotNull(updateAsyncRegistration); 8857 assertTrue(updateAsyncRegistration.getRetryCount() == originalRetryCount + 1); 8858 } 8859 } 8860 8861 @Test getSource_fetchesMatchingSourceFromDb()8862 public void getSource_fetchesMatchingSourceFromDb() { 8863 // Setup - insert 2 sources with different IDs 8864 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 8865 String sourceId1 = "source1"; 8866 Source source1WithoutDestinations = 8867 SourceFixture.getMinimalValidSourceBuilder() 8868 .setId(sourceId1) 8869 .setAppDestinations(null) 8870 .setWebDestinations(null) 8871 .build(); 8872 Source source1WithDestinations = 8873 SourceFixture.getMinimalValidSourceBuilder() 8874 .setId(sourceId1) 8875 .setAppDestinations(null) 8876 .setWebDestinations(null) 8877 .build(); 8878 insertInDb(db, source1WithDestinations); 8879 String sourceId2 = "source2"; 8880 Source source2WithoutDestinations = 8881 SourceFixture.getMinimalValidSourceBuilder() 8882 .setId(sourceId2) 8883 .setAppDestinations(null) 8884 .setWebDestinations(null) 8885 .build(); 8886 Source source2WithDestinations = 8887 SourceFixture.getMinimalValidSourceBuilder() 8888 .setId(sourceId2) 8889 .setAppDestinations(null) 8890 .setWebDestinations(null) 8891 .build(); 8892 insertInDb(db, source2WithDestinations); 8893 8894 // Execution 8895 mDatastoreManager.runInTransaction( 8896 (dao) -> { 8897 assertEquals(source1WithoutDestinations, dao.getSource(sourceId1)); 8898 assertEquals(source2WithoutDestinations, dao.getSource(sourceId2)); 8899 }); 8900 } 8901 8902 @Test getSourceRegistrant_fetchesMatchingSourceFromDb()8903 public void getSourceRegistrant_fetchesMatchingSourceFromDb() { 8904 // Setup - insert 2 sources with different IDs 8905 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 8906 String sourceId1 = "source1"; 8907 String registrant1 = "android-app://registrant.app1"; 8908 Source source1WithDestinations = 8909 SourceFixture.getMinimalValidSourceBuilder() 8910 .setId(sourceId1) 8911 .setAppDestinations(null) 8912 .setWebDestinations(null) 8913 .setRegistrant(Uri.parse(registrant1)) 8914 .build(); 8915 insertInDb(db, source1WithDestinations); 8916 8917 String sourceId2 = "source2"; 8918 String registrant2 = "android-app://registrant.app1"; 8919 Source source2WithDestinations = 8920 SourceFixture.getMinimalValidSourceBuilder() 8921 .setId(sourceId2) 8922 .setAppDestinations(null) 8923 .setWebDestinations(null) 8924 .setRegistrant(Uri.parse(registrant2)) 8925 .build(); 8926 insertInDb(db, source2WithDestinations); 8927 8928 // Execution 8929 mDatastoreManager.runInTransaction( 8930 (dao) -> { 8931 assertEquals(registrant1, dao.getSourceRegistrant(sourceId1)); 8932 assertEquals(registrant2, dao.getSourceRegistrant(sourceId2)); 8933 }); 8934 } 8935 8936 @Test getSource_nonExistingInDb_throwsException()8937 public void getSource_nonExistingInDb_throwsException() { 8938 // Setup - insert 2 sources with different IDs 8939 mFlags = mock(Flags.class); 8940 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 8941 doReturn(true).when(mFlags).getMeasurementEnableDatastoreManagerThrowDatastoreException(); 8942 doReturn(1.0f).when(mFlags).getMeasurementThrowUnknownExceptionSamplingRate(); 8943 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 8944 String sourceId1 = "source1"; 8945 Source source1WithDestinations = 8946 SourceFixture.getMinimalValidSourceBuilder() 8947 .setId(sourceId1) 8948 .setAppDestinations(null) 8949 .setWebDestinations(null) 8950 .build(); 8951 insertInDb(db, source1WithDestinations); 8952 8953 // Execution 8954 try { 8955 mDatastoreManager.runInTransaction( 8956 (dao) -> { 8957 dao.getSource("random_source_id"); 8958 }); 8959 fail(); 8960 } catch (IllegalStateException e) { 8961 Throwable cause = e.getCause(); 8962 assertEquals(DatastoreException.class, cause.getClass()); 8963 assertEquals("Source retrieval failed. Id: random_source_id", cause.getMessage()); 8964 } 8965 } 8966 8967 @Test getSource_nonExistingInDbNoSampling_swallowException()8968 public void getSource_nonExistingInDbNoSampling_swallowException() { 8969 // Setup - insert 2 sources with different IDs 8970 mFlags = mock(Flags.class); 8971 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 8972 doReturn(true).when(mFlags).getMeasurementEnableDatastoreManagerThrowDatastoreException(); 8973 doReturn(0.0f).when(mFlags).getMeasurementThrowUnknownExceptionSamplingRate(); 8974 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 8975 String sourceId1 = "source1"; 8976 Source source1WithDestinations = 8977 SourceFixture.getMinimalValidSourceBuilder() 8978 .setId(sourceId1) 8979 .setAppDestinations(null) 8980 .setWebDestinations(null) 8981 .build(); 8982 insertInDb(db, source1WithDestinations); 8983 8984 // Execution 8985 assertFalse( 8986 mDatastoreManager.runInTransaction( 8987 (dao) -> { 8988 dao.getSource("random_source_id"); 8989 })); 8990 } 8991 8992 @Test getSource_nonExistingInDbThrowingDisabled_swallowException()8993 public void getSource_nonExistingInDbThrowingDisabled_swallowException() { 8994 // Setup - insert 2 sources with different IDs 8995 mFlags = mock(Flags.class); 8996 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 8997 doReturn(false).when(mFlags).getMeasurementEnableDatastoreManagerThrowDatastoreException(); 8998 doReturn(1.0f).when(mFlags).getMeasurementThrowUnknownExceptionSamplingRate(); 8999 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 9000 String sourceId1 = "source1"; 9001 Source source1WithDestinations = 9002 SourceFixture.getMinimalValidSourceBuilder() 9003 .setId(sourceId1) 9004 .setAppDestinations(null) 9005 .setWebDestinations(null) 9006 .build(); 9007 insertInDb(db, source1WithDestinations); 9008 9009 // Execution 9010 assertFalse( 9011 mDatastoreManager.runInTransaction( 9012 (dao) -> { 9013 dao.getSource("random_source_id"); 9014 })); 9015 } 9016 9017 @Test fetchMatchingAggregateReports_returnsMatchingReports()9018 public void fetchMatchingAggregateReports_returnsMatchingReports() { 9019 // setup - create reports for 3*3 combinations of source and trigger 9020 List<Source> sources = 9021 Arrays.asList( 9022 SourceFixture.getMinimalValidSourceBuilder() 9023 .setEventId(new UnsignedLong(1L)) 9024 .setId("source1") 9025 .build(), 9026 SourceFixture.getMinimalValidSourceBuilder() 9027 .setEventId(new UnsignedLong(2L)) 9028 .setId("source2") 9029 .build(), 9030 SourceFixture.getMinimalValidSourceBuilder() 9031 .setEventId(new UnsignedLong(3L)) 9032 .setId("source3") 9033 .build()); 9034 List<Trigger> triggers = 9035 Arrays.asList( 9036 TriggerFixture.getValidTriggerBuilder().setId("trigger1").build(), 9037 TriggerFixture.getValidTriggerBuilder().setId("trigger2").build(), 9038 TriggerFixture.getValidTriggerBuilder().setId("trigger3").build()); 9039 List<AggregateReport> reports = 9040 ImmutableList.of( 9041 createAggregateReportForSourceAndTrigger(sources.get(0), triggers.get(0)), 9042 createAggregateReportForSourceAndTrigger(sources.get(0), triggers.get(1)), 9043 createAggregateReportForSourceAndTrigger(sources.get(0), triggers.get(2)), 9044 createAggregateReportForSourceAndTrigger(sources.get(1), triggers.get(0)), 9045 createAggregateReportForSourceAndTrigger(sources.get(1), triggers.get(1)), 9046 createAggregateReportForSourceAndTrigger(sources.get(1), triggers.get(2)), 9047 createAggregateReportForSourceAndTrigger(sources.get(2), triggers.get(0)), 9048 createAggregateReportForSourceAndTrigger(sources.get(2), triggers.get(1)), 9049 createAggregateReportForSourceAndTrigger(sources.get(2), triggers.get(2))); 9050 9051 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 9052 sources.forEach(source -> insertSource(source, source.getId())); 9053 triggers.forEach(trigger -> AbstractDbIntegrationTest.insertToDb(trigger, db)); 9054 reports.forEach( 9055 report -> 9056 mDatastoreManager.runInTransaction( 9057 (dao) -> dao.insertAggregateReport(report))); 9058 9059 mDatastoreManager.runInTransaction( 9060 (dao) -> { 9061 // Execution 9062 List<AggregateReport> aggregateReports = 9063 dao.fetchMatchingAggregateReports( 9064 Arrays.asList(sources.get(1).getId(), "nonMatchingSource"), 9065 Arrays.asList(triggers.get(2).getId(), "nonMatchingTrigger")); 9066 assertEquals(5, aggregateReports.size()); 9067 9068 aggregateReports = 9069 dao.fetchMatchingAggregateReports( 9070 Arrays.asList(sources.get(0).getId(), sources.get(1).getId()), 9071 Collections.emptyList()); 9072 assertEquals(6, aggregateReports.size()); 9073 9074 aggregateReports = 9075 dao.fetchMatchingAggregateReports( 9076 Collections.emptyList(), 9077 Arrays.asList( 9078 triggers.get(0).getId(), triggers.get(2).getId())); 9079 assertEquals(6, aggregateReports.size()); 9080 9081 aggregateReports = 9082 dao.fetchMatchingAggregateReports( 9083 Arrays.asList( 9084 sources.get(0).getId(), 9085 sources.get(1).getId(), 9086 sources.get(2).getId()), 9087 Arrays.asList( 9088 triggers.get(0).getId(), 9089 triggers.get(1).getId(), 9090 triggers.get(2).getId())); 9091 assertEquals(9, aggregateReports.size()); 9092 }); 9093 } 9094 9095 @Test fetchMatchingEventReports_returnsMatchingReports()9096 public void fetchMatchingEventReports_returnsMatchingReports() throws JSONException { 9097 // setup - create reports for 3*3 combinations of source and trigger 9098 List<Source> sources = 9099 Arrays.asList( 9100 SourceFixture.getMinimalValidSourceBuilder() 9101 .setEventId(new UnsignedLong(1L)) 9102 .setId("source1") 9103 .build(), 9104 SourceFixture.getMinimalValidSourceBuilder() 9105 .setEventId(new UnsignedLong(2L)) 9106 .setId("source2") 9107 .build(), 9108 SourceFixture.getMinimalValidSourceBuilder() 9109 .setEventId(new UnsignedLong(3L)) 9110 .setId("source3") 9111 .build()); 9112 List<Trigger> triggers = 9113 Arrays.asList( 9114 TriggerFixture.getValidTriggerBuilder() 9115 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 9116 .setId("trigger1") 9117 .build(), 9118 TriggerFixture.getValidTriggerBuilder() 9119 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 9120 .setId("trigger2") 9121 .build(), 9122 TriggerFixture.getValidTriggerBuilder() 9123 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 9124 .setId("trigger3") 9125 .build()); 9126 List<EventReport> reports = 9127 ImmutableList.of( 9128 createEventReportForSourceAndTrigger(sources.get(0), triggers.get(0)), 9129 createEventReportForSourceAndTrigger(sources.get(0), triggers.get(1)), 9130 createEventReportForSourceAndTrigger(sources.get(0), triggers.get(2)), 9131 createEventReportForSourceAndTrigger(sources.get(1), triggers.get(0)), 9132 createEventReportForSourceAndTrigger(sources.get(1), triggers.get(1)), 9133 createEventReportForSourceAndTrigger(sources.get(1), triggers.get(2)), 9134 createEventReportForSourceAndTrigger(sources.get(2), triggers.get(0)), 9135 createEventReportForSourceAndTrigger(sources.get(2), triggers.get(1)), 9136 createEventReportForSourceAndTrigger(sources.get(2), triggers.get(2))); 9137 9138 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 9139 sources.forEach(source -> insertSource(source, source.getId())); 9140 triggers.forEach(trigger -> AbstractDbIntegrationTest.insertToDb(trigger, db)); 9141 reports.forEach( 9142 report -> 9143 mDatastoreManager.runInTransaction((dao) -> dao.insertEventReport(report))); 9144 9145 mDatastoreManager.runInTransaction( 9146 (dao) -> { 9147 // Execution 9148 List<EventReport> eventReports = 9149 dao.fetchMatchingEventReports( 9150 Arrays.asList(sources.get(1).getId(), "nonMatchingSource"), 9151 Arrays.asList(triggers.get(2).getId(), "nonMatchingTrigger")); 9152 assertEquals(5, eventReports.size()); 9153 9154 eventReports = 9155 dao.fetchMatchingEventReports( 9156 Arrays.asList(sources.get(0).getId(), sources.get(1).getId()), 9157 Collections.emptyList()); 9158 assertEquals(6, eventReports.size()); 9159 9160 eventReports = 9161 dao.fetchMatchingEventReports( 9162 Collections.emptyList(), 9163 Arrays.asList( 9164 triggers.get(0).getId(), triggers.get(2).getId())); 9165 assertEquals(6, eventReports.size()); 9166 9167 eventReports = 9168 dao.fetchMatchingEventReports( 9169 Arrays.asList( 9170 sources.get(0).getId(), 9171 sources.get(1).getId(), 9172 sources.get(2).getId()), 9173 Arrays.asList( 9174 triggers.get(0).getId(), 9175 triggers.get(1).getId(), 9176 triggers.get(2).getId())); 9177 assertEquals(9, eventReports.size()); 9178 }); 9179 } 9180 9181 @Test fetchMatchingSources_bringsMatchingSources()9182 public void fetchMatchingSources_bringsMatchingSources() { 9183 // Setup 9184 Source source1 = 9185 SourceFixture.getMinimalValidSourceBuilder() 9186 .setEventId(new UnsignedLong(1L)) 9187 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 9188 .setEventTime(5000) 9189 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9190 .setId("source1") 9191 .build(); 9192 Source source2 = 9193 SourceFixture.getMinimalValidSourceBuilder() 9194 .setEventId(new UnsignedLong(2L)) 9195 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 9196 .setEventTime(10000) 9197 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9198 .setId("source2") 9199 .build(); 9200 Source source3 = 9201 SourceFixture.getMinimalValidSourceBuilder() 9202 .setEventId(new UnsignedLong(3L)) 9203 .setPublisher(WebUtil.validUri("https://subdomain2.site1.test")) 9204 .setEventTime(15000) 9205 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9206 .setId("source3") 9207 .build(); 9208 Source source4 = 9209 SourceFixture.getMinimalValidSourceBuilder() 9210 .setEventId(new UnsignedLong(4L)) 9211 .setPublisher(WebUtil.validUri("https://subdomain2.site2.test")) 9212 .setEventTime(15000) 9213 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9214 .setId("source4") 9215 .build(); 9216 Source source5 = 9217 SourceFixture.getMinimalValidSourceBuilder() 9218 .setEventId(new UnsignedLong(5L)) 9219 .setPublisher(WebUtil.validUri("https://subdomain2.site1.test")) 9220 .setEventTime(20000) 9221 .setRegistrant(Uri.parse("android-app://com.registrant2")) 9222 .setId("source5") 9223 .build(); 9224 List<Source> sources = List.of(source1, source2, source3, source4, source5); 9225 9226 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 9227 sources.forEach(source -> insertInDb(db, source)); 9228 9229 // Execution 9230 mDatastoreManager.runInTransaction( 9231 dao -> { 9232 // --- DELETE behaviour --- 9233 // Delete Nothing 9234 // No matches 9235 List<String> actualSources = 9236 dao.fetchMatchingSources( 9237 Uri.parse("android-app://com.registrant1"), 9238 Instant.ofEpochMilli(0), 9239 Instant.ofEpochMilli(50000), 9240 List.of(), 9241 List.of(), 9242 DeletionRequest.MATCH_BEHAVIOR_DELETE); 9243 assertEquals(0, actualSources.size()); 9244 9245 // 1 & 2 match registrant1 and "https://subdomain1.site1.test" publisher 9246 // origin 9247 actualSources = 9248 dao.fetchMatchingSources( 9249 Uri.parse("android-app://com.registrant1"), 9250 Instant.ofEpochMilli(0), 9251 Instant.ofEpochMilli(50000), 9252 List.of(WebUtil.validUri("https://subdomain1.site1.test")), 9253 List.of(), 9254 DeletionRequest.MATCH_BEHAVIOR_DELETE); 9255 assertEquals(2, actualSources.size()); 9256 9257 // Only 2 matches registrant1 and "https://subdomain1.site1.test" 9258 // publisher origin within 9259 // the range 9260 actualSources = 9261 dao.fetchMatchingSources( 9262 Uri.parse("android-app://com.registrant1"), 9263 Instant.ofEpochMilli(8000), 9264 Instant.ofEpochMilli(50000), 9265 List.of(WebUtil.validUri("https://subdomain1.site1.test")), 9266 List.of(), 9267 DeletionRequest.MATCH_BEHAVIOR_DELETE); 9268 assertEquals(1, actualSources.size()); 9269 9270 // 1,2 & 3 matches registrant1 and "https://site1.test" publisher origin 9271 actualSources = 9272 dao.fetchMatchingSources( 9273 Uri.parse("android-app://com.registrant1"), 9274 Instant.ofEpochMilli(0), 9275 Instant.ofEpochMilli(50000), 9276 List.of(), 9277 List.of(WebUtil.validUri("https://site1.test")), 9278 DeletionRequest.MATCH_BEHAVIOR_DELETE); 9279 assertEquals(3, actualSources.size()); 9280 9281 // 3 matches origin and 4 matches domain URI 9282 actualSources = 9283 dao.fetchMatchingSources( 9284 Uri.parse("android-app://com.registrant1"), 9285 Instant.ofEpochMilli(10000), 9286 Instant.ofEpochMilli(20000), 9287 List.of(WebUtil.validUri("https://subdomain2.site1.test")), 9288 List.of(WebUtil.validUri("https://site2.test")), 9289 DeletionRequest.MATCH_BEHAVIOR_DELETE); 9290 assertEquals(2, actualSources.size()); 9291 9292 // --- PRESERVE (anti-match exception registrant) behaviour --- 9293 // Preserve Nothing 9294 // 1,2,3 & 4 are match registrant1 9295 actualSources = 9296 dao.fetchMatchingSources( 9297 Uri.parse("android-app://com.registrant1"), 9298 Instant.ofEpochMilli(0), 9299 Instant.ofEpochMilli(50000), 9300 List.of(), 9301 List.of(), 9302 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 9303 assertEquals(4, actualSources.size()); 9304 9305 // 3 & 4 match registrant1 and don't match 9306 // "https://subdomain1.site1.test" publisher origin 9307 actualSources = 9308 dao.fetchMatchingSources( 9309 Uri.parse("android-app://com.registrant1"), 9310 Instant.ofEpochMilli(0), 9311 Instant.ofEpochMilli(50000), 9312 List.of(WebUtil.validUri("https://subdomain1.site1.test")), 9313 List.of(), 9314 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 9315 assertEquals(2, actualSources.size()); 9316 9317 // 3 & 4 match registrant1, in range and don't match 9318 // "https://subdomain1.site1.test" 9319 actualSources = 9320 dao.fetchMatchingSources( 9321 Uri.parse("android-app://com.registrant1"), 9322 Instant.ofEpochMilli(8000), 9323 Instant.ofEpochMilli(50000), 9324 List.of(WebUtil.validUri("https://subdomain1.site1.test")), 9325 List.of(), 9326 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 9327 assertEquals(2, actualSources.size()); 9328 9329 // Only 4 matches registrant1, in range and don't match 9330 // "https://site1.test" 9331 actualSources = 9332 dao.fetchMatchingSources( 9333 Uri.parse("android-app://com.registrant1"), 9334 Instant.ofEpochMilli(0), 9335 Instant.ofEpochMilli(50000), 9336 List.of(), 9337 List.of(WebUtil.validUri("https://site1.test")), 9338 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 9339 assertEquals(1, actualSources.size()); 9340 9341 // only 2 is registrant1 based, in range and does not match either 9342 // site2.test or subdomain2.site1.test 9343 actualSources = 9344 dao.fetchMatchingSources( 9345 Uri.parse("android-app://com.registrant1"), 9346 Instant.ofEpochMilli(10000), 9347 Instant.ofEpochMilli(20000), 9348 List.of(WebUtil.validUri("https://subdomain2.site1.test")), 9349 List.of(WebUtil.validUri("https://site2.test")), 9350 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 9351 assertEquals(1, actualSources.size()); 9352 }); 9353 } 9354 9355 @Test fetchFlexSourceIdsFor_bringsMatchingSources_expectedSourceReturned()9356 public void fetchFlexSourceIdsFor_bringsMatchingSources_expectedSourceReturned() 9357 throws JSONException { 9358 // Setup 9359 TriggerSpecs testTriggerSpecs = SourceFixture.getValidTriggerSpecsCountBased(); 9360 Source source1 = 9361 SourceFixture.getMinimalValidSourceBuilder() 9362 .setEventId(new UnsignedLong(1L)) 9363 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 9364 .setEventTime(5000) 9365 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9366 .setId("source1") 9367 .setTriggerSpecsString(testTriggerSpecs.encodeToJson()) 9368 .setMaxEventLevelReports(testTriggerSpecs.getMaxReports()) 9369 .setPrivacyParameters( 9370 testTriggerSpecs.encodePrivacyParametersToJsonString()) 9371 .build(); 9372 source1.buildTriggerSpecs(); 9373 Source source2 = 9374 SourceFixture.getMinimalValidSourceBuilder() 9375 .setEventId(new UnsignedLong(2L)) 9376 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 9377 .setEventTime(10000) 9378 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9379 .setId("source2") 9380 .setTriggerSpecsString(testTriggerSpecs.encodeToJson()) 9381 .setMaxEventLevelReports(testTriggerSpecs.getMaxReports()) 9382 .setPrivacyParameters( 9383 testTriggerSpecs.encodePrivacyParametersToJsonString()) 9384 .build(); 9385 source2.buildTriggerSpecs(); 9386 9387 List<TriggerSpecs> triggerSpecsList = List.of( 9388 source1.getTriggerSpecs(), 9389 source2.getTriggerSpecs()); 9390 9391 for (TriggerSpecs triggerSpecs : triggerSpecsList) { 9392 insertAttributedTrigger( 9393 triggerSpecs, 9394 EventReportFixture.getBaseEventReportBuild() 9395 .setTriggerId("123456") 9396 .setTriggerData(new UnsignedLong(2L)) 9397 .setTriggerPriority(1L) 9398 .setTriggerValue(1L) 9399 .build()); 9400 insertAttributedTrigger( 9401 triggerSpecs, 9402 EventReportFixture.getBaseEventReportBuild() 9403 .setTriggerId("234567") 9404 .setTriggerData(new UnsignedLong(2L)) 9405 .setTriggerPriority(1L) 9406 .setTriggerValue(1L) 9407 .build()); 9408 insertAttributedTrigger( 9409 triggerSpecs, 9410 EventReportFixture.getBaseEventReportBuild() 9411 .setTriggerId("345678") 9412 .setTriggerData(new UnsignedLong(2L)) 9413 .setTriggerPriority(1L) 9414 .setTriggerValue(1L) 9415 .build()); 9416 } 9417 9418 Source source3 = 9419 SourceFixture.getMinimalValidSourceBuilder() 9420 .setEventId(new UnsignedLong(2L)) 9421 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 9422 .setEventTime(10000) 9423 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9424 .setId("source3") 9425 .setTriggerSpecsString(testTriggerSpecs.encodeToJson()) 9426 .setMaxEventLevelReports(testTriggerSpecs.getMaxReports()) 9427 .setPrivacyParameters( 9428 testTriggerSpecs.encodePrivacyParametersToJsonString()) 9429 .build(); 9430 source3.buildTriggerSpecs(); 9431 9432 insertAttributedTrigger( 9433 source3.getTriggerSpecs(), 9434 EventReportFixture.getBaseEventReportBuild() 9435 .setTriggerId("123456") 9436 .setTriggerData(new UnsignedLong(2L)) 9437 .setTriggerPriority(1L) 9438 .setTriggerValue(1L) 9439 .build()); 9440 9441 List<Source> sources = List.of(source1, source2, source3); 9442 9443 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 9444 sources.forEach(source -> insertInDb(db, source)); 9445 9446 // Execution 9447 mDatastoreManager.runInTransaction( 9448 dao -> { 9449 Set<String> actualSources = 9450 dao.fetchFlexSourceIdsFor(Collections.singletonList("123456")); 9451 Set<String> expected = Set.of("source1", "source2", "source3"); 9452 assertEquals(expected, actualSources); 9453 }); 9454 9455 mDatastoreManager.runInTransaction( 9456 dao -> { 9457 Set<String> actualSources = 9458 dao.fetchFlexSourceIdsFor(Collections.singletonList("234567")); 9459 Set<String> expected = Set.of("source1", "source2"); 9460 assertEquals(expected, actualSources); 9461 }); 9462 9463 mDatastoreManager.runInTransaction( 9464 dao -> { 9465 Set<String> actualSources = 9466 dao.fetchFlexSourceIdsFor(Collections.singletonList("23456")); 9467 assertEquals(Collections.emptySet(), actualSources); 9468 }); 9469 9470 mDatastoreManager.runInTransaction( 9471 dao -> { 9472 Set<String> actualSources = dao.fetchFlexSourceIdsFor(new ArrayList<>()); 9473 assertEquals(Collections.emptySet(), actualSources); 9474 }); 9475 } 9476 9477 @Test fetchFlexSourceIdsFor_moreThanSqliteMaxCompoundSelect_expectedSourceReturned()9478 public void fetchFlexSourceIdsFor_moreThanSqliteMaxCompoundSelect_expectedSourceReturned() 9479 throws JSONException { 9480 // Setup 9481 TriggerSpecs testTriggerSpecs = SourceFixture.getValidTriggerSpecsCountBased(); 9482 Source source1 = 9483 SourceFixture.getMinimalValidSourceBuilder() 9484 .setEventId(new UnsignedLong(1L)) 9485 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 9486 .setEventTime(5000) 9487 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9488 .setId("source1") 9489 .setTriggerSpecsString(testTriggerSpecs.encodeToJson()) 9490 .setMaxEventLevelReports(testTriggerSpecs.getMaxReports()) 9491 .setPrivacyParameters( 9492 testTriggerSpecs.encodePrivacyParametersToJsonString()) 9493 .build(); 9494 source1.buildTriggerSpecs(); 9495 Source source2 = 9496 SourceFixture.getMinimalValidSourceBuilder() 9497 .setEventId(new UnsignedLong(2L)) 9498 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 9499 .setEventTime(10000) 9500 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9501 .setId("source2") 9502 .setTriggerSpecsString(testTriggerSpecs.encodeToJson()) 9503 .setMaxEventLevelReports(testTriggerSpecs.getMaxReports()) 9504 .setPrivacyParameters( 9505 testTriggerSpecs.encodePrivacyParametersToJsonString()) 9506 .build(); 9507 source2.buildTriggerSpecs(); 9508 9509 List<TriggerSpecs> triggerSpecsList = List.of( 9510 source1.getTriggerSpecs(), 9511 source2.getTriggerSpecs()); 9512 9513 for (TriggerSpecs triggerSpecs : triggerSpecsList) { 9514 insertAttributedTrigger( 9515 triggerSpecs, 9516 EventReportFixture.getBaseEventReportBuild() 9517 .setTriggerId("123456") 9518 .setTriggerData(new UnsignedLong(2L)) 9519 .setTriggerPriority(1L) 9520 .setTriggerValue(1L) 9521 .build()); 9522 insertAttributedTrigger( 9523 triggerSpecs, 9524 EventReportFixture.getBaseEventReportBuild() 9525 .setTriggerId("234567") 9526 .setTriggerData(new UnsignedLong(2L)) 9527 .setTriggerPriority(1L) 9528 .setTriggerValue(1L) 9529 .build()); 9530 insertAttributedTrigger( 9531 triggerSpecs, 9532 EventReportFixture.getBaseEventReportBuild() 9533 .setTriggerId("345678") 9534 .setTriggerData(new UnsignedLong(2L)) 9535 .setTriggerPriority(1L) 9536 .setTriggerValue(1L) 9537 .build()); 9538 } 9539 9540 Source source3 = 9541 SourceFixture.getMinimalValidSourceBuilder() 9542 .setEventId(new UnsignedLong(2L)) 9543 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 9544 .setEventTime(10000) 9545 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9546 .setId("source3") 9547 .setTriggerSpecsString(testTriggerSpecs.encodeToJson()) 9548 .setMaxEventLevelReports(testTriggerSpecs.getMaxReports()) 9549 .setPrivacyParameters( 9550 testTriggerSpecs.encodePrivacyParametersToJsonString()) 9551 .build(); 9552 source3.buildTriggerSpecs(); 9553 9554 insertAttributedTrigger( 9555 source3.getTriggerSpecs(), 9556 EventReportFixture.getBaseEventReportBuild() 9557 .setTriggerId("123456") 9558 .setTriggerData(new UnsignedLong(2L)) 9559 .setTriggerPriority(1L) 9560 .setTriggerValue(1L) 9561 .build()); 9562 9563 List<Source> sources = List.of(source1, source2, source3); 9564 9565 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 9566 sources.forEach(source -> insertInDb(db, source)); 9567 9568 // Execution 9569 mDatastoreManager.runInTransaction( 9570 dao -> { 9571 Set<String> actualSources = 9572 dao.fetchFlexSourceIdsFor(getRandomIdsWith(1000, "123456")); 9573 Set<String> expected = Set.of("source1", "source2", "source3"); 9574 assertEquals(expected, actualSources); 9575 }); 9576 9577 mDatastoreManager.runInTransaction( 9578 dao -> { 9579 Set<String> actualSources = 9580 dao.fetchFlexSourceIdsFor(getRandomIdsWith(1000, "234567")); 9581 Set<String> expected = Set.of("source1", "source2"); 9582 assertEquals(expected, actualSources); 9583 }); 9584 9585 mDatastoreManager.runInTransaction( 9586 dao -> { 9587 Set<String> actualSources = 9588 dao.fetchFlexSourceIdsFor(getRandomIdsWith(1000, "23456")); 9589 assertEquals(Collections.emptySet(), actualSources); 9590 }); 9591 9592 mDatastoreManager.runInTransaction( 9593 dao -> { 9594 Set<String> actualSources = dao.fetchFlexSourceIdsFor(new ArrayList<>()); 9595 assertEquals(Collections.emptySet(), actualSources); 9596 }); 9597 } 9598 9599 @Test fetchFlexSourceIdsFor_emptyInputSourceId_noSourceReturned()9600 public void fetchFlexSourceIdsFor_emptyInputSourceId_noSourceReturned() 9601 throws JSONException { 9602 // Setup 9603 TriggerSpecs testTriggerSpecs = SourceFixture.getValidTriggerSpecsCountBased(); 9604 Source source1 = 9605 SourceFixture.getMinimalValidSourceBuilder() 9606 .setEventId(new UnsignedLong(1L)) 9607 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 9608 .setEventTime(5000) 9609 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9610 .setId("source1") 9611 .setTriggerSpecsString(testTriggerSpecs.encodeToJson()) 9612 .setMaxEventLevelReports(testTriggerSpecs.getMaxReports()) 9613 .setPrivacyParameters( 9614 testTriggerSpecs.encodePrivacyParametersToJsonString()) 9615 .build(); 9616 source1.buildTriggerSpecs(); 9617 9618 Source source2 = 9619 SourceFixture.getMinimalValidSourceBuilder() 9620 .setEventId(new UnsignedLong(2L)) 9621 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 9622 .setEventTime(10000) 9623 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9624 .setId("source2") 9625 .setTriggerSpecsString(testTriggerSpecs.encodeToJson()) 9626 .setMaxEventLevelReports(testTriggerSpecs.getMaxReports()) 9627 .setPrivacyParameters( 9628 testTriggerSpecs.encodePrivacyParametersToJsonString()) 9629 .build(); 9630 source2.buildTriggerSpecs(); 9631 9632 List<TriggerSpecs> triggerSpecsList = List.of( 9633 source1.getTriggerSpecs(), 9634 source2.getTriggerSpecs()); 9635 9636 for (TriggerSpecs triggerSpecs : triggerSpecsList) { 9637 insertAttributedTrigger( 9638 triggerSpecs, 9639 EventReportFixture.getBaseEventReportBuild() 9640 .setTriggerId("123456") 9641 .setTriggerData(new UnsignedLong(2L)) 9642 .setTriggerPriority(1L) 9643 .setTriggerValue(1L) 9644 .build()); 9645 insertAttributedTrigger( 9646 triggerSpecs, 9647 EventReportFixture.getBaseEventReportBuild() 9648 .setTriggerId("234567") 9649 .setTriggerData(new UnsignedLong(2L)) 9650 .setTriggerPriority(1L) 9651 .setTriggerValue(1L) 9652 .build()); 9653 insertAttributedTrigger( 9654 triggerSpecs, 9655 EventReportFixture.getBaseEventReportBuild() 9656 .setTriggerId("345678") 9657 .setTriggerData(new UnsignedLong(2L)) 9658 .setTriggerPriority(1L) 9659 .setTriggerValue(1L) 9660 .build()); 9661 } 9662 9663 Source source3 = 9664 SourceFixture.getMinimalValidSourceBuilder() 9665 .setEventId(new UnsignedLong(2L)) 9666 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 9667 .setEventTime(10000) 9668 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9669 .setId("source3") 9670 .setTriggerSpecsString(testTriggerSpecs.encodeToJson()) 9671 .setMaxEventLevelReports(testTriggerSpecs.getMaxReports()) 9672 .setPrivacyParameters( 9673 testTriggerSpecs.encodePrivacyParametersToJsonString()) 9674 .build(); 9675 source3.buildTriggerSpecs(); 9676 9677 insertAttributedTrigger( 9678 source3.getTriggerSpecs(), 9679 EventReportFixture.getBaseEventReportBuild() 9680 .setTriggerId("123456") 9681 .setTriggerData(new UnsignedLong(2L)) 9682 .setTriggerPriority(1L) 9683 .setTriggerValue(1L) 9684 .build()); 9685 9686 List<Source> sources = List.of(source1, source2, source3); 9687 9688 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 9689 sources.forEach(source -> insertInDb(db, source)); 9690 9691 // Execution 9692 mDatastoreManager.runInTransaction( 9693 dao -> { 9694 Set<String> actualSources = 9695 dao.fetchFlexSourceIdsFor(Collections.singletonList("123456")); 9696 Set<String> expected = Set.of("source1", "source2", "source3"); 9697 assertEquals(expected, actualSources); 9698 }); 9699 9700 mDatastoreManager.runInTransaction( 9701 dao -> { 9702 Set<String> actualSources = 9703 dao.fetchFlexSourceIdsFor(Collections.singletonList("234567")); 9704 Set<String> expected = Set.of("source1", "source2"); 9705 assertEquals(expected, actualSources); 9706 }); 9707 9708 mDatastoreManager.runInTransaction( 9709 dao -> { 9710 Set<String> actualSources = 9711 dao.fetchFlexSourceIdsFor(Collections.singletonList("23456")); 9712 assertEquals(Collections.emptySet(), actualSources); 9713 }); 9714 } 9715 9716 @Test fetchFlexSourceIdsFor_doesNotMatchV1Sources()9717 public void fetchFlexSourceIdsFor_doesNotMatchV1Sources() throws JSONException { 9718 // Setup 9719 EventReport eventReport1 = 9720 EventReportFixture.getBaseEventReportBuild() 9721 .setTriggerId("123456") 9722 .setTriggerData(new UnsignedLong(2L)) 9723 .setTriggerPriority(1L) 9724 .setTriggerValue(1L) 9725 .build(); 9726 EventReport eventReport2 = 9727 EventReportFixture.getBaseEventReportBuild() 9728 .setTriggerId("234567") 9729 .setTriggerData(new UnsignedLong(2L)) 9730 .setTriggerPriority(1L) 9731 .setTriggerValue(1L) 9732 .build(); 9733 EventReport eventReport3 = 9734 EventReportFixture.getBaseEventReportBuild() 9735 .setTriggerId("345678") 9736 .setTriggerData(new UnsignedLong(2L)) 9737 .setTriggerPriority(1L) 9738 .setTriggerValue(1L) 9739 .build(); 9740 9741 TriggerSpecs testTriggerSpecs = SourceFixture.getValidTriggerSpecsCountBased(); 9742 9743 // Flex source 9744 Source source1 = 9745 SourceFixture.getMinimalValidSourceBuilder() 9746 .setEventId(new UnsignedLong(1L)) 9747 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 9748 .setEventTime(5000) 9749 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9750 .setId("source1") 9751 .setTriggerSpecsString(testTriggerSpecs.encodeToJson()) 9752 .setMaxEventLevelReports(testTriggerSpecs.getMaxReports()) 9753 .setPrivacyParameters( 9754 testTriggerSpecs.encodePrivacyParametersToJsonString()) 9755 .build(); 9756 source1.buildTriggerSpecs(); 9757 9758 insertAttributedTrigger(source1.getTriggerSpecs(), eventReport1); 9759 insertAttributedTrigger(source1.getTriggerSpecs(), eventReport2); 9760 insertAttributedTrigger(source1.getTriggerSpecs(), eventReport3); 9761 9762 // Non-flex (V1) source 9763 Source source2 = 9764 SourceFixture.getMinimalValidSourceBuilder() 9765 .setEventId(new UnsignedLong(2L)) 9766 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 9767 .setEventTime(10000) 9768 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9769 .setId("source2") 9770 .build(); 9771 source2.buildAttributedTriggers(); 9772 9773 insertAttributedTrigger(source2.getAttributedTriggers(), eventReport1); 9774 insertAttributedTrigger(source2.getAttributedTriggers(), eventReport2); 9775 insertAttributedTrigger(source2.getAttributedTriggers(), eventReport3); 9776 9777 // Flex source 9778 Source source3 = 9779 SourceFixture.getMinimalValidSourceBuilder() 9780 .setEventId(new UnsignedLong(2L)) 9781 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 9782 .setEventTime(10000) 9783 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9784 .setId("source3") 9785 .setTriggerSpecsString(testTriggerSpecs.encodeToJson()) 9786 .setMaxEventLevelReports(testTriggerSpecs.getMaxReports()) 9787 .setPrivacyParameters( 9788 testTriggerSpecs.encodePrivacyParametersToJsonString()) 9789 .build(); 9790 source3.buildTriggerSpecs(); 9791 9792 insertAttributedTrigger(source3.getTriggerSpecs(), eventReport1); 9793 9794 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 9795 List.of(source1, source2, source3).forEach(source -> insertInDb(db, source)); 9796 9797 // Execution 9798 mDatastoreManager.runInTransaction( 9799 dao -> { 9800 Set<String> actualSources = 9801 dao.fetchFlexSourceIdsFor(Collections.singletonList("123456")); 9802 Set<String> expected = Set.of("source1", "source3"); 9803 assertEquals(expected, actualSources); 9804 }); 9805 9806 mDatastoreManager.runInTransaction( 9807 dao -> { 9808 Set<String> actualSources = 9809 dao.fetchFlexSourceIdsFor( 9810 new ArrayList<>(Collections.singletonList("234567"))); 9811 Set<String> expected = Set.of("source1"); 9812 assertEquals(expected, actualSources); 9813 }); 9814 9815 mDatastoreManager.runInTransaction( 9816 dao -> { 9817 Set<String> actualSources = 9818 dao.fetchFlexSourceIdsFor(Collections.singletonList("23456")); 9819 assertEquals(Collections.emptySet(), actualSources); 9820 }); 9821 } 9822 9823 @Test fetchMatchingTriggers_bringsMatchingTriggers()9824 public void fetchMatchingTriggers_bringsMatchingTriggers() { 9825 // Setup 9826 Trigger trigger1 = 9827 TriggerFixture.getValidTriggerBuilder() 9828 .setAttributionDestination( 9829 WebUtil.validUri("https://subdomain1.site1.test")) 9830 .setTriggerTime(5000) 9831 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9832 .setId("trigger1") 9833 .build(); 9834 Trigger trigger2 = 9835 TriggerFixture.getValidTriggerBuilder() 9836 .setAttributionDestination( 9837 WebUtil.validUri("https://subdomain1.site1.test")) 9838 .setTriggerTime(10000) 9839 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9840 .setId("trigger2") 9841 .build(); 9842 Trigger trigger3 = 9843 TriggerFixture.getValidTriggerBuilder() 9844 .setAttributionDestination( 9845 WebUtil.validUri("https://subdomain2.site1.test")) 9846 .setTriggerTime(15000) 9847 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9848 .setId("trigger3") 9849 .build(); 9850 Trigger trigger4 = 9851 TriggerFixture.getValidTriggerBuilder() 9852 .setAttributionDestination( 9853 WebUtil.validUri("https://subdomain2.site2.test")) 9854 .setTriggerTime(15000) 9855 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9856 .setId("trigger4") 9857 .build(); 9858 Trigger trigger5 = 9859 TriggerFixture.getValidTriggerBuilder() 9860 .setAttributionDestination( 9861 WebUtil.validUri("https://subdomain2.site1.test")) 9862 .setTriggerTime(20000) 9863 .setRegistrant(Uri.parse("android-app://com.registrant2")) 9864 .setId("trigger5") 9865 .build(); 9866 List<Trigger> triggers = List.of(trigger1, trigger2, trigger3, trigger4, trigger5); 9867 9868 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 9869 triggers.forEach( 9870 trigger -> { 9871 ContentValues values = new ContentValues(); 9872 values.put(TriggerContract.ID, trigger.getId()); 9873 values.put( 9874 TriggerContract.ATTRIBUTION_DESTINATION, 9875 trigger.getAttributionDestination().toString()); 9876 values.put(TriggerContract.TRIGGER_TIME, trigger.getTriggerTime()); 9877 values.put(TriggerContract.ENROLLMENT_ID, trigger.getEnrollmentId()); 9878 values.put(TriggerContract.REGISTRANT, trigger.getRegistrant().toString()); 9879 values.put(TriggerContract.STATUS, trigger.getStatus()); 9880 db.insert(TriggerContract.TABLE, /* nullColumnHack */ null, values); 9881 }); 9882 9883 // Execution 9884 mDatastoreManager.runInTransaction( 9885 dao -> { 9886 // --- DELETE behaviour --- 9887 // Delete Nothing 9888 // No Matches 9889 Set<String> actualTriggers = 9890 dao.fetchMatchingTriggers( 9891 Uri.parse("android-app://com.registrant1"), 9892 Instant.ofEpochMilli(0), 9893 Instant.ofEpochMilli(50000), 9894 List.of(), 9895 List.of(), 9896 DeletionRequest.MATCH_BEHAVIOR_DELETE); 9897 assertEquals(0, actualTriggers.size()); 9898 9899 // 1 & 2 match registrant1 and "https://subdomain1.site1.test" 9900 // destination origin 9901 actualTriggers = 9902 dao.fetchMatchingTriggers( 9903 Uri.parse("android-app://com.registrant1"), 9904 Instant.ofEpochMilli(0), 9905 Instant.ofEpochMilli(50000), 9906 List.of(WebUtil.validUri("https://subdomain1.site1.test")), 9907 List.of(), 9908 DeletionRequest.MATCH_BEHAVIOR_DELETE); 9909 assertEquals(2, actualTriggers.size()); 9910 9911 // Only 2 matches registrant1 and "https://subdomain1.site1.test" 9912 // destination origin within the range 9913 actualTriggers = 9914 dao.fetchMatchingTriggers( 9915 Uri.parse("android-app://com.registrant1"), 9916 Instant.ofEpochMilli(8000), 9917 Instant.ofEpochMilli(50000), 9918 List.of(WebUtil.validUri("https://subdomain1.site1.test")), 9919 List.of(), 9920 DeletionRequest.MATCH_BEHAVIOR_DELETE); 9921 assertEquals(1, actualTriggers.size()); 9922 9923 // 1,2 & 3 matches registrant1 and "https://site1.test" destination 9924 // origin 9925 actualTriggers = 9926 dao.fetchMatchingTriggers( 9927 Uri.parse("android-app://com.registrant1"), 9928 Instant.ofEpochMilli(0), 9929 Instant.ofEpochMilli(50000), 9930 List.of(), 9931 List.of(WebUtil.validUri("https://site1.test")), 9932 DeletionRequest.MATCH_BEHAVIOR_DELETE); 9933 assertEquals(3, actualTriggers.size()); 9934 9935 // 3 matches origin and 4 matches domain URI 9936 actualTriggers = 9937 dao.fetchMatchingTriggers( 9938 Uri.parse("android-app://com.registrant1"), 9939 Instant.ofEpochMilli(10000), 9940 Instant.ofEpochMilli(20000), 9941 List.of(WebUtil.validUri("https://subdomain2.site1.test")), 9942 List.of(WebUtil.validUri("https://site2.test")), 9943 DeletionRequest.MATCH_BEHAVIOR_DELETE); 9944 assertEquals(2, actualTriggers.size()); 9945 9946 // --- PRESERVE (anti-match exception registrant) behaviour --- 9947 // Preserve Nothing 9948 // 1,2,3 & 4 are match registrant1 9949 actualTriggers = 9950 dao.fetchMatchingTriggers( 9951 Uri.parse("android-app://com.registrant1"), 9952 Instant.ofEpochMilli(0), 9953 Instant.ofEpochMilli(50000), 9954 List.of(), 9955 List.of(), 9956 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 9957 assertEquals(4, actualTriggers.size()); 9958 9959 // 3 & 4 match registrant1 and don't match 9960 // "https://subdomain1.site1.test" destination origin 9961 actualTriggers = 9962 dao.fetchMatchingTriggers( 9963 Uri.parse("android-app://com.registrant1"), 9964 Instant.ofEpochMilli(0), 9965 Instant.ofEpochMilli(50000), 9966 List.of(WebUtil.validUri("https://subdomain1.site1.test")), 9967 List.of(), 9968 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 9969 assertEquals(2, actualTriggers.size()); 9970 9971 // 3 & 4 match registrant1, in range and don't match 9972 // "https://subdomain1.site1.test" 9973 actualTriggers = 9974 dao.fetchMatchingTriggers( 9975 Uri.parse("android-app://com.registrant1"), 9976 Instant.ofEpochMilli(8000), 9977 Instant.ofEpochMilli(50000), 9978 List.of(WebUtil.validUri("https://subdomain1.site1.test")), 9979 List.of(), 9980 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 9981 assertEquals(2, actualTriggers.size()); 9982 9983 // Only 4 matches registrant1, in range and don't match 9984 // "https://site1.test" 9985 actualTriggers = 9986 dao.fetchMatchingTriggers( 9987 Uri.parse("android-app://com.registrant1"), 9988 Instant.ofEpochMilli(0), 9989 Instant.ofEpochMilli(50000), 9990 List.of(), 9991 List.of(WebUtil.validUri("https://site1.test")), 9992 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 9993 assertEquals(1, actualTriggers.size()); 9994 9995 // only 2 is registrant1 based, in range and does not match either 9996 // site2.test or subdomain2.site1.test 9997 actualTriggers = 9998 dao.fetchMatchingTriggers( 9999 Uri.parse("android-app://com.registrant1"), 10000 Instant.ofEpochMilli(10000), 10001 Instant.ofEpochMilli(20000), 10002 List.of(WebUtil.validUri("https://subdomain2.site1.test")), 10003 List.of(WebUtil.validUri("https://site2.test")), 10004 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 10005 assertEquals(1, actualTriggers.size()); 10006 }); 10007 } 10008 10009 @Test fetchMatchingAsyncRegistrations_bringsMatchingAsyncRegistrations()10010 public void fetchMatchingAsyncRegistrations_bringsMatchingAsyncRegistrations() { 10011 // Setup 10012 AsyncRegistration asyncRegistration1 = 10013 AsyncRegistrationFixture.getValidAsyncRegistrationBuilder() 10014 .setTopOrigin( 10015 WebUtil.validUri("https://subdomain1.site1.test")) 10016 .setRequestTime(5000) 10017 .setRegistrant(Uri.parse("android-app://com.registrant1")) 10018 .setId("asyncRegistration1") 10019 .build(); 10020 AsyncRegistration asyncRegistration2 = 10021 AsyncRegistrationFixture.getValidAsyncRegistrationBuilder() 10022 .setTopOrigin( 10023 WebUtil.validUri("https://subdomain1.site1.test")) 10024 .setRequestTime(10000) 10025 .setRegistrant(Uri.parse("android-app://com.registrant1")) 10026 .setId("asyncRegistration2") 10027 .build(); 10028 AsyncRegistration asyncRegistration3 = 10029 AsyncRegistrationFixture.getValidAsyncRegistrationBuilder() 10030 .setTopOrigin( 10031 WebUtil.validUri("https://subdomain2.site1.test")) 10032 .setRequestTime(15000) 10033 .setRegistrant(Uri.parse("android-app://com.registrant1")) 10034 .setId("asyncRegistration3") 10035 .build(); 10036 AsyncRegistration asyncRegistration4 = 10037 AsyncRegistrationFixture.getValidAsyncRegistrationBuilder() 10038 .setTopOrigin( 10039 WebUtil.validUri("https://subdomain2.site2.test")) 10040 .setRequestTime(15000) 10041 .setRegistrant(Uri.parse("android-app://com.registrant1")) 10042 .setId("asyncRegistration4") 10043 .build(); 10044 AsyncRegistration asyncRegistration5 = 10045 AsyncRegistrationFixture.getValidAsyncRegistrationBuilder() 10046 .setTopOrigin( 10047 WebUtil.validUri("https://subdomain2.site1.test")) 10048 .setRequestTime(20000) 10049 .setRegistrant(Uri.parse("android-app://com.registrant2")) 10050 .setId("asyncRegistration5") 10051 .build(); 10052 List<AsyncRegistration> asyncRegistrations = List.of( 10053 asyncRegistration1, asyncRegistration2, asyncRegistration3, asyncRegistration4, 10054 asyncRegistration5); 10055 10056 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 10057 asyncRegistrations.forEach( 10058 asyncRegistration -> { 10059 ContentValues values = new ContentValues(); 10060 values.put(AsyncRegistrationContract.ID, asyncRegistration.getId()); 10061 values.put( 10062 AsyncRegistrationContract.TOP_ORIGIN, 10063 asyncRegistration.getTopOrigin().toString()); 10064 values.put(AsyncRegistrationContract.REQUEST_TIME, 10065 asyncRegistration.getRequestTime()); 10066 values.put(AsyncRegistrationContract.REGISTRANT, 10067 asyncRegistration.getRegistrant().toString()); 10068 values.put(AsyncRegistrationContract.REGISTRATION_ID, 10069 UUID.randomUUID().toString()); 10070 db.insert(AsyncRegistrationContract.TABLE, /* nullColumnHack */ null, values); 10071 }); 10072 10073 // Execution 10074 mDatastoreManager.runInTransaction( 10075 dao -> { 10076 // --- DELETE behaviour --- 10077 // Delete Nothing 10078 // No Matches 10079 List<String> actualAsyncRegistrations = 10080 dao.fetchMatchingAsyncRegistrations( 10081 Uri.parse("android-app://com.registrant1"), 10082 Instant.ofEpochMilli(0), 10083 Instant.ofEpochMilli(50000), 10084 List.of(), 10085 List.of(), 10086 DeletionRequest.MATCH_BEHAVIOR_DELETE); 10087 assertEquals(0, actualAsyncRegistrations.size()); 10088 10089 // 1 & 2 match registrant1 and "https://subdomain1.site1.test" top- 10090 // origin origin 10091 actualAsyncRegistrations = 10092 dao.fetchMatchingAsyncRegistrations( 10093 Uri.parse("android-app://com.registrant1"), 10094 Instant.ofEpochMilli(0), 10095 Instant.ofEpochMilli(50000), 10096 List.of(WebUtil.validUri("https://subdomain1.site1.test")), 10097 List.of(), 10098 DeletionRequest.MATCH_BEHAVIOR_DELETE); 10099 assertEquals(2, actualAsyncRegistrations.size()); 10100 10101 // Only 2 matches registrant1 and "https://subdomain1.site1.test" 10102 // top-origin origin within the range 10103 actualAsyncRegistrations = 10104 dao.fetchMatchingAsyncRegistrations( 10105 Uri.parse("android-app://com.registrant1"), 10106 Instant.ofEpochMilli(8000), 10107 Instant.ofEpochMilli(50000), 10108 List.of(WebUtil.validUri("https://subdomain1.site1.test")), 10109 List.of(), 10110 DeletionRequest.MATCH_BEHAVIOR_DELETE); 10111 assertEquals(1, actualAsyncRegistrations.size()); 10112 10113 // 1,2 & 3 matches registrant1 and "https://site1.test" top-origin 10114 // origin 10115 actualAsyncRegistrations = 10116 dao.fetchMatchingAsyncRegistrations( 10117 Uri.parse("android-app://com.registrant1"), 10118 Instant.ofEpochMilli(0), 10119 Instant.ofEpochMilli(50000), 10120 List.of(), 10121 List.of(WebUtil.validUri("https://site1.test")), 10122 DeletionRequest.MATCH_BEHAVIOR_DELETE); 10123 assertEquals(3, actualAsyncRegistrations.size()); 10124 10125 // 3 matches origin and 4 matches domain URI 10126 actualAsyncRegistrations = 10127 dao.fetchMatchingAsyncRegistrations( 10128 Uri.parse("android-app://com.registrant1"), 10129 Instant.ofEpochMilli(10000), 10130 Instant.ofEpochMilli(20000), 10131 List.of(WebUtil.validUri("https://subdomain2.site1.test")), 10132 List.of(WebUtil.validUri("https://site2.test")), 10133 DeletionRequest.MATCH_BEHAVIOR_DELETE); 10134 assertEquals(2, actualAsyncRegistrations.size()); 10135 10136 // --- PRESERVE (anti-match exception registrant) behaviour --- 10137 // Preserve Nothing 10138 // 1,2,3 & 4 are match registrant1 10139 actualAsyncRegistrations = 10140 dao.fetchMatchingAsyncRegistrations( 10141 Uri.parse("android-app://com.registrant1"), 10142 Instant.ofEpochMilli(0), 10143 Instant.ofEpochMilli(50000), 10144 List.of(), 10145 List.of(), 10146 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 10147 assertEquals(4, actualAsyncRegistrations.size()); 10148 10149 // 3 & 4 match registrant1 and don't match 10150 // "https://subdomain1.site1.test" top-origin origin 10151 actualAsyncRegistrations = 10152 dao.fetchMatchingAsyncRegistrations( 10153 Uri.parse("android-app://com.registrant1"), 10154 Instant.ofEpochMilli(0), 10155 Instant.ofEpochMilli(50000), 10156 List.of(WebUtil.validUri("https://subdomain1.site1.test")), 10157 List.of(), 10158 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 10159 assertEquals(2, actualAsyncRegistrations.size()); 10160 10161 // 3 & 4 match registrant1, in range and don't match 10162 // "https://subdomain1.site1.test" 10163 actualAsyncRegistrations = 10164 dao.fetchMatchingAsyncRegistrations( 10165 Uri.parse("android-app://com.registrant1"), 10166 Instant.ofEpochMilli(8000), 10167 Instant.ofEpochMilli(50000), 10168 List.of(WebUtil.validUri("https://subdomain1.site1.test")), 10169 List.of(), 10170 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 10171 assertEquals(2, actualAsyncRegistrations.size()); 10172 10173 // Only 4 matches registrant1, in range and don't match 10174 // "https://site1.test" 10175 actualAsyncRegistrations = 10176 dao.fetchMatchingAsyncRegistrations( 10177 Uri.parse("android-app://com.registrant1"), 10178 Instant.ofEpochMilli(0), 10179 Instant.ofEpochMilli(50000), 10180 List.of(), 10181 List.of(WebUtil.validUri("https://site1.test")), 10182 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 10183 assertEquals(1, actualAsyncRegistrations.size()); 10184 10185 // only 2 is registrant1 based, in range and does not match either 10186 // site2.test or subdomain2.site1.test 10187 actualAsyncRegistrations = 10188 dao.fetchMatchingAsyncRegistrations( 10189 Uri.parse("android-app://com.registrant1"), 10190 Instant.ofEpochMilli(10000), 10191 Instant.ofEpochMilli(20000), 10192 List.of(WebUtil.validUri("https://subdomain2.site1.test")), 10193 List.of(WebUtil.validUri("https://site2.test")), 10194 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 10195 assertEquals(1, actualAsyncRegistrations.size()); 10196 }); 10197 } 10198 10199 @Test testUpdateSourceAggregateReportDedupKeys_updatesKeysInList()10200 public void testUpdateSourceAggregateReportDedupKeys_updatesKeysInList() { 10201 Source validSource = SourceFixture.getValidSource(); 10202 assertTrue(validSource.getAggregateReportDedupKeys().equals(new ArrayList<UnsignedLong>())); 10203 mDatastoreManager.runInTransaction((dao) -> dao.insertSource(validSource)); 10204 10205 String sourceId = getFirstSourceIdFromDatastore(); 10206 Source source = 10207 mDatastoreManager 10208 .runInTransactionWithResult( 10209 measurementDao -> measurementDao.getSource(sourceId)) 10210 .get(); 10211 10212 source.getAggregateReportDedupKeys().add(new UnsignedLong(10L)); 10213 mDatastoreManager.runInTransaction( 10214 (dao) -> dao.updateSourceAggregateReportDedupKeys(source)); 10215 10216 Source sourceAfterUpdate = 10217 mDatastoreManager 10218 .runInTransactionWithResult( 10219 measurementDao -> measurementDao.getSource(sourceId)) 10220 .get(); 10221 10222 assertTrue(sourceAfterUpdate.getAggregateReportDedupKeys().size() == 1); 10223 assertTrue(sourceAfterUpdate.getAggregateReportDedupKeys().get(0).getValue() == 10L); 10224 } 10225 10226 @Test testUpdateSourceAggregateReportDedupKeys_acceptsUnsignedLongValue()10227 public void testUpdateSourceAggregateReportDedupKeys_acceptsUnsignedLongValue() { 10228 Source validSource = SourceFixture.getValidSource(); 10229 assertTrue(validSource.getAggregateReportDedupKeys().equals(new ArrayList<UnsignedLong>())); 10230 mDatastoreManager.runInTransaction((dao) -> dao.insertSource(validSource)); 10231 10232 String sourceId = getFirstSourceIdFromDatastore(); 10233 Source source = 10234 mDatastoreManager 10235 .runInTransactionWithResult( 10236 measurementDao -> measurementDao.getSource(sourceId)) 10237 .get(); 10238 10239 UnsignedLong dedupKeyValue = new UnsignedLong("17293822569102704640"); 10240 source.getAggregateReportDedupKeys().add(dedupKeyValue); 10241 mDatastoreManager.runInTransaction( 10242 (dao) -> dao.updateSourceAggregateReportDedupKeys(source)); 10243 10244 Source sourceAfterUpdate = 10245 mDatastoreManager 10246 .runInTransactionWithResult( 10247 measurementDao -> measurementDao.getSource(sourceId)) 10248 .get(); 10249 10250 assertEquals(1, sourceAfterUpdate.getAggregateReportDedupKeys().size()); 10251 assertEquals(dedupKeyValue, sourceAfterUpdate.getAggregateReportDedupKeys().get(0)); 10252 } 10253 10254 @Test testUpdateSourceAggregatableNamedBudgetAndContribution_updatesBudgets()10255 public void testUpdateSourceAggregatableNamedBudgetAndContribution_updatesBudgets() { 10256 mFlags = mock(Flags.class); 10257 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 10258 when(mFlags.getMeasurementEnableAggregatableNamedBudgets()).thenReturn(true); 10259 when(mFlags.getMeasurementDbSizeLimit()).thenReturn(MEASUREMENT_DB_SIZE_LIMIT); 10260 10261 AggregatableNamedBudgets aggregatableNamedBudgets = new AggregatableNamedBudgets(); 10262 aggregatableNamedBudgets.createContributionBudget("budget1", 400); 10263 aggregatableNamedBudgets.setContribution("budget1", 150); 10264 Source validSource = 10265 insertSourceForAggregatableNamedBudgets( 10266 aggregatableNamedBudgets, 10267 SOURCE_EVENT_TIME, 10268 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 10269 List.of(APP_ONE_DESTINATION)); 10270 validSource.getAggregatableNamedBudgets().setContribution("budget1", 340); 10271 AggregatableNamedBudgets.BudgetAndContribution validSourceBudgetAndContribution = 10272 new AggregatableNamedBudgets.BudgetAndContribution(400); 10273 validSourceBudgetAndContribution.setAggregateContribution(340); 10274 mDatastoreManager.runInTransaction( 10275 (dao) -> 10276 dao.updateSourceAggregatableNamedBudgetAndContribution( 10277 validSource.getId(), "budget1", validSourceBudgetAndContribution)); 10278 10279 AggregatableNamedBudgets.BudgetAndContribution daoBudgetAndContribution = 10280 mDatastoreManager 10281 .runInTransactionWithResult( 10282 measurementDao -> 10283 measurementDao 10284 .getSourceAggregatableNamedBudgetAndContribution( 10285 validSource.getId(), "budget1")) 10286 .get(); 10287 AggregatableNamedBudgets.BudgetAndContribution expectedBudgetAndContribution = 10288 new AggregatableNamedBudgets.BudgetAndContribution(400); 10289 expectedBudgetAndContribution.setAggregateContribution(340); 10290 assertThat(daoBudgetAndContribution).isEqualTo(expectedBudgetAndContribution); 10291 } 10292 10293 @Test 10294 public void testUpdateSourceAggregatableNamedBudgetAndContribution_budgetDoesNotExistCreatesRow()10295 testUpdateSourceAggregatableNamedBudgetAndContribution_budgetDoesNotExistCreatesRow() { 10296 mFlags = mock(Flags.class); 10297 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 10298 when(mFlags.getMeasurementEnableAggregatableNamedBudgets()).thenReturn(true); 10299 when(mFlags.getMeasurementDbSizeLimit()).thenReturn(MEASUREMENT_DB_SIZE_LIMIT); 10300 10301 Source validSource = SourceFixture.getValidSource(); 10302 mDatastoreManager.runInTransaction((dao) -> dao.insertSource(validSource)); 10303 assertThat(validSource.getAggregatableNamedBudgets()).isNull(); 10304 10305 AggregatableNamedBudgets aggregatableNamedBudgets = new AggregatableNamedBudgets(); 10306 Source finalSource = 10307 Source.Builder.from(validSource) 10308 .setAggregatableNamedBudgets(aggregatableNamedBudgets) 10309 .build(); 10310 AggregatableNamedBudgets.BudgetAndContribution sourceBudgetAndContribution = 10311 new AggregatableNamedBudgets.BudgetAndContribution(100); 10312 sourceBudgetAndContribution.setAggregateContribution(70); 10313 mDatastoreManager.runInTransaction( 10314 (dao) -> 10315 dao.updateSourceAggregatableNamedBudgetAndContribution( 10316 finalSource.getId(), "budget1", sourceBudgetAndContribution)); 10317 10318 Source sourceAfterUpdate = 10319 mDatastoreManager 10320 .runInTransactionWithResult( 10321 measurementDao -> measurementDao.getSource(validSource.getId())) 10322 .get(); 10323 10324 AggregatableNamedBudgets.BudgetAndContribution daoBudgetAndContribution = 10325 mDatastoreManager 10326 .runInTransactionWithResult( 10327 measurementDao -> 10328 measurementDao 10329 .getSourceAggregatableNamedBudgetAndContribution( 10330 sourceAfterUpdate.getId(), "budget1")) 10331 .get(); 10332 assertThat(daoBudgetAndContribution).isEqualTo(sourceBudgetAndContribution); 10333 } 10334 10335 @Test testPersistAndRetrieveSource_handlesPreExistingNegativeValues()10336 public void testPersistAndRetrieveSource_handlesPreExistingNegativeValues() { 10337 // Setup 10338 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 10339 Source validSource = SourceFixture.getValidSource(); 10340 ContentValues values = new ContentValues(); 10341 values.put(SourceContract.ID, validSource.getId()); 10342 values.put(SourceContract.EVENT_ID, validSource.getEventId().toString()); 10343 values.put(SourceContract.ENROLLMENT_ID, validSource.getEnrollmentId()); 10344 values.put(SourceContract.REGISTRANT, validSource.getRegistrant().toString()); 10345 values.put(SourceContract.PUBLISHER, validSource.getPublisher().toString()); 10346 values.put( 10347 SourceContract.REGISTRATION_ORIGIN, validSource.getRegistrationOrigin().toString()); 10348 // Existing invalid values 10349 Long val1 = -123L; 10350 Long val2 = Long.MIN_VALUE; 10351 values.put( 10352 SourceContract.AGGREGATE_REPORT_DEDUP_KEYS, 10353 val1.toString() + "," + val2.toString()); 10354 db.insert(SourceContract.TABLE, /* nullColumnHack */ null, values); 10355 maybeInsertSourceDestinations(db, validSource, validSource.getId()); 10356 10357 // Execution 10358 Optional<Source> source = 10359 mDatastoreManager.runInTransactionWithResult( 10360 measurementDao -> measurementDao.getSource(validSource.getId())); 10361 assertTrue(source.isPresent()); 10362 List<UnsignedLong> aggReportDedupKeys = source.get().getAggregateReportDedupKeys(); 10363 assertEquals(2, aggReportDedupKeys.size()); 10364 assertEquals(val1, (Long) aggReportDedupKeys.get(0).getValue()); 10365 assertEquals(val2, (Long) aggReportDedupKeys.get(1).getValue()); 10366 } 10367 10368 @Test fetchTriggerMatchingSourcesForXna_filtersSourcesCorrectly()10369 public void fetchTriggerMatchingSourcesForXna_filtersSourcesCorrectly() { 10370 // Setup 10371 Uri matchingDestination = APP_ONE_DESTINATION; 10372 Uri nonMatchingDestination = APP_TWO_DESTINATION; 10373 String mmpMatchingEnrollmentId = "mmp1"; 10374 String mmpNonMatchingEnrollmentId = "mmpx"; 10375 String san1MatchingEnrollmentId = "san1EnrollmentId"; 10376 String san2MatchingEnrollmentId = "san2EnrollmentId"; 10377 String san3MatchingEnrollmentId = "san3EnrollmentId"; 10378 String san4NonMatchingEnrollmentId = "san4EnrollmentId"; 10379 String san5MatchingEnrollmentId = "san5EnrollmentId"; 10380 10381 Trigger trigger = 10382 TriggerFixture.getValidTriggerBuilder() 10383 .setAttributionDestination(matchingDestination) 10384 .setEnrollmentId(mmpMatchingEnrollmentId) 10385 .setRegistrant(TriggerFixture.ValidTriggerParams.REGISTRANT) 10386 .setTriggerTime(TriggerFixture.ValidTriggerParams.TRIGGER_TIME) 10387 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 10388 .setAggregateTriggerData( 10389 TriggerFixture.ValidTriggerParams.AGGREGATE_TRIGGER_DATA) 10390 .setAggregateValuesString( 10391 TriggerFixture.ValidTriggerParams.AGGREGATE_VALUES_STRING) 10392 .setFilters(TriggerFixture.ValidTriggerParams.TOP_LEVEL_FILTERS_JSON_STRING) 10393 .setNotFilters( 10394 TriggerFixture.ValidTriggerParams.TOP_LEVEL_NOT_FILTERS_JSON_STRING) 10395 .build(); 10396 Source s1MmpMatchingWithDestinations = 10397 createSourceBuilder() 10398 .setId("s1") 10399 .setEnrollmentId(mmpMatchingEnrollmentId) 10400 .setAppDestinations(List.of(matchingDestination)) 10401 .build(); 10402 Source s1MmpMatchingWithoutDestinations = 10403 createSourceBuilder() 10404 .setId("s1") 10405 .setEnrollmentId(mmpMatchingEnrollmentId) 10406 .setAppDestinations(null) 10407 .setWebDestinations(null) 10408 .setRegistrationId(s1MmpMatchingWithDestinations.getRegistrationId()) 10409 .build(); 10410 Source s2MmpDiffDestination = 10411 createSourceBuilder() 10412 .setId("s2") 10413 .setEnrollmentId(mmpMatchingEnrollmentId) 10414 .setAppDestinations(List.of(nonMatchingDestination)) 10415 .build(); 10416 Source s3MmpExpired = 10417 createSourceBuilder() 10418 .setId("s3") 10419 .setEnrollmentId(mmpMatchingEnrollmentId) 10420 .setAppDestinations(List.of(nonMatchingDestination)) 10421 // expired before trigger time 10422 .setExpiryTime(trigger.getTriggerTime() - DAYS.toMillis(1)) 10423 .build(); 10424 Source s4NonMatchingMmp = 10425 createSourceBuilder() 10426 .setId("s4") 10427 .setEnrollmentId(mmpNonMatchingEnrollmentId) 10428 .setAppDestinations(List.of(matchingDestination)) 10429 .build(); 10430 Source s5MmpMatchingWithDestinations = 10431 createSourceBuilder() 10432 .setId("s5") 10433 .setEnrollmentId(mmpMatchingEnrollmentId) 10434 .setAppDestinations(List.of(matchingDestination)) 10435 .build(); 10436 Source s5MmpMatchingWithoutDestinations = 10437 createSourceBuilder() 10438 .setId("s5") 10439 .setEnrollmentId(mmpMatchingEnrollmentId) 10440 .setAppDestinations(null) 10441 .setWebDestinations(null) 10442 .setRegistrationId(s5MmpMatchingWithDestinations.getRegistrationId()) 10443 .build(); 10444 Source s6San1MatchingWithDestinations = 10445 createSourceBuilder() 10446 .setId("s6") 10447 .setEnrollmentId(san1MatchingEnrollmentId) 10448 .setAppDestinations(List.of(matchingDestination)) 10449 .setSharedAggregationKeys(SHARED_AGGREGATE_KEYS) 10450 .build(); 10451 Source s6San1MatchingWithoutDestinations = 10452 createSourceBuilder() 10453 .setId("s6") 10454 .setEnrollmentId(san1MatchingEnrollmentId) 10455 .setAppDestinations(null) 10456 .setWebDestinations(null) 10457 .setRegistrationId(s6San1MatchingWithDestinations.getRegistrationId()) 10458 .setSharedAggregationKeys(SHARED_AGGREGATE_KEYS) 10459 .build(); 10460 Source s7San1DiffDestination = 10461 createSourceBuilder() 10462 .setId("s7") 10463 .setEnrollmentId(san1MatchingEnrollmentId) 10464 .setAppDestinations(List.of(nonMatchingDestination)) 10465 .setSharedAggregationKeys(SHARED_AGGREGATE_KEYS) 10466 .build(); 10467 Source s8San2MatchingWithDestinations = 10468 createSourceBuilder() 10469 .setId("s8") 10470 .setEnrollmentId(san2MatchingEnrollmentId) 10471 .setAppDestinations(List.of(matchingDestination)) 10472 .setSharedAggregationKeys(SHARED_AGGREGATE_KEYS) 10473 .build(); 10474 Source s8San2MatchingWithoutDestinations = 10475 createSourceBuilder() 10476 .setId("s8") 10477 .setEnrollmentId(san2MatchingEnrollmentId) 10478 .setAppDestinations(null) 10479 .setWebDestinations(null) 10480 .setRegistrationId(s8San2MatchingWithDestinations.getRegistrationId()) 10481 .setSharedAggregationKeys(SHARED_AGGREGATE_KEYS) 10482 .build(); 10483 Source s9San3XnaIgnored = 10484 createSourceBuilder() 10485 .setId("s9") 10486 .setEnrollmentId(san3MatchingEnrollmentId) 10487 .setAppDestinations(List.of(matchingDestination)) 10488 .setSharedAggregationKeys(SHARED_AGGREGATE_KEYS) 10489 .build(); 10490 Source s10San3MatchingWithDestinations = 10491 createSourceBuilder() 10492 .setId("s10") 10493 .setEnrollmentId(san3MatchingEnrollmentId) 10494 .setAppDestinations(List.of(matchingDestination)) 10495 .setSharedAggregationKeys(SHARED_AGGREGATE_KEYS) 10496 .build(); 10497 Source s10San3MatchingWithoutDestinations = 10498 createSourceBuilder() 10499 .setId("s10") 10500 .setEnrollmentId(san3MatchingEnrollmentId) 10501 .setAppDestinations(null) 10502 .setWebDestinations(null) 10503 .setRegistrationId(s10San3MatchingWithDestinations.getRegistrationId()) 10504 .setSharedAggregationKeys(SHARED_AGGREGATE_KEYS) 10505 .build(); 10506 Source s11San4EnrollmentNonMatching = 10507 createSourceBuilder() 10508 .setId("s11") 10509 .setEnrollmentId(san4NonMatchingEnrollmentId) 10510 .setAppDestinations(List.of(matchingDestination)) 10511 .setSharedAggregationKeys(SHARED_AGGREGATE_KEYS) 10512 .build(); 10513 Source s12San1NullSharedAggregationKeys = 10514 createSourceBuilder() 10515 .setId("s12") 10516 .setEnrollmentId(san1MatchingEnrollmentId) 10517 .setAppDestinations(List.of(matchingDestination)) 10518 .setSharedAggregationKeys(null) 10519 .build(); 10520 Source s13San1Expired = 10521 createSourceBuilder() 10522 .setId("s13") 10523 .setEnrollmentId(san1MatchingEnrollmentId) 10524 .setAppDestinations(List.of(matchingDestination)) 10525 // expired before trigger time 10526 .setExpiryTime(trigger.getTriggerTime() - DAYS.toMillis(1)) 10527 .build(); 10528 String registrationIdForTriggerAndOtherRegistration = UUID.randomUUID().toString(); 10529 Source s14San5RegIdClasesWithMmp = 10530 createSourceBuilder() 10531 .setId("s14") 10532 .setEnrollmentId(san5MatchingEnrollmentId) 10533 .setAppDestinations(List.of(matchingDestination)) 10534 .setRegistrationId(registrationIdForTriggerAndOtherRegistration) 10535 .build(); 10536 Source s15MmpMatchingWithDestinations = 10537 createSourceBuilder() 10538 .setId("s15") 10539 .setEnrollmentId(mmpMatchingEnrollmentId) 10540 .setAppDestinations(List.of(matchingDestination)) 10541 .setRegistrationId(registrationIdForTriggerAndOtherRegistration) 10542 .build(); 10543 Source s15MmpMatchingWithoutDestinations = 10544 createSourceBuilder() 10545 .setId("s15") 10546 .setEnrollmentId(mmpMatchingEnrollmentId) 10547 .setAppDestinations(null) 10548 .setWebDestinations(null) 10549 .setRegistrationId(registrationIdForTriggerAndOtherRegistration) 10550 .build(); 10551 List<Source> sources = 10552 Arrays.asList( 10553 s1MmpMatchingWithDestinations, 10554 s2MmpDiffDestination, 10555 s3MmpExpired, 10556 s4NonMatchingMmp, 10557 s5MmpMatchingWithDestinations, 10558 s6San1MatchingWithDestinations, 10559 s7San1DiffDestination, 10560 s8San2MatchingWithDestinations, 10561 s9San3XnaIgnored, 10562 s10San3MatchingWithDestinations, 10563 s11San4EnrollmentNonMatching, 10564 s12San1NullSharedAggregationKeys, 10565 s13San1Expired, 10566 s14San5RegIdClasesWithMmp, 10567 s15MmpMatchingWithDestinations); 10568 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 10569 Objects.requireNonNull(db); 10570 // Insert all sources to the DB 10571 sources.forEach(source -> insertSource(source, source.getId())); 10572 10573 // Insert XNA ignored sources 10574 ContentValues values = new ContentValues(); 10575 values.put(XnaIgnoredSourcesContract.SOURCE_ID, s9San3XnaIgnored.getId()); 10576 values.put(XnaIgnoredSourcesContract.ENROLLMENT_ID, mmpMatchingEnrollmentId); 10577 long row = db.insert(XnaIgnoredSourcesContract.TABLE, null, values); 10578 assertEquals(1, row); 10579 10580 List<Source> expectedMatchingSources = 10581 Arrays.asList( 10582 s1MmpMatchingWithoutDestinations, 10583 s5MmpMatchingWithoutDestinations, 10584 s6San1MatchingWithoutDestinations, 10585 s8San2MatchingWithoutDestinations, 10586 s10San3MatchingWithoutDestinations, 10587 s15MmpMatchingWithoutDestinations); 10588 Comparator<Source> sortingComparator = Comparator.comparing(Source::getId); 10589 expectedMatchingSources.sort(sortingComparator); 10590 10591 // Execution 10592 mDatastoreManager.runInTransaction( 10593 dao -> { 10594 List<String> matchingSanEnrollmentIds = 10595 Arrays.asList( 10596 san1MatchingEnrollmentId, 10597 san2MatchingEnrollmentId, 10598 san3MatchingEnrollmentId, 10599 san5MatchingEnrollmentId); 10600 List<Source> actualMatchingSources = 10601 dao.fetchTriggerMatchingSourcesForXna( 10602 trigger, matchingSanEnrollmentIds); 10603 actualMatchingSources.sort(sortingComparator); 10604 // Assertion 10605 assertEquals(expectedMatchingSources, actualMatchingSources); 10606 }); 10607 } 10608 10609 @Test insertIgnoredSourceForEnrollment_success()10610 public void insertIgnoredSourceForEnrollment_success() { 10611 // Setup 10612 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 10613 // Need to insert sources before, to honor the foreign key constraint 10614 insertSource(createSourceBuilder().setId("s1").build(), "s1"); 10615 insertSource(createSourceBuilder().setId("s2").build(), "s2"); 10616 10617 Pair<String, String> entry11 = new Pair<>("s1", "e1"); 10618 Pair<String, String> entry21 = new Pair<>("s2", "e1"); 10619 Pair<String, String> entry22 = new Pair<>("s2", "e2"); 10620 10621 mDatastoreManager.runInTransaction( 10622 dao -> { 10623 // Execution 10624 dao.insertIgnoredSourceForEnrollment(entry11.first, entry11.second); 10625 dao.insertIgnoredSourceForEnrollment(entry21.first, entry21.second); 10626 dao.insertIgnoredSourceForEnrollment(entry22.first, entry22.second); 10627 10628 // Assertion 10629 queryAndAssertSourceEntries(db, "e1", Arrays.asList("s1", "s2")); 10630 queryAndAssertSourceEntries(db, "e2", Collections.singletonList("s2")); 10631 }); 10632 } 10633 10634 @Test countDistinctDebugAdIdsUsedByEnrollment_oneTriggerAndSource()10635 public void countDistinctDebugAdIdsUsedByEnrollment_oneTriggerAndSource() { 10636 // Setup 10637 Source.Builder sourceBuilder = 10638 new Source.Builder() 10639 .setPublisher(SourceFixture.ValidSourceParams.PUBLISHER) 10640 .setRegistrant(SourceFixture.ValidSourceParams.REGISTRANT) 10641 .setSourceType(SourceFixture.ValidSourceParams.SOURCE_TYPE) 10642 .setEventId(SourceFixture.ValidSourceParams.SOURCE_EVENT_ID) 10643 .setRegistrationOrigin(SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN); 10644 Source s1 = 10645 sourceBuilder 10646 .setId("s1") 10647 .setPublisherType(EventSurfaceType.WEB) 10648 .setDebugAdId("debug-ad-id-1") 10649 .setEnrollmentId("enrollment-id-1") 10650 .build(); 10651 mDatastoreManager.runInTransaction(dao -> dao.insertSource(s1)); 10652 10653 Trigger.Builder triggerBuilder = 10654 new Trigger.Builder() 10655 .setAttributionDestination( 10656 TriggerFixture.ValidTriggerParams.ATTRIBUTION_DESTINATION) 10657 .setRegistrant(TriggerFixture.ValidTriggerParams.REGISTRANT) 10658 .setRegistrationOrigin( 10659 TriggerFixture.ValidTriggerParams.REGISTRATION_ORIGIN) 10660 .setAggregatableSourceRegistrationTimeConfig( 10661 Trigger.SourceRegistrationTimeConfig.INCLUDE); 10662 Trigger t1 = 10663 triggerBuilder 10664 .setId("t1") 10665 .setDestinationType(EventSurfaceType.WEB) 10666 .setDebugAdId("debug-ad-id-1") 10667 .setEnrollmentId("enrollment-id-1") 10668 .build(); 10669 mDatastoreManager.runInTransaction(dao -> dao.insertTrigger(t1)); 10670 10671 // Assertion 10672 assertTrue( 10673 mDatastoreManager.runInTransaction( 10674 dao -> 10675 assertEquals( 10676 1, 10677 dao.countDistinctDebugAdIdsUsedByEnrollment( 10678 "enrollment-id-1")))); 10679 } 10680 10681 @Test countDistinctDebugAdIdsUsedByEnrollment_nullValuesPresent()10682 public void countDistinctDebugAdIdsUsedByEnrollment_nullValuesPresent() { 10683 // Setup 10684 Source.Builder sourceBuilder = 10685 new Source.Builder() 10686 .setPublisher(SourceFixture.ValidSourceParams.PUBLISHER) 10687 .setRegistrant(SourceFixture.ValidSourceParams.REGISTRANT) 10688 .setSourceType(SourceFixture.ValidSourceParams.SOURCE_TYPE) 10689 .setEventId(SourceFixture.ValidSourceParams.SOURCE_EVENT_ID) 10690 .setRegistrationOrigin(SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN); 10691 // Source with debug AdId present 10692 Source s1 = 10693 sourceBuilder 10694 .setId("s1") 10695 .setPublisherType(EventSurfaceType.WEB) 10696 .setDebugAdId("debug-ad-id-1") 10697 .setEnrollmentId("enrollment-id-1") 10698 .build(); 10699 mDatastoreManager.runInTransaction(dao -> dao.insertSource(s1)); 10700 // Source with no debug AdId 10701 Source s2 = 10702 sourceBuilder 10703 .setId("s2") 10704 .setPublisherType(EventSurfaceType.WEB) 10705 .setEnrollmentId("enrollment-id-1") 10706 .build(); 10707 mDatastoreManager.runInTransaction(dao -> dao.insertSource(s2)); 10708 10709 Trigger.Builder triggerBuilder = 10710 new Trigger.Builder() 10711 .setAttributionDestination( 10712 TriggerFixture.ValidTriggerParams.ATTRIBUTION_DESTINATION) 10713 .setRegistrant(TriggerFixture.ValidTriggerParams.REGISTRANT) 10714 .setRegistrationOrigin( 10715 TriggerFixture.ValidTriggerParams.REGISTRATION_ORIGIN) 10716 .setAggregatableSourceRegistrationTimeConfig( 10717 Trigger.SourceRegistrationTimeConfig.INCLUDE); 10718 // Trigger with debug AdId present 10719 Trigger t1 = 10720 triggerBuilder 10721 .setId("t1") 10722 .setDestinationType(EventSurfaceType.WEB) 10723 .setDebugAdId("debug-ad-id-1") 10724 .setEnrollmentId("enrollment-id-1") 10725 .build(); 10726 mDatastoreManager.runInTransaction(dao -> dao.insertTrigger(t1)); 10727 // Trigger with no debug AdId 10728 Trigger t2 = 10729 triggerBuilder 10730 .setId("t2") 10731 .setDestinationType(EventSurfaceType.WEB) 10732 .setEnrollmentId("enrollment-id-1") 10733 .build(); 10734 mDatastoreManager.runInTransaction(dao -> dao.insertTrigger(t2)); 10735 10736 // Assertion 10737 assertTrue( 10738 mDatastoreManager.runInTransaction( 10739 dao -> 10740 assertEquals( 10741 1, 10742 dao.countDistinctDebugAdIdsUsedByEnrollment( 10743 "enrollment-id-1")))); 10744 } 10745 10746 @Test countDistinctDebugAdIdsUsedByEnrollment_multipleSourcesAndTriggers()10747 public void countDistinctDebugAdIdsUsedByEnrollment_multipleSourcesAndTriggers() { 10748 // Setup 10749 Source.Builder sourceBuilder = 10750 new Source.Builder() 10751 .setPublisher(SourceFixture.ValidSourceParams.PUBLISHER) 10752 .setRegistrant(SourceFixture.ValidSourceParams.REGISTRANT) 10753 .setSourceType(SourceFixture.ValidSourceParams.SOURCE_TYPE) 10754 .setEventId(SourceFixture.ValidSourceParams.SOURCE_EVENT_ID) 10755 .setRegistrationOrigin(SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN); 10756 // Multiple sources with same AdId 10757 Source s1 = 10758 sourceBuilder 10759 .setId("s1") 10760 .setPublisherType(EventSurfaceType.WEB) 10761 .setDebugAdId("debug-ad-id-1") 10762 .setEnrollmentId("enrollment-id-1") 10763 .build(); 10764 mDatastoreManager.runInTransaction(dao -> dao.insertSource(s1)); 10765 Source s2 = 10766 sourceBuilder 10767 .setId("s2") 10768 .setPublisherType(EventSurfaceType.WEB) 10769 .setDebugAdId("debug-ad-id-1") 10770 .setEnrollmentId("enrollment-id-1") 10771 .build(); 10772 mDatastoreManager.runInTransaction(dao -> dao.insertSource(s2)); 10773 Source s3 = 10774 sourceBuilder 10775 .setId("s3") 10776 .setPublisherType(EventSurfaceType.WEB) 10777 .setDebugAdId("debug-ad-id-1") 10778 .setEnrollmentId("enrollment-id-1") 10779 .build(); 10780 mDatastoreManager.runInTransaction(dao -> dao.insertSource(s3)); 10781 10782 Trigger.Builder triggerBuilder = 10783 new Trigger.Builder() 10784 .setAttributionDestination( 10785 TriggerFixture.ValidTriggerParams.ATTRIBUTION_DESTINATION) 10786 .setRegistrant(TriggerFixture.ValidTriggerParams.REGISTRANT) 10787 .setRegistrationOrigin( 10788 TriggerFixture.ValidTriggerParams.REGISTRATION_ORIGIN) 10789 .setAggregatableSourceRegistrationTimeConfig( 10790 Trigger.SourceRegistrationTimeConfig.INCLUDE); 10791 // Multiple triggers with same AdId 10792 Trigger t1 = 10793 triggerBuilder 10794 .setId("t1") 10795 .setDestinationType(EventSurfaceType.WEB) 10796 .setDebugAdId("debug-ad-id-1") 10797 .setEnrollmentId("enrollment-id-1") 10798 .build(); 10799 mDatastoreManager.runInTransaction(dao -> dao.insertTrigger(t1)); 10800 Trigger t2 = 10801 triggerBuilder 10802 .setId("t2") 10803 .setDestinationType(EventSurfaceType.WEB) 10804 .setDebugAdId("debug-ad-id-1") 10805 .setEnrollmentId("enrollment-id-1") 10806 .build(); 10807 mDatastoreManager.runInTransaction(dao -> dao.insertTrigger(t2)); 10808 Trigger t3 = 10809 triggerBuilder 10810 .setId("t3") 10811 .setDestinationType(EventSurfaceType.WEB) 10812 .setDebugAdId("debug-ad-id-1") 10813 .setEnrollmentId("enrollment-id-1") 10814 .build(); 10815 mDatastoreManager.runInTransaction(dao -> dao.insertTrigger(t3)); 10816 10817 // Assertion 10818 assertTrue( 10819 mDatastoreManager.runInTransaction( 10820 dao -> 10821 assertEquals( 10822 1, 10823 dao.countDistinctDebugAdIdsUsedByEnrollment( 10824 "enrollment-id-1")))); 10825 } 10826 10827 @Test countDistinctDebugAdIdsUsedByEnrollment_multipleAdIdsPresent()10828 public void countDistinctDebugAdIdsUsedByEnrollment_multipleAdIdsPresent() { 10829 // Setup 10830 Source.Builder sourceBuilder = 10831 new Source.Builder() 10832 .setPublisher(SourceFixture.ValidSourceParams.PUBLISHER) 10833 .setRegistrant(SourceFixture.ValidSourceParams.REGISTRANT) 10834 .setSourceType(SourceFixture.ValidSourceParams.SOURCE_TYPE) 10835 .setEventId(SourceFixture.ValidSourceParams.SOURCE_EVENT_ID) 10836 .setRegistrationOrigin(SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN); 10837 // Multiple sources with different AdIds but the same enrollmentId 10838 Source s1 = 10839 sourceBuilder 10840 .setId("s1") 10841 .setPublisherType(EventSurfaceType.WEB) 10842 .setDebugAdId("debug-ad-id-1") 10843 .setEnrollmentId("enrollment-id-1") 10844 .build(); 10845 mDatastoreManager.runInTransaction(dao -> dao.insertSource(s1)); 10846 Source s2 = 10847 sourceBuilder 10848 .setId("s2") 10849 .setPublisherType(EventSurfaceType.WEB) 10850 .setDebugAdId("debug-ad-id-2") 10851 .setEnrollmentId("enrollment-id-1") 10852 .build(); 10853 mDatastoreManager.runInTransaction(dao -> dao.insertSource(s2)); 10854 Source s3 = 10855 sourceBuilder 10856 .setId("s3") 10857 .setPublisherType(EventSurfaceType.WEB) 10858 .setDebugAdId("debug-ad-id-3") 10859 .setEnrollmentId("enrollment-id-1") 10860 .build(); 10861 mDatastoreManager.runInTransaction(dao -> dao.insertSource(s3)); 10862 10863 Trigger.Builder triggerBuilder = 10864 new Trigger.Builder() 10865 .setAttributionDestination( 10866 TriggerFixture.ValidTriggerParams.ATTRIBUTION_DESTINATION) 10867 .setRegistrant(TriggerFixture.ValidTriggerParams.REGISTRANT) 10868 .setRegistrationOrigin( 10869 TriggerFixture.ValidTriggerParams.REGISTRATION_ORIGIN) 10870 .setAggregatableSourceRegistrationTimeConfig( 10871 Trigger.SourceRegistrationTimeConfig.INCLUDE); 10872 // Multiple triggers with different AdIds but the same enrollmentId 10873 Trigger t1 = 10874 triggerBuilder 10875 .setId("t1") 10876 .setDestinationType(EventSurfaceType.WEB) 10877 .setDebugAdId("debug-ad-id-4") 10878 .setEnrollmentId("enrollment-id-1") 10879 .build(); 10880 mDatastoreManager.runInTransaction(dao -> dao.insertTrigger(t1)); 10881 Trigger t2 = 10882 triggerBuilder 10883 .setId("t2") 10884 .setDestinationType(EventSurfaceType.WEB) 10885 .setDebugAdId("debug-ad-id-5") 10886 .setEnrollmentId("enrollment-id-1") 10887 .build(); 10888 mDatastoreManager.runInTransaction(dao -> dao.insertTrigger(t2)); 10889 Trigger t3 = 10890 triggerBuilder 10891 .setId("t3") 10892 .setDestinationType(EventSurfaceType.WEB) 10893 .setDebugAdId("debug-ad-id-6") 10894 .setEnrollmentId("enrollment-id-1") 10895 .build(); 10896 mDatastoreManager.runInTransaction(dao -> dao.insertTrigger(t3)); 10897 10898 // Assertion 10899 assertTrue( 10900 mDatastoreManager.runInTransaction( 10901 dao -> 10902 assertEquals( 10903 6, 10904 dao.countDistinctDebugAdIdsUsedByEnrollment( 10905 "enrollment-id-1")))); 10906 } 10907 10908 @Test countDistinctDebugAdIdsUsedByEnrollment_multipleEnrollmentIdsPresent()10909 public void countDistinctDebugAdIdsUsedByEnrollment_multipleEnrollmentIdsPresent() { 10910 // Setup 10911 Source.Builder sourceBuilder = 10912 new Source.Builder() 10913 .setPublisher(SourceFixture.ValidSourceParams.PUBLISHER) 10914 .setRegistrant(SourceFixture.ValidSourceParams.REGISTRANT) 10915 .setSourceType(SourceFixture.ValidSourceParams.SOURCE_TYPE) 10916 .setEventId(SourceFixture.ValidSourceParams.SOURCE_EVENT_ID) 10917 .setRegistrationOrigin(SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN); 10918 // Multiple sources with different AdIds and differing enrollmentIds 10919 Source s1 = 10920 sourceBuilder 10921 .setId("s1") 10922 .setPublisherType(EventSurfaceType.WEB) 10923 .setDebugAdId("debug-ad-id-1") 10924 .setEnrollmentId("enrollment-id-1") 10925 .build(); 10926 mDatastoreManager.runInTransaction(dao -> dao.insertSource(s1)); 10927 Source s2 = 10928 sourceBuilder 10929 .setId("s2") 10930 .setPublisherType(EventSurfaceType.WEB) 10931 .setDebugAdId("debug-ad-id-2") 10932 .setEnrollmentId("enrollment-id-2") 10933 .build(); 10934 mDatastoreManager.runInTransaction(dao -> dao.insertSource(s2)); 10935 Source s3 = 10936 sourceBuilder 10937 .setId("s3") 10938 .setPublisherType(EventSurfaceType.WEB) 10939 .setDebugAdId("debug-ad-id-3") 10940 .setEnrollmentId("enrollment-id-2") 10941 .build(); 10942 mDatastoreManager.runInTransaction(dao -> dao.insertSource(s3)); 10943 10944 Trigger.Builder triggerBuilder = 10945 new Trigger.Builder() 10946 .setAttributionDestination( 10947 TriggerFixture.ValidTriggerParams.ATTRIBUTION_DESTINATION) 10948 .setRegistrant(TriggerFixture.ValidTriggerParams.REGISTRANT) 10949 .setRegistrationOrigin( 10950 TriggerFixture.ValidTriggerParams.REGISTRATION_ORIGIN) 10951 .setAggregatableSourceRegistrationTimeConfig( 10952 Trigger.SourceRegistrationTimeConfig.INCLUDE); 10953 // Multiple triggers with different AdIds and differing enrollmentIds 10954 Trigger t1 = 10955 triggerBuilder 10956 .setId("t1") 10957 .setDestinationType(EventSurfaceType.WEB) 10958 .setDebugAdId("debug-ad-id-4") 10959 .setEnrollmentId("enrollment-id-1") 10960 .build(); 10961 mDatastoreManager.runInTransaction(dao -> dao.insertTrigger(t1)); 10962 Trigger t2 = 10963 triggerBuilder 10964 .setId("t2") 10965 .setDestinationType(EventSurfaceType.WEB) 10966 .setDebugAdId("debug-ad-id-5") 10967 .setEnrollmentId("enrollment-id-2") 10968 .build(); 10969 mDatastoreManager.runInTransaction(dao -> dao.insertTrigger(t2)); 10970 Trigger t3 = 10971 triggerBuilder 10972 .setId("t3") 10973 .setDestinationType(EventSurfaceType.WEB) 10974 .setDebugAdId("debug-ad-id-6") 10975 .setEnrollmentId("enrollment-id-2") 10976 .build(); 10977 mDatastoreManager.runInTransaction(dao -> dao.insertTrigger(t3)); 10978 10979 // Assertion 10980 assertTrue( 10981 mDatastoreManager.runInTransaction( 10982 dao -> 10983 assertEquals( 10984 2, 10985 dao.countDistinctDebugAdIdsUsedByEnrollment( 10986 "enrollment-id-1")))); 10987 assertTrue( 10988 mDatastoreManager.runInTransaction( 10989 dao -> 10990 assertEquals( 10991 4, 10992 dao.countDistinctDebugAdIdsUsedByEnrollment( 10993 "enrollment-id-2")))); 10994 } 10995 10996 @Test getPendingAggregateReportIdsByCoordinatorInWindow()10997 public void getPendingAggregateReportIdsByCoordinatorInWindow() { 10998 AggregateReport ar11 = 10999 AggregateReportFixture.getValidAggregateReportBuilder() 11000 .setId("11") 11001 .setAggregationCoordinatorOrigin(Uri.parse("https://url1.test")) 11002 .build(); 11003 AggregateReport ar12 = 11004 AggregateReportFixture.getValidAggregateReportBuilder() 11005 .setId("12") 11006 .setAggregationCoordinatorOrigin(Uri.parse("https://url1.test")) 11007 .build(); 11008 AggregateReport ar21 = 11009 AggregateReportFixture.getValidAggregateReportBuilder() 11010 .setId("21") 11011 .setAggregationCoordinatorOrigin(Uri.parse("https://url2.test")) 11012 .build(); 11013 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 11014 AbstractDbIntegrationTest.insertToDb(ar11, db); 11015 AbstractDbIntegrationTest.insertToDb(ar12, db); 11016 AbstractDbIntegrationTest.insertToDb(ar21, db); 11017 11018 Optional<Map<String, List<String>>> resOpt = 11019 mDatastoreManager.runInTransactionWithResult( 11020 (dao) -> 11021 dao.getPendingAggregateReportIdsByCoordinatorInWindow( 11022 AggregateReportFixture.ValidAggregateReportParams 11023 .TRIGGER_TIME, 11024 AggregateReportFixture.ValidAggregateReportParams 11025 .TRIGGER_TIME 11026 + DAYS.toMillis(30))); 11027 assertTrue(resOpt.isPresent()); 11028 Map<String, List<String>> res = resOpt.get(); 11029 assertEquals(2, res.size()); 11030 11031 // URL 1 11032 List<String> url1Ids = res.get("https://url1.test"); 11033 url1Ids.sort(String.CASE_INSENSITIVE_ORDER); 11034 assertEquals(2, url1Ids.size()); 11035 assertEquals("11", url1Ids.get(0)); 11036 assertEquals("12", url1Ids.get(1)); 11037 // URL 2 11038 List<String> url2Ids = res.get("https://url2.test"); 11039 url2Ids.sort(String.CASE_INSENSITIVE_ORDER); 11040 assertEquals(1, url2Ids.size()); 11041 assertEquals("21", url2Ids.get(0)); 11042 } 11043 11044 @Test getPendingAggregateReportIdsByCoordinatorInWindowWithRetryLimit()11045 public void getPendingAggregateReportIdsByCoordinatorInWindowWithRetryLimit() { 11046 // Mocking that the flags return a Max Retry of 1 11047 Flags mockFlags = Mockito.mock(Flags.class); 11048 ExtendedMockito.doReturn(mockFlags).when(FlagsFactory::getFlags); 11049 ExtendedMockito.doReturn(1).when(mockFlags).getMeasurementReportingRetryLimit(); 11050 ExtendedMockito.doReturn(true).when(mockFlags).getMeasurementReportingRetryLimitEnabled(); 11051 11052 AggregateReport ar11 = 11053 AggregateReportFixture.getValidAggregateReportBuilder() 11054 .setId("11") 11055 .setAggregationCoordinatorOrigin(Uri.parse("https://url1.test")) 11056 .build(); 11057 AggregateReport ar12 = 11058 AggregateReportFixture.getValidAggregateReportBuilder() 11059 .setId("12") 11060 .setAggregationCoordinatorOrigin(Uri.parse("https://url1.test")) 11061 .build(); 11062 AggregateReport ar21 = 11063 AggregateReportFixture.getValidAggregateReportBuilder() 11064 .setId("21") 11065 .setAggregationCoordinatorOrigin(Uri.parse("https://url2.test")) 11066 .build(); 11067 AggregateReport ar31 = 11068 AggregateReportFixture.getValidAggregateReportBuilder() 11069 .setId("31") 11070 .setAggregationCoordinatorOrigin(Uri.parse("https://url3.test")) 11071 .build(); 11072 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 11073 AbstractDbIntegrationTest.insertToDb(ar11, db); 11074 AbstractDbIntegrationTest.insertToDb(ar12, db); 11075 AbstractDbIntegrationTest.insertToDb(ar21, db); 11076 AbstractDbIntegrationTest.insertToDb(ar31, db); 11077 11078 Optional<Map<String, List<String>>> resOpt = 11079 mDatastoreManager.runInTransactionWithResult( 11080 (dao) -> { 11081 return dao.getPendingAggregateReportIdsByCoordinatorInWindow( 11082 AggregateReportFixture.ValidAggregateReportParams.TRIGGER_TIME, 11083 AggregateReportFixture.ValidAggregateReportParams.TRIGGER_TIME 11084 + DAYS.toMillis(30)); 11085 }); 11086 assertTrue(resOpt.isPresent()); 11087 Map<String, List<String>> res = resOpt.get(); 11088 assertEquals(3, res.size()); 11089 11090 // URL 1 11091 List<String> url1Ids = res.get("https://url1.test"); 11092 url1Ids.sort(String.CASE_INSENSITIVE_ORDER); 11093 assertEquals(2, url1Ids.size()); 11094 assertEquals("11", url1Ids.get(0)); 11095 assertEquals("12", url1Ids.get(1)); 11096 // URL 2 11097 List<String> url2Ids = res.get("https://url2.test"); 11098 url2Ids.sort(String.CASE_INSENSITIVE_ORDER); 11099 assertEquals(1, url2Ids.size()); 11100 assertEquals("21", url2Ids.get(0)); 11101 11102 resOpt = 11103 mDatastoreManager.runInTransactionWithResult( 11104 (dao) -> { 11105 // Adds records to KeyValueData table for Retry Count. 11106 dao.incrementAndGetReportingRetryCount( 11107 ar31.getId(), DataType.AGGREGATE_REPORT_RETRY_COUNT); 11108 return dao.getPendingAggregateReportIdsByCoordinatorInWindow( 11109 AggregateReportFixture.ValidAggregateReportParams.TRIGGER_TIME, 11110 AggregateReportFixture.ValidAggregateReportParams.TRIGGER_TIME 11111 + DAYS.toMillis(30)); 11112 }); 11113 res = resOpt.get(); 11114 11115 assertEquals(2, res.size()); 11116 11117 // URL 1 11118 url1Ids = res.get("https://url1.test"); 11119 url1Ids.sort(String.CASE_INSENSITIVE_ORDER); 11120 assertEquals(2, url1Ids.size()); 11121 assertEquals("11", url1Ids.get(0)); 11122 assertEquals("12", url1Ids.get(1)); 11123 // URL 2 11124 url2Ids = res.get("https://url2.test"); 11125 url2Ids.sort(String.CASE_INSENSITIVE_ORDER); 11126 assertEquals(1, url2Ids.size()); 11127 assertEquals("21", url2Ids.get(0)); 11128 } 11129 11130 @Test getPendingAggregateDebugReportIdsByCoordinator()11131 public void getPendingAggregateDebugReportIdsByCoordinator() { 11132 AggregateReport ar11 = 11133 AggregateReportFixture.getValidAggregateReportBuilder() 11134 .setId("11") 11135 .setAggregationCoordinatorOrigin(Uri.parse("https://url1.test")) 11136 .build(); 11137 AggregateReport ar12 = 11138 AggregateReportFixture.getValidAggregateReportBuilder() 11139 .setId("12") 11140 .setAggregationCoordinatorOrigin(Uri.parse("https://url1.test")) 11141 .build(); 11142 AggregateReport ar21 = 11143 AggregateReportFixture.getValidAggregateReportBuilder() 11144 .setId("21") 11145 .setAggregationCoordinatorOrigin(Uri.parse("https://url2.test")) 11146 .build(); 11147 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 11148 AbstractDbIntegrationTest.insertToDb(ar11, db); 11149 AbstractDbIntegrationTest.insertToDb(ar12, db); 11150 AbstractDbIntegrationTest.insertToDb(ar21, db); 11151 11152 Optional<Map<String, List<String>>> resOpt = 11153 mDatastoreManager.runInTransactionWithResult( 11154 (dao) -> 11155 dao.getPendingAggregateReportIdsByCoordinatorInWindow( 11156 AggregateReportFixture.ValidAggregateReportParams 11157 .TRIGGER_TIME, 11158 AggregateReportFixture.ValidAggregateReportParams 11159 .TRIGGER_TIME 11160 + DAYS.toMillis(30))); 11161 assertTrue(resOpt.isPresent()); 11162 Map<String, List<String>> res = resOpt.get(); 11163 assertEquals(2, res.size()); 11164 11165 // URL 1 11166 List<String> url1Ids = res.get("https://url1.test"); 11167 assertEquals(2, url1Ids.size()); 11168 url1Ids.sort(String.CASE_INSENSITIVE_ORDER); 11169 assertEquals("11", url1Ids.get(0)); 11170 assertEquals("12", url1Ids.get(1)); 11171 // URL 2 11172 List<String> url2Ids = res.get("https://url2.test"); 11173 url2Ids.sort(String.CASE_INSENSITIVE_ORDER); 11174 assertEquals(1, url2Ids.size()); 11175 assertEquals("21", url2Ids.get(0)); 11176 } 11177 11178 @Test getPendingAggregateDebugReportIdsByCoordinatorWithRetryLimit()11179 public void getPendingAggregateDebugReportIdsByCoordinatorWithRetryLimit() { 11180 // Mocking that the flags return a Max Retry of 1 11181 Flags mockFlags = Mockito.mock(Flags.class); 11182 ExtendedMockito.doReturn(mockFlags).when(FlagsFactory::getFlags); 11183 ExtendedMockito.doReturn(1).when(mockFlags).getMeasurementReportingRetryLimit(); 11184 ExtendedMockito.doReturn(true).when(mockFlags).getMeasurementReportingRetryLimitEnabled(); 11185 11186 AggregateReport ar11 = 11187 AggregateReportFixture.getValidAggregateReportBuilder() 11188 .setId("11") 11189 .setAggregationCoordinatorOrigin(Uri.parse("https://url1.test")) 11190 .setDebugReportStatus(AggregateReport.DebugReportStatus.PENDING) 11191 .build(); 11192 AggregateReport ar12 = 11193 AggregateReportFixture.getValidAggregateReportBuilder() 11194 .setId("12") 11195 .setAggregationCoordinatorOrigin(Uri.parse("https://url1.test")) 11196 .setDebugReportStatus(AggregateReport.DebugReportStatus.PENDING) 11197 .build(); 11198 AggregateReport ar21 = 11199 AggregateReportFixture.getValidAggregateReportBuilder() 11200 .setId("21") 11201 .setAggregationCoordinatorOrigin(Uri.parse("https://url2.test")) 11202 .setDebugReportStatus(AggregateReport.DebugReportStatus.PENDING) 11203 .build(); 11204 AggregateReport ar31 = 11205 AggregateReportFixture.getValidAggregateReportBuilder() 11206 .setId("31") 11207 .setAggregationCoordinatorOrigin(Uri.parse("https://url3.test")) 11208 .setDebugReportStatus(AggregateReport.DebugReportStatus.PENDING) 11209 .build(); 11210 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 11211 AbstractDbIntegrationTest.insertToDb(ar11, db); 11212 AbstractDbIntegrationTest.insertToDb(ar12, db); 11213 AbstractDbIntegrationTest.insertToDb(ar21, db); 11214 AbstractDbIntegrationTest.insertToDb(ar31, db); 11215 11216 Optional<Map<String, List<String>>> resOpt = 11217 mDatastoreManager.runInTransactionWithResult( 11218 (dao) -> dao.getPendingAggregateDebugReportIdsByCoordinator()); 11219 assertTrue(resOpt.isPresent()); 11220 Map<String, List<String>> res = resOpt.get(); 11221 assertEquals(3, res.size()); 11222 11223 resOpt = 11224 mDatastoreManager.runInTransactionWithResult( 11225 (dao) -> { 11226 // Adds records to KeyValueData table for Retry Count. 11227 dao.incrementAndGetReportingRetryCount( 11228 ar31.getId(), DataType.AGGREGATE_REPORT_RETRY_COUNT); 11229 return dao.getPendingAggregateDebugReportIdsByCoordinator(); 11230 }); 11231 assertTrue(resOpt.isPresent()); 11232 res = resOpt.get(); 11233 assertEquals(2, res.size()); 11234 11235 // URL 1 11236 List<String> url1Ids = res.get("https://url1.test"); 11237 assertEquals(2, url1Ids.size()); 11238 url1Ids.sort(String.CASE_INSENSITIVE_ORDER); 11239 assertEquals("11", url1Ids.get(0)); 11240 assertEquals("12", url1Ids.get(1)); 11241 // URL 2 11242 List<String> url2Ids = res.get("https://url2.test"); 11243 url2Ids.sort(String.CASE_INSENSITIVE_ORDER); 11244 assertEquals(1, url2Ids.size()); 11245 assertEquals("21", url2Ids.get(0)); 11246 } 11247 11248 @Test getPendingEventReportIdsInWindowWithRetry()11249 public void getPendingEventReportIdsInWindowWithRetry() { 11250 // Mocking that the flags return a Max Retry of 1 11251 Flags mockFlags = Mockito.mock(Flags.class); 11252 ExtendedMockito.doReturn(mockFlags).when(FlagsFactory::getFlags); 11253 ExtendedMockito.doReturn(1).when(mockFlags).getMeasurementReportingRetryLimit(); 11254 ExtendedMockito.doReturn(true).when(mockFlags).getMeasurementReportingRetryLimitEnabled(); 11255 11256 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 11257 11258 EventReport er1 = 11259 generateMockEventReport(WebUtil.validUrl("https://destination-1.test"), 1); 11260 EventReport er2 = 11261 generateMockEventReport(WebUtil.validUrl("https://destination-2.test"), 2); 11262 List.of(er1, er2) 11263 .forEach( 11264 eventReport -> { 11265 ContentValues values = new ContentValues(); 11266 values.put(EventReportContract.ID, eventReport.getId()); 11267 values.put( 11268 EventReportContract.ATTRIBUTION_DESTINATION, 11269 eventReport.getAttributionDestinations().get(0).toString()); 11270 values.put( 11271 EventReportContract.REPORT_TIME, 11272 EventReportFixture.ValidEventReportParams.TRIGGER_TIME 11273 + DAYS.toMillis(15)); 11274 values.put(EventReportContract.STATUS, EventReport.Status.PENDING); 11275 db.insert(EventReportContract.TABLE, null, values); 11276 }); 11277 Optional<List<String>> resOpt = 11278 mDatastoreManager.runInTransactionWithResult( 11279 (dao) -> 11280 dao.getPendingEventReportIdsInWindow( 11281 EventReportFixture.ValidEventReportParams.TRIGGER_TIME, 11282 EventReportFixture.ValidEventReportParams.TRIGGER_TIME 11283 + DAYS.toMillis(30))); 11284 assertTrue(resOpt.isPresent()); 11285 List<String> res = resOpt.get(); 11286 assertEquals(2, res.size()); 11287 assertTrue(res.containsAll(List.of("1", "2"))); 11288 resOpt = 11289 mDatastoreManager.runInTransactionWithResult( 11290 (dao) -> { 11291 // Adds records to KeyValueData table for Retry Count. 11292 dao.incrementAndGetReportingRetryCount( 11293 "1", DataType.EVENT_REPORT_RETRY_COUNT); 11294 return dao.getPendingEventReportIdsInWindow( 11295 EventReportFixture.ValidEventReportParams.TRIGGER_TIME, 11296 EventReportFixture.ValidEventReportParams.TRIGGER_TIME 11297 + DAYS.toMillis(30)); 11298 }); 11299 res = resOpt.get(); 11300 assertEquals(1, res.size()); 11301 assertEquals(List.of("2"), res); 11302 } 11303 11304 @Test getPendingEventDebugReportIdsWithRetryLimit()11305 public void getPendingEventDebugReportIdsWithRetryLimit() { 11306 // Mocking that the flags return a Max Retry of 1 11307 Flags mockFlags = Mockito.mock(Flags.class); 11308 ExtendedMockito.doReturn(mockFlags).when(FlagsFactory::getFlags); 11309 ExtendedMockito.doReturn(1).when(mockFlags).getMeasurementReportingRetryLimit(); 11310 ExtendedMockito.doReturn(true).when(mockFlags).getMeasurementReportingRetryLimitEnabled(); 11311 11312 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 11313 11314 EventReport er1 = 11315 generateMockEventReport(WebUtil.validUrl("https://destination-1.test"), 1); 11316 EventReport er2 = 11317 generateMockEventReport(WebUtil.validUrl("https://destination-2.test"), 2); 11318 List.of(er1, er2) 11319 .forEach( 11320 eventReport -> { 11321 ContentValues values = new ContentValues(); 11322 values.put(EventReportContract.ID, eventReport.getId()); 11323 values.put( 11324 EventReportContract.ATTRIBUTION_DESTINATION, 11325 eventReport.getAttributionDestinations().get(0).toString()); 11326 values.put( 11327 EventReportContract.DEBUG_REPORT_STATUS, 11328 EventReport.DebugReportStatus.PENDING); 11329 db.insert(EventReportContract.TABLE, null, values); 11330 }); 11331 11332 Optional<List<String>> resOpt = 11333 mDatastoreManager.runInTransactionWithResult( 11334 (dao) -> dao.getPendingDebugEventReportIds()); 11335 assertTrue(resOpt.isPresent()); 11336 List<String> res = resOpt.get(); 11337 assertEquals(2, res.size()); 11338 assertTrue(res.containsAll(List.of("1", "2"))); 11339 resOpt = 11340 mDatastoreManager.runInTransactionWithResult( 11341 (dao) -> { 11342 // Adds records to KeyValueData table for Retry Count. 11343 dao.incrementAndGetReportingRetryCount( 11344 "1", DataType.DEBUG_EVENT_REPORT_RETRY_COUNT); 11345 return dao.getPendingDebugEventReportIds(); 11346 }); 11347 res = resOpt.get(); 11348 assertEquals(1, res.size()); 11349 assertEquals(List.of("2"), res); 11350 } 11351 11352 @Test getNonExpiredAggregateEncryptionKeys()11353 public void getNonExpiredAggregateEncryptionKeys() { 11354 AggregateEncryptionKey ek11 = 11355 new AggregateEncryptionKey.Builder() 11356 .setId("11") 11357 .setKeyId("11") 11358 .setPublicKey("11") 11359 .setExpiry(11) 11360 .setAggregationCoordinatorOrigin(Uri.parse("https://1coordinator.test")) 11361 .build(); 11362 // ek12 will not be fetched because expiry (5) < 10 11363 AggregateEncryptionKey ek12 = 11364 new AggregateEncryptionKey.Builder() 11365 .setId("12") 11366 .setKeyId("12") 11367 .setPublicKey("12") 11368 .setExpiry(5) 11369 .setAggregationCoordinatorOrigin(Uri.parse("https://1coordinator.test")) 11370 .build(); 11371 11372 AggregateEncryptionKey ek21 = 11373 new AggregateEncryptionKey.Builder() 11374 .setId("21") 11375 .setKeyId("21") 11376 .setPublicKey("21") 11377 .setExpiry(10) 11378 .setAggregationCoordinatorOrigin(Uri.parse("https://2coordinator.test")) 11379 .build(); 11380 AggregateEncryptionKey ek22 = 11381 new AggregateEncryptionKey.Builder() 11382 .setId("22") 11383 .setKeyId("22") 11384 .setPublicKey("22") 11385 .setExpiry(15) 11386 .setAggregationCoordinatorOrigin(Uri.parse("https://2coordinator.test")) 11387 .build(); 11388 11389 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 11390 AbstractDbIntegrationTest.insertToDb(ek11, db); 11391 AbstractDbIntegrationTest.insertToDb(ek12, db); 11392 AbstractDbIntegrationTest.insertToDb(ek21, db); 11393 AbstractDbIntegrationTest.insertToDb(ek22, db); 11394 11395 List<AggregateEncryptionKey> res1 = 11396 mDatastoreManager 11397 .runInTransactionWithResult( 11398 (dao) -> { 11399 return dao.getNonExpiredAggregateEncryptionKeys( 11400 Uri.parse("https://1coordinator.test"), 10); 11401 }) 11402 .orElseThrow(); 11403 // ek12 will not be fetched because expiry (5) < 10 11404 assertEquals(1, res1.size()); 11405 assertEquals(ek11, res1.get(0)); 11406 11407 List<AggregateEncryptionKey> res2 = 11408 mDatastoreManager 11409 .runInTransactionWithResult( 11410 (dao) -> { 11411 return dao.getNonExpiredAggregateEncryptionKeys( 11412 Uri.parse("https://2coordinator.test"), 10); 11413 }) 11414 .orElseThrow(); 11415 res1.sort((x, y) -> String.CASE_INSENSITIVE_ORDER.compare(x.getId(), y.getId())); 11416 assertEquals(2, res2.size()); 11417 assertEquals(ek21, res2.get(0)); 11418 assertEquals(ek22, res2.get(1)); 11419 } 11420 11421 @Test incrementReportRetryIncrements()11422 public void incrementReportRetryIncrements() { 11423 final String eventId = "TestIdEvent"; 11424 final String aggregateId = "TestIdAggregate"; 11425 final String debugId = "TestIdDebug"; 11426 11427 mDatastoreManager.runInTransaction( 11428 (dao) -> { 11429 dao.incrementAndGetReportingRetryCount( 11430 eventId, DataType.EVENT_REPORT_RETRY_COUNT); 11431 dao.incrementAndGetReportingRetryCount( 11432 eventId, DataType.DEBUG_EVENT_REPORT_RETRY_COUNT); 11433 dao.incrementAndGetReportingRetryCount( 11434 aggregateId, DataType.AGGREGATE_REPORT_RETRY_COUNT); 11435 dao.incrementAndGetReportingRetryCount( 11436 aggregateId, DataType.DEBUG_AGGREGATE_REPORT_RETRY_COUNT); 11437 dao.incrementAndGetReportingRetryCount( 11438 debugId, DataType.DEBUG_REPORT_RETRY_COUNT); 11439 }); 11440 Optional<KeyValueData> eventCount = 11441 mDatastoreManager.runInTransactionWithResult( 11442 (dao) -> dao.getKeyValueData(eventId, DataType.EVENT_REPORT_RETRY_COUNT)); 11443 Optional<KeyValueData> debugEventCount = 11444 mDatastoreManager.runInTransactionWithResult( 11445 (dao) -> 11446 dao.getKeyValueData( 11447 eventId, DataType.DEBUG_EVENT_REPORT_RETRY_COUNT)); 11448 Optional<KeyValueData> aggregateCount = 11449 mDatastoreManager.runInTransactionWithResult( 11450 (dao) -> 11451 dao.getKeyValueData( 11452 aggregateId, DataType.AGGREGATE_REPORT_RETRY_COUNT)); 11453 Optional<KeyValueData> debugAggregateCount = 11454 mDatastoreManager.runInTransactionWithResult( 11455 (dao) -> 11456 dao.getKeyValueData( 11457 aggregateId, DataType.DEBUG_AGGREGATE_REPORT_RETRY_COUNT)); 11458 Optional<KeyValueData> debugCount = 11459 mDatastoreManager.runInTransactionWithResult( 11460 (dao) -> dao.getKeyValueData(debugId, DataType.DEBUG_REPORT_RETRY_COUNT)); 11461 11462 assertTrue(eventCount.isPresent()); 11463 assertEquals(1, (eventCount.get().getReportRetryCount())); 11464 assertTrue(debugEventCount.isPresent()); 11465 assertEquals(1, (debugEventCount.get().getReportRetryCount())); 11466 assertTrue(aggregateCount.isPresent()); 11467 assertEquals(1, (aggregateCount.get().getReportRetryCount())); 11468 assertTrue(debugAggregateCount.isPresent()); 11469 assertEquals(1, (debugAggregateCount.get().getReportRetryCount())); 11470 assertTrue(debugCount.isPresent()); 11471 assertEquals(1, (debugCount.get().getReportRetryCount())); 11472 11473 mDatastoreManager.runInTransaction( 11474 (dao) -> { 11475 dao.incrementAndGetReportingRetryCount( 11476 eventId, DataType.EVENT_REPORT_RETRY_COUNT); 11477 dao.incrementAndGetReportingRetryCount( 11478 aggregateId, DataType.AGGREGATE_REPORT_RETRY_COUNT); 11479 }); 11480 eventCount = 11481 mDatastoreManager.runInTransactionWithResult( 11482 (dao) -> dao.getKeyValueData(eventId, DataType.EVENT_REPORT_RETRY_COUNT)); 11483 aggregateCount = 11484 mDatastoreManager.runInTransactionWithResult( 11485 (dao) -> 11486 dao.getKeyValueData( 11487 aggregateId, DataType.AGGREGATE_REPORT_RETRY_COUNT)); 11488 debugEventCount = 11489 mDatastoreManager.runInTransactionWithResult( 11490 (dao) -> 11491 dao.getKeyValueData( 11492 eventId, DataType.DEBUG_EVENT_REPORT_RETRY_COUNT)); 11493 debugAggregateCount = 11494 mDatastoreManager.runInTransactionWithResult( 11495 (dao) -> 11496 dao.getKeyValueData( 11497 aggregateId, DataType.DEBUG_AGGREGATE_REPORT_RETRY_COUNT)); 11498 debugCount = 11499 mDatastoreManager.runInTransactionWithResult( 11500 (dao) -> dao.getKeyValueData(debugId, DataType.DEBUG_REPORT_RETRY_COUNT)); 11501 11502 assertTrue(eventCount.isPresent()); 11503 assertEquals(2, (eventCount.get().getReportRetryCount())); 11504 assertTrue(debugEventCount.isPresent()); 11505 assertEquals(1, (debugEventCount.get().getReportRetryCount())); 11506 assertTrue(aggregateCount.isPresent()); 11507 assertEquals(2, (aggregateCount.get().getReportRetryCount())); 11508 assertTrue(debugAggregateCount.isPresent()); 11509 assertEquals(1, (debugAggregateCount.get().getReportRetryCount())); 11510 assertTrue(debugCount.isPresent()); 11511 assertEquals(1, (debugCount.get().getReportRetryCount())); 11512 11513 mDatastoreManager.runInTransaction( 11514 (dao) -> { 11515 dao.incrementAndGetReportingRetryCount( 11516 eventId, DataType.DEBUG_EVENT_REPORT_RETRY_COUNT); 11517 dao.incrementAndGetReportingRetryCount( 11518 aggregateId, DataType.DEBUG_AGGREGATE_REPORT_RETRY_COUNT); 11519 dao.incrementAndGetReportingRetryCount( 11520 debugId, DataType.DEBUG_REPORT_RETRY_COUNT); 11521 }); 11522 eventCount = 11523 mDatastoreManager.runInTransactionWithResult( 11524 (dao) -> dao.getKeyValueData(eventId, DataType.EVENT_REPORT_RETRY_COUNT)); 11525 aggregateCount = 11526 mDatastoreManager.runInTransactionWithResult( 11527 (dao) -> 11528 dao.getKeyValueData( 11529 aggregateId, DataType.AGGREGATE_REPORT_RETRY_COUNT)); 11530 debugEventCount = 11531 mDatastoreManager.runInTransactionWithResult( 11532 (dao) -> 11533 dao.getKeyValueData( 11534 eventId, DataType.DEBUG_EVENT_REPORT_RETRY_COUNT)); 11535 debugAggregateCount = 11536 mDatastoreManager.runInTransactionWithResult( 11537 (dao) -> 11538 dao.getKeyValueData( 11539 aggregateId, DataType.DEBUG_AGGREGATE_REPORT_RETRY_COUNT)); 11540 debugCount = 11541 mDatastoreManager.runInTransactionWithResult( 11542 (dao) -> dao.getKeyValueData(debugId, DataType.DEBUG_REPORT_RETRY_COUNT)); 11543 11544 assertTrue(eventCount.isPresent()); 11545 assertEquals(2, (eventCount.get().getReportRetryCount())); 11546 assertTrue(debugEventCount.isPresent()); 11547 assertEquals(2, (debugEventCount.get().getReportRetryCount())); 11548 assertTrue(aggregateCount.isPresent()); 11549 assertEquals(2, (aggregateCount.get().getReportRetryCount())); 11550 assertTrue(debugAggregateCount.isPresent()); 11551 assertEquals(2, (debugAggregateCount.get().getReportRetryCount())); 11552 assertTrue(debugCount.isPresent()); 11553 assertEquals(2, (debugCount.get().getReportRetryCount())); 11554 } 11555 11556 @Test countNavigationSourcesPerReportingOriginQuery()11557 public void countNavigationSourcesPerReportingOriginQuery() { 11558 final String registrationId1 = "registrationId1"; 11559 final String registrationId2 = "registrationId2"; 11560 Source source1 = 11561 SourceFixture.getValidSourceBuilder() 11562 .setRegistrationId(registrationId1) 11563 .setSourceType(Source.SourceType.NAVIGATION) 11564 .setRegistrationOrigin(REGISTRATION_ORIGIN) 11565 .build(); 11566 Source source2 = 11567 SourceFixture.getValidSourceBuilder() 11568 .setRegistrationId(registrationId1) 11569 .setSourceType(Source.SourceType.EVENT) 11570 .setRegistrationOrigin(REGISTRATION_ORIGIN) 11571 .build(); 11572 Source source3 = 11573 SourceFixture.getValidSourceBuilder() 11574 .setRegistrationId(registrationId1) 11575 .setSourceType(Source.SourceType.NAVIGATION) 11576 .setRegistrationOrigin(REGISTRATION_ORIGIN) 11577 .build(); 11578 Source source4 = 11579 SourceFixture.getValidSourceBuilder() 11580 .setRegistrationId(registrationId2) 11581 .setSourceType(Source.SourceType.EVENT) 11582 .setRegistrationOrigin(REGISTRATION_ORIGIN_2) 11583 .build(); 11584 Source source5 = 11585 SourceFixture.getValidSourceBuilder() 11586 .setRegistrationId(registrationId2) 11587 .setSourceType(Source.SourceType.NAVIGATION) 11588 .setRegistrationOrigin(REGISTRATION_ORIGIN_2) 11589 .build(); 11590 Arrays.asList(source1, source2, source3, source4, source5).stream() 11591 .forEach(source -> insertSource(source)); 11592 assertThat( 11593 mDatastoreManager.runInTransactionWithResult( 11594 (dao) -> 11595 dao.countNavigationSourcesPerReportingOrigin( 11596 REGISTRATION_ORIGIN, registrationId1))) 11597 .isEqualTo(Optional.of(2L)); 11598 assertThat( 11599 mDatastoreManager.runInTransactionWithResult( 11600 (dao) -> 11601 dao.countNavigationSourcesPerReportingOrigin( 11602 REGISTRATION_ORIGIN_2, registrationId2))) 11603 .isEqualTo(Optional.of(1L)); 11604 assertThat( 11605 mDatastoreManager.runInTransactionWithResult( 11606 (dao) -> 11607 dao.countNavigationSourcesPerReportingOrigin( 11608 REGISTRATION_ORIGIN, registrationId2))) 11609 .isEqualTo(Optional.of(0L)); 11610 } 11611 verifySourceStatus(@onNull Source source, @Source.Status int status)11612 private void verifySourceStatus(@NonNull Source source, @Source.Status int status) { 11613 assertThat( 11614 mDatastoreManager 11615 .runInTransactionWithResult( 11616 measurementDao -> measurementDao.getSource(source.getId())) 11617 .get() 11618 .getStatus()) 11619 .isEqualTo(status); 11620 } 11621 11622 @Test 11623 public void testUpdateSourcesForAttributionScope_diffMaxViewStates_ignoresSourcesDeletesReports()11624 testUpdateSourcesForAttributionScope_diffMaxViewStates_ignoresSourcesDeletesReports() { 11625 mFlags = mock(Flags.class); 11626 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 11627 doReturn(true).when(mFlags).getMeasurementEnableAttributionScope(); 11628 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mFlags).getMeasurementDbSizeLimit(); 11629 11630 Source source1 = 11631 insertSourceForAttributionScope( 11632 List.of("1"), 11633 ATTRIBUTION_SCOPE_LIMIT, 11634 MAX_EVENT_STATES, 11635 SOURCE_EVENT_TIME, 11636 List.of(WEB_ONE_DESTINATION), 11637 null); 11638 EventReport pastFakeEventReport = 11639 new EventReport.Builder() 11640 .setId("1") 11641 .setSourceId(source1.getId()) 11642 .setSourceEventId(source1.getEventId()) 11643 .setReportTime(SOURCE_EVENT_TIME) 11644 .setAttributionDestinations(List.of(WEB_ONE_DESTINATION)) 11645 .setTriggerTime(SOURCE_EVENT_TIME) 11646 .setSourceType(source1.getSourceType()) 11647 .setStatus(EventReport.Status.PENDING) 11648 .setRegistrationOrigin(source1.getRegistrationOrigin()) 11649 .build(); 11650 EventReport fakeEventReport1 = 11651 new EventReport.Builder() 11652 .setId("2") 11653 .setSourceId(source1.getId()) 11654 .setSourceEventId(source1.getEventId()) 11655 .setReportTime(SOURCE_EVENT_TIME + 1000) 11656 .setAttributionDestinations(List.of(WEB_ONE_DESTINATION)) 11657 .setTriggerTime(SOURCE_EVENT_TIME + 1000) 11658 .setSourceType(source1.getSourceType()) 11659 .setStatus(EventReport.Status.PENDING) 11660 .setRegistrationOrigin(source1.getRegistrationOrigin()) 11661 .build(); 11662 // Deleted fake event report for comparison. 11663 EventReport deletedFakeEventReport1 = 11664 new EventReport.Builder() 11665 .setId("3") 11666 .setSourceId(source1.getId()) 11667 .setSourceEventId(source1.getEventId()) 11668 .setReportTime(SOURCE_EVENT_TIME + 1000) 11669 .setAttributionDestinations(List.of(WEB_ONE_DESTINATION)) 11670 .setTriggerTime(SOURCE_EVENT_TIME + 1000) 11671 .setSourceType(source1.getSourceType()) 11672 .setStatus(EventReport.Status.MARKED_TO_DELETE) 11673 .setRegistrationOrigin(source1.getRegistrationOrigin()) 11674 .build(); 11675 mDatastoreManager.runInTransaction( 11676 (dao) -> { 11677 dao.insertEventReport(pastFakeEventReport); 11678 dao.insertEventReport(fakeEventReport1); 11679 dao.updateSourcesForAttributionScope(source1); 11680 }); 11681 Arrays.asList(source1).stream() 11682 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 11683 assertThat( 11684 mDatastoreManager 11685 .runInTransactionWithResult( 11686 measurementDao -> 11687 measurementDao.getSourceEventReports(source1)) 11688 .get()) 11689 .containsExactly(pastFakeEventReport, fakeEventReport1); 11690 11691 Source source2 = 11692 insertSourceForAttributionScope( 11693 List.of("2"), 11694 ATTRIBUTION_SCOPE_LIMIT, 11695 MAX_EVENT_STATES + 1, 11696 SOURCE_EVENT_TIME + 1, 11697 List.of(WEB_ONE_DESTINATION), 11698 null); 11699 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source2)); 11700 Arrays.asList(source1).stream() 11701 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 11702 Arrays.asList(source2).stream() 11703 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 11704 assertThat( 11705 mDatastoreManager 11706 .runInTransactionWithResult( 11707 measurementDao -> 11708 measurementDao.getSourceEventReports(source1)) 11709 .get()) 11710 .containsExactly(pastFakeEventReport, deletedFakeEventReport1); 11711 11712 Source source3 = 11713 insertSourceForAttributionScope( 11714 List.of("3"), 11715 ATTRIBUTION_SCOPE_LIMIT, 11716 MAX_EVENT_STATES, 11717 SOURCE_EVENT_TIME + 2, 11718 List.of(WEB_TWO_DESTINATION), 11719 null); 11720 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source3)); 11721 Arrays.asList(source1).stream() 11722 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 11723 Arrays.asList(source2, source3).stream() 11724 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 11725 11726 Source source4 = 11727 insertSourceForAttributionScope( 11728 List.of("5"), 11729 ATTRIBUTION_SCOPE_LIMIT, 11730 MAX_EVENT_STATES, 11731 SOURCE_EVENT_TIME + 3, 11732 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 11733 null); 11734 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source4)); 11735 Arrays.asList(source1, source2).stream() 11736 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 11737 Arrays.asList(source3, source4).stream() 11738 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 11739 11740 Source source5 = 11741 insertSourceForAttributionScope( 11742 List.of("4"), 11743 ATTRIBUTION_SCOPE_LIMIT, 11744 MAX_EVENT_STATES + 1, 11745 SOURCE_EVENT_TIME + 4, 11746 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 11747 null); 11748 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source5)); 11749 Arrays.asList(source1, source2, source3, source4).stream() 11750 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 11751 Arrays.asList(source5).stream() 11752 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 11753 11754 // Sources for different reporting origin with different max event states. 11755 Source source6 = 11756 insertSourceForAttributionScope( 11757 List.of("4"), 11758 ATTRIBUTION_SCOPE_LIMIT, 11759 MAX_EVENT_STATES, 11760 SOURCE_EVENT_TIME + 5, 11761 List.of(WEB_ONE_DESTINATION), 11762 null, 11763 REGISTRATION_ORIGIN_2); 11764 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source6)); 11765 Arrays.asList(source1, source2, source3, source4).stream() 11766 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 11767 Arrays.asList(source5, source6).stream() 11768 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 11769 11770 // Sources for different reporting origin with the same max event states. 11771 Source source7 = 11772 insertSourceForAttributionScope( 11773 List.of("4"), 11774 ATTRIBUTION_SCOPE_LIMIT + 1, 11775 MAX_EVENT_STATES, 11776 SOURCE_EVENT_TIME + 5, 11777 List.of(WEB_TWO_DESTINATION), 11778 null, 11779 REGISTRATION_ORIGIN_2); 11780 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source7)); 11781 Arrays.asList(source1, source2, source3, source4).stream() 11782 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 11783 Arrays.asList(source5, source6, source7).stream() 11784 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 11785 11786 Source source8 = 11787 insertSourceForAttributionScope( 11788 List.of("4"), 11789 ATTRIBUTION_SCOPE_LIMIT, 11790 MAX_EVENT_STATES + 1, 11791 SOURCE_EVENT_TIME + 5, 11792 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 11793 null); 11794 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source8)); 11795 Arrays.asList(source1, source2, source3, source4).stream() 11796 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 11797 Arrays.asList(source5, source6, source7, source8).stream() 11798 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 11799 } 11800 11801 @Test testUpdateSourcesForAttributionScope_smallerLimit_ignoresSourcesDeletesReports()11802 public void testUpdateSourcesForAttributionScope_smallerLimit_ignoresSourcesDeletesReports() { 11803 mFlags = mock(Flags.class); 11804 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 11805 doReturn(true).when(mFlags).getMeasurementEnableAttributionScope(); 11806 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mFlags).getMeasurementDbSizeLimit(); 11807 Source source1 = 11808 insertSourceForAttributionScope( 11809 List.of("1"), 11810 ATTRIBUTION_SCOPE_LIMIT, 11811 MAX_EVENT_STATES, 11812 SOURCE_EVENT_TIME, 11813 List.of(WEB_ONE_DESTINATION), 11814 null); 11815 EventReport pastFakeEventReport = 11816 new EventReport.Builder() 11817 .setId("1") 11818 .setSourceId(source1.getId()) 11819 .setSourceEventId(source1.getEventId()) 11820 .setReportTime(SOURCE_EVENT_TIME) 11821 .setAttributionDestinations(List.of(WEB_ONE_DESTINATION)) 11822 .setTriggerTime(SOURCE_EVENT_TIME) 11823 .setSourceType(source1.getSourceType()) 11824 .setStatus(EventReport.Status.PENDING) 11825 .setRegistrationOrigin(source1.getRegistrationOrigin()) 11826 .build(); 11827 EventReport fakeEventReport1 = 11828 new EventReport.Builder() 11829 .setId("2") 11830 .setSourceId(source1.getId()) 11831 .setSourceEventId(source1.getEventId()) 11832 .setReportTime(SOURCE_EVENT_TIME + 1000) 11833 .setAttributionDestinations(List.of(WEB_ONE_DESTINATION)) 11834 .setTriggerTime(SOURCE_EVENT_TIME + 1000) 11835 .setSourceType(source1.getSourceType()) 11836 .setStatus(EventReport.Status.PENDING) 11837 .setRegistrationOrigin(source1.getRegistrationOrigin()) 11838 .build(); 11839 // Delete fake event report for comparison. 11840 EventReport deletedFakeEventReport1 = 11841 new EventReport.Builder() 11842 .setId("3") 11843 .setSourceId(source1.getId()) 11844 .setSourceEventId(source1.getEventId()) 11845 .setReportTime(SOURCE_EVENT_TIME + 1000) 11846 .setAttributionDestinations(List.of(WEB_ONE_DESTINATION)) 11847 .setTriggerTime(SOURCE_EVENT_TIME + 1000) 11848 .setSourceType(source1.getSourceType()) 11849 .setStatus(EventReport.Status.MARKED_TO_DELETE) 11850 .setRegistrationOrigin(source1.getRegistrationOrigin()) 11851 .build(); 11852 mDatastoreManager.runInTransaction( 11853 (dao) -> { 11854 dao.insertEventReport(pastFakeEventReport); 11855 dao.insertEventReport(fakeEventReport1); 11856 dao.updateSourcesForAttributionScope(source1); 11857 }); 11858 Arrays.asList(source1).stream() 11859 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 11860 assertThat( 11861 mDatastoreManager 11862 .runInTransactionWithResult( 11863 measurementDao -> 11864 measurementDao.getSourceEventReports(source1)) 11865 .get()) 11866 .containsExactly(pastFakeEventReport, fakeEventReport1); 11867 11868 Source source2 = 11869 insertSourceForAttributionScope( 11870 List.of("2"), 11871 /* attributionScopeLimit= */ 8L, 11872 MAX_EVENT_STATES, 11873 SOURCE_EVENT_TIME + 1, 11874 List.of(WEB_ONE_DESTINATION), 11875 null); 11876 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source2)); 11877 Arrays.asList(source1).stream() 11878 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 11879 Arrays.asList(source2).stream() 11880 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 11881 assertThat( 11882 mDatastoreManager 11883 .runInTransactionWithResult( 11884 measurementDao -> 11885 measurementDao.getSourceEventReports(source1)) 11886 .get()) 11887 .containsExactly(pastFakeEventReport, deletedFakeEventReport1); 11888 11889 Source source3 = 11890 insertSourceForAttributionScope( 11891 List.of("3"), 11892 /* attributionScopeLimit= */ 4L, 11893 MAX_EVENT_STATES, 11894 SOURCE_EVENT_TIME + 2, 11895 List.of(WEB_TWO_DESTINATION), 11896 null); 11897 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source3)); 11898 Arrays.asList(source1).stream() 11899 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 11900 Arrays.asList(source2, source3).stream() 11901 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 11902 11903 Source source4 = 11904 insertSourceForAttributionScope( 11905 List.of("3"), 11906 /* attributionScopeLimit= */ 7L, 11907 MAX_EVENT_STATES, 11908 SOURCE_EVENT_TIME + 3, 11909 List.of(WEB_TWO_DESTINATION), 11910 null); 11911 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source4)); 11912 Arrays.asList(source1, source3).stream() 11913 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 11914 Arrays.asList(source2, source4).stream() 11915 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 11916 11917 Source source5 = 11918 insertSourceForAttributionScope( 11919 List.of("3"), 11920 /* attributionScopeLimit= */ 4L, 11921 MAX_EVENT_STATES, 11922 SOURCE_EVENT_TIME + 2, 11923 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 11924 null); 11925 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source5)); 11926 Arrays.asList(source1, source3).stream() 11927 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 11928 Arrays.asList(source2, source4, source5).stream() 11929 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 11930 11931 Source source6 = 11932 insertSourceForAttributionScope( 11933 List.of("3"), 11934 /* attributionScopeLimit= */ 6L, 11935 MAX_EVENT_STATES, 11936 SOURCE_EVENT_TIME + 2, 11937 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 11938 null); 11939 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source6)); 11940 Arrays.asList(source1, source3, source5).stream() 11941 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 11942 Arrays.asList(source2, source4, source6).stream() 11943 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 11944 11945 // Sources for different reporting origin with different max event states. 11946 Source source7 = 11947 insertSourceForAttributionScope( 11948 List.of("4"), 11949 /* attributionScopeLimit= */ 6L, 11950 MAX_EVENT_STATES, 11951 SOURCE_EVENT_TIME + 3, 11952 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 11953 null, 11954 REGISTRATION_ORIGIN_2); 11955 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source7)); 11956 Arrays.asList(source1, source3, source5).stream() 11957 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 11958 Arrays.asList(source2, source4, source6, source7).stream() 11959 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 11960 11961 // Sources for different reporting origin with the same max event states. 11962 Source source8 = 11963 insertSourceForAttributionScope( 11964 List.of("4"), 11965 /* attributionScopeLimit= */ 4L, 11966 MAX_EVENT_STATES, 11967 SOURCE_EVENT_TIME + 3, 11968 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 11969 null, 11970 REGISTRATION_ORIGIN_2); 11971 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source8)); 11972 Arrays.asList(source1, source3, source5).stream() 11973 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 11974 Arrays.asList(source2, source4, source6, source7, source8).stream() 11975 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 11976 11977 Source source9 = 11978 insertSourceForAttributionScope( 11979 List.of("4"), 11980 /* attributionScopeLimit= */ 5L, 11981 MAX_EVENT_STATES, 11982 SOURCE_EVENT_TIME + 4, 11983 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 11984 null); 11985 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source9)); 11986 Arrays.stream(new Source[] {source1, source3, source5}) 11987 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 11988 Arrays.stream(new Source[] {source2, source4, source6, source7, source8, source9}) 11989 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 11990 } 11991 11992 @Test 11993 public void testUpdateSourcesForAttributionScope_scopedSource_ignoresNonScopedAndDeletesReports()11994 testUpdateSourcesForAttributionScope_scopedSource_ignoresNonScopedAndDeletesReports() { 11995 mFlags = mock(Flags.class); 11996 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 11997 doReturn(true).when(mFlags).getMeasurementEnableAttributionScope(); 11998 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mFlags).getMeasurementDbSizeLimit(); 11999 12000 Source source1 = 12001 insertSourceForAttributionScope( 12002 null, null, null, SOURCE_EVENT_TIME, List.of(WEB_ONE_DESTINATION), null); 12003 EventReport pastFakeEventReport = 12004 new EventReport.Builder() 12005 .setId("1") 12006 .setSourceId(source1.getId()) 12007 .setSourceEventId(source1.getEventId()) 12008 .setReportTime(SOURCE_EVENT_TIME) 12009 .setAttributionDestinations(List.of(WEB_ONE_DESTINATION)) 12010 .setTriggerTime(SOURCE_EVENT_TIME) 12011 .setSourceType(source1.getSourceType()) 12012 .setStatus(EventReport.Status.PENDING) 12013 .setRegistrationOrigin(source1.getRegistrationOrigin()) 12014 .build(); 12015 EventReport fakeEventReport1 = 12016 new EventReport.Builder() 12017 .setId("2") 12018 .setSourceId(source1.getId()) 12019 .setSourceEventId(source1.getEventId()) 12020 .setReportTime(SOURCE_EVENT_TIME + 1000) 12021 .setAttributionDestinations(List.of(WEB_ONE_DESTINATION)) 12022 .setTriggerTime(SOURCE_EVENT_TIME + 1000) 12023 .setSourceType(source1.getSourceType()) 12024 .setStatus(EventReport.Status.PENDING) 12025 .setRegistrationOrigin(source1.getRegistrationOrigin()) 12026 .build(); 12027 // Deleted fake event report for comparison. 12028 EventReport deletedFakeEventReport1 = 12029 new EventReport.Builder() 12030 .setId("3") 12031 .setSourceId(source1.getId()) 12032 .setSourceEventId(source1.getEventId()) 12033 .setReportTime(SOURCE_EVENT_TIME + 1000) 12034 .setAttributionDestinations(List.of(WEB_ONE_DESTINATION)) 12035 .setTriggerTime(SOURCE_EVENT_TIME + 1000) 12036 .setSourceType(source1.getSourceType()) 12037 .setStatus(EventReport.Status.MARKED_TO_DELETE) 12038 .setRegistrationOrigin(source1.getRegistrationOrigin()) 12039 .build(); 12040 mDatastoreManager.runInTransaction( 12041 (dao) -> { 12042 dao.insertEventReport(pastFakeEventReport); 12043 dao.insertEventReport(fakeEventReport1); 12044 dao.updateSourcesForAttributionScope(source1); 12045 }); 12046 Arrays.asList(source1).stream() 12047 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12048 assertThat( 12049 mDatastoreManager 12050 .runInTransactionWithResult( 12051 measurementDao -> 12052 measurementDao.getSourceEventReports(source1)) 12053 .get()) 12054 .containsExactly(pastFakeEventReport, fakeEventReport1); 12055 12056 Source source2 = 12057 insertSourceForAttributionScope( 12058 List.of("2"), 12059 ATTRIBUTION_SCOPE_LIMIT, 12060 MAX_EVENT_STATES, 12061 SOURCE_EVENT_TIME + 1, 12062 List.of(WEB_ONE_DESTINATION), 12063 null); 12064 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source2)); 12065 Arrays.asList(source1).stream() 12066 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12067 Arrays.asList(source2).stream() 12068 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12069 assertThat( 12070 mDatastoreManager 12071 .runInTransactionWithResult( 12072 measurementDao -> 12073 measurementDao.getSourceEventReports(source1)) 12074 .get()) 12075 .containsExactly(pastFakeEventReport, deletedFakeEventReport1); 12076 12077 Source source3 = 12078 insertSourceForAttributionScope( 12079 List.of("3"), 12080 ATTRIBUTION_SCOPE_LIMIT, 12081 MAX_EVENT_STATES, 12082 SOURCE_EVENT_TIME + 2, 12083 List.of(WEB_TWO_DESTINATION), 12084 null); 12085 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source3)); 12086 Arrays.asList(source1).stream() 12087 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12088 Arrays.asList(source2, source3).stream() 12089 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12090 } 12091 12092 @Test testUpdateSourcesForAttributionScope_newNonScopedSource_removesScopes()12093 public void testUpdateSourcesForAttributionScope_newNonScopedSource_removesScopes() { 12094 mFlags = mock(Flags.class); 12095 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 12096 doReturn(true).when(mFlags).getMeasurementEnableAttributionScope(); 12097 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mFlags).getMeasurementDbSizeLimit(); 12098 Consumer<? super Source> verifyAttributionScopeEmptyFn = 12099 source -> { 12100 assertThat( 12101 mDatastoreManager 12102 .runInTransactionWithResult( 12103 measurementDao -> 12104 measurementDao 12105 .getSourceAttributionScopes( 12106 source.getId())) 12107 .get()) 12108 .isEmpty(); 12109 Source savedSource = 12110 mDatastoreManager 12111 .runInTransactionWithResult( 12112 measurementDao -> 12113 measurementDao.getSource(source.getId())) 12114 .get(); 12115 assertThat(savedSource.getAttributionScopeLimit()).isNull(); 12116 assertThat(savedSource.getMaxEventStates()).isNull(); 12117 }; 12118 12119 Consumer<? super Source> verifyAttributionScopeUnchangedFn = 12120 source -> { 12121 assertThat( 12122 mDatastoreManager 12123 .runInTransactionWithResult( 12124 measurementDao -> 12125 measurementDao 12126 .getSourceAttributionScopes( 12127 source.getId())) 12128 .get()) 12129 .containsExactlyElementsIn(source.getAttributionScopes()); 12130 Source savedSource = 12131 mDatastoreManager 12132 .runInTransactionWithResult( 12133 measurementDao -> 12134 measurementDao.getSource(source.getId())) 12135 .get(); 12136 assertThat(savedSource.getAttributionScopeLimit()) 12137 .isEqualTo(savedSource.getAttributionScopeLimit()); 12138 assertThat(savedSource.getMaxEventStates()) 12139 .isEqualTo(savedSource.getMaxEventStates()); 12140 }; 12141 12142 Source source1 = 12143 insertSourceForAttributionScope( 12144 List.of("1"), 12145 ATTRIBUTION_SCOPE_LIMIT, 12146 MAX_EVENT_STATES, 12147 SOURCE_EVENT_TIME, 12148 List.of(WEB_ONE_DESTINATION), 12149 null); 12150 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source1)); 12151 Arrays.asList(source1).stream() 12152 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12153 Arrays.asList(source1).stream().forEach(verifyAttributionScopeUnchangedFn); 12154 12155 Source source2 = 12156 insertSourceForAttributionScope( 12157 List.of("2"), 12158 ATTRIBUTION_SCOPE_LIMIT, 12159 MAX_EVENT_STATES, 12160 SOURCE_EVENT_TIME + 1, 12161 List.of(WEB_ONE_DESTINATION), 12162 null); 12163 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source2)); 12164 Arrays.asList(source1, source2).stream() 12165 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12166 Arrays.asList(source1, source2).stream().forEach(verifyAttributionScopeUnchangedFn); 12167 12168 Source source3 = 12169 insertSourceForAttributionScope( 12170 null, 12171 null, 12172 null, 12173 SOURCE_EVENT_TIME + 2, 12174 List.of(WEB_TWO_DESTINATION), 12175 null); 12176 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source3)); 12177 Arrays.asList(source1, source2, source3).stream() 12178 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12179 // Source3 is for a different destination, attribution scopes should not be cleared. 12180 Arrays.asList(source1, source2).stream().forEach(verifyAttributionScopeUnchangedFn); 12181 12182 Source source4 = 12183 insertSourceForAttributionScope( 12184 null, 12185 null, 12186 null, 12187 SOURCE_EVENT_TIME + 3, 12188 List.of(WEB_ONE_DESTINATION), 12189 null); 12190 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source4)); 12191 Arrays.asList(source1, source2, source3, source4).stream() 12192 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12193 // Source4 is the same destination, clear attribution scopes for older sources. 12194 Arrays.asList(source1, source2).stream().forEach(verifyAttributionScopeEmptyFn); 12195 } 12196 12197 @Test testUpdateSourcesForAttributionScope_scopesNotSelected_ignoreSources()12198 public void testUpdateSourcesForAttributionScope_scopesNotSelected_ignoreSources() { 12199 mFlags = mock(Flags.class); 12200 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 12201 doReturn(true).when(mFlags).getMeasurementEnableAttributionScope(); 12202 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mFlags).getMeasurementDbSizeLimit(); 12203 // Below are the sources registered with attribution scopes and destinations. 12204 // For each destination, two sources are registered, and only one's scopes are to be 12205 // deleted. 12206 // For registration R1: 12207 // S1: attribution scopes -> [0, ""], destinations -> [D1] 12208 // S2: attribution scopes -> [3, 4, 5], destinations -> [D1] 12209 // S3: attribution scopes -> [0, 1], destinations -> [D2] 12210 // S4: attribution scopes -> [1, 3], destinations -> [D2] 12211 // S5: attribution scopes -> [1, 2], destinations -> [D3] 12212 // S6: attribution scopes -> [2, 3, 4], destinations -> [D3] 12213 // S7: attribution scopes -> [2], destinations -> [D4] 12214 // S8: attribution scopes -> [1, 2], destinations -> [D4], shares same timestamp as S7. 12215 // S12: attribution scopes -> [3, 4], destinations -> [D1, D2, D3, D4] 12216 // For registration R2 to test interplay cross reporting origin: 12217 // If the reporting origin were R1, the attribution scopes for S9, S10, and S11 would 12218 // have been removed. 12219 // S9: attribution scopes -> [0], destinations -> [D1] 12220 // S10: attribution scopes -> [0, 1], destinations -> [D2] 12221 // S11: attribution scopes -> [1, 2], destinations -> [D3] 12222 // The selected attribution scopes for each destination are: 12223 // D1: [3, 4, 5] => Scope for S1 to be removed. 12224 // D2: [1, 3, 4] => Scope for S3 to be removed. 12225 // D3: [2, 3, 4] => Scope for S5 to be removed. 12226 // D4: [2, 3, 4] => Scope for S8 to be removed. S7 and S8 share the same timestamp; the 12227 // attribution scope with the higher value will be selected. 12228 12229 // S1: attribution scopes -> [0], destinations -> [D1] 12230 Source source1 = 12231 insertSourceForAttributionScope( 12232 List.of("0", ""), 12233 ATTRIBUTION_SCOPE_LIMIT, 12234 MAX_EVENT_STATES, 12235 SOURCE_EVENT_TIME, 12236 null, 12237 List.of(APP_ONE_DESTINATION)); 12238 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source1)); 12239 Arrays.stream(new Source[] {source1}) 12240 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12241 12242 // S2: attribution scopes -> [3, 4, 5], destinations -> [D1] 12243 Source source2 = 12244 insertSourceForAttributionScope( 12245 List.of("3", "4", "5"), 12246 ATTRIBUTION_SCOPE_LIMIT, 12247 MAX_EVENT_STATES, 12248 SOURCE_EVENT_TIME + 1, 12249 null, 12250 List.of(APP_ONE_DESTINATION)); 12251 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source2)); 12252 Arrays.stream(new Source[] {source1}) 12253 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12254 Arrays.stream(new Source[] {source2}) 12255 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12256 12257 // S3: attribution scopes -> [0, 1], destinations -> [D2] 12258 Source source3 = 12259 insertSourceForAttributionScope( 12260 List.of("0", "1"), 12261 ATTRIBUTION_SCOPE_LIMIT, 12262 MAX_EVENT_STATES, 12263 SOURCE_EVENT_TIME + 2, 12264 List.of(WEB_ONE_DESTINATION), 12265 null); 12266 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source3)); 12267 Arrays.stream(new Source[] {source1}) 12268 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12269 Arrays.stream(new Source[] {source2, source3}) 12270 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12271 12272 // S4: attribution scopes -> [1, 3], destinations -> [D2] 12273 Source source4 = 12274 insertSourceForAttributionScope( 12275 List.of("1", "3"), 12276 ATTRIBUTION_SCOPE_LIMIT, 12277 MAX_EVENT_STATES, 12278 SOURCE_EVENT_TIME + 3, 12279 List.of(WEB_ONE_DESTINATION), 12280 null); 12281 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source4)); 12282 Arrays.stream(new Source[] {source1}) 12283 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12284 Arrays.stream(new Source[] {source2, source3, source4}) 12285 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12286 12287 // S5: attribution scopes -> [1, 2], destinations -> [D3] 12288 Source source5 = 12289 insertSourceForAttributionScope( 12290 List.of("1", "2"), 12291 ATTRIBUTION_SCOPE_LIMIT, 12292 MAX_EVENT_STATES, 12293 SOURCE_EVENT_TIME + 4, 12294 List.of(WEB_TWO_DESTINATION), 12295 null); 12296 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source5)); 12297 Arrays.stream(new Source[] {source1}) 12298 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12299 Arrays.stream(new Source[] {source2, source3, source4, source5}) 12300 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12301 12302 // S6: attribution scopes -> [2, 3, 4], destinations -> [D3] 12303 Source source6 = 12304 insertSourceForAttributionScope( 12305 List.of("2", "3", "4"), 12306 ATTRIBUTION_SCOPE_LIMIT, 12307 MAX_EVENT_STATES, 12308 SOURCE_EVENT_TIME + 5, 12309 List.of(WEB_TWO_DESTINATION), 12310 null); 12311 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source6)); 12312 Arrays.stream(new Source[] {source1, source5}) 12313 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12314 Arrays.stream(new Source[] {source2, source3, source4, source6}) 12315 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12316 12317 // S7: attribution scopes -> [2], destinations -> [D4] 12318 Source source7 = 12319 insertSourceForAttributionScope( 12320 List.of("2"), 12321 ATTRIBUTION_SCOPE_LIMIT, 12322 MAX_EVENT_STATES, 12323 SOURCE_EVENT_TIME + 6, 12324 List.of(WEB_THREE_DESTINATION), 12325 null); 12326 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source7)); 12327 Arrays.stream(new Source[] {source1, source5}) 12328 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12329 Arrays.stream(new Source[] {source2, source3, source4, source6, source7}) 12330 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12331 12332 // S8: attribution scopes -> [1, 2], destinations -> [D4], shares same timestamp as S7. 12333 Source source8 = 12334 insertSourceForAttributionScope( 12335 List.of("1", "2"), 12336 ATTRIBUTION_SCOPE_LIMIT, 12337 MAX_EVENT_STATES, 12338 SOURCE_EVENT_TIME + 6, 12339 List.of(WEB_THREE_DESTINATION), 12340 null); 12341 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source8)); 12342 Arrays.stream(new Source[] {source1, source5}) 12343 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12344 Arrays.stream(new Source[] {source2, source3, source4, source6, source7, source8}) 12345 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12346 12347 // S9: attribution scopes -> [0], destinations -> [D1], reporting origin -> R2 12348 Source source9 = 12349 insertSourceForAttributionScope( 12350 List.of("0"), 12351 ATTRIBUTION_SCOPE_LIMIT, 12352 MAX_EVENT_STATES, 12353 SOURCE_EVENT_TIME, 12354 null, 12355 List.of(APP_ONE_DESTINATION), 12356 REGISTRATION_ORIGIN_2); 12357 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source9)); 12358 Arrays.stream(new Source[] {source1, source5}) 12359 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12360 Arrays.stream(new Source[] {source2, source3, source4, source6, source7, source8, source9}) 12361 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12362 12363 // S10: attribution scopes -> [0, 1], destinations -> [D2], reporting origin -> R2 12364 Source source10 = 12365 insertSourceForAttributionScope( 12366 List.of("0", "1"), 12367 ATTRIBUTION_SCOPE_LIMIT, 12368 MAX_EVENT_STATES, 12369 SOURCE_EVENT_TIME + 2, 12370 List.of(WEB_ONE_DESTINATION), 12371 null, 12372 REGISTRATION_ORIGIN_2); 12373 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source10)); 12374 Arrays.stream(new Source[] {source1, source5}) 12375 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12376 Arrays.stream( 12377 new Source[] { 12378 source2, source3, source4, source6, source7, source8, source9, source10 12379 }) 12380 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12381 12382 // S11: attribution scopes -> [1, 2], destinations -> [D3], reporting origin -> R2 12383 Source source11 = 12384 insertSourceForAttributionScope( 12385 List.of("1", "2"), 12386 ATTRIBUTION_SCOPE_LIMIT, 12387 MAX_EVENT_STATES, 12388 SOURCE_EVENT_TIME + 4, 12389 List.of(WEB_TWO_DESTINATION), 12390 null, 12391 REGISTRATION_ORIGIN_2); 12392 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source11)); 12393 Arrays.stream(new Source[] {source1, source5}) 12394 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12395 Arrays.stream( 12396 new Source[] { 12397 source2, source3, source4, source6, source7, source8, source9, source10, 12398 source11 12399 }) 12400 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12401 12402 // S12: attribution scopes -> [3, 4], destinations -> [D1, D2, D3, D4] 12403 Source source12 = 12404 insertSourceForAttributionScope( 12405 List.of("3", "4"), 12406 ATTRIBUTION_SCOPE_LIMIT, 12407 MAX_EVENT_STATES, 12408 SOURCE_EVENT_TIME + 8, 12409 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION, WEB_THREE_DESTINATION), 12410 List.of(APP_ONE_DESTINATION)); 12411 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source12)); 12412 Arrays.stream(new Source[] {source1, source3, source5, source8}) 12413 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12414 Arrays.stream( 12415 new Source[] { 12416 source2, source4, source6, source7, source9, source10, source11, 12417 source12 12418 }) 12419 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12420 } 12421 12422 @Test getLatestReportTimeInBatchWindow_singleAggregateReport_returnsSingleReportTime()12423 public void getLatestReportTimeInBatchWindow_singleAggregateReport_returnsSingleReportTime() { 12424 Source source = 12425 SourceFixture.getMinimalValidSourceBuilder() 12426 .setEventId(new UnsignedLong(1L)) 12427 .setId("source1") 12428 .build(); 12429 12430 long scheduledReportTime = 1L; 12431 AggregateReport report = 12432 generateMockAggregateReport( 12433 WebUtil.validUrl("https://destination-1.test"), 12434 1, 12435 "source1", 12436 scheduledReportTime); 12437 12438 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 12439 Objects.requireNonNull(db); 12440 insertSource(source, source.getId()); 12441 getAggregateReportConsumer(db).accept(report); 12442 12443 Long result = 12444 mDatastoreManager 12445 .runInTransactionWithResult( 12446 measurementDao -> 12447 measurementDao.getLatestReportTimeInBatchWindow( 12448 mFlags 12449 .getMeasurementReportingJobServiceBatchWindowMillis())) 12450 .orElseThrow(); 12451 12452 assertEquals(scheduledReportTime, result.longValue()); 12453 } 12454 12455 @Test testGetLatestReportTimeInBatchWindow_singleEventReport_returnsSingleReportTime()12456 public void testGetLatestReportTimeInBatchWindow_singleEventReport_returnsSingleReportTime() { 12457 long reportTime = 1L; 12458 EventReport report = 12459 generateMockEventReport( 12460 WebUtil.validUrl("https://destination-1.test"), 1, reportTime); 12461 12462 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 12463 Objects.requireNonNull(db); 12464 getEventReportConsumer(db).accept(report); 12465 12466 Long result = 12467 mDatastoreManager 12468 .runInTransactionWithResult( 12469 measurementDao -> 12470 measurementDao.getLatestReportTimeInBatchWindow( 12471 mFlags 12472 .getMeasurementReportingJobServiceBatchWindowMillis())) 12473 .orElseThrow(); 12474 12475 assertEquals(reportTime, result.longValue()); 12476 } 12477 12478 @Test 12479 public void testGetLatestReportTimeInBatchWindow_twoAggregateReport_bothInBatchWindow_returnsTwoReportTimes()12480 testGetLatestReportTimeInBatchWindow_twoAggregateReport_bothInBatchWindow_returnsTwoReportTimes() { 12481 String sourceId = "source1"; 12482 Source source = 12483 SourceFixture.getMinimalValidSourceBuilder() 12484 .setEventId(new UnsignedLong(1L)) 12485 .setId(sourceId) 12486 .build(); 12487 12488 long firstScheduledReportTime = 1L; 12489 long secondScheduledReportTime = 12490 firstScheduledReportTime 12491 + MEASUREMENT_REPORTING_JOB_SERVICE_BATCH_WINDOW_MILLIS 12492 - 1L; 12493 12494 String destination = WebUtil.validUrl("https://destination-1.test"); 12495 List<AggregateReport> reports = 12496 Arrays.asList( 12497 generateMockAggregateReport( 12498 destination, 1, sourceId, firstScheduledReportTime), 12499 generateMockAggregateReport( 12500 destination, 2, sourceId, secondScheduledReportTime)); 12501 12502 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 12503 Objects.requireNonNull(db); 12504 insertSource(source, source.getId()); 12505 reports.forEach(getAggregateReportConsumer(db)); 12506 12507 Long result = 12508 mDatastoreManager 12509 .runInTransactionWithResult( 12510 measurementDao -> 12511 measurementDao.getLatestReportTimeInBatchWindow( 12512 mFlags 12513 .getMeasurementReportingJobServiceBatchWindowMillis())) 12514 .orElseThrow(); 12515 12516 assertEquals(secondScheduledReportTime, result.longValue()); 12517 } 12518 12519 @Test 12520 public void testGetLatestReportTimeInBatchWindow_twoAggReport_oneAfterBatchWindow_returnOneReportTime()12521 testGetLatestReportTimeInBatchWindow_twoAggReport_oneAfterBatchWindow_returnOneReportTime() { 12522 String sourceId = "source1"; 12523 Source source = 12524 SourceFixture.getMinimalValidSourceBuilder() 12525 .setEventId(new UnsignedLong(1L)) 12526 .setId(sourceId) 12527 .build(); 12528 12529 long firstScheduledReportTime = 1L; 12530 long secondScheduledReportTime = 12531 firstScheduledReportTime 12532 + MEASUREMENT_REPORTING_JOB_SERVICE_BATCH_WINDOW_MILLIS 12533 + 1L; 12534 12535 String destination = WebUtil.validUrl("https://destination-1.test"); 12536 List<AggregateReport> reports = 12537 Arrays.asList( 12538 generateMockAggregateReport( 12539 destination, 1, sourceId, firstScheduledReportTime), 12540 generateMockAggregateReport( 12541 destination, 2, sourceId, secondScheduledReportTime)); 12542 12543 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 12544 Objects.requireNonNull(db); 12545 insertSource(source, source.getId()); 12546 reports.forEach(getAggregateReportConsumer(db)); 12547 12548 Long result = 12549 mDatastoreManager 12550 .runInTransactionWithResult( 12551 measurementDao -> 12552 measurementDao.getLatestReportTimeInBatchWindow( 12553 mFlags 12554 .getMeasurementReportingJobServiceBatchWindowMillis())) 12555 .orElseThrow(); 12556 12557 // secondScheduledReportTime should not be returned as it was outside the batch window. 12558 assertEquals(firstScheduledReportTime, result.longValue()); 12559 } 12560 12561 @Test 12562 public void testGetLatestReportTimeInBatchWindow_twoEventReport_bothInBatchWindow_returnSecondReportTime()12563 testGetLatestReportTimeInBatchWindow_twoEventReport_bothInBatchWindow_returnSecondReportTime() { 12564 long firstScheduledReportTime = 1L; 12565 long secondScheduledReportTime = 12566 firstScheduledReportTime 12567 + MEASUREMENT_REPORTING_JOB_SERVICE_BATCH_WINDOW_MILLIS 12568 - 1L; 12569 12570 String destination = WebUtil.validUrl("https://destination-1.test"); 12571 List<EventReport> reports = 12572 Arrays.asList( 12573 generateMockEventReport(destination, 1, firstScheduledReportTime), 12574 generateMockEventReport(destination, 2, secondScheduledReportTime)); 12575 12576 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 12577 Objects.requireNonNull(db); 12578 12579 Consumer<EventReport> eventReportConsumer = getEventReportConsumer(db); 12580 reports.forEach(eventReportConsumer); 12581 12582 Long result = 12583 mDatastoreManager 12584 .runInTransactionWithResult( 12585 measurementDao -> 12586 measurementDao.getLatestReportTimeInBatchWindow( 12587 mFlags 12588 .getMeasurementReportingJobServiceBatchWindowMillis())) 12589 .orElseThrow(); 12590 12591 assertEquals(secondScheduledReportTime, result.longValue()); 12592 } 12593 12594 @Test 12595 public void testGetLatestReportTimeInBatchWindow_twoEventReport_oneAfterBatchWindow_returnFirstReportTime()12596 testGetLatestReportTimeInBatchWindow_twoEventReport_oneAfterBatchWindow_returnFirstReportTime() { 12597 long firstScheduledReportTime = 1L; 12598 long secondScheduledReportTime = 12599 firstScheduledReportTime 12600 + MEASUREMENT_REPORTING_JOB_SERVICE_BATCH_WINDOW_MILLIS 12601 + 1L; 12602 12603 String destination = WebUtil.validUrl("https://destination-1.test"); 12604 List<EventReport> reports = 12605 Arrays.asList( 12606 generateMockEventReport(destination, 1, firstScheduledReportTime), 12607 generateMockEventReport(destination, 2, secondScheduledReportTime)); 12608 12609 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 12610 Objects.requireNonNull(db); 12611 12612 Consumer<EventReport> eventReportConsumer = getEventReportConsumer(db); 12613 reports.forEach(eventReportConsumer); 12614 12615 Long result = 12616 mDatastoreManager 12617 .runInTransactionWithResult( 12618 measurementDao -> 12619 measurementDao.getLatestReportTimeInBatchWindow( 12620 mFlags 12621 .getMeasurementReportingJobServiceBatchWindowMillis())) 12622 .orElseThrow(); 12623 12624 // secondScheduledReportTime should not be returned as it was outside the batch window. 12625 assertEquals(firstScheduledReportTime, result.longValue()); 12626 } 12627 12628 @Test 12629 public void testGetLatestReportTimeInBatchWindow_oneAggReport_oneEventReport_bothInBatchWindow_returnSecondReportTime()12630 testGetLatestReportTimeInBatchWindow_oneAggReport_oneEventReport_bothInBatchWindow_returnSecondReportTime() { 12631 long firstScheduledReportTime = 1L; 12632 long secondScheduledReportTime = 12633 firstScheduledReportTime 12634 + MEASUREMENT_REPORTING_JOB_SERVICE_BATCH_WINDOW_MILLIS 12635 - 1L; 12636 12637 Source source = 12638 SourceFixture.getMinimalValidSourceBuilder() 12639 .setEventId(new UnsignedLong(1L)) 12640 .setId("source1") 12641 .build(); 12642 12643 String destination = WebUtil.validUrl("https://destination-1.test"); 12644 AggregateReport aggregateReport = 12645 generateMockAggregateReport(destination, 1, "source1", firstScheduledReportTime); 12646 EventReport eventReport = 12647 generateMockEventReport(destination, 1, secondScheduledReportTime); 12648 12649 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 12650 Objects.requireNonNull(db); 12651 insertSource(source, source.getId()); 12652 getEventReportConsumer(db).accept(eventReport); 12653 getAggregateReportConsumer(db).accept(aggregateReport); 12654 12655 Long result = 12656 mDatastoreManager 12657 .runInTransactionWithResult( 12658 measurementDao -> 12659 measurementDao.getLatestReportTimeInBatchWindow( 12660 mFlags 12661 .getMeasurementReportingJobServiceBatchWindowMillis())) 12662 .orElseThrow(); 12663 12664 assertEquals(secondScheduledReportTime, result.longValue()); 12665 } 12666 12667 @Test 12668 public void testGetLatestReportTimeInBatchWindow_oneAggReport_oneEventReport_oneAfterBatchWindow_returnFirstReportTime()12669 testGetLatestReportTimeInBatchWindow_oneAggReport_oneEventReport_oneAfterBatchWindow_returnFirstReportTime() { 12670 long firstScheduledReportTime = 1L; 12671 long secondScheduledReportTime = 12672 firstScheduledReportTime 12673 + MEASUREMENT_REPORTING_JOB_SERVICE_BATCH_WINDOW_MILLIS 12674 + 1L; 12675 12676 String sourceId = "source1"; 12677 Source source = 12678 SourceFixture.getMinimalValidSourceBuilder() 12679 .setEventId(new UnsignedLong(1L)) 12680 .setId(sourceId) 12681 .build(); 12682 12683 String destination = WebUtil.validUrl("https://destination-1.test"); 12684 AggregateReport aggregateReport = 12685 generateMockAggregateReport(destination, 1, sourceId, firstScheduledReportTime); 12686 EventReport eventReport = 12687 generateMockEventReport(destination, 1, secondScheduledReportTime); 12688 12689 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 12690 Objects.requireNonNull(db); 12691 insertSource(source, source.getId()); 12692 getEventReportConsumer(db).accept(eventReport); 12693 getAggregateReportConsumer(db).accept(aggregateReport); 12694 12695 Long result = 12696 mDatastoreManager 12697 .runInTransactionWithResult( 12698 measurementDao -> 12699 measurementDao.getLatestReportTimeInBatchWindow( 12700 mFlags 12701 .getMeasurementReportingJobServiceBatchWindowMillis())) 12702 .orElseThrow(); 12703 12704 assertEquals(firstScheduledReportTime, result.longValue()); 12705 } 12706 12707 @Test 12708 public void testGetLatestReportTimeInBatchWindow_manyAggReport_manyEventReport_returnLatestReportTime()12709 testGetLatestReportTimeInBatchWindow_manyAggReport_manyEventReport_returnLatestReportTime() { 12710 String sourceId = "source1"; 12711 Source source = 12712 SourceFixture.getMinimalValidSourceBuilder() 12713 .setEventId(new UnsignedLong(1L)) 12714 .setId(sourceId) 12715 .build(); 12716 List<EventReport> eventReports = new ArrayList<>(); 12717 List<AggregateReport> aggregateReports = new ArrayList<>(); 12718 int firstReportTime = 1; 12719 String destination = WebUtil.validUrl("https://destination-1.test"); 12720 for (int i = firstReportTime; i < MEASUREMENT_MAX_EVENT_REPORTS_PER_DESTINATION; i++) { 12721 eventReports.add( 12722 generateMockEventReport(destination, /* id= */ i, /* reportTime= */ i)); 12723 } 12724 12725 for (int i = firstReportTime; i < MEASUREMENT_MAX_AGGREGATE_REPORTS_PER_SOURCE; i++) { 12726 aggregateReports.add( 12727 generateMockAggregateReport( 12728 destination, /* id= */ i, sourceId, /* reportTime= */ i)); 12729 } 12730 12731 // Add one more aggregate report that is scheduled at the very edge of the batch window. 12732 // The report time for this report should be returned as the report time. 12733 long lastReportTime = MEASUREMENT_REPORTING_JOB_SERVICE_BATCH_WINDOW_MILLIS; 12734 int lastId = MEASUREMENT_MAX_AGGREGATE_REPORTS_PER_SOURCE; 12735 aggregateReports.add( 12736 generateMockAggregateReport(destination, lastId, sourceId, lastReportTime)); 12737 12738 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 12739 Objects.requireNonNull(db); 12740 insertSource(source, source.getId()); 12741 eventReports.forEach(getEventReportConsumer(db)); 12742 aggregateReports.forEach(getAggregateReportConsumer(db)); 12743 12744 Long result = 12745 mDatastoreManager 12746 .runInTransactionWithResult( 12747 measurementDao -> 12748 measurementDao.getLatestReportTimeInBatchWindow( 12749 mFlags 12750 .getMeasurementReportingJobServiceBatchWindowMillis())) 12751 .orElseThrow(); 12752 12753 assertEquals(lastReportTime, result.longValue()); 12754 } 12755 12756 @Test testGetLatestReportTimeInBatchWindow_noReports_returnNull()12757 public void testGetLatestReportTimeInBatchWindow_noReports_returnNull() { 12758 Optional<Long> results = 12759 mDatastoreManager.runInTransactionWithResult( 12760 measurementDao -> 12761 measurementDao.getLatestReportTimeInBatchWindow( 12762 mFlags 12763 .getMeasurementReportingJobServiceBatchWindowMillis())); 12764 12765 assertTrue(results.isEmpty()); 12766 } 12767 12768 @Test testInsertSource_withDestinationLimitPriorityEnabled_fetchesTheSetValue()12769 public void testInsertSource_withDestinationLimitPriorityEnabled_fetchesTheSetValue() { 12770 // Setup 12771 mFlags = mock(Flags.class); 12772 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 12773 doReturn(true).when(mFlags).getMeasurementEnableSourceDestinationLimitPriority(); 12774 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mFlags).getMeasurementDbSizeLimit(); 12775 12776 // Execution 12777 Source validSource = 12778 SourceFixture.getValidSourceBuilder() 12779 .setDestinationLimitPriority( 12780 SourceFixture.ValidSourceParams.DESTINATION_LIMIT_PRIORITY) 12781 .build(); 12782 mDatastoreManager.runInTransaction((dao) -> dao.insertSource(validSource)); 12783 12784 // Assertion 12785 String sourceId = getFirstSourceIdFromDatastore(); 12786 Source source = 12787 mDatastoreManager 12788 .runInTransactionWithResult( 12789 measurementDao -> measurementDao.getSource(sourceId)) 12790 .orElseThrow(() -> new IllegalStateException("Source is null")); 12791 assertEquals( 12792 SourceFixture.ValidSourceParams.DESTINATION_LIMIT_PRIORITY, 12793 source.getDestinationLimitPriority()); 12794 } 12795 12796 @Test testInsertAggregateDebugReportRecords_sourceAndTriggerIdPresent_succeeds()12797 public void testInsertAggregateDebugReportRecords_sourceAndTriggerIdPresent_succeeds() { 12798 // insert source & trigger to honor the foreign key constraint 12799 Source source = 12800 SourceFixture.getMinimalValidSourceBuilder() 12801 .setId("S1") 12802 .setEventId(new UnsignedLong(3L)) 12803 .setEnrollmentId("1") 12804 .build(); 12805 Trigger trigger = 12806 TriggerFixture.getValidTriggerBuilder().setId("T1").setEnrollmentId("2").build(); 12807 AggregateDebugReportRecord validAggregateDebugReportRecord = 12808 new AggregateDebugReportRecord.Builder( 12809 /* reportGenerationTime= */ 1000L, 12810 /* topLevelRegistrant= */ TOP_LEVEL_REGISTRANT_1, 12811 /* registrantApp= */ Uri.parse("com.test1.myapp"), 12812 /* registrationOrigin= */ Uri.parse("https://destination.test"), 12813 /* contributions= */ 9) 12814 .setSourceId("S1") 12815 .setTriggerId("T1") 12816 .build(); 12817 12818 // Execution 12819 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 12820 Objects.requireNonNull(db); 12821 insertSource(source, source.getId()); 12822 AbstractDbIntegrationTest.insertToDb(trigger, db); 12823 mDatastoreManager.runInTransaction( 12824 (dao) -> dao.insertAggregateDebugReportRecord(validAggregateDebugReportRecord)); 12825 12826 // Assertion 12827 try (Cursor cursor = 12828 MeasurementDbHelper.getInstance() 12829 .getReadableDatabase() 12830 .query( 12831 AggregatableDebugReportBudgetTrackerContract.TABLE, 12832 null, 12833 null, 12834 null, 12835 null, 12836 null, 12837 null)) { 12838 assertThat(cursor.moveToNext()).isTrue(); 12839 assertThat(validAggregateDebugReportRecord.getReportGenerationTime()) 12840 .isEqualTo( 12841 cursor.getInt( 12842 cursor.getColumnIndex( 12843 AggregatableDebugReportBudgetTrackerContract 12844 .REPORT_GENERATION_TIME))); 12845 assertThat(validAggregateDebugReportRecord.getTopLevelRegistrant().toString()) 12846 .isEqualTo( 12847 cursor.getString( 12848 cursor.getColumnIndex( 12849 AggregatableDebugReportBudgetTrackerContract 12850 .TOP_LEVEL_REGISTRANT))); 12851 assertThat(validAggregateDebugReportRecord.getRegistrantApp().toString()) 12852 .isEqualTo( 12853 cursor.getString( 12854 cursor.getColumnIndex( 12855 AggregatableDebugReportBudgetTrackerContract 12856 .REGISTRANT_APP))); 12857 assertThat(validAggregateDebugReportRecord.getRegistrationOrigin().toString()) 12858 .isEqualTo( 12859 cursor.getString( 12860 cursor.getColumnIndex( 12861 AggregatableDebugReportBudgetTrackerContract 12862 .REGISTRATION_ORIGIN))); 12863 assertThat(validAggregateDebugReportRecord.getSourceId()) 12864 .isEqualTo( 12865 cursor.getString( 12866 cursor.getColumnIndex( 12867 AggregatableDebugReportBudgetTrackerContract 12868 .SOURCE_ID))); 12869 assertThat(validAggregateDebugReportRecord.getTriggerId()) 12870 .isEqualTo( 12871 cursor.getString( 12872 cursor.getColumnIndex( 12873 AggregatableDebugReportBudgetTrackerContract 12874 .TRIGGER_ID))); 12875 assertThat(validAggregateDebugReportRecord.getContributions()) 12876 .isEqualTo( 12877 cursor.getInt( 12878 cursor.getColumnIndex( 12879 AggregatableDebugReportBudgetTrackerContract 12880 .CONTRIBUTIONS))); 12881 } 12882 } 12883 12884 @Test testInsertAggregateDebugReportRecords_nullTriggerId_succeeds()12885 public void testInsertAggregateDebugReportRecords_nullTriggerId_succeeds() { 12886 // insert source & trigger to honor the foreign key constraint 12887 Source source = 12888 SourceFixture.getMinimalValidSourceBuilder() 12889 .setId("S1") 12890 .setEventId(new UnsignedLong(3L)) 12891 .setEnrollmentId("1") 12892 .build(); 12893 AggregateDebugReportRecord validAggregateDebugReportRecord = 12894 new AggregateDebugReportRecord.Builder( 12895 /* reportGenerationTime= */ 1000L, 12896 /* topLevelRegistrant= */ TOP_LEVEL_REGISTRANT_1, 12897 /* registrantApp= */ Uri.parse("com.test1.myapp"), 12898 /* registrationOrigin= */ Uri.parse("https://destination.test"), 12899 /* contributions= */ 9) 12900 .setSourceId("S1") 12901 .build(); 12902 12903 // Execution 12904 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 12905 Objects.requireNonNull(db); 12906 insertSource(source, source.getId()); 12907 mDatastoreManager.runInTransaction( 12908 (dao) -> dao.insertAggregateDebugReportRecord(validAggregateDebugReportRecord)); 12909 12910 // Assertion 12911 try (Cursor cursor = 12912 MeasurementDbHelper.getInstance() 12913 .getReadableDatabase() 12914 .query( 12915 AggregatableDebugReportBudgetTrackerContract.TABLE, 12916 null, 12917 null, 12918 null, 12919 null, 12920 null, 12921 null)) { 12922 assertThat(cursor.moveToNext()).isTrue(); 12923 assertThat(validAggregateDebugReportRecord.getReportGenerationTime()) 12924 .isEqualTo( 12925 cursor.getInt( 12926 cursor.getColumnIndex( 12927 AggregatableDebugReportBudgetTrackerContract 12928 .REPORT_GENERATION_TIME))); 12929 assertThat(validAggregateDebugReportRecord.getTopLevelRegistrant().toString()) 12930 .isEqualTo( 12931 cursor.getString( 12932 cursor.getColumnIndex( 12933 AggregatableDebugReportBudgetTrackerContract 12934 .TOP_LEVEL_REGISTRANT))); 12935 assertThat(validAggregateDebugReportRecord.getRegistrantApp().toString()) 12936 .isEqualTo( 12937 cursor.getString( 12938 cursor.getColumnIndex( 12939 AggregatableDebugReportBudgetTrackerContract 12940 .REGISTRANT_APP))); 12941 assertThat(validAggregateDebugReportRecord.getRegistrationOrigin().toString()) 12942 .isEqualTo( 12943 cursor.getString( 12944 cursor.getColumnIndex( 12945 AggregatableDebugReportBudgetTrackerContract 12946 .REGISTRATION_ORIGIN))); 12947 assertThat(validAggregateDebugReportRecord.getSourceId()) 12948 .isEqualTo( 12949 cursor.getString( 12950 cursor.getColumnIndex( 12951 AggregatableDebugReportBudgetTrackerContract 12952 .SOURCE_ID))); 12953 assertThat(validAggregateDebugReportRecord.getTriggerId()) 12954 .isEqualTo( 12955 cursor.getString( 12956 cursor.getColumnIndex( 12957 AggregatableDebugReportBudgetTrackerContract 12958 .TRIGGER_ID))); 12959 assertThat(validAggregateDebugReportRecord.getContributions()) 12960 .isEqualTo( 12961 cursor.getInt( 12962 cursor.getColumnIndex( 12963 AggregatableDebugReportBudgetTrackerContract 12964 .CONTRIBUTIONS))); 12965 } 12966 } 12967 12968 @Test testInsertAggregateDebugReportRecords_nullSourceAndTriggerId_succeeds()12969 public void testInsertAggregateDebugReportRecords_nullSourceAndTriggerId_succeeds() { 12970 AggregateDebugReportRecord validAggregateDebugReportRecord = 12971 new AggregateDebugReportRecord.Builder( 12972 /* reportGenerationTime= */ 1000L, 12973 /* topLevelRegistrant= */ TOP_LEVEL_REGISTRANT_1, 12974 /* registrantApp= */ Uri.parse("com.test1.myapp"), 12975 /* registrationOrigin= */ Uri.parse("https://destination.test"), 12976 /* contributions= */ 9) 12977 .build(); 12978 12979 // Execution 12980 mDatastoreManager.runInTransaction( 12981 (dao) -> dao.insertAggregateDebugReportRecord(validAggregateDebugReportRecord)); 12982 12983 // Assertion 12984 try (Cursor cursor = 12985 MeasurementDbHelper.getInstance() 12986 .getReadableDatabase() 12987 .query( 12988 AggregatableDebugReportBudgetTrackerContract.TABLE, 12989 null, 12990 null, 12991 null, 12992 null, 12993 null, 12994 null)) { 12995 assertThat(cursor.moveToNext()).isTrue(); 12996 assertThat(validAggregateDebugReportRecord.getReportGenerationTime()) 12997 .isEqualTo( 12998 cursor.getInt( 12999 cursor.getColumnIndex( 13000 AggregatableDebugReportBudgetTrackerContract 13001 .REPORT_GENERATION_TIME))); 13002 assertThat(validAggregateDebugReportRecord.getTopLevelRegistrant().toString()) 13003 .isEqualTo( 13004 cursor.getString( 13005 cursor.getColumnIndex( 13006 AggregatableDebugReportBudgetTrackerContract 13007 .TOP_LEVEL_REGISTRANT))); 13008 assertThat(validAggregateDebugReportRecord.getRegistrantApp().toString()) 13009 .isEqualTo( 13010 cursor.getString( 13011 cursor.getColumnIndex( 13012 AggregatableDebugReportBudgetTrackerContract 13013 .REGISTRANT_APP))); 13014 assertThat(validAggregateDebugReportRecord.getRegistrationOrigin().toString()) 13015 .isEqualTo( 13016 cursor.getString( 13017 cursor.getColumnIndex( 13018 AggregatableDebugReportBudgetTrackerContract 13019 .REGISTRATION_ORIGIN))); 13020 assertThat(validAggregateDebugReportRecord.getSourceId()) 13021 .isEqualTo( 13022 cursor.getString( 13023 cursor.getColumnIndex( 13024 AggregatableDebugReportBudgetTrackerContract 13025 .SOURCE_ID))); 13026 assertThat(validAggregateDebugReportRecord.getTriggerId()) 13027 .isEqualTo( 13028 cursor.getString( 13029 cursor.getColumnIndex( 13030 AggregatableDebugReportBudgetTrackerContract 13031 .TRIGGER_ID))); 13032 assertThat(validAggregateDebugReportRecord.getContributions()) 13033 .isEqualTo( 13034 cursor.getInt( 13035 cursor.getColumnIndex( 13036 AggregatableDebugReportBudgetTrackerContract 13037 .CONTRIBUTIONS))); 13038 } 13039 } 13040 13041 @Test testGetTotalAggregateDebugReportBudget()13042 public void testGetTotalAggregateDebugReportBudget() { 13043 // insert source & trigger to honor the foreign key constraint 13044 Source source = 13045 SourceFixture.getMinimalValidSourceBuilder() 13046 .setId("S1") 13047 .setEventId(new UnsignedLong(3L)) 13048 .setEnrollmentId("1") 13049 .build(); 13050 Trigger trigger = 13051 TriggerFixture.getValidTriggerBuilder().setId("T1").setEnrollmentId("2").build(); 13052 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 13053 Objects.requireNonNull(db); 13054 insertSource(source, source.getId()); 13055 AbstractDbIntegrationTest.insertToDb(trigger, db); 13056 13057 // test case 1 (publisher query): outside time window + same publisher 13058 AggregateDebugReportRecord validAggregateDebugReportRecord1 = 13059 new AggregateDebugReportRecord.Builder( 13060 /* reportGenerationTime= */ 1000L, 13061 /* topLevelRegistrant= */ TOP_LEVEL_REGISTRANT_1, 13062 /* registrantApp= */ Uri.parse("com.test.myapp"), 13063 /* registrationOrigin= */ Uri.parse("https://destination0.test"), 13064 /* contributions= */ 9) 13065 .setSourceId("S1") 13066 .setTriggerId("T1") 13067 .build(); 13068 13069 // test case 2 (publisher query): inside time window + same publisher + different origin 13070 AggregateDebugReportRecord validAggregateDebugReportRecord2 = 13071 new AggregateDebugReportRecord.Builder( 13072 /* reportGenerationTime= */ 2000L, 13073 /* topLevelRegistrant= */ TOP_LEVEL_REGISTRANT_1, 13074 /* registrantApp= */ Uri.parse("com.test.myapp"), 13075 /* registrationOrigin= */ Uri.parse("https://destination1.test"), 13076 /* contributions= */ 16) 13077 .setSourceId("S1") 13078 .setTriggerId("T1") 13079 .build(); 13080 AggregateDebugReportRecord validAggregateDebugReportRecord3 = 13081 new AggregateDebugReportRecord.Builder( 13082 /* reportGenerationTime= */ 3000L, 13083 /* topLevelRegistrant= */ TOP_LEVEL_REGISTRANT_1, 13084 /* registrantApp= */ Uri.parse("com.test.myapp"), 13085 /* registrationOrigin= */ Uri.parse("https://destination2.test"), 13086 /* contributions= */ 25) 13087 .setSourceId("S1") 13088 .setTriggerId("T1") 13089 .build(); 13090 AggregateDebugReportRecord validAggregateDebugReportRecord4 = 13091 new AggregateDebugReportRecord.Builder( 13092 /* reportGenerationTime= */ 4000L, 13093 /* topLevelRegistrant= */ TOP_LEVEL_REGISTRANT_1, 13094 /* registrantApp= */ Uri.parse("com.test.myapp"), 13095 /* registrationOrigin= */ Uri.parse("https://destination3.test"), 13096 /* contributions= */ 36) 13097 .setSourceId("S1") 13098 .setTriggerId("T1") 13099 .build(); 13100 13101 // test case 3 (publisher + origin query): inside time window + same publisher + same origin 13102 AggregateDebugReportRecord validAggregateDebugReportRecord5 = 13103 new AggregateDebugReportRecord.Builder( 13104 /* reportGenerationTime= */ 5000L, 13105 /* topLevelRegistrant= */ TOP_LEVEL_REGISTRANT_2, 13106 /* registrantApp= */ Uri.parse("com.test.myapp"), 13107 /* registrationOrigin= */ Uri.parse("https://destination4.test"), 13108 /* contributions= */ 49) 13109 .setSourceId("S1") 13110 .setTriggerId("T1") 13111 .build(); 13112 AggregateDebugReportRecord validAggregateDebugReportRecord6 = 13113 new AggregateDebugReportRecord.Builder( 13114 /* reportGenerationTime= */ 6000L, 13115 /* topLevelRegistrant= */ TOP_LEVEL_REGISTRANT_2, 13116 /* registrantApp= */ Uri.parse("com.test.myapp"), 13117 /* registrationOrigin= */ Uri.parse("https://destination4.test"), 13118 /* contributions= */ 64) 13119 .setSourceId("S1") 13120 .setTriggerId("T1") 13121 .build(); 13122 List<AggregateDebugReportRecord> validAggregateDebugReportRecords = 13123 Arrays.asList( 13124 validAggregateDebugReportRecord1, 13125 validAggregateDebugReportRecord2, 13126 validAggregateDebugReportRecord3, 13127 validAggregateDebugReportRecord4, 13128 validAggregateDebugReportRecord5, 13129 validAggregateDebugReportRecord6); 13130 13131 for (AggregateDebugReportRecord validAggregateDebugReportRecord : 13132 validAggregateDebugReportRecords) { 13133 mDatastoreManager.runInTransaction( 13134 (dao) -> dao.insertAggregateDebugReportRecord(validAggregateDebugReportRecord)); 13135 } 13136 13137 // test case 1 13138 int budget1 = 13139 mDatastoreManager 13140 .runInTransactionWithResult( 13141 (dao) -> 13142 dao.sumAggregateDebugReportBudgetXPublisherXWindow( 13143 /* publisher= */ TOP_LEVEL_REGISTRANT_1, 13144 /* publisherType= */ EventSurfaceType.APP, 13145 /* windowStartTime= */ 1000L)) 13146 .get(); 13147 assertThat(budget1).isEqualTo((16 + 25 + 36)); 13148 13149 // test case 2 13150 int budget2 = 13151 mDatastoreManager 13152 .runInTransactionWithResult( 13153 (dao) -> 13154 dao.sumAggregateDebugReportBudgetXPublisherXWindow( 13155 /* publisher= */ TOP_LEVEL_REGISTRANT_1, 13156 /* publisherType= */ EventSurfaceType.APP, 13157 /* windowStartTime= */ 1001L)) 13158 .get(); 13159 assertThat(budget2).isEqualTo((16 + 25 + 36)); 13160 13161 // test case 3 13162 int budget3 = 13163 mDatastoreManager 13164 .runInTransactionWithResult( 13165 (dao) -> 13166 dao.sumAggregateDebugReportBudgetXOriginXPublisherXWindow( 13167 /* publisher= */ TOP_LEVEL_REGISTRANT_2, 13168 /* publisherType= */ EventSurfaceType.APP, 13169 /* origin= */ Uri.parse( 13170 "https://destination4.test"), 13171 /* windowStartTime= */ 1001L)) 13172 .get(); 13173 assertThat(budget3).isEqualTo((49 + 64)); 13174 } 13175 13176 @Test updateSourceAggregateDebugContributions_updateFromPreValue_success()13177 public void updateSourceAggregateDebugContributions_updateFromPreValue_success() { 13178 // Setup 13179 mFlags = mock(Flags.class); 13180 ExtendedMockito.doReturn(mFlags).when(FlagsFactory::getFlags); 13181 doReturn(true).when(mFlags).getMeasurementEnableAggregateDebugReporting(); 13182 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mFlags).getMeasurementDbSizeLimit(); 13183 int initialContributions = 1024; 13184 Source source = 13185 SourceFixture.getValidSourceBuilder() 13186 .setAggregateDebugReportContributions(initialContributions) 13187 .build(); 13188 insertSource(source, source.getId()); 13189 13190 // Verify persisted value 13191 assertThat(getFirstSourceFromDb().getAggregateDebugReportContributions()) 13192 .isEqualTo(initialContributions); 13193 13194 // Execution 13195 // Update the value 13196 int expectedUpdatedContributions = 65536; 13197 source.setAggregateDebugContributions(expectedUpdatedContributions); 13198 mDatastoreManager.runInTransaction( 13199 measurementDao -> measurementDao.updateSourceAggregateDebugContributions(source)); 13200 13201 // Verification 13202 assertThat(getFirstSourceFromDb().getAggregateDebugReportContributions()) 13203 .isEqualTo(expectedUpdatedContributions); 13204 } 13205 getFirstSourceFromDb()13206 private Source getFirstSourceFromDb() { 13207 return mDatastoreManager 13208 .runInTransactionWithResult( 13209 measurementDao -> 13210 measurementDao.getSource( 13211 getFirstIdFromDatastore( 13212 SourceContract.TABLE, SourceContract.ID))) 13213 .orElseThrow(); 13214 } 13215 getAggregateReportConsumer(SQLiteDatabase db)13216 private static Consumer<AggregateReport> getAggregateReportConsumer(SQLiteDatabase db) { 13217 Consumer<AggregateReport> aggregateReportConsumer = 13218 aggregateReport -> { 13219 ContentValues values = new ContentValues(); 13220 values.put(MeasurementTables.AggregateReport.ID, aggregateReport.getId()); 13221 values.put( 13222 MeasurementTables.AggregateReport.SOURCE_ID, 13223 aggregateReport.getSourceId()); 13224 values.put( 13225 MeasurementTables.AggregateReport.ATTRIBUTION_DESTINATION, 13226 aggregateReport.getAttributionDestination().toString()); 13227 values.put( 13228 MeasurementTables.AggregateReport.SCHEDULED_REPORT_TIME, 13229 aggregateReport.getScheduledReportTime()); 13230 values.put( 13231 MeasurementTables.AggregateReport.STATUS, aggregateReport.getStatus()); 13232 db.insert(MeasurementTables.AggregateReport.TABLE, null, values); 13233 }; 13234 return aggregateReportConsumer; 13235 } 13236 getEventReportConsumer(SQLiteDatabase db)13237 private static Consumer<EventReport> getEventReportConsumer(SQLiteDatabase db) { 13238 Consumer<EventReport> eventReportConsumer = 13239 eventReport -> { 13240 ContentValues values = new ContentValues(); 13241 values.put(EventReportContract.ID, eventReport.getId()); 13242 values.put( 13243 EventReportContract.ATTRIBUTION_DESTINATION, 13244 eventReport.getAttributionDestinations().get(0).toString()); 13245 values.put(EventReportContract.REPORT_TIME, eventReport.getReportTime()); 13246 values.put(EventReportContract.STATUS, EventReport.Status.PENDING); 13247 13248 db.insert(EventReportContract.TABLE, null, values); 13249 }; 13250 return eventReportConsumer; 13251 } 13252 insertInDb(SQLiteDatabase db, Source source)13253 private void insertInDb(SQLiteDatabase db, Source source) { 13254 ContentValues values = new ContentValues(); 13255 values.put(SourceContract.ID, source.getId()); 13256 values.put(SourceContract.STATUS, Source.Status.ACTIVE); 13257 values.put(SourceContract.EVENT_TIME, source.getEventTime()); 13258 values.put(SourceContract.EXPIRY_TIME, source.getExpiryTime()); 13259 values.put(SourceContract.ENROLLMENT_ID, source.getEnrollmentId()); 13260 values.put(SourceContract.PUBLISHER, source.getPublisher().toString()); 13261 values.put(SourceContract.REGISTRANT, source.getRegistrant().toString()); 13262 values.put(SourceContract.REGISTRATION_ORIGIN, source.getRegistrationOrigin().toString()); 13263 if (source.getAttributedTriggers() != null) { 13264 values.put(SourceContract.EVENT_ATTRIBUTION_STATUS, source.attributedTriggersToJson()); 13265 } 13266 if (source.getTriggerSpecs() != null) { 13267 values.put(SourceContract.TRIGGER_SPECS, source.getTriggerSpecs().encodeToJson()); 13268 } 13269 db.insert(SourceContract.TABLE, null, values); 13270 13271 // Insert source destinations 13272 if (source.getAppDestinations() != null) { 13273 for (Uri appDestination : source.getAppDestinations()) { 13274 ContentValues destinationValues = new ContentValues(); 13275 destinationValues.put( 13276 MeasurementTables.SourceDestination.SOURCE_ID, source.getId()); 13277 destinationValues.put( 13278 MeasurementTables.SourceDestination.DESTINATION_TYPE, EventSurfaceType.APP); 13279 destinationValues.put( 13280 MeasurementTables.SourceDestination.DESTINATION, appDestination.toString()); 13281 db.insert(MeasurementTables.SourceDestination.TABLE, null, destinationValues); 13282 } 13283 } 13284 13285 if (source.getWebDestinations() != null) { 13286 for (Uri webDestination : source.getWebDestinations()) { 13287 ContentValues destinationValues = new ContentValues(); 13288 destinationValues.put( 13289 MeasurementTables.SourceDestination.SOURCE_ID, source.getId()); 13290 destinationValues.put( 13291 MeasurementTables.SourceDestination.DESTINATION_TYPE, EventSurfaceType.WEB); 13292 destinationValues.put( 13293 MeasurementTables.SourceDestination.DESTINATION, webDestination.toString()); 13294 db.insert(MeasurementTables.SourceDestination.TABLE, null, destinationValues); 13295 } 13296 } 13297 } 13298 queryAndAssertSourceEntries( SQLiteDatabase db, String enrollmentId, List<String> expectedSourceIds)13299 private void queryAndAssertSourceEntries( 13300 SQLiteDatabase db, String enrollmentId, List<String> expectedSourceIds) { 13301 try (Cursor cursor = 13302 db.query( 13303 XnaIgnoredSourcesContract.TABLE, 13304 new String[] {XnaIgnoredSourcesContract.SOURCE_ID}, 13305 XnaIgnoredSourcesContract.ENROLLMENT_ID + " = ?", 13306 new String[] {enrollmentId}, 13307 null, 13308 null, 13309 null)) { 13310 assertEquals(expectedSourceIds.size(), cursor.getCount()); 13311 for (int i = 0; i < expectedSourceIds.size() && cursor.moveToNext(); i++) { 13312 assertEquals(expectedSourceIds.get(i), cursor.getString(0)); 13313 } 13314 } 13315 } 13316 createSourceBuilder()13317 private Source.Builder createSourceBuilder() { 13318 return new Source.Builder() 13319 .setEventId(SourceFixture.ValidSourceParams.SOURCE_EVENT_ID) 13320 .setPublisher(SourceFixture.ValidSourceParams.PUBLISHER) 13321 .setAppDestinations(SourceFixture.ValidSourceParams.ATTRIBUTION_DESTINATIONS) 13322 .setWebDestinations(SourceFixture.ValidSourceParams.WEB_DESTINATIONS) 13323 .setEnrollmentId(SourceFixture.ValidSourceParams.ENROLLMENT_ID) 13324 .setRegistrant(SourceFixture.ValidSourceParams.REGISTRANT) 13325 .setEventTime(SOURCE_EVENT_TIME) 13326 .setExpiryTime(SourceFixture.ValidSourceParams.EXPIRY_TIME) 13327 .setPriority(SourceFixture.ValidSourceParams.PRIORITY) 13328 .setSourceType(SourceFixture.ValidSourceParams.SOURCE_TYPE) 13329 .setInstallAttributionWindow( 13330 SourceFixture.ValidSourceParams.INSTALL_ATTRIBUTION_WINDOW) 13331 .setInstallCooldownWindow(SourceFixture.ValidSourceParams.INSTALL_COOLDOWN_WINDOW) 13332 .setAttributionMode(SourceFixture.ValidSourceParams.ATTRIBUTION_MODE) 13333 .setAggregateSource(SourceFixture.ValidSourceParams.buildAggregateSource()) 13334 .setFilterDataString(SourceFixture.ValidSourceParams.buildFilterDataString()) 13335 .setSharedFilterDataKeys(SourceFixture.ValidSourceParams.SHARED_FILTER_DATA_KEYS) 13336 .setIsDebugReporting(true) 13337 .setRegistrationId(UUID.randomUUID().toString()) 13338 .setSharedAggregationKeys(SHARED_AGGREGATE_KEYS) 13339 .setInstallTime(SourceFixture.ValidSourceParams.INSTALL_TIME) 13340 .setRegistrationOrigin(SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN); 13341 } 13342 createAggregateReportForSourceAndTrigger( Source source, Trigger trigger)13343 private AggregateReport createAggregateReportForSourceAndTrigger( 13344 Source source, Trigger trigger) { 13345 return createAggregateReportForSourceAndTrigger( 13346 UUID.randomUUID().toString(), source, trigger); 13347 } 13348 createEventReportForSourceAndTrigger(Source source, Trigger trigger)13349 private EventReport createEventReportForSourceAndTrigger(Source source, Trigger trigger) 13350 throws JSONException { 13351 return createEventReportForSourceAndTrigger(UUID.randomUUID().toString(), source, trigger); 13352 } 13353 createAggregateReportForSourceAndTrigger( String reportId, Source source, Trigger trigger)13354 private AggregateReport createAggregateReportForSourceAndTrigger( 13355 String reportId, Source source, Trigger trigger) { 13356 return AggregateReportFixture.getValidAggregateReportBuilder() 13357 .setId(reportId) 13358 .setSourceId(source.getId()) 13359 .setTriggerId(trigger.getId()) 13360 .setTriggerTime(trigger.getTriggerTime()) 13361 .build(); 13362 } 13363 createEventReportForSourceAndTrigger( String reportId, Source source, Trigger trigger)13364 private EventReport createEventReportForSourceAndTrigger( 13365 String reportId, Source source, Trigger trigger) throws JSONException { 13366 EventTrigger eventTrigger = trigger.parseEventTriggers( 13367 FakeFlagsFactory.getFlagsForTest()).get(0); 13368 return new EventReport.Builder() 13369 .populateFromSourceAndTrigger( 13370 source, 13371 trigger, 13372 eventTrigger.getTriggerData(), 13373 eventTrigger, 13374 new Pair<>(null, null), 13375 new EventReportWindowCalcDelegate(mFlags), 13376 new SourceNoiseHandler(mFlags), 13377 source.getAttributionDestinations(trigger.getDestinationType())) 13378 .setId(reportId) 13379 .setSourceEventId(source.getEventId()) 13380 .setSourceId(source.getId()) 13381 .setTriggerId(trigger.getId()) 13382 .build(); 13383 } 13384 createEventReportForSourceAndTriggerForUninstall( String reportId, Source source, Trigger trigger)13385 private EventReport createEventReportForSourceAndTriggerForUninstall( 13386 String reportId, Source source, Trigger trigger) throws JSONException { 13387 EventTrigger eventTrigger = 13388 trigger.parseEventTriggers(FakeFlagsFactory.getFlagsForTest()).get(0); 13389 return new EventReport.Builder() 13390 .setTriggerTime(trigger.getTriggerTime()) 13391 .setSourceEventId(source.getEventId()) 13392 .setEnrollmentId(source.getEnrollmentId()) 13393 .setStatus(EventReport.Status.PENDING) 13394 .setSourceType(source.getSourceType()) 13395 .setDebugReportStatus(EventReport.DebugReportStatus.NONE) 13396 .setRegistrationOrigin(trigger.getRegistrationOrigin()) 13397 .setTriggerPriority(eventTrigger.getTriggerPriority()) 13398 .setTriggerData(eventTrigger.getTriggerData()) 13399 .setId(reportId) 13400 .setAttributionDestinations( 13401 source.getAttributionDestinations(trigger.getDestinationType())) 13402 .setSourceId(source.getId()) 13403 .setTriggerId(trigger.getId()) 13404 .build(); 13405 } 13406 createDebugReport()13407 private DebugReport createDebugReport() { 13408 return createDebugReport(UUID.randomUUID().toString(), REGISTRANT, INSERTION_TIME); 13409 } 13410 createDebugReport(String id, Uri registrant, long insertionTime)13411 private DebugReport createDebugReport(String id, Uri registrant, long insertionTime) { 13412 return new DebugReport.Builder() 13413 .setId(id) 13414 .setType("trigger-event-deduplicated") 13415 .setBody( 13416 " {\n" 13417 + " \"attribution_destination\":" 13418 + " \"https://destination.example\",\n" 13419 + " \"source_event_id\": \"45623\"\n" 13420 + " }") 13421 .setEnrollmentId("1") 13422 .setRegistrationOrigin(REGISTRATION_ORIGIN) 13423 .setRegistrant(registrant) 13424 .setInsertionTime(insertionTime) 13425 .build(); 13426 } 13427 buildDebugReportWithInstalledRegistrant(String id)13428 private DebugReport buildDebugReportWithInstalledRegistrant(String id) { 13429 return new DebugReport.Builder() 13430 .setId(id) 13431 .setType("trigger-event-deduplicated") 13432 .setBody( 13433 " {\n" 13434 + " \"attribution_destination\":" 13435 + " \"https://destination.example\",\n" 13436 + " \"source_event_id\": \"45623\"\n" 13437 + " }") 13438 .setEnrollmentId("1") 13439 .setRegistrationOrigin(REGISTRATION_ORIGIN) 13440 .setRegistrant(INSTALLED_REGISTRANT) 13441 .setInsertionTime(INSERTION_TIME) 13442 .build(); 13443 } 13444 buildDebugReportWithNotInstalledRegistrant(String id)13445 private DebugReport buildDebugReportWithNotInstalledRegistrant(String id) { 13446 return new DebugReport.Builder() 13447 .setId(id) 13448 .setType("trigger-event-deduplicated") 13449 .setBody( 13450 " {\n" 13451 + " \"attribution_destination\":" 13452 + " \"https://destination.example\",\n" 13453 + " \"source_event_id\": \"45623\"\n" 13454 + " }") 13455 .setEnrollmentId("1") 13456 .setRegistrationOrigin(REGISTRATION_ORIGIN) 13457 .setRegistrant(NOT_INSTALLED_REGISTRANT) 13458 .setInsertionTime(INSERTION_TIME) 13459 .build(); 13460 } 13461 buildRegistrant(String appName)13462 private Uri buildRegistrant(String appName) { 13463 return Uri.parse("android-app://" + appName); 13464 } 13465 insertAsyncRecordForPackageName(Uri... registrants)13466 private void insertAsyncRecordForPackageName(Uri... registrants) { 13467 for (Uri registrant : registrants) { 13468 AsyncRegistration validRecord = 13469 AsyncRegistrationFixture.getValidAsyncRegistrationBuilder() 13470 .setRegistrant(registrant) 13471 .build(); 13472 13473 mDatastoreManager.runInTransaction((dao) -> dao.insertAsyncRegistration(validRecord)); 13474 } 13475 } 13476 insertSourceForPackageName(Uri... registrants)13477 private void insertSourceForPackageName(Uri... registrants) { 13478 for (Uri registrant : registrants) { 13479 Source validSource = 13480 SourceFixture.getValidSourceBuilder().setRegistrant(registrant).build(); 13481 13482 mDatastoreManager.runInTransaction((dao) -> dao.insertSource(validSource)); 13483 } 13484 } 13485 insertSourceForAttributionScope( List<String> attributionScopes, Long attributionScopeLimit, Long maxEventStates, long eventTime, List<Uri> webDestinations, List<Uri> appDestinations)13486 private Source insertSourceForAttributionScope( 13487 List<String> attributionScopes, 13488 Long attributionScopeLimit, 13489 Long maxEventStates, 13490 long eventTime, 13491 List<Uri> webDestinations, 13492 List<Uri> appDestinations) { 13493 return insertSourceForAttributionScope( 13494 attributionScopes, 13495 attributionScopeLimit, 13496 maxEventStates, 13497 eventTime, 13498 webDestinations, 13499 appDestinations, 13500 SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN); 13501 } 13502 insertSourceForAttributionScope( List<String> attributionScopes, Long attributionScopeLimit, Long maxEventStates, long eventTime, List<Uri> webDestinations, List<Uri> appDestinations, @NonNull Uri reportingOrigin)13503 private Source insertSourceForAttributionScope( 13504 List<String> attributionScopes, 13505 Long attributionScopeLimit, 13506 Long maxEventStates, 13507 long eventTime, 13508 List<Uri> webDestinations, 13509 List<Uri> appDestinations, 13510 @NonNull Uri reportingOrigin) { 13511 return insertSourceForAttributionScope( 13512 attributionScopes, 13513 attributionScopeLimit, 13514 maxEventStates, 13515 eventTime, 13516 webDestinations, 13517 appDestinations, 13518 reportingOrigin, 13519 SourceFixture.ValidSourceParams.REGISTRATION_ID, 13520 Source.SourceType.EVENT, 13521 Source.Status.ACTIVE); 13522 } 13523 insertSourceForAttributionScope( List<String> attributionScopes, Long attributionScopeLimit, Long maxEventStates, long eventTime, List<Uri> webDestinations, List<Uri> appDestinations, @NonNull Uri reportingOrigin, String registrationId, Source.SourceType sourceType, @Source.Status int status)13524 private Source insertSourceForAttributionScope( 13525 List<String> attributionScopes, 13526 Long attributionScopeLimit, 13527 Long maxEventStates, 13528 long eventTime, 13529 List<Uri> webDestinations, 13530 List<Uri> appDestinations, 13531 @NonNull Uri reportingOrigin, 13532 String registrationId, 13533 Source.SourceType sourceType, 13534 @Source.Status int status) { 13535 Source validSource = 13536 SourceFixture.getValidSourceBuilder() 13537 .setEventTime(eventTime) 13538 .setAttributionScopeLimit(attributionScopeLimit) 13539 .setMaxEventStates(maxEventStates) 13540 .setWebDestinations(webDestinations) 13541 .setAppDestinations(appDestinations) 13542 .setAttributionScopes(attributionScopes) 13543 .setRegistrationOrigin(reportingOrigin) 13544 .setSourceType(sourceType) 13545 .setStatus(status) 13546 .setRegistrationId(registrationId) 13547 .build(); 13548 13549 mDatastoreManager.runInTransaction( 13550 (dao) -> { 13551 dao.insertSource(validSource); 13552 Source insertedSource = dao.getSource(validSource.getId()); 13553 boolean attributionScopeEnabled = mFlags.getMeasurementEnableAttributionScope(); 13554 assertThat(insertedSource.getMaxEventStates()) 13555 .isEqualTo(attributionScopeEnabled ? maxEventStates : null); 13556 assertThat(insertedSource.getAttributionScopeLimit()) 13557 .isEqualTo(attributionScopeEnabled ? attributionScopeLimit : null); 13558 assertThat(dao.getSourceAttributionScopes(validSource.getId())) 13559 .containsExactlyElementsIn( 13560 (!attributionScopeEnabled || attributionScopes == null) 13561 ? List.of() 13562 : attributionScopes); 13563 }); 13564 return validSource; 13565 } 13566 insertSourceForAggregatableNamedBudgets( AggregatableNamedBudgets aggregatableNamedBudgets, long eventTime, List<Uri> webDestinations, List<Uri> appDestinations)13567 private Source insertSourceForAggregatableNamedBudgets( 13568 AggregatableNamedBudgets aggregatableNamedBudgets, 13569 long eventTime, 13570 List<Uri> webDestinations, 13571 List<Uri> appDestinations) { 13572 Source validSource = 13573 SourceFixture.getValidSourceBuilder() 13574 .setEventTime(eventTime) 13575 .setWebDestinations(webDestinations) 13576 .setAppDestinations(appDestinations) 13577 .setRegistrationOrigin(SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN) 13578 .setSourceType(Source.SourceType.EVENT) 13579 .setStatus(Source.Status.ACTIVE) 13580 .setRegistrationId(SourceFixture.ValidSourceParams.REGISTRATION_ID) 13581 .setAggregatableNamedBudgets(aggregatableNamedBudgets) 13582 .build(); 13583 13584 mDatastoreManager.runInTransaction((dao) -> dao.insertSource(validSource)); 13585 return validSource; 13586 } 13587 insertSourceForPopulatingAggregatableNamedBudgets( long eventTime, List<Uri> webDestinations, List<Uri> appDestinations)13588 private Source insertSourceForPopulatingAggregatableNamedBudgets( 13589 long eventTime, List<Uri> webDestinations, List<Uri> appDestinations) { 13590 Source validSource = 13591 SourceFixture.getValidSourceBuilder() 13592 .setEventTime(eventTime) 13593 .setWebDestinations(webDestinations) 13594 .setAppDestinations(appDestinations) 13595 .setRegistrationOrigin(SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN) 13596 .setSourceType(Source.SourceType.EVENT) 13597 .setStatus(Source.Status.ACTIVE) 13598 .setRegistrationId(SourceFixture.ValidSourceParams.REGISTRATION_ID) 13599 .build(); 13600 13601 mDatastoreManager.runInTransaction((dao) -> dao.insertSource(validSource)); 13602 return validSource; 13603 } 13604 insertTriggerForPackageName(Uri... registrants)13605 private void insertTriggerForPackageName(Uri... registrants) { 13606 for (Uri registrant : registrants) { 13607 Trigger validTrigger = 13608 TriggerFixture.getValidTriggerBuilder().setRegistrant(registrant).build(); 13609 13610 mDatastoreManager.runInTransaction((dao) -> dao.insertTrigger(validTrigger)); 13611 } 13612 } 13613 setupSourceAndTriggerData()13614 private void setupSourceAndTriggerData() { 13615 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 13616 List<Source> sourcesList = new ArrayList<>(); 13617 sourcesList.add( 13618 SourceFixture.getMinimalValidSourceBuilder() 13619 .setId("S1") 13620 .setRegistrant(APP_TWO_SOURCES) 13621 .setPublisher(APP_TWO_PUBLISHER) 13622 .setPublisherType(EventSurfaceType.APP) 13623 .build()); 13624 sourcesList.add( 13625 SourceFixture.getMinimalValidSourceBuilder() 13626 .setId("S2") 13627 .setRegistrant(APP_TWO_SOURCES) 13628 .setPublisher(APP_TWO_PUBLISHER) 13629 .setPublisherType(EventSurfaceType.APP) 13630 .build()); 13631 sourcesList.add( 13632 SourceFixture.getMinimalValidSourceBuilder() 13633 .setId("S3") 13634 .setRegistrant(APP_ONE_SOURCE) 13635 .setPublisher(APP_ONE_PUBLISHER) 13636 .setPublisherType(EventSurfaceType.APP) 13637 .build()); 13638 sourcesList.add( 13639 SourceFixture.getMinimalValidSourceBuilder() 13640 .setId("S4") 13641 .setRegistrant(APP_ONE_SOURCE) 13642 .setPublisher(APP_ONE_PUBLISHER) 13643 .setPublisherType(EventSurfaceType.APP) 13644 .setStatus(Source.Status.MARKED_TO_DELETE) 13645 .build()); 13646 sourcesList.add( 13647 SourceFixture.getMinimalValidSourceBuilder() 13648 .setId("S5") 13649 .setRegistrant(APP_TWO_SOURCES) 13650 .setPublisher(APP_TWO_PUBLISHER) 13651 .setPublisherType(EventSurfaceType.APP) 13652 .setStatus(Source.Status.MARKED_TO_DELETE) 13653 .build()); 13654 sourcesList.forEach(source -> insertSource(source, source.getId())); 13655 List<Trigger> triggersList = new ArrayList<>(); 13656 triggersList.add( 13657 TriggerFixture.getValidTriggerBuilder() 13658 .setId("T1") 13659 .setRegistrant(APP_TWO_DESTINATION) 13660 .build()); 13661 triggersList.add( 13662 TriggerFixture.getValidTriggerBuilder() 13663 .setId("T2") 13664 .setRegistrant(APP_TWO_DESTINATION) 13665 .build()); 13666 triggersList.add( 13667 TriggerFixture.getValidTriggerBuilder() 13668 .setId("T3") 13669 .setRegistrant(APP_ONE_DESTINATION) 13670 .build()); 13671 13672 // Add web triggers. 13673 triggersList.add( 13674 TriggerFixture.getValidTriggerBuilder() 13675 .setId("T4") 13676 .setRegistrant(APP_BROWSER) 13677 .setAttributionDestination(WEB_ONE_DESTINATION) 13678 .build()); 13679 triggersList.add( 13680 TriggerFixture.getValidTriggerBuilder() 13681 .setId("T5") 13682 .setRegistrant(APP_BROWSER) 13683 .setAttributionDestination(WEB_ONE_DESTINATION_DIFFERENT_SUBDOMAIN) 13684 .build()); 13685 triggersList.add( 13686 TriggerFixture.getValidTriggerBuilder() 13687 .setId("T7") 13688 .setRegistrant(APP_BROWSER) 13689 .setAttributionDestination(WEB_ONE_DESTINATION_DIFFERENT_SUBDOMAIN_2) 13690 .build()); 13691 triggersList.add( 13692 TriggerFixture.getValidTriggerBuilder() 13693 .setId("T8") 13694 .setRegistrant(APP_BROWSER) 13695 .setAttributionDestination(WEB_TWO_DESTINATION) 13696 .build()); 13697 triggersList.add( 13698 TriggerFixture.getValidTriggerBuilder() 13699 .setId("T9") 13700 .setRegistrant(APP_BROWSER) 13701 .setAttributionDestination(WEB_TWO_DESTINATION_WITH_PATH) 13702 .build()); 13703 13704 for (Trigger trigger : triggersList) { 13705 ContentValues values = new ContentValues(); 13706 values.put("_id", trigger.getId()); 13707 values.put("registrant", trigger.getRegistrant().toString()); 13708 values.put("attribution_destination", trigger.getAttributionDestination().toString()); 13709 long row = db.insert("msmt_trigger", null, values); 13710 assertNotEquals("Trigger insertion failed", -1, row); 13711 } 13712 } 13713 createWebTrigger(Uri attributionDestination)13714 private Trigger createWebTrigger(Uri attributionDestination) { 13715 return TriggerFixture.getValidTriggerBuilder() 13716 .setId("ID" + mValueId++) 13717 .setAttributionDestination(attributionDestination) 13718 .setRegistrant(APP_BROWSER) 13719 .build(); 13720 } 13721 createAppTrigger(Uri registrant, Uri destination)13722 private Trigger createAppTrigger(Uri registrant, Uri destination) { 13723 return TriggerFixture.getValidTriggerBuilder() 13724 .setId("ID" + mValueId++) 13725 .setAttributionDestination(destination) 13726 .setRegistrant(registrant) 13727 .build(); 13728 } 13729 addTriggersToDatabase(List<Trigger> triggersList)13730 private void addTriggersToDatabase(List<Trigger> triggersList) { 13731 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 13732 13733 for (Trigger trigger : triggersList) { 13734 ContentValues values = new ContentValues(); 13735 values.put("_id", trigger.getId()); 13736 values.put("registrant", trigger.getRegistrant().toString()); 13737 values.put("attribution_destination", trigger.getAttributionDestination().toString()); 13738 long row = db.insert("msmt_trigger", null, values); 13739 assertNotEquals("Trigger insertion failed", -1, row); 13740 } 13741 } 13742 setupSourceDataForPublisherTypeWeb()13743 private void setupSourceDataForPublisherTypeWeb() { 13744 List<Source> sourcesList = new ArrayList<>(); 13745 sourcesList.add( 13746 SourceFixture.getMinimalValidSourceBuilder() 13747 .setId("W1") 13748 .setPublisher(WEB_PUBLISHER_ONE) 13749 .setPublisherType(EventSurfaceType.WEB) 13750 .build()); 13751 sourcesList.add( 13752 SourceFixture.getMinimalValidSourceBuilder() 13753 .setId("W21") 13754 .setPublisher(WEB_PUBLISHER_TWO) 13755 .setPublisherType(EventSurfaceType.WEB) 13756 .build()); 13757 sourcesList.add( 13758 SourceFixture.getMinimalValidSourceBuilder() 13759 .setId("W22") 13760 .setPublisher(WEB_PUBLISHER_TWO) 13761 .setPublisherType(EventSurfaceType.WEB) 13762 .build()); 13763 sourcesList.add( 13764 SourceFixture.getMinimalValidSourceBuilder() 13765 .setId("W23") 13766 .setPublisher(WEB_PUBLISHER_TWO) 13767 .setPublisherType(EventSurfaceType.WEB) 13768 .setStatus(Source.Status.MARKED_TO_DELETE) 13769 .build()); 13770 sourcesList.add( 13771 SourceFixture.getMinimalValidSourceBuilder() 13772 .setId("S3") 13773 .setPublisher(WEB_PUBLISHER_THREE) 13774 .setPublisherType(EventSurfaceType.WEB) 13775 .build()); 13776 sourcesList.forEach(source -> insertSource(source, source.getId())); 13777 } 13778 createSourceForIATest( String id, long currentTime, long priority, int eventTimePastDays, boolean expiredIAWindow, String enrollmentId)13779 private Source.Builder createSourceForIATest( 13780 String id, 13781 long currentTime, 13782 long priority, 13783 int eventTimePastDays, 13784 boolean expiredIAWindow, 13785 String enrollmentId) { 13786 return createSourceForIATest( 13787 id, 13788 currentTime, 13789 priority, 13790 eventTimePastDays, 13791 expiredIAWindow, 13792 enrollmentId, 13793 REGISTRATION_ORIGIN); 13794 } 13795 createSourceForIATest( String id, long currentTime, long priority, int eventTimePastDays, boolean expiredIAWindow, String enrollmentId, Uri registrationOrigin)13796 private Source.Builder createSourceForIATest( 13797 String id, 13798 long currentTime, 13799 long priority, 13800 int eventTimePastDays, 13801 boolean expiredIAWindow, 13802 String enrollmentId, 13803 Uri registrationOrigin) { 13804 return new Source.Builder() 13805 .setId(id) 13806 .setPublisher(Uri.parse("android-app://com.example.sample")) 13807 .setRegistrant(Uri.parse("android-app://com.example.sample")) 13808 .setEnrollmentId(enrollmentId) 13809 .setExpiryTime(currentTime + DAYS.toMillis(30)) 13810 .setInstallAttributionWindow(DAYS.toMillis(expiredIAWindow ? 0 : 30)) 13811 .setAppDestinations(List.of(INSTALLED_PACKAGE)) 13812 .setEventTime( 13813 currentTime 13814 - DAYS.toMillis(eventTimePastDays == -1 ? 10 : eventTimePastDays)) 13815 .setPriority(priority == -1 ? 100 : priority) 13816 .setRegistrationOrigin(registrationOrigin); 13817 } 13818 generateMockAggregateReport(String attributionDestination, int id)13819 private AggregateReport generateMockAggregateReport(String attributionDestination, int id) { 13820 return new AggregateReport.Builder() 13821 .setId(String.valueOf(id)) 13822 .setAttributionDestination(Uri.parse(attributionDestination)) 13823 .build(); 13824 } 13825 generateMockAggregateReport( String attributionDestination, int id, String sourceId, String api)13826 private AggregateReport generateMockAggregateReport( 13827 String attributionDestination, int id, String sourceId, String api) { 13828 return new AggregateReport.Builder() 13829 .setId(String.valueOf(id)) 13830 .setSourceId(sourceId) 13831 .setAttributionDestination(Uri.parse(attributionDestination)) 13832 .setApi(api) 13833 .build(); 13834 } 13835 generateMockAggregateReport( String attributionDestination, int id, String sourceId, long reportTime)13836 private AggregateReport generateMockAggregateReport( 13837 String attributionDestination, int id, String sourceId, long reportTime) { 13838 return new AggregateReport.Builder() 13839 .setId(String.valueOf(id)) 13840 .setSourceId(sourceId) 13841 .setAttributionDestination(Uri.parse(attributionDestination)) 13842 .setScheduledReportTime(reportTime) 13843 .build(); 13844 } 13845 generateMockEventReport(String attributionDestination, int id)13846 private EventReport generateMockEventReport(String attributionDestination, int id) { 13847 return new EventReport.Builder() 13848 .setId(String.valueOf(id)) 13849 .setAttributionDestinations(List.of(Uri.parse(attributionDestination))) 13850 .build(); 13851 } 13852 generateMockEventReport( String attributionDestination, int id, long reportTime)13853 private EventReport generateMockEventReport( 13854 String attributionDestination, int id, long reportTime) { 13855 return new EventReport.Builder() 13856 .setId(String.valueOf(id)) 13857 .setAttributionDestinations(List.of(Uri.parse(attributionDestination))) 13858 .setReportTime(reportTime) 13859 .build(); 13860 } 13861 assertAggregateReportCount( List<String> attributionDestinations, int destinationType, List<Integer> expectedCounts)13862 private void assertAggregateReportCount( 13863 List<String> attributionDestinations, 13864 int destinationType, 13865 List<Integer> expectedCounts) { 13866 IntStream.range(0, attributionDestinations.size()) 13867 .forEach( 13868 i -> { 13869 DatastoreManager.ThrowingCheckedFunction<Integer> 13870 aggregateReportCountPerDestination = 13871 measurementDao -> 13872 measurementDao 13873 .getNumAggregateReportsPerDestination( 13874 Uri.parse( 13875 attributionDestinations 13876 .get(i)), 13877 destinationType); 13878 assertEquals( 13879 expectedCounts.get(i), 13880 mDatastoreManager 13881 .runInTransactionWithResult( 13882 aggregateReportCountPerDestination) 13883 .orElseThrow()); 13884 }); 13885 } 13886 assertEventReportCount( List<String> attributionDestinations, int destinationType, List<Integer> expectedCounts)13887 private void assertEventReportCount( 13888 List<String> attributionDestinations, 13889 int destinationType, 13890 List<Integer> expectedCounts) { 13891 IntStream.range(0, attributionDestinations.size()) 13892 .forEach( 13893 i -> { 13894 DatastoreManager.ThrowingCheckedFunction<Integer> 13895 numEventReportsPerDestination = 13896 measurementDao -> 13897 measurementDao.getNumEventReportsPerDestination( 13898 Uri.parse( 13899 attributionDestinations.get(i)), 13900 destinationType); 13901 assertEquals( 13902 expectedCounts.get(i), 13903 mDatastoreManager 13904 .runInTransactionWithResult( 13905 numEventReportsPerDestination) 13906 .orElseThrow()); 13907 }); 13908 } 13909 createAppDestinationVariants(int destinationNum)13910 private List<String> createAppDestinationVariants(int destinationNum) { 13911 return Arrays.asList( 13912 "android-app://subdomain.destination-" + destinationNum + ".app/abcd", 13913 "android-app://subdomain.destination-" + destinationNum + ".app", 13914 "android-app://destination-" + destinationNum + ".app/abcd", 13915 "android-app://destination-" + destinationNum + ".app", 13916 "android-app://destination-" + destinationNum + ".ap"); 13917 } 13918 createWebDestinationVariants(int destinationNum)13919 private List<String> createWebDestinationVariants(int destinationNum) { 13920 return Arrays.asList( 13921 "https://subdomain.destination-" + destinationNum + ".com/abcd", 13922 "https://subdomain.destination-" + destinationNum + ".com", 13923 "https://destination-" + destinationNum + ".com/abcd", 13924 "https://destination-" + destinationNum + ".com", 13925 "https://destination-" + destinationNum + ".co"); 13926 } 13927 getInstallAttributionStatus(String sourceDbId, SQLiteDatabase db)13928 private boolean getInstallAttributionStatus(String sourceDbId, SQLiteDatabase db) { 13929 Cursor cursor = 13930 db.query( 13931 SourceContract.TABLE, 13932 new String[] {SourceContract.IS_INSTALL_ATTRIBUTED}, 13933 SourceContract.ID + " = ? ", 13934 new String[] {sourceDbId}, 13935 null, 13936 null, 13937 null, 13938 null); 13939 assertTrue(cursor.moveToFirst()); 13940 return cursor.getInt(0) == 1; 13941 } 13942 getInstallAttributionInstallTime(String sourceDbId, SQLiteDatabase db)13943 private Long getInstallAttributionInstallTime(String sourceDbId, SQLiteDatabase db) { 13944 Cursor cursor = 13945 db.query( 13946 SourceContract.TABLE, 13947 new String[] {SourceContract.INSTALL_TIME}, 13948 SourceContract.ID + " = ? ", 13949 new String[] {sourceDbId}, 13950 null, 13951 null, 13952 null, 13953 null); 13954 assertTrue(cursor.moveToFirst()); 13955 if (!cursor.isNull(0)) { 13956 return cursor.getLong(0); 13957 } 13958 return null; 13959 } 13960 removeSources(List<String> dbIds, SQLiteDatabase db)13961 private void removeSources(List<String> dbIds, SQLiteDatabase db) { 13962 db.delete( 13963 SourceContract.TABLE, 13964 SourceContract.ID + " IN ( ? )", 13965 new String[] {String.join(",", dbIds)}); 13966 } 13967 maybeInsertSourceDestinations( SQLiteDatabase db, Source source, String sourceId)13968 private static void maybeInsertSourceDestinations( 13969 SQLiteDatabase db, Source source, String sourceId) { 13970 if (source.getAppDestinations() != null) { 13971 for (Uri appDestination : source.getAppDestinations()) { 13972 ContentValues values = new ContentValues(); 13973 values.put(MeasurementTables.SourceDestination.SOURCE_ID, sourceId); 13974 values.put( 13975 MeasurementTables.SourceDestination.DESTINATION_TYPE, EventSurfaceType.APP); 13976 values.put( 13977 MeasurementTables.SourceDestination.DESTINATION, appDestination.toString()); 13978 long row = db.insert(MeasurementTables.SourceDestination.TABLE, null, values); 13979 assertNotEquals("Source app destination insertion failed", -1, row); 13980 } 13981 } 13982 if (source.getWebDestinations() != null) { 13983 for (Uri webDestination : source.getWebDestinations()) { 13984 ContentValues values = new ContentValues(); 13985 values.put(MeasurementTables.SourceDestination.SOURCE_ID, sourceId); 13986 values.put( 13987 MeasurementTables.SourceDestination.DESTINATION_TYPE, EventSurfaceType.WEB); 13988 values.put( 13989 MeasurementTables.SourceDestination.DESTINATION, webDestination.toString()); 13990 long row = db.insert(MeasurementTables.SourceDestination.TABLE, null, values); 13991 assertNotEquals("Source web destination insertion failed", -1, row); 13992 } 13993 } 13994 } 13995 getAttributionBuilder(Source source, Trigger trigger)13996 private static Attribution.Builder getAttributionBuilder(Source source, Trigger trigger) { 13997 return new Attribution.Builder() 13998 .setEnrollmentId(source.getEnrollmentId()) 13999 .setDestinationOrigin(source.getWebDestinations().get(0).toString()) 14000 .setDestinationSite(source.getAppDestinations().get(0).toString()) 14001 .setSourceOrigin(source.getPublisher().toString()) 14002 .setSourceSite(source.getPublisher().toString()) 14003 .setRegistrant(source.getRegistrant().toString()) 14004 .setTriggerTime( 14005 trigger.getTriggerTime() - MEASUREMENT_RATE_LIMIT_WINDOW_MILLISECONDS + 1) 14006 .setRegistrationOrigin(trigger.getRegistrationOrigin()); 14007 } 14008 14009 /** Create {@link Attribution} object from SQLite datastore. */ constructAttributionFromCursor(Cursor cursor)14010 private static Attribution constructAttributionFromCursor(Cursor cursor) { 14011 Attribution.Builder builder = new Attribution.Builder(); 14012 int index = cursor.getColumnIndex(AttributionContract.ID); 14013 if (index > -1 && !cursor.isNull(index)) { 14014 builder.setId(cursor.getString(index)); 14015 } 14016 index = cursor.getColumnIndex(AttributionContract.SCOPE); 14017 if (index > -1 && !cursor.isNull(index)) { 14018 builder.setScope(cursor.getInt(index)); 14019 } 14020 index = cursor.getColumnIndex(AttributionContract.SOURCE_SITE); 14021 if (index > -1 && !cursor.isNull(index)) { 14022 builder.setSourceSite(cursor.getString(index)); 14023 } 14024 index = cursor.getColumnIndex(AttributionContract.SOURCE_ORIGIN); 14025 if (index > -1 && !cursor.isNull(index)) { 14026 builder.setSourceOrigin(cursor.getString(index)); 14027 } 14028 index = cursor.getColumnIndex(AttributionContract.DESTINATION_SITE); 14029 if (index > -1 && !cursor.isNull(index)) { 14030 builder.setDestinationSite(cursor.getString(index)); 14031 } 14032 index = cursor.getColumnIndex(AttributionContract.DESTINATION_ORIGIN); 14033 if (index > -1 && !cursor.isNull(index)) { 14034 builder.setDestinationOrigin(cursor.getString(index)); 14035 } 14036 index = cursor.getColumnIndex(AttributionContract.ENROLLMENT_ID); 14037 if (index > -1 && !cursor.isNull(index)) { 14038 builder.setEnrollmentId(cursor.getString(index)); 14039 } 14040 index = cursor.getColumnIndex(AttributionContract.TRIGGER_TIME); 14041 if (index > -1 && !cursor.isNull(index)) { 14042 builder.setTriggerTime(cursor.getLong(index)); 14043 } 14044 index = cursor.getColumnIndex(AttributionContract.REGISTRANT); 14045 if (index > -1 && !cursor.isNull(index)) { 14046 builder.setRegistrant(cursor.getString(index)); 14047 } 14048 index = cursor.getColumnIndex(AttributionContract.REGISTRATION_ORIGIN); 14049 if (index > -1 && !cursor.isNull(index)) { 14050 builder.setRegistrationOrigin(Uri.parse(cursor.getString(index))); 14051 } 14052 return builder.build(); 14053 } 14054 getAttribution(String attributionId, SQLiteDatabase db)14055 private Attribution getAttribution(String attributionId, SQLiteDatabase db) { 14056 try (Cursor cursor = 14057 db.query( 14058 AttributionContract.TABLE, 14059 /* columns= */ null, 14060 AttributionContract.ID + " = ? ", 14061 new String[] {attributionId}, 14062 /* groupBy= */ null, 14063 /* having= */ null, 14064 /* orderBy= */ null, 14065 /* limit= */ null)) { 14066 if (cursor.getCount() == 0) { 14067 return null; 14068 } 14069 cursor.moveToNext(); 14070 return constructAttributionFromCursor(cursor); 14071 } 14072 } 14073 insertAttributedTrigger(TriggerSpecs triggerSpecs, EventReport eventReport)14074 private static void insertAttributedTrigger(TriggerSpecs triggerSpecs, 14075 EventReport eventReport) { 14076 triggerSpecs.getAttributedTriggers().add( 14077 new AttributedTrigger( 14078 eventReport.getTriggerId(), 14079 eventReport.getTriggerPriority(), 14080 eventReport.getTriggerData(), 14081 eventReport.getTriggerValue(), 14082 eventReport.getTriggerTime(), 14083 eventReport.getTriggerDedupKey(), 14084 eventReport.getTriggerDebugKey(), 14085 false)); 14086 } 14087 insertAttributedTrigger(List<AttributedTrigger> attributedTriggers, EventReport eventReport)14088 private static void insertAttributedTrigger(List<AttributedTrigger> attributedTriggers, 14089 EventReport eventReport) { 14090 attributedTriggers.add( 14091 new AttributedTrigger( 14092 eventReport.getTriggerId(), 14093 eventReport.getTriggerData(), 14094 eventReport.getTriggerDedupKey())); 14095 } 14096 getRandomIdsWith(int numIds, String idToInclude)14097 private static List<String> getRandomIdsWith(int numIds, String idToInclude) { 14098 List<String> result = new ArrayList<>(); 14099 result.add(idToInclude); 14100 for (int i = 0; i < numIds; i++) { 14101 result.add(UUID.randomUUID().toString()); 14102 } 14103 return result; 14104 } 14105 getFirstSourceIdFromDatastore()14106 private static String getFirstSourceIdFromDatastore() { 14107 return getFirstIdFromDatastore(SourceContract.TABLE, SourceContract.ID); 14108 } 14109 getFirstIdFromDatastore(String tableName, String idColumn)14110 private static String getFirstIdFromDatastore(String tableName, String idColumn) { 14111 try (Cursor cursor = 14112 MeasurementDbHelper.getInstance() 14113 .getReadableDatabase() 14114 .query(tableName, new String[] {idColumn}, null, null, null, null, null)) { 14115 assertTrue(cursor.moveToNext()); 14116 return cursor.getString(cursor.getColumnIndex(idColumn)); 14117 } 14118 } 14119 getNullableUriList(List<Uri> uris)14120 private static List<Uri> getNullableUriList(List<Uri> uris) { 14121 return uris == null ? null : uris; 14122 } 14123 getMatchingSourceIds(Trigger trigger)14124 private List<String> getMatchingSourceIds(Trigger trigger) { 14125 List<Source> result = 14126 mDatastoreManager 14127 .runInTransactionWithResult( 14128 measurementDao -> measurementDao.getMatchingActiveSources(trigger)) 14129 .orElseThrow(); 14130 return result.stream().map(Source::getId).collect(Collectors.toList()); 14131 } 14132 getMatchingSources(Trigger trigger)14133 private List<Source> getMatchingSources(Trigger trigger) { 14134 return mDatastoreManager 14135 .runInTransactionWithResult( 14136 measurementDao -> measurementDao.getMatchingActiveSources(trigger)) 14137 .orElseThrow(); 14138 } 14139 } 14140