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.spy; 52 import static org.mockito.Mockito.when; 53 54 import static java.util.concurrent.TimeUnit.DAYS; 55 56 import android.adservices.measurement.DeletionRequest; 57 import android.content.ContentValues; 58 import android.database.Cursor; 59 import android.database.DatabaseUtils; 60 import android.database.sqlite.SQLiteDatabase; 61 import android.net.Uri; 62 import android.util.Pair; 63 64 import androidx.annotation.NonNull; 65 import androidx.annotation.Nullable; 66 67 import com.android.adservices.common.AdServicesExtendedMockitoTestCase; 68 import com.android.adservices.common.WebUtil; 69 import com.android.adservices.data.measurement.MeasurementTables.DebugReportContract; 70 import com.android.adservices.service.Flags; 71 import com.android.adservices.service.FlagsFactory; 72 import com.android.adservices.service.measurement.AggregatableNamedBudgets; 73 import com.android.adservices.service.measurement.AsyncRegistrationFixture; 74 import com.android.adservices.service.measurement.AsyncRegistrationFixture.ValidAsyncRegistrationParams; 75 import com.android.adservices.service.measurement.AttributedTrigger; 76 import com.android.adservices.service.measurement.Attribution; 77 import com.android.adservices.service.measurement.CountUniqueMetadata; 78 import com.android.adservices.service.measurement.CountUniqueReport; 79 import com.android.adservices.service.measurement.CountUniqueReportFixture; 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 import com.android.modules.utils.testing.ExtendedMockitoRule.SpyStatic; 104 105 import com.google.common.collect.ImmutableList; 106 import com.google.common.collect.ImmutableMultiset; 107 import com.google.common.truth.Truth; 108 109 import org.json.JSONException; 110 import org.junit.After; 111 import org.junit.Before; 112 import org.junit.Test; 113 import org.mockito.Mockito; 114 import org.mockito.internal.util.collections.Sets; 115 116 import java.time.Instant; 117 import java.util.ArrayList; 118 import java.util.Arrays; 119 import java.util.Collection; 120 import java.util.Collections; 121 import java.util.Comparator; 122 import java.util.HashSet; 123 import java.util.List; 124 import java.util.Map; 125 import java.util.Objects; 126 import java.util.Optional; 127 import java.util.Set; 128 import java.util.UUID; 129 import java.util.concurrent.TimeUnit; 130 import java.util.concurrent.atomic.AtomicLong; 131 import java.util.function.Consumer; 132 import java.util.function.Function; 133 import java.util.stream.Collectors; 134 import java.util.stream.IntStream; 135 import java.util.stream.Stream; 136 137 @SpyStatic(FlagsFactory.class) 138 @SpyStatic(MeasurementDbHelper.class) 139 public final class MeasurementDaoTest extends AdServicesExtendedMockitoTestCase { 140 private static final Uri APP_TWO_SOURCES = Uri.parse("android-app://com.example1.two-sources"); 141 private static final Uri APP_ONE_SOURCE = Uri.parse("android-app://com.example2.one-source"); 142 private static final String DEFAULT_ENROLLMENT_ID = "enrollment-id"; 143 private static final String ENROLLMENT_ID1 = "enrollment-id1"; 144 private static final Uri APP_TWO_PUBLISHER = 145 Uri.parse("android-app://com.publisher2.two-sources"); 146 private static final Uri APP_ONE_PUBLISHER = 147 Uri.parse("android-app://com.publisher1.one-source"); 148 private static final Uri APP_NO_PUBLISHER = 149 Uri.parse("android-app://com.publisher3.no-sources"); 150 private static final Uri APP_BROWSER = Uri.parse("android-app://com.example1.browser"); 151 private static final Uri WEB_ONE_DESTINATION = WebUtil.validUri("https://example1.test"); 152 private static final Uri WEB_ONE_DESTINATION_DIFFERENT_SUBDOMAIN = 153 WebUtil.validUri("https://store.example1.test"); 154 private static final Uri WEB_ONE_DESTINATION_DIFFERENT_SUBDOMAIN_2 = 155 WebUtil.validUri("https://foo.example1.test"); 156 private static final Uri WEB_TWO_DESTINATION = WebUtil.validUri("https://example2.test"); 157 private static final Uri WEB_THREE_DESTINATION = WebUtil.validUri("https://example3.test"); 158 private static final Uri WEB_TWO_DESTINATION_WITH_PATH = 159 WebUtil.validUri("https://www.example2.test/ad/foo"); 160 private static final Uri APP_ONE_DESTINATION = 161 Uri.parse("android-app://com.example1.one-trigger"); 162 private static final Uri APP_TWO_DESTINATION = 163 Uri.parse("android-app://com.example1.two-triggers"); 164 private static final Uri APP_THREE_DESTINATION = 165 Uri.parse("android-app://com.example1.three-triggers"); 166 private static final Uri APP_THREE_DESTINATION_PATH1 = 167 Uri.parse("android-app://com.example1.three-triggers/path1"); 168 private static final Uri APP_THREE_DESTINATION_PATH2 = 169 Uri.parse("android-app://com.example1.three-triggers/path2"); 170 private static final Uri APP_NO_TRIGGERS = Uri.parse("android-app://com.example1.no-triggers"); 171 private static final Uri INSTALLED_PACKAGE = Uri.parse("android-app://com.example.installed"); 172 private static final Uri WEB_PUBLISHER_ONE = WebUtil.validUri("https://not.example.test"); 173 private static final Uri WEB_PUBLISHER_TWO = WebUtil.validUri("https://notexample.test"); 174 // Differs from WEB_PUBLISHER_ONE by scheme. 175 private static final Uri WEB_PUBLISHER_THREE = WebUtil.validUri("http://not.example.test"); 176 private static final Uri APP_DESTINATION = Uri.parse("android-app://com.destination.example"); 177 private static final Uri REGISTRATION_ORIGIN = 178 WebUtil.validUri("https://subdomain.example.test"); 179 180 private static final Uri REGISTRANT = Uri.parse("android-app://com.example.abc"); 181 private static final Uri INSTALLED_REGISTRANT = Uri.parse("android-app://installed-registrant"); 182 private static final Uri NOT_INSTALLED_REGISTRANT = 183 Uri.parse("android-app://not-installed-registrant"); 184 185 private static final long INSERTION_TIME = 1617297798; 186 private static final long COOLDOWN_WINDOW = TimeUnit.HOURS.toMillis(2); 187 private static final long ATTRIBUTION_SCOPE_LIMIT = 3L; 188 private static final long MAX_EVENT_STATES = 1000L; 189 private static final String REGISTRATION_ID2 = "R2"; 190 191 // Fake ID count for initializing triggers. 192 private int mValueId = 1; 193 // TODO(b/384798806): ideally it should use mFakeFlags, but this class mixes usage of fake and 194 // mock flags - some tests can only pass if mLegacyFlags is set to mMockFlags 195 private Flags mLegacyFlags = mFakeFlags; 196 private DatastoreManager mDatastoreManager; 197 public static final Uri REGISTRATION_ORIGIN_2 = 198 WebUtil.validUri("https://subdomain_2.example.test"); 199 public static final Uri REGISTRATION_ORIGIN_3 = 200 WebUtil.validUri("https://subdomain_3.example.test"); 201 public static final Uri REGISTRATION_ORIGIN_4 = 202 WebUtil.validUri("https://subdomain_4.example.test"); 203 public static final Uri REGISTRATION_ORIGIN_5 = 204 WebUtil.validUri("https://subdomain_5.example.test"); 205 206 public static final Uri TOP_LEVEL_REGISTRANT_1 = Uri.parse("android-app://com.example1.sample"); 207 public static final Uri TOP_LEVEL_REGISTRANT_2 = Uri.parse("android-app://com.example2.sample"); 208 209 @Before before()210 public void before() { 211 mocker.mockGetFlags(mFakeFlags); 212 mDatastoreManager = 213 new SQLDatastoreManager( 214 MeasurementDbHelper.getInstance(), 215 Mockito.mock(AdServicesErrorLogger.class)); 216 } 217 218 @After cleanup()219 public void cleanup() { 220 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 221 for (String table : ALL_MSMT_TABLES) { 222 db.delete(table, null, null); 223 } 224 } 225 226 @Test testInsertSource()227 public void testInsertSource() { 228 Source validSource = 229 SourceFixture.getValidSourceBuilder() 230 .setEventReportWindows("{'start_time': 1, 'end_times': ['3600', '7200']}") 231 .setStatus(Source.Status.MARKED_TO_DELETE) 232 .setTriggerDataMatching(Source.TriggerDataMatching.EXACT) 233 .setTriggerData(Set.of(new UnsignedLong(23L), new UnsignedLong(1L))) 234 .build(); 235 mDatastoreManager.runInTransaction((dao) -> dao.insertSource(validSource)); 236 237 String sourceId = getFirstSourceIdFromDatastore(); 238 Source source = 239 mDatastoreManager 240 .runInTransactionWithResult( 241 measurementDao -> measurementDao.getSource(sourceId)) 242 .get(); 243 244 assertNotNull(source); 245 assertNotNull(source.getId()); 246 assertNull(source.getAppDestinations()); 247 assertNull(source.getWebDestinations()); 248 assertEquals(validSource.getEnrollmentId(), source.getEnrollmentId()); 249 assertEquals(validSource.getRegistrant(), source.getRegistrant()); 250 assertEquals(validSource.getEventTime(), source.getEventTime()); 251 assertEquals(validSource.getExpiryTime(), source.getExpiryTime()); 252 assertEquals(validSource.getStatus(), source.getStatus()); 253 assertEquals(validSource.getEventReportWindow(), source.getEventReportWindow()); 254 assertEquals( 255 validSource.getAggregatableReportWindow(), source.getAggregatableReportWindow()); 256 assertEquals(validSource.getPriority(), source.getPriority()); 257 assertEquals(validSource.getSourceType(), source.getSourceType()); 258 assertEquals( 259 validSource.getInstallAttributionWindow(), source.getInstallAttributionWindow()); 260 assertEquals(validSource.getInstallCooldownWindow(), source.getInstallCooldownWindow()); 261 assertEquals(validSource.getAttributionMode(), source.getAttributionMode()); 262 assertEquals( 263 validSource.getReinstallReattributionWindow(), 264 source.getReinstallReattributionWindow()); 265 assertEquals(validSource.getAggregateSource(), source.getAggregateSource()); 266 assertEquals(validSource.getFilterDataString(), source.getFilterDataString()); 267 assertEquals(validSource.getSharedFilterDataKeys(), source.getSharedFilterDataKeys()); 268 assertEquals(validSource.getAggregateContributions(), source.getAggregateContributions()); 269 assertEquals(validSource.isDebugReporting(), source.isDebugReporting()); 270 assertEquals(validSource.getSharedAggregationKeys(), source.getSharedAggregationKeys()); 271 assertEquals(validSource.getRegistrationId(), source.getRegistrationId()); 272 assertEquals(validSource.getInstallTime(), source.getInstallTime()); 273 assertEquals(validSource.getPlatformAdId(), source.getPlatformAdId()); 274 assertEquals(validSource.getDebugAdId(), source.getDebugAdId()); 275 assertEquals(validSource.getRegistrationOrigin(), source.getRegistrationOrigin()); 276 assertEquals( 277 validSource.hasCoarseEventReportDestinations(), 278 source.hasCoarseEventReportDestinations()); 279 assertEquals(validSource.getTriggerDataMatching(), source.getTriggerDataMatching()); 280 assertEquals(validSource.getTriggerData(), source.getTriggerData()); 281 assertEquals(validSource.getEventReportWindows(), source.getEventReportWindows()); 282 assertEquals(SourceFixture.ValidSourceParams.SHARED_DEBUG_KEY, source.getSharedDebugKey()); 283 assertEquals(0L, source.getDestinationLimitPriority()); 284 285 // Assert destinations were inserted into the source destination table. 286 287 Pair<List<Uri>, List<Uri>> destinations = 288 mDatastoreManager 289 .runInTransactionWithResult( 290 measurementDao -> 291 measurementDao.getSourceDestinations(source.getId())) 292 .get(); 293 assertTrue( 294 ImmutableMultiset.copyOf(validSource.getAppDestinations()) 295 .equals(ImmutableMultiset.copyOf(destinations.first))); 296 assertTrue( 297 ImmutableMultiset.copyOf(validSource.getWebDestinations()) 298 .equals(ImmutableMultiset.copyOf(destinations.second))); 299 } 300 301 @Test testInsertSource_flexibleEventReport_equal()302 public void testInsertSource_flexibleEventReport_equal() throws Exception { 303 Source validSource = SourceFixture.getValidSourceWithFlexEventReport(); 304 mDatastoreManager.runInTransaction((dao) -> dao.insertSource(validSource)); 305 306 String sourceId = getFirstSourceIdFromDatastore(); 307 Source source = 308 mDatastoreManager 309 .runInTransactionWithResult( 310 measurementDao -> measurementDao.getSource(sourceId)) 311 .get(); 312 source.buildTriggerSpecs(); 313 314 assertNotNull(source); 315 assertNotNull(source.getId()); 316 assertNull(source.getAppDestinations()); 317 assertNull(source.getWebDestinations()); 318 assertEquals(validSource.getEnrollmentId(), source.getEnrollmentId()); 319 assertEquals(validSource.getRegistrant(), source.getRegistrant()); 320 assertEquals(validSource.getEventTime(), source.getEventTime()); 321 assertEquals(validSource.getExpiryTime(), source.getExpiryTime()); 322 assertEquals(validSource.getEventReportWindow(), source.getEventReportWindow()); 323 assertEquals( 324 validSource.getAggregatableReportWindow(), source.getAggregatableReportWindow()); 325 assertEquals(validSource.getPriority(), source.getPriority()); 326 assertEquals(validSource.getSourceType(), source.getSourceType()); 327 assertEquals( 328 validSource.getInstallAttributionWindow(), source.getInstallAttributionWindow()); 329 assertEquals(validSource.getInstallCooldownWindow(), source.getInstallCooldownWindow()); 330 assertEquals(validSource.getAttributionMode(), source.getAttributionMode()); 331 assertEquals(validSource.getAggregateSource(), source.getAggregateSource()); 332 assertEquals(validSource.getFilterDataString(), source.getFilterDataString()); 333 assertEquals(validSource.getAggregateContributions(), source.getAggregateContributions()); 334 assertEquals(validSource.isDebugReporting(), source.isDebugReporting()); 335 assertEquals(validSource.getSharedAggregationKeys(), source.getSharedAggregationKeys()); 336 assertEquals(validSource.getRegistrationId(), source.getRegistrationId()); 337 assertEquals(validSource.getInstallTime(), source.getInstallTime()); 338 assertEquals(validSource.getPlatformAdId(), source.getPlatformAdId()); 339 assertEquals(validSource.getDebugAdId(), source.getDebugAdId()); 340 assertEquals(validSource.getRegistrationOrigin(), source.getRegistrationOrigin()); 341 assertEquals( 342 validSource.getTriggerSpecs().getMaxReports(), 343 source.getMaxEventLevelReports().intValue()); 344 assertEquals( 345 validSource.getTriggerSpecs().encodeToJson(), 346 source.getTriggerSpecsString()); 347 assertNull(source.getEventAttributionStatus()); 348 assertEquals( 349 validSource.getTriggerSpecs().encodePrivacyParametersToJsonString(), 350 source.getPrivacyParameters()); 351 assertEquals(validSource.getTriggerSpecs(), source.getTriggerSpecs()); 352 353 // Assert destinations were inserted into the source destination table. 354 Pair<List<Uri>, List<Uri>> destinations = 355 mDatastoreManager 356 .runInTransactionWithResult( 357 measurementDao -> 358 measurementDao.getSourceDestinations(source.getId())) 359 .get(); 360 assertTrue( 361 ImmutableMultiset.copyOf(validSource.getAppDestinations()) 362 .equals(ImmutableMultiset.copyOf(destinations.first))); 363 assertTrue( 364 ImmutableMultiset.copyOf(validSource.getWebDestinations()) 365 .equals(ImmutableMultiset.copyOf(destinations.second))); 366 } 367 368 @Test testInsertSource_reachedDbSizeLimitOnEdgeCase_doNotInsert()369 public void testInsertSource_reachedDbSizeLimitOnEdgeCase_doNotInsert() { 370 insertSourceReachingDbSizeLimit(/* dbSize= */ 100L, /* dbSizeMaxLimit= */ 100L); 371 } 372 373 @Test testInsertSource_reachedDbSizeLimitUpperEdgeCase_doNotInsert()374 public void testInsertSource_reachedDbSizeLimitUpperEdgeCase_doNotInsert() { 375 insertSourceReachingDbSizeLimit(/* dbSize= */ 101L, /* dbSizeMaxLimit= */ 100L); 376 } 377 378 @Test testInsertSource_attributionScopeEnabled_success()379 public void testInsertSource_attributionScopeEnabled_success() { 380 mLegacyFlags = mMockFlags; 381 mocker.mockGetFlags(mMockFlags); 382 doReturn(true).when(mMockFlags).getMeasurementEnableAttributionScope(); 383 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mMockFlags).getMeasurementDbSizeLimit(); 384 385 Source validSource = 386 insertSourceForAttributionScope( 387 List.of("1", "2", "3"), 388 ATTRIBUTION_SCOPE_LIMIT, 389 MAX_EVENT_STATES, 390 SOURCE_EVENT_TIME, 391 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 392 List.of(APP_ONE_DESTINATION)); 393 Source source = 394 mDatastoreManager 395 .runInTransactionWithResult( 396 measurementDao -> measurementDao.getSource(validSource.getId())) 397 .get(); 398 assertThat(source.getAttributionScopeLimit()) 399 .isEqualTo(validSource.getAttributionScopeLimit()); 400 assertThat(source.getMaxEventStates()).isEqualTo(validSource.getMaxEventStates()); 401 List<String> attributionScopes = 402 mDatastoreManager 403 .runInTransactionWithResult( 404 measurementDao -> 405 measurementDao.getSourceAttributionScopes(source.getId())) 406 .get(); 407 assertThat(attributionScopes).containsExactlyElementsIn(validSource.getAttributionScopes()); 408 } 409 410 @Test 411 public void testInsertSource_attributionScopeDisabled_doesNotInsertAttributionScopeRelatedData()412 testInsertSource_attributionScopeDisabled_doesNotInsertAttributionScopeRelatedData() { 413 mocker.mockGetFlags(mMockFlags); 414 doReturn(false).when(mMockFlags).getMeasurementEnableAttributionScope(); 415 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mMockFlags).getMeasurementDbSizeLimit(); 416 417 Source validSource = 418 insertSourceForAttributionScope( 419 List.of("1", "2", "3"), 420 ATTRIBUTION_SCOPE_LIMIT, 421 MAX_EVENT_STATES, 422 SOURCE_EVENT_TIME, 423 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 424 List.of(APP_ONE_DESTINATION)); 425 Source source = 426 mDatastoreManager 427 .runInTransactionWithResult( 428 measurementDao -> measurementDao.getSource(validSource.getId())) 429 .get(); 430 431 assertThat(source.getAttributionScopeLimit()).isEqualTo(null); 432 assertThat(source.getMaxEventStates()).isEqualTo(null); 433 List<String> attributionScopes = 434 mDatastoreManager 435 .runInTransactionWithResult( 436 measurementDao -> 437 measurementDao.getSourceAttributionScopes(source.getId())) 438 .get(); 439 assertThat(attributionScopes).isEmpty(); 440 } 441 442 @Test testInsertSource_aggregateDebugReportingEnabled_success()443 public void testInsertSource_aggregateDebugReportingEnabled_success() { 444 mocker.mockGetFlags(mMockFlags); 445 doReturn(true).when(mMockFlags).getMeasurementEnableAggregateDebugReporting(); 446 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mMockFlags).getMeasurementDbSizeLimit(); 447 448 Source validSource = 449 SourceFixture.getValidSourceBuilder() 450 .setEventReportWindows("{'start_time': 1, 'end_times': ['3600', '7200']}") 451 .setStatus(Source.Status.MARKED_TO_DELETE) 452 .setTriggerDataMatching(Source.TriggerDataMatching.EXACT) 453 .setTriggerData(Set.of(new UnsignedLong(23L), new UnsignedLong(1L))) 454 .build(); 455 mDatastoreManager.runInTransaction((dao) -> dao.insertSource(validSource)); 456 457 String sourceId = getFirstSourceIdFromDatastore(); 458 Source source = 459 mDatastoreManager 460 .runInTransactionWithResult( 461 measurementDao -> measurementDao.getSource(sourceId)) 462 .get(); 463 464 assertThat(source).isNotNull(); 465 assertThat(source.getId()).isNotNull(); 466 assertThat(source.getAppDestinations()).isNull(); 467 assertThat(source.getWebDestinations()).isNull(); 468 assertThat(validSource.getEnrollmentId()).isEqualTo(source.getEnrollmentId()); 469 assertThat(validSource.getRegistrant()).isEqualTo(source.getRegistrant()); 470 assertThat(validSource.getEventTime()).isEqualTo(source.getEventTime()); 471 assertThat(validSource.getExpiryTime()).isEqualTo(source.getExpiryTime()); 472 assertThat(validSource.getStatus()).isEqualTo(source.getStatus()); 473 assertThat(validSource.getEventReportWindow()).isEqualTo(source.getEventReportWindow()); 474 assertThat(validSource.getAggregatableReportWindow()) 475 .isEqualTo(source.getAggregatableReportWindow()); 476 assertThat(validSource.getPriority()).isEqualTo(source.getPriority()); 477 assertThat(validSource.getSourceType()).isEqualTo(source.getSourceType()); 478 assertThat(validSource.getInstallAttributionWindow()) 479 .isEqualTo(source.getInstallAttributionWindow()); 480 assertThat(validSource.getInstallCooldownWindow()) 481 .isEqualTo(source.getInstallCooldownWindow()); 482 assertThat(validSource.getAttributionMode()).isEqualTo(source.getAttributionMode()); 483 assertThat(validSource.getReinstallReattributionWindow()) 484 .isEqualTo(source.getReinstallReattributionWindow()); 485 assertThat(validSource.getAggregateSource()).isEqualTo(source.getAggregateSource()); 486 assertThat(validSource.getFilterDataString()).isEqualTo(source.getFilterDataString()); 487 assertThat(validSource.getSharedFilterDataKeys()) 488 .isEqualTo(source.getSharedFilterDataKeys()); 489 assertThat(validSource.getAggregateContributions()) 490 .isEqualTo(source.getAggregateContributions()); 491 assertThat(validSource.isDebugReporting()).isEqualTo(source.isDebugReporting()); 492 assertThat(validSource.getSharedAggregationKeys()) 493 .isEqualTo(source.getSharedAggregationKeys()); 494 assertThat(validSource.getRegistrationId()).isEqualTo(source.getRegistrationId()); 495 assertThat(validSource.getInstallTime()).isEqualTo(source.getInstallTime()); 496 assertThat(validSource.getPlatformAdId()).isEqualTo(source.getPlatformAdId()); 497 assertThat(validSource.getDebugAdId()).isEqualTo(source.getDebugAdId()); 498 assertThat(validSource.getRegistrationOrigin()).isEqualTo(source.getRegistrationOrigin()); 499 assertThat(validSource.hasCoarseEventReportDestinations()) 500 .isEqualTo(source.hasCoarseEventReportDestinations()); 501 assertThat(validSource.getTriggerDataMatching()).isEqualTo(source.getTriggerDataMatching()); 502 assertThat(validSource.getTriggerData()).isEqualTo(source.getTriggerData()); 503 assertThat(validSource.getEventReportWindows()).isEqualTo(source.getEventReportWindows()); 504 assertThat(SourceFixture.ValidSourceParams.SHARED_DEBUG_KEY) 505 .isEqualTo(source.getSharedDebugKey()); 506 assertThat(source.getDestinationLimitPriority()).isEqualTo(0L); 507 assertThat(validSource.getAggregateDebugReportingString()) 508 .isEqualTo(source.getAggregateDebugReportingString()); 509 assertThat(validSource.getAggregateDebugReportContributions()) 510 .isEqualTo(source.getAggregateDebugReportContributions()); 511 512 // Assert destinations were inserted into the source destination table. 513 514 Pair<List<Uri>, List<Uri>> destinations = 515 mDatastoreManager 516 .runInTransactionWithResult( 517 measurementDao -> 518 measurementDao.getSourceDestinations(source.getId())) 519 .get(); 520 assertThat( 521 ImmutableMultiset.copyOf(validSource.getAppDestinations()) 522 .equals(ImmutableMultiset.copyOf(destinations.first))) 523 .isTrue(); 524 assertThat( 525 ImmutableMultiset.copyOf(validSource.getWebDestinations()) 526 .equals(ImmutableMultiset.copyOf(destinations.second))) 527 .isTrue(); 528 } 529 530 @Test testInsertSource_aggregateDebugReportingDisabled_relatedDataNotInserted()531 public void testInsertSource_aggregateDebugReportingDisabled_relatedDataNotInserted() { 532 mocker.mockGetFlags(mMockFlags); 533 doReturn(false).when(mMockFlags).getMeasurementEnableAggregateDebugReporting(); 534 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mMockFlags).getMeasurementDbSizeLimit(); 535 536 Source validSource = 537 SourceFixture.getValidSourceBuilder() 538 .setEventReportWindows("{'start_time': 1, 'end_times': ['3600', '7200']}") 539 .setStatus(Source.Status.MARKED_TO_DELETE) 540 .setTriggerDataMatching(Source.TriggerDataMatching.EXACT) 541 .setTriggerData(Set.of(new UnsignedLong(23L), new UnsignedLong(1L))) 542 .build(); 543 mDatastoreManager.runInTransaction((dao) -> dao.insertSource(validSource)); 544 545 String sourceId = getFirstSourceIdFromDatastore(); 546 Source source = 547 mDatastoreManager 548 .runInTransactionWithResult( 549 measurementDao -> measurementDao.getSource(sourceId)) 550 .get(); 551 552 assertThat(source).isNotNull(); 553 assertThat(source.getId()).isNotNull(); 554 assertThat(source.getAppDestinations()).isNull(); 555 assertThat(source.getWebDestinations()).isNull(); 556 assertThat(validSource.getEnrollmentId()).isEqualTo(source.getEnrollmentId()); 557 assertThat(validSource.getRegistrant()).isEqualTo(source.getRegistrant()); 558 assertThat(validSource.getEventTime()).isEqualTo(source.getEventTime()); 559 assertThat(validSource.getExpiryTime()).isEqualTo(source.getExpiryTime()); 560 assertThat(validSource.getStatus()).isEqualTo(source.getStatus()); 561 assertThat(validSource.getEventReportWindow()).isEqualTo(source.getEventReportWindow()); 562 assertThat(validSource.getAggregatableReportWindow()) 563 .isEqualTo(source.getAggregatableReportWindow()); 564 assertThat(validSource.getPriority()).isEqualTo(source.getPriority()); 565 assertThat(validSource.getSourceType()).isEqualTo(source.getSourceType()); 566 assertThat(validSource.getInstallAttributionWindow()) 567 .isEqualTo(source.getInstallAttributionWindow()); 568 assertThat(validSource.getInstallCooldownWindow()) 569 .isEqualTo(source.getInstallCooldownWindow()); 570 assertThat(validSource.getAttributionMode()).isEqualTo(source.getAttributionMode()); 571 assertThat(validSource.getReinstallReattributionWindow()) 572 .isEqualTo(source.getReinstallReattributionWindow()); 573 assertThat(validSource.getAggregateSource()).isEqualTo(source.getAggregateSource()); 574 assertThat(validSource.getFilterDataString()).isEqualTo(source.getFilterDataString()); 575 assertThat(validSource.getSharedFilterDataKeys()) 576 .isEqualTo(source.getSharedFilterDataKeys()); 577 assertThat(validSource.getAggregateContributions()) 578 .isEqualTo(source.getAggregateContributions()); 579 assertThat(validSource.isDebugReporting()).isEqualTo(source.isDebugReporting()); 580 assertThat(validSource.getSharedAggregationKeys()) 581 .isEqualTo(source.getSharedAggregationKeys()); 582 assertThat(validSource.getRegistrationId()).isEqualTo(source.getRegistrationId()); 583 assertThat(validSource.getInstallTime()).isEqualTo(source.getInstallTime()); 584 assertThat(validSource.getPlatformAdId()).isEqualTo(source.getPlatformAdId()); 585 assertThat(validSource.getDebugAdId()).isEqualTo(source.getDebugAdId()); 586 assertThat(validSource.getRegistrationOrigin()).isEqualTo(source.getRegistrationOrigin()); 587 assertThat(validSource.hasCoarseEventReportDestinations()) 588 .isEqualTo(source.hasCoarseEventReportDestinations()); 589 assertThat(validSource.getTriggerDataMatching()).isEqualTo(source.getTriggerDataMatching()); 590 assertThat(validSource.getTriggerData()).isEqualTo(source.getTriggerData()); 591 assertThat(validSource.getEventReportWindows()).isEqualTo(source.getEventReportWindows()); 592 assertThat(SourceFixture.ValidSourceParams.SHARED_DEBUG_KEY) 593 .isEqualTo(source.getSharedDebugKey()); 594 assertThat(source.getDestinationLimitPriority()).isEqualTo(0L); 595 assertThat(source.getAggregateDebugReportingString()).isEqualTo(null); 596 assertThat(source.getAggregateDebugReportContributions()).isEqualTo(0L); 597 598 // Assert destinations were inserted into the source destination table. 599 600 Pair<List<Uri>, List<Uri>> destinations = 601 mDatastoreManager 602 .runInTransactionWithResult( 603 measurementDao -> 604 measurementDao.getSourceDestinations(source.getId())) 605 .get(); 606 assertThat( 607 ImmutableMultiset.copyOf(validSource.getAppDestinations()) 608 .equals(ImmutableMultiset.copyOf(destinations.first))) 609 .isTrue(); 610 assertThat( 611 ImmutableMultiset.copyOf(validSource.getWebDestinations()) 612 .equals(ImmutableMultiset.copyOf(destinations.second))) 613 .isTrue(); 614 } 615 616 @Test testInsertSource_aggregatableNamedBudgetsEnabled_success()617 public void testInsertSource_aggregatableNamedBudgetsEnabled_success() { 618 mocker.mockGetFlags(mMockFlags); 619 when(mMockFlags.getMeasurementEnableAggregatableNamedBudgets()).thenReturn(true); 620 when(mMockFlags.getMeasurementDbSizeLimit()).thenReturn(MEASUREMENT_DB_SIZE_LIMIT); 621 622 AggregatableNamedBudgets aggregatableNamedBudgets = new AggregatableNamedBudgets(); 623 aggregatableNamedBudgets.createContributionBudget("budget1", 400); 624 aggregatableNamedBudgets.setContribution("budget1", 150); 625 aggregatableNamedBudgets.createContributionBudget("budget2", 750); 626 aggregatableNamedBudgets.setContribution("budget2", 320); 627 Source validSource = 628 insertSourceForAggregatableNamedBudgets( 629 aggregatableNamedBudgets, 630 SOURCE_EVENT_TIME, 631 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 632 List.of(APP_ONE_DESTINATION)); 633 634 AggregatableNamedBudgets.BudgetAndContribution daoBudgetAndContribution1 = 635 mDatastoreManager 636 .runInTransactionWithResult( 637 measurementDao -> 638 measurementDao 639 .getSourceAggregatableNamedBudgetAndContribution( 640 validSource.getId(), "budget1")) 641 .get(); 642 assertThat(daoBudgetAndContribution1.getAggregateContribution()) 643 .isEqualTo( 644 validSource 645 .getAggregatableNamedBudgets() 646 .maybeGetContribution("budget1") 647 .get()); 648 assertThat(daoBudgetAndContribution1.getBudget()) 649 .isEqualTo( 650 validSource.getAggregatableNamedBudgets().maybeGetBudget("budget1").get()); 651 AggregatableNamedBudgets.BudgetAndContribution daoBudgetAndContribution2 = 652 mDatastoreManager 653 .runInTransactionWithResult( 654 measurementDao -> 655 measurementDao 656 .getSourceAggregatableNamedBudgetAndContribution( 657 validSource.getId(), "budget2")) 658 .get(); 659 assertThat(daoBudgetAndContribution2.getAggregateContribution()) 660 .isEqualTo( 661 validSource 662 .getAggregatableNamedBudgets() 663 .maybeGetContribution("budget2") 664 .get()); 665 assertThat(daoBudgetAndContribution2.getBudget()) 666 .isEqualTo( 667 validSource.getAggregatableNamedBudgets().maybeGetBudget("budget2").get()); 668 } 669 670 @Test testInsertSource_aggregatableNamedBudgetsDisabled_relatedDataNotInserted()671 public void testInsertSource_aggregatableNamedBudgetsDisabled_relatedDataNotInserted() { 672 mocker.mockGetFlags(mMockFlags); 673 when(mMockFlags.getMeasurementEnableAggregatableNamedBudgets()).thenReturn(false); 674 when(mMockFlags.getMeasurementDbSizeLimit()).thenReturn(MEASUREMENT_DB_SIZE_LIMIT); 675 676 AggregatableNamedBudgets aggregatableNamedBudgets = new AggregatableNamedBudgets(); 677 aggregatableNamedBudgets.createContributionBudget("budget3", 470); 678 aggregatableNamedBudgets.setContribution("budget3", 153); 679 Source validSource = 680 insertSourceForAggregatableNamedBudgets( 681 aggregatableNamedBudgets, 682 SOURCE_EVENT_TIME, 683 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 684 List.of(APP_ONE_DESTINATION)); 685 686 Optional<AggregatableNamedBudgets.BudgetAndContribution> daoBudgetAndContribution = 687 mDatastoreManager.runInTransactionWithResult( 688 measurementDao -> 689 measurementDao.getSourceAggregatableNamedBudgetAndContribution( 690 validSource.getId(), "budget3")); 691 assertThat(daoBudgetAndContribution).isEmpty(); 692 } 693 insertSourceReachingDbSizeLimit(long dbSize, long dbSizeMaxLimit)694 private void insertSourceReachingDbSizeLimit(long dbSize, long dbSizeMaxLimit) { 695 final Source validSource = SourceFixture.getValidSource(); 696 697 // Mocking that the DB file has a size of 100 bytes 698 final MeasurementDbHelper spyMeasurementDbHelper = spy(MeasurementDbHelper.getInstance()); 699 ExtendedMockito.doReturn(spyMeasurementDbHelper).when(MeasurementDbHelper::getInstance); 700 ExtendedMockito.doReturn(dbSize).when(spyMeasurementDbHelper).getDbFileSize(); 701 702 // Mocking that the flags return a max limit size of 100 bytes 703 Flags mockFlags = Mockito.mock(Flags.class); 704 ExtendedMockito.doReturn(mockFlags).when(FlagsFactory::getFlags); 705 ExtendedMockito.doReturn(dbSizeMaxLimit).when(mockFlags).getMeasurementDbSizeLimit(); 706 707 mDatastoreManager.runInTransaction((dao) -> dao.insertSource(validSource)); 708 709 try (Cursor sourceCursor = 710 MeasurementDbHelper.getInstance() 711 .getReadableDatabase() 712 .query(SourceContract.TABLE, null, null, null, null, null, null)) { 713 assertFalse(sourceCursor.moveToNext()); 714 } 715 } 716 717 @Test testInsertTrigger()718 public void testInsertTrigger() { 719 Trigger validTrigger = TriggerFixture.getValidTrigger(); 720 mDatastoreManager.runInTransaction((dao) -> dao.insertTrigger(validTrigger)); 721 722 try (Cursor triggerCursor = 723 MeasurementDbHelper.getInstance() 724 .getReadableDatabase() 725 .query(TriggerContract.TABLE, null, null, null, null, null, null)) { 726 assertTrue(triggerCursor.moveToNext()); 727 Trigger trigger = SqliteObjectMapper.constructTriggerFromCursor(triggerCursor); 728 assertNotNull(trigger); 729 assertNotNull(trigger.getId()); 730 assertEquals( 731 validTrigger.getAttributionDestination(), trigger.getAttributionDestination()); 732 assertEquals(validTrigger.getDestinationType(), trigger.getDestinationType()); 733 assertEquals(validTrigger.getEnrollmentId(), trigger.getEnrollmentId()); 734 assertEquals(validTrigger.getRegistrant(), trigger.getRegistrant()); 735 assertEquals(validTrigger.getTriggerTime(), trigger.getTriggerTime()); 736 assertEquals(validTrigger.getEventTriggers(), trigger.getEventTriggers()); 737 assertEquals(validTrigger.getAttributionConfig(), trigger.getAttributionConfig()); 738 assertEquals(validTrigger.getAdtechKeyMapping(), trigger.getAdtechKeyMapping()); 739 assertEquals(validTrigger.getPlatformAdId(), trigger.getPlatformAdId()); 740 assertEquals(validTrigger.getDebugAdId(), trigger.getDebugAdId()); 741 assertEquals(validTrigger.getRegistrationOrigin(), trigger.getRegistrationOrigin()); 742 assertEquals( 743 validTrigger.getAggregationCoordinatorOrigin(), 744 trigger.getAggregationCoordinatorOrigin()); 745 assertEquals( 746 validTrigger.getAggregatableSourceRegistrationTimeConfig(), 747 trigger.getAggregatableSourceRegistrationTimeConfig()); 748 assertEquals(validTrigger.getTriggerContextId(), trigger.getTriggerContextId()); 749 assertEquals( 750 validTrigger.getAttributionScopesString(), 751 trigger.getAttributionScopesString()); 752 assertEquals( 753 validTrigger.getAggregatableFilteringIdMaxBytes(), 754 trigger.getAggregatableFilteringIdMaxBytes()); 755 assertThat(validTrigger.getAggregateDebugReportingString()) 756 .isEqualTo(trigger.getAggregateDebugReportingString()); 757 } 758 } 759 760 @Test testInsertTrigger_reachedDbSizeLimitOnEdgeCase_doNotInsert()761 public void testInsertTrigger_reachedDbSizeLimitOnEdgeCase_doNotInsert() { 762 insertTriggerReachingDbSizeLimit(/* dbSize= */ 100L, /* dbSizeMaxLimit= */ 100L); 763 } 764 765 @Test testInsertTrigger_reachedDbSizeLimitUpperEdgeCase_doNotInsert()766 public void testInsertTrigger_reachedDbSizeLimitUpperEdgeCase_doNotInsert() { 767 insertTriggerReachingDbSizeLimit(/* dbSize= */ 101L, /* dbSizeMaxLimit= */ 100L); 768 } 769 insertTriggerReachingDbSizeLimit(long dbSize, long dbSizeMaxLimit)770 private void insertTriggerReachingDbSizeLimit(long dbSize, long dbSizeMaxLimit) { 771 final Trigger validTrigger = TriggerFixture.getValidTrigger(); 772 773 // Mocking that the DB file has a size of 100 bytes 774 final MeasurementDbHelper spyMeasurementDbHelper = spy(MeasurementDbHelper.getInstance()); 775 ExtendedMockito.doReturn(spyMeasurementDbHelper).when(MeasurementDbHelper::getInstance); 776 ExtendedMockito.doReturn(dbSize).when(spyMeasurementDbHelper).getDbFileSize(); 777 778 // Mocking that the flags return a max limit size of 100 bytes 779 Flags mockFlags = Mockito.mock(Flags.class); 780 ExtendedMockito.doReturn(mockFlags).when(FlagsFactory::getFlags); 781 ExtendedMockito.doReturn(dbSizeMaxLimit).when(mockFlags).getMeasurementDbSizeLimit(); 782 783 mDatastoreManager.runInTransaction((dao) -> dao.insertTrigger(validTrigger)); 784 785 try (Cursor sourceCursor = 786 MeasurementDbHelper.getInstance() 787 .getReadableDatabase() 788 .query(TriggerContract.TABLE, null, null, null, null, null, null)) { 789 assertFalse(sourceCursor.moveToNext()); 790 } 791 } 792 793 @Test testGetNumSourcesPerPublisher_publisherTypeApp()794 public void testGetNumSourcesPerPublisher_publisherTypeApp() { 795 setupSourceAndTriggerData(); 796 mDatastoreManager.runInTransaction( 797 measurementDao -> { 798 assertEquals( 799 2, 800 measurementDao.getNumSourcesPerPublisher( 801 APP_TWO_PUBLISHER, EventSurfaceType.APP)); 802 }); 803 mDatastoreManager.runInTransaction( 804 measurementDao -> { 805 assertEquals( 806 1, 807 measurementDao.getNumSourcesPerPublisher( 808 APP_ONE_PUBLISHER, EventSurfaceType.APP)); 809 }); 810 mDatastoreManager.runInTransaction( 811 measurementDao -> { 812 assertEquals( 813 0, 814 measurementDao.getNumSourcesPerPublisher( 815 APP_NO_PUBLISHER, EventSurfaceType.APP)); 816 }); 817 } 818 819 @Test testGetNumSourcesPerPublisher_publisherTypeWeb()820 public void testGetNumSourcesPerPublisher_publisherTypeWeb() { 821 setupSourceDataForPublisherTypeWeb(); 822 mDatastoreManager.runInTransaction( 823 measurementDao -> { 824 assertEquals( 825 1, 826 measurementDao.getNumSourcesPerPublisher( 827 WEB_PUBLISHER_ONE, EventSurfaceType.WEB)); 828 }); 829 mDatastoreManager.runInTransaction( 830 measurementDao -> { 831 assertEquals( 832 2, 833 measurementDao.getNumSourcesPerPublisher( 834 WEB_PUBLISHER_TWO, EventSurfaceType.WEB)); 835 }); 836 mDatastoreManager.runInTransaction( 837 measurementDao -> { 838 assertEquals( 839 1, 840 measurementDao.getNumSourcesPerPublisher( 841 WEB_PUBLISHER_THREE, EventSurfaceType.WEB)); 842 }); 843 } 844 845 @Test testGetUninstalledAppNamesContainingData_withDataInSource()846 public void testGetUninstalledAppNamesContainingData_withDataInSource() { 847 // Setup records in source storage 848 final List<Uri> appsInstalled = 849 List.of(buildRegistrant("foo"), buildRegistrant("bar"), buildRegistrant("baz")); 850 final List<Uri> appsNotInstalled = List.of(buildRegistrant("qux"), buildRegistrant("quux")); 851 insertSourceForPackageName( 852 Stream.concat(appsInstalled.stream(), appsNotInstalled.stream()) 853 .toArray(Uri[]::new)); 854 855 // Execution 856 final Optional<List<Uri>> uninstalledAppNames = 857 mDatastoreManager.runInTransactionWithResult( 858 measurementDao -> 859 measurementDao.getUninstalledAppNamesHavingMeasurementData( 860 appsInstalled)); 861 862 // Validation, apps not installed should be returned 863 assertNotNull(uninstalledAppNames); 864 assertTrue(uninstalledAppNames.isPresent()); 865 assertEquals(2, uninstalledAppNames.get().size()); 866 Set<Uri> result = new HashSet<>(uninstalledAppNames.get()); 867 assertTrue(result.contains(buildRegistrant("qux"))); 868 assertTrue(result.contains(buildRegistrant("quux"))); 869 } 870 871 @Test testGetUninstalledAppNamesContainingData_withDataInTrigger()872 public void testGetUninstalledAppNamesContainingData_withDataInTrigger() { 873 // Setup records in trigger storage 874 final List<Uri> appsInstalled = 875 List.of(buildRegistrant("foo"), buildRegistrant("bar"), buildRegistrant("baz")); 876 final List<Uri> appsNotInstalled = List.of(buildRegistrant("qux"), buildRegistrant("quux")); 877 insertTriggerForPackageName( 878 Stream.concat(appsInstalled.stream(), appsNotInstalled.stream()) 879 .toArray(Uri[]::new)); 880 881 // Execution 882 final Optional<List<Uri>> uninstalledAppNames = 883 mDatastoreManager.runInTransactionWithResult( 884 measurementDao -> 885 measurementDao.getUninstalledAppNamesHavingMeasurementData( 886 appsInstalled)); 887 888 // Validation, apps not installed should be returned 889 assertNotNull(uninstalledAppNames); 890 assertTrue(uninstalledAppNames.isPresent()); 891 assertEquals(2, uninstalledAppNames.get().size()); 892 Set<Uri> result = new HashSet<>(uninstalledAppNames.get()); 893 assertTrue(result.contains(buildRegistrant("qux"))); 894 assertTrue(result.contains(buildRegistrant("quux"))); 895 } 896 897 @Test testGetUninstalledAppNamesContainingData_withDataInTriggerAndSource()898 public void testGetUninstalledAppNamesContainingData_withDataInTriggerAndSource() { 899 // Setup records in source and trigger storage 900 final List<Uri> appsInstalled = 901 List.of(buildRegistrant("foo"), buildRegistrant("bar"), buildRegistrant("baz")); 902 final List<Uri> appsNotInstalled = List.of(buildRegistrant("qux"), buildRegistrant("quux")); 903 final Uri[] appNames = 904 Stream.concat(appsInstalled.stream(), appsNotInstalled.stream()) 905 .toArray(Uri[]::new); 906 insertSourceForPackageName(appNames); 907 insertTriggerForPackageName(appNames); 908 909 // Execution 910 final Optional<List<Uri>> uninstalledAppNames = 911 mDatastoreManager.runInTransactionWithResult( 912 measurementDao -> 913 measurementDao.getUninstalledAppNamesHavingMeasurementData( 914 appsInstalled)); 915 916 // Validation, apps not installed should be returned 917 assertNotNull(uninstalledAppNames); 918 assertTrue(uninstalledAppNames.isPresent()); 919 assertEquals(2, uninstalledAppNames.get().size()); 920 Set<Uri> result = new HashSet<>(uninstalledAppNames.get()); 921 assertTrue(result.contains(buildRegistrant("qux"))); 922 assertTrue(result.contains(buildRegistrant("quux"))); 923 } 924 925 @Test testGetUninstalledAppNamesContainingData_withDataInAsync()926 public void testGetUninstalledAppNamesContainingData_withDataInAsync() { 927 // Setup records in source storage 928 final List<Uri> appsInstalled = 929 List.of(buildRegistrant("foo"), buildRegistrant("bar"), buildRegistrant("baz")); 930 final List<Uri> appsNotInstalled = List.of(buildRegistrant("qux"), buildRegistrant("quux")); 931 insertAsyncRecordForPackageName( 932 Stream.concat(appsInstalled.stream(), appsNotInstalled.stream()) 933 .toArray(Uri[]::new)); 934 935 // Execution 936 final Optional<List<Uri>> uninstalledAppNames = 937 mDatastoreManager.runInTransactionWithResult( 938 measurementDao -> 939 measurementDao.getUninstalledAppNamesHavingMeasurementData( 940 appsInstalled)); 941 942 // Validation, apps not installed should be returned 943 assertNotNull(uninstalledAppNames); 944 assertTrue(uninstalledAppNames.isPresent()); 945 assertEquals(2, uninstalledAppNames.get().size()); 946 Set<Uri> result = new HashSet<>(uninstalledAppNames.get()); 947 assertTrue(result.contains(buildRegistrant("qux"))); 948 assertTrue(result.contains(buildRegistrant("quux"))); 949 } 950 951 @Test testGetUninstalledAppNamesContainingData_withAnyDataAndNoAppsUninstalled()952 public void testGetUninstalledAppNamesContainingData_withAnyDataAndNoAppsUninstalled() { 953 // Setup records in source storage (any storage), all these apps are still installed 954 final List<Uri> appsInstalled = 955 List.of( 956 buildRegistrant("foo"), 957 buildRegistrant("bar"), 958 buildRegistrant("baz"), 959 buildRegistrant("qux"), 960 buildRegistrant("quux")); 961 insertSourceForPackageName(appsInstalled.stream().toArray(Uri[]::new)); 962 963 // Execution 964 final Optional<List<Uri>> uninstalledAppNames = 965 mDatastoreManager.runInTransactionWithResult( 966 measurementDao -> 967 measurementDao.getUninstalledAppNamesHavingMeasurementData( 968 appsInstalled)); 969 970 // Validation, apps not installed should be returned 971 assertNotNull(uninstalledAppNames); 972 assertTrue(uninstalledAppNames.isPresent()); 973 assertTrue(uninstalledAppNames.get().isEmpty()); 974 assertEquals(0, uninstalledAppNames.get().size()); 975 } 976 977 @Test testGetUninstalledAppNamesContainingData_withNoData()978 public void testGetUninstalledAppNamesContainingData_withNoData() { 979 // Setup records, these apps don't have source nor trigger data 980 final List<Uri> appsInstalled = 981 List.of( 982 buildRegistrant("foo"), 983 buildRegistrant("bar"), 984 buildRegistrant("baz"), 985 buildRegistrant("qux"), 986 buildRegistrant("quux")); 987 988 // Execution 989 final Optional<List<Uri>> uninstalledAppNames = 990 mDatastoreManager.runInTransactionWithResult( 991 measurementDao -> 992 measurementDao.getUninstalledAppNamesHavingMeasurementData( 993 appsInstalled)); 994 995 // Validation, apps not installed should be returned 996 assertNotNull(uninstalledAppNames); 997 assertTrue(uninstalledAppNames.isPresent()); 998 assertTrue(uninstalledAppNames.get().isEmpty()); 999 assertEquals(0, uninstalledAppNames.get().size()); 1000 } 1001 1002 @Test testCountDistinctReportingOriginPerPublisherXDestinationInAttribution_atWindow()1003 public void testCountDistinctReportingOriginPerPublisherXDestinationInAttribution_atWindow() { 1004 Uri sourceSite = Uri.parse("android-app://publisher.app"); 1005 Uri appDestination = Uri.parse("android-app://destination.app"); 1006 String registrant = "android-app://registrant.app"; 1007 List<Attribution> attributionsWithAppDestinations = 1008 getAttributionsWithDifferentReportingOrigins( 1009 4, appDestination, 5000000001L, sourceSite, registrant); 1010 for (Attribution attribution : attributionsWithAppDestinations) { 1011 insertAttribution(attribution); 1012 } 1013 Uri excludedRegistrationOrigin = WebUtil.validUri("https://subdomain0.example.test"); 1014 mDatastoreManager.runInTransaction( 1015 measurementDao -> { 1016 assertEquals( 1017 Integer.valueOf(3), 1018 measurementDao 1019 .countDistinctReportingOriginsPerPublisherXDestInAttribution( 1020 sourceSite, 1021 appDestination, 1022 excludedRegistrationOrigin, 1023 5000000000L, 1024 6000000000L)); 1025 }); 1026 } 1027 1028 @Test testCountDistinctReportingOriginsPerPublisherXDestInAttribution_beyondWindow()1029 public void testCountDistinctReportingOriginsPerPublisherXDestInAttribution_beyondWindow() { 1030 Uri sourceSite = Uri.parse("android-app://publisher.app"); 1031 Uri appDestination = Uri.parse("android-app://destination.app"); 1032 String registrant = "android-app://registrant.app"; 1033 List<Attribution> attributionsWithAppDestinations = 1034 getAttributionsWithDifferentReportingOrigins( 1035 4, appDestination, 5000000000L, sourceSite, registrant); 1036 for (Attribution attribution : attributionsWithAppDestinations) { 1037 insertAttribution(attribution); 1038 } 1039 Uri excludedReportingOrigin = WebUtil.validUri("https://subdomain.example0.test"); 1040 mDatastoreManager.runInTransaction( 1041 measurementDao -> { 1042 assertEquals( 1043 Integer.valueOf(0), 1044 measurementDao 1045 .countDistinctReportingOriginsPerPublisherXDestInAttribution( 1046 sourceSite, 1047 appDestination, 1048 excludedReportingOrigin, 1049 5000000000L, 1050 6000000000L)); 1051 }); 1052 } 1053 1054 @Test testInsertDebugReport()1055 public void testInsertDebugReport() { 1056 DebugReport debugReport = createDebugReport(); 1057 mDatastoreManager.runInTransaction((dao) -> dao.insertDebugReport(debugReport)); 1058 1059 try (Cursor cursor = 1060 MeasurementDbHelper.getInstance() 1061 .getReadableDatabase() 1062 .query(DebugReportContract.TABLE, null, null, null, null, null, null)) { 1063 assertTrue(cursor.moveToNext()); 1064 DebugReport report = SqliteObjectMapper.constructDebugReportFromCursor(cursor); 1065 assertNotNull(report); 1066 assertNotNull(report.getId()); 1067 assertEquals(debugReport.getType(), report.getType()); 1068 assertEquals(debugReport.getBody().toString(), report.getBody().toString()); 1069 assertEquals(debugReport.getEnrollmentId(), report.getEnrollmentId()); 1070 assertEquals(debugReport.getRegistrationOrigin(), report.getRegistrationOrigin()); 1071 assertEquals(debugReport.getReferenceId(), report.getReferenceId()); 1072 assertEquals(debugReport.getInsertionTime(), report.getInsertionTime()); 1073 assertEquals(debugReport.getRegistrant(), report.getRegistrant()); 1074 } 1075 } 1076 1077 @Test singleAppTrigger_triggersPerDestination_returnsOne()1078 public void singleAppTrigger_triggersPerDestination_returnsOne() { 1079 List<Trigger> triggerList = new ArrayList<>(); 1080 triggerList.add(createAppTrigger(APP_ONE_DESTINATION, APP_ONE_DESTINATION)); 1081 addTriggersToDatabase(triggerList); 1082 1083 mDatastoreManager.runInTransaction( 1084 measurementDao -> 1085 assertThat( 1086 measurementDao.getNumTriggersPerDestination( 1087 APP_ONE_DESTINATION, EventSurfaceType.APP)) 1088 .isEqualTo(1)); 1089 } 1090 1091 @Test multipleAppTriggers_similarUris_triggersPerDestination()1092 public void multipleAppTriggers_similarUris_triggersPerDestination() { 1093 List<Trigger> triggerList = new ArrayList<>(); 1094 triggerList.add(createAppTrigger(APP_TWO_DESTINATION, APP_TWO_DESTINATION)); 1095 triggerList.add(createAppTrigger(APP_TWO_DESTINATION, APP_TWO_DESTINATION)); 1096 addTriggersToDatabase(triggerList); 1097 1098 mDatastoreManager.runInTransaction( 1099 measurementDao -> 1100 assertThat( 1101 measurementDao.getNumTriggersPerDestination( 1102 APP_TWO_DESTINATION, EventSurfaceType.APP)) 1103 .isEqualTo(2)); 1104 } 1105 1106 @Test noAppTriggers_triggersPerDestination_returnsNone()1107 public void noAppTriggers_triggersPerDestination_returnsNone() { 1108 mDatastoreManager.runInTransaction( 1109 measurementDao -> 1110 assertThat( 1111 measurementDao.getNumTriggersPerDestination( 1112 APP_NO_TRIGGERS, EventSurfaceType.APP)) 1113 .isEqualTo(0)); 1114 } 1115 1116 @Test multipleAppTriggers_differentPaths_returnsAllMatching()1117 public void multipleAppTriggers_differentPaths_returnsAllMatching() { 1118 List<Trigger> triggerList = new ArrayList<>(); 1119 triggerList.add(createAppTrigger(APP_THREE_DESTINATION, APP_THREE_DESTINATION)); 1120 triggerList.add(createAppTrigger(APP_THREE_DESTINATION, APP_THREE_DESTINATION_PATH1)); 1121 triggerList.add(createAppTrigger(APP_THREE_DESTINATION, APP_THREE_DESTINATION_PATH2)); 1122 addTriggersToDatabase(triggerList); 1123 1124 mDatastoreManager.runInTransaction( 1125 measurementDao -> { 1126 assertThat( 1127 measurementDao.getNumTriggersPerDestination( 1128 APP_THREE_DESTINATION, EventSurfaceType.APP)) 1129 .isEqualTo(3); 1130 // Try the same thing, but use the app uri with path to find number of triggers. 1131 assertThat( 1132 measurementDao.getNumTriggersPerDestination( 1133 APP_THREE_DESTINATION_PATH1, EventSurfaceType.APP)) 1134 .isEqualTo(3); 1135 assertThat( 1136 measurementDao.getNumTriggersPerDestination( 1137 APP_THREE_DESTINATION_PATH2, EventSurfaceType.APP)) 1138 .isEqualTo(3); 1139 Uri unseenAppThreePath = 1140 Uri.parse("android-app://com.example1.three-triggers/path3"); 1141 assertThat( 1142 measurementDao.getNumTriggersPerDestination( 1143 unseenAppThreePath, EventSurfaceType.APP)) 1144 .isEqualTo(3); 1145 }); 1146 } 1147 1148 @Test singleWebTrigger_triggersPerDestination_returnsOne()1149 public void singleWebTrigger_triggersPerDestination_returnsOne() { 1150 List<Trigger> triggerList = new ArrayList<>(); 1151 triggerList.add(createWebTrigger(WEB_ONE_DESTINATION)); 1152 addTriggersToDatabase(triggerList); 1153 1154 mDatastoreManager.runInTransaction( 1155 measurementDao -> { 1156 assertThat( 1157 measurementDao.getNumTriggersPerDestination( 1158 WEB_ONE_DESTINATION, EventSurfaceType.WEB)) 1159 .isEqualTo(1); 1160 }); 1161 } 1162 1163 @Test webTriggerMultipleSubDomains_triggersPerDestination_returnsAllMatching()1164 public void webTriggerMultipleSubDomains_triggersPerDestination_returnsAllMatching() { 1165 List<Trigger> triggerList = new ArrayList<>(); 1166 triggerList.add(createWebTrigger(WEB_ONE_DESTINATION)); 1167 triggerList.add(createWebTrigger(WEB_ONE_DESTINATION_DIFFERENT_SUBDOMAIN)); 1168 triggerList.add(createWebTrigger(WEB_ONE_DESTINATION_DIFFERENT_SUBDOMAIN_2)); 1169 addTriggersToDatabase(triggerList); 1170 1171 mDatastoreManager.runInTransaction( 1172 measurementDao -> { 1173 assertThat( 1174 measurementDao.getNumTriggersPerDestination( 1175 WEB_ONE_DESTINATION, EventSurfaceType.WEB)) 1176 .isEqualTo(3); 1177 assertThat( 1178 measurementDao.getNumTriggersPerDestination( 1179 WEB_ONE_DESTINATION_DIFFERENT_SUBDOMAIN, 1180 EventSurfaceType.WEB)) 1181 .isEqualTo(3); 1182 assertThat( 1183 measurementDao.getNumTriggersPerDestination( 1184 WEB_ONE_DESTINATION_DIFFERENT_SUBDOMAIN_2, 1185 EventSurfaceType.WEB)) 1186 .isEqualTo(3); 1187 assertThat( 1188 measurementDao.getNumTriggersPerDestination( 1189 WebUtil.validUri("https://new-subdomain.example1.test"), 1190 EventSurfaceType.WEB)) 1191 .isEqualTo(3); 1192 assertThat( 1193 measurementDao.getNumTriggersPerDestination( 1194 WebUtil.validUri("https://example1.test"), 1195 EventSurfaceType.WEB)) 1196 .isEqualTo(3); 1197 }); 1198 } 1199 1200 @Test webTriggerWithoutSubdomains_triggersPerDestination_returnsAllMatching()1201 public void webTriggerWithoutSubdomains_triggersPerDestination_returnsAllMatching() { 1202 List<Trigger> triggerList = new ArrayList<>(); 1203 Uri webDestinationWithoutSubdomain = WebUtil.validUri("https://example1.test"); 1204 Uri webDestinationWithoutSubdomainPath1 = WebUtil.validUri("https://example1.test/path1"); 1205 Uri webDestinationWithoutSubdomainPath2 = WebUtil.validUri("https://example1.test/path2"); 1206 Uri webDestinationWithoutSubdomainPath3 = WebUtil.validUri("https://example1.test/path3"); 1207 triggerList.add(createWebTrigger(webDestinationWithoutSubdomain)); 1208 triggerList.add(createWebTrigger(webDestinationWithoutSubdomainPath1)); 1209 triggerList.add(createWebTrigger(webDestinationWithoutSubdomainPath2)); 1210 addTriggersToDatabase(triggerList); 1211 1212 mDatastoreManager.runInTransaction( 1213 measurementDao -> { 1214 assertThat( 1215 measurementDao.getNumTriggersPerDestination( 1216 webDestinationWithoutSubdomain, EventSurfaceType.WEB)) 1217 .isEqualTo(3); 1218 assertThat( 1219 measurementDao.getNumTriggersPerDestination( 1220 webDestinationWithoutSubdomainPath1, 1221 EventSurfaceType.WEB)) 1222 .isEqualTo(3); 1223 assertThat( 1224 measurementDao.getNumTriggersPerDestination( 1225 webDestinationWithoutSubdomainPath2, 1226 EventSurfaceType.WEB)) 1227 .isEqualTo(3); 1228 assertThat( 1229 measurementDao.getNumTriggersPerDestination( 1230 webDestinationWithoutSubdomainPath3, 1231 EventSurfaceType.WEB)) 1232 .isEqualTo(3); 1233 }); 1234 } 1235 1236 @Test webTriggerDifferentPaths_triggersPerDestination_returnsAllMatching()1237 public void webTriggerDifferentPaths_triggersPerDestination_returnsAllMatching() { 1238 List<Trigger> triggerList = new ArrayList<>(); 1239 triggerList.add(createWebTrigger(WEB_TWO_DESTINATION)); 1240 triggerList.add(createWebTrigger(WEB_TWO_DESTINATION_WITH_PATH)); 1241 addTriggersToDatabase(triggerList); 1242 1243 mDatastoreManager.runInTransaction( 1244 measurementDao -> { 1245 assertThat( 1246 measurementDao.getNumTriggersPerDestination( 1247 WEB_TWO_DESTINATION, EventSurfaceType.WEB)) 1248 .isEqualTo(2); 1249 assertThat( 1250 measurementDao.getNumTriggersPerDestination( 1251 WEB_TWO_DESTINATION_WITH_PATH, EventSurfaceType.WEB)) 1252 .isEqualTo(2); 1253 }); 1254 } 1255 1256 @Test noMathingWebTriggers_triggersPerDestination_returnsZero()1257 public void noMathingWebTriggers_triggersPerDestination_returnsZero() { 1258 List<Trigger> triggerList = new ArrayList<>(); 1259 triggerList.add(createWebTrigger(WEB_ONE_DESTINATION)); 1260 addTriggersToDatabase(triggerList); 1261 1262 mDatastoreManager.runInTransaction( 1263 measurementDao -> { 1264 Uri differentScheme = WebUtil.validUri("http://www.example1.test"); 1265 assertThat( 1266 measurementDao.getNumTriggersPerDestination( 1267 differentScheme, EventSurfaceType.WEB)) 1268 .isEqualTo(0); 1269 1270 Uri notMatchingUrl2 = WebUtil.validUri("https://www.not-example1.test"); 1271 assertThat( 1272 measurementDao.getNumTriggersPerDestination( 1273 notMatchingUrl2, EventSurfaceType.WEB)) 1274 .isEqualTo(0); 1275 1276 Uri notMatchingUrl = WebUtil.validUri("https://www.not-example-1.test"); 1277 assertThat( 1278 measurementDao.getNumTriggersPerDestination( 1279 notMatchingUrl, EventSurfaceType.WEB)) 1280 .isEqualTo(0); 1281 }); 1282 } 1283 1284 @Test testCountDistinctReportingOriginsPerPublisherXDestinationInAttribution_appDest()1285 public void testCountDistinctReportingOriginsPerPublisherXDestinationInAttribution_appDest() { 1286 Uri sourceSite = Uri.parse("android-app://publisher.app"); 1287 Uri webDestination = WebUtil.validUri("https://web-destination.test"); 1288 Uri appDestination = Uri.parse("android-app://destination.app"); 1289 String registrant = "android-app://registrant.app"; 1290 List<Attribution> attributionsWithAppDestinations1 = 1291 getAttributionsWithDifferentReportingOrigins( 1292 4, appDestination, 5000000000L, sourceSite, registrant); 1293 List<Attribution> attributionsWithAppDestinations2 = 1294 getAttributionsWithDifferentReportingOrigins( 1295 2, appDestination, 5000000000L, sourceSite, registrant); 1296 List<Attribution> attributionsWithWebDestinations = 1297 getAttributionsWithDifferentReportingOrigins( 1298 2, webDestination, 5500000000L, sourceSite, registrant); 1299 List<Attribution> attributionsOutOfWindow = 1300 getAttributionsWithDifferentReportingOrigins( 1301 10, appDestination, 50000000000L, sourceSite, registrant); 1302 for (Attribution attribution : attributionsWithAppDestinations1) { 1303 insertAttribution(attribution); 1304 } 1305 for (Attribution attribution : attributionsWithAppDestinations2) { 1306 insertAttribution(attribution); 1307 } 1308 for (Attribution attribution : attributionsWithWebDestinations) { 1309 insertAttribution(attribution); 1310 } 1311 for (Attribution attribution : attributionsOutOfWindow) { 1312 insertAttribution(attribution); 1313 } 1314 Uri excludedReportingOrigin = WebUtil.validUri("https://subdomain0.example.test"); 1315 mDatastoreManager.runInTransaction( 1316 measurementDao -> { 1317 assertEquals( 1318 Integer.valueOf(3), 1319 measurementDao 1320 .countDistinctReportingOriginsPerPublisherXDestInAttribution( 1321 sourceSite, 1322 appDestination, 1323 excludedReportingOrigin, 1324 4000000000L, 1325 6000000000L)); 1326 }); 1327 } 1328 1329 @Test testCountDistinctReportingOriginsPerPublisherXDestinationInAttribution_webDest()1330 public void testCountDistinctReportingOriginsPerPublisherXDestinationInAttribution_webDest() { 1331 Uri sourceSite = Uri.parse("android-app://publisher.app"); 1332 Uri webDestination = WebUtil.validUri("https://web-destination.test"); 1333 Uri appDestination = Uri.parse("android-app://destination.app"); 1334 String registrant = "android-app://registrant.app"; 1335 List<Attribution> attributionsWithAppDestinations = 1336 getAttributionsWithDifferentReportingOrigins( 1337 2, appDestination, 5000000000L, sourceSite, registrant); 1338 List<Attribution> attributionsWithWebDestinations1 = 1339 getAttributionsWithDifferentReportingOrigins( 1340 4, webDestination, 5000000000L, sourceSite, registrant); 1341 List<Attribution> attributionsWithWebDestinations2 = 1342 getAttributionsWithDifferentReportingOrigins( 1343 2, webDestination, 5500000000L, sourceSite, registrant); 1344 List<Attribution> attributionsOutOfWindow = 1345 getAttributionsWithDifferentReportingOrigins( 1346 10, webDestination, 50000000000L, sourceSite, registrant); 1347 for (Attribution attribution : attributionsWithAppDestinations) { 1348 insertAttribution(attribution); 1349 } 1350 for (Attribution attribution : attributionsWithWebDestinations1) { 1351 insertAttribution(attribution); 1352 } 1353 for (Attribution attribution : attributionsWithWebDestinations2) { 1354 insertAttribution(attribution); 1355 } 1356 for (Attribution attribution : attributionsOutOfWindow) { 1357 insertAttribution(attribution); 1358 } 1359 Uri excludedReportingOrigin = WebUtil.validUri("https://subdomain3.example.test"); 1360 mDatastoreManager.runInTransaction( 1361 measurementDao -> { 1362 assertEquals( 1363 Integer.valueOf(3), 1364 measurementDao 1365 .countDistinctReportingOriginsPerPublisherXDestInAttribution( 1366 sourceSite, 1367 webDestination, 1368 excludedReportingOrigin, 1369 4000000000L, 1370 6000000000L)); 1371 }); 1372 } 1373 1374 @Test testCountDistinctDestinations_atWindow()1375 public void testCountDistinctDestinations_atWindow() { 1376 Uri publisher = Uri.parse("android-app://publisher.app"); 1377 List<Source> activeSourcesWithAppAndWebDestinations = 1378 getSourcesWithDifferentDestinations( 1379 4, 1380 true, 1381 true, 1382 4500000001L, 1383 publisher, 1384 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1385 Source.Status.ACTIVE); 1386 for (Source source : activeSourcesWithAppAndWebDestinations) { 1387 insertSource(source); 1388 } 1389 List<Uri> excludedDestinations = 1390 List.of(WebUtil.validUri("https://web-destination-2.test")); 1391 mDatastoreManager.runInTransaction( 1392 measurementDao -> { 1393 assertEquals( 1394 Integer.valueOf(3), 1395 measurementDao 1396 .countDistinctDestPerPubXEnrollmentInUnexpiredSourceInWindow( 1397 publisher, 1398 EventSurfaceType.APP, 1399 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1400 excludedDestinations, 1401 EventSurfaceType.WEB, 1402 4500000000L, 1403 6000000000L)); 1404 }); 1405 mDatastoreManager.runInTransaction( 1406 measurementDao -> { 1407 assertEquals( 1408 Integer.valueOf(3), 1409 measurementDao 1410 .countDistinctDestinationsPerPubXEnrollmentInUnexpiredSource( 1411 publisher, 1412 EventSurfaceType.APP, 1413 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1414 excludedDestinations, 1415 EventSurfaceType.WEB, 1416 6000000000L)); 1417 }); 1418 mDatastoreManager.runInTransaction( 1419 measurementDao -> { 1420 assertEquals( 1421 Integer.valueOf(3), 1422 measurementDao 1423 .countDistinctDestinationsPerPublisherPerRateLimitWindow( 1424 publisher, 1425 EventSurfaceType.APP, 1426 excludedDestinations, 1427 EventSurfaceType.WEB, 1428 4500000000L, 1429 6000000000L)); 1430 }); 1431 } 1432 1433 @Test testCountDistinctDestinations_expiredSource()1434 public void testCountDistinctDestinations_expiredSource() { 1435 Uri publisher = Uri.parse("android-app://publisher.app"); 1436 List<Source> activeSourcesWithAppAndWebDestinations = 1437 getSourcesWithDifferentDestinations( 1438 4, 1439 true, 1440 true, 1441 4500000001L, 1442 publisher, 1443 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1444 Source.Status.ACTIVE); 1445 List<Source> expiredSourcesWithAppAndWebDestinations = 1446 getSourcesWithDifferentDestinations( 1447 6, 1448 true, 1449 true, 1450 4500000001L, 1451 6000000000L, 1452 publisher, 1453 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1454 Source.Status.ACTIVE, 1455 REGISTRATION_ORIGIN); 1456 for (Source source : activeSourcesWithAppAndWebDestinations) { 1457 insertSource(source); 1458 } 1459 for (Source source : expiredSourcesWithAppAndWebDestinations) { 1460 insertSource(source); 1461 } 1462 List<Uri> excludedDestinations = 1463 List.of(WebUtil.validUri("https://web-destination-2.test")); 1464 mDatastoreManager.runInTransaction( 1465 measurementDao -> { 1466 assertEquals( 1467 Integer.valueOf(3), 1468 measurementDao 1469 .countDistinctDestPerPubXEnrollmentInUnexpiredSourceInWindow( 1470 publisher, 1471 EventSurfaceType.APP, 1472 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1473 excludedDestinations, 1474 EventSurfaceType.WEB, 1475 4500000000L, 1476 6000000000L)); 1477 }); 1478 mDatastoreManager.runInTransaction( 1479 measurementDao -> { 1480 assertEquals( 1481 Integer.valueOf(3), 1482 measurementDao 1483 .countDistinctDestinationsPerPubXEnrollmentInUnexpiredSource( 1484 publisher, 1485 EventSurfaceType.APP, 1486 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1487 excludedDestinations, 1488 EventSurfaceType.WEB, 1489 6000000000L)); 1490 }); 1491 mDatastoreManager.runInTransaction( 1492 measurementDao -> { 1493 assertEquals( 1494 Integer.valueOf(3), 1495 measurementDao 1496 .countDistinctDestinationsPerPublisherPerRateLimitWindow( 1497 publisher, 1498 EventSurfaceType.APP, 1499 excludedDestinations, 1500 EventSurfaceType.WEB, 1501 4500000000L, 1502 6000000000L)); 1503 }); 1504 } 1505 1506 @Test testCountDistinctDestinations_beyondWindow()1507 public void testCountDistinctDestinations_beyondWindow() { 1508 Uri publisher = Uri.parse("android-app://publisher.app"); 1509 List<Source> activeSourcesWithAppAndWebDestinations = 1510 getSourcesWithDifferentDestinations( 1511 4, 1512 true, 1513 true, 1514 4500000000L, 1515 publisher, 1516 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1517 Source.Status.ACTIVE); 1518 for (Source source : activeSourcesWithAppAndWebDestinations) { 1519 insertSource(source); 1520 } 1521 List<Uri> excludedDestinations = 1522 List.of(WebUtil.validUri("https://web-destination-2.test")); 1523 mDatastoreManager.runInTransaction( 1524 measurementDao -> { 1525 assertEquals( 1526 Integer.valueOf(0), 1527 measurementDao 1528 .countDistinctDestPerPubXEnrollmentInUnexpiredSourceInWindow( 1529 publisher, 1530 EventSurfaceType.APP, 1531 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1532 excludedDestinations, 1533 EventSurfaceType.WEB, 1534 4500000000L, 1535 6000000000L)); 1536 }); 1537 mDatastoreManager.runInTransaction( 1538 measurementDao -> { 1539 assertEquals( 1540 Integer.valueOf(3), 1541 measurementDao 1542 .countDistinctDestinationsPerPubXEnrollmentInUnexpiredSource( 1543 publisher, 1544 EventSurfaceType.APP, 1545 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1546 excludedDestinations, 1547 EventSurfaceType.WEB, 1548 6000000000L)); 1549 }); 1550 mDatastoreManager.runInTransaction( 1551 measurementDao -> { 1552 assertEquals( 1553 Integer.valueOf(0), 1554 measurementDao 1555 .countDistinctDestinationsPerPublisherPerRateLimitWindow( 1556 publisher, 1557 EventSurfaceType.APP, 1558 excludedDestinations, 1559 EventSurfaceType.WEB, 1560 4500000000L, 1561 6000000000L)); 1562 }); 1563 } 1564 1565 @Test testCountDistinctDestinations_appPublisher()1566 public void testCountDistinctDestinations_appPublisher() { 1567 Uri publisher = Uri.parse("android-app://publisher.app"); 1568 List<Source> activeSourcesWithAppAndWebDestinations = 1569 getSourcesWithDifferentDestinations( 1570 4, 1571 true, 1572 true, 1573 4500000000L, 1574 publisher, 1575 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1576 Source.Status.ACTIVE); 1577 List<Source> activeSourcesWithAppDestinations = 1578 getSourcesWithDifferentDestinations( 1579 2, 1580 true, 1581 false, 1582 5000000000L, 1583 publisher, 1584 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1585 Source.Status.ACTIVE); 1586 List<Source> activeSourcesWithWebDestinations = 1587 getSourcesWithDifferentDestinations( 1588 2, 1589 false, 1590 true, 1591 5500000000L, 1592 publisher, 1593 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1594 Source.Status.ACTIVE); 1595 List<Source> activeSourcesOutOfWindow = 1596 getSourcesWithDifferentDestinations( 1597 10, 1598 true, 1599 true, 1600 50000000000L, 1601 publisher, 1602 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1603 Source.Status.ACTIVE); 1604 List<Source> ignoredSources = 1605 getSourcesWithDifferentDestinations( 1606 6, 1607 true, 1608 true, 1609 5000000000L, 1610 publisher, 1611 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1612 Source.Status.IGNORED); 1613 for (Source source : activeSourcesWithAppAndWebDestinations) { 1614 insertSource(source); 1615 } 1616 for (Source source : activeSourcesWithAppDestinations) { 1617 insertSource(source); 1618 } 1619 for (Source source : activeSourcesWithWebDestinations) { 1620 insertSource(source); 1621 } 1622 for (Source source : activeSourcesOutOfWindow) { 1623 insertSource(source); 1624 } 1625 for (Source source : ignoredSources) { 1626 insertSource(source); 1627 } 1628 List<Uri> excludedDestinations = 1629 List.of(WebUtil.validUri("https://web-destination-2.test")); 1630 mDatastoreManager.runInTransaction( 1631 measurementDao -> { 1632 assertEquals( 1633 Integer.valueOf(5), 1634 measurementDao 1635 .countDistinctDestPerPubXEnrollmentInUnexpiredSourceInWindow( 1636 publisher, 1637 EventSurfaceType.APP, 1638 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1639 excludedDestinations, 1640 EventSurfaceType.WEB, 1641 4000000000L, 1642 6000000000L)); 1643 }); 1644 mDatastoreManager.runInTransaction( 1645 measurementDao -> { 1646 assertEquals( 1647 Integer.valueOf(9), 1648 measurementDao 1649 .countDistinctDestinationsPerPubXEnrollmentInUnexpiredSource( 1650 publisher, 1651 EventSurfaceType.APP, 1652 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1653 excludedDestinations, 1654 EventSurfaceType.WEB, 1655 6000000000L)); 1656 }); 1657 mDatastoreManager.runInTransaction( 1658 measurementDao -> { 1659 assertEquals( 1660 Integer.valueOf(5), 1661 measurementDao 1662 .countDistinctDestinationsPerPublisherPerRateLimitWindow( 1663 publisher, 1664 EventSurfaceType.APP, 1665 excludedDestinations, 1666 EventSurfaceType.WEB, 1667 4000000000L, 1668 6000000000L)); 1669 }); 1670 } 1671 1672 // (Testing countDistinctDestinationsPerPublisherInActiveSource) 1673 @Test testCountDistinctDestinations_appPublisher_differentEnrollment()1674 public void testCountDistinctDestinations_appPublisher_differentEnrollment() { 1675 Uri publisher = Uri.parse("android-app://publisher.app"); 1676 List<Source> activeSourcesWithAppAndWebDestinations = 1677 getSourcesWithDifferentDestinations( 1678 4, 1679 true, 1680 true, 1681 4500000000L, 1682 publisher, 1683 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1684 Source.Status.ACTIVE); 1685 List<Source> activeSourcesWithAppDestinations = 1686 getSourcesWithDifferentDestinations( 1687 2, 1688 true, 1689 false, 1690 5000000000L, 1691 publisher, 1692 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1693 Source.Status.ACTIVE); 1694 List<Source> activeSourcesWithWebDestinations = 1695 getSourcesWithDifferentDestinations( 1696 2, 1697 false, 1698 true, 1699 5500000000L, 1700 publisher, 1701 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1702 Source.Status.ACTIVE); 1703 List<Source> activeSourcesOutOfWindow = 1704 getSourcesWithDifferentDestinations( 1705 10, 1706 true, 1707 true, 1708 50000000000L, 1709 publisher, 1710 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1711 Source.Status.ACTIVE); 1712 List<Source> ignoredSources = 1713 getSourcesWithDifferentDestinations( 1714 6, 1715 true, 1716 true, 1717 5000000000L, 1718 publisher, 1719 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1720 Source.Status.IGNORED); 1721 for (Source source : activeSourcesWithAppAndWebDestinations) { 1722 insertSource(source); 1723 } 1724 for (Source source : activeSourcesWithAppDestinations) { 1725 insertSource(source); 1726 } 1727 for (Source source : activeSourcesWithWebDestinations) { 1728 insertSource(source); 1729 } 1730 for (Source source : activeSourcesOutOfWindow) { 1731 insertSource(source); 1732 } 1733 for (Source source : ignoredSources) { 1734 insertSource(source); 1735 } 1736 List<Uri> excludedDestinations = 1737 List.of(WebUtil.validUri("https://web-destination-2.test")); 1738 mDatastoreManager.runInTransaction( 1739 measurementDao -> { 1740 assertEquals( 1741 Integer.valueOf(0), 1742 measurementDao 1743 .countDistinctDestPerPubXEnrollmentInUnexpiredSourceInWindow( 1744 publisher, 1745 EventSurfaceType.APP, 1746 "unmatched-enrollment-id", 1747 excludedDestinations, 1748 EventSurfaceType.WEB, 1749 4000000000L, 1750 6000000000L)); 1751 }); 1752 mDatastoreManager.runInTransaction( 1753 measurementDao -> { 1754 assertEquals( 1755 Integer.valueOf(0), 1756 measurementDao 1757 .countDistinctDestinationsPerPubXEnrollmentInUnexpiredSource( 1758 publisher, 1759 EventSurfaceType.APP, 1760 "unmatched-enrollment-id", 1761 excludedDestinations, 1762 EventSurfaceType.WEB, 1763 6000000000L)); 1764 }); 1765 mDatastoreManager.runInTransaction( 1766 measurementDao -> { 1767 assertEquals( 1768 Integer.valueOf(5), 1769 measurementDao 1770 .countDistinctDestinationsPerPublisherPerRateLimitWindow( 1771 publisher, 1772 EventSurfaceType.APP, 1773 excludedDestinations, 1774 EventSurfaceType.WEB, 1775 4000000000L, 1776 6000000000L)); 1777 }); 1778 } 1779 1780 @Test testCountDistinctDestinations_webPublisher_exactMatch()1781 public void testCountDistinctDestinations_webPublisher_exactMatch() { 1782 Uri publisher = WebUtil.validUri("https://publisher.test"); 1783 List<Source> activeSourcesWithAppAndWebDestinations = 1784 getSourcesWithDifferentDestinations( 1785 4, 1786 true, 1787 true, 1788 4500000000L, 1789 publisher, 1790 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1791 Source.Status.ACTIVE); 1792 List<Source> activeSourcesWithAppDestinations = 1793 getSourcesWithDifferentDestinations( 1794 2, 1795 true, 1796 false, 1797 5000000000L, 1798 publisher, 1799 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1800 Source.Status.ACTIVE); 1801 List<Source> activeSourcesWithWebDestinations = 1802 getSourcesWithDifferentDestinations( 1803 2, 1804 false, 1805 true, 1806 5500000000L, 1807 publisher, 1808 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1809 Source.Status.ACTIVE); 1810 List<Source> activeSourcesOutOfWindow = 1811 getSourcesWithDifferentDestinations( 1812 10, 1813 true, 1814 true, 1815 50000000000L, 1816 publisher, 1817 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1818 Source.Status.ACTIVE); 1819 List<Source> ignoredSources = 1820 getSourcesWithDifferentDestinations( 1821 6, 1822 true, 1823 true, 1824 5000000000L, 1825 publisher, 1826 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1827 Source.Status.IGNORED); 1828 for (Source source : activeSourcesWithAppAndWebDestinations) { 1829 insertSource(source); 1830 } 1831 for (Source source : activeSourcesWithAppDestinations) { 1832 insertSource(source); 1833 } 1834 for (Source source : activeSourcesWithWebDestinations) { 1835 insertSource(source); 1836 } 1837 for (Source source : activeSourcesOutOfWindow) { 1838 insertSource(source); 1839 } 1840 for (Source source : ignoredSources) { 1841 insertSource(source); 1842 } 1843 List<Uri> excludedDestinations = 1844 List.of(WebUtil.validUri("https://web-destination-2.test")); 1845 mDatastoreManager.runInTransaction( 1846 measurementDao -> { 1847 assertEquals( 1848 Integer.valueOf(5), 1849 measurementDao 1850 .countDistinctDestPerPubXEnrollmentInUnexpiredSourceInWindow( 1851 publisher, 1852 EventSurfaceType.WEB, 1853 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1854 excludedDestinations, 1855 EventSurfaceType.WEB, 1856 4000000000L, 1857 6000000000L)); 1858 }); 1859 mDatastoreManager.runInTransaction( 1860 measurementDao -> { 1861 assertEquals( 1862 Integer.valueOf(9), 1863 measurementDao 1864 .countDistinctDestinationsPerPubXEnrollmentInUnexpiredSource( 1865 publisher, 1866 EventSurfaceType.WEB, 1867 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1868 excludedDestinations, 1869 EventSurfaceType.WEB, 1870 6000000000L)); 1871 }); 1872 mDatastoreManager.runInTransaction( 1873 measurementDao -> { 1874 assertEquals( 1875 Integer.valueOf(5), 1876 measurementDao 1877 .countDistinctDestinationsPerPublisherPerRateLimitWindow( 1878 publisher, 1879 EventSurfaceType.WEB, 1880 excludedDestinations, 1881 EventSurfaceType.WEB, 1882 4000000000L, 1883 6000000000L)); 1884 }); 1885 } 1886 1887 @Test testCountDistinctDestinations_webPublisher_doesNotMatchDomainAsSuffix()1888 public void testCountDistinctDestinations_webPublisher_doesNotMatchDomainAsSuffix() { 1889 Uri publisher = WebUtil.validUri("https://publisher.test"); 1890 Uri publisherAsSuffix = WebUtil.validUri("https://prefix-publisher.test"); 1891 List<Source> activeSourcesWithAppAndWebDestinations = 1892 getSourcesWithDifferentDestinations( 1893 8, 1894 true, 1895 true, 1896 4500000000L, 1897 publisherAsSuffix, 1898 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1899 Source.Status.ACTIVE); 1900 List<Source> ignoredSourcesWithAppAndWebDestinations = 1901 getSourcesWithDifferentDestinations( 1902 4, 1903 true, 1904 true, 1905 4500000000L, 1906 publisher, 1907 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1908 Source.Status.IGNORED); 1909 List<Source> activeSourcesWithAppDestinations = 1910 getSourcesWithDifferentDestinations( 1911 2, 1912 true, 1913 false, 1914 5000000000L, 1915 publisher, 1916 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1917 Source.Status.ACTIVE); 1918 List<Source> activeSourcesWithWebDestinations = 1919 getSourcesWithDifferentDestinations( 1920 2, 1921 false, 1922 true, 1923 5500000000L, 1924 publisher, 1925 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1926 Source.Status.ACTIVE); 1927 List<Source> activeSourcesOutOfWindow = 1928 getSourcesWithDifferentDestinations( 1929 10, 1930 true, 1931 true, 1932 50000000000L, 1933 publisherAsSuffix, 1934 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1935 Source.Status.ACTIVE); 1936 List<Source> markedAsDeletedSources = 1937 getSourcesWithDifferentDestinations( 1938 5, 1939 true, 1940 true, 1941 5000000000L, 1942 publisher, 1943 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1944 Source.Status.MARKED_TO_DELETE); 1945 for (Source source : activeSourcesWithAppAndWebDestinations) { 1946 insertSource(source); 1947 } 1948 for (Source source : ignoredSourcesWithAppAndWebDestinations) { 1949 insertSource(source); 1950 } 1951 for (Source source : activeSourcesWithAppDestinations) { 1952 insertSource(source); 1953 } 1954 for (Source source : activeSourcesWithWebDestinations) { 1955 insertSource(source); 1956 } 1957 for (Source source : activeSourcesOutOfWindow) { 1958 insertSource(source); 1959 } 1960 for (Source source : markedAsDeletedSources) { 1961 insertSource(source); 1962 } 1963 List<Uri> excludedDestinations = 1964 List.of(WebUtil.validUri("https://web-destination-2.test")); 1965 mDatastoreManager.runInTransaction( 1966 measurementDao -> { 1967 assertEquals( 1968 Integer.valueOf(4), 1969 measurementDao 1970 .countDistinctDestPerPubXEnrollmentInUnexpiredSourceInWindow( 1971 publisher, 1972 EventSurfaceType.WEB, 1973 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1974 excludedDestinations, 1975 EventSurfaceType.WEB, 1976 4000000000L, 1977 6000000000L)); 1978 }); 1979 mDatastoreManager.runInTransaction( 1980 measurementDao -> { 1981 assertEquals( 1982 Integer.valueOf(3), 1983 measurementDao 1984 .countDistinctDestinationsPerPubXEnrollmentInUnexpiredSource( 1985 publisher, 1986 EventSurfaceType.WEB, 1987 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 1988 excludedDestinations, 1989 EventSurfaceType.WEB, 1990 6000000000L)); 1991 }); 1992 mDatastoreManager.runInTransaction( 1993 measurementDao -> 1994 assertEquals( 1995 Integer.valueOf(4), 1996 measurementDao 1997 .countDistinctDestinationsPerPublisherPerRateLimitWindow( 1998 publisher, 1999 EventSurfaceType.WEB, 2000 excludedDestinations, 2001 EventSurfaceType.WEB, 2002 4000000000L, 2003 6000000000L))); 2004 } 2005 2006 @Test testCountDistinctDestinations_webPublisher_doesNotMatchDifferentScheme()2007 public void testCountDistinctDestinations_webPublisher_doesNotMatchDifferentScheme() { 2008 Uri publisher = WebUtil.validUri("https://publisher.test"); 2009 Uri publisherWithDifferentScheme = WebUtil.validUri("http://publisher.test"); 2010 List<Source> activeSourcesWithAppAndWebDestinations = 2011 getSourcesWithDifferentDestinations( 2012 4, 2013 true, 2014 true, 2015 4500000000L, 2016 publisherWithDifferentScheme, 2017 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2018 Source.Status.ACTIVE); 2019 List<Source> activeSourcesWithAppDestinations = 2020 getSourcesWithDifferentDestinations( 2021 2, 2022 true, 2023 false, 2024 5000000000L, 2025 publisher, 2026 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2027 Source.Status.ACTIVE); 2028 List<Source> activeSourcesWithWebDestinations = 2029 getSourcesWithDifferentDestinations( 2030 2, 2031 false, 2032 true, 2033 5500000000L, 2034 publisher, 2035 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2036 Source.Status.ACTIVE); 2037 List<Source> activeSourcesOutOfWindow = 2038 getSourcesWithDifferentDestinations( 2039 10, 2040 true, 2041 true, 2042 50000000000L, 2043 publisher, 2044 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2045 Source.Status.ACTIVE); 2046 List<Source> ignoredSources = 2047 getSourcesWithDifferentDestinations( 2048 6, 2049 true, 2050 true, 2051 5000000000L, 2052 publisher, 2053 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2054 Source.Status.IGNORED); 2055 for (Source source : activeSourcesWithAppAndWebDestinations) { 2056 insertSource(source); 2057 } 2058 for (Source source : activeSourcesWithAppDestinations) { 2059 insertSource(source); 2060 } 2061 for (Source source : activeSourcesWithWebDestinations) { 2062 insertSource(source); 2063 } 2064 for (Source source : activeSourcesOutOfWindow) { 2065 insertSource(source); 2066 } 2067 for (Source source : ignoredSources) { 2068 insertSource(source); 2069 } 2070 List<Uri> excludedDestinations = 2071 List.of(WebUtil.validUri("https://web-destination-2.test")); 2072 mDatastoreManager.runInTransaction( 2073 measurementDao -> { 2074 assertEquals( 2075 Integer.valueOf(5), 2076 measurementDao 2077 .countDistinctDestPerPubXEnrollmentInUnexpiredSourceInWindow( 2078 publisher, 2079 EventSurfaceType.WEB, 2080 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2081 excludedDestinations, 2082 EventSurfaceType.WEB, 2083 4000000000L, 2084 6000000000L)); 2085 }); 2086 mDatastoreManager.runInTransaction( 2087 measurementDao -> { 2088 assertEquals( 2089 Integer.valueOf(9), 2090 measurementDao 2091 .countDistinctDestinationsPerPubXEnrollmentInUnexpiredSource( 2092 publisher, 2093 EventSurfaceType.WEB, 2094 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2095 excludedDestinations, 2096 EventSurfaceType.WEB, 2097 6000000000L)); 2098 }); 2099 mDatastoreManager.runInTransaction( 2100 measurementDao -> { 2101 assertEquals( 2102 Integer.valueOf(5), 2103 measurementDao 2104 .countDistinctDestinationsPerPublisherPerRateLimitWindow( 2105 publisher, 2106 EventSurfaceType.WEB, 2107 excludedDestinations, 2108 EventSurfaceType.WEB, 2109 4000000000L, 2110 6000000000L)); 2111 }); 2112 } 2113 2114 @Test testCountDistinctDestinations_webPublisher_multipleDestinations()2115 public void testCountDistinctDestinations_webPublisher_multipleDestinations() { 2116 Uri publisher = WebUtil.validUri("https://publisher.test"); 2117 // One source with multiple destinations 2118 Source activeSourceWithAppAndWebDestinations = 2119 getSourceWithDifferentDestinations( 2120 3, 2121 true, 2122 true, 2123 4500000000L, 2124 publisher, 2125 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2126 Source.Status.ACTIVE); 2127 List<Source> activeSourcesWithAppDestinations = 2128 getSourcesWithDifferentDestinations( 2129 2, 2130 true, 2131 false, 2132 5000000000L, 2133 publisher, 2134 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2135 Source.Status.ACTIVE); 2136 List<Source> activeSourcesWithWebDestinations = 2137 getSourcesWithDifferentDestinations( 2138 1, 2139 false, 2140 true, 2141 5500000000L, 2142 publisher, 2143 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2144 Source.Status.ACTIVE); 2145 List<Source> activeSourcesOutOfWindow = 2146 getSourcesWithDifferentDestinations( 2147 10, 2148 true, 2149 true, 2150 50000000000L, 2151 publisher, 2152 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2153 Source.Status.ACTIVE); 2154 List<Source> ignoredSources = 2155 getSourcesWithDifferentDestinations( 2156 4, 2157 true, 2158 true, 2159 5000000000L, 2160 publisher, 2161 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2162 Source.Status.IGNORED); 2163 insertSource(activeSourceWithAppAndWebDestinations); 2164 for (Source source : activeSourcesWithAppDestinations) { 2165 insertSource(source); 2166 } 2167 for (Source source : activeSourcesWithWebDestinations) { 2168 insertSource(source); 2169 } 2170 for (Source source : activeSourcesOutOfWindow) { 2171 insertSource(source); 2172 } 2173 for (Source source : ignoredSources) { 2174 insertSource(source); 2175 } 2176 List<Uri> excludedDestinations = 2177 List.of( 2178 WebUtil.validUri("https://web-destination-1.test"), 2179 WebUtil.validUri("https://web-destination-2.test")); 2180 mDatastoreManager.runInTransaction( 2181 measurementDao -> { 2182 assertEquals( 2183 Integer.valueOf(2), 2184 measurementDao 2185 .countDistinctDestPerPubXEnrollmentInUnexpiredSourceInWindow( 2186 publisher, 2187 EventSurfaceType.WEB, 2188 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2189 excludedDestinations, 2190 EventSurfaceType.WEB, 2191 4000000000L, 2192 6000000000L)); 2193 }); 2194 mDatastoreManager.runInTransaction( 2195 measurementDao -> { 2196 assertEquals( 2197 Integer.valueOf(8), 2198 measurementDao 2199 .countDistinctDestinationsPerPubXEnrollmentInUnexpiredSource( 2200 publisher, 2201 EventSurfaceType.WEB, 2202 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2203 excludedDestinations, 2204 EventSurfaceType.WEB, 2205 6000000000L)); 2206 }); 2207 mDatastoreManager.runInTransaction( 2208 measurementDao -> { 2209 assertEquals( 2210 Integer.valueOf(2), 2211 measurementDao 2212 .countDistinctDestinationsPerPublisherPerRateLimitWindow( 2213 publisher, 2214 EventSurfaceType.WEB, 2215 excludedDestinations, 2216 EventSurfaceType.WEB, 2217 4000000000L, 2218 6000000000L)); 2219 }); 2220 } 2221 2222 // Tests countSourcesPerPublisherXEnrollmentExcludingRegistrationOriginSinceTime 2223 @Test testCountSourcesExclRegOrigin_forSameOrigin_returnsZero()2224 public void testCountSourcesExclRegOrigin_forSameOrigin_returnsZero() { 2225 // Positive case. For same registration origin we always pass the 1 origin 2226 // per site limit and return 0 2227 Uri appPublisher = Uri.parse("android-app://publisher.app"); 2228 List<Source> sourcesMoreThanOneDayOld = 2229 getSourcesWithDifferentDestinations( 2230 5, 2231 true, 2232 true, 2233 System.currentTimeMillis() - DAYS.toMillis(2), 2234 appPublisher, 2235 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2236 Source.Status.ACTIVE, 2237 REGISTRATION_ORIGIN); 2238 2239 List<Source> sourcesRecent = 2240 getSourcesWithDifferentDestinations( 2241 5, 2242 true, 2243 true, 2244 System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(2), 2245 appPublisher, 2246 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2247 Source.Status.ACTIVE, 2248 REGISTRATION_ORIGIN); 2249 2250 for (Source source : sourcesMoreThanOneDayOld) { 2251 insertSource(source); 2252 } 2253 for (Source source : sourcesRecent) { 2254 insertSource(source); 2255 } 2256 mDatastoreManager.runInTransaction( 2257 measurementDao -> { 2258 assertEquals( 2259 Integer.valueOf(0), 2260 measurementDao 2261 .countDistinctRegOriginPerPublisherXEnrollmentExclRegOrigin( 2262 REGISTRATION_ORIGIN, 2263 appPublisher, 2264 EventSurfaceType.APP, 2265 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2266 System.currentTimeMillis(), 2267 MEASUREMENT_MIN_REPORTING_ORIGIN_UPDATE_WINDOW)); 2268 }); 2269 } 2270 2271 @Test testCountSourcesExclRegOrigin_forDifferentAppPublisher_returnsZero()2272 public void testCountSourcesExclRegOrigin_forDifferentAppPublisher_returnsZero() { 2273 // Positive case. For different publisher we always pass the 1 origin 2274 // per site limit and return 0 2275 Uri appPublisher = Uri.parse("android-app://publisher.app"); 2276 Uri appPublisher2 = Uri.parse("android-app://publisher2.app"); 2277 List<Source> sources = 2278 getSourcesWithDifferentDestinations( 2279 5, 2280 true, 2281 true, 2282 System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(2), 2283 appPublisher, 2284 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2285 Source.Status.ACTIVE, 2286 REGISTRATION_ORIGIN); 2287 for (Source source : sources) { 2288 insertSource(source); 2289 } 2290 mDatastoreManager.runInTransaction( 2291 measurementDao -> { 2292 assertEquals( 2293 Integer.valueOf(0), 2294 measurementDao 2295 .countDistinctRegOriginPerPublisherXEnrollmentExclRegOrigin( 2296 REGISTRATION_ORIGIN_2, 2297 appPublisher2, 2298 EventSurfaceType.APP, 2299 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2300 System.currentTimeMillis(), 2301 MEASUREMENT_MIN_REPORTING_ORIGIN_UPDATE_WINDOW)); 2302 }); 2303 } 2304 2305 @Test testCountSourcesExclRegOrigin_forDifferentWebPublisher_returnsZero()2306 public void testCountSourcesExclRegOrigin_forDifferentWebPublisher_returnsZero() { 2307 // Positive case. For different publisher we always pass the 1 origin 2308 // per site limit and return 0 2309 Uri publisher = WebUtil.validUri("https://publisher.test"); 2310 Uri publisher2 = WebUtil.validUri("https://publisher2.test"); 2311 List<Source> sources = 2312 getSourcesWithDifferentDestinations( 2313 5, 2314 true, 2315 true, 2316 System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(2), 2317 publisher, 2318 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2319 Source.Status.ACTIVE, 2320 REGISTRATION_ORIGIN); 2321 for (Source source : sources) { 2322 insertSource(source); 2323 } 2324 mDatastoreManager.runInTransaction( 2325 measurementDao -> { 2326 assertEquals( 2327 Integer.valueOf(0), 2328 measurementDao 2329 .countDistinctRegOriginPerPublisherXEnrollmentExclRegOrigin( 2330 REGISTRATION_ORIGIN_2, 2331 publisher2, 2332 EventSurfaceType.WEB, 2333 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2334 System.currentTimeMillis(), 2335 MEASUREMENT_MIN_REPORTING_ORIGIN_UPDATE_WINDOW)); 2336 }); 2337 } 2338 2339 @Test testCountSourcesExclRegOrigin_forDifferentEnrollment_returnsZero()2340 public void testCountSourcesExclRegOrigin_forDifferentEnrollment_returnsZero() { 2341 // Positive case. For different enrollment (aka reporting site) 2342 // we always pass the 1 origin per site limit and return 0 2343 String differentEnrollment = "new-enrollment"; 2344 Uri differentSite = WebUtil.validUri("https://subdomain.different-site.test"); 2345 Uri appPublisher = Uri.parse("android-app://publisher.app"); 2346 List<Source> sources = 2347 getSourcesWithDifferentDestinations( 2348 5, 2349 true, 2350 true, 2351 System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(2), 2352 appPublisher, 2353 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2354 Source.Status.ACTIVE, 2355 REGISTRATION_ORIGIN); 2356 for (Source source : sources) { 2357 insertSource(source); 2358 } 2359 mDatastoreManager.runInTransaction( 2360 measurementDao -> { 2361 assertEquals( 2362 Integer.valueOf(0), 2363 measurementDao 2364 .countDistinctRegOriginPerPublisherXEnrollmentExclRegOrigin( 2365 differentSite, 2366 appPublisher, 2367 EventSurfaceType.APP, 2368 differentEnrollment, 2369 System.currentTimeMillis(), 2370 MEASUREMENT_MIN_REPORTING_ORIGIN_UPDATE_WINDOW)); 2371 }); 2372 } 2373 2374 @Test testCountSourcesExclRegOrigin_forDifferentOriginMoreThanTimeWindow_returnsZero()2375 public void testCountSourcesExclRegOrigin_forDifferentOriginMoreThanTimeWindow_returnsZero() { 2376 // Positive case. For different origin with same enrollment 2377 // more than time window of 1 day we always pass the 1 origin per site 2378 // limit and return 0 2379 Uri appPublisher = Uri.parse("android-app://publisher.app"); 2380 List<Source> sources = 2381 getSourcesWithDifferentDestinations( 2382 5, 2383 true, 2384 true, 2385 System.currentTimeMillis() - DAYS.toMillis(2), 2386 appPublisher, 2387 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2388 Source.Status.ACTIVE, 2389 REGISTRATION_ORIGIN); 2390 for (Source source : sources) { 2391 insertSource(source); 2392 } 2393 mDatastoreManager.runInTransaction( 2394 measurementDao -> { 2395 assertEquals( 2396 Integer.valueOf(0), 2397 measurementDao 2398 .countDistinctRegOriginPerPublisherXEnrollmentExclRegOrigin( 2399 REGISTRATION_ORIGIN_2, 2400 appPublisher, 2401 EventSurfaceType.APP, 2402 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2403 System.currentTimeMillis(), 2404 MEASUREMENT_MIN_REPORTING_ORIGIN_UPDATE_WINDOW)); 2405 }); 2406 } 2407 2408 // Tests countSourcesPerPublisherXEnrollmentExcludingRegistrationOriginSinceTime 2409 @Test testCountSources_forDifferentOriginWithinTimeWindow_returnsNumOfSources()2410 public void testCountSources_forDifferentOriginWithinTimeWindow_returnsNumOfSources() { 2411 // Negative case. For different origin with same enrollment 2412 // we always fail the 1 origin per site limit and return 1 2413 Uri appPublisher = Uri.parse("android-app://publisher.app"); 2414 List<Source> sources = 2415 getSourcesWithDifferentDestinations( 2416 5, 2417 true, 2418 true, 2419 System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(2), 2420 appPublisher, 2421 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2422 Source.Status.ACTIVE, 2423 REGISTRATION_ORIGIN); 2424 for (Source source : sources) { 2425 insertSource(source); 2426 } 2427 mDatastoreManager.runInTransaction( 2428 measurementDao -> { 2429 assertEquals( 2430 Integer.valueOf(1), 2431 measurementDao 2432 .countDistinctRegOriginPerPublisherXEnrollmentExclRegOrigin( 2433 REGISTRATION_ORIGIN_2, 2434 appPublisher, 2435 EventSurfaceType.APP, 2436 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 2437 System.currentTimeMillis(), 2438 MEASUREMENT_MIN_REPORTING_ORIGIN_UPDATE_WINDOW)); 2439 }); 2440 } 2441 2442 @Test testCountDistinctRegistrationOriginPerPublisherXDestinationInSource_atWindow()2443 public void testCountDistinctRegistrationOriginPerPublisherXDestinationInSource_atWindow() { 2444 Uri publisher = Uri.parse("android-app://publisher.app"); 2445 List<Uri> webDestinations = List.of(WebUtil.validUri("https://web-destination.test")); 2446 List<Uri> appDestinations = List.of(Uri.parse("android-app://destination.app")); 2447 List<Source> activeSourcesWithAppAndWebDestinations = 2448 getSourcesWithDifferentRegistrationOrigins( 2449 2, 2450 appDestinations, 2451 webDestinations, 2452 4500000001L, 2453 publisher, 2454 Source.Status.ACTIVE); 2455 for (Source source : activeSourcesWithAppAndWebDestinations) { 2456 insertSource(source); 2457 } 2458 Uri excludedRegistrationOrigin = WebUtil.validUri("https://subdomain1.example.test"); 2459 mDatastoreManager.runInTransaction( 2460 measurementDao -> { 2461 assertEquals( 2462 Integer.valueOf(1), 2463 measurementDao 2464 .countDistinctReportingOriginsPerPublisherXDestinationInSource( 2465 publisher, 2466 EventSurfaceType.APP, 2467 appDestinations, 2468 excludedRegistrationOrigin, 2469 4500000000L, 2470 6000000000L)); 2471 }); 2472 } 2473 2474 @Test testCountDistinctReportingOriginsPerPublisherXDestinationInSource_beyondWindow()2475 public void testCountDistinctReportingOriginsPerPublisherXDestinationInSource_beyondWindow() { 2476 Uri publisher = Uri.parse("android-app://publisher.app"); 2477 List<Uri> webDestinations = List.of(WebUtil.validUri("https://web-destination.test")); 2478 List<Uri> appDestinations = List.of(Uri.parse("android-app://destination.app")); 2479 List<Source> activeSourcesWithAppAndWebDestinations = 2480 getSourcesWithDifferentRegistrationOrigins( 2481 2, 2482 appDestinations, 2483 webDestinations, 2484 4500000000L, 2485 publisher, 2486 Source.Status.ACTIVE); 2487 for (Source source : activeSourcesWithAppAndWebDestinations) { 2488 insertSource(source); 2489 } 2490 Uri excludedReportingOrigin = WebUtil.validUri("https://subdomain1.example.test"); 2491 mDatastoreManager.runInTransaction( 2492 measurementDao -> { 2493 assertEquals( 2494 Integer.valueOf(0), 2495 measurementDao 2496 .countDistinctReportingOriginsPerPublisherXDestinationInSource( 2497 publisher, 2498 EventSurfaceType.APP, 2499 appDestinations, 2500 excludedReportingOrigin, 2501 4500000000L, 2502 6000000000L)); 2503 }); 2504 } 2505 2506 @Test testCountDistinctReportingOriginsPerPublisherXDestinationInSource_expiredSource()2507 public void testCountDistinctReportingOriginsPerPublisherXDestinationInSource_expiredSource() { 2508 Uri publisher = Uri.parse("android-app://publisher.app"); 2509 List<Uri> webDestinations = List.of(WebUtil.validUri("https://web-destination.test")); 2510 List<Uri> appDestinations = List.of(Uri.parse("android-app://destination.app")); 2511 List<Source> activeSourcesWithAppAndWebDestinations = 2512 getSourcesWithDifferentRegistrationOrigins( 2513 2, 2514 appDestinations, 2515 webDestinations, 2516 4500000001L, 2517 publisher, 2518 Source.Status.ACTIVE); 2519 List<Source> expiredSourcesWithAppAndWebDestinations = 2520 getSourcesWithDifferentRegistrationOrigins( 2521 4, 2522 appDestinations, 2523 webDestinations, 2524 4500000001L, 2525 6000000000L, 2526 publisher, 2527 Source.Status.ACTIVE); 2528 for (Source source : activeSourcesWithAppAndWebDestinations) { 2529 insertSource(source); 2530 } 2531 for (Source source : expiredSourcesWithAppAndWebDestinations) { 2532 insertSource(source); 2533 } 2534 Uri excludedReportingOrigin = WebUtil.validUri("https://subdomain1.example.test"); 2535 mDatastoreManager.runInTransaction( 2536 measurementDao -> { 2537 assertEquals( 2538 Integer.valueOf(3), 2539 measurementDao 2540 .countDistinctReportingOriginsPerPublisherXDestinationInSource( 2541 publisher, 2542 EventSurfaceType.APP, 2543 appDestinations, 2544 excludedReportingOrigin, 2545 4500000000L, 2546 6000000000L)); 2547 }); 2548 } 2549 2550 @Test testCountDistinctReportingOriginsPerPublisherXDestinationInSource_appDestination()2551 public void testCountDistinctReportingOriginsPerPublisherXDestinationInSource_appDestination() { 2552 Uri publisher = Uri.parse("android-app://publisher.app"); 2553 List<Uri> webDestinations = List.of(WebUtil.validUri("https://web-destination.test")); 2554 List<Uri> appDestinations = List.of(Uri.parse("android-app://destination.app")); 2555 List<Source> activeSourcesWithAppAndWebDestinations = 2556 getSourcesWithDifferentRegistrationOrigins( 2557 2, 2558 appDestinations, 2559 webDestinations, 2560 4500000000L, 2561 publisher, 2562 Source.Status.ACTIVE); 2563 List<Source> activeSourcesWithAppDestinations = 2564 getSourcesWithDifferentRegistrationOrigins( 2565 2, appDestinations, null, 5000000000L, publisher, Source.Status.ACTIVE); 2566 List<Source> activeSourcesWithWebDestinations = 2567 getSourcesWithDifferentRegistrationOrigins( 2568 2, null, webDestinations, 5500000000L, publisher, Source.Status.ACTIVE); 2569 List<Source> activeSourcesOutOfWindow = 2570 getSourcesWithDifferentRegistrationOrigins( 2571 10, 2572 appDestinations, 2573 webDestinations, 2574 50000000000L, 2575 publisher, 2576 Source.Status.ACTIVE); 2577 List<Source> ignoredSources = 2578 getSourcesWithDifferentRegistrationOrigins( 2579 3, 2580 appDestinations, 2581 webDestinations, 2582 5000000000L, 2583 publisher, 2584 Source.Status.IGNORED); 2585 for (Source source : activeSourcesWithAppAndWebDestinations) { 2586 insertSource(source); 2587 } 2588 for (Source source : activeSourcesWithAppDestinations) { 2589 insertSource(source); 2590 } 2591 for (Source source : activeSourcesWithWebDestinations) { 2592 insertSource(source); 2593 } 2594 for (Source source : activeSourcesOutOfWindow) { 2595 insertSource(source); 2596 } 2597 for (Source source : ignoredSources) { 2598 insertSource(source); 2599 } 2600 String excludedEnrollmentId = "enrollment-id-1"; 2601 Uri excludedReportingOrigin = WebUtil.validUri("https://subdomain1.example.test"); 2602 mDatastoreManager.runInTransaction( 2603 measurementDao -> { 2604 assertEquals( 2605 Integer.valueOf(2), 2606 measurementDao 2607 .countDistinctReportingOriginsPerPublisherXDestinationInSource( 2608 publisher, 2609 EventSurfaceType.APP, 2610 appDestinations, 2611 excludedReportingOrigin, 2612 4000000000L, 2613 6000000000L)); 2614 }); 2615 } 2616 2617 @Test testCountDistinctReportingOriginsPerPublisherXDestinationInSource_webDestination()2618 public void testCountDistinctReportingOriginsPerPublisherXDestinationInSource_webDestination() { 2619 Uri publisher = Uri.parse("android-app://publisher.app"); 2620 List<Uri> webDestinations = List.of(WebUtil.validUri("https://web-destination.test")); 2621 List<Uri> appDestinations = List.of(Uri.parse("android-app://destination.app")); 2622 List<Source> activeSourcesWithAppAndWebDestinations = 2623 getSourcesWithDifferentRegistrationOrigins( 2624 2, 2625 appDestinations, 2626 webDestinations, 2627 4500000000L, 2628 publisher, 2629 Source.Status.ACTIVE); 2630 List<Source> activeSourcesWithAppDestinations = 2631 getSourcesWithDifferentRegistrationOrigins( 2632 2, appDestinations, null, 5000000000L, publisher, Source.Status.ACTIVE); 2633 List<Source> activeSourcesWithWebDestinations = 2634 getSourcesWithDifferentRegistrationOrigins( 2635 2, null, webDestinations, 5500000000L, publisher, Source.Status.ACTIVE); 2636 List<Source> activeSourcesOutOfWindow = 2637 getSourcesWithDifferentRegistrationOrigins( 2638 10, 2639 appDestinations, 2640 webDestinations, 2641 50000000000L, 2642 publisher, 2643 Source.Status.ACTIVE); 2644 List<Source> ignoredSources = 2645 getSourcesWithDifferentRegistrationOrigins( 2646 3, 2647 appDestinations, 2648 webDestinations, 2649 5000000000L, 2650 publisher, 2651 Source.Status.IGNORED); 2652 for (Source source : activeSourcesWithAppAndWebDestinations) { 2653 insertSource(source); 2654 } 2655 for (Source source : activeSourcesWithAppDestinations) { 2656 insertSource(source); 2657 } 2658 for (Source source : activeSourcesWithWebDestinations) { 2659 insertSource(source); 2660 } 2661 for (Source source : activeSourcesOutOfWindow) { 2662 insertSource(source); 2663 } 2664 for (Source source : ignoredSources) { 2665 insertSource(source); 2666 } 2667 String excludedEnrollmentId = "enrollment-id-22"; 2668 Uri excludedReportingOrigin = WebUtil.validUri("https://subdomain22.example.test"); 2669 mDatastoreManager.runInTransaction( 2670 measurementDao -> { 2671 assertEquals( 2672 Integer.valueOf(3), 2673 measurementDao 2674 .countDistinctReportingOriginsPerPublisherXDestinationInSource( 2675 publisher, 2676 EventSurfaceType.WEB, 2677 webDestinations, 2678 excludedReportingOrigin, 2679 4000000000L, 2680 6000000000L)); 2681 }); 2682 } 2683 2684 // countDistinctEnrollmentsPerPublisherXDestinationInSource 2685 @Test countDistinctReportingOriginsPerPublisher_webDestination_multipleDestinations()2686 public void countDistinctReportingOriginsPerPublisher_webDestination_multipleDestinations() { 2687 Uri publisher = Uri.parse("android-app://publisher.app"); 2688 List<Uri> webDestinations1 = List.of(WebUtil.validUri("https://web-destination-1.test")); 2689 List<Uri> webDestinations2 = 2690 List.of( 2691 WebUtil.validUri("https://web-destination-1.test"), 2692 WebUtil.validUri("https://web-destination-2.test")); 2693 List<Uri> appDestinations = List.of(Uri.parse("android-app://destination.app")); 2694 List<Source> activeSourcesWithAppAndWebDestinations = 2695 getSourcesWithDifferentRegistrationOrigins( 2696 3, 2697 appDestinations, 2698 webDestinations1, 2699 4500000000L, 2700 publisher, 2701 Source.Status.ACTIVE); 2702 List<Source> activeSourcesWithAppDestinations = 2703 getSourcesWithDifferentRegistrationOrigins( 2704 2, appDestinations, null, 5000000000L, publisher, Source.Status.ACTIVE); 2705 List<Source> activeSourcesWithWebDestinations = 2706 getSourcesWithDifferentRegistrationOrigins( 2707 2, null, webDestinations2, 5500000000L, publisher, Source.Status.ACTIVE); 2708 List<Source> activeSourcesOutOfWindow = 2709 getSourcesWithDifferentRegistrationOrigins( 2710 10, 2711 appDestinations, 2712 webDestinations2, 2713 50000000000L, 2714 publisher, 2715 Source.Status.ACTIVE); 2716 List<Source> ignoredSources = 2717 getSourcesWithDifferentRegistrationOrigins( 2718 2, 2719 appDestinations, 2720 webDestinations1, 2721 5000000000L, 2722 publisher, 2723 Source.Status.IGNORED); 2724 for (Source source : activeSourcesWithAppAndWebDestinations) { 2725 insertSource(source); 2726 } 2727 for (Source source : activeSourcesWithAppDestinations) { 2728 insertSource(source); 2729 } 2730 for (Source source : activeSourcesWithWebDestinations) { 2731 insertSource(source); 2732 } 2733 for (Source source : activeSourcesOutOfWindow) { 2734 insertSource(source); 2735 } 2736 for (Source source : ignoredSources) { 2737 insertSource(source); 2738 } 2739 String excludedEnrollmentId = "enrollment-id-1"; 2740 Uri excludedReportingOrigin = WebUtil.validUri("https://subdomain1.example.test"); 2741 mDatastoreManager.runInTransaction( 2742 measurementDao -> { 2743 assertEquals( 2744 Integer.valueOf(2), 2745 measurementDao 2746 .countDistinctReportingOriginsPerPublisherXDestinationInSource( 2747 publisher, 2748 EventSurfaceType.WEB, 2749 webDestinations2, 2750 excludedReportingOrigin, 2751 4000000000L, 2752 6000000000L)); 2753 }); 2754 } 2755 2756 @Test testInstallAttribution_selectHighestPriority()2757 public void testInstallAttribution_selectHighestPriority() { 2758 long currentTimestamp = System.currentTimeMillis(); 2759 2760 insertSource( 2761 createSourceForIATest( 2762 "IA1", currentTimestamp, 100, -1, false, DEFAULT_ENROLLMENT_ID) 2763 .setInstallCooldownWindow(COOLDOWN_WINDOW) 2764 .build(), 2765 "IA1"); 2766 insertSource( 2767 createSourceForIATest("IA2", currentTimestamp, 50, -1, false, DEFAULT_ENROLLMENT_ID) 2768 .setInstallCooldownWindow(COOLDOWN_WINDOW) 2769 .build(), 2770 "IA2"); 2771 // Should select id IA1 because it has higher priority 2772 assertTrue( 2773 mDatastoreManager.runInTransaction( 2774 measurementDao -> { 2775 measurementDao.doInstallAttribution( 2776 INSTALLED_PACKAGE, currentTimestamp); 2777 })); 2778 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 2779 assertTrue(getInstallAttributionStatus("IA1", db)); 2780 assertFalse(getInstallAttributionStatus("IA2", db)); 2781 removeSources(Arrays.asList("IA1", "IA2"), db); 2782 } 2783 2784 @Test testInstallAttribution_selectLatest()2785 public void testInstallAttribution_selectLatest() { 2786 long currentTimestamp = System.currentTimeMillis(); 2787 insertSource( 2788 createSourceForIATest("IA1", currentTimestamp, -1, 10, false, DEFAULT_ENROLLMENT_ID) 2789 .setInstallCooldownWindow(COOLDOWN_WINDOW) 2790 .build(), 2791 "IA1"); 2792 insertSource( 2793 createSourceForIATest("IA2", currentTimestamp, -1, 5, false, DEFAULT_ENROLLMENT_ID) 2794 .setInstallCooldownWindow(COOLDOWN_WINDOW) 2795 .build(), 2796 "IA2"); 2797 // Should select id=IA2 as it is latest 2798 assertTrue( 2799 mDatastoreManager.runInTransaction( 2800 measurementDao -> { 2801 measurementDao.doInstallAttribution( 2802 INSTALLED_PACKAGE, currentTimestamp); 2803 })); 2804 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 2805 assertFalse(getInstallAttributionStatus("IA1", db)); 2806 assertTrue(getInstallAttributionStatus("IA2", db)); 2807 2808 removeSources(Arrays.asList("IA1", "IA2"), db); 2809 } 2810 2811 @Test installAttribution_noCooldownWindow_ignoredToBeMarked()2812 public void installAttribution_noCooldownWindow_ignoredToBeMarked() { 2813 long currentTimestamp = System.currentTimeMillis(); 2814 insertSource( 2815 createSourceForIATest("IA1", currentTimestamp, -1, 10, false, DEFAULT_ENROLLMENT_ID) 2816 .setInstallCooldownWindow(0) 2817 .build(), 2818 "IA1"); 2819 // Should select id=IA2 as it is latest 2820 assertTrue( 2821 mDatastoreManager.runInTransaction( 2822 measurementDao -> { 2823 measurementDao.doInstallAttribution( 2824 INSTALLED_PACKAGE, currentTimestamp); 2825 })); 2826 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 2827 assertFalse(getInstallAttributionStatus("IA1", db)); 2828 2829 removeSources(Arrays.asList("IA1", "IA2"), db); 2830 } 2831 2832 @Test testInstallAttribution_ignoreNewerSources()2833 public void testInstallAttribution_ignoreNewerSources() { 2834 long currentTimestamp = System.currentTimeMillis(); 2835 insertSource( 2836 createSourceForIATest("IA1", currentTimestamp, -1, 10, false, DEFAULT_ENROLLMENT_ID) 2837 .setInstallCooldownWindow(COOLDOWN_WINDOW) 2838 .build(), 2839 "IA1"); 2840 insertSource( 2841 createSourceForIATest("IA2", currentTimestamp, -1, 5, false, DEFAULT_ENROLLMENT_ID) 2842 .setInstallCooldownWindow(COOLDOWN_WINDOW) 2843 .build(), 2844 "IA2"); 2845 // Should select id=IA1 as it is the only valid choice. 2846 // id=IA2 is newer than the evenTimestamp of install event. 2847 assertTrue( 2848 mDatastoreManager.runInTransaction( 2849 measurementDao -> { 2850 measurementDao.doInstallAttribution( 2851 INSTALLED_PACKAGE, currentTimestamp - DAYS.toMillis(7)); 2852 })); 2853 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 2854 assertTrue(getInstallAttributionStatus("IA1", db)); 2855 assertFalse(getInstallAttributionStatus("IA2", db)); 2856 removeSources(Arrays.asList("IA1", "IA2"), db); 2857 } 2858 2859 @Test testInstallAttribution_noValidSource()2860 public void testInstallAttribution_noValidSource() { 2861 long currentTimestamp = System.currentTimeMillis(); 2862 insertSource( 2863 createSourceForIATest("IA1", currentTimestamp, 10, 10, true, DEFAULT_ENROLLMENT_ID) 2864 .build(), 2865 "IA1"); 2866 insertSource( 2867 createSourceForIATest("IA2", currentTimestamp, 10, 11, true, DEFAULT_ENROLLMENT_ID) 2868 .build(), 2869 "IA2"); 2870 // Should not update any sources. 2871 assertTrue( 2872 mDatastoreManager.runInTransaction( 2873 measurementDao -> 2874 measurementDao.doInstallAttribution( 2875 INSTALLED_PACKAGE, currentTimestamp))); 2876 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 2877 assertFalse(getInstallAttributionStatus("IA1", db)); 2878 assertFalse(getInstallAttributionStatus("IA2", db)); 2879 removeSources(Arrays.asList("IA1", "IA2"), db); 2880 } 2881 2882 @Test installAttribution_install_installTimeEqualsEventTime()2883 public void installAttribution_install_installTimeEqualsEventTime() { 2884 long currentTimestamp = System.currentTimeMillis(); 2885 insertSource( 2886 createSourceForIATest("IA1", currentTimestamp, -1, 10, false, DEFAULT_ENROLLMENT_ID) 2887 .setInstallCooldownWindow(COOLDOWN_WINDOW) 2888 .build(), 2889 "IA1"); 2890 assertTrue( 2891 mDatastoreManager.runInTransaction( 2892 measurementDao -> { 2893 measurementDao.doInstallAttribution( 2894 INSTALLED_PACKAGE, currentTimestamp - DAYS.toMillis(7)); 2895 })); 2896 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 2897 assertEquals( 2898 currentTimestamp - DAYS.toMillis(7), 2899 getInstallAttributionInstallTime("IA1", db).longValue()); 2900 removeSources(Arrays.asList("IA1"), db); 2901 } 2902 2903 @Test 2904 public void testInstallAttribution_reinstallReattributionEnabled_skipsAttributionForReinstall()2905 testInstallAttribution_reinstallReattributionEnabled_skipsAttributionForReinstall() { 2906 mocker.mockGetFlags(mMockFlags); 2907 doReturn(true).when(mMockFlags).getMeasurementEnableReinstallReattribution(); 2908 long currentTimestamp = System.currentTimeMillis(); 2909 long reinstallWindow = DAYS.toMillis(50); 2910 insertSource( 2911 createSourceForIATest( 2912 /* id= */ "IA1", 2913 currentTimestamp, 2914 /* priority= */ -1, 2915 /* eventTimePastDays= */ 10, 2916 /* expiredIAWindow= */ false, 2917 DEFAULT_ENROLLMENT_ID) 2918 .setReinstallReattributionWindow(reinstallWindow) 2919 .setInstallCooldownWindow(COOLDOWN_WINDOW) 2920 .setInstallAttributed(true) 2921 .build(), 2922 /* sourceId= */ "IA1"); 2923 2924 insertSource( 2925 createSourceForIATest( 2926 /* id= */ "IA2", 2927 currentTimestamp, 2928 /* priority= */ -1, 2929 /* eventTimePastDays= */ 5, 2930 /* expiredIAWindow= */ false, 2931 DEFAULT_ENROLLMENT_ID) 2932 .setReinstallReattributionWindow(reinstallWindow) 2933 .setInstallCooldownWindow(COOLDOWN_WINDOW) 2934 .build(), 2935 /* sourceId= */ "IA2"); 2936 insertSource( 2937 createSourceForIATest( 2938 /* id= */ "IA3", 2939 currentTimestamp, 2940 /* priority= */ -1, 2941 /* eventTimePastDays= */ 10, 2942 /* expiredIAWindow= */ false, 2943 ENROLLMENT_ID1, 2944 REGISTRATION_ORIGIN_2) 2945 .setReinstallReattributionWindow(reinstallWindow) 2946 .setInstallCooldownWindow(COOLDOWN_WINDOW) 2947 .build(), 2948 /* sourceId= */ "IA3"); 2949 insertSource( 2950 createSourceForIATest( 2951 /* id= */ "IA4", 2952 currentTimestamp, 2953 /* priority= */ -1, 2954 /* eventTimePastDays= */ 5, 2955 /* expiredIAWindow= */ false, 2956 ENROLLMENT_ID1, 2957 REGISTRATION_ORIGIN_2) 2958 .setReinstallReattributionWindow(reinstallWindow) 2959 .setInstallCooldownWindow(COOLDOWN_WINDOW) 2960 .build(), 2961 /* sourceId= */ "IA4"); 2962 assertTrue( 2963 mDatastoreManager.runInTransaction( 2964 measurementDao -> { 2965 measurementDao.insertOrUpdateAppReportHistory( 2966 INSTALLED_PACKAGE, REGISTRATION_ORIGIN, currentTimestamp); 2967 })); 2968 2969 assertTrue( 2970 mDatastoreManager.runInTransaction( 2971 measurementDao -> { 2972 measurementDao.doInstallAttribution( 2973 INSTALLED_PACKAGE, currentTimestamp); 2974 })); 2975 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 2976 assertTrue(getInstallAttributionStatus(/* sourceDbId= */ "IA1", db)); 2977 assertFalse(getInstallAttributionStatus(/* sourceDbId= */ "IA2", db)); 2978 assertFalse(getInstallAttributionStatus(/* sourceDbId= */ "IA3", db)); 2979 assertTrue(getInstallAttributionStatus(/* sourceDbId= */ "IA4", db)); 2980 2981 removeSources(Arrays.asList("IA1", "IA2", "IA3", "IA4"), db); 2982 } 2983 2984 @Test testInstallAttribution_reinstallReattributionDisabled_doesNotSkipReinstall()2985 public void testInstallAttribution_reinstallReattributionDisabled_doesNotSkipReinstall() { 2986 mocker.mockGetFlags(mMockFlags); 2987 doReturn(false).when(mMockFlags).getMeasurementEnableReinstallReattribution(); 2988 long currentTimestamp = System.currentTimeMillis(); 2989 insertSource( 2990 createSourceForIATest( 2991 /* id= */ "IA1", 2992 currentTimestamp, 2993 /* priority= */ -1, 2994 /* eventTimePastDays= */ 10, 2995 /* expiredIAWindow= */ false, 2996 DEFAULT_ENROLLMENT_ID) 2997 .setInstallCooldownWindow(COOLDOWN_WINDOW) 2998 .setInstallAttributed(true) 2999 .build(), 3000 "IA1"); 3001 insertSource( 3002 createSourceForIATest( 3003 /* id= */ "IA2", 3004 currentTimestamp, 3005 /* priority= */ -1, 3006 /* eventTimePastDays= */ 5, 3007 /* expiredIAWindow= */ false, 3008 DEFAULT_ENROLLMENT_ID) 3009 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3010 .build(), 3011 "IA2"); 3012 insertSource( 3013 createSourceForIATest( 3014 /* id= */ "IA3", 3015 currentTimestamp, 3016 /* priority= */ -1, 3017 /* eventTimePastDays= */ 10, 3018 /* expiredIAWindow= */ false, 3019 ENROLLMENT_ID1, 3020 REGISTRATION_ORIGIN_2) 3021 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3022 .build(), 3023 "IA3"); 3024 insertSource( 3025 createSourceForIATest( 3026 /* id= */ "IA4", 3027 currentTimestamp, 3028 /* priority= */ -1, 3029 /* eventTimePastDays= */ 5, 3030 /* expiredIAWindow= */ false, 3031 ENROLLMENT_ID1, 3032 REGISTRATION_ORIGIN_2) 3033 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3034 .build(), 3035 "IA4"); 3036 assertTrue( 3037 mDatastoreManager.runInTransaction( 3038 measurementDao -> { 3039 measurementDao.insertOrUpdateAppReportHistory( 3040 INSTALLED_PACKAGE, REGISTRATION_ORIGIN, currentTimestamp); 3041 })); 3042 // Should select id=IA2 as it is latest 3043 assertTrue( 3044 mDatastoreManager.runInTransaction( 3045 measurementDao -> { 3046 measurementDao.doInstallAttribution( 3047 INSTALLED_PACKAGE, currentTimestamp); 3048 })); 3049 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 3050 assertTrue(getInstallAttributionStatus(/* sourceDbId= */ "IA1", db)); 3051 assertTrue(getInstallAttributionStatus(/* sourceDbId= */ "IA2", db)); 3052 assertFalse(getInstallAttributionStatus(/* sourceDbId= */ "IA3", db)); 3053 assertTrue(getInstallAttributionStatus(/* sourceDbId= */ "IA4", db)); 3054 3055 removeSources(Arrays.asList("IA1", "IA2", "IA3", "IA4"), db); 3056 } 3057 3058 @Test 3059 public void testInstallAttribution_reinstallReattributionEnabledNoWindow_doesNotSkipReinstall()3060 testInstallAttribution_reinstallReattributionEnabledNoWindow_doesNotSkipReinstall() { 3061 mocker.mockGetFlags(mMockFlags); 3062 doReturn(true).when(mMockFlags).getMeasurementEnableReinstallReattribution(); 3063 long currentTimestamp = System.currentTimeMillis(); 3064 insertSource( 3065 createSourceForIATest( 3066 /* id= */ "IA1", 3067 currentTimestamp, 3068 /* priority= */ -1, 3069 /* eventTimePastDays= */ 10, 3070 /* expiredIAWindow= */ false, 3071 DEFAULT_ENROLLMENT_ID) 3072 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3073 .setReinstallReattributionWindow(0L) 3074 .setInstallAttributed(true) 3075 .build(), 3076 "IA1"); 3077 insertSource( 3078 createSourceForIATest( 3079 /* id= */ "IA2", 3080 currentTimestamp, 3081 /* priority= */ -1, 3082 /* eventTimePastDays= */ 5, 3083 /* expiredIAWindow= */ false, 3084 DEFAULT_ENROLLMENT_ID) 3085 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3086 .setReinstallReattributionWindow(0L) 3087 .build(), 3088 "IA2"); 3089 insertSource( 3090 createSourceForIATest( 3091 /* id= */ "IA3", 3092 currentTimestamp, 3093 /* priority= */ -1, 3094 /* eventTimePastDays= */ 10, 3095 /* expiredIAWindow= */ false, 3096 ENROLLMENT_ID1, 3097 REGISTRATION_ORIGIN_2) 3098 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3099 .setReinstallReattributionWindow(0L) 3100 .build(), 3101 "IA3"); 3102 insertSource( 3103 createSourceForIATest( 3104 /* id= */ "IA4", 3105 currentTimestamp, 3106 /* priority= */ -1, 3107 /* eventTimePastDays= */ 5, 3108 /* expiredIAWindow= */ false, 3109 ENROLLMENT_ID1, 3110 REGISTRATION_ORIGIN_2) 3111 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3112 .setReinstallReattributionWindow(0L) 3113 .build(), 3114 "IA4"); 3115 assertTrue( 3116 mDatastoreManager.runInTransaction( 3117 measurementDao -> { 3118 measurementDao.insertOrUpdateAppReportHistory( 3119 INSTALLED_PACKAGE, REGISTRATION_ORIGIN, currentTimestamp); 3120 })); 3121 // Should select id=IA2 as it is latest 3122 assertTrue( 3123 mDatastoreManager.runInTransaction( 3124 measurementDao -> { 3125 measurementDao.doInstallAttribution( 3126 INSTALLED_PACKAGE, currentTimestamp); 3127 })); 3128 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 3129 assertTrue(getInstallAttributionStatus(/* sourceDbId= */ "IA1", db)); 3130 assertTrue(getInstallAttributionStatus(/* sourceDbId= */ "IA2", db)); 3131 assertFalse(getInstallAttributionStatus(/* sourceDbId= */ "IA3", db)); 3132 assertTrue(getInstallAttributionStatus(/* sourceDbId= */ "IA4", db)); 3133 3134 removeSources(Arrays.asList("IA1", "IA2", "IA3", "IA4"), db); 3135 } 3136 3137 @Test testInstallAttribution_reinstallReattributionEnabledNoReinstall_doesNotSkip()3138 public void testInstallAttribution_reinstallReattributionEnabledNoReinstall_doesNotSkip() { 3139 mocker.mockGetFlags(mMockFlags); 3140 doReturn(true).when(mMockFlags).getMeasurementEnableReinstallReattribution(); 3141 long currentTimestamp = System.currentTimeMillis(); 3142 insertSource( 3143 createSourceForIATest( 3144 /* id= */ "IA1", 3145 currentTimestamp, 3146 /* priority= */ -1, 3147 /* eventTimePastDays= */ 10, 3148 /* expiredIAWindow= */ false, 3149 DEFAULT_ENROLLMENT_ID) 3150 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3151 .setReinstallReattributionWindow(0L) 3152 .build(), 3153 "IA1"); 3154 insertSource( 3155 createSourceForIATest( 3156 /* id= */ "IA2", 3157 currentTimestamp, 3158 /* priority= */ -1, 3159 /* eventTimePastDays= */ 5, 3160 /* expiredIAWindow= */ false, 3161 DEFAULT_ENROLLMENT_ID) 3162 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3163 .setReinstallReattributionWindow(0L) 3164 .build(), 3165 "IA2"); 3166 3167 assertTrue( 3168 mDatastoreManager.runInTransaction( 3169 measurementDao -> { 3170 measurementDao.insertOrUpdateAppReportHistory( 3171 INSTALLED_PACKAGE, REGISTRATION_ORIGIN, currentTimestamp); 3172 })); 3173 // Should select id=IA2 as it is latest 3174 assertTrue( 3175 mDatastoreManager.runInTransaction( 3176 measurementDao -> { 3177 measurementDao.doInstallAttribution( 3178 INSTALLED_PACKAGE, currentTimestamp); 3179 })); 3180 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 3181 assertFalse(getInstallAttributionStatus(/* sourceDbId= */ "IA1", db)); 3182 assertTrue(getInstallAttributionStatus(/* sourceDbId= */ "IA2", db)); 3183 3184 removeSources(Arrays.asList("IA1", "IA2"), db); 3185 } 3186 3187 @Test doInstallAttribution_noValidSourceStatus_IgnoresSources()3188 public void doInstallAttribution_noValidSourceStatus_IgnoresSources() { 3189 long currentTimestamp = System.currentTimeMillis(); 3190 Source source = 3191 createSourceForIATest( 3192 "IA1", currentTimestamp, 100, -1, false, DEFAULT_ENROLLMENT_ID) 3193 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3194 .build(); 3195 3196 // Execution 3197 // Active source should get install attributed 3198 source.setStatus(Source.Status.ACTIVE); 3199 insertSource(source, source.getId()); 3200 assertTrue( 3201 mDatastoreManager.runInTransaction( 3202 measurementDao -> 3203 measurementDao.doInstallAttribution( 3204 INSTALLED_PACKAGE, currentTimestamp))); 3205 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 3206 assertTrue(getInstallAttributionStatus("IA1", db)); 3207 removeSources(Collections.singletonList("IA1"), db); 3208 3209 // Active source should not get install attributed 3210 source.setStatus(Source.Status.IGNORED); 3211 insertSource(source, source.getId()); 3212 assertTrue( 3213 mDatastoreManager.runInTransaction( 3214 measurementDao -> 3215 measurementDao.doInstallAttribution( 3216 INSTALLED_PACKAGE, currentTimestamp))); 3217 assertFalse(getInstallAttributionStatus("IA1", db)); 3218 removeSources(Collections.singletonList("IA1"), db); 3219 3220 // MARKED_TO_DELETE source should not get install attributed 3221 source.setStatus(Source.Status.MARKED_TO_DELETE); 3222 insertSource(source, source.getId()); 3223 assertTrue( 3224 mDatastoreManager.runInTransaction( 3225 measurementDao -> 3226 measurementDao.doInstallAttribution( 3227 INSTALLED_PACKAGE, currentTimestamp))); 3228 assertFalse(getInstallAttributionStatus("IA1", db)); 3229 removeSources(Collections.singletonList("IA1"), db); 3230 } 3231 3232 @Test 3233 public void doInstallAttribution_withSourcesAcrossEnrollments_marksOneInstallFromEachRegOrigin()3234 doInstallAttribution_withSourcesAcrossEnrollments_marksOneInstallFromEachRegOrigin() { 3235 long currentTimestamp = System.currentTimeMillis(); 3236 3237 // Enrollment1: Choose IA2 because that's newer and still occurred before install 3238 insertSource( 3239 createSourceForIATest( 3240 "IA1", 3241 currentTimestamp, 3242 -1, 3243 10, 3244 false, 3245 DEFAULT_ENROLLMENT_ID + "_1") 3246 .setRegistrationOrigin(WebUtil.validUri("https://subdomain.example1.test")) 3247 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3248 .build(), 3249 "IA1"); 3250 insertSource( 3251 createSourceForIATest( 3252 "IA2", currentTimestamp, -1, 9, false, DEFAULT_ENROLLMENT_ID + "_1") 3253 .setRegistrationOrigin(WebUtil.validUri("https://subdomain.example1.test")) 3254 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3255 .build(), 3256 "IA2"); 3257 3258 // Enrollment2: Choose IA4 because IA3's install attribution window has expired 3259 insertSource( 3260 createSourceForIATest( 3261 "IA3", currentTimestamp, -1, 10, true, DEFAULT_ENROLLMENT_ID + "_2") 3262 .setRegistrationOrigin(WebUtil.validUri("https://subdomain.example2.test")) 3263 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3264 .build(), 3265 "IA3"); 3266 insertSource( 3267 createSourceForIATest( 3268 "IA4", currentTimestamp, -1, 9, false, DEFAULT_ENROLLMENT_ID + "_2") 3269 .setRegistrationOrigin(WebUtil.validUri("https://subdomain.example2.test")) 3270 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3271 .build(), 3272 "IA4"); 3273 3274 // Enrollment3: Choose IA5 because IA6 was registered after install event 3275 insertSource( 3276 createSourceForIATest( 3277 "IA5", 3278 currentTimestamp, 3279 -1, 3280 10, 3281 false, 3282 DEFAULT_ENROLLMENT_ID + "_3") 3283 .setRegistrationOrigin(WebUtil.validUri("https://subdomain.example3.test")) 3284 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3285 .build(), 3286 "IA5"); 3287 insertSource( 3288 createSourceForIATest( 3289 "IA6", currentTimestamp, -1, 5, false, DEFAULT_ENROLLMENT_ID + "_3") 3290 .setRegistrationOrigin(WebUtil.validUri("https://subdomain.example3.test")) 3291 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3292 .build(), 3293 "IA6"); 3294 3295 // Enrollment4: Choose IA8 due to higher priority 3296 insertSource( 3297 createSourceForIATest( 3298 "IA7", currentTimestamp, 5, 10, false, DEFAULT_ENROLLMENT_ID + "_4") 3299 .setRegistrationOrigin(WebUtil.validUri("https://subdomain.example4.test")) 3300 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3301 .build(), 3302 "IA7"); 3303 insertSource( 3304 createSourceForIATest( 3305 "IA8", 3306 currentTimestamp, 3307 10, 3308 10, 3309 false, 3310 DEFAULT_ENROLLMENT_ID + "_4") 3311 .setRegistrationOrigin(WebUtil.validUri("https://subdomain.example4.test")) 3312 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3313 .build(), 3314 "IA8"); 3315 3316 // Enrollment5: Choose none because both sources are ineligible 3317 // Expired install attribution window 3318 insertSource( 3319 createSourceForIATest( 3320 "IA9", currentTimestamp, 5, 31, true, DEFAULT_ENROLLMENT_ID + "_5") 3321 .setRegistrationOrigin(WebUtil.validUri("https://subdomain.example5.test")) 3322 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3323 .build(), 3324 "IA9"); 3325 // Registered after install attribution 3326 insertSource( 3327 createSourceForIATest( 3328 "IA10", 3329 currentTimestamp, 3330 10, 3331 3, 3332 false, 3333 DEFAULT_ENROLLMENT_ID + "_5") 3334 .setRegistrationOrigin(WebUtil.validUri("https://subdomain.example5.test")) 3335 .setInstallCooldownWindow(COOLDOWN_WINDOW) 3336 .build(), 3337 "IA10"); 3338 3339 assertTrue( 3340 mDatastoreManager.runInTransaction( 3341 measurementDao -> { 3342 measurementDao.doInstallAttribution( 3343 INSTALLED_PACKAGE, currentTimestamp - DAYS.toMillis(7)); 3344 })); 3345 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 3346 assertTrue(getInstallAttributionStatus("IA2", db)); 3347 assertTrue(getInstallAttributionStatus("IA4", db)); 3348 assertTrue(getInstallAttributionStatus("IA5", db)); 3349 assertTrue(getInstallAttributionStatus("IA8", db)); 3350 3351 assertFalse(getInstallAttributionStatus("IA1", db)); 3352 assertFalse(getInstallAttributionStatus("IA3", db)); 3353 assertFalse(getInstallAttributionStatus("IA6", db)); 3354 assertFalse(getInstallAttributionStatus("IA7", db)); 3355 assertFalse(getInstallAttributionStatus("IA9", db)); 3356 assertFalse(getInstallAttributionStatus("IA10", db)); 3357 3358 removeSources( 3359 Arrays.asList( 3360 "IA1", "IA2", "IA3", "IA4", "IA5", "IA6", "IA7", "IA8", "IA8", "IA10"), 3361 db); 3362 } 3363 3364 @Test testInsertCountUniqueReport_forValidEvent_isSuccess()3365 public void testInsertCountUniqueReport_forValidEvent_isSuccess() { 3366 String reportId = "reportId"; 3367 String payload = "payload"; 3368 Uri reportingOrigin = Uri.parse("https://test.foo"); 3369 int status = CountUniqueReport.ReportDeliveryStatus.PENDING; 3370 int debugStatus = CountUniqueReport.ReportDeliveryStatus.PENDING; 3371 Long scheduledReportTime = 1726874188124L; 3372 String version = "0.1"; 3373 String debugKey = "asadsadsa="; 3374 String contextId = "testContextId"; 3375 String enrollmentId = "test-id"; 3376 int contributionValue = 5; 3377 long contributionTime = 1726874188232L; 3378 3379 CountUniqueReport report = 3380 createCountUniqueReport( 3381 reportId, 3382 payload, 3383 reportingOrigin, 3384 status, 3385 debugStatus, 3386 scheduledReportTime, 3387 version, 3388 debugKey, 3389 contextId, 3390 enrollmentId, 3391 contributionValue, 3392 contributionTime); 3393 3394 boolean result = 3395 mDatastoreManager.runInTransaction( 3396 (dao) -> { 3397 dao.insertCountUniqueReport(report); 3398 }); 3399 assertThat(result).isTrue(); 3400 try (Cursor cursor = 3401 MeasurementDbHelper.getInstance() 3402 .getReadableDatabase() 3403 .query( 3404 MeasurementTables.CountUniqueReportingContract.TABLE, 3405 null, 3406 null, 3407 null, 3408 null, 3409 null, 3410 null)) { 3411 3412 assertThat(cursor.getCount()).isEqualTo(1); 3413 List<CountUniqueReport> reports = new ArrayList<>(); 3414 3415 while (cursor.moveToNext()) { 3416 CountUniqueReport reportFromDb = 3417 SqliteObjectMapper.constructCountUniqueReport(cursor); 3418 reports.add(reportFromDb); 3419 } 3420 3421 for (CountUniqueReport r : reports) { 3422 assertThat(r.getReportId()).isEqualTo(reportId); 3423 assertThat(r.getPayload()).isEqualTo(payload); 3424 assertThat(r.getReportingOrigin()).isEqualTo(reportingOrigin); 3425 assertThat(r.getStatus()).isEqualTo(status); 3426 assertThat(r.getDebugReportStatus()).isEqualTo(debugStatus); 3427 assertThat(r.getScheduledReportTime()).isEqualTo(scheduledReportTime); 3428 assertThat(r.getApiVersion()).isEqualTo(version); 3429 assertThat(r.getDebugKey()).isEqualTo(debugKey); 3430 assertThat(r.getContextId()).isEqualTo(contextId); 3431 assertThat(r.getEnrollmentId()).isEqualTo(enrollmentId); 3432 assertThat(r.getContributionValue()).isEqualTo(contributionValue); 3433 assertThat(r.getContributionTime()).isEqualTo(contributionTime); 3434 } 3435 } 3436 } 3437 3438 @Test testInsertCountUniqueMetadata_setIgnoreIfPresentTrue_ignoresIfPresent()3439 public void testInsertCountUniqueMetadata_setIgnoreIfPresentTrue_ignoresIfPresent() { 3440 String key = "key1"; 3441 Integer value1 = 1; 3442 Integer value2 = 2; 3443 Long expirationTime = 1726874188124L; 3444 Uri reportingOrigin = Uri.parse("https://test.foo"); 3445 3446 CountUniqueMetadata metadata = 3447 new CountUniqueMetadata.Builder() 3448 .setKey(key) 3449 .setValue(value1) 3450 .setExpirationTime(expirationTime) 3451 .setReportingOrigin(reportingOrigin) 3452 .build(); 3453 CountUniqueMetadata metadata2 = 3454 new CountUniqueMetadata.Builder() 3455 .setKey(key) 3456 .setValue(value2) 3457 .setExpirationTime(expirationTime) 3458 .setReportingOrigin(reportingOrigin) 3459 .build(); 3460 3461 boolean result = 3462 mDatastoreManager.runInTransaction( 3463 (dao) -> { 3464 dao.insertCountUniqueMetadata(metadata, true); 3465 }); 3466 assertThat(result).isTrue(); 3467 try (Cursor cursor = 3468 MeasurementDbHelper.getInstance() 3469 .getReadableDatabase() 3470 .query( 3471 MeasurementTables.CountUniqueMetadataContract.TABLE, 3472 null, 3473 null, 3474 null, 3475 null, 3476 null, 3477 null)) { 3478 3479 assertThat(cursor.getCount()).isEqualTo(1); 3480 cursor.moveToNext(); 3481 CountUniqueMetadata metadataFromDb = 3482 SqliteObjectMapper.constructCountUniqueMetadata(cursor); 3483 assertThat(metadataFromDb.getKey()).isEqualTo(key); 3484 assertThat(metadataFromDb.getValue()).isEqualTo(value1); 3485 assertThat(metadataFromDb.getReportingOrigin()).isEqualTo(reportingOrigin); 3486 assertThat(metadataFromDb.getExpirationTime()).isEqualTo(expirationTime); 3487 } 3488 3489 result = 3490 mDatastoreManager.runInTransaction( 3491 (dao) -> { 3492 dao.insertCountUniqueMetadata(metadata2, true); 3493 }); 3494 assertThat(result).isTrue(); 3495 try (Cursor cursor = 3496 MeasurementDbHelper.getInstance() 3497 .getReadableDatabase() 3498 .query( 3499 MeasurementTables.CountUniqueMetadataContract.TABLE, 3500 null, 3501 null, 3502 null, 3503 null, 3504 null, 3505 null)) { 3506 3507 assertThat(cursor.getCount()).isEqualTo(1); 3508 cursor.moveToNext(); 3509 CountUniqueMetadata metadataFromDb = 3510 SqliteObjectMapper.constructCountUniqueMetadata(cursor); 3511 assertThat(metadataFromDb.getKey()).isEqualTo(key); 3512 assertThat(metadataFromDb.getValue()).isEqualTo(value1); // should not be value2 3513 assertThat(metadataFromDb.getReportingOrigin()).isEqualTo(reportingOrigin); 3514 assertThat(metadataFromDb.getExpirationTime()).isEqualTo(expirationTime); 3515 } 3516 } 3517 3518 @Test testInsertCountUniqueMetadata_setIgnoreIfPresentFalse_updatesRecord()3519 public void testInsertCountUniqueMetadata_setIgnoreIfPresentFalse_updatesRecord() { 3520 String key = "key1"; 3521 Integer value1 = 1; 3522 Integer value2 = 2; 3523 Long expirationTime = 1726874188124L; 3524 Uri reportingOrigin = Uri.parse("https://test.foo"); 3525 3526 CountUniqueMetadata metadata1 = 3527 new CountUniqueMetadata.Builder() 3528 .setKey(key) 3529 .setValue(value1) 3530 .setExpirationTime(expirationTime) 3531 .setReportingOrigin(reportingOrigin) 3532 .build(); 3533 3534 CountUniqueMetadata metadata2 = 3535 new CountUniqueMetadata.Builder() 3536 .setKey(key) 3537 .setValue(value2) 3538 .setExpirationTime(expirationTime) 3539 .setReportingOrigin(reportingOrigin) 3540 .build(); 3541 3542 boolean result = 3543 mDatastoreManager.runInTransaction( 3544 (dao) -> { 3545 dao.insertCountUniqueMetadata(metadata1, false); 3546 }); 3547 assertThat(result).isTrue(); 3548 try (Cursor cursor = 3549 MeasurementDbHelper.getInstance() 3550 .getReadableDatabase() 3551 .query( 3552 MeasurementTables.CountUniqueMetadataContract.TABLE, 3553 null, 3554 null, 3555 null, 3556 null, 3557 null, 3558 null)) { 3559 3560 assertThat(cursor.getCount()).isEqualTo(1); 3561 cursor.moveToNext(); 3562 CountUniqueMetadata metadataFromDb = 3563 SqliteObjectMapper.constructCountUniqueMetadata(cursor); 3564 assertThat(metadataFromDb.getKey()).isEqualTo(key); 3565 assertThat(metadataFromDb.getValue()).isEqualTo(value1); 3566 assertThat(metadataFromDb.getReportingOrigin()).isEqualTo(reportingOrigin); 3567 assertThat(metadataFromDb.getExpirationTime()).isEqualTo(expirationTime); 3568 } 3569 3570 result = 3571 mDatastoreManager.runInTransaction( 3572 (dao) -> { 3573 dao.insertCountUniqueMetadata(metadata2, false); 3574 }); 3575 assertThat(result).isTrue(); 3576 try (Cursor cursor = 3577 MeasurementDbHelper.getInstance() 3578 .getReadableDatabase() 3579 .query( 3580 MeasurementTables.CountUniqueMetadataContract.TABLE, 3581 null, 3582 null, 3583 null, 3584 null, 3585 null, 3586 null)) { 3587 3588 assertThat(cursor.getCount()).isEqualTo(1); 3589 cursor.moveToNext(); 3590 CountUniqueMetadata metadataFromDb = 3591 SqliteObjectMapper.constructCountUniqueMetadata(cursor); 3592 assertThat(metadataFromDb.getKey()).isEqualTo(key); 3593 assertThat(metadataFromDb.getValue()).isEqualTo(value2); // record updated to value2 3594 assertThat(metadataFromDb.getReportingOrigin()).isEqualTo(reportingOrigin); 3595 assertThat(metadataFromDb.getExpirationTime()).isEqualTo(expirationTime); 3596 } 3597 } 3598 3599 @Test testDeleteCountUniqueMetadata_forKeyAndOrigin_deletesRecord()3600 public void testDeleteCountUniqueMetadata_forKeyAndOrigin_deletesRecord() { 3601 String key1 = "key1"; 3602 String key2 = "key2"; 3603 Integer value1 = 1; 3604 Integer value2 = 2; 3605 Long expirationTime = 1726874188124L; 3606 Uri reportingOrigin = Uri.parse("https://test.foo"); 3607 3608 CountUniqueMetadata metadata1 = 3609 new CountUniqueMetadata.Builder() 3610 .setKey(key1) 3611 .setValue(value1) 3612 .setExpirationTime(expirationTime) 3613 .setReportingOrigin(reportingOrigin) 3614 .build(); 3615 CountUniqueMetadata metadata2 = 3616 new CountUniqueMetadata.Builder() 3617 .setKey(key2) 3618 .setValue(value2) 3619 .setExpirationTime(expirationTime) 3620 .setReportingOrigin(reportingOrigin) 3621 .build(); 3622 3623 boolean result = 3624 mDatastoreManager.runInTransaction( 3625 (dao) -> { 3626 dao.insertCountUniqueMetadata(metadata1, false); 3627 dao.insertCountUniqueMetadata(metadata2, false); 3628 }); 3629 assertThat(result).isTrue(); 3630 try (Cursor cursor = 3631 MeasurementDbHelper.getInstance() 3632 .getReadableDatabase() 3633 .query( 3634 MeasurementTables.CountUniqueMetadataContract.TABLE, 3635 null, 3636 null, 3637 null, 3638 null, 3639 null, 3640 null)) { 3641 assertThat(cursor.getCount()).isEqualTo(2); 3642 } 3643 3644 result = 3645 mDatastoreManager.runInTransaction( 3646 (dao) -> { 3647 dao.deleteCountUniqueMetadata(key1, reportingOrigin); 3648 }); 3649 assertThat(result).isTrue(); 3650 try (Cursor cursor = 3651 MeasurementDbHelper.getInstance() 3652 .getReadableDatabase() 3653 .query( 3654 MeasurementTables.CountUniqueMetadataContract.TABLE, 3655 null, 3656 null, 3657 null, 3658 null, 3659 null, 3660 null)) { 3661 assertThat(cursor.getCount()).isEqualTo(1); 3662 } 3663 3664 // verify if deletion for key that does not exist fails silently 3665 result = 3666 mDatastoreManager.runInTransaction( 3667 (dao) -> { 3668 dao.deleteCountUniqueMetadata(key1, reportingOrigin); 3669 }); 3670 assertThat(result).isTrue(); 3671 try (Cursor cursor = 3672 MeasurementDbHelper.getInstance() 3673 .getReadableDatabase() 3674 .query( 3675 MeasurementTables.CountUniqueMetadataContract.TABLE, 3676 null, 3677 null, 3678 null, 3679 null, 3680 null, 3681 null)) { 3682 assertThat(cursor.getCount()).isEqualTo(1); 3683 } 3684 } 3685 3686 @Test testGetCountUniqueMetadata_IfPresent_returnsMetadata()3687 public void testGetCountUniqueMetadata_IfPresent_returnsMetadata() { 3688 String key = "key1"; 3689 Integer value1 = 1; 3690 Long expirationTime = 1726874188124L; 3691 Uri reportingOrigin = Uri.parse("https://test.foo"); 3692 3693 CountUniqueMetadata m = 3694 new CountUniqueMetadata.Builder() 3695 .setKey(key) 3696 .setValue(value1) 3697 .setExpirationTime(expirationTime) 3698 .setReportingOrigin(reportingOrigin) 3699 .build(); 3700 3701 mDatastoreManager.runInTransaction( 3702 (dao) -> { 3703 dao.insertCountUniqueMetadata(m, true); 3704 CountUniqueMetadata metadata = dao.getCountUniqueMetadata(key, reportingOrigin); 3705 assertThat(metadata.getKey()).isEqualTo(key); 3706 assertThat(metadata.getValue()).isEqualTo(value1); 3707 assertThat(metadata.getReportingOrigin()).isEqualTo(reportingOrigin); 3708 assertThat(metadata.getExpirationTime()).isEqualTo(expirationTime); 3709 }); 3710 } 3711 3712 @Test testGetCountUniqueMetadata_IfAbsent_throwsException()3713 public void testGetCountUniqueMetadata_IfAbsent_throwsException() { 3714 String key = "key1"; 3715 String key2 = "key2"; 3716 Integer value1 = 1; 3717 Long expirationTime = 1726874188124L; 3718 Uri reportingOrigin = Uri.parse("https://test.foo"); 3719 3720 CountUniqueMetadata m = 3721 new CountUniqueMetadata.Builder() 3722 .setKey(key) 3723 .setValue(value1) 3724 .setExpirationTime(expirationTime) 3725 .setReportingOrigin(reportingOrigin) 3726 .build(); 3727 3728 mDatastoreManager.runInTransaction( 3729 (dao) -> { 3730 dao.insertCountUniqueMetadata(m, true); 3731 assertThrows( 3732 DatastoreException.class, 3733 () -> dao.getCountUniqueMetadata(key2, reportingOrigin)); 3734 }); 3735 } 3736 3737 @Test deleteFlexEventReportsAndAttributions_success()3738 public void deleteFlexEventReportsAndAttributions_success() throws Exception { 3739 // Setup - Creates the following - 3740 // source - S1, S2 3741 // trigger - T1, T2, T3 3742 // event reports - (E13, E23) (E11_1, E11_2, E11_3) (E21) (E22_1, E22_2) 3743 // attributions 3744 // (ATT11_00 (aggregate scope), ATT11_01 (aggregate scope)) 3745 // (ATT11_1, ATT11_2, ATT11_3) (ATT21) (ATT22_1, ATT22_2) 3746 prepareDataForFlexEventReportAndAttributionDeletion(); 3747 3748 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 3749 3750 // Assert attributions present 3751 assertNotNull(getAttribution("ATT11_1", db)); 3752 assertNotNull(getAttribution("ATT11_2", db)); 3753 assertNotNull(getAttribution("ATT11_3", db)); 3754 assertNotNull(getAttribution("ATT21", db)); 3755 assertNotNull(getAttribution("ATT22_1", db)); 3756 assertNotNull(getAttribution("ATT22_2", db)); 3757 3758 mDatastoreManager.runInTransaction( 3759 measurementDao -> { 3760 // Assert sources and triggers present 3761 assertNotNull(measurementDao.getSource("S1")); 3762 assertNotNull(measurementDao.getSource("S2")); 3763 assertNotNull(measurementDao.getTrigger("T1")); 3764 assertNotNull(measurementDao.getTrigger("T2")); 3765 3766 // Validate presence of unmatched event reports 3767 measurementDao.getEventReport("E13"); 3768 measurementDao.getEventReport("E23"); 3769 3770 // Event report group 1 3771 // Validate event reports present 3772 EventReport e111 = measurementDao.getEventReport("E11_1"); 3773 EventReport e112 = measurementDao.getEventReport("E11_2"); 3774 3775 // Deletion 3776 measurementDao.deleteFlexEventReportsAndAttributions( 3777 List.of(e111, e112)); 3778 3779 // Validate event report deletion 3780 assertThrows( 3781 DatastoreException.class, 3782 () -> { 3783 measurementDao.getEventReport("E11_1"); 3784 }); 3785 assertThrows( 3786 DatastoreException.class, 3787 () -> { 3788 measurementDao.getEventReport("E11_2"); 3789 }); 3790 assertNotNull(measurementDao.getEventReport("E11_3")); 3791 3792 // Event report group 2 3793 // Validate event reports present 3794 EventReport e21 = measurementDao.getEventReport("E21"); 3795 3796 // Deletion 3797 measurementDao.deleteFlexEventReportsAndAttributions( 3798 List.of(e21)); 3799 3800 // Validate event report deletion 3801 assertThrows( 3802 DatastoreException.class, 3803 () -> { 3804 measurementDao.getEventReport("E21"); 3805 }); 3806 3807 // Event report group 3 3808 // Validate event reports present (retrieval doesn't throw) 3809 measurementDao.getEventReport("E22_1"); 3810 EventReport e222 = measurementDao.getEventReport("E22_2"); 3811 3812 // Deletion 3813 measurementDao.deleteFlexEventReportsAndAttributions( 3814 List.of(e222)); 3815 3816 // Validate event report deletion 3817 assertThrows( 3818 DatastoreException.class, 3819 () -> { 3820 measurementDao.getEventReport("E22_2"); 3821 }); 3822 assertNotNull(measurementDao.getEventReport("E22_1")); 3823 3824 // Validate sources and triggers present 3825 assertNotNull(measurementDao.getSource("S1")); 3826 assertNotNull(measurementDao.getSource("S2")); 3827 assertNotNull(measurementDao.getTrigger("T1")); 3828 assertNotNull(measurementDao.getTrigger("T2")); 3829 3830 // Validate presence of unmatched event reports 3831 measurementDao.getEventReport("E13"); 3832 measurementDao.getEventReport("E23"); 3833 }); 3834 3835 // Validate attribution deletion 3836 assertNotNull(getAttribution("ATT11_00", db)); 3837 assertNotNull(getAttribution("ATT11_01", db)); 3838 assertNull(getAttribution("ATT11_1", db)); 3839 assertNull(getAttribution("ATT11_2", db)); 3840 assertNotNull(getAttribution("ATT11_3", db)); 3841 assertNull(getAttribution("ATT21", db)); 3842 // Attribution deletion order within the group associated with an event report is generally 3843 // by insertion order, although it's not guaranteed. We deleted event report E22_2 but the 3844 // first limited associated attribution returned is ATT22_1. 3845 assertNull(getAttribution("ATT22_1", db)); 3846 assertNotNull(getAttribution("ATT22_2", db)); 3847 } 3848 3849 @Test deleteSources_providedIds_deletesMatchingSourcesAndRelatedData()3850 public void deleteSources_providedIds_deletesMatchingSourcesAndRelatedData() throws Exception { 3851 // Setup - Creates the following - 3852 // source - S1, S2, S3, S4 3853 // trigger - T1, T2, T3, T4 3854 // event reports - E11, E12, E21, E22, E23, E33, E44 3855 // aggregate reports - AR11, AR12, AR21, AR34 3856 // attributions - ATT11, ATT12, ATT21, ATT22, ATT33, ATT44 3857 prepareDataForSourceAndTriggerDeletion(); 3858 3859 // Execution 3860 mDatastoreManager.runInTransaction( 3861 measurementDao -> { 3862 measurementDao.deleteSources(List.of("S1", "S2")); 3863 3864 assertThrows( 3865 DatastoreException.class, 3866 () -> { 3867 measurementDao.getSource("S1"); 3868 }); 3869 assertThrows( 3870 DatastoreException.class, 3871 () -> { 3872 measurementDao.getSource("S2"); 3873 }); 3874 3875 assertNotNull(measurementDao.getSource("S3")); 3876 assertNotNull(measurementDao.getSource("S4")); 3877 assertNotNull(measurementDao.getTrigger("T1")); 3878 assertNotNull(measurementDao.getTrigger("T2")); 3879 assertNotNull(measurementDao.getTrigger("T3")); 3880 assertNotNull(measurementDao.getTrigger("T4")); 3881 3882 assertThrows( 3883 DatastoreException.class, 3884 () -> { 3885 measurementDao.getEventReport("E11"); 3886 }); 3887 assertThrows( 3888 DatastoreException.class, 3889 () -> { 3890 measurementDao.getEventReport("E12"); 3891 }); 3892 assertThrows( 3893 DatastoreException.class, 3894 () -> { 3895 measurementDao.getEventReport("E21"); 3896 }); 3897 assertThrows( 3898 DatastoreException.class, 3899 () -> { 3900 measurementDao.getEventReport("E22"); 3901 }); 3902 assertThrows( 3903 DatastoreException.class, 3904 () -> { 3905 measurementDao.getEventReport("E23"); 3906 }); 3907 assertNotNull(measurementDao.getEventReport("E33")); 3908 assertNotNull(measurementDao.getEventReport("E44")); 3909 3910 assertThrows( 3911 DatastoreException.class, 3912 () -> { 3913 measurementDao.getAggregateReport("AR11"); 3914 }); 3915 assertThrows( 3916 DatastoreException.class, 3917 () -> { 3918 measurementDao.getAggregateReport("AR12"); 3919 }); 3920 assertThrows( 3921 DatastoreException.class, 3922 () -> { 3923 measurementDao.getAggregateReport("AR21"); 3924 }); 3925 assertNotNull(measurementDao.getAggregateReport("AR34")); 3926 }); 3927 3928 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 3929 assertEquals(2, DatabaseUtils.queryNumEntries(db, AttributionContract.TABLE)); 3930 } 3931 3932 @Test deleteSource_providedId_deletesMatchingXnaIgnoredSource()3933 public void deleteSource_providedId_deletesMatchingXnaIgnoredSource() { 3934 // Setup 3935 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 3936 3937 Source s1 = 3938 SourceFixture.getMinimalValidSourceBuilder() 3939 .setEventId(new UnsignedLong(1L)) 3940 .setId("S1") 3941 .setEnrollmentId("1") 3942 .build(); 3943 3944 ContentValues sourceValues = new ContentValues(); 3945 sourceValues.put(SourceContract.ID, s1.getId()); 3946 sourceValues.put(SourceContract.EVENT_ID, s1.getEventId().getValue()); 3947 3948 ContentValues xnaIgnoredSourceValues = new ContentValues(); 3949 xnaIgnoredSourceValues.put(XnaIgnoredSourcesContract.SOURCE_ID, s1.getId()); 3950 xnaIgnoredSourceValues.put(XnaIgnoredSourcesContract.ENROLLMENT_ID, s1.getEnrollmentId()); 3951 3952 // Execution 3953 db.insert(SourceContract.TABLE, null, sourceValues); 3954 db.insert(XnaIgnoredSourcesContract.TABLE, null, xnaIgnoredSourceValues); 3955 3956 // Assertion 3957 assertEquals(1, DatabaseUtils.queryNumEntries(db, SourceContract.TABLE)); 3958 assertEquals(1, DatabaseUtils.queryNumEntries(db, XnaIgnoredSourcesContract.TABLE)); 3959 3960 // Execution 3961 removeSources(Collections.singletonList(s1.getId()), db); 3962 3963 // Assertion 3964 assertEquals(0, DatabaseUtils.queryNumEntries(db, SourceContract.TABLE)); 3965 assertEquals(0, DatabaseUtils.queryNumEntries(db, XnaIgnoredSourcesContract.TABLE)); 3966 } 3967 3968 @Test deleteTriggers_providedIds_deletesMatchingTriggersAndRelatedData()3969 public void deleteTriggers_providedIds_deletesMatchingTriggersAndRelatedData() 3970 throws Exception { 3971 // Setup - Creates the following - 3972 // source - S1, S2, S3, S4 3973 // trigger - T1, T2, T3, T4 3974 // event reports - E11, E12, E21, E22, E23, E33, E44 3975 // aggregate reports - AR11, AR12, AR21, AR34 3976 // attributions - ATT11, ATT12, ATT21, ATT22, ATT33, ATT44 3977 prepareDataForSourceAndTriggerDeletion(); 3978 3979 // Execution 3980 mDatastoreManager.runInTransaction( 3981 measurementDao -> { 3982 measurementDao.deleteTriggers(List.of("T1", "T2")); 3983 3984 assertNotNull(measurementDao.getSource("S1")); 3985 assertNotNull(measurementDao.getSource("S2")); 3986 assertNotNull(measurementDao.getSource("S3")); 3987 assertNotNull(measurementDao.getSource("S4")); 3988 assertThrows(DatastoreException.class, () -> measurementDao.getTrigger("T1")); 3989 assertThrows(DatastoreException.class, () -> measurementDao.getTrigger("T2")); 3990 assertNotNull(measurementDao.getTrigger("T3")); 3991 assertNotNull(measurementDao.getTrigger("T4")); 3992 3993 assertThrows( 3994 DatastoreException.class, () -> measurementDao.getEventReport("E11")); 3995 assertThrows( 3996 DatastoreException.class, () -> measurementDao.getEventReport("E12")); 3997 assertThrows( 3998 DatastoreException.class, () -> measurementDao.getEventReport("E21")); 3999 assertThrows( 4000 DatastoreException.class, () -> measurementDao.getEventReport("E22")); 4001 assertNotNull(measurementDao.getEventReport("E23")); 4002 assertNotNull(measurementDao.getEventReport("E33")); 4003 assertNotNull(measurementDao.getEventReport("E44")); 4004 4005 assertThrows( 4006 DatastoreException.class, 4007 () -> measurementDao.getAggregateReport("AR11")); 4008 assertThrows( 4009 DatastoreException.class, 4010 () -> measurementDao.getAggregateReport("AR12")); 4011 assertThrows( 4012 DatastoreException.class, 4013 () -> measurementDao.getAggregateReport("AR21")); 4014 4015 assertNotNull(measurementDao.getAggregateReport("AR34")); 4016 }); 4017 4018 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 4019 assertEquals(2, DatabaseUtils.queryNumEntries(db, AttributionContract.TABLE)); 4020 } 4021 4022 // Setup - Creates the following - 4023 // source - S1, S2 4024 // trigger - T1, T2 4025 // event reports - E11_1, E11_2, E11_3, E21, E22_1, E22_2 4026 // attributions - ATT11_1, ATT11_2, ATT11_3, ATT21, ATT22_1, ATT22_2 prepareDataForFlexEventReportAndAttributionDeletion()4027 private void prepareDataForFlexEventReportAndAttributionDeletion() throws Exception { 4028 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 4029 Source s1 = 4030 SourceFixture.getMinimalValidSourceBuilder() 4031 .setEventId(new UnsignedLong(1L)) 4032 .setId("S1") 4033 .build(); 4034 Source s2 = 4035 SourceFixture.getMinimalValidSourceBuilder() 4036 .setEventId(new UnsignedLong(2L)) 4037 .setId("S2") 4038 .build(); 4039 Trigger t1 = 4040 TriggerFixture.getValidTriggerBuilder() 4041 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 4042 .setId("T1") 4043 .build(); 4044 Trigger t2 = 4045 TriggerFixture.getValidTriggerBuilder() 4046 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 4047 .setId("T2") 4048 .build(); 4049 Trigger t3 = 4050 TriggerFixture.getValidTriggerBuilder() 4051 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 4052 .setId("T2") 4053 .build(); 4054 EventReport e111 = createEventReportForSourceAndTrigger("E11_1", s1, t1); 4055 EventReport e112 = createEventReportForSourceAndTrigger("E11_2", s1, t1); 4056 EventReport e113 = createEventReportForSourceAndTrigger("E11_3", s1, t1); 4057 EventReport e21 = createEventReportForSourceAndTrigger("E21", s2, t1); 4058 EventReport e221 = createEventReportForSourceAndTrigger("E22_1", s2, t2); 4059 EventReport e222 = createEventReportForSourceAndTrigger("E22_2", s2, t2); 4060 EventReport e13 = createEventReportForSourceAndTrigger("E13", s1, t3); 4061 EventReport e23 = createEventReportForSourceAndTrigger("E23", s2, t3); 4062 Attribution att1100 = 4063 createAttribution( 4064 "ATT11_00", 4065 Attribution.Scope.AGGREGATE, 4066 s1.getId(), 4067 t1.getId()); 4068 Attribution att1101 = 4069 createAttribution( 4070 "ATT11_01", 4071 Attribution.Scope.AGGREGATE, 4072 s1.getId(), 4073 t1.getId()); 4074 Attribution att111 = 4075 createAttribution( 4076 "ATT11_1", s1.getId(), t1.getId()); 4077 Attribution att112 = 4078 createAttribution( 4079 "ATT11_2", s1.getId(), t1.getId()); 4080 Attribution att113 = 4081 createAttribution( 4082 "ATT11_3", s1.getId(), t1.getId()); 4083 Attribution att21 = 4084 createAttribution( 4085 "ATT21", s2.getId(), t1.getId()); 4086 Attribution att221 = 4087 createAttribution( 4088 "ATT22_1", s2.getId(), t2.getId()); 4089 Attribution att222 = 4090 createAttribution( 4091 "ATT22_2", s2.getId(), t2.getId()); 4092 4093 insertSource(s1, s1.getId()); 4094 insertSource(s2, s2.getId()); 4095 AbstractDbIntegrationTest.insertToDb(t1, db); 4096 AbstractDbIntegrationTest.insertToDb(t2, db); 4097 AbstractDbIntegrationTest.insertToDb(e111, db); 4098 AbstractDbIntegrationTest.insertToDb(e112, db); 4099 AbstractDbIntegrationTest.insertToDb(e113, db); 4100 AbstractDbIntegrationTest.insertToDb(e21, db); 4101 AbstractDbIntegrationTest.insertToDb(e221, db); 4102 AbstractDbIntegrationTest.insertToDb(e222, db); 4103 AbstractDbIntegrationTest.insertToDb(e13, db); 4104 AbstractDbIntegrationTest.insertToDb(e23, db); 4105 AbstractDbIntegrationTest.insertToDb(att1100, db); 4106 AbstractDbIntegrationTest.insertToDb(att1101, db); 4107 AbstractDbIntegrationTest.insertToDb(att111, db); 4108 AbstractDbIntegrationTest.insertToDb(att112, db); 4109 AbstractDbIntegrationTest.insertToDb(att113, db); 4110 AbstractDbIntegrationTest.insertToDb(att21, db); 4111 AbstractDbIntegrationTest.insertToDb(att221, db); 4112 AbstractDbIntegrationTest.insertToDb(att222, db); 4113 } 4114 4115 // Setup - Creates the following - 4116 // source - S1, S2, S3, S4 4117 // trigger - T1, T2, T3, T4 4118 // event reports - E11, E12, E21, E22, E23, E33, E44 4119 // aggregate reports - AR11, AR12, AR21, AR34 4120 // attributions - ATT11, ATT12, ATT21, ATT22, ATT33, ATT44 prepareDataForSourceAndTriggerDeletion()4121 private void prepareDataForSourceAndTriggerDeletion() throws Exception { 4122 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 4123 Source s1 = 4124 SourceFixture.getMinimalValidSourceBuilder() 4125 .setEventId(new UnsignedLong(1L)) 4126 .setId("S1") 4127 .build(); // deleted 4128 Source s2 = 4129 SourceFixture.getMinimalValidSourceBuilder() 4130 .setEventId(new UnsignedLong(2L)) 4131 .setId("S2") 4132 .build(); // deleted 4133 Source s3 = 4134 SourceFixture.getMinimalValidSourceBuilder() 4135 .setEventId(new UnsignedLong(3L)) 4136 .setId("S3") 4137 .build(); 4138 Source s4 = 4139 SourceFixture.getMinimalValidSourceBuilder() 4140 .setEventId(new UnsignedLong(4L)) 4141 .setId("S4") 4142 .build(); 4143 Trigger t1 = 4144 TriggerFixture.getValidTriggerBuilder() 4145 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 4146 .setId("T1") 4147 .build(); 4148 Trigger t2 = 4149 TriggerFixture.getValidTriggerBuilder() 4150 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 4151 .setId("T2") 4152 .build(); 4153 Trigger t3 = 4154 TriggerFixture.getValidTriggerBuilder() 4155 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 4156 .setId("T3") 4157 .build(); 4158 Trigger t4 = 4159 TriggerFixture.getValidTriggerBuilder() 4160 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 4161 .setId("T4") 4162 .build(); 4163 EventReport e11 = createEventReportForSourceAndTrigger("E11", s1, t1); 4164 EventReport e12 = createEventReportForSourceAndTrigger("E12", s1, t2); 4165 EventReport e21 = createEventReportForSourceAndTrigger("E21", s2, t1); 4166 EventReport e22 = createEventReportForSourceAndTrigger("E22", s2, t2); 4167 EventReport e23 = createEventReportForSourceAndTrigger("E23", s2, t3); 4168 EventReport e33 = createEventReportForSourceAndTrigger("E33", s3, t3); 4169 EventReport e44 = createEventReportForSourceAndTrigger("E44", s4, t4); 4170 AggregateReport ar11 = createAggregateReportForSourceAndTrigger("AR11", s1, t1); 4171 AggregateReport ar12 = createAggregateReportForSourceAndTrigger("AR12", s1, t2); 4172 AggregateReport ar21 = createAggregateReportForSourceAndTrigger("AR21", s2, t1); 4173 AggregateReport ar34 = createAggregateReportForSourceAndTrigger("AR34", s3, t4); 4174 Attribution att11 = 4175 createAttribution( 4176 "ATT11", s1.getId(), t1.getId()); // deleted 4177 Attribution att12 = 4178 createAttribution( 4179 "ATT12", s1.getId(), t2.getId()); // deleted 4180 Attribution att21 = 4181 createAttribution( 4182 "ATT21", s2.getId(), t1.getId()); // deleted 4183 Attribution att22 = 4184 createAttribution( 4185 "ATT22", s2.getId(), t2.getId()); // deleted 4186 Attribution att33 = 4187 createAttribution("ATT33", s3.getId(), t3.getId()); 4188 Attribution att44 = 4189 createAttribution("ATT44", s4.getId(), t4.getId()); 4190 4191 insertSource(s1, s1.getId()); 4192 insertSource(s2, s2.getId()); 4193 insertSource(s3, s3.getId()); 4194 insertSource(s4, s4.getId()); 4195 4196 AbstractDbIntegrationTest.insertToDb(t1, db); 4197 AbstractDbIntegrationTest.insertToDb(t2, db); 4198 AbstractDbIntegrationTest.insertToDb(t3, db); 4199 AbstractDbIntegrationTest.insertToDb(t4, db); 4200 4201 AbstractDbIntegrationTest.insertToDb(e11, db); 4202 AbstractDbIntegrationTest.insertToDb(e12, db); 4203 AbstractDbIntegrationTest.insertToDb(e21, db); 4204 AbstractDbIntegrationTest.insertToDb(e22, db); 4205 AbstractDbIntegrationTest.insertToDb(e23, db); 4206 AbstractDbIntegrationTest.insertToDb(e33, db); 4207 AbstractDbIntegrationTest.insertToDb(e44, db); 4208 4209 AbstractDbIntegrationTest.insertToDb(ar11, db); 4210 AbstractDbIntegrationTest.insertToDb(ar12, db); 4211 AbstractDbIntegrationTest.insertToDb(ar21, db); 4212 AbstractDbIntegrationTest.insertToDb(ar34, db); 4213 4214 AbstractDbIntegrationTest.insertToDb(att11, db); 4215 AbstractDbIntegrationTest.insertToDb(att12, db); 4216 AbstractDbIntegrationTest.insertToDb(att21, db); 4217 AbstractDbIntegrationTest.insertToDb(att22, db); 4218 AbstractDbIntegrationTest.insertToDb(att33, db); 4219 AbstractDbIntegrationTest.insertToDb(att44, db); 4220 } 4221 4222 @Test testUndoInstallAttribution_noMarkedSource()4223 public void testUndoInstallAttribution_noMarkedSource() { 4224 long currentTimestamp = System.currentTimeMillis(); 4225 Source source = 4226 createSourceForIATest("IA1", currentTimestamp, 10, 10, false, DEFAULT_ENROLLMENT_ID) 4227 .build(); 4228 source.setInstallAttributed(true); 4229 insertSource(source, source.getId()); 4230 assertTrue( 4231 mDatastoreManager.runInTransaction( 4232 measurementDao -> 4233 measurementDao.undoInstallAttribution(INSTALLED_PACKAGE))); 4234 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 4235 // Should set installAttributed = false for id=IA1 4236 assertFalse(getInstallAttributionStatus("IA1", db)); 4237 } 4238 4239 @Test undoInstallAttribution_uninstall_nullInstallTime()4240 public void undoInstallAttribution_uninstall_nullInstallTime() { 4241 long currentTimestamp = System.currentTimeMillis(); 4242 Source source = 4243 createSourceForIATest("IA1", currentTimestamp, 10, 10, false, DEFAULT_ENROLLMENT_ID) 4244 .build(); 4245 source.setInstallAttributed(true); 4246 insertSource(source, source.getId()); 4247 assertTrue( 4248 mDatastoreManager.runInTransaction( 4249 measurementDao -> 4250 measurementDao.undoInstallAttribution(INSTALLED_PACKAGE))); 4251 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 4252 // Should set installTime = null for id=IA1 4253 assertNull(getInstallAttributionInstallTime("IA1", db)); 4254 } 4255 4256 @Test getSourceDestinations_returnsExpected()4257 public void getSourceDestinations_returnsExpected() { 4258 // Insert two sources with some intersection of destinations 4259 // and assert all destination queries work. 4260 4261 // First source 4262 List<Uri> webDestinations1 = 4263 List.of( 4264 Uri.parse("https://first-place.test"), 4265 Uri.parse("https://second-place.test"), 4266 Uri.parse("https://third-place.test")); 4267 List<Uri> appDestinations1 = List.of(Uri.parse("android-app://test.first-place")); 4268 Source source1 = 4269 SourceFixture.getMinimalValidSourceBuilder() 4270 .setId("1") 4271 .setAppDestinations(appDestinations1) 4272 .setWebDestinations(webDestinations1) 4273 .build(); 4274 insertSource(source1, source1.getId()); 4275 4276 // Second source 4277 List<Uri> webDestinations2 = 4278 List.of( 4279 Uri.parse("https://not-first-place.test"), 4280 Uri.parse("https://not-second-place.test"), 4281 Uri.parse("https://third-place.test")); 4282 List<Uri> appDestinations2 = List.of(Uri.parse("android-app://test.not-first-place")); 4283 Source source2 = 4284 SourceFixture.getMinimalValidSourceBuilder() 4285 .setId("2") 4286 .setAppDestinations(appDestinations2) 4287 .setWebDestinations(webDestinations2) 4288 .build(); 4289 insertSource(source2, source2.getId()); 4290 4291 Pair<List<Uri>, List<Uri>> result1 = 4292 mDatastoreManager 4293 .runInTransactionWithResult( 4294 measurementDao -> 4295 measurementDao.getSourceDestinations(source1.getId())) 4296 .get(); 4297 // Assert first app destinations 4298 assertTrue( 4299 ImmutableMultiset.copyOf(source1.getAppDestinations()) 4300 .equals(ImmutableMultiset.copyOf(result1.first))); 4301 // Assert first web destinations 4302 assertTrue( 4303 ImmutableMultiset.copyOf(source1.getWebDestinations()) 4304 .equals(ImmutableMultiset.copyOf(result1.second))); 4305 4306 Pair<List<Uri>, List<Uri>> result2 = 4307 mDatastoreManager 4308 .runInTransactionWithResult( 4309 measurementDao -> 4310 measurementDao.getSourceDestinations(source2.getId())) 4311 .get(); 4312 // Assert second app destinations 4313 assertTrue( 4314 ImmutableMultiset.copyOf(source2.getAppDestinations()) 4315 .equals(ImmutableMultiset.copyOf(result2.first))); 4316 // Assert second web destinations 4317 assertTrue( 4318 ImmutableMultiset.copyOf(source2.getWebDestinations()) 4319 .equals(ImmutableMultiset.copyOf(result2.second))); 4320 } 4321 4322 @Test countNumAggregateReportsPerSource_returnsExpected()4323 public void countNumAggregateReportsPerSource_returnsExpected() { 4324 List<Source> sources = 4325 Arrays.asList( 4326 SourceFixture.getMinimalValidSourceBuilder() 4327 .setEventId(new UnsignedLong(1L)) 4328 .setId("source1") 4329 .build(), 4330 SourceFixture.getMinimalValidSourceBuilder() 4331 .setEventId(new UnsignedLong(2L)) 4332 .setId("source2") 4333 .build(), 4334 SourceFixture.getMinimalValidSourceBuilder() 4335 .setEventId(new UnsignedLong(3L)) 4336 .setId("source3") 4337 .build()); 4338 List<AggregateReport> reports = 4339 Arrays.asList( 4340 generateMockAggregateReport( 4341 WebUtil.validUrl("https://destination-1.test"), 4342 1, 4343 "source1", 4344 AggregateReportFixture.ValidAggregateReportParams.API), 4345 generateMockAggregateReport( 4346 WebUtil.validUrl("https://destination-1.test"), 4347 2, 4348 "source1", 4349 AggregateReportFixture.ValidAggregateReportParams.API), 4350 generateMockAggregateReport( 4351 WebUtil.validUrl("https://destination-2.test"), 4352 3, 4353 "source2", 4354 AggregateReportFixture.ValidAggregateReportParams.API), 4355 generateMockAggregateReportBuilder( 4356 WebUtil.validUrl("https://destination-2.test"), 4357 33, 4358 "source2", 4359 AggregateReportFixture.ValidAggregateReportParams.API) 4360 .setTriggerContextId("12345") 4361 .build(), 4362 generateMockAggregateReport( 4363 WebUtil.validUrl("https://destination-1.test"), 4364 4, 4365 "source3", 4366 AggregateDebugReportApi.AGGREGATE_DEBUG_REPORT_API), 4367 generateMockAggregateReport( 4368 WebUtil.validUrl("https://destination-1.test"), 4369 5, 4370 "source3", 4371 AggregateDebugReportApi.AGGREGATE_DEBUG_REPORT_API), 4372 generateMockAggregateReport( 4373 WebUtil.validUrl("https://destination-2.test"), 4374 6, 4375 "source3", 4376 AggregateDebugReportApi.AGGREGATE_DEBUG_REPORT_API)); 4377 4378 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 4379 Objects.requireNonNull(db); 4380 sources.forEach(source -> insertSource(source, source.getId())); 4381 Consumer<AggregateReport> aggregateReportConsumer = 4382 aggregateReport -> { 4383 ContentValues values = new ContentValues(); 4384 values.put(MeasurementTables.AggregateReport.ID, aggregateReport.getId()); 4385 values.put( 4386 MeasurementTables.AggregateReport.SOURCE_ID, 4387 aggregateReport.getSourceId()); 4388 values.put( 4389 MeasurementTables.AggregateReport.ATTRIBUTION_DESTINATION, 4390 aggregateReport.getAttributionDestination().toString()); 4391 values.put(MeasurementTables.AggregateReport.API, aggregateReport.getApi()); 4392 values.put( 4393 MeasurementTables.AggregateReport.TRIGGER_CONTEXT_ID, 4394 aggregateReport.getTriggerContextId()); 4395 db.insert(MeasurementTables.AggregateReport.TABLE, null, values); 4396 }; 4397 reports.forEach(aggregateReportConsumer); 4398 4399 mDatastoreManager.runInTransaction( 4400 measurementDao -> { 4401 assertThat( 4402 measurementDao.countNumAggregateReportsPerSource( 4403 "source1", 4404 AggregateReportFixture.ValidAggregateReportParams.API)) 4405 .isEqualTo(2); 4406 assertThat( 4407 measurementDao.countNumAggregateReportsPerSource( 4408 "source2", 4409 AggregateReportFixture.ValidAggregateReportParams.API)) 4410 .isEqualTo(2); 4411 assertThat( 4412 measurementDao.countNumAggregateReportsPerSource( 4413 "source3", 4414 AggregateReportFixture.ValidAggregateReportParams.API)) 4415 .isEqualTo(0); 4416 assertThat( 4417 measurementDao.countNumAggregateReportsPerSource( 4418 "source1", 4419 AggregateDebugReportApi.AGGREGATE_DEBUG_REPORT_API)) 4420 .isEqualTo(0); 4421 assertThat( 4422 measurementDao.countNumAggregateReportsPerSource( 4423 "source2", 4424 AggregateDebugReportApi.AGGREGATE_DEBUG_REPORT_API)) 4425 .isEqualTo(0); 4426 assertThat( 4427 measurementDao.countNumAggregateReportsPerSource( 4428 "source3", 4429 AggregateDebugReportApi.AGGREGATE_DEBUG_REPORT_API)) 4430 .isEqualTo(3); 4431 }); 4432 } 4433 4434 @Test countNumAggregateReportsPerSource_unboundedReportsWithTriggerContextId_expected()4435 public void countNumAggregateReportsPerSource_unboundedReportsWithTriggerContextId_expected() { 4436 mLegacyFlags = mMockFlags; 4437 mocker.mockGetFlags(mMockFlags); 4438 doReturn(true).when(mMockFlags).getMeasurementEnableUnboundedReportsWithTriggerContextId(); 4439 4440 List<Source> sources = 4441 Arrays.asList( 4442 SourceFixture.getMinimalValidSourceBuilder() 4443 .setEventId(new UnsignedLong(1L)) 4444 .setId("source1") 4445 .build(), 4446 SourceFixture.getMinimalValidSourceBuilder() 4447 .setEventId(new UnsignedLong(2L)) 4448 .setId("source2") 4449 .build()); 4450 List<AggregateReport> reports = 4451 Arrays.asList( 4452 generateMockAggregateReport( 4453 WebUtil.validUrl("https://destination-1.test"), 4454 1, 4455 "source1", 4456 AggregateReportFixture.ValidAggregateReportParams.API), 4457 generateMockAggregateReport( 4458 WebUtil.validUrl("https://destination-1.test"), 4459 2, 4460 "source1", 4461 AggregateReportFixture.ValidAggregateReportParams.API), 4462 generateMockAggregateReport( 4463 WebUtil.validUrl("https://destination-2.test"), 4464 3, 4465 "source2", 4466 AggregateReportFixture.ValidAggregateReportParams.API), 4467 // Report should not be counted since trigger context ID is present. 4468 generateMockAggregateReportBuilder( 4469 WebUtil.validUrl("https://destination-2.test"), 4470 33, 4471 "source2", 4472 AggregateReportFixture.ValidAggregateReportParams.API) 4473 .setTriggerContextId("12345") 4474 .build()); 4475 4476 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 4477 Objects.requireNonNull(db); 4478 sources.forEach(source -> insertSource(source, source.getId())); 4479 Consumer<AggregateReport> aggregateReportConsumer = 4480 aggregateReport -> { 4481 ContentValues values = new ContentValues(); 4482 values.put(MeasurementTables.AggregateReport.ID, aggregateReport.getId()); 4483 values.put( 4484 MeasurementTables.AggregateReport.SOURCE_ID, 4485 aggregateReport.getSourceId()); 4486 values.put( 4487 MeasurementTables.AggregateReport.ATTRIBUTION_DESTINATION, 4488 aggregateReport.getAttributionDestination().toString()); 4489 values.put(MeasurementTables.AggregateReport.API, aggregateReport.getApi()); 4490 values.put( 4491 MeasurementTables.AggregateReport.TRIGGER_CONTEXT_ID, 4492 aggregateReport.getTriggerContextId()); 4493 db.insert(MeasurementTables.AggregateReport.TABLE, null, values); 4494 }; 4495 reports.forEach(aggregateReportConsumer); 4496 4497 mDatastoreManager.runInTransaction( 4498 measurementDao -> { 4499 assertThat( 4500 measurementDao.countNumAggregateReportsPerSource( 4501 "source1", 4502 AggregateReportFixture.ValidAggregateReportParams.API)) 4503 .isEqualTo(2); 4504 assertThat( 4505 measurementDao.countNumAggregateReportsPerSource( 4506 "source2", 4507 AggregateReportFixture.ValidAggregateReportParams.API)) 4508 .isEqualTo(1); 4509 assertThat( 4510 measurementDao.countNumAggregateReportsPerSource( 4511 "source1", 4512 AggregateDebugReportApi.AGGREGATE_DEBUG_REPORT_API)) 4513 .isEqualTo(0); 4514 assertThat( 4515 measurementDao.countNumAggregateReportsPerSource( 4516 "source2", 4517 AggregateDebugReportApi.AGGREGATE_DEBUG_REPORT_API)) 4518 .isEqualTo(0); 4519 }); 4520 } 4521 4522 @Test getNumAggregateReportsPerDestination_returnsExpected()4523 public void getNumAggregateReportsPerDestination_returnsExpected() { 4524 List<AggregateReport> reportsWithPlainDestination = 4525 Arrays.asList( 4526 generateMockAggregateReport( 4527 WebUtil.validUrl("https://destination-1.test"), 1)); 4528 List<AggregateReport> reportsWithPlainAndSubDomainDestination = 4529 Arrays.asList( 4530 generateMockAggregateReport( 4531 WebUtil.validUrl("https://destination-2.test"), 2), 4532 generateMockAggregateReport( 4533 WebUtil.validUrl("https://subdomain.destination-2.test"), 3)); 4534 List<AggregateReport> reportsWithPlainAndPathDestination = 4535 Arrays.asList( 4536 generateMockAggregateReport( 4537 WebUtil.validUrl("https://subdomain.destination-3.test"), 4), 4538 generateMockAggregateReport( 4539 WebUtil.validUrl("https://subdomain.destination-3.test/abcd"), 5)); 4540 List<AggregateReport> reportsWithAll3Types = 4541 Arrays.asList( 4542 generateMockAggregateReport( 4543 WebUtil.validUrl("https://destination-4.test"), 6), 4544 generateMockAggregateReport( 4545 WebUtil.validUrl("https://subdomain.destination-4.test"), 7), 4546 generateMockAggregateReport( 4547 WebUtil.validUrl("https://subdomain.destination-4.test/abcd"), 8)); 4548 List<AggregateReport> reportsWithAndroidAppDestination = 4549 Arrays.asList(generateMockAggregateReport("android-app://destination-5.app", 9)); 4550 4551 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 4552 Objects.requireNonNull(db); 4553 Stream.of( 4554 reportsWithPlainDestination, 4555 reportsWithPlainAndSubDomainDestination, 4556 reportsWithPlainAndPathDestination, 4557 reportsWithAll3Types, 4558 reportsWithAndroidAppDestination) 4559 .flatMap(Collection::stream) 4560 .forEach( 4561 aggregateReport -> { 4562 ContentValues values = new ContentValues(); 4563 values.put( 4564 MeasurementTables.AggregateReport.ID, aggregateReport.getId()); 4565 values.put( 4566 MeasurementTables.AggregateReport.ATTRIBUTION_DESTINATION, 4567 aggregateReport.getAttributionDestination().toString()); 4568 values.put( 4569 MeasurementTables.AggregateReport.IS_FAKE_REPORT, 4570 aggregateReport.isFakeReport()); 4571 db.insert(MeasurementTables.AggregateReport.TABLE, null, values); 4572 }); 4573 4574 List<String> attributionDestinations1 = createWebDestinationVariants(1); 4575 List<String> attributionDestinations2 = createWebDestinationVariants(2); 4576 List<String> attributionDestinations3 = createWebDestinationVariants(3); 4577 List<String> attributionDestinations4 = createWebDestinationVariants(4); 4578 List<String> attributionDestinations5 = createAppDestinationVariants(5); 4579 4580 // expected query return values for attribution destination variants 4581 List<Integer> destination1ExpectedCounts = Arrays.asList(1, 1, 1, 1, 0); 4582 List<Integer> destination2ExpectedCounts = Arrays.asList(2, 2, 2, 2, 0); 4583 List<Integer> destination3ExpectedCounts = Arrays.asList(2, 2, 2, 2, 0); 4584 List<Integer> destination4ExpectedCounts = Arrays.asList(3, 3, 3, 3, 0); 4585 List<Integer> destination5ExpectedCounts = Arrays.asList(0, 0, 1, 1, 0); 4586 assertAggregateReportCount( 4587 attributionDestinations1, EventSurfaceType.WEB, destination1ExpectedCounts); 4588 assertAggregateReportCount( 4589 attributionDestinations2, EventSurfaceType.WEB, destination2ExpectedCounts); 4590 assertAggregateReportCount( 4591 attributionDestinations3, EventSurfaceType.WEB, destination3ExpectedCounts); 4592 assertAggregateReportCount( 4593 attributionDestinations4, EventSurfaceType.WEB, destination4ExpectedCounts); 4594 assertAggregateReportCount( 4595 attributionDestinations5, EventSurfaceType.APP, destination5ExpectedCounts); 4596 } 4597 4598 @Test getAggregateReportById_fakeReport()4599 public void getAggregateReportById_fakeReport() { 4600 AggregateReport ar11 = 4601 AggregateReportFixture.getValidAggregateReportBuilder() 4602 .setId("11") 4603 .setIsFakeReport(true) 4604 .build(); 4605 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 4606 AbstractDbIntegrationTest.insertToDb(ar11, db); 4607 4608 Optional<AggregateReport> resOpt = 4609 mDatastoreManager.runInTransactionWithResult((dao) -> dao.getAggregateReport("11")); 4610 assertTrue(resOpt.isPresent()); 4611 AggregateReport res = resOpt.get(); 4612 assertTrue(res.isFakeReport()); 4613 } 4614 4615 @Test getNumEventReportsPerDestination_returnsExpected()4616 public void getNumEventReportsPerDestination_returnsExpected() { 4617 List<EventReport> reportsWithPlainDestination = 4618 Arrays.asList( 4619 generateMockEventReport(WebUtil.validUrl("https://destination-1.test"), 1)); 4620 List<EventReport> reportsWithPlainAndSubDomainDestination = 4621 Arrays.asList( 4622 generateMockEventReport(WebUtil.validUrl("https://destination-2.test"), 2), 4623 generateMockEventReport( 4624 WebUtil.validUrl("https://subdomain.destination-2.test"), 3)); 4625 List<EventReport> reportsWithPlainAndPathDestination = 4626 Arrays.asList( 4627 generateMockEventReport( 4628 WebUtil.validUrl("https://subdomain.destination-3.test"), 4), 4629 generateMockEventReport( 4630 WebUtil.validUrl("https://subdomain.destination-3.test/abcd"), 5)); 4631 List<EventReport> reportsWithAll3Types = 4632 Arrays.asList( 4633 generateMockEventReport(WebUtil.validUrl("https://destination-4.test"), 6), 4634 generateMockEventReport( 4635 WebUtil.validUrl("https://subdomain.destination-4.test"), 7), 4636 generateMockEventReport( 4637 WebUtil.validUrl("https://subdomain.destination-4.test/abcd"), 8)); 4638 List<EventReport> reportsWithAndroidAppDestination = 4639 Arrays.asList(generateMockEventReport("android-app://destination-5.app", 9)); 4640 4641 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 4642 Objects.requireNonNull(db); 4643 Stream.of( 4644 reportsWithPlainDestination, 4645 reportsWithPlainAndSubDomainDestination, 4646 reportsWithPlainAndPathDestination, 4647 reportsWithAll3Types, 4648 reportsWithAndroidAppDestination) 4649 .flatMap(Collection::stream) 4650 .forEach( 4651 eventReport -> { 4652 ContentValues values = new ContentValues(); 4653 values.put(EventReportContract.ID, eventReport.getId()); 4654 values.put( 4655 EventReportContract.ATTRIBUTION_DESTINATION, 4656 eventReport.getAttributionDestinations().get(0).toString()); 4657 db.insert(EventReportContract.TABLE, null, values); 4658 }); 4659 4660 List<String> attributionDestinations1 = createWebDestinationVariants(1); 4661 List<String> attributionDestinations2 = createWebDestinationVariants(2); 4662 List<String> attributionDestinations3 = createWebDestinationVariants(3); 4663 List<String> attributionDestinations4 = createWebDestinationVariants(4); 4664 List<String> attributionDestinations5 = createAppDestinationVariants(5); 4665 4666 // expected query return values for attribution destination variants 4667 List<Integer> destination1ExpectedCounts = Arrays.asList(1, 1, 1, 1, 0); 4668 List<Integer> destination2ExpectedCounts = Arrays.asList(2, 2, 2, 2, 0); 4669 List<Integer> destination3ExpectedCounts = Arrays.asList(2, 2, 2, 2, 0); 4670 List<Integer> destination4ExpectedCounts = Arrays.asList(3, 3, 3, 3, 0); 4671 List<Integer> destination5ExpectedCounts = Arrays.asList(0, 0, 1, 1, 0); 4672 assertEventReportCount( 4673 attributionDestinations1, EventSurfaceType.WEB, destination1ExpectedCounts); 4674 assertEventReportCount( 4675 attributionDestinations2, EventSurfaceType.WEB, destination2ExpectedCounts); 4676 assertEventReportCount( 4677 attributionDestinations3, EventSurfaceType.WEB, destination3ExpectedCounts); 4678 assertEventReportCount( 4679 attributionDestinations4, EventSurfaceType.WEB, destination4ExpectedCounts); 4680 assertEventReportCount( 4681 attributionDestinations5, EventSurfaceType.APP, destination5ExpectedCounts); 4682 } 4683 4684 @Test testGetSourceEventReports()4685 public void testGetSourceEventReports() { 4686 List<Source> sourceList = 4687 Arrays.asList( 4688 SourceFixture.getMinimalValidSourceBuilder() 4689 .setId("1") 4690 .setEventId(new UnsignedLong(3L)) 4691 .setEnrollmentId("1") 4692 .build(), 4693 SourceFixture.getMinimalValidSourceBuilder() 4694 .setId("2") 4695 .setEventId(new UnsignedLong(4L)) 4696 .setEnrollmentId("1") 4697 .build(), 4698 // Should always be ignored 4699 SourceFixture.getMinimalValidSourceBuilder() 4700 .setId("3") 4701 .setEventId(new UnsignedLong(4L)) 4702 .setEnrollmentId("2") 4703 .build(), 4704 SourceFixture.getMinimalValidSourceBuilder() 4705 .setId("15") 4706 .setEventId(new UnsignedLong(15L)) 4707 .setEnrollmentId("2") 4708 .build(), 4709 SourceFixture.getMinimalValidSourceBuilder() 4710 .setId("16") 4711 .setEventId(new UnsignedLong(16L)) 4712 .setEnrollmentId("2") 4713 .build(), 4714 SourceFixture.getMinimalValidSourceBuilder() 4715 .setId("20") 4716 .setEventId(new UnsignedLong(20L)) 4717 .setEnrollmentId("2") 4718 .build()); 4719 4720 List<Trigger> triggers = 4721 Arrays.asList( 4722 TriggerFixture.getValidTriggerBuilder() 4723 .setId("101") 4724 .setEnrollmentId("2") 4725 .build(), 4726 TriggerFixture.getValidTriggerBuilder() 4727 .setId("102") 4728 .setEnrollmentId("2") 4729 .build(), 4730 TriggerFixture.getValidTriggerBuilder() 4731 .setId("201") 4732 .setEnrollmentId("2") 4733 .build(), 4734 TriggerFixture.getValidTriggerBuilder() 4735 .setId("202") 4736 .setEnrollmentId("2") 4737 .build(), 4738 TriggerFixture.getValidTriggerBuilder() 4739 .setId("1001") 4740 .setEnrollmentId("2") 4741 .build()); 4742 4743 // Should match with source 1 4744 List<EventReport> reportList1 = new ArrayList<>(); 4745 reportList1.add( 4746 new EventReport.Builder() 4747 .setId("1") 4748 .setSourceEventId(new UnsignedLong(3L)) 4749 .setEnrollmentId("1") 4750 .setAttributionDestinations(sourceList.get(0).getAppDestinations()) 4751 .setSourceType(sourceList.get(0).getSourceType()) 4752 .setSourceId("1") 4753 .setTriggerId("101") 4754 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4755 .build()); 4756 reportList1.add( 4757 new EventReport.Builder() 4758 .setId("7") 4759 .setSourceEventId(new UnsignedLong(3L)) 4760 .setEnrollmentId("1") 4761 .setAttributionDestinations(List.of(APP_DESTINATION)) 4762 .setSourceType(sourceList.get(0).getSourceType()) 4763 .setSourceId("1") 4764 .setTriggerId("102") 4765 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4766 .build()); 4767 4768 // Should match with source 2 4769 List<EventReport> reportList2 = new ArrayList<>(); 4770 reportList2.add( 4771 new EventReport.Builder() 4772 .setId("3") 4773 .setSourceEventId(new UnsignedLong(4L)) 4774 .setEnrollmentId("1") 4775 .setAttributionDestinations(sourceList.get(1).getAppDestinations()) 4776 .setSourceType(sourceList.get(1).getSourceType()) 4777 .setSourceId("2") 4778 .setTriggerId("201") 4779 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4780 .build()); 4781 reportList2.add( 4782 new EventReport.Builder() 4783 .setId("8") 4784 .setSourceEventId(new UnsignedLong(4L)) 4785 .setEnrollmentId("1") 4786 .setAttributionDestinations(sourceList.get(1).getAppDestinations()) 4787 .setSourceType(sourceList.get(1).getSourceType()) 4788 .setSourceId("2") 4789 .setTriggerId("202") 4790 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4791 .build()); 4792 4793 List<EventReport> reportList3 = new ArrayList<>(); 4794 // Should not match with any source 4795 reportList3.add( 4796 new EventReport.Builder() 4797 .setId("2") 4798 .setSourceEventId(new UnsignedLong(5L)) 4799 .setEnrollmentId("1") 4800 .setSourceType(Source.SourceType.EVENT) 4801 .setAttributionDestinations(List.of(APP_DESTINATION)) 4802 .setSourceId("15") 4803 .setTriggerId("1001") 4804 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4805 .build()); 4806 reportList3.add( 4807 new EventReport.Builder() 4808 .setId("4") 4809 .setSourceEventId(new UnsignedLong(6L)) 4810 .setEnrollmentId("1") 4811 .setSourceType(Source.SourceType.EVENT) 4812 .setAttributionDestinations(List.of(APP_DESTINATION)) 4813 .setSourceId("16") 4814 .setTriggerId("1001") 4815 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4816 .setTriggerValue(100L) 4817 .build()); 4818 reportList3.add( 4819 new EventReport.Builder() 4820 .setId("5") 4821 .setSourceEventId(new UnsignedLong(1L)) 4822 .setEnrollmentId("1") 4823 .setSourceType(Source.SourceType.EVENT) 4824 .setAttributionDestinations(List.of(APP_DESTINATION)) 4825 .setSourceId("15") 4826 .setTriggerId("1001") 4827 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4828 .setTriggerValue(120L) 4829 .build()); 4830 reportList3.add( 4831 new EventReport.Builder() 4832 .setId("6") 4833 .setSourceEventId(new UnsignedLong(2L)) 4834 .setEnrollmentId("1") 4835 .setSourceType(Source.SourceType.EVENT) 4836 .setAttributionDestinations(List.of(APP_DESTINATION)) 4837 .setSourceId("20") 4838 .setTriggerId("1001") 4839 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4840 .setTriggerValue(200L) 4841 .build()); 4842 4843 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 4844 Objects.requireNonNull(db); 4845 sourceList.forEach(source -> insertSource(source, source.getId())); 4846 triggers.forEach(trigger -> AbstractDbIntegrationTest.insertToDb(trigger, db)); 4847 4848 Stream.of(reportList1, reportList2, reportList3) 4849 .flatMap(Collection::stream) 4850 .forEach( 4851 (eventReport -> { 4852 mDatastoreManager.runInTransaction( 4853 (dao) -> dao.insertEventReport(eventReport)); 4854 })); 4855 4856 assertEquals( 4857 reportList1, 4858 mDatastoreManager 4859 .runInTransactionWithResult( 4860 measurementDao -> 4861 measurementDao.getSourceEventReports(sourceList.get(0))) 4862 .orElseThrow()); 4863 4864 assertEquals( 4865 reportList2, 4866 mDatastoreManager 4867 .runInTransactionWithResult( 4868 measurementDao -> 4869 measurementDao.getSourceEventReports(sourceList.get(1))) 4870 .orElseThrow()); 4871 } 4872 4873 @Test getSourceEventReports_sourcesWithSameEventId_haveSeparateEventReportsMatch()4874 public void getSourceEventReports_sourcesWithSameEventId_haveSeparateEventReportsMatch() { 4875 List<Source> sourceList = 4876 Arrays.asList( 4877 SourceFixture.getMinimalValidSourceBuilder() 4878 .setId("1") 4879 .setEventId(new UnsignedLong(1L)) 4880 .setEnrollmentId("1") 4881 .build(), 4882 SourceFixture.getMinimalValidSourceBuilder() 4883 .setId("2") 4884 .setEventId(new UnsignedLong(1L)) 4885 .setEnrollmentId("1") 4886 .build(), 4887 SourceFixture.getMinimalValidSourceBuilder() 4888 .setId("3") 4889 .setEventId(new UnsignedLong(2L)) 4890 .setEnrollmentId("2") 4891 .build(), 4892 SourceFixture.getMinimalValidSourceBuilder() 4893 .setId("4") 4894 .setEventId(new UnsignedLong(2L)) 4895 .setEnrollmentId("2") 4896 .build()); 4897 4898 List<Trigger> triggers = 4899 Arrays.asList( 4900 TriggerFixture.getValidTriggerBuilder() 4901 .setId("101") 4902 .setEnrollmentId("2") 4903 .build(), 4904 TriggerFixture.getValidTriggerBuilder() 4905 .setId("102") 4906 .setEnrollmentId("2") 4907 .build()); 4908 4909 // Should match with source 1 4910 List<EventReport> reportList1 = new ArrayList<>(); 4911 reportList1.add( 4912 new EventReport.Builder() 4913 .setId("1") 4914 .setSourceEventId(new UnsignedLong(1L)) 4915 .setEnrollmentId("1") 4916 .setAttributionDestinations(sourceList.get(0).getAppDestinations()) 4917 .setSourceType(sourceList.get(0).getSourceType()) 4918 .setSourceId("1") 4919 .setTriggerId("101") 4920 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4921 .build()); 4922 reportList1.add( 4923 new EventReport.Builder() 4924 .setId("2") 4925 .setSourceEventId(new UnsignedLong(1L)) 4926 .setEnrollmentId("1") 4927 .setAttributionDestinations(List.of(APP_DESTINATION)) 4928 .setSourceType(sourceList.get(0).getSourceType()) 4929 .setSourceId("1") 4930 .setTriggerId("102") 4931 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4932 .build()); 4933 4934 // Should match with source 2 4935 List<EventReport> reportList2 = new ArrayList<>(); 4936 reportList2.add( 4937 new EventReport.Builder() 4938 .setId("3") 4939 .setSourceEventId(new UnsignedLong(2L)) 4940 .setEnrollmentId("1") 4941 .setAttributionDestinations(sourceList.get(1).getAppDestinations()) 4942 .setSourceType(sourceList.get(1).getSourceType()) 4943 .setSourceId("2") 4944 .setTriggerId("101") 4945 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4946 .build()); 4947 reportList2.add( 4948 new EventReport.Builder() 4949 .setId("4") 4950 .setSourceEventId(new UnsignedLong(2L)) 4951 .setEnrollmentId("1") 4952 .setAttributionDestinations(sourceList.get(1).getAppDestinations()) 4953 .setSourceType(sourceList.get(1).getSourceType()) 4954 .setSourceId("2") 4955 .setTriggerId("102") 4956 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4957 .build()); 4958 4959 // Match with source3 4960 List<EventReport> reportList3 = new ArrayList<>(); 4961 reportList3.add( 4962 new EventReport.Builder() 4963 .setId("5") 4964 .setSourceEventId(new UnsignedLong(2L)) 4965 .setEnrollmentId("2") 4966 .setSourceType(Source.SourceType.EVENT) 4967 .setAttributionDestinations(List.of(APP_DESTINATION)) 4968 .setSourceId("3") 4969 .setTriggerId("101") 4970 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4971 .build()); 4972 reportList3.add( 4973 new EventReport.Builder() 4974 .setId("6") 4975 .setSourceEventId(new UnsignedLong(2L)) 4976 .setEnrollmentId("2") 4977 .setSourceType(Source.SourceType.EVENT) 4978 .setAttributionDestinations(List.of(APP_DESTINATION)) 4979 .setSourceId("3") 4980 .setTriggerId("102") 4981 .setRegistrationOrigin(REGISTRATION_ORIGIN) 4982 .build()); 4983 4984 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 4985 Objects.requireNonNull(db); 4986 sourceList.forEach(source -> insertSource(source, source.getId())); 4987 triggers.forEach(trigger -> AbstractDbIntegrationTest.insertToDb(trigger, db)); 4988 4989 Stream.of(reportList1, reportList2, reportList3) 4990 .flatMap(Collection::stream) 4991 .forEach( 4992 (eventReport -> { 4993 mDatastoreManager.runInTransaction( 4994 (dao) -> dao.insertEventReport(eventReport)); 4995 })); 4996 4997 assertEquals( 4998 reportList1, 4999 mDatastoreManager 5000 .runInTransactionWithResult( 5001 measurementDao -> 5002 measurementDao.getSourceEventReports(sourceList.get(0))) 5003 .orElseThrow()); 5004 5005 assertEquals( 5006 reportList2, 5007 mDatastoreManager 5008 .runInTransactionWithResult( 5009 measurementDao -> 5010 measurementDao.getSourceEventReports(sourceList.get(1))) 5011 .orElseThrow()); 5012 5013 assertEquals( 5014 reportList3, 5015 mDatastoreManager 5016 .runInTransactionWithResult( 5017 measurementDao -> 5018 measurementDao.getSourceEventReports(sourceList.get(2))) 5019 .orElseThrow()); 5020 } 5021 5022 @Test testUpdateSourceStatus()5023 public void testUpdateSourceStatus() { 5024 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 5025 Objects.requireNonNull(db); 5026 5027 List<Source> sourceList = new ArrayList<>(); 5028 sourceList.add(SourceFixture.getMinimalValidSourceBuilder().setId("1").build()); 5029 sourceList.add(SourceFixture.getMinimalValidSourceBuilder().setId("2").build()); 5030 sourceList.add(SourceFixture.getMinimalValidSourceBuilder().setId("3").build()); 5031 sourceList.forEach( 5032 source -> { 5033 ContentValues values = new ContentValues(); 5034 values.put(SourceContract.ID, source.getId()); 5035 values.put(SourceContract.STATUS, 1); 5036 db.insert(SourceContract.TABLE, null, values); 5037 }); 5038 5039 // Multiple Elements 5040 assertTrue( 5041 mDatastoreManager.runInTransaction( 5042 measurementDao -> 5043 measurementDao.updateSourceStatus( 5044 List.of("1", "2", "3"), Source.Status.IGNORED))); 5045 5046 // Single Element 5047 assertTrue( 5048 mDatastoreManager.runInTransaction( 5049 measurementDao -> 5050 measurementDao.updateSourceStatus( 5051 List.of("1", "2"), Source.Status.IGNORED))); 5052 } 5053 5054 @Test updateSourceAttributedTriggers_baseline_equal()5055 public void updateSourceAttributedTriggers_baseline_equal() throws Exception { 5056 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 5057 Objects.requireNonNull(db); 5058 5059 List<Source> sourceList = new ArrayList<>(); 5060 sourceList.add( 5061 SourceFixture.getValidFullSourceBuilderWithFlexEventReportValueSum() 5062 .setId("1") 5063 .build()); 5064 sourceList.add( 5065 SourceFixture.getValidFullSourceBuilderWithFlexEventReportValueSum() 5066 .setId("2") 5067 .build()); 5068 sourceList.add( 5069 SourceFixture.getValidFullSourceBuilderWithFlexEventReportValueSum() 5070 .setId("3") 5071 .build()); 5072 mDatastoreManager.runInTransaction( 5073 (dao) -> { 5074 for (Source source : sourceList) { 5075 dao.insertSource(source); 5076 } 5077 }); 5078 List<EventReport> eventReportList = new ArrayList<>(); 5079 eventReportList.add( 5080 EventReportFixture.getBaseEventReportBuild() 5081 .setTriggerData(new UnsignedLong(1L)) 5082 .setTriggerPriority(3L) 5083 .setTriggerValue(5L) 5084 .setReportTime(10000L) 5085 .build()); 5086 Source originalSource = sourceList.get(0); 5087 insertAttributedTrigger(originalSource.getTriggerSpecs(), eventReportList.get(0)); 5088 Optional<Source> newSource = 5089 mDatastoreManager.runInTransactionWithResult( 5090 measurementDao -> measurementDao.getSource(originalSource.getId())); 5091 assertTrue(newSource.isPresent()); 5092 5093 assertNotEquals(newSource.get(), originalSource); 5094 newSource.get().buildTriggerSpecs(); 5095 assertEquals(0, newSource.get().getTriggerSpecs().getAttributedTriggers().size()); 5096 5097 mDatastoreManager.runInTransaction( 5098 measurementDao -> 5099 measurementDao.updateSourceAttributedTriggers( 5100 originalSource.getId(), 5101 originalSource.attributedTriggersToJsonFlexApi())); 5102 5103 mDatastoreManager.runInTransaction( 5104 measurementDao -> { 5105 assertEquals( 5106 originalSource.attributedTriggersToJsonFlexApi(), 5107 measurementDao 5108 .getSource(originalSource.getId()) 5109 .getEventAttributionStatus()); 5110 }); 5111 } 5112 5113 @Test testGetMatchingActiveSources()5114 public void testGetMatchingActiveSources() { 5115 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 5116 Objects.requireNonNull(db); 5117 String enrollmentId = "enrollment-id"; 5118 Uri appDestination = Uri.parse("android-app://com.example.abc"); 5119 Uri webDestination = WebUtil.validUri("https://example.test"); 5120 Uri webDestinationWithSubdomain = WebUtil.validUri("https://xyz.example.test"); 5121 Source sApp1 = 5122 SourceFixture.getMinimalValidSourceBuilder() 5123 .setId("1") 5124 .setEventTime(10) 5125 .setExpiryTime(20) 5126 .setAppDestinations(List.of(appDestination)) 5127 .setEnrollmentId(enrollmentId) 5128 .build(); 5129 Source sApp2 = 5130 SourceFixture.getMinimalValidSourceBuilder() 5131 .setId("2") 5132 .setEventTime(10) 5133 .setExpiryTime(50) 5134 .setAppDestinations(List.of(appDestination)) 5135 .setEnrollmentId(enrollmentId) 5136 .build(); 5137 Source sApp3 = 5138 SourceFixture.getMinimalValidSourceBuilder() 5139 .setId("3") 5140 .setEventTime(20) 5141 .setExpiryTime(50) 5142 .setAppDestinations(List.of(appDestination)) 5143 .setEnrollmentId(enrollmentId) 5144 .build(); 5145 Source sApp4 = 5146 SourceFixture.getMinimalValidSourceBuilder() 5147 .setId("4") 5148 .setEventTime(30) 5149 .setExpiryTime(50) 5150 .setAppDestinations(List.of(appDestination)) 5151 .setEnrollmentId(enrollmentId) 5152 .build(); 5153 Source sWeb5 = 5154 SourceFixture.getMinimalValidSourceBuilder() 5155 .setId("5") 5156 .setEventTime(10) 5157 .setExpiryTime(20) 5158 .setWebDestinations(List.of(webDestination)) 5159 .setEnrollmentId(enrollmentId) 5160 .build(); 5161 Source sWeb6 = 5162 SourceFixture.getMinimalValidSourceBuilder() 5163 .setId("6") 5164 .setEventTime(10) 5165 .setExpiryTime(50) 5166 .setWebDestinations(List.of(webDestination)) 5167 .setEnrollmentId(enrollmentId) 5168 .build(); 5169 Source sAppWeb7 = 5170 SourceFixture.getMinimalValidSourceBuilder() 5171 .setId("7") 5172 .setEventTime(10) 5173 .setExpiryTime(20) 5174 .setAppDestinations(List.of(appDestination)) 5175 .setWebDestinations(List.of(webDestination)) 5176 .setEnrollmentId(enrollmentId) 5177 .build(); 5178 5179 List<Source> sources = Arrays.asList(sApp1, sApp2, sApp3, sApp4, sWeb5, sWeb6, sAppWeb7); 5180 sources.forEach(source -> insertInDb(db, source)); 5181 5182 Function<Trigger, List<Source>> runFunc = 5183 trigger -> { 5184 List<Source> result = 5185 mDatastoreManager 5186 .runInTransactionWithResult( 5187 measurementDao -> 5188 measurementDao.getMatchingActiveSources( 5189 trigger)) 5190 .orElseThrow(); 5191 result.sort(Comparator.comparing(Source::getId)); 5192 return result; 5193 }; 5194 5195 // Trigger Time > sApp1's eventTime and < sApp1's expiryTime 5196 // Trigger Time > sApp2's eventTime and < sApp2's expiryTime 5197 // Trigger Time < sApp3's eventTime 5198 // Trigger Time < sApp4's eventTime 5199 // sApp5 and sApp6 don't have app destination 5200 // Trigger Time > sAppWeb7's eventTime and < sAppWeb7's expiryTime 5201 // Expected: Match with sApp1, sApp2, sAppWeb7 5202 Trigger trigger1MatchSource1And2 = 5203 TriggerFixture.getValidTriggerBuilder() 5204 .setTriggerTime(12) 5205 .setEnrollmentId(enrollmentId) 5206 .setAttributionDestination(appDestination) 5207 .setDestinationType(EventSurfaceType.APP) 5208 .build(); 5209 List<Source> result1 = runFunc.apply(trigger1MatchSource1And2); 5210 assertEquals(3, result1.size()); 5211 assertEquals(sApp1.getId(), result1.get(0).getId()); 5212 assertEquals(sApp2.getId(), result1.get(1).getId()); 5213 assertEquals(sAppWeb7.getId(), result1.get(2).getId()); 5214 5215 // Trigger Time > sApp1's eventTime and = sApp1's expiryTime 5216 // Trigger Time > sApp2's eventTime and < sApp2's expiryTime 5217 // Trigger Time = sApp3's eventTime 5218 // Trigger Time < sApp4's eventTime 5219 // sApp5 and sApp6 don't have app destination 5220 // Trigger Time > sAppWeb7's eventTime and = sAppWeb7's expiryTime 5221 // Expected: Match with sApp2, sApp3 5222 Trigger trigger2MatchSource127 = 5223 TriggerFixture.getValidTriggerBuilder() 5224 .setTriggerTime(20) 5225 .setEnrollmentId(enrollmentId) 5226 .setAttributionDestination(appDestination) 5227 .setDestinationType(EventSurfaceType.APP) 5228 .build(); 5229 5230 List<Source> result2 = runFunc.apply(trigger2MatchSource127); 5231 assertEquals(2, result2.size()); 5232 assertEquals(sApp2.getId(), result2.get(0).getId()); 5233 assertEquals(sApp3.getId(), result2.get(1).getId()); 5234 5235 // Trigger Time > sApp1's expiryTime 5236 // Trigger Time > sApp2's eventTime and < sApp2's expiryTime 5237 // Trigger Time > sApp3's eventTime and < sApp3's expiryTime 5238 // Trigger Time < sApp4's eventTime 5239 // sApp5 and sApp6 don't have app destination 5240 // Trigger Time > sAppWeb7's expiryTime 5241 // Expected: Match with sApp2, sApp3 5242 Trigger trigger3MatchSource237 = 5243 TriggerFixture.getValidTriggerBuilder() 5244 .setTriggerTime(21) 5245 .setEnrollmentId(enrollmentId) 5246 .setAttributionDestination(appDestination) 5247 .setDestinationType(EventSurfaceType.APP) 5248 .build(); 5249 5250 List<Source> result3 = runFunc.apply(trigger3MatchSource237); 5251 assertEquals(2, result3.size()); 5252 assertEquals(sApp2.getId(), result3.get(0).getId()); 5253 assertEquals(sApp3.getId(), result3.get(1).getId()); 5254 5255 // Trigger Time > sApp1's expiryTime 5256 // Trigger Time > sApp2's eventTime and < sApp2's expiryTime 5257 // Trigger Time > sApp3's eventTime and < sApp3's expiryTime 5258 // Trigger Time > sApp4's eventTime and < sApp4's expiryTime 5259 // sApp5 and sApp6 don't have app destination 5260 // Trigger Time > sAppWeb7's expiryTime 5261 // Expected: Match with sApp2, sApp3 and sApp4 5262 Trigger trigger4MatchSource1And2And3 = 5263 TriggerFixture.getValidTriggerBuilder() 5264 .setTriggerTime(31) 5265 .setEnrollmentId(enrollmentId) 5266 .setAttributionDestination(appDestination) 5267 .setDestinationType(EventSurfaceType.APP) 5268 .build(); 5269 5270 List<Source> result4 = runFunc.apply(trigger4MatchSource1And2And3); 5271 assertEquals(3, result4.size()); 5272 assertEquals(sApp2.getId(), result4.get(0).getId()); 5273 assertEquals(sApp3.getId(), result4.get(1).getId()); 5274 assertEquals(sApp4.getId(), result4.get(2).getId()); 5275 5276 // sApp1, sApp2, sApp3, sApp4 don't have web destination 5277 // Trigger Time > sWeb5's eventTime and < sApp5's expiryTime 5278 // Trigger Time > sWeb6's eventTime and < sApp6's expiryTime 5279 // Trigger Time > sAppWeb7's eventTime and < sAppWeb7's expiryTime 5280 // Expected: Match with sApp5, sApp6, sAppWeb7 5281 Trigger trigger5MatchSource567 = 5282 TriggerFixture.getValidTriggerBuilder() 5283 .setTriggerTime(12) 5284 .setEnrollmentId(enrollmentId) 5285 .setAttributionDestination(webDestination) 5286 .setDestinationType(EventSurfaceType.WEB) 5287 .build(); 5288 List<Source> result5 = runFunc.apply(trigger5MatchSource567); 5289 assertEquals(3, result1.size()); 5290 assertEquals(sWeb5.getId(), result5.get(0).getId()); 5291 assertEquals(sWeb6.getId(), result5.get(1).getId()); 5292 assertEquals(sAppWeb7.getId(), result5.get(2).getId()); 5293 5294 // sApp1, sApp2, sApp3, sApp4 don't have web destination 5295 // Trigger Time > sWeb5's expiryTime 5296 // Trigger Time > sWeb6's eventTime and < sApp6's expiryTime 5297 // Trigger Time > sWeb7's expiryTime 5298 // Expected: Match with sApp6 only 5299 Trigger trigger6MatchSource67 = 5300 TriggerFixture.getValidTriggerBuilder() 5301 .setTriggerTime(21) 5302 .setEnrollmentId(enrollmentId) 5303 .setAttributionDestination(webDestinationWithSubdomain) 5304 .setDestinationType(EventSurfaceType.WEB) 5305 .build(); 5306 5307 List<Source> result6 = runFunc.apply(trigger6MatchSource67); 5308 assertEquals(1, result6.size()); 5309 assertEquals(sWeb6.getId(), result6.get(0).getId()); 5310 5311 // Trigger with different subdomain than source 5312 // Expected: No Match found 5313 Trigger triggerDifferentRegistrationOrigin = 5314 TriggerFixture.getValidTriggerBuilder() 5315 .setTriggerTime(12) 5316 .setEnrollmentId(enrollmentId) 5317 .setAttributionDestination(appDestination) 5318 .setDestinationType(EventSurfaceType.APP) 5319 .setRegistrationOrigin( 5320 WebUtil.validUri("https://subdomain-different.example.test")) 5321 .build(); 5322 5323 List<Source> result7 = runFunc.apply(triggerDifferentRegistrationOrigin); 5324 assertTrue(result7.isEmpty()); 5325 5326 // Trigger with different domain than source 5327 // Expected: No Match found 5328 Trigger triggerDifferentDomainOrigin = 5329 TriggerFixture.getValidTriggerBuilder() 5330 .setTriggerTime(12) 5331 .setEnrollmentId(enrollmentId) 5332 .setAttributionDestination(appDestination) 5333 .setDestinationType(EventSurfaceType.APP) 5334 .setRegistrationOrigin( 5335 WebUtil.validUri("https://subdomain.example-different.test")) 5336 .build(); 5337 5338 List<Source> result8 = runFunc.apply(triggerDifferentDomainOrigin); 5339 assertTrue(result8.isEmpty()); 5340 5341 // Trigger with different port than source 5342 // Expected: No Match found 5343 Trigger triggerDifferentPort = 5344 TriggerFixture.getValidTriggerBuilder() 5345 .setTriggerTime(12) 5346 .setEnrollmentId(enrollmentId) 5347 .setAttributionDestination(appDestination) 5348 .setDestinationType(EventSurfaceType.APP) 5349 .setRegistrationOrigin( 5350 WebUtil.validUri("https://subdomain.example.test:8083")) 5351 .build(); 5352 5353 List<Source> result9 = runFunc.apply(triggerDifferentPort); 5354 assertTrue(result9.isEmpty()); 5355 5356 // Enrollment id for trigger and source not same 5357 // Registration Origin for trigger and source same 5358 // Expected: Match with sApp1, sApp2, sAppWeb7 5359 Trigger triggerDifferentEnrollmentSameRegistration = 5360 TriggerFixture.getValidTriggerBuilder() 5361 .setTriggerTime(12) 5362 .setEnrollmentId("different-enrollment-id") 5363 .setAttributionDestination(appDestination) 5364 .setDestinationType(EventSurfaceType.APP) 5365 .build(); 5366 List<Source> result10 = runFunc.apply(triggerDifferentEnrollmentSameRegistration); 5367 assertEquals(3, result10.size()); 5368 assertEquals(sApp1.getId(), result10.get(0).getId()); 5369 assertEquals(sApp2.getId(), result10.get(1).getId()); 5370 assertEquals(sAppWeb7.getId(), result10.get(2).getId()); 5371 } 5372 5373 @Test testGetMatchingActiveSources_multipleDestinations()5374 public void testGetMatchingActiveSources_multipleDestinations() { 5375 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 5376 String enrollmentId = "enrollment-id"; 5377 Uri webDestination1 = WebUtil.validUri("https://example.test"); 5378 Uri webDestination1WithSubdomain = WebUtil.validUri("https://xyz.example.test"); 5379 Uri webDestination2 = WebUtil.validUri("https://example2.test"); 5380 Source sWeb1 = 5381 SourceFixture.getMinimalValidSourceBuilder() 5382 .setId("1") 5383 .setEventTime(10) 5384 .setExpiryTime(20) 5385 .setWebDestinations(List.of(webDestination1)) 5386 .setEnrollmentId(enrollmentId) 5387 .build(); 5388 Source sWeb2 = 5389 SourceFixture.getMinimalValidSourceBuilder() 5390 .setId("2") 5391 .setEventTime(10) 5392 .setExpiryTime(50) 5393 .setWebDestinations(List.of(webDestination1, webDestination2)) 5394 .setEnrollmentId(enrollmentId) 5395 .build(); 5396 Source sAppWeb3 = 5397 SourceFixture.getMinimalValidSourceBuilder() 5398 .setId("3") 5399 .setEventTime(10) 5400 .setExpiryTime(20) 5401 .setWebDestinations(List.of(webDestination1)) 5402 .setEnrollmentId(enrollmentId) 5403 .build(); 5404 5405 List<Source> sources = Arrays.asList(sWeb1, sWeb2, sAppWeb3); 5406 sources.forEach(source -> insertInDb(db, source)); 5407 5408 Function<Trigger, List<Source>> getMatchingSources = 5409 trigger -> { 5410 List<Source> result = 5411 mDatastoreManager 5412 .runInTransactionWithResult( 5413 measurementDao -> 5414 measurementDao.getMatchingActiveSources( 5415 trigger)) 5416 .orElseThrow(); 5417 result.sort(Comparator.comparing(Source::getId)); 5418 return result; 5419 }; 5420 5421 Trigger triggerMatchSourceWeb2 = 5422 TriggerFixture.getValidTriggerBuilder() 5423 .setTriggerTime(21) 5424 .setEnrollmentId(enrollmentId) 5425 .setAttributionDestination(webDestination1WithSubdomain) 5426 .setDestinationType(EventSurfaceType.WEB) 5427 .build(); 5428 5429 List<Source> result = getMatchingSources.apply(triggerMatchSourceWeb2); 5430 assertEquals(1, result.size()); 5431 assertEquals(sWeb2.getId(), result.get(0).getId()); 5432 } 5433 5434 @Test testGetMatchingActiveSources_attributionScopeEnabled_populateScopes()5435 public void testGetMatchingActiveSources_attributionScopeEnabled_populateScopes() { 5436 mLegacyFlags = mMockFlags; 5437 mocker.mockGetFlags(mMockFlags); 5438 doReturn(true).when(mMockFlags).getMeasurementEnableAttributionScope(); 5439 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mMockFlags).getMeasurementDbSizeLimit(); 5440 5441 // S0: attribution scopes -> [], destinations -> [D1, D2] 5442 Source source0 = 5443 insertSourceForAttributionScope( 5444 /* attributionScopes= */ null, 5445 /* attributionScopeLimit= */ null, 5446 /* maxEventStates= */ null, 5447 SOURCE_EVENT_TIME, 5448 List.of(WEB_ONE_DESTINATION), 5449 List.of(APP_ONE_DESTINATION)); 5450 // S1: attribution scopes -> ["1", "2"], destinations -> [D1] 5451 Source source1 = 5452 insertSourceForAttributionScope( 5453 List.of("1", "2"), 5454 ATTRIBUTION_SCOPE_LIMIT, 5455 MAX_EVENT_STATES, 5456 SOURCE_EVENT_TIME + 1, 5457 null, 5458 List.of(APP_ONE_DESTINATION)); 5459 // S2: attribution scopes -> ["2", "3"], destinations -> [D2] 5460 Source source2 = 5461 insertSourceForAttributionScope( 5462 List.of("2", "3"), 5463 ATTRIBUTION_SCOPE_LIMIT, 5464 MAX_EVENT_STATES, 5465 SOURCE_EVENT_TIME + 2, 5466 List.of(WEB_ONE_DESTINATION), 5467 null); 5468 5469 Trigger trigger0 = 5470 TriggerFixture.getValidTriggerBuilder() 5471 .setTriggerTime(SOURCE_EVENT_TIME + 3) 5472 .setAttributionDestination(APP_ONE_DESTINATION) 5473 .setDestinationType(EventSurfaceType.APP) 5474 .build(); 5475 List<Source> matchingSources0 = getMatchingSources(trigger0); 5476 assertThat(matchingSources0.size()).isEqualTo(2); 5477 List<String> matchingSourceIds0 = 5478 matchingSources0.stream().map(Source::getId).collect(Collectors.toList()); 5479 List<List<String>> matchingSourceAttributionScopes0 = 5480 matchingSources0.stream() 5481 .map(Source::getAttributionScopes) 5482 .collect(Collectors.toList()); 5483 assertThat(matchingSourceIds0).containsExactly(source0.getId(), source1.getId()); 5484 // Source attribution scopes won't be populated if trigger doesn't have attribution scope. 5485 assertThat(matchingSourceAttributionScopes0).containsExactly(null, null); 5486 5487 Trigger trigger1 = 5488 TriggerFixture.getValidTriggerBuilder() 5489 .setTriggerTime(SOURCE_EVENT_TIME + 4) 5490 .setAttributionScopesString("1") 5491 .setAttributionDestination(APP_ONE_DESTINATION) 5492 .setDestinationType(EventSurfaceType.APP) 5493 .build(); 5494 List<Source> matchingSources1 = getMatchingSources(trigger1); 5495 List<String> matchingSourceIds1 = 5496 matchingSources1.stream().map(Source::getId).collect(Collectors.toList()); 5497 List<List<String>> matchingSourceAttributionScopes1 = 5498 matchingSources1.stream() 5499 .map(Source::getAttributionScopes) 5500 .collect(Collectors.toList()); 5501 assertThat(matchingSourceIds1).containsExactly(source0.getId(), source1.getId()); 5502 assertThat(matchingSourceAttributionScopes1) 5503 .containsExactly(source0.getAttributionScopes(), source1.getAttributionScopes()); 5504 5505 Trigger trigger2 = 5506 TriggerFixture.getValidTriggerBuilder() 5507 .setTriggerTime(SOURCE_EVENT_TIME + 5) 5508 .setAttributionScopesString("2") 5509 .setAttributionDestination(WEB_ONE_DESTINATION) 5510 .setDestinationType(EventSurfaceType.WEB) 5511 .build(); 5512 List<Source> matchingSources2 = getMatchingSources(trigger2); 5513 List<String> matchingSourceIds2 = 5514 matchingSources2.stream().map(Source::getId).collect(Collectors.toList()); 5515 List<List<String>> matchingSourceAttributionScopes2 = 5516 matchingSources2.stream() 5517 .map(Source::getAttributionScopes) 5518 .collect(Collectors.toList()); 5519 assertThat(matchingSourceIds2).containsExactly(source0.getId(), source2.getId()); 5520 assertThat(matchingSourceAttributionScopes2) 5521 .containsExactly(source0.getAttributionScopes(), source2.getAttributionScopes()); 5522 } 5523 5524 @Test testGetAttributionScopesForRegistration()5525 public void testGetAttributionScopesForRegistration() { 5526 mLegacyFlags = mMockFlags; 5527 mocker.mockGetFlags(mMockFlags); 5528 doReturn(true).when(mMockFlags).getMeasurementEnableAttributionScope(); 5529 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mMockFlags).getMeasurementDbSizeLimit(); 5530 5531 insertSourceForAttributionScope( 5532 List.of("1"), 5533 ATTRIBUTION_SCOPE_LIMIT, 5534 MAX_EVENT_STATES, 5535 SOURCE_EVENT_TIME, 5536 List.of(WEB_ONE_DESTINATION), 5537 List.of(APP_ONE_DESTINATION), 5538 SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN, 5539 SourceFixture.ValidSourceParams.REGISTRATION_ID, 5540 Source.SourceType.NAVIGATION, 5541 Source.Status.ACTIVE); 5542 insertSourceForAttributionScope( 5543 List.of("2"), 5544 ATTRIBUTION_SCOPE_LIMIT, 5545 MAX_EVENT_STATES, 5546 SOURCE_EVENT_TIME, 5547 List.of(WEB_ONE_DESTINATION), 5548 List.of(APP_ONE_DESTINATION), 5549 SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN, 5550 SourceFixture.ValidSourceParams.REGISTRATION_ID, 5551 Source.SourceType.NAVIGATION, 5552 Source.Status.ACTIVE); 5553 insertSourceForAttributionScope( 5554 List.of("3"), 5555 ATTRIBUTION_SCOPE_LIMIT, 5556 MAX_EVENT_STATES, 5557 SOURCE_EVENT_TIME, 5558 List.of(WEB_ONE_DESTINATION), 5559 List.of(APP_ONE_DESTINATION), 5560 REGISTRATION_ORIGIN_2, 5561 REGISTRATION_ID2, 5562 Source.SourceType.NAVIGATION, 5563 Source.Status.ACTIVE); 5564 // Ignored source, attribution scopes ignored. 5565 insertSourceForAttributionScope( 5566 List.of("4"), 5567 ATTRIBUTION_SCOPE_LIMIT, 5568 MAX_EVENT_STATES, 5569 SOURCE_EVENT_TIME, 5570 List.of(WEB_ONE_DESTINATION), 5571 List.of(APP_ONE_DESTINATION), 5572 SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN, 5573 SourceFixture.ValidSourceParams.REGISTRATION_ID, 5574 Source.SourceType.NAVIGATION, 5575 Source.Status.IGNORED); 5576 5577 // Execution 5578 mDatastoreManager.runInTransaction( 5579 (dao) -> { 5580 assertThat( 5581 dao.getAttributionScopesForRegistration( 5582 SourceFixture.ValidSourceParams.REGISTRATION_ID, 5583 SourceFixture.ValidSourceParams 5584 .REGISTRATION_ORIGIN 5585 .toString()) 5586 .get()) 5587 .containsExactly("1", "2"); 5588 assertThat( 5589 dao.getAttributionScopesForRegistration( 5590 REGISTRATION_ID2, 5591 REGISTRATION_ORIGIN_2.toString()) 5592 .get()) 5593 .containsExactly("3"); 5594 assertThat( 5595 dao.getAttributionScopesForRegistration( 5596 SourceFixture.ValidSourceParams.REGISTRATION_ID, 5597 SourceFixture.ValidSourceParams 5598 .REGISTRATION_ORIGIN 5599 .toString()) 5600 .get()) 5601 .containsExactly("1", "2"); 5602 assertThat( 5603 dao.getAttributionScopesForRegistration( 5604 REGISTRATION_ID2, 5605 REGISTRATION_ORIGIN_2.toString()) 5606 .get()) 5607 .containsExactly("3"); 5608 assertThat( 5609 dao.getAttributionScopesForRegistration( 5610 SourceFixture.ValidSourceParams.REGISTRATION_ID, 5611 REGISTRATION_ORIGIN_2.toString()) 5612 .isEmpty()) 5613 .isTrue(); 5614 }); 5615 } 5616 5617 @Test testGetMatchingActiveDelayedSources()5618 public void testGetMatchingActiveDelayedSources() { 5619 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 5620 Objects.requireNonNull(db); 5621 String enrollmentId = "enrollment-id"; 5622 Uri appDestination = Uri.parse("android-app://com.example.abc"); 5623 Uri webDestination = WebUtil.validUri("https://example.test"); 5624 Source sApp1 = 5625 SourceFixture.getMinimalValidSourceBuilder() 5626 .setId("1") 5627 .setEventTime(10) 5628 .setExpiryTime(20) 5629 .setAppDestinations(List.of(appDestination)) 5630 .setEnrollmentId(enrollmentId) 5631 .build(); 5632 Source sApp2 = 5633 SourceFixture.getMinimalValidSourceBuilder() 5634 .setId("2") 5635 .setEventTime(140) 5636 .setExpiryTime(200) 5637 .setAppDestinations(List.of(appDestination)) 5638 .setEnrollmentId(enrollmentId) 5639 .build(); 5640 Source sApp3 = 5641 SourceFixture.getMinimalValidSourceBuilder() 5642 .setId("3") 5643 .setEventTime(20) 5644 .setExpiryTime(50) 5645 .setAppDestinations(List.of(appDestination)) 5646 .setEnrollmentId(enrollmentId) 5647 .build(); 5648 Source sApp4 = 5649 SourceFixture.getMinimalValidSourceBuilder() 5650 .setId("4") 5651 .setEventTime(16) 5652 .setExpiryTime(50) 5653 .setAppDestinations(List.of(appDestination)) 5654 .setEnrollmentId(enrollmentId) 5655 .build(); 5656 Source sWeb5 = 5657 SourceFixture.getMinimalValidSourceBuilder() 5658 .setId("5") 5659 .setEventTime(13) 5660 .setExpiryTime(20) 5661 .setWebDestinations(List.of(webDestination)) 5662 .setEnrollmentId(enrollmentId) 5663 .build(); 5664 Source sWeb6 = 5665 SourceFixture.getMinimalValidSourceBuilder() 5666 .setId("6") 5667 .setEventTime(14) 5668 .setExpiryTime(50) 5669 .setWebDestinations(List.of(webDestination)) 5670 .setEnrollmentId(enrollmentId) 5671 .build(); 5672 Source sAppWeb7 = 5673 SourceFixture.getMinimalValidSourceBuilder() 5674 .setId("7") 5675 .setEventTime(10) 5676 .setExpiryTime(20) 5677 .setAppDestinations(List.of(appDestination)) 5678 .setWebDestinations(List.of(webDestination)) 5679 .setEnrollmentId(enrollmentId) 5680 .build(); 5681 Source sAppWeb8 = 5682 SourceFixture.getMinimalValidSourceBuilder() 5683 .setId("8") 5684 .setEventTime(15) 5685 .setExpiryTime(25) 5686 .setAppDestinations(List.of(appDestination)) 5687 .setWebDestinations(List.of(webDestination)) 5688 .setEnrollmentId(enrollmentId) 5689 .build(); 5690 5691 List<Source> sources = 5692 Arrays.asList(sApp1, sApp2, sApp3, sApp4, sWeb5, sWeb6, sAppWeb7, sAppWeb8); 5693 sources.forEach(source -> insertInDb(db, source)); 5694 5695 Function<Trigger, Optional<Source>> runFunc = 5696 trigger -> { 5697 Optional<Source> result = 5698 mDatastoreManager 5699 .runInTransactionWithResult( 5700 measurementDao -> 5701 measurementDao 5702 .getNearestDelayedMatchingActiveSource( 5703 trigger)) 5704 .orElseThrow(); 5705 return result; 5706 }; 5707 5708 // sApp1's eventTime <= Trigger Time 5709 // Trigger Time + MAX_DELAYED_SOURCE_REGISTRATION_WINDOW > sApp2's eventTime 5710 // Trigger Time < sApp3's eventTime <= Trigger Time + MAX_DELAYED_SOURCE_REGISTRATION_WINDOW 5711 // Trigger Time < sApp4's eventTime <= Trigger Time + MAX_DELAYED_SOURCE_REGISTRATION_WINDOW 5712 // sWeb5 and sWeb6 don't have app destination 5713 // sAppWeb7's eventTime <= Trigger Time 5714 // Trigger Time < sAppWeb8's eventTime <= Trigger Time + 5715 // MAX_DELAYED_SOURCE_REGISTRATION_WINDOW 5716 // Expected: Match with sAppWeb8 5717 Trigger trigger1MatchSource8 = 5718 TriggerFixture.getValidTriggerBuilder() 5719 .setTriggerTime(12) 5720 .setEnrollmentId(enrollmentId) 5721 .setAttributionDestination(appDestination) 5722 .setDestinationType(EventSurfaceType.APP) 5723 .build(); 5724 Optional<Source> result1 = runFunc.apply(trigger1MatchSource8); 5725 assertEquals(sAppWeb8.getId(), result1.get().getId()); 5726 5727 // sApp1's eventTime <= Trigger Time 5728 // Trigger Time + MAX_DELAYED_SOURCE_REGISTRATION_WINDOW > sApp2's eventTime 5729 // Trigger Time < sApp3's eventTime <= Trigger Time + MAX_DELAYED_SOURCE_REGISTRATION_WINDOW 5730 // Trigger Time < sApp4's eventTime <= Trigger Time + MAX_DELAYED_SOURCE_REGISTRATION_WINDOW 5731 // sWeb5 and sWeb6 don't have app destination 5732 // sAppWeb7's eventTime <= Trigger Time 5733 // sAppWeb8's eventTime <= Trigger Time 5734 // Expected: Match with sApp4 5735 Trigger trigger2MatchSource4 = 5736 TriggerFixture.getValidTriggerBuilder() 5737 .setTriggerTime(15) 5738 .setEnrollmentId(enrollmentId) 5739 .setAttributionDestination(appDestination) 5740 .setDestinationType(EventSurfaceType.APP) 5741 .build(); 5742 Optional<Source> result2 = runFunc.apply(trigger2MatchSource4); 5743 assertEquals(sApp4.getId(), result2.get().getId()); 5744 5745 // sApp1's eventTime <= Trigger Time 5746 // sApp2's eventTime <= Trigger Time 5747 // sApp3's eventTime <= Trigger Time 5748 // sApp4's eventTime <= Trigger Time 5749 // sWeb5 and sWeb6 don't have app destination 5750 // sAppWeb7's eventTime <= Trigger Time 5751 // sAppWeb8's eventTime <= Trigger Time 5752 // Expected: no match 5753 Trigger trigger3NoMatchingSource = 5754 TriggerFixture.getValidTriggerBuilder() 5755 .setTriggerTime(150) 5756 .setEnrollmentId(enrollmentId) 5757 .setAttributionDestination(appDestination) 5758 .setDestinationType(EventSurfaceType.APP) 5759 .build(); 5760 Optional<Source> result3 = runFunc.apply(trigger3NoMatchingSource); 5761 assertFalse(result3.isPresent()); 5762 } 5763 5764 @Test testInsertAggregateEncryptionKey()5765 public void testInsertAggregateEncryptionKey() { 5766 String keyId = "38b1d571-f924-4dc0-abe1-e2bac9b6a6be"; 5767 String publicKey = "/amqBgfDOvHAIuatDyoHxhfHaMoYA4BDxZxwtWBRQhc="; 5768 long expiry = 1653620135831L; 5769 Uri aggregationOrigin = WebUtil.validUri("https://a.test"); 5770 5771 mDatastoreManager.runInTransaction( 5772 (dao) -> 5773 dao.insertAggregateEncryptionKey( 5774 new AggregateEncryptionKey.Builder() 5775 .setKeyId(keyId) 5776 .setPublicKey(publicKey) 5777 .setExpiry(expiry) 5778 .setAggregationCoordinatorOrigin(aggregationOrigin) 5779 .build())); 5780 5781 try (Cursor cursor = 5782 MeasurementDbHelper.getInstance() 5783 .getReadableDatabase() 5784 .query( 5785 MeasurementTables.AggregateEncryptionKey.TABLE, 5786 null, 5787 null, 5788 null, 5789 null, 5790 null, 5791 null)) { 5792 assertTrue(cursor.moveToNext()); 5793 AggregateEncryptionKey aggregateEncryptionKey = 5794 SqliteObjectMapper.constructAggregateEncryptionKeyFromCursor(cursor); 5795 assertNotNull(aggregateEncryptionKey); 5796 assertNotNull(aggregateEncryptionKey.getId()); 5797 assertEquals(keyId, aggregateEncryptionKey.getKeyId()); 5798 assertEquals(publicKey, aggregateEncryptionKey.getPublicKey()); 5799 assertEquals(expiry, aggregateEncryptionKey.getExpiry()); 5800 assertEquals( 5801 aggregationOrigin, aggregateEncryptionKey.getAggregationCoordinatorOrigin()); 5802 } 5803 } 5804 5805 @Test testInsertAggregateReport()5806 public void testInsertAggregateReport() { 5807 AggregateReport validAggregateReport = AggregateReportFixture.getValidAggregateReport(); 5808 mDatastoreManager.runInTransaction( 5809 (dao) -> dao.insertAggregateReport(validAggregateReport)); 5810 5811 try (Cursor cursor = 5812 MeasurementDbHelper.getInstance() 5813 .getReadableDatabase() 5814 .query( 5815 MeasurementTables.AggregateReport.TABLE, 5816 null, 5817 null, 5818 null, 5819 null, 5820 null, 5821 null)) { 5822 assertTrue(cursor.moveToNext()); 5823 AggregateReport aggregateReport = SqliteObjectMapper.constructAggregateReport(cursor); 5824 assertNotNull(aggregateReport); 5825 assertNotNull(aggregateReport.getId()); 5826 assertEquals(validAggregateReport, aggregateReport); 5827 } 5828 } 5829 5830 @Test testInsertAggregateReport_withNullSourceRegistrationTime()5831 public void testInsertAggregateReport_withNullSourceRegistrationTime() { 5832 AggregateReport.Builder builder = AggregateReportFixture.getValidAggregateReportBuilder(); 5833 builder.setSourceRegistrationTime(null); 5834 AggregateReport aggregateReportWithNullSourceRegistrationTime = builder.build(); 5835 mDatastoreManager.runInTransaction( 5836 (dao) -> dao.insertAggregateReport(aggregateReportWithNullSourceRegistrationTime)); 5837 5838 try (Cursor cursor = 5839 MeasurementDbHelper.getInstance() 5840 .getReadableDatabase() 5841 .query( 5842 MeasurementTables.AggregateReport.TABLE, 5843 null, 5844 null, 5845 null, 5846 null, 5847 null, 5848 null)) { 5849 assertTrue(cursor.moveToNext()); 5850 AggregateReport aggregateReport = SqliteObjectMapper.constructAggregateReport(cursor); 5851 assertNotNull(aggregateReport); 5852 assertNotNull(aggregateReport.getId()); 5853 assertEquals(aggregateReportWithNullSourceRegistrationTime, aggregateReport); 5854 } 5855 } 5856 5857 @Test testExistsSourcesWithSameDestination()5858 public void testExistsSourcesWithSameDestination() { 5859 long currentTime = System.currentTimeMillis(); 5860 Uri destination1 = Uri.parse("android-app://com.destination1"); 5861 Uri destination2 = Uri.parse("android-app://com.destination2"); 5862 Uri destination3 = Uri.parse("android-app://com.destination3"); 5863 Uri destination4 = Uri.parse("android-app://com.destination4"); 5864 Uri destination5 = Uri.parse("android-app://com.destination5"); 5865 5866 // Return true. 5867 Source s1 = 5868 SourceFixture.getMinimalValidSourceBuilder() 5869 .setId("S1") 5870 .setStatus(0) 5871 .setExpiryTime(currentTime + 1000L) 5872 .setAppDestinations(List.of(destination1)) 5873 .build(); 5874 5875 // Inactive source. Return false. 5876 Source s2 = 5877 SourceFixture.getMinimalValidSourceBuilder() 5878 .setId("S1") 5879 .setStatus(1) 5880 .setExpiryTime(currentTime + 1000L) 5881 .setAppDestinations(List.of(destination2)) 5882 .build(); 5883 5884 // Expired source. Return false. 5885 Source s3 = 5886 SourceFixture.getMinimalValidSourceBuilder() 5887 .setId("S3") 5888 .setStatus(0) 5889 .setExpiryTime(currentTime - 1000L) 5890 .setAppDestinations(List.of(destination3)) 5891 .build(); 5892 5893 // Web source. Return false. 5894 Source s4 = 5895 SourceFixture.getMinimalValidSourceBuilder() 5896 .setId("S4") 5897 .setStatus(0) 5898 .setExpiryTime(currentTime + 1000L) 5899 .setWebDestinations(List.of(destination4)) 5900 .build(); 5901 5902 insertSource(s1, "S1"); 5903 insertSource(s2, "S2"); 5904 insertSource(s3, "S3"); 5905 insertSource(s4, "S4"); 5906 5907 Optional<Boolean> result1 = 5908 mDatastoreManager.runInTransactionWithResult( 5909 (dao) -> dao.existsActiveSourcesWithDestination(destination1, currentTime)); 5910 5911 Optional<Boolean> result2 = 5912 mDatastoreManager.runInTransactionWithResult( 5913 (dao) -> dao.existsActiveSourcesWithDestination(destination2, currentTime)); 5914 5915 Optional<Boolean> result3 = 5916 mDatastoreManager.runInTransactionWithResult( 5917 (dao) -> dao.existsActiveSourcesWithDestination(destination3, currentTime)); 5918 5919 Optional<Boolean> result4 = 5920 mDatastoreManager.runInTransactionWithResult( 5921 (dao) -> dao.existsActiveSourcesWithDestination(destination4, currentTime)); 5922 5923 // No source with app destination 5. Return false. 5924 Optional<Boolean> result5 = 5925 mDatastoreManager.runInTransactionWithResult( 5926 (dao) -> dao.existsActiveSourcesWithDestination(destination5, currentTime)); 5927 5928 assertThat(result1.get()).isTrue(); 5929 assertThat(result2.get()).isFalse(); 5930 assertThat(result3.get()).isFalse(); 5931 assertThat(result4.get()).isFalse(); 5932 assertThat(result5.get()).isFalse(); 5933 } 5934 5935 @Test testInsertAggregateReport_withTriggerTime()5936 public void testInsertAggregateReport_withTriggerTime() { 5937 AggregateReport.Builder builder = AggregateReportFixture.getValidAggregateReportBuilder(); 5938 builder.setTriggerTime(1L); 5939 AggregateReport aggregateReportWithTriggerTime = builder.build(); 5940 mDatastoreManager.runInTransaction( 5941 (dao) -> dao.insertAggregateReport(aggregateReportWithTriggerTime)); 5942 5943 try (Cursor cursor = 5944 MeasurementDbHelper.getInstance() 5945 .getReadableDatabase() 5946 .query( 5947 MeasurementTables.AggregateReport.TABLE, 5948 null, 5949 null, 5950 null, 5951 null, 5952 null, 5953 null)) { 5954 assertTrue(cursor.moveToNext()); 5955 AggregateReport aggregateReport = SqliteObjectMapper.constructAggregateReport(cursor); 5956 assertNotNull(aggregateReport); 5957 assertNotNull(aggregateReport.getId()); 5958 assertEquals(aggregateReportWithTriggerTime, aggregateReport); 5959 } 5960 } 5961 5962 @Test testInsertAggregateReport_withAggregatableFilteringIdMaxBytes()5963 public void testInsertAggregateReport_withAggregatableFilteringIdMaxBytes() { 5964 AggregateReport.Builder builder = AggregateReportFixture.getValidAggregateReportBuilder(); 5965 builder.setAggregatableFilteringIdMaxBytes(2); 5966 AggregateReport aggregateReportWithAggregatableFilteringIdMaxBytes = builder.build(); 5967 mDatastoreManager.runInTransaction( 5968 (dao) -> 5969 dao.insertAggregateReport( 5970 aggregateReportWithAggregatableFilteringIdMaxBytes)); 5971 5972 try (Cursor cursor = 5973 MeasurementDbHelper.getInstance() 5974 .getReadableDatabase() 5975 .query( 5976 MeasurementTables.AggregateReport.TABLE, 5977 null, 5978 null, 5979 null, 5980 null, 5981 null, 5982 null)) { 5983 assertTrue(cursor.moveToNext()); 5984 AggregateReport aggregateReport = SqliteObjectMapper.constructAggregateReport(cursor); 5985 assertNotNull(aggregateReport); 5986 assertNotNull(aggregateReport.getId()); 5987 assertEquals(aggregateReportWithAggregatableFilteringIdMaxBytes, aggregateReport); 5988 } 5989 } 5990 5991 @Test testDeleteAllMeasurementDataWithEmptyList()5992 public void testDeleteAllMeasurementDataWithEmptyList() { 5993 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 5994 5995 Source source = SourceFixture.getMinimalValidSourceBuilder().setId("S1").build(); 5996 ContentValues sourceValue = new ContentValues(); 5997 sourceValue.put("_id", source.getId()); 5998 db.insert(SourceContract.TABLE, null, sourceValue); 5999 6000 Trigger trigger = TriggerFixture.getValidTriggerBuilder().setId("T1").build(); 6001 ContentValues triggerValue = new ContentValues(); 6002 triggerValue.put("_id", trigger.getId()); 6003 db.insert(TriggerContract.TABLE, null, triggerValue); 6004 6005 EventReport eventReport = new EventReport.Builder().setId("E1").build(); 6006 ContentValues eventReportValue = new ContentValues(); 6007 eventReportValue.put("_id", eventReport.getId()); 6008 db.insert(EventReportContract.TABLE, null, eventReportValue); 6009 6010 AggregateReport aggregateReport = new AggregateReport.Builder().setId("A1").build(); 6011 ContentValues aggregateReportValue = new ContentValues(); 6012 aggregateReportValue.put("_id", aggregateReport.getId()); 6013 db.insert(MeasurementTables.AggregateReport.TABLE, null, aggregateReportValue); 6014 6015 ContentValues rateLimitValue = new ContentValues(); 6016 rateLimitValue.put(AttributionContract.ID, "ARL1"); 6017 rateLimitValue.put(AttributionContract.SOURCE_SITE, "sourceSite"); 6018 rateLimitValue.put(AttributionContract.SOURCE_ORIGIN, "sourceOrigin"); 6019 rateLimitValue.put(AttributionContract.DESTINATION_SITE, "destinationSite"); 6020 rateLimitValue.put(AttributionContract.TRIGGER_TIME, 5L); 6021 rateLimitValue.put(AttributionContract.REGISTRANT, "registrant"); 6022 rateLimitValue.put(AttributionContract.ENROLLMENT_ID, "enrollmentId"); 6023 6024 db.insert(AttributionContract.TABLE, null, rateLimitValue); 6025 6026 AggregateEncryptionKey key = 6027 new AggregateEncryptionKey.Builder() 6028 .setId("K1") 6029 .setKeyId("keyId") 6030 .setPublicKey("publicKey") 6031 .setExpiry(1) 6032 .setAggregationCoordinatorOrigin(Uri.parse("https://1.test")) 6033 .build(); 6034 ContentValues keyValues = new ContentValues(); 6035 keyValues.put("_id", key.getId()); 6036 6037 mDatastoreManager.runInTransaction( 6038 (dao) -> dao.deleteAllMeasurementData(Collections.emptyList())); 6039 6040 for (String table : ALL_MSMT_TABLES) { 6041 assertThat( 6042 db.query( 6043 /* table */ table, 6044 /* columns */ null, 6045 /* selection */ null, 6046 /* selectionArgs */ null, 6047 /* groupBy */ null, 6048 /* having */ null, 6049 /* orderedBy */ null) 6050 .getCount()) 6051 .isEqualTo(0); 6052 } 6053 } 6054 6055 @Test testDeleteAllMeasurementDataWithNonEmptyList()6056 public void testDeleteAllMeasurementDataWithNonEmptyList() { 6057 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 6058 6059 Source source = SourceFixture.getMinimalValidSourceBuilder().setId("S1").build(); 6060 ContentValues sourceValue = new ContentValues(); 6061 sourceValue.put("_id", source.getId()); 6062 db.insert(SourceContract.TABLE, null, sourceValue); 6063 6064 Trigger trigger = TriggerFixture.getValidTriggerBuilder().setId("T1").build(); 6065 ContentValues triggerValue = new ContentValues(); 6066 triggerValue.put("_id", trigger.getId()); 6067 db.insert(TriggerContract.TABLE, null, triggerValue); 6068 6069 EventReport eventReport = new EventReport.Builder().setId("E1").build(); 6070 ContentValues eventReportValue = new ContentValues(); 6071 eventReportValue.put("_id", eventReport.getId()); 6072 db.insert(EventReportContract.TABLE, null, eventReportValue); 6073 6074 AggregateReport aggregateReport = new AggregateReport.Builder().setId("A1").build(); 6075 ContentValues aggregateReportValue = new ContentValues(); 6076 aggregateReportValue.put("_id", aggregateReport.getId()); 6077 db.insert(MeasurementTables.AggregateReport.TABLE, null, aggregateReportValue); 6078 6079 ContentValues rateLimitValue = new ContentValues(); 6080 rateLimitValue.put(AttributionContract.ID, "ARL1"); 6081 rateLimitValue.put(AttributionContract.SOURCE_SITE, "sourceSite"); 6082 rateLimitValue.put(AttributionContract.SOURCE_ORIGIN, "sourceOrigin"); 6083 rateLimitValue.put(AttributionContract.DESTINATION_SITE, "destinationSite"); 6084 rateLimitValue.put(AttributionContract.TRIGGER_TIME, 5L); 6085 rateLimitValue.put(AttributionContract.REGISTRANT, "registrant"); 6086 rateLimitValue.put(AttributionContract.ENROLLMENT_ID, "enrollmentId"); 6087 db.insert(AttributionContract.TABLE, null, rateLimitValue); 6088 6089 AggregateEncryptionKey key = 6090 new AggregateEncryptionKey.Builder() 6091 .setId("K1") 6092 .setKeyId("keyId") 6093 .setPublicKey("publicKey") 6094 .setExpiry(1) 6095 .setAggregationCoordinatorOrigin(Uri.parse("https://1.test")) 6096 .build(); 6097 ContentValues keyValues = new ContentValues(); 6098 keyValues.put("_id", key.getId()); 6099 6100 List<String> excludedTables = List.of(SourceContract.TABLE); 6101 6102 mDatastoreManager.runInTransaction((dao) -> dao.deleteAllMeasurementData(excludedTables)); 6103 6104 for (String table : ALL_MSMT_TABLES) { 6105 if (!excludedTables.contains(table)) { 6106 assertThat( 6107 db.query( 6108 /* table */ table, 6109 /* columns */ null, 6110 /* selection */ null, 6111 /* selectionArgs */ null, 6112 /* groupBy */ null, 6113 /* having */ null, 6114 /* orderedBy */ null) 6115 .getCount()) 6116 .isEqualTo(0); 6117 } else { 6118 assertThat( 6119 db.query( 6120 /* table */ table, 6121 /* columns */ null, 6122 /* selection */ null, 6123 /* selectionArgs */ null, 6124 /* groupBy */ null, 6125 /* having */ null, 6126 /* orderedBy */ null) 6127 .getCount()) 6128 .isNotEqualTo(0); 6129 } 6130 } 6131 } 6132 6133 /** Test that the variable ALL_MSMT_TABLES actually has all the measurement related tables. */ 6134 @Test testAllMsmtTables()6135 public void testAllMsmtTables() { 6136 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 6137 Cursor cursor = 6138 db.query( 6139 "sqlite_master", 6140 /* columns */ null, 6141 /* selection */ "type = ? AND name like ?", 6142 /* selectionArgs*/ new String[] {"table", MSMT_TABLE_PREFIX + "%"}, 6143 /* groupBy */ null, 6144 /* having */ null, 6145 /* orderBy */ null); 6146 6147 List<String> tableNames = new ArrayList<>(); 6148 while (cursor.moveToNext()) { 6149 String tableName = cursor.getString(cursor.getColumnIndex("name")); 6150 tableNames.add(tableName); 6151 } 6152 assertThat(tableNames.size()).isEqualTo(ALL_MSMT_TABLES.length); 6153 for (String tableName : tableNames) { 6154 assertThat(ALL_MSMT_TABLES).asList().contains(tableName); 6155 } 6156 } 6157 6158 @Test insertAttributionRateLimit()6159 public void insertAttributionRateLimit() { 6160 // Setup 6161 Source source = SourceFixture.getValidSource(); 6162 Trigger trigger = 6163 TriggerFixture.getValidTriggerBuilder() 6164 .setTriggerTime(source.getEventTime() + TimeUnit.HOURS.toMillis(1)) 6165 .build(); 6166 Attribution attribution = 6167 new Attribution.Builder() 6168 .setScope(Attribution.Scope.AGGREGATE) 6169 .setEnrollmentId(source.getEnrollmentId()) 6170 .setDestinationOrigin(source.getWebDestinations().get(0).toString()) 6171 .setDestinationSite(source.getAppDestinations().get(0).toString()) 6172 .setSourceOrigin(source.getPublisher().toString()) 6173 .setSourceSite(source.getPublisher().toString()) 6174 .setRegistrant(source.getRegistrant().toString()) 6175 .setTriggerTime(trigger.getTriggerTime()) 6176 .setRegistrationOrigin(trigger.getRegistrationOrigin()) 6177 .setReportId(UUID.randomUUID().toString()) 6178 .build(); 6179 6180 // Execution 6181 mDatastoreManager.runInTransaction( 6182 (dao) -> { 6183 dao.insertAttribution(attribution); 6184 }); 6185 6186 // Assertion 6187 try (Cursor cursor = 6188 MeasurementDbHelper.getInstance() 6189 .getReadableDatabase() 6190 .query(AttributionContract.TABLE, null, null, null, null, null, null)) { 6191 assertTrue(cursor.moveToNext()); 6192 assertEquals( 6193 attribution.getScope(), 6194 cursor.getInt(cursor.getColumnIndex(AttributionContract.SCOPE))); 6195 assertEquals( 6196 attribution.getEnrollmentId(), 6197 cursor.getString(cursor.getColumnIndex(AttributionContract.ENROLLMENT_ID))); 6198 assertEquals( 6199 attribution.getDestinationOrigin(), 6200 cursor.getString( 6201 cursor.getColumnIndex(AttributionContract.DESTINATION_ORIGIN))); 6202 assertEquals( 6203 attribution.getDestinationSite(), 6204 cursor.getString(cursor.getColumnIndex(AttributionContract.DESTINATION_SITE))); 6205 assertEquals( 6206 attribution.getSourceOrigin(), 6207 cursor.getString(cursor.getColumnIndex(AttributionContract.SOURCE_ORIGIN))); 6208 assertEquals( 6209 attribution.getSourceSite(), 6210 cursor.getString(cursor.getColumnIndex(AttributionContract.SOURCE_SITE))); 6211 assertEquals( 6212 attribution.getRegistrant(), 6213 cursor.getString(cursor.getColumnIndex(AttributionContract.REGISTRANT))); 6214 assertEquals( 6215 attribution.getTriggerTime(), 6216 cursor.getLong(cursor.getColumnIndex(AttributionContract.TRIGGER_TIME))); 6217 assertEquals( 6218 attribution.getRegistrationOrigin(), 6219 Uri.parse( 6220 cursor.getString( 6221 cursor.getColumnIndex( 6222 AttributionContract.REGISTRATION_ORIGIN)))); 6223 assertEquals( 6224 attribution.getReportId(), 6225 cursor.getString(cursor.getColumnIndex(AttributionContract.REPORT_ID))); 6226 } 6227 } 6228 6229 @Test getAttributionsPerRateLimitWindow_atTimeWindowScoped_countsAttribution()6230 public void getAttributionsPerRateLimitWindow_atTimeWindowScoped_countsAttribution() { 6231 // Setup 6232 Source source = SourceFixture.getValidSource(); 6233 Trigger trigger = 6234 TriggerFixture.getValidTriggerBuilder() 6235 .setTriggerTime(source.getEventTime() + TimeUnit.HOURS.toMillis(1)) 6236 .build(); 6237 6238 Attribution eventAttribution = 6239 getAttributionBuilder(source, trigger).setScope(Attribution.Scope.EVENT).build(); 6240 6241 Attribution aggregateAttribution = 6242 getAttributionBuilder(source, trigger) 6243 .setScope(Attribution.Scope.AGGREGATE) 6244 .build(); 6245 6246 // Execution 6247 mDatastoreManager.runInTransaction( 6248 (dao) -> { 6249 dao.insertAttribution(eventAttribution); 6250 dao.insertAttribution(aggregateAttribution); 6251 }); 6252 6253 // Assertion 6254 AtomicLong eventAttributionsCount = new AtomicLong(); 6255 AtomicLong aggregateAttributionsCount = new AtomicLong(); 6256 mDatastoreManager.runInTransaction( 6257 (dao) -> { 6258 eventAttributionsCount.set( 6259 dao.getAttributionsPerRateLimitWindow( 6260 Attribution.Scope.EVENT, source, trigger)); 6261 aggregateAttributionsCount.set( 6262 dao.getAttributionsPerRateLimitWindow( 6263 Attribution.Scope.AGGREGATE, source, trigger)); 6264 }); 6265 6266 assertEquals(1L, eventAttributionsCount.get()); 6267 assertEquals(1L, aggregateAttributionsCount.get()); 6268 } 6269 6270 @Test getAttributionsPerRateLimitWindow_beyondTimeWindowScoped_countsAttribution()6271 public void getAttributionsPerRateLimitWindow_beyondTimeWindowScoped_countsAttribution() { 6272 // Setup 6273 Source source = SourceFixture.getValidSource(); 6274 Trigger trigger = 6275 TriggerFixture.getValidTriggerBuilder() 6276 .setTriggerTime(source.getEventTime() + TimeUnit.HOURS.toMillis(1)) 6277 .build(); 6278 6279 Attribution eventAttribution = 6280 getAttributionBuilder(source, trigger) 6281 .setTriggerTime( 6282 trigger.getTriggerTime() 6283 - MEASUREMENT_RATE_LIMIT_WINDOW_MILLISECONDS) 6284 .setScope(Attribution.Scope.EVENT) 6285 .build(); 6286 6287 Attribution aggregateAttribution = 6288 getAttributionBuilder(source, trigger) 6289 .setTriggerTime( 6290 trigger.getTriggerTime() 6291 - MEASUREMENT_RATE_LIMIT_WINDOW_MILLISECONDS) 6292 .setScope(Attribution.Scope.AGGREGATE) 6293 .build(); 6294 6295 // Execution 6296 mDatastoreManager.runInTransaction( 6297 (dao) -> { 6298 dao.insertAttribution(eventAttribution); 6299 dao.insertAttribution(aggregateAttribution); 6300 }); 6301 6302 // Assertion 6303 AtomicLong eventAttributionsCount = new AtomicLong(); 6304 AtomicLong aggregateAttributionsCount = new AtomicLong(); 6305 mDatastoreManager.runInTransaction( 6306 (dao) -> { 6307 eventAttributionsCount.set( 6308 dao.getAttributionsPerRateLimitWindow( 6309 Attribution.Scope.EVENT, source, trigger)); 6310 aggregateAttributionsCount.set( 6311 dao.getAttributionsPerRateLimitWindow( 6312 Attribution.Scope.AGGREGATE, source, trigger)); 6313 }); 6314 6315 assertEquals(0L, eventAttributionsCount.get()); 6316 assertEquals(0L, aggregateAttributionsCount.get()); 6317 } 6318 6319 @Test testTransactionRollbackForRuntimeException()6320 public void testTransactionRollbackForRuntimeException() { 6321 assertThrows( 6322 IllegalArgumentException.class, 6323 () -> 6324 mDatastoreManager.runInTransaction( 6325 (dao) -> { 6326 dao.insertSource(SourceFixture.getValidSource()); 6327 // build() call throws IllegalArgumentException 6328 Trigger trigger = new Trigger.Builder().build(); 6329 dao.insertTrigger(trigger); 6330 })); 6331 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 6332 Objects.requireNonNull(db); 6333 // There should be no insertions 6334 assertEquals( 6335 0, db.query(SourceContract.TABLE, null, null, null, null, null, null).getCount()); 6336 assertEquals( 6337 0, db.query(TriggerContract.TABLE, null, null, null, null, null, null).getCount()); 6338 } 6339 6340 @Test testDeleteEventReportAndAttribution()6341 public void testDeleteEventReportAndAttribution() throws Exception { 6342 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 6343 Source s1 = 6344 SourceFixture.getMinimalValidSourceBuilder() 6345 .setEventId(new UnsignedLong(1L)) 6346 .setId("S1") 6347 .build(); 6348 Trigger t1 = 6349 TriggerFixture.getValidTriggerBuilder() 6350 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 6351 .setId("T1") 6352 .build(); 6353 Trigger t2 = 6354 TriggerFixture.getValidTriggerBuilder() 6355 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 6356 .setId("T2") 6357 .build(); 6358 EventReport e11 = createEventReportForSourceAndTrigger("E11", s1, t1); 6359 EventReport e12 = createEventReportForSourceAndTrigger("E12", s1, t2); 6360 6361 Attribution aggregateAttribution11 = 6362 createAttribution( 6363 "ATT11_aggregate", 6364 Attribution.Scope.AGGREGATE, 6365 s1.getId(), 6366 t1.getId(), 6367 "E11"); 6368 Attribution aggregateAttribution12 = 6369 createAttribution( 6370 "ATT12_aggregate", 6371 Attribution.Scope.AGGREGATE, 6372 s1.getId(), 6373 t2.getId(), 6374 "E12"); 6375 Attribution eventAttribution11 = 6376 createAttribution( 6377 "ATT11_event", 6378 Attribution.Scope.EVENT, 6379 s1.getId(), 6380 t1.getId(), 6381 "E11"); 6382 Attribution eventAttribution12 = 6383 createAttribution( 6384 "ATT12_event", 6385 Attribution.Scope.EVENT, 6386 s1.getId(), 6387 t2.getId(), 6388 "E12"); 6389 6390 insertSource(s1, s1.getId()); 6391 AbstractDbIntegrationTest.insertToDb(t1, db); 6392 AbstractDbIntegrationTest.insertToDb(t2, db); 6393 AbstractDbIntegrationTest.insertToDb(e11, db); 6394 AbstractDbIntegrationTest.insertToDb(e12, db); 6395 AbstractDbIntegrationTest.insertToDb(aggregateAttribution11, db); 6396 AbstractDbIntegrationTest.insertToDb(aggregateAttribution12, db); 6397 AbstractDbIntegrationTest.insertToDb(eventAttribution11, db); 6398 AbstractDbIntegrationTest.insertToDb(eventAttribution12, db); 6399 6400 // Assert attributions present 6401 assertNotNull(getAttribution("ATT11_aggregate", db)); 6402 assertNotNull(getAttribution("ATT12_aggregate", db)); 6403 assertNotNull(getAttribution("ATT11_event", db)); 6404 assertNotNull(getAttribution("ATT12_event", db)); 6405 6406 mDatastoreManager.runInTransaction( 6407 measurementDao -> { 6408 // Assert sources and triggers present 6409 assertNotNull(measurementDao.getSource("S1")); 6410 assertNotNull(measurementDao.getTrigger("T1")); 6411 assertNotNull(measurementDao.getTrigger("T2")); 6412 6413 // Validate presence of event reports 6414 measurementDao.getEventReport("E11"); 6415 measurementDao.getEventReport("E12"); 6416 6417 // Deletion 6418 measurementDao.deleteEventReportAndAttribution(e11); 6419 6420 // Validate event report deletion 6421 assertThrows( 6422 DatastoreException.class, 6423 () -> { 6424 measurementDao.getEventReport("E11"); 6425 }); 6426 assertNotNull(measurementDao.getEventReport("E12")); 6427 6428 // Validate sources and triggers present 6429 assertNotNull(measurementDao.getSource("S1")); 6430 assertNotNull(measurementDao.getTrigger("T1")); 6431 assertNotNull(measurementDao.getTrigger("T2")); 6432 }); 6433 6434 // Validate attribution deletion 6435 assertNotNull(getAttribution("ATT11_aggregate", db)); 6436 assertNotNull(getAttribution("ATT12_aggregate", db)); 6437 assertNull(getAttribution("ATT11_event", db)); 6438 assertNotNull(getAttribution("ATT12_event", db)); 6439 } 6440 6441 @Test testDeleteDebugReport()6442 public void testDeleteDebugReport() { 6443 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 6444 DebugReport debugReport = createDebugReport(); 6445 6446 ContentValues values = new ContentValues(); 6447 values.put(DebugReportContract.ID, debugReport.getId()); 6448 values.put(DebugReportContract.TYPE, debugReport.getType()); 6449 values.put(DebugReportContract.BODY, debugReport.getBody().toString()); 6450 values.put(DebugReportContract.ENROLLMENT_ID, debugReport.getEnrollmentId()); 6451 values.put( 6452 DebugReportContract.REGISTRATION_ORIGIN, 6453 debugReport.getRegistrationOrigin().toString()); 6454 db.insert(DebugReportContract.TABLE, null, values); 6455 6456 long count = 6457 DatabaseUtils.queryNumEntries(db, DebugReportContract.TABLE, /* selection */ null); 6458 assertEquals(1, count); 6459 6460 assertTrue( 6461 mDatastoreManager.runInTransaction( 6462 measurementDao -> measurementDao.deleteDebugReport(debugReport.getId()))); 6463 6464 count = DatabaseUtils.queryNumEntries(db, DebugReportContract.TABLE, /* selection */ null); 6465 assertEquals(0, count); 6466 } 6467 6468 @Test testDeleteDebugReports_byRegistrantAndRange()6469 public void testDeleteDebugReports_byRegistrantAndRange() { 6470 final String registrantMatching = "foo"; 6471 final long insertionTimeWithinRange = 1701206853050L; 6472 final long insertionTimeNotWithinRange = 1701206853000L; 6473 DebugReport debugReportMatchingRegistrantAndWithinRange = 6474 createDebugReport( 6475 /* id= */ "1", 6476 buildRegistrant(registrantMatching), 6477 insertionTimeWithinRange); 6478 6479 DebugReport debugReportNotMatchingRegistrantAndWithinRange = 6480 createDebugReport(/* id= */ "2", buildRegistrant("bar"), insertionTimeWithinRange); 6481 6482 DebugReport debugReportMatchingRegistrantAndNotWithinRange = 6483 createDebugReport( 6484 /* id= */ "3", 6485 buildRegistrant(registrantMatching), 6486 insertionTimeNotWithinRange); 6487 6488 mDatastoreManager.runInTransaction( 6489 (dao) -> { 6490 dao.insertDebugReport(debugReportMatchingRegistrantAndWithinRange); 6491 dao.insertDebugReport(debugReportNotMatchingRegistrantAndWithinRange); 6492 dao.insertDebugReport(debugReportMatchingRegistrantAndNotWithinRange); 6493 }); 6494 6495 mDatastoreManager.runInTransaction( 6496 (dao) -> 6497 dao.deleteDebugReports( 6498 buildRegistrant(registrantMatching), 6499 /* start= */ Instant.ofEpochMilli(insertionTimeWithinRange - 1), 6500 /* end= */ Instant.ofEpochMilli(insertionTimeWithinRange + 1))); 6501 6502 Set<String> ids = new HashSet<>(); 6503 try (Cursor cursor = 6504 MeasurementDbHelper.getInstance() 6505 .getReadableDatabase() 6506 .query(DebugReportContract.TABLE, null, null, null, null, null, null)) { 6507 while (cursor.moveToNext()) { 6508 ids.add(cursor.getString(cursor.getColumnIndexOrThrow(DebugReportContract.ID))); 6509 } 6510 } 6511 assertEquals(2, ids.size()); 6512 assertTrue(ids.contains("2")); 6513 assertTrue(ids.contains("3")); 6514 } 6515 6516 @Test testGetDebugReportIds()6517 public void testGetDebugReportIds() { 6518 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 6519 DebugReport debugReport = createDebugReport(); 6520 6521 ContentValues values = new ContentValues(); 6522 values.put(DebugReportContract.ID, debugReport.getId()); 6523 values.put(DebugReportContract.TYPE, debugReport.getType()); 6524 values.put(DebugReportContract.BODY, debugReport.getBody().toString()); 6525 values.put(DebugReportContract.ENROLLMENT_ID, debugReport.getEnrollmentId()); 6526 values.put( 6527 DebugReportContract.REGISTRATION_ORIGIN, 6528 debugReport.getRegistrationOrigin().toString()); 6529 db.insert(DebugReportContract.TABLE, null, values); 6530 6531 long count = 6532 DatabaseUtils.queryNumEntries(db, DebugReportContract.TABLE, /* selection */ null); 6533 assertEquals(1, count); 6534 6535 assertTrue( 6536 mDatastoreManager.runInTransaction( 6537 measurementDao -> 6538 assertEquals( 6539 List.of(debugReport.getId()), 6540 measurementDao.getDebugReportIds()))); 6541 } 6542 6543 @Test testGetDebugReportIdsWithRetryLimit()6544 public void testGetDebugReportIdsWithRetryLimit() { 6545 // Mocking that the flags return a Max Retry of 1 6546 Flags mockFlags = Mockito.mock(Flags.class); 6547 ExtendedMockito.doReturn(mockFlags).when(FlagsFactory::getFlags); 6548 ExtendedMockito.doReturn(1).when(mockFlags).getMeasurementReportingRetryLimit(); 6549 ExtendedMockito.doReturn(true).when(mockFlags).getMeasurementReportingRetryLimitEnabled(); 6550 6551 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 6552 DebugReport debugReport = createDebugReport(); 6553 6554 ContentValues values = new ContentValues(); 6555 values.put(DebugReportContract.ID, debugReport.getId()); 6556 values.put(DebugReportContract.TYPE, debugReport.getType()); 6557 values.put(DebugReportContract.BODY, debugReport.getBody().toString()); 6558 values.put(DebugReportContract.ENROLLMENT_ID, debugReport.getEnrollmentId()); 6559 values.put( 6560 DebugReportContract.REGISTRATION_ORIGIN, 6561 debugReport.getRegistrationOrigin().toString()); 6562 db.insert(DebugReportContract.TABLE, null, values); 6563 6564 long count = 6565 DatabaseUtils.queryNumEntries(db, DebugReportContract.TABLE, /* selection */ null); 6566 assertEquals(1, count); 6567 6568 assertTrue( 6569 mDatastoreManager.runInTransaction( 6570 measurementDao -> 6571 assertEquals( 6572 List.of(debugReport.getId()), 6573 measurementDao.getDebugReportIds()))); 6574 assertTrue( 6575 mDatastoreManager.runInTransaction( 6576 measurementDao -> { 6577 // Adds records to KeyValueData table for Retry Count. 6578 measurementDao.incrementAndGetReportingRetryCount( 6579 debugReport.getId(), DataType.DEBUG_REPORT_RETRY_COUNT); 6580 assertTrue(measurementDao.getDebugReportIds().isEmpty()); 6581 })); 6582 } 6583 6584 @Test testDeleteExpiredRecordsForAsyncRegistrations()6585 public void testDeleteExpiredRecordsForAsyncRegistrations() { 6586 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 6587 6588 List<AsyncRegistration> asyncRegistrationList = new ArrayList<>(); 6589 int retryLimit = Flags.MEASUREMENT_MAX_RETRIES_PER_REGISTRATION_REQUEST; 6590 6591 // Will be deleted by request time 6592 asyncRegistrationList.add( 6593 new AsyncRegistration.Builder() 6594 .setId("1") 6595 .setOsDestination(Uri.parse("android-app://installed-app-destination")) 6596 .setRegistrant(INSTALLED_REGISTRANT) 6597 .setTopOrigin(INSTALLED_REGISTRANT) 6598 .setAdIdPermission(false) 6599 .setType(AsyncRegistration.RegistrationType.APP_SOURCE) 6600 .setRequestTime(1) 6601 .setRetryCount(retryLimit - 1L) 6602 .setRegistrationId(UUID.randomUUID().toString()) 6603 .build()); 6604 6605 // Will be deleted by either request time or retry limit 6606 asyncRegistrationList.add( 6607 new AsyncRegistration.Builder() 6608 .setId("2") 6609 .setOsDestination(Uri.parse("android-app://installed-app-destination")) 6610 .setRegistrant(INSTALLED_REGISTRANT) 6611 .setTopOrigin(INSTALLED_REGISTRANT) 6612 .setAdIdPermission(false) 6613 .setType(AsyncRegistration.RegistrationType.APP_SOURCE) 6614 .setRequestTime(1) 6615 .setRetryCount(retryLimit) 6616 .setRegistrationId(UUID.randomUUID().toString()) 6617 .build()); 6618 6619 // Will not be deleted 6620 asyncRegistrationList.add( 6621 new AsyncRegistration.Builder() 6622 .setId("3") 6623 .setOsDestination(Uri.parse("android-app://not-installed-app-destination")) 6624 .setRegistrant(INSTALLED_REGISTRANT) 6625 .setTopOrigin(INSTALLED_REGISTRANT) 6626 .setAdIdPermission(false) 6627 .setType(AsyncRegistration.RegistrationType.APP_SOURCE) 6628 .setRequestTime(Long.MAX_VALUE) 6629 .setRetryCount(retryLimit - 1L) 6630 .setRegistrationId(UUID.randomUUID().toString()) 6631 .build()); 6632 6633 // Will be deleted due to retry limit 6634 asyncRegistrationList.add( 6635 new AsyncRegistration.Builder() 6636 .setId("4") 6637 .setOsDestination(Uri.parse("android-app://not-installed-app-destination")) 6638 .setRegistrant(INSTALLED_REGISTRANT) 6639 .setTopOrigin(INSTALLED_REGISTRANT) 6640 .setAdIdPermission(false) 6641 .setType(AsyncRegistration.RegistrationType.APP_SOURCE) 6642 .setRequestTime(Long.MAX_VALUE) 6643 .setRetryCount(retryLimit) 6644 .setRegistrationId(UUID.randomUUID().toString()) 6645 .build()); 6646 6647 asyncRegistrationList.forEach( 6648 asyncRegistration -> { 6649 ContentValues values = new ContentValues(); 6650 values.put(AsyncRegistrationContract.ID, asyncRegistration.getId()); 6651 values.put( 6652 AsyncRegistrationContract.REGISTRANT, 6653 asyncRegistration.getRegistrant().toString()); 6654 values.put( 6655 AsyncRegistrationContract.TOP_ORIGIN, 6656 asyncRegistration.getTopOrigin().toString()); 6657 values.put( 6658 AsyncRegistrationContract.OS_DESTINATION, 6659 asyncRegistration.getOsDestination().toString()); 6660 values.put( 6661 AsyncRegistrationContract.AD_ID_PERMISSION, 6662 asyncRegistration.getDebugKeyAllowed()); 6663 values.put( 6664 AsyncRegistrationContract.TYPE, asyncRegistration.getType().toString()); 6665 values.put( 6666 AsyncRegistrationContract.REQUEST_TIME, 6667 asyncRegistration.getRequestTime()); 6668 values.put( 6669 AsyncRegistrationContract.RETRY_COUNT, 6670 asyncRegistration.getRetryCount()); 6671 values.put( 6672 AsyncRegistrationContract.REGISTRATION_ID, 6673 asyncRegistration.getRegistrationId()); 6674 db.insert(AsyncRegistrationContract.TABLE, /* nullColumnHack */ null, values); 6675 }); 6676 6677 long count = 6678 DatabaseUtils.queryNumEntries( 6679 db, AsyncRegistrationContract.TABLE, /* selection */ null); 6680 assertEquals(4, count); 6681 6682 long earliestValidInsertion = System.currentTimeMillis() - 2; 6683 6684 assertTrue( 6685 mDatastoreManager.runInTransaction( 6686 measurementDao -> 6687 measurementDao.deleteExpiredRecords( 6688 earliestValidInsertion, 6689 retryLimit, 6690 /* earliestValidAppReportInsertion */ null, 6691 /* earliestValidAggregateDebugReportInsertion */ 0))); 6692 6693 count = 6694 DatabaseUtils.queryNumEntries( 6695 db, AsyncRegistrationContract.TABLE, /* selection */ null); 6696 assertEquals(1, count); 6697 6698 Cursor cursor = 6699 db.query( 6700 AsyncRegistrationContract.TABLE, 6701 /* columns */ null, 6702 /* selection */ null, 6703 /* selectionArgs */ null, 6704 /* groupBy */ null, 6705 /* having */ null, 6706 /* orderBy */ null); 6707 6708 Set<String> ids = new HashSet<>(Arrays.asList("3")); 6709 List<AsyncRegistration> asyncRegistrations = new ArrayList<>(); 6710 while (cursor.moveToNext()) { 6711 AsyncRegistration asyncRegistration = 6712 SqliteObjectMapper.constructAsyncRegistration(cursor); 6713 asyncRegistrations.add(asyncRegistration); 6714 } 6715 for (AsyncRegistration asyncRegistration : asyncRegistrations) { 6716 assertTrue(ids.contains(asyncRegistration.getId())); 6717 } 6718 } 6719 6720 @Test deleteExpiredRecords_registrationRedirectCount()6721 public void deleteExpiredRecords_registrationRedirectCount() { 6722 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 6723 List<Pair<String, String>> regIdCounts = 6724 List.of( 6725 new Pair<>("reg1", "1"), 6726 new Pair<>("reg2", "2"), 6727 new Pair<>("reg3", "3"), 6728 new Pair<>("reg4", "4")); 6729 for (Pair<String, String> regIdCount : regIdCounts) { 6730 ContentValues contentValues = new ContentValues(); 6731 contentValues.put( 6732 KeyValueDataContract.DATA_TYPE, 6733 KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT.toString()); 6734 contentValues.put(KeyValueDataContract.KEY, regIdCount.first); 6735 contentValues.put(KeyValueDataContract.VALUE, regIdCount.second); 6736 db.insert(KeyValueDataContract.TABLE, null, contentValues); 6737 } 6738 AsyncRegistration asyncRegistration1 = 6739 AsyncRegistrationFixture.getValidAsyncRegistrationBuilder() 6740 .setRegistrationId("reg1") 6741 .setRequestTime(System.currentTimeMillis() + 60000) // Avoid deletion 6742 .build(); 6743 AsyncRegistration asyncRegistration2 = 6744 AsyncRegistrationFixture.getValidAsyncRegistrationBuilder() 6745 .setRegistrationId("reg2") 6746 .setRequestTime(System.currentTimeMillis() + 60000) // Avoid deletion 6747 .build(); 6748 List<AsyncRegistration> asyncRegistrations = 6749 List.of(asyncRegistration1, asyncRegistration2); 6750 asyncRegistrations.forEach( 6751 asyncRegistration -> 6752 mDatastoreManager.runInTransaction( 6753 dao -> dao.insertAsyncRegistration(asyncRegistration))); 6754 6755 long earliestValidInsertion = System.currentTimeMillis() - 60000; 6756 int retryLimit = Flags.MEASUREMENT_MAX_RETRIES_PER_REGISTRATION_REQUEST; 6757 assertTrue( 6758 mDatastoreManager.runInTransaction( 6759 (dao) -> 6760 dao.deleteExpiredRecords( 6761 earliestValidInsertion, 6762 retryLimit, 6763 /* earliestValidAggregateDebugReportInsertion */ null, 6764 /* earliestValidAggregateDebugReportInsertion */ 0))); 6765 6766 Cursor cursor = 6767 db.query( 6768 KeyValueDataContract.TABLE, 6769 null, 6770 null, 6771 null, 6772 null, 6773 null, 6774 KeyValueDataContract.KEY); 6775 assertEquals(2, cursor.getCount()); 6776 cursor.moveToNext(); 6777 assertEquals( 6778 KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT.toString(), 6779 cursor.getString(cursor.getColumnIndex(KeyValueDataContract.DATA_TYPE))); 6780 assertEquals("reg1", cursor.getString(cursor.getColumnIndex(KeyValueDataContract.KEY))); 6781 assertEquals("1", cursor.getString(cursor.getColumnIndex(KeyValueDataContract.VALUE))); 6782 cursor.moveToNext(); 6783 assertEquals( 6784 KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT.toString(), 6785 cursor.getString(cursor.getColumnIndex(KeyValueDataContract.DATA_TYPE))); 6786 assertEquals("reg2", cursor.getString(cursor.getColumnIndex(KeyValueDataContract.KEY))); 6787 assertEquals("2", cursor.getString(cursor.getColumnIndex(KeyValueDataContract.VALUE))); 6788 cursor.close(); 6789 } 6790 6791 @Test deleteExpiredRecords_skipDeliveredEventReportsOutsideWindow()6792 public void deleteExpiredRecords_skipDeliveredEventReportsOutsideWindow() { 6793 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 6794 ContentValues sourceValid = new ContentValues(); 6795 sourceValid.put(SourceContract.ID, "s1"); 6796 sourceValid.put(SourceContract.EVENT_TIME, System.currentTimeMillis()); 6797 6798 ContentValues sourceExpired = new ContentValues(); 6799 sourceExpired.put(SourceContract.ID, "s2"); 6800 sourceExpired.put( 6801 SourceContract.EVENT_TIME, System.currentTimeMillis() - DAYS.toMillis(20)); 6802 6803 ContentValues triggerValid = new ContentValues(); 6804 triggerValid.put(TriggerContract.ID, "t1"); 6805 triggerValid.put(TriggerContract.TRIGGER_TIME, System.currentTimeMillis()); 6806 6807 ContentValues triggerExpired = new ContentValues(); 6808 triggerExpired.put(TriggerContract.ID, "t2"); 6809 triggerExpired.put( 6810 TriggerContract.TRIGGER_TIME, System.currentTimeMillis() - DAYS.toMillis(20)); 6811 6812 db.insert(SourceContract.TABLE, null, sourceValid); 6813 db.insert(SourceContract.TABLE, null, sourceExpired); 6814 db.insert(TriggerContract.TABLE, null, triggerValid); 6815 db.insert(TriggerContract.TABLE, null, triggerExpired); 6816 6817 ContentValues eventReport_NotDelivered_WithinWindow = new ContentValues(); 6818 eventReport_NotDelivered_WithinWindow.put(EventReportContract.ID, "e1"); 6819 eventReport_NotDelivered_WithinWindow.put( 6820 EventReportContract.REPORT_TIME, System.currentTimeMillis()); 6821 eventReport_NotDelivered_WithinWindow.put( 6822 EventReportContract.STATUS, EventReport.Status.PENDING); 6823 eventReport_NotDelivered_WithinWindow.put( 6824 EventReportContract.SOURCE_ID, sourceValid.getAsString(SourceContract.ID)); 6825 eventReport_NotDelivered_WithinWindow.put( 6826 EventReportContract.TRIGGER_ID, triggerValid.getAsString(TriggerContract.ID)); 6827 db.insert(EventReportContract.TABLE, null, eventReport_NotDelivered_WithinWindow); 6828 6829 ContentValues eventReport_Delivered_WithinWindow = new ContentValues(); 6830 eventReport_Delivered_WithinWindow.put(EventReportContract.ID, "e2"); 6831 eventReport_Delivered_WithinWindow.put( 6832 EventReportContract.REPORT_TIME, System.currentTimeMillis()); 6833 eventReport_Delivered_WithinWindow.put( 6834 EventReportContract.STATUS, EventReport.Status.DELIVERED); 6835 eventReport_Delivered_WithinWindow.put( 6836 EventReportContract.SOURCE_ID, sourceValid.getAsString(SourceContract.ID)); 6837 eventReport_Delivered_WithinWindow.put( 6838 EventReportContract.TRIGGER_ID, triggerValid.getAsString(TriggerContract.ID)); 6839 db.insert(EventReportContract.TABLE, null, eventReport_Delivered_WithinWindow); 6840 6841 ContentValues eventReport_Delivered_OutsideWindow = new ContentValues(); 6842 eventReport_Delivered_OutsideWindow.put(EventReportContract.ID, "e3"); 6843 eventReport_Delivered_OutsideWindow.put( 6844 EventReportContract.REPORT_TIME, System.currentTimeMillis() - DAYS.toMillis(20)); 6845 eventReport_Delivered_OutsideWindow.put( 6846 EventReportContract.STATUS, EventReport.Status.DELIVERED); 6847 eventReport_Delivered_OutsideWindow.put( 6848 EventReportContract.SOURCE_ID, sourceValid.getAsString(SourceContract.ID)); 6849 eventReport_Delivered_OutsideWindow.put( 6850 EventReportContract.TRIGGER_ID, triggerValid.getAsString(TriggerContract.ID)); 6851 db.insert(EventReportContract.TABLE, null, eventReport_Delivered_OutsideWindow); 6852 6853 ContentValues eventReport_NotDelivered_OutsideWindow = new ContentValues(); 6854 eventReport_NotDelivered_OutsideWindow.put(EventReportContract.ID, "e4"); 6855 eventReport_NotDelivered_OutsideWindow.put( 6856 EventReportContract.REPORT_TIME, System.currentTimeMillis() - DAYS.toMillis(20)); 6857 eventReport_NotDelivered_OutsideWindow.put( 6858 EventReportContract.STATUS, EventReport.Status.PENDING); 6859 eventReport_NotDelivered_OutsideWindow.put( 6860 EventReportContract.SOURCE_ID, sourceValid.getAsString(SourceContract.ID)); 6861 eventReport_NotDelivered_OutsideWindow.put( 6862 EventReportContract.TRIGGER_ID, triggerValid.getAsString(TriggerContract.ID)); 6863 db.insert(EventReportContract.TABLE, null, eventReport_NotDelivered_OutsideWindow); 6864 6865 ContentValues eventReport_expiredSource = new ContentValues(); 6866 eventReport_expiredSource.put(EventReportContract.ID, "e5"); 6867 eventReport_expiredSource.put(EventReportContract.REPORT_TIME, System.currentTimeMillis()); 6868 eventReport_expiredSource.put(EventReportContract.STATUS, EventReport.Status.PENDING); 6869 eventReport_expiredSource.put( 6870 EventReportContract.SOURCE_ID, sourceExpired.getAsString(SourceContract.ID)); 6871 eventReport_expiredSource.put( 6872 EventReportContract.TRIGGER_ID, triggerValid.getAsString(TriggerContract.ID)); 6873 db.insert(EventReportContract.TABLE, null, eventReport_expiredSource); 6874 6875 ContentValues eventReport_expiredTrigger = new ContentValues(); 6876 eventReport_expiredTrigger.put(EventReportContract.ID, "e6"); 6877 eventReport_expiredTrigger.put(EventReportContract.REPORT_TIME, System.currentTimeMillis()); 6878 eventReport_expiredTrigger.put(EventReportContract.STATUS, EventReport.Status.PENDING); 6879 eventReport_expiredTrigger.put( 6880 EventReportContract.SOURCE_ID, sourceValid.getAsString(SourceContract.ID)); 6881 eventReport_expiredTrigger.put( 6882 EventReportContract.TRIGGER_ID, triggerExpired.getAsString(TriggerContract.ID)); 6883 db.insert(EventReportContract.TABLE, null, eventReport_expiredTrigger); 6884 6885 long earliestValidInsertion = System.currentTimeMillis() - DAYS.toMillis(10); 6886 int retryLimit = Flags.MEASUREMENT_MAX_RETRIES_PER_REGISTRATION_REQUEST; 6887 mDatastoreManager.runInTransaction( 6888 measurementDao -> 6889 measurementDao.deleteExpiredRecords( 6890 earliestValidInsertion, 6891 retryLimit, 6892 /* earliestValidAppReportInsertion */ null, 6893 /* earliestValidAggregateDebugReportInsertion */ 0)); 6894 6895 List<ContentValues> deletedReports = 6896 List.of(eventReport_expiredSource, eventReport_expiredTrigger); 6897 6898 List<ContentValues> notDeletedReports = 6899 List.of( 6900 eventReport_Delivered_OutsideWindow, 6901 eventReport_Delivered_WithinWindow, 6902 eventReport_NotDelivered_OutsideWindow, 6903 eventReport_NotDelivered_WithinWindow); 6904 6905 assertEquals( 6906 notDeletedReports.size(), 6907 DatabaseUtils.longForQuery( 6908 db, 6909 "SELECT COUNT(" 6910 + EventReportContract.ID 6911 + ") FROM " 6912 + EventReportContract.TABLE 6913 + " WHERE " 6914 + EventReportContract.ID 6915 + " IN (" 6916 + notDeletedReports.stream() 6917 .map( 6918 (eR) -> { 6919 return DatabaseUtils.sqlEscapeString( 6920 eR.getAsString(EventReportContract.ID)); 6921 }) 6922 .collect(Collectors.joining(",")) 6923 + ")", 6924 null)); 6925 6926 assertEquals( 6927 0, 6928 DatabaseUtils.longForQuery( 6929 db, 6930 "SELECT COUNT(" 6931 + EventReportContract.ID 6932 + ") FROM " 6933 + EventReportContract.TABLE 6934 + " WHERE " 6935 + EventReportContract.ID 6936 + " IN (" 6937 + deletedReports.stream() 6938 .map( 6939 (eR) -> { 6940 return DatabaseUtils.sqlEscapeString( 6941 eR.getAsString(EventReportContract.ID)); 6942 }) 6943 .collect(Collectors.joining(",")) 6944 + ")", 6945 null)); 6946 } 6947 6948 @Test deleteExpiredRecords_VerboseDebugReportsWhileLimitingRetries()6949 public void deleteExpiredRecords_VerboseDebugReportsWhileLimitingRetries() { 6950 // Mocking that the flags return a Max Retry of 1 6951 Flags mockFlags = Mockito.mock(Flags.class); 6952 ExtendedMockito.doReturn(mockFlags).when(FlagsFactory::getFlags); 6953 ExtendedMockito.doReturn(1).when(mockFlags).getMeasurementReportingRetryLimit(); 6954 ExtendedMockito.doReturn(true).when(mockFlags).getMeasurementReportingRetryLimitEnabled(); 6955 6956 SQLiteDatabase db = MeasurementDbHelper.getInstance().getReadableDatabase(); 6957 6958 DebugReport debugReport1 = 6959 new DebugReport.Builder() 6960 .setId("reportId1") 6961 .setType("trigger-event-deduplicated") 6962 .setBody( 6963 " {\n" 6964 + " \"attribution_destination\":" 6965 + " \"https://destination.example\",\n" 6966 + " \"source_event_id\": \"45623\"\n" 6967 + " }") 6968 .setEnrollmentId("1") 6969 .setRegistrationOrigin(REGISTRATION_ORIGIN) 6970 .build(); 6971 6972 DebugReport debugReport2 = 6973 new DebugReport.Builder() 6974 .setId("reportId2") 6975 .setType("trigger-event-deduplicated") 6976 .setBody( 6977 " {\n" 6978 + " \"attribution_destination\":" 6979 + " \"https://destination.example\",\n" 6980 + " \"source_event_id\": \"45623\"\n" 6981 + " }") 6982 .setEnrollmentId("1") 6983 .setRegistrationOrigin(REGISTRATION_ORIGIN) 6984 .build(); 6985 6986 mDatastoreManager.runInTransaction((dao) -> dao.insertDebugReport(debugReport1)); 6987 mDatastoreManager.runInTransaction((dao) -> dao.insertDebugReport(debugReport2)); 6988 6989 mDatastoreManager.runInTransaction( 6990 dao -> 6991 dao.deleteExpiredRecords( 6992 /* earliestValidInsertion */ 0, 6993 /* registrationRetryLimit */ 0, 6994 /* earliestValidAppReportInsertion */ null, 6995 /* earliestValidAggregateDebugReportInsertion */ 0)); 6996 assertEquals( 6997 2, 6998 DatabaseUtils.longForQuery( 6999 db, 7000 "SELECT COUNT(" 7001 + DebugReportContract.ID 7002 + ") FROM " 7003 + DebugReportContract.TABLE, 7004 null)); 7005 // Increment Attempt Record 1 7006 mDatastoreManager.runInTransaction( 7007 (dao) -> 7008 dao.incrementAndGetReportingRetryCount( 7009 debugReport1.getId(), DataType.DEBUG_REPORT_RETRY_COUNT)); 7010 // Delete Expired (Record 1) 7011 mDatastoreManager.runInTransaction( 7012 dao -> 7013 dao.deleteExpiredRecords( 7014 /* earliestValidInsertion */ 0, 7015 /* registrationRetryLimit */ 0, 7016 /* earliestValidAppReportInsertion */ null, 7017 /* earliestValidAggregateDebugReportInsertion */ 0)); 7018 7019 // Assert Record 2 remains. 7020 assertEquals( 7021 1, 7022 DatabaseUtils.longForQuery( 7023 db, 7024 "SELECT COUNT(" 7025 + DebugReportContract.ID 7026 + ") FROM " 7027 + DebugReportContract.TABLE 7028 + " WHERE " 7029 + DebugReportContract.ID 7030 + " = ?", 7031 new String[] {debugReport2.getId()})); 7032 7033 // Assert Record 1 Removed 7034 assertEquals( 7035 0, 7036 DatabaseUtils.longForQuery( 7037 db, 7038 "SELECT COUNT(" 7039 + DebugReportContract.ID 7040 + ") FROM " 7041 + DebugReportContract.TABLE 7042 + " WHERE " 7043 + DebugReportContract.ID 7044 + " = ?", 7045 new String[] {debugReport1.getId()})); 7046 } 7047 7048 @Test deleteExpiredRecords_VerboseDebugReportsWhileNotLimitingRetries()7049 public void deleteExpiredRecords_VerboseDebugReportsWhileNotLimitingRetries() { 7050 // Mocking that the retry Limiting Disable, but has limit number, 7051 Flags mockFlags = Mockito.mock(Flags.class); 7052 ExtendedMockito.doReturn(mockFlags).when(FlagsFactory::getFlags); 7053 ExtendedMockito.doReturn(1).when(mockFlags).getMeasurementReportingRetryLimit(); 7054 ExtendedMockito.doReturn(false).when(mockFlags).getMeasurementReportingRetryLimitEnabled(); 7055 7056 SQLiteDatabase db = MeasurementDbHelper.getInstance().getReadableDatabase(); 7057 7058 DebugReport debugReport1 = 7059 new DebugReport.Builder() 7060 .setId("reportId1") 7061 .setType("trigger-event-deduplicated") 7062 .setBody( 7063 " {\n" 7064 + " \"attribution_destination\":" 7065 + " \"https://destination.example\",\n" 7066 + " \"source_event_id\": \"45623\"\n" 7067 + " }") 7068 .setEnrollmentId("1") 7069 .setRegistrationOrigin(REGISTRATION_ORIGIN) 7070 .setInsertionTime(System.currentTimeMillis() + 60000L) 7071 .build(); 7072 7073 DebugReport debugReport2 = 7074 new DebugReport.Builder() 7075 .setId("reportId2") 7076 .setType("trigger-event-deduplicated") 7077 .setBody( 7078 " {\n" 7079 + " \"attribution_destination\":" 7080 + " \"https://destination.example\",\n" 7081 + " \"source_event_id\": \"45623\"\n" 7082 + " }") 7083 .setEnrollmentId("1") 7084 .setRegistrationOrigin(REGISTRATION_ORIGIN) 7085 .setInsertionTime(System.currentTimeMillis() - 60000L) 7086 .build(); 7087 // Insert 7088 mDatastoreManager.runInTransaction((dao) -> dao.insertDebugReport(debugReport1)); 7089 mDatastoreManager.runInTransaction((dao) -> dao.insertDebugReport(debugReport2)); 7090 7091 // Increment Attempt 7092 mDatastoreManager.runInTransaction( 7093 (dao) -> 7094 dao.incrementAndGetReportingRetryCount( 7095 debugReport1.getId(), DataType.DEBUG_REPORT_RETRY_COUNT)); 7096 // Delete Expired 7097 long earliestValidInsertion = System.currentTimeMillis(); 7098 7099 assertTrue( 7100 mDatastoreManager.runInTransaction( 7101 measurementDao -> 7102 measurementDao.deleteExpiredRecords( 7103 earliestValidInsertion, 7104 /* earliestValidAppReportInsertion */ 0, 7105 /* earliestValidAppReportInsertion */ null, 7106 /* earliestValidAggregateDebugReportInsertion */ 0))); 7107 7108 // Assert Record 1 remains because not expired and Retry Limiting Off. 7109 assertEquals( 7110 1, 7111 DatabaseUtils.longForQuery( 7112 db, 7113 "SELECT COUNT(" 7114 + DebugReportContract.ID 7115 + ") FROM " 7116 + DebugReportContract.TABLE 7117 + " WHERE " 7118 + DebugReportContract.ID 7119 + " = ?", 7120 new String[] {debugReport1.getId()})); 7121 7122 // Assert Record 2 Removed because expired. 7123 assertEquals( 7124 0, 7125 DatabaseUtils.longForQuery( 7126 db, 7127 "SELECT COUNT(" 7128 + DebugReportContract.ID 7129 + ") FROM " 7130 + DebugReportContract.TABLE 7131 + " WHERE " 7132 + DebugReportContract.ID 7133 + " = ?", 7134 new String[] {debugReport2.getId()})); 7135 } 7136 7137 @Test deleteExpiredRecords_RetryKeyValueData()7138 public void deleteExpiredRecords_RetryKeyValueData() { 7139 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 7140 // Non-stale join record 7141 DebugReport debugReport = createDebugReport(); 7142 mDatastoreManager.runInTransaction((dao) -> dao.insertDebugReport(debugReport)); 7143 7144 // Should Remain 7145 ContentValues nonStaleValues = new ContentValues(); 7146 nonStaleValues.put( 7147 KeyValueDataContract.DATA_TYPE, DataType.DEBUG_REPORT_RETRY_COUNT.toString()); 7148 nonStaleValues.put(KeyValueDataContract.KEY, debugReport.getId()); 7149 nonStaleValues.put(KeyValueDataContract.VALUE, "1"); 7150 db.insert(KeyValueDataContract.TABLE, null, nonStaleValues); 7151 7152 // Should Delete 7153 ContentValues staleValues = new ContentValues(); 7154 staleValues.put( 7155 KeyValueDataContract.DATA_TYPE, DataType.DEBUG_REPORT_RETRY_COUNT.toString()); 7156 staleValues.put(KeyValueDataContract.KEY, "stale-key"); 7157 staleValues.put(KeyValueDataContract.VALUE, "1"); 7158 db.insert(KeyValueDataContract.TABLE, null, staleValues); 7159 7160 mDatastoreManager.runInTransaction( 7161 dao -> 7162 dao.deleteExpiredRecords( 7163 /* earliestValidInsertion */ 0, 7164 /* registrationRetryLimit */ 0, 7165 /* earliestValidAppReportInsertion */ null, 7166 /* earliestValidAggregateDebugReportInsertion */ 0)); 7167 7168 // Assert Non-Stale record remains. 7169 assertEquals( 7170 1, 7171 DatabaseUtils.longForQuery( 7172 db, 7173 "SELECT COUNT(" 7174 + KeyValueDataContract.KEY 7175 + ") FROM " 7176 + KeyValueDataContract.TABLE 7177 + " WHERE " 7178 + KeyValueDataContract.KEY 7179 + " = ?", 7180 new String[] {nonStaleValues.getAsString(KeyValueDataContract.KEY)})); 7181 7182 // Assert Stale Record Removed 7183 assertEquals( 7184 0, 7185 DatabaseUtils.longForQuery( 7186 db, 7187 "SELECT COUNT(" 7188 + KeyValueDataContract.KEY 7189 + ") FROM " 7190 + KeyValueDataContract.TABLE 7191 + " WHERE " 7192 + KeyValueDataContract.KEY 7193 + " = ?", 7194 new String[] {staleValues.getAsString(KeyValueDataContract.KEY)})); 7195 } 7196 7197 @Test deleteExpiredRecords_reinstallAttributionEnabled_deletesExpiredAppInstallHistory()7198 public void deleteExpiredRecords_reinstallAttributionEnabled_deletesExpiredAppInstallHistory() { 7199 Flags mockFlags = Mockito.mock(Flags.class); 7200 ExtendedMockito.doReturn(mockFlags).when(FlagsFactory::getFlags); 7201 ExtendedMockito.doReturn(true).when(mockFlags).getMeasurementEnableReinstallReattribution(); 7202 7203 SQLiteDatabase db = MeasurementDbHelper.getInstance().getReadableDatabase(); 7204 long now = System.currentTimeMillis(); 7205 mDatastoreManager.runInTransaction( 7206 (dao) -> 7207 dao.insertOrUpdateAppReportHistory( 7208 INSTALLED_PACKAGE, 7209 REGISTRATION_ORIGIN, 7210 /* lastReportDeliveredTimestamp= */ now 7211 - TimeUnit.DAYS.toMillis(1))); 7212 mDatastoreManager.runInTransaction( 7213 (dao) -> 7214 dao.insertOrUpdateAppReportHistory( 7215 INSTALLED_PACKAGE, 7216 REGISTRATION_ORIGIN_2, 7217 /* lastReportDeliveredTimestamp= */ now 7218 - TimeUnit.DAYS.toMillis(3))); 7219 7220 long earliestValidInsertion = now - TimeUnit.DAYS.toMillis(2); 7221 assertTrue( 7222 mDatastoreManager.runInTransaction( 7223 measurementDao -> 7224 measurementDao.deleteExpiredRecords( 7225 earliestValidInsertion, 7226 /* registrationRetryLimit= */ 0, 7227 earliestValidInsertion, 7228 /* earliestValidAggregateDebugReportInsertion */ 0))); 7229 // Assert Record 1 remains because not expired and Retry Limiting Off. 7230 assertEquals( 7231 1, 7232 DatabaseUtils.longForQuery( 7233 db, 7234 "SELECT COUNT(" 7235 + AppReportHistoryContract.REGISTRATION_ORIGIN 7236 + ") FROM " 7237 + AppReportHistoryContract.TABLE 7238 + " WHERE " 7239 + AppReportHistoryContract.REGISTRATION_ORIGIN 7240 + " = ?", 7241 new String[] {REGISTRATION_ORIGIN.toString()})); 7242 7243 // Assert Record 2 Removed because expired. 7244 assertEquals( 7245 0, 7246 DatabaseUtils.longForQuery( 7247 db, 7248 "SELECT COUNT(" 7249 + AppReportHistoryContract.REGISTRATION_ORIGIN 7250 + ") FROM " 7251 + AppReportHistoryContract.TABLE 7252 + " WHERE " 7253 + AppReportHistoryContract.REGISTRATION_ORIGIN 7254 + " = ?", 7255 new String[] {REGISTRATION_ORIGIN_2.toString()})); 7256 } 7257 7258 @Test deleteExpiredRecords_withAdrBudgetTrackerRecords_deletesOlderRecords()7259 public void deleteExpiredRecords_withAdrBudgetTrackerRecords_deletesOlderRecords() { 7260 // Setup 7261 Flags mockFlags = Mockito.mock(Flags.class); 7262 ExtendedMockito.doReturn(mockFlags).when(FlagsFactory::getFlags); 7263 ExtendedMockito.doReturn(true) 7264 .when(mockFlags) 7265 .getMeasurementEnableAggregateDebugReporting(); 7266 long currentTime = System.currentTimeMillis(); 7267 7268 // record1 - 6 hour old report record 7269 AggregateDebugReportRecord record1 = 7270 new AggregateDebugReportRecord.Builder( 7271 currentTime - TimeUnit.HOURS.toMillis(6), 7272 Uri.parse("android-app://com.example.abc"), 7273 APP_ONE_PUBLISHER, 7274 REGISTRATION_ORIGIN, 7275 1) 7276 .build(); 7277 // record2 - 1 day 1 millisecond old report record 7278 AggregateDebugReportRecord record2 = 7279 new AggregateDebugReportRecord.Builder( 7280 currentTime - DAYS.toMillis(1) - 1, 7281 Uri.parse("android-app://com.example.def"), 7282 APP_TWO_PUBLISHER, 7283 REGISTRATION_ORIGIN_2, 7284 10) 7285 .build(); 7286 // record3 - 10 days old report record 7287 AggregateDebugReportRecord record3 = 7288 new AggregateDebugReportRecord.Builder( 7289 currentTime - DAYS.toMillis(10), 7290 Uri.parse("android-app://com.example.ghi"), 7291 APP_ONE_PUBLISHER, 7292 REGISTRATION_ORIGIN_3, 7293 100) 7294 .build(); 7295 // record4 - 1 minute old report record 7296 AggregateDebugReportRecord record4 = 7297 new AggregateDebugReportRecord.Builder( 7298 currentTime - TimeUnit.MINUTES.toMillis(1), 7299 Uri.parse("android-app://com.example.abc"), 7300 APP_TWO_SOURCES, 7301 REGISTRATION_ORIGIN_4, 7302 1000) 7303 .build(); 7304 List<AggregateDebugReportRecord> records = 7305 Arrays.asList(record1, record2, record3, record4); 7306 records.forEach( 7307 record -> 7308 mDatastoreManager.runInTransaction( 7309 dao -> dao.insertAggregateDebugReportRecord(record))); 7310 7311 // Execution - delete records older than 1 day - should retain record1 & record4 7312 mDatastoreManager.runInTransaction( 7313 dao -> 7314 dao.deleteExpiredRecords( 7315 /* earliestValidInsertion */ 0L, 7316 /* registrationRetryLimit */ 0, 7317 /* earliestValidAppReportInsertion */ null, 7318 currentTime - DAYS.toMillis(1))); 7319 7320 // Assertion 7321 assertThat( 7322 DatabaseUtils.queryNumEntries( 7323 MeasurementDbHelper.getInstance().getReadableDatabase(), 7324 AggregatableDebugReportBudgetTrackerContract.TABLE)) 7325 .isEqualTo(2); 7326 try (Cursor recordCursor = 7327 MeasurementDbHelper.getInstance() 7328 .getReadableDatabase() 7329 .query( 7330 AggregatableDebugReportBudgetTrackerContract.TABLE, 7331 new String[] { 7332 AggregatableDebugReportBudgetTrackerContract.REGISTRATION_ORIGIN 7333 }, 7334 null, 7335 null, 7336 null, 7337 null, 7338 AggregatableDebugReportBudgetTrackerContract.REGISTRATION_ORIGIN)) { 7339 assertThat(recordCursor.getCount()).isEqualTo(2); 7340 assertThat(recordCursor.moveToNext()).isTrue(); 7341 assertThat( 7342 recordCursor.getString( 7343 recordCursor.getColumnIndex( 7344 AggregatableDebugReportBudgetTrackerContract 7345 .REGISTRATION_ORIGIN))) 7346 .isEqualTo(REGISTRATION_ORIGIN.toString()); 7347 assertThat(recordCursor.moveToNext()).isTrue(); 7348 assertThat( 7349 recordCursor.getString( 7350 recordCursor.getColumnIndex( 7351 AggregatableDebugReportBudgetTrackerContract 7352 .REGISTRATION_ORIGIN))) 7353 .isEqualTo(REGISTRATION_ORIGIN_4.toString()); 7354 } 7355 } 7356 7357 @Test deleteExpiredRecords_withAdrRecords_deletesRecordsDueToSourceTriggerDeletion()7358 public void deleteExpiredRecords_withAdrRecords_deletesRecordsDueToSourceTriggerDeletion() { 7359 // Setup 7360 Flags mockFlags = Mockito.mock(Flags.class); 7361 ExtendedMockito.doReturn(mockFlags).when(FlagsFactory::getFlags); 7362 ExtendedMockito.doReturn(true) 7363 .when(mockFlags) 7364 .getMeasurementEnableAggregateDebugReporting(); 7365 long currentTime = System.currentTimeMillis(); 7366 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 7367 7368 // 30 days old 7369 Source s1 = 7370 SourceFixture.getValidSourceBuilder() 7371 .setId("S1") 7372 .setEventTime(currentTime - DAYS.toMillis(30)) 7373 .build(); 7374 Trigger t1 = 7375 TriggerFixture.getValidTriggerBuilder() 7376 .setId("T1") 7377 .setTriggerTime(currentTime - DAYS.toMillis(30)) 7378 .build(); 7379 AbstractDbIntegrationTest.insertToDb(s1, db); 7380 AbstractDbIntegrationTest.insertToDb(t1, db); 7381 7382 // 1 day old 7383 Source s2 = 7384 SourceFixture.getValidSourceBuilder() 7385 .setId("S2") 7386 .setEventTime(currentTime - DAYS.toMillis(1)) 7387 .build(); 7388 Trigger t2 = 7389 TriggerFixture.getValidTriggerBuilder() 7390 .setId("T2") 7391 .setTriggerTime(currentTime - DAYS.toMillis(1)) 7392 .build(); 7393 AbstractDbIntegrationTest.insertToDb(s2, db); 7394 AbstractDbIntegrationTest.insertToDb(t2, db); 7395 7396 // deleted - record1 - 6 hour old report record but source and trigger are 30 days old 7397 AggregateDebugReportRecord record1 = 7398 new AggregateDebugReportRecord.Builder( 7399 currentTime - TimeUnit.HOURS.toMillis(6), 7400 Uri.parse("android-app://com.example.abc"), 7401 APP_ONE_PUBLISHER, 7402 REGISTRATION_ORIGIN, 7403 1) 7404 .setSourceId(s1.getId()) 7405 .setTriggerId(t1.getId()) 7406 .build(); 7407 // deleted - record2 - 6 hour old report record but source is 30 days old 7408 AggregateDebugReportRecord record2 = 7409 new AggregateDebugReportRecord.Builder( 7410 currentTime - TimeUnit.HOURS.toMillis(6), 7411 Uri.parse("android-app://com.example.def"), 7412 APP_TWO_PUBLISHER, 7413 REGISTRATION_ORIGIN_2, 7414 10) 7415 .setSourceId(s1.getId()) 7416 .setTriggerId(null) 7417 .build(); 7418 // deleted - record3 - 6 hour old report record but trigger is 30 days old 7419 AggregateDebugReportRecord record3 = 7420 new AggregateDebugReportRecord.Builder( 7421 currentTime - TimeUnit.HOURS.toMillis(6), 7422 Uri.parse("android-app://com.example.ghi"), 7423 APP_ONE_PUBLISHER, 7424 REGISTRATION_ORIGIN_3, 7425 100) 7426 .setSourceId(null) 7427 .setTriggerId(t1.getId()) 7428 .build(); 7429 // retained - record4 - 6 hour old report record and source is 1 day old 7430 AggregateDebugReportRecord record4 = 7431 new AggregateDebugReportRecord.Builder( 7432 currentTime - TimeUnit.HOURS.toMillis(6), 7433 Uri.parse("android-app://com.example.jkl"), 7434 APP_TWO_SOURCES, 7435 REGISTRATION_ORIGIN_4, 7436 1000) 7437 .setSourceId(s2.getId()) 7438 .setTriggerId(null) 7439 .build(); 7440 // retained - record4 - 6 hour old report record and trigger is 1 day old 7441 AggregateDebugReportRecord record5 = 7442 new AggregateDebugReportRecord.Builder( 7443 currentTime - TimeUnit.HOURS.toMillis(6), 7444 Uri.parse("android-app://com.example.mno"), 7445 APP_TWO_SOURCES, 7446 REGISTRATION_ORIGIN_5, 7447 1000) 7448 .setSourceId(null) 7449 .setTriggerId(t2.getId()) 7450 .build(); 7451 List<AggregateDebugReportRecord> records = 7452 Arrays.asList(record1, record2, record3, record4, record5); 7453 records.forEach( 7454 record -> 7455 mDatastoreManager.runInTransaction( 7456 dao -> dao.insertAggregateDebugReportRecord(record))); 7457 7458 // Execution - delete records older than 10 days - should retain record4 & record5 7459 mDatastoreManager.runInTransaction( 7460 dao -> 7461 dao.deleteExpiredRecords( 7462 /* earliestValidInsertion */ currentTime - DAYS.toMillis(10), 7463 /* registrationRetryLimit */ 0, 7464 /* earliestValidAppReportInsertion */ null, 7465 /* earliestValidAggregateDebugReportInsertion */ 0)); 7466 7467 // Assertion 7468 assertThat( 7469 DatabaseUtils.queryNumEntries( 7470 MeasurementDbHelper.getInstance().getReadableDatabase(), 7471 AggregatableDebugReportBudgetTrackerContract.TABLE)) 7472 .isEqualTo(2); 7473 try (Cursor recordCursor = 7474 MeasurementDbHelper.getInstance() 7475 .getReadableDatabase() 7476 .query( 7477 AggregatableDebugReportBudgetTrackerContract.TABLE, 7478 new String[] { 7479 AggregatableDebugReportBudgetTrackerContract.REGISTRATION_ORIGIN 7480 }, 7481 null, 7482 null, 7483 null, 7484 null, 7485 AggregatableDebugReportBudgetTrackerContract.REGISTRATION_ORIGIN)) { 7486 assertThat(recordCursor.getCount()).isEqualTo(2); 7487 assertThat(recordCursor.moveToNext()).isTrue(); 7488 assertThat( 7489 recordCursor.getString( 7490 recordCursor.getColumnIndex( 7491 AggregatableDebugReportBudgetTrackerContract 7492 .REGISTRATION_ORIGIN))) 7493 .isEqualTo(REGISTRATION_ORIGIN_4.toString()); 7494 assertThat(recordCursor.moveToNext()).isTrue(); 7495 assertThat( 7496 recordCursor.getString( 7497 recordCursor.getColumnIndex( 7498 AggregatableDebugReportBudgetTrackerContract 7499 .REGISTRATION_ORIGIN))) 7500 .isEqualTo(REGISTRATION_ORIGIN_5.toString()); 7501 } 7502 } 7503 7504 @Test getRegistrationRedirectCount_keyMissing()7505 public void getRegistrationRedirectCount_keyMissing() { 7506 Optional<KeyValueData> optKeyValueData = 7507 mDatastoreManager.runInTransactionWithResult( 7508 (dao) -> 7509 dao.getKeyValueData( 7510 "missing_random_id", DataType.REGISTRATION_REDIRECT_COUNT)); 7511 assertTrue(optKeyValueData.isPresent()); 7512 KeyValueData keyValueData = optKeyValueData.get(); 7513 assertEquals(KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT, keyValueData.getDataType()); 7514 assertEquals("missing_random_id", keyValueData.getKey()); 7515 assertNull(keyValueData.getValue()); 7516 assertEquals(1, keyValueData.getRegistrationRedirectCount()); 7517 } 7518 7519 @Test getRegistrationRedirectCount_keyExists()7520 public void getRegistrationRedirectCount_keyExists() { 7521 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 7522 ContentValues contentValues = new ContentValues(); 7523 contentValues.put( 7524 KeyValueDataContract.DATA_TYPE, 7525 KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT.toString()); 7526 contentValues.put(KeyValueDataContract.KEY, "random_id"); 7527 contentValues.put(KeyValueDataContract.VALUE, "2"); 7528 db.insert(KeyValueDataContract.TABLE, null, contentValues); 7529 Optional<KeyValueData> optKeyValueData = 7530 mDatastoreManager.runInTransactionWithResult( 7531 (dao) -> 7532 dao.getKeyValueData( 7533 "random_id", 7534 KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT)); 7535 assertTrue(optKeyValueData.isPresent()); 7536 KeyValueData keyValueData = optKeyValueData.get(); 7537 assertEquals(KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT, keyValueData.getDataType()); 7538 assertEquals("random_id", keyValueData.getKey()); 7539 assertEquals("2", keyValueData.getValue()); 7540 assertEquals(2, keyValueData.getRegistrationRedirectCount()); 7541 } 7542 7543 @Test updateRegistrationRedirectCount_keyMissing()7544 public void updateRegistrationRedirectCount_keyMissing() { 7545 KeyValueData keyValueData = 7546 new KeyValueData.Builder() 7547 .setDataType(KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT) 7548 .setKey("key_1") 7549 .setValue("4") 7550 .build(); 7551 mDatastoreManager.runInTransaction((dao) -> dao.insertOrUpdateKeyValueData(keyValueData)); 7552 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 7553 Cursor cursor = db.query(KeyValueDataContract.TABLE, null, null, null, null, null, null); 7554 assertEquals(1, cursor.getCount()); 7555 cursor.moveToNext(); 7556 assertEquals( 7557 KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT.toString(), 7558 cursor.getString(cursor.getColumnIndex(KeyValueDataContract.DATA_TYPE))); 7559 assertEquals("key_1", cursor.getString(cursor.getColumnIndex(KeyValueDataContract.KEY))); 7560 assertEquals("4", cursor.getString(cursor.getColumnIndex(KeyValueDataContract.VALUE))); 7561 cursor.close(); 7562 } 7563 7564 @Test updateRegistrationRedirectCount_keyExists()7565 public void updateRegistrationRedirectCount_keyExists() { 7566 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 7567 ContentValues contentValues = new ContentValues(); 7568 contentValues.put( 7569 KeyValueDataContract.DATA_TYPE, 7570 KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT.toString()); 7571 contentValues.put(KeyValueDataContract.KEY, "key_1"); 7572 contentValues.put(KeyValueDataContract.VALUE, "2"); 7573 db.insert(KeyValueDataContract.TABLE, null, contentValues); 7574 7575 KeyValueData keyValueData = 7576 new KeyValueData.Builder() 7577 .setDataType(KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT) 7578 .setKey("key_1") 7579 .setValue("4") 7580 .build(); 7581 mDatastoreManager.runInTransaction((dao) -> dao.insertOrUpdateKeyValueData(keyValueData)); 7582 7583 Cursor cursor = db.query(KeyValueDataContract.TABLE, null, null, null, null, null, null); 7584 assertEquals(1, cursor.getCount()); 7585 cursor.moveToNext(); 7586 assertEquals( 7587 KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT.toString(), 7588 cursor.getString(cursor.getColumnIndex(KeyValueDataContract.DATA_TYPE))); 7589 assertEquals("key_1", cursor.getString(cursor.getColumnIndex(KeyValueDataContract.KEY))); 7590 assertEquals("4", cursor.getString(cursor.getColumnIndex(KeyValueDataContract.VALUE))); 7591 cursor.close(); 7592 } 7593 7594 @Test keyValueDataTable_PrimaryKeyConstraint()7595 public void keyValueDataTable_PrimaryKeyConstraint() { 7596 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 7597 ContentValues contentValues1 = new ContentValues(); 7598 contentValues1.put( 7599 KeyValueDataContract.DATA_TYPE, 7600 KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT.toString()); 7601 contentValues1.put(KeyValueDataContract.KEY, "key_1"); 7602 contentValues1.put(KeyValueDataContract.VALUE, "2"); 7603 7604 assertNotEquals(-1, db.insert(KeyValueDataContract.TABLE, null, contentValues1)); 7605 7606 // Should fail because we are using <DataType, Key> as primary key 7607 assertEquals(-1, db.insert(KeyValueDataContract.TABLE, null, contentValues1)); 7608 7609 ContentValues contentValues2 = new ContentValues(); 7610 contentValues2.put( 7611 KeyValueDataContract.DATA_TYPE, 7612 KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT.toString()); 7613 contentValues2.put(KeyValueDataContract.KEY, "key_2"); 7614 contentValues2.put(KeyValueDataContract.VALUE, "2"); 7615 7616 assertNotEquals(-1, db.insert(KeyValueDataContract.TABLE, null, contentValues2)); 7617 } 7618 7619 @Test 7620 public void fetchSourceIdsForLowestPriorityDest_appDestEmptyExclusions_delLowPriorityDestination()7621 fetchSourceIdsForLowestPriorityDest_appDestEmptyExclusions_delLowPriorityDestination() { 7622 // Setup 7623 mocker.mockGetFlags(mMockFlags); 7624 doReturn(true).when(mMockFlags).getMeasurementEnableSourceDestinationLimitPriority(); 7625 long baseEventTime = System.currentTimeMillis(); 7626 long commonExpiryTime = baseEventTime + DAYS.toMillis(30); 7627 insertSource( 7628 createSourceBuilder() 7629 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app1"))) 7630 .setWebDestinations(List.of(Uri.parse("https://web1.example.com"))) 7631 .setEventTime(baseEventTime) 7632 .setExpiryTime(commonExpiryTime) 7633 .setDestinationLimitPriority(10) 7634 .build(), 7635 "s11"); 7636 insertSource( 7637 createSourceBuilder() 7638 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app2"))) 7639 .setWebDestinations(List.of(Uri.parse("https://web2.example.com"))) 7640 .setEventTime(baseEventTime + DAYS.toMillis(1)) 7641 .setExpiryTime(commonExpiryTime) 7642 .setDestinationLimitPriority(20) 7643 .build(), 7644 "s21"); 7645 insertSource( 7646 createSourceBuilder() 7647 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app3"))) 7648 .setWebDestinations(List.of(Uri.parse("https://web3.example.com"))) 7649 .setEventTime(baseEventTime + DAYS.toMillis(2)) 7650 .setExpiryTime(commonExpiryTime) 7651 .setDestinationLimitPriority(30) 7652 .build(), 7653 "s31"); 7654 insertSource( 7655 createSourceBuilder() 7656 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app1"))) 7657 .setWebDestinations(List.of(Uri.parse("https://web1.example.com"))) 7658 .setEventTime(baseEventTime + DAYS.toMillis(3)) 7659 .setExpiryTime(commonExpiryTime) 7660 .setDestinationLimitPriority(10) 7661 .build(), 7662 "s12"); 7663 insertSource( 7664 createSourceBuilder() 7665 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app2"))) 7666 .setWebDestinations(List.of(Uri.parse("https://web2.example.com"))) 7667 .setEventTime(baseEventTime + DAYS.toMillis(4)) 7668 .setExpiryTime(commonExpiryTime) 7669 .setDestinationLimitPriority(20) 7670 .build(), 7671 "s22"); 7672 7673 // Execute 7674 // com.example.app1 has the least priority of all as 10 7675 mDatastoreManager.runInTransaction( 7676 (dao) -> { 7677 Pair<Long, List<String>> destinationPriorityAndSourcesTodelete = 7678 dao.fetchSourceIdsForLowestPriorityDestinationXEnrollmentXPublisher( 7679 SourceFixture.ValidSourceParams.PUBLISHER, 7680 EventSurfaceType.APP, 7681 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 7682 Collections.emptyList(), 7683 EventSurfaceType.APP, 7684 baseEventTime + DAYS.toMillis(10) // request time 7685 ); 7686 7687 assertEquals(10L, (long) destinationPriorityAndSourcesTodelete.first); 7688 assertEquals( 7689 Sets.newSet("s11", "s12"), 7690 new HashSet<>(destinationPriorityAndSourcesTodelete.second)); 7691 }); 7692 } 7693 7694 @Test 7695 public void fetchSourceIdsForLowPriorityDest_webDestEmptyExclusions_delLowPriorityDestinations()7696 fetchSourceIdsForLowPriorityDest_webDestEmptyExclusions_delLowPriorityDestinations() { 7697 // Setup 7698 mocker.mockGetFlags(mMockFlags); 7699 doReturn(true).when(mMockFlags).getMeasurementEnableSourceDestinationLimitPriority(); 7700 long baseEventTime = System.currentTimeMillis(); 7701 long commonExpiryTime = baseEventTime + DAYS.toMillis(30); 7702 insertSource( 7703 createSourceBuilder() 7704 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app1"))) 7705 .setWebDestinations(List.of(Uri.parse("https://web1.example.com"))) 7706 .setEventTime(baseEventTime) 7707 .setExpiryTime(commonExpiryTime) 7708 .setDestinationLimitPriority(10) 7709 .build(), 7710 "s11"); 7711 insertSource( 7712 createSourceBuilder() 7713 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app2"))) 7714 .setWebDestinations(List.of(Uri.parse("https://web2.example.com"))) 7715 .setEventTime(baseEventTime + DAYS.toMillis(1)) 7716 .setExpiryTime(commonExpiryTime) 7717 .setDestinationLimitPriority(20) 7718 .build(), 7719 "s21"); 7720 insertSource( 7721 createSourceBuilder() 7722 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app3"))) 7723 .setWebDestinations(List.of(Uri.parse("https://web3.example.com"))) 7724 .setEventTime(baseEventTime + DAYS.toMillis(2)) 7725 .setExpiryTime(commonExpiryTime) 7726 .setDestinationLimitPriority(30) 7727 .build(), 7728 "s31"); 7729 insertSource( 7730 createSourceBuilder() 7731 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app1"))) 7732 .setWebDestinations(List.of(Uri.parse("https://web1.example.com"))) 7733 .setEventTime(baseEventTime + DAYS.toMillis(3)) 7734 .setExpiryTime(commonExpiryTime) 7735 .setDestinationLimitPriority(40) 7736 .build(), 7737 "s12"); 7738 insertSource( 7739 createSourceBuilder() 7740 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app2"))) 7741 .setWebDestinations(List.of(Uri.parse("https://web2.example.com"))) 7742 .setEventTime(baseEventTime + DAYS.toMillis(4)) 7743 .setExpiryTime(commonExpiryTime) 7744 .setDestinationLimitPriority(20) 7745 .build(), 7746 "s22"); 7747 7748 // Execute 7749 // web1.example.com has priority 10 with s11 and priority 40 with s12, the higher one will 7750 // be considered, i.e. 40. web2.example.com" has priority as 20 through both s21 and s22, 7751 // its associated sources will be deleted instead. 7752 mDatastoreManager.runInTransaction( 7753 (dao) -> { 7754 Pair<Long, List<String>> destinationPriorityAndSourcesToDelete = 7755 dao.fetchSourceIdsForLowestPriorityDestinationXEnrollmentXPublisher( 7756 SourceFixture.ValidSourceParams.PUBLISHER, 7757 EventSurfaceType.APP, 7758 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 7759 Collections.emptyList(), 7760 EventSurfaceType.WEB, 7761 baseEventTime + DAYS.toMillis(10) // request time 7762 ); 7763 7764 assertEquals(20L, (long) destinationPriorityAndSourcesToDelete.first); 7765 assertEquals( 7766 Sets.newSet("s21", "s22"), 7767 new HashSet<>(destinationPriorityAndSourcesToDelete.second)); 7768 }); 7769 } 7770 7771 @Test fetchSourceIdsForLowPriorityDest_appDestEmptyExclusions_delLruDestinations()7772 public void fetchSourceIdsForLowPriorityDest_appDestEmptyExclusions_delLruDestinations() { 7773 // Setup 7774 long baseEventTime = System.currentTimeMillis(); 7775 insert5SourcesForLruDestDeletion(baseEventTime); 7776 7777 // Execute 7778 // com.example.app3 would be the least recently used destination, as 1 & 2 are used 7779 // afterwards 7780 mDatastoreManager.runInTransaction( 7781 (dao) -> { 7782 Pair<Long, List<String>> destinationPriorityAndSourcesToDelete = 7783 dao.fetchSourceIdsForLowestPriorityDestinationXEnrollmentXPublisher( 7784 SourceFixture.ValidSourceParams.PUBLISHER, 7785 EventSurfaceType.APP, 7786 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 7787 Collections.emptyList(), 7788 EventSurfaceType.APP, 7789 baseEventTime + DAYS.toMillis(10) // request time 7790 ); 7791 7792 assertEquals(0, (long) destinationPriorityAndSourcesToDelete.first); 7793 assertEquals(List.of("s31"), destinationPriorityAndSourcesToDelete.second); 7794 }); 7795 } 7796 7797 @Test 7798 public void fetchSourceIdsForLowPriorityDest_appDestWebPubEmptyExclusions_delLowPrioDestSource()7799 fetchSourceIdsForLowPriorityDest_appDestWebPubEmptyExclusions_delLowPrioDestSource() { 7800 // Setup 7801 long baseEventTime = System.currentTimeMillis(); 7802 long commonExpiryTime = baseEventTime + DAYS.toMillis(30); 7803 insertSource( 7804 createSourceBuilder() 7805 .setPublisher(Uri.parse("https://web.example.com")) 7806 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app1"))) 7807 .setWebDestinations(List.of(Uri.parse("https://web1.example.com"))) 7808 .setEventTime(baseEventTime) 7809 .setExpiryTime(commonExpiryTime) 7810 .build(), 7811 "s11"); 7812 insertSource( 7813 createSourceBuilder() 7814 .setPublisher(Uri.parse("https://web.example.com")) 7815 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app2"))) 7816 .setWebDestinations(List.of(Uri.parse("https://web2.example.com"))) 7817 .setEventTime(baseEventTime + DAYS.toMillis(1)) 7818 .setExpiryTime(commonExpiryTime) 7819 .build(), 7820 "s21"); 7821 insertSource( 7822 createSourceBuilder() 7823 .setPublisher(Uri.parse("https://web.example.com")) 7824 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app3"))) 7825 .setWebDestinations(List.of(Uri.parse("https://web3.example.com"))) 7826 .setEventTime(baseEventTime + DAYS.toMillis(2)) 7827 .setExpiryTime(commonExpiryTime) 7828 .build(), 7829 "s31"); 7830 insertSource( 7831 createSourceBuilder() 7832 .setPublisher(Uri.parse("https://web.example.com")) 7833 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app1"))) 7834 .setWebDestinations(List.of(Uri.parse("https://web1.example.com"))) 7835 .setEventTime(baseEventTime + DAYS.toMillis(3)) 7836 .setExpiryTime(commonExpiryTime) 7837 .build(), 7838 "s12"); 7839 insertSource( 7840 createSourceBuilder() 7841 .setPublisher(Uri.parse("https://web.example.com")) 7842 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app2"))) 7843 .setWebDestinations(List.of(Uri.parse("https://web2.example.com"))) 7844 .setEventTime(baseEventTime + DAYS.toMillis(4)) 7845 .setExpiryTime(commonExpiryTime) 7846 .build(), 7847 "s22"); 7848 7849 // Execute 7850 // com.example.app3 would be the least recently used destination, as 1 & 2 are used 7851 // afterwards 7852 mDatastoreManager.runInTransaction( 7853 (dao) -> { 7854 Pair<Long, List<String>> destinationPriorityAndSourcesToDelete = 7855 dao.fetchSourceIdsForLowestPriorityDestinationXEnrollmentXPublisher( 7856 Uri.parse("https://web.example.com"), 7857 EventSurfaceType.WEB, 7858 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 7859 Collections.emptyList(), 7860 EventSurfaceType.APP, 7861 baseEventTime + DAYS.toMillis(10) // request time 7862 ); 7863 7864 assertEquals(0L, (long) destinationPriorityAndSourcesToDelete.first); 7865 assertEquals(List.of("s31"), destinationPriorityAndSourcesToDelete.second); 7866 }); 7867 } 7868 7869 @Test 7870 public void fetchSourceIdsForLowPriorityDest_diffEnrollments_delOldDestSourceForChosenEnrollment()7871 fetchSourceIdsForLowPriorityDest_diffEnrollments_delOldDestSourceForChosenEnrollment() { 7872 // Setup 7873 long baseEventTime = System.currentTimeMillis(); 7874 long commonExpiryTime = baseEventTime + DAYS.toMillis(30); 7875 insertSource( 7876 createSourceBuilder() 7877 .setEnrollmentId("enrollment1") 7878 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app1"))) 7879 .setEventTime(baseEventTime) 7880 .setExpiryTime(commonExpiryTime) 7881 .build(), 7882 "s11"); 7883 insertSource( 7884 createSourceBuilder() 7885 .setEnrollmentId("enrollment1") 7886 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app2"))) 7887 .setEventTime(baseEventTime + DAYS.toMillis(1)) 7888 .setExpiryTime(commonExpiryTime) 7889 .build(), 7890 "s21"); 7891 insertSource( 7892 createSourceBuilder() 7893 .setEnrollmentId("enrollment1") 7894 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app3"))) 7895 .setEventTime(baseEventTime + DAYS.toMillis(2)) 7896 .setExpiryTime(commonExpiryTime) 7897 .build(), 7898 "s31"); 7899 insertSource( 7900 createSourceBuilder() 7901 .setEnrollmentId("enrollment2") 7902 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app1"))) 7903 .setEventTime(baseEventTime + DAYS.toMillis(3)) 7904 .setExpiryTime(commonExpiryTime) 7905 .build(), 7906 "s12"); 7907 insertSource( 7908 createSourceBuilder() 7909 .setEnrollmentId("enrollment2") 7910 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app2"))) 7911 .setEventTime(baseEventTime + DAYS.toMillis(4)) 7912 .setExpiryTime(commonExpiryTime) 7913 .build(), 7914 "s22"); 7915 7916 // Execute 7917 // com.example.app1 would be the least recently used destination for enrollment2, that will 7918 // be deleted 7919 mDatastoreManager.runInTransaction( 7920 (dao) -> { 7921 Pair<Long, List<String>> destinationPriorityAndSourcesToDelete = 7922 dao.fetchSourceIdsForLowestPriorityDestinationXEnrollmentXPublisher( 7923 SourceFixture.ValidSourceParams.PUBLISHER, 7924 EventSurfaceType.APP, 7925 "enrollment2", 7926 Collections.emptyList(), 7927 EventSurfaceType.APP, 7928 baseEventTime + DAYS.toMillis(10) // request time 7929 ); 7930 7931 assertEquals(0L, (long) destinationPriorityAndSourcesToDelete.first); 7932 assertEquals(List.of("s12"), destinationPriorityAndSourcesToDelete.second); 7933 }); 7934 } 7935 7936 @Test 7937 public void fetchSourceIdsForLowPriorityDest_appDestExcludeLruSource_deletes2ndLruDestSources()7938 fetchSourceIdsForLowPriorityDest_appDestExcludeLruSource_deletes2ndLruDestSources() { 7939 // Setup 7940 long baseEventTime = System.currentTimeMillis(); 7941 insert5SourcesForLruDestDeletion(System.currentTimeMillis()); 7942 7943 // Execute 7944 // com.example.app1 would be the second least recently used destination, as 2 is used 7945 // afterwards and 3 is ignored to be deleted. 7946 mDatastoreManager.runInTransaction( 7947 (dao) -> { 7948 Pair<Long, List<String>> destinationPriorityAndSourcesToDelete = 7949 dao.fetchSourceIdsForLowestPriorityDestinationXEnrollmentXPublisher( 7950 SourceFixture.ValidSourceParams.PUBLISHER, 7951 EventSurfaceType.APP, 7952 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 7953 List.of(Uri.parse("android-app://com.example.app3")), 7954 EventSurfaceType.APP, 7955 baseEventTime + DAYS.toMillis(10) // request time 7956 ); 7957 7958 assertEquals(0L, (long) destinationPriorityAndSourcesToDelete.first); 7959 assertEquals( 7960 List.of("s11", "s12"), destinationPriorityAndSourcesToDelete.second); 7961 }); 7962 } 7963 insert5SourcesForLruDestDeletion(long baseEventTime)7964 private void insert5SourcesForLruDestDeletion(long baseEventTime) { 7965 long commonExpiryTime = baseEventTime + DAYS.toMillis(30); 7966 insertSource( 7967 createSourceBuilder() 7968 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app1"))) 7969 .setWebDestinations(List.of(Uri.parse("https://web1.example.com"))) 7970 .setEventTime(baseEventTime) 7971 .setExpiryTime(commonExpiryTime) 7972 .build(), 7973 "s11"); 7974 insertSource( 7975 createSourceBuilder() 7976 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app2"))) 7977 .setWebDestinations(List.of(Uri.parse("https://web2.example.com"))) 7978 .setEventTime(baseEventTime + DAYS.toMillis(1)) 7979 .setExpiryTime(commonExpiryTime) 7980 .build(), 7981 "s21"); 7982 insertSource( 7983 createSourceBuilder() 7984 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app3"))) 7985 .setWebDestinations(List.of(Uri.parse("https://web3.example.com"))) 7986 .setEventTime(baseEventTime + DAYS.toMillis(2)) 7987 .setExpiryTime(commonExpiryTime) 7988 .build(), 7989 "s31"); 7990 insertSource( 7991 createSourceBuilder() 7992 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app1"))) 7993 .setWebDestinations(List.of(Uri.parse("https://web1.example.com"))) 7994 .setEventTime(baseEventTime + DAYS.toMillis(3)) 7995 .setExpiryTime(commonExpiryTime) 7996 .build(), 7997 "s12"); 7998 insertSource( 7999 createSourceBuilder() 8000 .setAppDestinations(List.of(Uri.parse("android-app://com.example.app2"))) 8001 .setWebDestinations(List.of(Uri.parse("https://web2.example.com"))) 8002 .setEventTime(baseEventTime + DAYS.toMillis(4)) 8003 .setExpiryTime(commonExpiryTime) 8004 .build(), 8005 "s22"); 8006 } 8007 8008 @Test deletePendingAggregateReportsAndAttributionsForSources_success()8009 public void deletePendingAggregateReportsAndAttributionsForSources_success() { 8010 // Setup 8011 long baseTime = System.currentTimeMillis(); 8012 // Sources 8013 insertSource(SourceFixture.getValidSource(), "S1"); 8014 insertSource(SourceFixture.getValidSource(), "S2"); 8015 insertSource(SourceFixture.getValidSource(), "S3"); 8016 insertSource(SourceFixture.getValidSource(), "S4"); 8017 8018 mDatastoreManager.runInTransaction( 8019 (dao) -> { 8020 // Aggregate reports 8021 // Should get deleted 8022 AggregateReport agg1 = 8023 AggregateReportFixture.getValidAggregateReportBuilder() 8024 .setId("Agg1") 8025 .setSourceId("S1") 8026 .setScheduledReportTime(baseTime + TimeUnit.HOURS.toMillis(1)) 8027 .setStatus(AggregateReport.Status.PENDING) 8028 .build(); 8029 dao.insertAggregateReport(agg1); 8030 dao.insertAttribution( 8031 createAttribution( 8032 "Att1", Attribution.Scope.AGGREGATE, "S1", null, agg1.getId())); 8033 8034 // Should not get deleted because S2 is not provided 8035 AggregateReport agg2 = 8036 AggregateReportFixture.getValidAggregateReportBuilder() 8037 .setId("Agg2") 8038 .setSourceId("S2") 8039 .setScheduledReportTime(baseTime + TimeUnit.HOURS.toMillis(1)) 8040 .setStatus(AggregateReport.Status.PENDING) 8041 .build(); 8042 dao.insertAggregateReport(agg2); 8043 dao.insertAttribution( 8044 createAttribution( 8045 "Att2", Attribution.Scope.AGGREGATE, "S2", null, agg2.getId())); 8046 8047 // Infeasible case, but it should not get deleted because its status is 8048 // DELIVERED 8049 AggregateReport agg3 = 8050 AggregateReportFixture.getValidAggregateReportBuilder() 8051 .setId("Agg3") 8052 .setSourceId("S3") 8053 .setScheduledReportTime(baseTime + TimeUnit.HOURS.toMillis(1)) 8054 .setStatus(AggregateReport.Status.DELIVERED) 8055 .build(); 8056 dao.insertAggregateReport(agg3); 8057 dao.insertAttribution( 8058 createAttribution( 8059 "Att3", Attribution.Scope.AGGREGATE, "S3", null, agg3.getId())); 8060 8061 // Execution 8062 dao.deletePendingAggregateReportsAndAttributionsForSources(List.of("S1", "S3")); 8063 8064 // Assertion 8065 assertThrows(DatastoreException.class, () -> dao.getAggregateReport("Agg1")); 8066 assertEquals(agg2, dao.getAggregateReport("Agg2")); 8067 assertEquals(agg3, dao.getAggregateReport("Agg3")); 8068 }); 8069 8070 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 8071 assertEquals(2, DatabaseUtils.queryNumEntries(db, AttributionContract.TABLE)); 8072 Set<String> reportIds = new HashSet<>(); 8073 try (Cursor cursor = 8074 db.rawQuery( 8075 "SELECT " 8076 + AttributionContract.REPORT_ID 8077 + " FROM " 8078 + AttributionContract.TABLE, 8079 null)) { 8080 while (cursor.moveToNext()) { 8081 reportIds.add(cursor.getString(0)); 8082 } 8083 } 8084 assertEquals(Set.of("Agg2", "Agg3"), reportIds); 8085 } 8086 8087 @Test fetchMatchingSourcesUninstall_outsideReportLifetime_deleteSources()8088 public void fetchMatchingSourcesUninstall_outsideReportLifetime_deleteSources() 8089 throws Exception { 8090 // Setup 8091 mocker.mockGetFlags(mMockFlags); 8092 doReturn(true).when(mMockFlags).getMeasurementEnableMinReportLifespanForUninstall(); 8093 doReturn(TimeUnit.DAYS.toSeconds(1)) 8094 .when(mMockFlags) 8095 .getMeasurementMinReportLifespanForUninstallSeconds(); 8096 8097 long currentTime = System.currentTimeMillis(); 8098 long baseEventTime = currentTime - DAYS.toMillis(3); 8099 long expiryTime = baseEventTime + DAYS.toMillis(30); 8100 8101 List<Source> sources = 8102 Arrays.asList( 8103 SourceFixture.getMinimalValidSourceBuilder() 8104 .setEventId(new UnsignedLong(1L)) 8105 .setId("source1") 8106 .setEventTime(baseEventTime) 8107 .setExpiryTime(expiryTime) 8108 .build()); 8109 8110 // All trigger times more than 24 hours ago. 8111 List<Trigger> triggers = 8112 Arrays.asList( 8113 TriggerFixture.getValidTriggerBuilder() 8114 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 8115 .setId("trigger1") 8116 .setTriggerTime( 8117 currentTime - DAYS.toMillis(1) - TimeUnit.HOURS.toMillis(1)) 8118 .build(), 8119 TriggerFixture.getValidTriggerBuilder() 8120 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 8121 .setId("trigger2") 8122 .setTriggerTime(currentTime - DAYS.toMillis(2)) 8123 .build(), 8124 TriggerFixture.getValidTriggerBuilder() 8125 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 8126 .setId("trigger3") 8127 .setTriggerTime(currentTime - DAYS.toMillis(3)) 8128 .build()); 8129 8130 EventReport report0 = 8131 createEventReportForSourceAndTriggerForUninstall( 8132 "report0", sources.get(0), triggers.get(0)); 8133 EventReport report1 = 8134 createEventReportForSourceAndTriggerForUninstall( 8135 "report1", sources.get(0), triggers.get(1)); 8136 8137 AggregateReport aggregateReport0 = 8138 createAggregateReportForSourceAndTrigger( 8139 "areport0", sources.get(0), triggers.get(2)); 8140 8141 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 8142 sources.forEach(source -> insertSource(source, source.getId())); 8143 triggers.forEach(trigger -> AbstractDbIntegrationTest.insertToDb(trigger, db)); 8144 8145 mDatastoreManager.runInTransaction( 8146 (dao) -> { 8147 dao.insertEventReport(report0); 8148 dao.insertEventReport(report1); 8149 dao.insertAggregateReport(aggregateReport0); 8150 }); 8151 8152 // Execution 8153 mDatastoreManager.runInTransaction( 8154 dao -> { 8155 Pair<List<String>, List<String>> actualSources = 8156 dao.fetchMatchingSourcesUninstall( 8157 SourceFixture.ValidSourceParams.REGISTRANT, currentTime); 8158 // Source is deleted 8159 Truth.assertThat(actualSources.first.size()).isEqualTo(1); 8160 Truth.assertThat(actualSources.second.size()).isEqualTo(0); 8161 }); 8162 } 8163 8164 @Test fetchMatchingSourcesUninstall_withinReportLifetime_ignoreSources()8165 public void fetchMatchingSourcesUninstall_withinReportLifetime_ignoreSources() 8166 throws Exception { 8167 // Setup 8168 mocker.mockGetFlags(mMockFlags); 8169 doReturn(true).when(mMockFlags).getMeasurementEnableMinReportLifespanForUninstall(); 8170 doReturn(TimeUnit.DAYS.toSeconds(1)) 8171 .when(mMockFlags) 8172 .getMeasurementMinReportLifespanForUninstallSeconds(); 8173 8174 long currentTime = System.currentTimeMillis(); 8175 long baseEventTime = currentTime - DAYS.toMillis(3); 8176 long expiryTime = baseEventTime + DAYS.toMillis(30); 8177 8178 List<Source> sources = 8179 Arrays.asList( 8180 SourceFixture.getMinimalValidSourceBuilder() 8181 .setEventId(new UnsignedLong(1L)) 8182 .setId("source1") 8183 .setEventTime(baseEventTime) 8184 .setExpiryTime(expiryTime) 8185 .build()); 8186 8187 // All trigger times more than 24 hours ago except trigger1. 8188 List<Trigger> triggers = 8189 Arrays.asList( 8190 TriggerFixture.getValidTriggerBuilder() 8191 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 8192 .setId("trigger1") 8193 .setTriggerTime(currentTime) 8194 .build(), 8195 TriggerFixture.getValidTriggerBuilder() 8196 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 8197 .setId("trigger2") 8198 .setTriggerTime(currentTime - DAYS.toMillis(3)) 8199 .build(), 8200 TriggerFixture.getValidTriggerBuilder() 8201 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 8202 .setId("trigger3") 8203 .setTriggerTime(currentTime - DAYS.toMillis(3)) 8204 .build()); 8205 8206 EventReport report0 = 8207 createEventReportForSourceAndTriggerForUninstall( 8208 "report0", sources.get(0), triggers.get(0)); 8209 EventReport report1 = 8210 createEventReportForSourceAndTriggerForUninstall( 8211 "report1", sources.get(0), triggers.get(1)); 8212 8213 AggregateReport aggregateReport0 = 8214 createAggregateReportForSourceAndTrigger( 8215 "areport0", sources.get(0), triggers.get(2)); 8216 8217 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 8218 sources.forEach(source -> insertSource(source, source.getId())); 8219 triggers.forEach(trigger -> AbstractDbIntegrationTest.insertToDb(trigger, db)); 8220 8221 mDatastoreManager.runInTransaction( 8222 (dao) -> { 8223 dao.insertEventReport(report0); 8224 dao.insertEventReport(report1); 8225 dao.insertAggregateReport(aggregateReport0); 8226 }); 8227 8228 // Execution 8229 mDatastoreManager.runInTransaction( 8230 dao -> { 8231 Pair<List<String>, List<String>> actualSources = 8232 dao.fetchMatchingSourcesUninstall( 8233 SourceFixture.ValidSourceParams.REGISTRANT, currentTime); 8234 // Source is ignored 8235 Truth.assertThat(actualSources.first.size()).isEqualTo(0); 8236 Truth.assertThat(actualSources.second.size()).isEqualTo(1); 8237 }); 8238 } 8239 8240 @Test fetchMatchingSourcesUninstall_deleteAndIgnoreSources()8241 public void fetchMatchingSourcesUninstall_deleteAndIgnoreSources() throws Exception { 8242 // Setup 8243 mocker.mockGetFlags(mMockFlags); 8244 doReturn(true).when(mMockFlags).getMeasurementEnableMinReportLifespanForUninstall(); 8245 doReturn(TimeUnit.DAYS.toSeconds(1)) 8246 .when(mMockFlags) 8247 .getMeasurementMinReportLifespanForUninstallSeconds(); 8248 8249 long currentTime = System.currentTimeMillis(); 8250 long baseEventTime = currentTime - DAYS.toMillis(3); 8251 long expiryTime = baseEventTime + DAYS.toMillis(30); 8252 8253 List<Source> sources = 8254 Arrays.asList( 8255 SourceFixture.getMinimalValidSourceBuilder() 8256 .setEventId(new UnsignedLong(1L)) 8257 .setId("source1") 8258 .setEventTime(baseEventTime) 8259 .setExpiryTime(expiryTime) 8260 .build(), 8261 SourceFixture.getMinimalValidSourceBuilder() 8262 .setEventId(new UnsignedLong(1L)) 8263 .setId("source2") 8264 .setEventTime(baseEventTime) 8265 .setExpiryTime(expiryTime) 8266 .build(), 8267 SourceFixture.getMinimalValidSourceBuilder() 8268 .setEventId(new UnsignedLong(1L)) 8269 .setId("source3") 8270 .setEventTime(baseEventTime) 8271 .setExpiryTime(expiryTime) 8272 .build()); 8273 8274 // All trigger times more than 24 hours ago except trigger1. 8275 List<Trigger> triggers = 8276 Arrays.asList( 8277 TriggerFixture.getValidTriggerBuilder() 8278 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 8279 .setId("trigger1") 8280 .setTriggerTime(currentTime) 8281 .build(), 8282 TriggerFixture.getValidTriggerBuilder() 8283 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 8284 .setId("trigger2") 8285 .setTriggerTime(currentTime - DAYS.toMillis(3)) 8286 .build(), 8287 TriggerFixture.getValidTriggerBuilder() 8288 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 8289 .setId("trigger3") 8290 .setTriggerTime(currentTime - DAYS.toMillis(2)) 8291 .build()); 8292 8293 EventReport report0 = 8294 createEventReportForSourceAndTriggerForUninstall( 8295 "report0", sources.get(0), triggers.get(0)); 8296 EventReport report1 = 8297 createEventReportForSourceAndTriggerForUninstall( 8298 "report1", sources.get(1), triggers.get(1)); 8299 8300 AggregateReport aggregateReport0 = 8301 createAggregateReportForSourceAndTrigger( 8302 "areport0", sources.get(2), triggers.get(2)); 8303 8304 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 8305 sources.forEach(source -> insertSource(source, source.getId())); 8306 triggers.forEach(trigger -> AbstractDbIntegrationTest.insertToDb(trigger, db)); 8307 8308 mDatastoreManager.runInTransaction( 8309 (dao) -> { 8310 dao.insertEventReport(report0); 8311 dao.insertEventReport(report1); 8312 dao.insertAggregateReport(aggregateReport0); 8313 }); 8314 8315 // Execution 8316 mDatastoreManager.runInTransaction( 8317 dao -> { 8318 Pair<List<String>, List<String>> actualSources = 8319 dao.fetchMatchingSourcesUninstall( 8320 SourceFixture.ValidSourceParams.REGISTRANT, currentTime); 8321 // Source is ignored 8322 Truth.assertThat(actualSources.first.size()).isEqualTo(2); 8323 Truth.assertThat(actualSources.second.size()).isEqualTo(1); 8324 }); 8325 } 8326 8327 @Test fetchMatchingTriggersUninstall_outsideReportLifetime_deleteTriggers()8328 public void fetchMatchingTriggersUninstall_outsideReportLifetime_deleteTriggers() 8329 throws Exception { 8330 // Setup 8331 mocker.mockGetFlags(mMockFlags); 8332 doReturn(true).when(mMockFlags).getMeasurementEnableMinReportLifespanForUninstall(); 8333 doReturn(TimeUnit.DAYS.toSeconds(1)) 8334 .when(mMockFlags) 8335 .getMeasurementMinReportLifespanForUninstallSeconds(); 8336 8337 long currentTime = System.currentTimeMillis(); 8338 long baseEventTime = currentTime - DAYS.toMillis(3); 8339 long expiryTime = baseEventTime + DAYS.toMillis(30); 8340 8341 List<Source> sources = 8342 Arrays.asList( 8343 SourceFixture.getMinimalValidSourceBuilder() 8344 .setEventId(new UnsignedLong(1L)) 8345 .setId("source1") 8346 .setEventTime(baseEventTime) 8347 .setExpiryTime(expiryTime) 8348 .build()); 8349 8350 List<Trigger> triggers = 8351 Arrays.asList( 8352 TriggerFixture.getValidTriggerBuilder() 8353 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 8354 .setId("trigger1") 8355 .setTriggerTime( 8356 currentTime - DAYS.toMillis(1) - TimeUnit.HOURS.toMillis(1)) 8357 .build(), 8358 TriggerFixture.getValidTriggerBuilder() 8359 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 8360 .setId("trigger2") 8361 .setTriggerTime(currentTime - DAYS.toMillis(2)) 8362 .build(), 8363 TriggerFixture.getValidTriggerBuilder() 8364 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 8365 .setId("trigger3") 8366 .setTriggerTime(currentTime - DAYS.toMillis(3)) 8367 .build()); 8368 8369 EventReport report0 = 8370 createEventReportForSourceAndTriggerForUninstall( 8371 "report0", sources.get(0), triggers.get(0)); 8372 EventReport report1 = 8373 createEventReportForSourceAndTriggerForUninstall( 8374 "report1", sources.get(0), triggers.get(1)); 8375 8376 AggregateReport aggregateReport0 = 8377 createAggregateReportForSourceAndTrigger( 8378 "areport0", sources.get(0), triggers.get(2)); 8379 8380 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 8381 sources.forEach(source -> insertSource(source, source.getId())); 8382 triggers.forEach(trigger -> AbstractDbIntegrationTest.insertToDb(trigger, db)); 8383 8384 mDatastoreManager.runInTransaction( 8385 (dao) -> { 8386 dao.insertEventReport(report0); 8387 dao.insertEventReport(report1); 8388 dao.insertAggregateReport(aggregateReport0); 8389 }); 8390 8391 // Execution 8392 mDatastoreManager.runInTransaction( 8393 dao -> { 8394 Pair<List<String>, List<String>> actualTriggers = 8395 dao.fetchMatchingTriggersUninstall( 8396 TriggerFixture.ValidTriggerParams.REGISTRANT, currentTime); 8397 // Triggers are deleted 8398 Truth.assertThat(actualTriggers.first.size()).isEqualTo(3); 8399 Truth.assertThat(actualTriggers.second.size()).isEqualTo(0); 8400 }); 8401 } 8402 8403 @Test fetchMatchingTriggersUninstall_withinReportLifetime_ignoreTriggers()8404 public void fetchMatchingTriggersUninstall_withinReportLifetime_ignoreTriggers() 8405 throws Exception { 8406 // Setup 8407 mocker.mockGetFlags(mMockFlags); 8408 doReturn(true).when(mMockFlags).getMeasurementEnableMinReportLifespanForUninstall(); 8409 doReturn(TimeUnit.DAYS.toSeconds(1)) 8410 .when(mMockFlags) 8411 .getMeasurementMinReportLifespanForUninstallSeconds(); 8412 8413 long currentTime = System.currentTimeMillis(); 8414 long baseEventTime = currentTime - DAYS.toMillis(3); 8415 long expiryTime = baseEventTime + DAYS.toMillis(30); 8416 8417 List<Source> sources = 8418 Arrays.asList( 8419 SourceFixture.getMinimalValidSourceBuilder() 8420 .setEventId(new UnsignedLong(1L)) 8421 .setId("source1") 8422 .setEventTime(baseEventTime) 8423 .setExpiryTime(expiryTime) 8424 .build()); 8425 8426 List<Trigger> triggers = 8427 Arrays.asList( 8428 TriggerFixture.getValidTriggerBuilder() 8429 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 8430 .setId("trigger1") 8431 .setTriggerTime(currentTime - TimeUnit.HOURS.toMillis(23)) 8432 .build(), 8433 TriggerFixture.getValidTriggerBuilder() 8434 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 8435 .setId("trigger2") 8436 .setTriggerTime(currentTime - TimeUnit.HOURS.toMillis(1)) 8437 .build(), 8438 TriggerFixture.getValidTriggerBuilder() 8439 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 8440 .setId("trigger3") 8441 .setTriggerTime(currentTime - TimeUnit.HOURS.toMillis(12)) 8442 .build()); 8443 8444 EventReport report0 = 8445 createEventReportForSourceAndTriggerForUninstall( 8446 "report0", sources.get(0), triggers.get(0)); 8447 EventReport report1 = 8448 createEventReportForSourceAndTriggerForUninstall( 8449 "report1", sources.get(0), triggers.get(1)); 8450 8451 AggregateReport aggregateReport0 = 8452 createAggregateReportForSourceAndTrigger( 8453 "areport0", sources.get(0), triggers.get(2)); 8454 8455 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 8456 sources.forEach(source -> insertSource(source, source.getId())); 8457 triggers.forEach(trigger -> AbstractDbIntegrationTest.insertToDb(trigger, db)); 8458 8459 mDatastoreManager.runInTransaction( 8460 (dao) -> { 8461 dao.insertEventReport(report0); 8462 dao.insertEventReport(report1); 8463 dao.insertAggregateReport(aggregateReport0); 8464 }); 8465 8466 // Execution 8467 mDatastoreManager.runInTransaction( 8468 dao -> { 8469 Pair<List<String>, List<String>> actualTriggers = 8470 dao.fetchMatchingTriggersUninstall( 8471 TriggerFixture.ValidTriggerParams.REGISTRANT, currentTime); 8472 // Triggers are ignored 8473 Truth.assertThat(actualTriggers.first.size()).isEqualTo(0); 8474 Truth.assertThat(actualTriggers.second.size()).isEqualTo(3); 8475 }); 8476 } 8477 8478 @Test fetchMatchingTriggersUninstall_deleteAndIgnoreTriggers()8479 public void fetchMatchingTriggersUninstall_deleteAndIgnoreTriggers() throws Exception { 8480 // Setup 8481 mocker.mockGetFlags(mMockFlags); 8482 doReturn(true).when(mMockFlags).getMeasurementEnableMinReportLifespanForUninstall(); 8483 doReturn(TimeUnit.DAYS.toSeconds(1)) 8484 .when(mMockFlags) 8485 .getMeasurementMinReportLifespanForUninstallSeconds(); 8486 8487 long currentTime = System.currentTimeMillis(); 8488 long baseEventTime = currentTime - DAYS.toMillis(3); 8489 long expiryTime = baseEventTime + DAYS.toMillis(30); 8490 8491 List<Source> sources = 8492 Arrays.asList( 8493 SourceFixture.getMinimalValidSourceBuilder() 8494 .setEventId(new UnsignedLong(1L)) 8495 .setId("source1") 8496 .setEventTime(baseEventTime) 8497 .setExpiryTime(expiryTime) 8498 .build(), 8499 SourceFixture.getMinimalValidSourceBuilder() 8500 .setEventId(new UnsignedLong(1L)) 8501 .setId("source2") 8502 .setEventTime(baseEventTime) 8503 .setExpiryTime(expiryTime) 8504 .build(), 8505 SourceFixture.getMinimalValidSourceBuilder() 8506 .setEventId(new UnsignedLong(1L)) 8507 .setId("source3") 8508 .setEventTime(baseEventTime) 8509 .setExpiryTime(expiryTime) 8510 .build()); 8511 8512 List<Trigger> triggers = 8513 Arrays.asList( 8514 TriggerFixture.getValidTriggerBuilder() 8515 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 8516 .setId("trigger1") 8517 .setTriggerTime(currentTime) 8518 .build(), 8519 TriggerFixture.getValidTriggerBuilder() 8520 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 8521 .setId("trigger2") 8522 .setTriggerTime(currentTime - DAYS.toMillis(2)) 8523 .build(), 8524 TriggerFixture.getValidTriggerBuilder() 8525 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 8526 .setId("trigger3") 8527 .setTriggerTime(currentTime - DAYS.toMillis(3)) 8528 .build()); 8529 8530 EventReport report0 = 8531 createEventReportForSourceAndTriggerForUninstall( 8532 "report0", sources.get(0), triggers.get(0)); 8533 EventReport report1 = 8534 createEventReportForSourceAndTriggerForUninstall( 8535 "report1", sources.get(1), triggers.get(1)); 8536 8537 AggregateReport aggregateReport0 = 8538 createAggregateReportForSourceAndTrigger( 8539 "areport0", sources.get(2), triggers.get(2)); 8540 8541 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 8542 sources.forEach(source -> insertSource(source, source.getId())); 8543 triggers.forEach(trigger -> AbstractDbIntegrationTest.insertToDb(trigger, db)); 8544 8545 mDatastoreManager.runInTransaction( 8546 (dao) -> { 8547 dao.insertEventReport(report0); 8548 dao.insertEventReport(report1); 8549 dao.insertAggregateReport(aggregateReport0); 8550 }); 8551 8552 // Execution 8553 mDatastoreManager.runInTransaction( 8554 dao -> { 8555 Pair<List<String>, List<String>> actualTriggers = 8556 dao.fetchMatchingTriggersUninstall( 8557 TriggerFixture.ValidTriggerParams.REGISTRANT, currentTime); 8558 // Triggers are deleted 8559 Truth.assertThat(actualTriggers.first.size()).isEqualTo(2); 8560 Truth.assertThat(actualTriggers.second.size()).isEqualTo(1); 8561 }); 8562 } 8563 8564 @Test fetchMatchingSourcesTriggersUninstall_noReportsDelete()8565 public void fetchMatchingSourcesTriggersUninstall_noReportsDelete() throws Exception { 8566 // Setup 8567 mocker.mockGetFlags(mMockFlags); 8568 doReturn(true).when(mMockFlags).getMeasurementEnableMinReportLifespanForUninstall(); 8569 doReturn(TimeUnit.DAYS.toSeconds(1)) 8570 .when(mMockFlags) 8571 .getMeasurementMinReportLifespanForUninstallSeconds(); 8572 8573 long currentTime = System.currentTimeMillis(); 8574 long baseEventTime = currentTime - DAYS.toMillis(3); 8575 long expiryTime = baseEventTime + DAYS.toMillis(30); 8576 8577 List<Source> sources = 8578 Arrays.asList( 8579 SourceFixture.getMinimalValidSourceBuilder() 8580 .setEventId(new UnsignedLong(1L)) 8581 .setId("source1") 8582 .setEventTime(baseEventTime) 8583 .setExpiryTime(expiryTime) 8584 .build(), 8585 SourceFixture.getMinimalValidSourceBuilder() 8586 .setEventId(new UnsignedLong(1L)) 8587 .setId("source2") 8588 .setEventTime(baseEventTime) 8589 .setExpiryTime(expiryTime) 8590 .build()); 8591 8592 List<Trigger> triggers = 8593 Arrays.asList( 8594 TriggerFixture.getValidTriggerBuilder() 8595 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 8596 .setId("trigger1") 8597 .setTriggerTime(currentTime) 8598 .build(), 8599 TriggerFixture.getValidTriggerBuilder() 8600 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 8601 .setId("trigger2") 8602 .setTriggerTime(currentTime - DAYS.toMillis(2)) 8603 .build()); 8604 8605 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 8606 sources.forEach(source -> insertSource(source, source.getId())); 8607 triggers.forEach(trigger -> AbstractDbIntegrationTest.insertToDb(trigger, db)); 8608 8609 // Execution 8610 mDatastoreManager.runInTransaction( 8611 dao -> { 8612 Pair<List<String>, List<String>> actualTriggers = 8613 dao.fetchMatchingTriggersUninstall( 8614 TriggerFixture.ValidTriggerParams.REGISTRANT, currentTime); 8615 Pair<List<String>, List<String>> actualSources = 8616 dao.fetchMatchingSourcesUninstall( 8617 SourceFixture.ValidSourceParams.REGISTRANT, currentTime); 8618 8619 // All Sources are deleted 8620 Truth.assertThat(actualSources.first.size()).isEqualTo(2); 8621 Truth.assertThat(actualSources.second.size()).isEqualTo(0); 8622 8623 // All Triggers are deleted 8624 Truth.assertThat(actualTriggers.first.size()).isEqualTo(2); 8625 Truth.assertThat(actualTriggers.second.size()).isEqualTo(0); 8626 }); 8627 } 8628 8629 @Test deletePendingFakeEventReportsForSources_success()8630 public void deletePendingFakeEventReportsForSources_success() { 8631 // Setup 8632 long baseTime = SOURCE_EVENT_TIME; 8633 // Sources 8634 insertSource(SourceFixture.getValidSource(), "S1"); 8635 insertSource(SourceFixture.getValidSource(), "S2"); 8636 insertSource(SourceFixture.getValidSource(), "S3"); 8637 insertSource(SourceFixture.getValidSource(), "S4"); 8638 8639 mDatastoreManager.runInTransaction( 8640 (dao) -> { 8641 // Event reports 8642 // Should get deleted 8643 EventReport eventReport1 = 8644 EventReportFixture.getBaseEventReportBuild() 8645 .setId("Event1") 8646 .setSourceId("S1") 8647 .setReportTime(baseTime + TimeUnit.HOURS.toMillis(1)) 8648 // trigger time > source event time => fake report 8649 .setTriggerTime(baseTime + TimeUnit.HOURS.toMillis(1) + 1L) 8650 .setStatus(EventReport.Status.PENDING) 8651 .setTriggerId(null) 8652 .build(); 8653 dao.insertEventReport(eventReport1); 8654 dao.insertAttribution( 8655 createAttribution( 8656 "Att1", 8657 Attribution.Scope.EVENT, 8658 "S1", 8659 null, 8660 eventReport1.getId())); 8661 8662 // Should not get deleted because S2 is not provided 8663 EventReport eventReport2 = 8664 EventReportFixture.getBaseEventReportBuild() 8665 .setId("Event2") 8666 .setSourceId("S2") 8667 .setReportTime(baseTime + TimeUnit.HOURS.toMillis(1)) 8668 // trigger time > source event time => fake report 8669 .setTriggerTime( 8670 baseTime 8671 + TimeUnit.HOURS.toMillis(1) 8672 + TimeUnit.MINUTES.toMillis(1)) 8673 .setStatus(EventReport.Status.PENDING) 8674 .setTriggerId(null) 8675 .build(); 8676 dao.insertEventReport(eventReport2); 8677 dao.insertAttribution( 8678 createAttribution( 8679 "Att2", 8680 Attribution.Scope.EVENT, 8681 "S2", 8682 null, 8683 eventReport2.getId())); 8684 8685 // Should not get deleted because it's a real report 8686 EventReport eventReport3 = 8687 EventReportFixture.getBaseEventReportBuild() 8688 .setId("Event3") 8689 .setSourceId("S1") 8690 .setReportTime(baseTime + TimeUnit.HOURS.toMillis(1)) 8691 // trigger time < source event time => real report 8692 .setTriggerTime(baseTime - 1L) 8693 .setStatus(EventReport.Status.PENDING) 8694 .setTriggerId(null) 8695 .build(); 8696 dao.insertEventReport(eventReport3); 8697 dao.insertAttribution( 8698 createAttribution( 8699 "Att3", 8700 Attribution.Scope.EVENT, 8701 "S1", 8702 null, 8703 eventReport3.getId())); 8704 8705 // Infeasible case, but it should not get deleted because its status is 8706 // DELIVERED 8707 EventReport eventReport4 = 8708 EventReportFixture.getBaseEventReportBuild() 8709 .setId("Event4") 8710 .setSourceId("S3") 8711 .setReportTime(baseTime + TimeUnit.HOURS.toMillis(1)) 8712 // trigger time > source event time => fake report 8713 .setTriggerTime( 8714 baseTime 8715 + TimeUnit.HOURS.toMillis(1) 8716 + TimeUnit.SECONDS.toMillis(1)) 8717 .setStatus(EventReport.Status.DELIVERED) 8718 .setTriggerId(null) 8719 .build(); 8720 dao.insertEventReport(eventReport4); 8721 dao.insertAttribution( 8722 createAttribution( 8723 "Att4", 8724 Attribution.Scope.EVENT, 8725 "S3", 8726 null, 8727 eventReport4.getId())); 8728 8729 // Execution 8730 dao.deleteFutureFakeEventReportsForSources(List.of("S1", "S3"), baseTime); 8731 8732 // Assertion 8733 assertThrows(DatastoreException.class, () -> dao.getEventReport("Event1")); 8734 assertEquals(eventReport2, dao.getEventReport("Event2")); 8735 assertEquals(eventReport3, dao.getEventReport("Event3")); 8736 assertEquals(eventReport4, dao.getEventReport("Event4")); 8737 }); 8738 8739 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 8740 assertEquals(4, DatabaseUtils.queryNumEntries(db, AttributionContract.TABLE)); 8741 Set<String> reportIds = new HashSet<>(); 8742 try (Cursor cursor = 8743 db.rawQuery( 8744 "SELECT " 8745 + AttributionContract.REPORT_ID 8746 + " FROM " 8747 + AttributionContract.TABLE, 8748 null)) { 8749 while (cursor.moveToNext()) { 8750 reportIds.add(cursor.getString(0)); 8751 } 8752 } 8753 assertEquals(Set.of("Event1", "Event2", "Event3", "Event4"), reportIds); 8754 } 8755 getSourceWithDifferentDestinations( int numDestinations, boolean hasAppDestinations, boolean hasWebDestinations, long eventTime, Uri publisher, String enrollmentId, @Source.Status int sourceStatus)8756 private static Source getSourceWithDifferentDestinations( 8757 int numDestinations, 8758 boolean hasAppDestinations, 8759 boolean hasWebDestinations, 8760 long eventTime, 8761 Uri publisher, 8762 String enrollmentId, 8763 @Source.Status int sourceStatus) { 8764 List<Uri> appDestinations = null; 8765 List<Uri> webDestinations = null; 8766 if (hasAppDestinations) { 8767 appDestinations = new ArrayList<>(); 8768 appDestinations.add(Uri.parse("android-app://com.app-destination")); 8769 } 8770 if (hasWebDestinations) { 8771 webDestinations = new ArrayList<>(); 8772 for (int i = 0; i < numDestinations; i++) { 8773 webDestinations.add( 8774 Uri.parse("https://web-destination-" + String.valueOf(i) + ".com")); 8775 } 8776 } 8777 long expiryTime = 8778 eventTime 8779 + TimeUnit.SECONDS.toMillis( 8780 MEASUREMENT_MAX_REPORTING_REGISTER_SOURCE_EXPIRATION_IN_SECONDS); 8781 return new Source.Builder() 8782 .setEventId(new UnsignedLong(0L)) 8783 .setEventTime(eventTime) 8784 .setExpiryTime(expiryTime) 8785 .setPublisher(publisher) 8786 .setAppDestinations(appDestinations) 8787 .setWebDestinations(webDestinations) 8788 .setEnrollmentId(enrollmentId) 8789 .setRegistrant(SourceFixture.ValidSourceParams.REGISTRANT) 8790 .setStatus(sourceStatus) 8791 .setRegistrationOrigin(REGISTRATION_ORIGIN) 8792 .build(); 8793 } 8794 getSourcesWithDifferentDestinations( int numSources, boolean hasAppDestinations, boolean hasWebDestinations, long eventTime, Uri publisher, String enrollmentId, @Source.Status int sourceStatus, Uri registrationOrigin)8795 private static List<Source> getSourcesWithDifferentDestinations( 8796 int numSources, 8797 boolean hasAppDestinations, 8798 boolean hasWebDestinations, 8799 long eventTime, 8800 Uri publisher, 8801 String enrollmentId, 8802 @Source.Status int sourceStatus, 8803 Uri registrationOrigin) { 8804 long expiryTime = 8805 eventTime 8806 + TimeUnit.SECONDS.toMillis( 8807 MEASUREMENT_MAX_REPORTING_REGISTER_SOURCE_EXPIRATION_IN_SECONDS); 8808 return getSourcesWithDifferentDestinations( 8809 numSources, 8810 hasAppDestinations, 8811 hasWebDestinations, 8812 eventTime, 8813 expiryTime, 8814 publisher, 8815 enrollmentId, 8816 sourceStatus, 8817 registrationOrigin); 8818 } 8819 getSourcesWithDifferentDestinations( int numSources, boolean hasAppDestinations, boolean hasWebDestinations, long eventTime, Uri publisher, String enrollmentId, @Source.Status int sourceStatus)8820 private static List<Source> getSourcesWithDifferentDestinations( 8821 int numSources, 8822 boolean hasAppDestinations, 8823 boolean hasWebDestinations, 8824 long eventTime, 8825 Uri publisher, 8826 String enrollmentId, 8827 @Source.Status int sourceStatus) { 8828 long expiryTime = 8829 eventTime 8830 + TimeUnit.SECONDS.toMillis( 8831 MEASUREMENT_MAX_REPORTING_REGISTER_SOURCE_EXPIRATION_IN_SECONDS); 8832 return getSourcesWithDifferentDestinations( 8833 numSources, 8834 hasAppDestinations, 8835 hasWebDestinations, 8836 eventTime, 8837 expiryTime, 8838 publisher, 8839 enrollmentId, 8840 sourceStatus, 8841 REGISTRATION_ORIGIN); 8842 } 8843 getSourcesWithDifferentDestinations( int numSources, boolean hasAppDestinations, boolean hasWebDestinations, long eventTime, long expiryTime, Uri publisher, String enrollmentId, @Source.Status int sourceStatus, Uri registrationOrigin)8844 private static List<Source> getSourcesWithDifferentDestinations( 8845 int numSources, 8846 boolean hasAppDestinations, 8847 boolean hasWebDestinations, 8848 long eventTime, 8849 long expiryTime, 8850 Uri publisher, 8851 String enrollmentId, 8852 @Source.Status int sourceStatus, 8853 Uri registrationOrigin) { 8854 List<Source> sources = new ArrayList<>(); 8855 for (int i = 0; i < numSources; i++) { 8856 Source.Builder sourceBuilder = 8857 new Source.Builder() 8858 .setEventId(new UnsignedLong(0L)) 8859 .setEventTime(eventTime) 8860 .setExpiryTime(expiryTime) 8861 .setPublisher(publisher) 8862 .setEnrollmentId(enrollmentId) 8863 .setRegistrant(SourceFixture.ValidSourceParams.REGISTRANT) 8864 .setStatus(sourceStatus) 8865 .setRegistrationOrigin(registrationOrigin); 8866 if (hasAppDestinations) { 8867 sourceBuilder.setAppDestinations( 8868 List.of(Uri.parse("android-app://app-destination-" + String.valueOf(i)))); 8869 } 8870 if (hasWebDestinations) { 8871 sourceBuilder.setWebDestinations( 8872 List.of( 8873 Uri.parse( 8874 "https://web-destination-" + String.valueOf(i) + ".com"))); 8875 } 8876 sources.add(sourceBuilder.build()); 8877 } 8878 return sources; 8879 } 8880 getSourcesWithDifferentRegistrationOrigins( int numSources, List<Uri> appDestinations, List<Uri> webDestinations, long eventTime, Uri publisher, @Source.Status int sourceStatus)8881 private static List<Source> getSourcesWithDifferentRegistrationOrigins( 8882 int numSources, 8883 List<Uri> appDestinations, 8884 List<Uri> webDestinations, 8885 long eventTime, 8886 Uri publisher, 8887 @Source.Status int sourceStatus) { 8888 long expiryTime = 8889 eventTime 8890 + TimeUnit.SECONDS.toMillis( 8891 MEASUREMENT_MAX_REPORTING_REGISTER_SOURCE_EXPIRATION_IN_SECONDS); 8892 return getSourcesWithDifferentRegistrationOrigins( 8893 numSources, 8894 appDestinations, 8895 webDestinations, 8896 eventTime, 8897 expiryTime, 8898 publisher, 8899 sourceStatus); 8900 } 8901 getSourcesWithDifferentRegistrationOrigins( int numSources, List<Uri> appDestinations, List<Uri> webDestinations, long eventTime, long expiryTime, Uri publisher, @Source.Status int sourceStatus)8902 private static List<Source> getSourcesWithDifferentRegistrationOrigins( 8903 int numSources, 8904 List<Uri> appDestinations, 8905 List<Uri> webDestinations, 8906 long eventTime, 8907 long expiryTime, 8908 Uri publisher, 8909 @Source.Status int sourceStatus) { 8910 List<Source> sources = new ArrayList<>(); 8911 for (int i = 0; i < numSources; i++) { 8912 Source.Builder sourceBuilder = 8913 new Source.Builder() 8914 .setEventId(new UnsignedLong(0L)) 8915 .setEventTime(eventTime) 8916 .setExpiryTime(expiryTime) 8917 .setPublisher(publisher) 8918 .setRegistrant(SourceFixture.ValidSourceParams.REGISTRANT) 8919 .setStatus(sourceStatus) 8920 .setAppDestinations(getNullableUriList(appDestinations)) 8921 .setWebDestinations(getNullableUriList(webDestinations)) 8922 .setEnrollmentId("enrollment-id") 8923 .setRegistrationOrigin( 8924 WebUtil.validUri("https://subdomain" + i + ".example.test")); 8925 sources.add(sourceBuilder.build()); 8926 } 8927 return sources; 8928 } 8929 getAttributionsWithDifferentReportingOrigins( int numAttributions, Uri destinationSite, long triggerTime, Uri sourceSite, String registrant)8930 private static List<Attribution> getAttributionsWithDifferentReportingOrigins( 8931 int numAttributions, 8932 Uri destinationSite, 8933 long triggerTime, 8934 Uri sourceSite, 8935 String registrant) { 8936 List<Attribution> attributions = new ArrayList<>(); 8937 for (int i = 0; i < numAttributions; i++) { 8938 Attribution.Builder attributionBuilder = 8939 new Attribution.Builder() 8940 .setTriggerTime(triggerTime) 8941 .setSourceSite(sourceSite.toString()) 8942 .setSourceOrigin(sourceSite.toString()) 8943 .setDestinationSite(destinationSite.toString()) 8944 .setDestinationOrigin(destinationSite.toString()) 8945 .setEnrollmentId("enrollment-id") 8946 .setRegistrationOrigin( 8947 WebUtil.validUri("https://subdomain" + i + ".example.test")) 8948 .setRegistrant(registrant); 8949 attributions.add(attributionBuilder.build()); 8950 } 8951 return attributions; 8952 } 8953 insertAttribution(Attribution attribution)8954 private static void insertAttribution(Attribution attribution) { 8955 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 8956 ContentValues values = new ContentValues(); 8957 values.put(AttributionContract.ID, UUID.randomUUID().toString()); 8958 values.put(AttributionContract.SOURCE_SITE, attribution.getSourceSite()); 8959 values.put(AttributionContract.DESTINATION_SITE, attribution.getDestinationSite()); 8960 values.put(AttributionContract.ENROLLMENT_ID, attribution.getEnrollmentId()); 8961 values.put(AttributionContract.TRIGGER_TIME, attribution.getTriggerTime()); 8962 values.put(AttributionContract.SOURCE_ID, attribution.getSourceId()); 8963 values.put(AttributionContract.TRIGGER_ID, attribution.getTriggerId()); 8964 values.put( 8965 AttributionContract.REGISTRATION_ORIGIN, 8966 attribution.getRegistrationOrigin().toString()); 8967 long row = db.insert("msmt_attribution", null, values); 8968 assertNotEquals("Attribution insertion failed", -1, row); 8969 } 8970 createAttribution( String attributionId, String sourceId, String triggerId)8971 private static Attribution createAttribution( 8972 String attributionId, 8973 String sourceId, 8974 String triggerId) { 8975 return createAttribution(attributionId, Attribution.Scope.EVENT, sourceId, triggerId); 8976 } 8977 createAttribution( String attributionId, @Attribution.Scope int scope, String sourceId, String triggerId)8978 private static Attribution createAttribution( 8979 String attributionId, 8980 @Attribution.Scope int scope, 8981 String sourceId, 8982 String triggerId) { 8983 return createAttribution(attributionId, scope, sourceId, triggerId, null); 8984 } 8985 createAttribution( String attributionId, @Attribution.Scope int scope, String sourceId, String triggerId, String reportId)8986 private static Attribution createAttribution( 8987 String attributionId, 8988 @Attribution.Scope int scope, 8989 String sourceId, 8990 String triggerId, 8991 String reportId) { 8992 return new Attribution.Builder() 8993 .setId(attributionId) 8994 .setScope(scope) 8995 .setTriggerTime(0L) 8996 .setSourceSite("android-app://source.app") 8997 .setSourceOrigin("android-app://source.app") 8998 .setDestinationSite("android-app://destination.app") 8999 .setDestinationOrigin("android-app://destination.app") 9000 .setEnrollmentId("enrollment-id-") 9001 .setRegistrant("android-app://registrant.app") 9002 .setSourceId(sourceId) 9003 .setTriggerId(triggerId) 9004 .setRegistrationOrigin(REGISTRATION_ORIGIN) 9005 .setReportId(reportId) 9006 .build(); 9007 } 9008 insertSource(Source source)9009 private static void insertSource(Source source) { 9010 insertSource(source, UUID.randomUUID().toString()); 9011 } 9012 9013 // This is needed because MeasurementDao::insertSource inserts a default value for status. insertSource(Source source, String sourceId)9014 private static void insertSource(Source source, String sourceId) { 9015 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 9016 ContentValues values = new ContentValues(); 9017 values.put(SourceContract.ID, sourceId); 9018 if (source.getEventId() != null) { 9019 values.put(SourceContract.EVENT_ID, source.getEventId().getValue()); 9020 } 9021 values.put(SourceContract.PUBLISHER, source.getPublisher().toString()); 9022 values.put(SourceContract.PUBLISHER_TYPE, source.getPublisherType()); 9023 values.put(SourceContract.ENROLLMENT_ID, source.getEnrollmentId()); 9024 values.put(SourceContract.EVENT_TIME, source.getEventTime()); 9025 values.put(SourceContract.EXPIRY_TIME, source.getExpiryTime()); 9026 values.put(SourceContract.PRIORITY, source.getPriority()); 9027 values.put(SourceContract.STATUS, source.getStatus()); 9028 values.put(SourceContract.SOURCE_TYPE, source.getSourceType().toString()); 9029 values.put(SourceContract.REGISTRANT, source.getRegistrant().toString()); 9030 values.put(SourceContract.INSTALL_ATTRIBUTION_WINDOW, source.getInstallAttributionWindow()); 9031 values.put(SourceContract.INSTALL_COOLDOWN_WINDOW, source.getInstallCooldownWindow()); 9032 values.put(SourceContract.ATTRIBUTION_MODE, source.getAttributionMode()); 9033 values.put(SourceContract.AGGREGATE_SOURCE, source.getAggregateSource()); 9034 values.put(SourceContract.FILTER_DATA, source.getFilterDataString()); 9035 values.put(SourceContract.SHARED_FILTER_DATA_KEYS, source.getSharedFilterDataKeys()); 9036 values.put(SourceContract.AGGREGATE_CONTRIBUTIONS, source.getAggregateContributions()); 9037 values.put(SourceContract.DEBUG_REPORTING, source.isDebugReporting()); 9038 values.put(SourceContract.DEBUG_AD_ID, source.getDebugAdId()); 9039 values.put(SourceContract.INSTALL_TIME, source.getInstallTime()); 9040 values.put(SourceContract.REGISTRATION_ID, source.getRegistrationId()); 9041 values.put(SourceContract.SHARED_AGGREGATION_KEYS, source.getSharedAggregationKeys()); 9042 values.put(SourceContract.REGISTRATION_ORIGIN, source.getRegistrationOrigin().toString()); 9043 values.put(SourceContract.DESTINATION_LIMIT_PRIORITY, source.getDestinationLimitPriority()); 9044 values.put(SourceContract.IS_INSTALL_ATTRIBUTED, source.isInstallAttributed()); 9045 values.put( 9046 SourceContract.AGGREGATE_DEBUG_REPORT_CONTRIBUTIONS, 9047 source.getAggregateDebugReportContributions()); 9048 values.put( 9049 SourceContract.REINSTALL_REATTRIBUTION_WINDOW, 9050 source.getReinstallReattributionWindow()); 9051 if (source.getDebugKey() != null) { 9052 values.put(SourceContract.DEBUG_KEY, source.getDebugKey().getValue()); 9053 } 9054 long row = db.insert(SourceContract.TABLE, null, values); 9055 assertNotEquals("Source insertion failed", -1, row); 9056 9057 maybeInsertSourceDestinations(db, source, sourceId); 9058 } 9059 getNullableUnsignedLong(@ullable UnsignedLong ulong)9060 private static Long getNullableUnsignedLong(@Nullable UnsignedLong ulong) { 9061 return Optional.ofNullable(ulong).map(UnsignedLong::getValue).orElse(null); 9062 } 9063 insertTrigger(Trigger trigger, String triggerId)9064 private static void insertTrigger(Trigger trigger, String triggerId) { 9065 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 9066 ContentValues values = new ContentValues(); 9067 values.put(TriggerContract.ID, triggerId); 9068 values.put( 9069 TriggerContract.ATTRIBUTION_DESTINATION, 9070 trigger.getAttributionDestination().toString()); 9071 values.put(TriggerContract.DESTINATION_TYPE, trigger.getDestinationType()); 9072 values.put(TriggerContract.TRIGGER_TIME, trigger.getTriggerTime()); 9073 values.put(TriggerContract.EVENT_TRIGGERS, trigger.getEventTriggers()); 9074 values.put(TriggerContract.STATUS, Trigger.Status.PENDING); 9075 values.put(TriggerContract.ENROLLMENT_ID, trigger.getEnrollmentId()); 9076 values.put(TriggerContract.REGISTRANT, trigger.getRegistrant().toString()); 9077 values.put(TriggerContract.AGGREGATE_TRIGGER_DATA, trigger.getAggregateTriggerData()); 9078 values.put(TriggerContract.AGGREGATE_VALUES, trigger.getAggregateValuesString()); 9079 values.put( 9080 TriggerContract.AGGREGATABLE_DEDUPLICATION_KEYS, 9081 trigger.getAggregateDeduplicationKeys()); 9082 values.put(TriggerContract.FILTERS, trigger.getFilters()); 9083 values.put(TriggerContract.NOT_FILTERS, trigger.getNotFilters()); 9084 values.put(TriggerContract.DEBUG_KEY, getNullableUnsignedLong(trigger.getDebugKey())); 9085 values.put(TriggerContract.DEBUG_REPORTING, trigger.isDebugReporting()); 9086 values.put(TriggerContract.AD_ID_PERMISSION, trigger.hasAdIdPermission()); 9087 values.put(TriggerContract.AR_DEBUG_PERMISSION, trigger.hasArDebugPermission()); 9088 values.put(TriggerContract.ATTRIBUTION_CONFIG, trigger.getAttributionConfig()); 9089 values.put(TriggerContract.X_NETWORK_KEY_MAPPING, trigger.getAdtechKeyMapping()); 9090 values.put(TriggerContract.DEBUG_JOIN_KEY, trigger.getDebugJoinKey()); 9091 values.put(TriggerContract.PLATFORM_AD_ID, trigger.getPlatformAdId()); 9092 values.put(TriggerContract.DEBUG_AD_ID, trigger.getDebugAdId()); 9093 values.put(TriggerContract.REGISTRATION_ORIGIN, trigger.getRegistrationOrigin().toString()); 9094 values.put( 9095 TriggerContract.AGGREGATION_COORDINATOR_ORIGIN, 9096 getNullableUriString((List<Uri>) trigger.getAggregationCoordinatorOrigin())); 9097 values.put( 9098 TriggerContract.AGGREGATABLE_SOURCE_REGISTRATION_TIME_CONFIG, 9099 trigger.getAggregatableSourceRegistrationTimeConfig().name()); 9100 values.put(TriggerContract.TRIGGER_CONTEXT_ID, trigger.getTriggerContextId()); 9101 values.put(TriggerContract.ATTRIBUTION_SCOPES, trigger.getAttributionScopesString()); 9102 values.put( 9103 TriggerContract.AGGREGATABLE_FILTERING_ID_MAX_BYTES, 9104 trigger.getAggregatableFilteringIdMaxBytes()); 9105 values.put( 9106 TriggerContract.AGGREGATE_DEBUG_REPORTING, 9107 trigger.getAggregateDebugReportingString()); 9108 values.put(TriggerContract.NAMED_BUDGETS, trigger.getNamedBudgetsString()); 9109 long row = db.insert(TriggerContract.TABLE, null, values); 9110 assertThat(row).isNotEqualTo(-1); 9111 } 9112 getNullableUriString(List<Uri> uriList)9113 private static String getNullableUriString(List<Uri> uriList) { 9114 return Optional.ofNullable(uriList).map(uris -> uris.get(0).toString()).orElse(null); 9115 } 9116 9117 /** Test that the AsyncRegistration is inserted correctly. */ 9118 @Test testInsertAsyncRegistration()9119 public void testInsertAsyncRegistration() { 9120 AsyncRegistration validAsyncRegistration = 9121 AsyncRegistrationFixture.getValidAsyncRegistration(); 9122 String validAsyncRegistrationId = validAsyncRegistration.getId(); 9123 9124 mDatastoreManager.runInTransaction( 9125 (dao) -> dao.insertAsyncRegistration(validAsyncRegistration)); 9126 9127 try (Cursor cursor = 9128 MeasurementDbHelper.getInstance() 9129 .getReadableDatabase() 9130 .query( 9131 AsyncRegistrationContract.TABLE, 9132 null, 9133 AsyncRegistrationContract.ID + " = ? ", 9134 new String[] {validAsyncRegistrationId}, 9135 null, 9136 null, 9137 null)) { 9138 9139 assertTrue(cursor.moveToNext()); 9140 AsyncRegistration asyncRegistration = 9141 SqliteObjectMapper.constructAsyncRegistration(cursor); 9142 assertNotNull(asyncRegistration); 9143 assertNotNull(asyncRegistration.getId()); 9144 assertEquals(asyncRegistration.getId(), validAsyncRegistration.getId()); 9145 assertNotNull(asyncRegistration.getRegistrationUri()); 9146 assertNotNull(asyncRegistration.getTopOrigin()); 9147 assertEquals(asyncRegistration.getTopOrigin(), validAsyncRegistration.getTopOrigin()); 9148 assertNotNull(asyncRegistration.getRegistrant()); 9149 assertEquals(asyncRegistration.getRegistrant(), validAsyncRegistration.getRegistrant()); 9150 assertNotNull(asyncRegistration.getSourceType()); 9151 assertEquals(asyncRegistration.getSourceType(), validAsyncRegistration.getSourceType()); 9152 assertEquals( 9153 asyncRegistration.getDebugKeyAllowed(), 9154 validAsyncRegistration.getDebugKeyAllowed()); 9155 assertEquals(asyncRegistration.getRetryCount(), validAsyncRegistration.getRetryCount()); 9156 assertEquals( 9157 asyncRegistration.getRequestTime(), validAsyncRegistration.getRequestTime()); 9158 assertNotNull(asyncRegistration.getOsDestination()); 9159 assertEquals( 9160 asyncRegistration.getOsDestination(), 9161 validAsyncRegistration.getOsDestination()); 9162 assertNotNull(asyncRegistration.getRegistrationUri()); 9163 assertEquals( 9164 asyncRegistration.getRegistrationUri(), 9165 validAsyncRegistration.getRegistrationUri()); 9166 assertEquals( 9167 asyncRegistration.getDebugKeyAllowed(), 9168 validAsyncRegistration.getDebugKeyAllowed()); 9169 assertEquals( 9170 asyncRegistration.getPlatformAdId(), validAsyncRegistration.getPlatformAdId()); 9171 assertEquals(asyncRegistration.getPostBody(), validAsyncRegistration.getPostBody()); 9172 assertEquals( 9173 asyncRegistration.getRedirectBehavior(), 9174 validAsyncRegistration.getRedirectBehavior()); 9175 } 9176 } 9177 9178 /** Test that records in AsyncRegistration queue are fetched properly. */ 9179 @Test testFetchNextQueuedAsyncRegistration_validRetryLimit()9180 public void testFetchNextQueuedAsyncRegistration_validRetryLimit() { 9181 AsyncRegistration asyncRegistration = AsyncRegistrationFixture.getValidAsyncRegistration(); 9182 String asyncRegistrationId = asyncRegistration.getId(); 9183 9184 mDatastoreManager.runInTransaction((dao) -> dao.insertAsyncRegistration(asyncRegistration)); 9185 mDatastoreManager.runInTransaction( 9186 (dao) -> { 9187 AsyncRegistration fetchedAsyncRegistration = 9188 dao.fetchNextQueuedAsyncRegistration((short) 1, new HashSet<>()); 9189 assertNotNull(fetchedAsyncRegistration); 9190 assertEquals(fetchedAsyncRegistration.getId(), asyncRegistrationId); 9191 fetchedAsyncRegistration.incrementRetryCount(); 9192 dao.updateRetryCount(fetchedAsyncRegistration); 9193 }); 9194 9195 mDatastoreManager.runInTransaction( 9196 (dao) -> { 9197 AsyncRegistration fetchedAsyncRegistration = 9198 dao.fetchNextQueuedAsyncRegistration((short) 1, new HashSet<>()); 9199 assertNull(fetchedAsyncRegistration); 9200 }); 9201 } 9202 9203 /** Test that records in AsyncRegistration queue are fetched properly. */ 9204 @Test testFetchNextQueuedAsyncRegistration_excludeByOrigin()9205 public void testFetchNextQueuedAsyncRegistration_excludeByOrigin() { 9206 Uri origin1 = Uri.parse("https://adtech1.test"); 9207 Uri origin2 = Uri.parse("https://adtech2.test"); 9208 Uri regUri1 = origin1.buildUpon().appendPath("/hello").build(); 9209 Uri regUri2 = origin2; 9210 AsyncRegistration asyncRegistration1 = 9211 AsyncRegistrationFixture.getValidAsyncRegistrationBuilder() 9212 .setRegistrationUri(regUri1) 9213 .build(); 9214 AsyncRegistration asyncRegistration2 = 9215 AsyncRegistrationFixture.getValidAsyncRegistrationBuilder() 9216 .setRegistrationUri(regUri2) 9217 .build(); 9218 9219 mDatastoreManager.runInTransaction( 9220 (dao) -> { 9221 dao.insertAsyncRegistration(asyncRegistration1); 9222 dao.insertAsyncRegistration(asyncRegistration2); 9223 }); 9224 // Should fetch none 9225 Set<Uri> excludedOrigins1 = Set.of(origin1, origin2); 9226 Optional<AsyncRegistration> optAsyncRegistration = 9227 mDatastoreManager.runInTransactionWithResult( 9228 (dao) -> dao.fetchNextQueuedAsyncRegistration((short) 4, excludedOrigins1)); 9229 assertTrue(optAsyncRegistration.isEmpty()); 9230 9231 // Should fetch only origin1 9232 Set<Uri> excludedOrigins2 = Set.of(origin2); 9233 optAsyncRegistration = 9234 mDatastoreManager.runInTransactionWithResult( 9235 (dao) -> dao.fetchNextQueuedAsyncRegistration((short) 4, excludedOrigins2)); 9236 assertTrue(optAsyncRegistration.isPresent()); 9237 assertEquals(regUri1, optAsyncRegistration.get().getRegistrationUri()); 9238 9239 // Should fetch only origin2 9240 Set<Uri> excludedOrigins3 = Set.of(origin1); 9241 optAsyncRegistration = 9242 mDatastoreManager.runInTransactionWithResult( 9243 (dao) -> dao.fetchNextQueuedAsyncRegistration((short) 4, excludedOrigins3)); 9244 assertTrue(optAsyncRegistration.isPresent()); 9245 assertEquals(regUri2, optAsyncRegistration.get().getRegistrationUri()); 9246 } 9247 9248 /** Test that AsyncRegistration is deleted correctly. */ 9249 @Test testDeleteAsyncRegistration()9250 public void testDeleteAsyncRegistration() { 9251 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 9252 AsyncRegistration asyncRegistration = AsyncRegistrationFixture.getValidAsyncRegistration(); 9253 String asyncRegistrationID = asyncRegistration.getId(); 9254 9255 mDatastoreManager.runInTransaction((dao) -> dao.insertAsyncRegistration(asyncRegistration)); 9256 try (Cursor cursor = 9257 MeasurementDbHelper.getInstance() 9258 .getReadableDatabase() 9259 .query( 9260 AsyncRegistrationContract.TABLE, 9261 null, 9262 AsyncRegistrationContract.ID + " = ? ", 9263 new String[] {asyncRegistration.getId().toString()}, 9264 null, 9265 null, 9266 null)) { 9267 assertTrue(cursor.moveToNext()); 9268 AsyncRegistration updateAsyncRegistration = 9269 SqliteObjectMapper.constructAsyncRegistration(cursor); 9270 assertNotNull(updateAsyncRegistration); 9271 } 9272 mDatastoreManager.runInTransaction( 9273 (dao) -> dao.deleteAsyncRegistration(asyncRegistration.getId())); 9274 9275 db.query( 9276 /* table */ AsyncRegistrationContract.TABLE, 9277 /* columns */ null, 9278 /* selection */ AsyncRegistrationContract.ID + " = ? ", 9279 /* selectionArgs */ new String[] {asyncRegistrationID.toString()}, 9280 /* groupBy */ null, 9281 /* having */ null, 9282 /* orderedBy */ null); 9283 9284 assertThat( 9285 db.query( 9286 /* table */ AsyncRegistrationContract.TABLE, 9287 /* columns */ null, 9288 /* selection */ AsyncRegistrationContract.ID + " = ? ", 9289 /* selectionArgs */ new String[] { 9290 asyncRegistrationID.toString() 9291 }, 9292 /* groupBy */ null, 9293 /* having */ null, 9294 /* orderedBy */ null) 9295 .getCount()) 9296 .isEqualTo(0); 9297 } 9298 9299 @Test testDeleteAsyncRegistration_missingRecord()9300 public void testDeleteAsyncRegistration_missingRecord() { 9301 mDatastoreManager.runInTransaction( 9302 (dao) -> 9303 assertThrows( 9304 "Async Registration already deleted", 9305 DatastoreException.class, 9306 () -> dao.deleteAsyncRegistration("missingAsyncRegId"))); 9307 } 9308 9309 @Test deleteAsyncRegistrations_success()9310 public void deleteAsyncRegistrations_success() { 9311 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 9312 AsyncRegistration ar1 = 9313 new AsyncRegistration.Builder() 9314 .setId("1") 9315 .setRegistrant(Uri.parse("android-app://installed-registrant1")) 9316 .setTopOrigin(Uri.parse("android-app://installed-registrant1")) 9317 .setAdIdPermission(false) 9318 .setType(AsyncRegistration.RegistrationType.APP_SOURCE) 9319 .setRequestTime(1) 9320 .setRegistrationId(ValidAsyncRegistrationParams.REGISTRATION_ID) 9321 .build(); 9322 9323 AsyncRegistration ar2 = 9324 new AsyncRegistration.Builder() 9325 .setId("2") 9326 .setRegistrant(Uri.parse("android-app://installed-registrant2")) 9327 .setTopOrigin(Uri.parse("android-app://installed-registrant2")) 9328 .setAdIdPermission(false) 9329 .setType(AsyncRegistration.RegistrationType.APP_SOURCE) 9330 .setRequestTime(Long.MAX_VALUE) 9331 .setRegistrationId(ValidAsyncRegistrationParams.REGISTRATION_ID) 9332 .build(); 9333 9334 AsyncRegistration ar3 = 9335 new AsyncRegistration.Builder() 9336 .setId("3") 9337 .setRegistrant(Uri.parse("android-app://installed-registrant3")) 9338 .setTopOrigin(Uri.parse("android-app://installed-registrant3")) 9339 .setAdIdPermission(false) 9340 .setType(AsyncRegistration.RegistrationType.APP_SOURCE) 9341 .setRequestTime(Long.MAX_VALUE) 9342 .setRegistrationId(ValidAsyncRegistrationParams.REGISTRATION_ID) 9343 .build(); 9344 9345 List<AsyncRegistration> asyncRegistrationList = List.of(ar1, ar2, ar3); 9346 asyncRegistrationList.forEach( 9347 asyncRegistration -> { 9348 ContentValues values = new ContentValues(); 9349 values.put(AsyncRegistrationContract.ID, asyncRegistration.getId()); 9350 values.put( 9351 AsyncRegistrationContract.REQUEST_TIME, 9352 asyncRegistration.getRequestTime()); 9353 values.put( 9354 AsyncRegistrationContract.REGISTRANT, 9355 asyncRegistration.getRegistrant().toString()); 9356 values.put( 9357 AsyncRegistrationContract.TOP_ORIGIN, 9358 asyncRegistration.getTopOrigin().toString()); 9359 values.put( 9360 AsyncRegistrationContract.REGISTRATION_ID, 9361 asyncRegistration.getRegistrationId()); 9362 db.insert(AsyncRegistrationContract.TABLE, /* nullColumnHack */ null, values); 9363 }); 9364 9365 mDatastoreManager.runInTransaction( 9366 (dao) -> dao.deleteAsyncRegistrations(List.of("1", "3"))); 9367 9368 assertThat( 9369 db.query( 9370 /* table */ AsyncRegistrationContract.TABLE, 9371 /* columns */ null, 9372 /* selection */ null, 9373 /* selectionArgs */ null, 9374 /* groupBy */ null, 9375 /* having */ null, 9376 /* orderedBy */ null) 9377 .getCount()) 9378 .isEqualTo(1); 9379 9380 assertThat( 9381 db.query( 9382 /* table */ AsyncRegistrationContract.TABLE, 9383 /* columns */ null, 9384 /* selection */ AsyncRegistrationContract.ID + " = ? ", 9385 /* selectionArgs */ new String[] {"2"}, 9386 /* groupBy */ null, 9387 /* having */ null, 9388 /* orderedBy */ null) 9389 .getCount()) 9390 .isEqualTo(1); 9391 } 9392 9393 /** Test that retry count in AsyncRegistration is updated correctly. */ 9394 @Test testUpdateAsyncRegistrationRetryCount()9395 public void testUpdateAsyncRegistrationRetryCount() { 9396 AsyncRegistration asyncRegistration = AsyncRegistrationFixture.getValidAsyncRegistration(); 9397 String asyncRegistrationId = asyncRegistration.getId(); 9398 long originalRetryCount = asyncRegistration.getRetryCount(); 9399 9400 mDatastoreManager.runInTransaction((dao) -> dao.insertAsyncRegistration(asyncRegistration)); 9401 mDatastoreManager.runInTransaction( 9402 (dao) -> { 9403 asyncRegistration.incrementRetryCount(); 9404 dao.updateRetryCount(asyncRegistration); 9405 }); 9406 9407 try (Cursor cursor = 9408 MeasurementDbHelper.getInstance() 9409 .getReadableDatabase() 9410 .query( 9411 AsyncRegistrationContract.TABLE, 9412 null, 9413 AsyncRegistrationContract.ID + " = ? ", 9414 new String[] {asyncRegistrationId}, 9415 null, 9416 null, 9417 null)) { 9418 assertTrue(cursor.moveToNext()); 9419 AsyncRegistration updateAsyncRegistration = 9420 SqliteObjectMapper.constructAsyncRegistration(cursor); 9421 assertNotNull(updateAsyncRegistration); 9422 assertTrue(updateAsyncRegistration.getRetryCount() == originalRetryCount + 1); 9423 } 9424 } 9425 9426 @Test getSource_fetchesMatchingSourceFromDb()9427 public void getSource_fetchesMatchingSourceFromDb() { 9428 // Setup - insert 2 sources with different IDs 9429 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 9430 String sourceId1 = "source1"; 9431 Source source1WithoutDestinations = 9432 SourceFixture.getMinimalValidSourceBuilder() 9433 .setId(sourceId1) 9434 .setAppDestinations(null) 9435 .setWebDestinations(null) 9436 .build(); 9437 Source source1WithDestinations = 9438 SourceFixture.getMinimalValidSourceBuilder() 9439 .setId(sourceId1) 9440 .setAppDestinations(null) 9441 .setWebDestinations(null) 9442 .build(); 9443 insertInDb(db, source1WithDestinations); 9444 String sourceId2 = "source2"; 9445 Source source2WithoutDestinations = 9446 SourceFixture.getMinimalValidSourceBuilder() 9447 .setId(sourceId2) 9448 .setAppDestinations(null) 9449 .setWebDestinations(null) 9450 .build(); 9451 Source source2WithDestinations = 9452 SourceFixture.getMinimalValidSourceBuilder() 9453 .setId(sourceId2) 9454 .setAppDestinations(null) 9455 .setWebDestinations(null) 9456 .build(); 9457 insertInDb(db, source2WithDestinations); 9458 9459 // Execution 9460 mDatastoreManager.runInTransaction( 9461 (dao) -> { 9462 assertEquals(source1WithoutDestinations, dao.getSource(sourceId1)); 9463 assertEquals(source2WithoutDestinations, dao.getSource(sourceId2)); 9464 }); 9465 } 9466 9467 @Test getSourceRegistrant_fetchesMatchingSourceFromDb()9468 public void getSourceRegistrant_fetchesMatchingSourceFromDb() { 9469 // Setup - insert 2 sources with different IDs 9470 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 9471 String sourceId1 = "source1"; 9472 String registrant1 = "android-app://registrant.app1"; 9473 Source source1WithDestinations = 9474 SourceFixture.getMinimalValidSourceBuilder() 9475 .setId(sourceId1) 9476 .setAppDestinations(null) 9477 .setWebDestinations(null) 9478 .setRegistrant(Uri.parse(registrant1)) 9479 .build(); 9480 insertInDb(db, source1WithDestinations); 9481 9482 String sourceId2 = "source2"; 9483 String registrant2 = "android-app://registrant.app1"; 9484 Source source2WithDestinations = 9485 SourceFixture.getMinimalValidSourceBuilder() 9486 .setId(sourceId2) 9487 .setAppDestinations(null) 9488 .setWebDestinations(null) 9489 .setRegistrant(Uri.parse(registrant2)) 9490 .build(); 9491 insertInDb(db, source2WithDestinations); 9492 9493 // Execution 9494 mDatastoreManager.runInTransaction( 9495 (dao) -> { 9496 assertEquals(registrant1, dao.getSourceRegistrant(sourceId1)); 9497 assertEquals(registrant2, dao.getSourceRegistrant(sourceId2)); 9498 }); 9499 } 9500 9501 @Test getSource_nonExistingInDb_throwsException()9502 public void getSource_nonExistingInDb_throwsException() { 9503 // Setup - insert 2 sources with different IDs 9504 mocker.mockGetFlags(mMockFlags); 9505 doReturn(true) 9506 .when(mMockFlags) 9507 .getMeasurementEnableDatastoreManagerThrowDatastoreException(); 9508 doReturn(1.0f).when(mMockFlags).getMeasurementThrowUnknownExceptionSamplingRate(); 9509 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 9510 String sourceId1 = "source1"; 9511 Source source1WithDestinations = 9512 SourceFixture.getMinimalValidSourceBuilder() 9513 .setId(sourceId1) 9514 .setAppDestinations(null) 9515 .setWebDestinations(null) 9516 .build(); 9517 insertInDb(db, source1WithDestinations); 9518 9519 // Execution 9520 try { 9521 mDatastoreManager.runInTransaction( 9522 (dao) -> { 9523 dao.getSource("random_source_id"); 9524 }); 9525 fail(); 9526 } catch (IllegalStateException e) { 9527 Throwable cause = e.getCause(); 9528 assertEquals(DatastoreException.class, cause.getClass()); 9529 assertEquals("Source retrieval failed. Id: random_source_id", cause.getMessage()); 9530 } 9531 } 9532 9533 @Test getSource_nonExistingInDbNoSampling_swallowException()9534 public void getSource_nonExistingInDbNoSampling_swallowException() { 9535 // Setup - insert 2 sources with different IDs 9536 mocker.mockGetFlags(mMockFlags); 9537 doReturn(true) 9538 .when(mMockFlags) 9539 .getMeasurementEnableDatastoreManagerThrowDatastoreException(); 9540 doReturn(0.0f).when(mMockFlags).getMeasurementThrowUnknownExceptionSamplingRate(); 9541 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 9542 String sourceId1 = "source1"; 9543 Source source1WithDestinations = 9544 SourceFixture.getMinimalValidSourceBuilder() 9545 .setId(sourceId1) 9546 .setAppDestinations(null) 9547 .setWebDestinations(null) 9548 .build(); 9549 insertInDb(db, source1WithDestinations); 9550 9551 // Execution 9552 assertFalse( 9553 mDatastoreManager.runInTransaction( 9554 (dao) -> { 9555 dao.getSource("random_source_id"); 9556 })); 9557 } 9558 9559 @Test getSource_nonExistingInDbThrowingDisabled_swallowException()9560 public void getSource_nonExistingInDbThrowingDisabled_swallowException() { 9561 // Setup - insert 2 sources with different IDs 9562 mocker.mockGetFlags(mMockFlags); 9563 doReturn(false) 9564 .when(mMockFlags) 9565 .getMeasurementEnableDatastoreManagerThrowDatastoreException(); 9566 doReturn(1.0f).when(mMockFlags).getMeasurementThrowUnknownExceptionSamplingRate(); 9567 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 9568 String sourceId1 = "source1"; 9569 Source source1WithDestinations = 9570 SourceFixture.getMinimalValidSourceBuilder() 9571 .setId(sourceId1) 9572 .setAppDestinations(null) 9573 .setWebDestinations(null) 9574 .build(); 9575 insertInDb(db, source1WithDestinations); 9576 9577 // Execution 9578 assertFalse( 9579 mDatastoreManager.runInTransaction( 9580 (dao) -> { 9581 dao.getSource("random_source_id"); 9582 })); 9583 } 9584 9585 @Test fetchMatchingAggregateReports_returnsMatchingReports()9586 public void fetchMatchingAggregateReports_returnsMatchingReports() { 9587 // setup - create reports for 3*3 combinations of source and trigger 9588 List<Source> sources = 9589 Arrays.asList( 9590 SourceFixture.getMinimalValidSourceBuilder() 9591 .setEventId(new UnsignedLong(1L)) 9592 .setId("source1") 9593 .build(), 9594 SourceFixture.getMinimalValidSourceBuilder() 9595 .setEventId(new UnsignedLong(2L)) 9596 .setId("source2") 9597 .build(), 9598 SourceFixture.getMinimalValidSourceBuilder() 9599 .setEventId(new UnsignedLong(3L)) 9600 .setId("source3") 9601 .build()); 9602 List<Trigger> triggers = 9603 Arrays.asList( 9604 TriggerFixture.getValidTriggerBuilder().setId("trigger1").build(), 9605 TriggerFixture.getValidTriggerBuilder().setId("trigger2").build(), 9606 TriggerFixture.getValidTriggerBuilder().setId("trigger3").build()); 9607 List<AggregateReport> reports = 9608 ImmutableList.of( 9609 createAggregateReportForSourceAndTrigger(sources.get(0), triggers.get(0)), 9610 createAggregateReportForSourceAndTrigger(sources.get(0), triggers.get(1)), 9611 createAggregateReportForSourceAndTrigger(sources.get(0), triggers.get(2)), 9612 createAggregateReportForSourceAndTrigger(sources.get(1), triggers.get(0)), 9613 createAggregateReportForSourceAndTrigger(sources.get(1), triggers.get(1)), 9614 createAggregateReportForSourceAndTrigger(sources.get(1), triggers.get(2)), 9615 createAggregateReportForSourceAndTrigger(sources.get(2), triggers.get(0)), 9616 createAggregateReportForSourceAndTrigger(sources.get(2), triggers.get(1)), 9617 createAggregateReportForSourceAndTrigger(sources.get(2), triggers.get(2))); 9618 9619 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 9620 sources.forEach(source -> insertSource(source, source.getId())); 9621 triggers.forEach(trigger -> AbstractDbIntegrationTest.insertToDb(trigger, db)); 9622 reports.forEach( 9623 report -> 9624 mDatastoreManager.runInTransaction( 9625 (dao) -> dao.insertAggregateReport(report))); 9626 9627 mDatastoreManager.runInTransaction( 9628 (dao) -> { 9629 // Execution 9630 List<AggregateReport> aggregateReports = 9631 dao.fetchMatchingAggregateReports( 9632 Arrays.asList(sources.get(1).getId(), "nonMatchingSource"), 9633 Arrays.asList(triggers.get(2).getId(), "nonMatchingTrigger")); 9634 assertEquals(5, aggregateReports.size()); 9635 9636 aggregateReports = 9637 dao.fetchMatchingAggregateReports( 9638 Arrays.asList(sources.get(0).getId(), sources.get(1).getId()), 9639 Collections.emptyList()); 9640 assertEquals(6, aggregateReports.size()); 9641 9642 aggregateReports = 9643 dao.fetchMatchingAggregateReports( 9644 Collections.emptyList(), 9645 Arrays.asList( 9646 triggers.get(0).getId(), triggers.get(2).getId())); 9647 assertEquals(6, aggregateReports.size()); 9648 9649 aggregateReports = 9650 dao.fetchMatchingAggregateReports( 9651 Arrays.asList( 9652 sources.get(0).getId(), 9653 sources.get(1).getId(), 9654 sources.get(2).getId()), 9655 Arrays.asList( 9656 triggers.get(0).getId(), 9657 triggers.get(1).getId(), 9658 triggers.get(2).getId())); 9659 assertEquals(9, aggregateReports.size()); 9660 }); 9661 } 9662 9663 @Test fetchMatchingEventReports_returnsMatchingReports()9664 public void fetchMatchingEventReports_returnsMatchingReports() throws Exception { 9665 // setup - create reports for 3*3 combinations of source and trigger 9666 List<Source> sources = 9667 Arrays.asList( 9668 SourceFixture.getMinimalValidSourceBuilder() 9669 .setEventId(new UnsignedLong(1L)) 9670 .setId("source1") 9671 .build(), 9672 SourceFixture.getMinimalValidSourceBuilder() 9673 .setEventId(new UnsignedLong(2L)) 9674 .setId("source2") 9675 .build(), 9676 SourceFixture.getMinimalValidSourceBuilder() 9677 .setEventId(new UnsignedLong(3L)) 9678 .setId("source3") 9679 .build()); 9680 List<Trigger> triggers = 9681 Arrays.asList( 9682 TriggerFixture.getValidTriggerBuilder() 9683 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 9684 .setId("trigger1") 9685 .build(), 9686 TriggerFixture.getValidTriggerBuilder() 9687 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 9688 .setId("trigger2") 9689 .build(), 9690 TriggerFixture.getValidTriggerBuilder() 9691 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 9692 .setId("trigger3") 9693 .build()); 9694 List<EventReport> reports = 9695 ImmutableList.of( 9696 createEventReportForSourceAndTrigger(sources.get(0), triggers.get(0)), 9697 createEventReportForSourceAndTrigger(sources.get(0), triggers.get(1)), 9698 createEventReportForSourceAndTrigger(sources.get(0), triggers.get(2)), 9699 createEventReportForSourceAndTrigger(sources.get(1), triggers.get(0)), 9700 createEventReportForSourceAndTrigger(sources.get(1), triggers.get(1)), 9701 createEventReportForSourceAndTrigger(sources.get(1), triggers.get(2)), 9702 createEventReportForSourceAndTrigger(sources.get(2), triggers.get(0)), 9703 createEventReportForSourceAndTrigger(sources.get(2), triggers.get(1)), 9704 createEventReportForSourceAndTrigger(sources.get(2), triggers.get(2))); 9705 9706 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 9707 sources.forEach(source -> insertSource(source, source.getId())); 9708 triggers.forEach(trigger -> AbstractDbIntegrationTest.insertToDb(trigger, db)); 9709 reports.forEach( 9710 report -> 9711 mDatastoreManager.runInTransaction((dao) -> dao.insertEventReport(report))); 9712 9713 mDatastoreManager.runInTransaction( 9714 (dao) -> { 9715 // Execution 9716 List<EventReport> eventReports = 9717 dao.fetchMatchingEventReports( 9718 Arrays.asList(sources.get(1).getId(), "nonMatchingSource"), 9719 Arrays.asList(triggers.get(2).getId(), "nonMatchingTrigger")); 9720 assertEquals(5, eventReports.size()); 9721 9722 eventReports = 9723 dao.fetchMatchingEventReports( 9724 Arrays.asList(sources.get(0).getId(), sources.get(1).getId()), 9725 Collections.emptyList()); 9726 assertEquals(6, eventReports.size()); 9727 9728 eventReports = 9729 dao.fetchMatchingEventReports( 9730 Collections.emptyList(), 9731 Arrays.asList( 9732 triggers.get(0).getId(), triggers.get(2).getId())); 9733 assertEquals(6, eventReports.size()); 9734 9735 eventReports = 9736 dao.fetchMatchingEventReports( 9737 Arrays.asList( 9738 sources.get(0).getId(), 9739 sources.get(1).getId(), 9740 sources.get(2).getId()), 9741 Arrays.asList( 9742 triggers.get(0).getId(), 9743 triggers.get(1).getId(), 9744 triggers.get(2).getId())); 9745 assertEquals(9, eventReports.size()); 9746 }); 9747 } 9748 9749 @Test fetchMatchingSources_bringsMatchingSources()9750 public void fetchMatchingSources_bringsMatchingSources() { 9751 // Setup 9752 Source source1 = 9753 SourceFixture.getMinimalValidSourceBuilder() 9754 .setEventId(new UnsignedLong(1L)) 9755 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 9756 .setEventTime(5000) 9757 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9758 .setId("source1") 9759 .build(); 9760 Source source2 = 9761 SourceFixture.getMinimalValidSourceBuilder() 9762 .setEventId(new UnsignedLong(2L)) 9763 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 9764 .setEventTime(10000) 9765 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9766 .setId("source2") 9767 .build(); 9768 Source source3 = 9769 SourceFixture.getMinimalValidSourceBuilder() 9770 .setEventId(new UnsignedLong(3L)) 9771 .setPublisher(WebUtil.validUri("https://subdomain2.site1.test")) 9772 .setEventTime(15000) 9773 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9774 .setId("source3") 9775 .build(); 9776 Source source4 = 9777 SourceFixture.getMinimalValidSourceBuilder() 9778 .setEventId(new UnsignedLong(4L)) 9779 .setPublisher(WebUtil.validUri("https://subdomain2.site2.test")) 9780 .setEventTime(15000) 9781 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9782 .setId("source4") 9783 .build(); 9784 Source source5 = 9785 SourceFixture.getMinimalValidSourceBuilder() 9786 .setEventId(new UnsignedLong(5L)) 9787 .setPublisher(WebUtil.validUri("https://subdomain2.site1.test")) 9788 .setEventTime(20000) 9789 .setRegistrant(Uri.parse("android-app://com.registrant2")) 9790 .setId("source5") 9791 .build(); 9792 List<Source> sources = List.of(source1, source2, source3, source4, source5); 9793 9794 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 9795 sources.forEach(source -> insertInDb(db, source)); 9796 9797 // Execution 9798 mDatastoreManager.runInTransaction( 9799 dao -> { 9800 // --- DELETE behaviour --- 9801 // Delete Nothing 9802 // No matches 9803 List<String> actualSources = 9804 dao.fetchMatchingSources( 9805 Uri.parse("android-app://com.registrant1"), 9806 Instant.ofEpochMilli(0), 9807 Instant.ofEpochMilli(50000), 9808 List.of(), 9809 List.of(), 9810 DeletionRequest.MATCH_BEHAVIOR_DELETE); 9811 assertEquals(0, actualSources.size()); 9812 9813 // 1 & 2 match registrant1 and "https://subdomain1.site1.test" publisher 9814 // origin 9815 actualSources = 9816 dao.fetchMatchingSources( 9817 Uri.parse("android-app://com.registrant1"), 9818 Instant.ofEpochMilli(0), 9819 Instant.ofEpochMilli(50000), 9820 List.of(WebUtil.validUri("https://subdomain1.site1.test")), 9821 List.of(), 9822 DeletionRequest.MATCH_BEHAVIOR_DELETE); 9823 assertEquals(2, actualSources.size()); 9824 9825 // Only 2 matches registrant1 and "https://subdomain1.site1.test" 9826 // publisher origin within 9827 // the range 9828 actualSources = 9829 dao.fetchMatchingSources( 9830 Uri.parse("android-app://com.registrant1"), 9831 Instant.ofEpochMilli(8000), 9832 Instant.ofEpochMilli(50000), 9833 List.of(WebUtil.validUri("https://subdomain1.site1.test")), 9834 List.of(), 9835 DeletionRequest.MATCH_BEHAVIOR_DELETE); 9836 assertEquals(1, actualSources.size()); 9837 9838 // 1,2 & 3 matches registrant1 and "https://site1.test" publisher origin 9839 actualSources = 9840 dao.fetchMatchingSources( 9841 Uri.parse("android-app://com.registrant1"), 9842 Instant.ofEpochMilli(0), 9843 Instant.ofEpochMilli(50000), 9844 List.of(), 9845 List.of(WebUtil.validUri("https://site1.test")), 9846 DeletionRequest.MATCH_BEHAVIOR_DELETE); 9847 assertEquals(3, actualSources.size()); 9848 9849 // 3 matches origin and 4 matches domain URI 9850 actualSources = 9851 dao.fetchMatchingSources( 9852 Uri.parse("android-app://com.registrant1"), 9853 Instant.ofEpochMilli(10000), 9854 Instant.ofEpochMilli(20000), 9855 List.of(WebUtil.validUri("https://subdomain2.site1.test")), 9856 List.of(WebUtil.validUri("https://site2.test")), 9857 DeletionRequest.MATCH_BEHAVIOR_DELETE); 9858 assertEquals(2, actualSources.size()); 9859 9860 // --- PRESERVE (anti-match exception registrant) behaviour --- 9861 // Preserve Nothing 9862 // 1,2,3 & 4 are match registrant1 9863 actualSources = 9864 dao.fetchMatchingSources( 9865 Uri.parse("android-app://com.registrant1"), 9866 Instant.ofEpochMilli(0), 9867 Instant.ofEpochMilli(50000), 9868 List.of(), 9869 List.of(), 9870 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 9871 assertEquals(4, actualSources.size()); 9872 9873 // 3 & 4 match registrant1 and don't match 9874 // "https://subdomain1.site1.test" publisher origin 9875 actualSources = 9876 dao.fetchMatchingSources( 9877 Uri.parse("android-app://com.registrant1"), 9878 Instant.ofEpochMilli(0), 9879 Instant.ofEpochMilli(50000), 9880 List.of(WebUtil.validUri("https://subdomain1.site1.test")), 9881 List.of(), 9882 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 9883 assertEquals(2, actualSources.size()); 9884 9885 // 3 & 4 match registrant1, in range and don't match 9886 // "https://subdomain1.site1.test" 9887 actualSources = 9888 dao.fetchMatchingSources( 9889 Uri.parse("android-app://com.registrant1"), 9890 Instant.ofEpochMilli(8000), 9891 Instant.ofEpochMilli(50000), 9892 List.of(WebUtil.validUri("https://subdomain1.site1.test")), 9893 List.of(), 9894 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 9895 assertEquals(2, actualSources.size()); 9896 9897 // Only 4 matches registrant1, in range and don't match 9898 // "https://site1.test" 9899 actualSources = 9900 dao.fetchMatchingSources( 9901 Uri.parse("android-app://com.registrant1"), 9902 Instant.ofEpochMilli(0), 9903 Instant.ofEpochMilli(50000), 9904 List.of(), 9905 List.of(WebUtil.validUri("https://site1.test")), 9906 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 9907 assertEquals(1, actualSources.size()); 9908 9909 // only 2 is registrant1 based, in range and does not match either 9910 // site2.test or subdomain2.site1.test 9911 actualSources = 9912 dao.fetchMatchingSources( 9913 Uri.parse("android-app://com.registrant1"), 9914 Instant.ofEpochMilli(10000), 9915 Instant.ofEpochMilli(20000), 9916 List.of(WebUtil.validUri("https://subdomain2.site1.test")), 9917 List.of(WebUtil.validUri("https://site2.test")), 9918 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 9919 assertEquals(1, actualSources.size()); 9920 }); 9921 } 9922 9923 @Test fetchFlexSourceIdsFor_bringsMatchingSources_expectedSourceReturned()9924 public void fetchFlexSourceIdsFor_bringsMatchingSources_expectedSourceReturned() 9925 throws Exception { 9926 // Setup 9927 TriggerSpecs testTriggerSpecs = SourceFixture.getValidTriggerSpecsCountBased(); 9928 Source source1 = 9929 SourceFixture.getMinimalValidSourceBuilder() 9930 .setEventId(new UnsignedLong(1L)) 9931 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 9932 .setEventTime(5000) 9933 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9934 .setId("source1") 9935 .setTriggerSpecsString(testTriggerSpecs.encodeToJson()) 9936 .setMaxEventLevelReports(testTriggerSpecs.getMaxReports()) 9937 .setPrivacyParameters( 9938 testTriggerSpecs.encodePrivacyParametersToJsonString()) 9939 .build(); 9940 source1.buildTriggerSpecs(); 9941 Source source2 = 9942 SourceFixture.getMinimalValidSourceBuilder() 9943 .setEventId(new UnsignedLong(2L)) 9944 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 9945 .setEventTime(10000) 9946 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9947 .setId("source2") 9948 .setTriggerSpecsString(testTriggerSpecs.encodeToJson()) 9949 .setMaxEventLevelReports(testTriggerSpecs.getMaxReports()) 9950 .setPrivacyParameters( 9951 testTriggerSpecs.encodePrivacyParametersToJsonString()) 9952 .build(); 9953 source2.buildTriggerSpecs(); 9954 9955 List<TriggerSpecs> triggerSpecsList = List.of( 9956 source1.getTriggerSpecs(), 9957 source2.getTriggerSpecs()); 9958 9959 for (TriggerSpecs triggerSpecs : triggerSpecsList) { 9960 insertAttributedTrigger( 9961 triggerSpecs, 9962 EventReportFixture.getBaseEventReportBuild() 9963 .setTriggerId("123456") 9964 .setTriggerData(new UnsignedLong(2L)) 9965 .setTriggerPriority(1L) 9966 .setTriggerValue(1L) 9967 .build()); 9968 insertAttributedTrigger( 9969 triggerSpecs, 9970 EventReportFixture.getBaseEventReportBuild() 9971 .setTriggerId("234567") 9972 .setTriggerData(new UnsignedLong(2L)) 9973 .setTriggerPriority(1L) 9974 .setTriggerValue(1L) 9975 .build()); 9976 insertAttributedTrigger( 9977 triggerSpecs, 9978 EventReportFixture.getBaseEventReportBuild() 9979 .setTriggerId("345678") 9980 .setTriggerData(new UnsignedLong(2L)) 9981 .setTriggerPriority(1L) 9982 .setTriggerValue(1L) 9983 .build()); 9984 } 9985 9986 Source source3 = 9987 SourceFixture.getMinimalValidSourceBuilder() 9988 .setEventId(new UnsignedLong(2L)) 9989 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 9990 .setEventTime(10000) 9991 .setRegistrant(Uri.parse("android-app://com.registrant1")) 9992 .setId("source3") 9993 .setTriggerSpecsString(testTriggerSpecs.encodeToJson()) 9994 .setMaxEventLevelReports(testTriggerSpecs.getMaxReports()) 9995 .setPrivacyParameters( 9996 testTriggerSpecs.encodePrivacyParametersToJsonString()) 9997 .build(); 9998 source3.buildTriggerSpecs(); 9999 10000 insertAttributedTrigger( 10001 source3.getTriggerSpecs(), 10002 EventReportFixture.getBaseEventReportBuild() 10003 .setTriggerId("123456") 10004 .setTriggerData(new UnsignedLong(2L)) 10005 .setTriggerPriority(1L) 10006 .setTriggerValue(1L) 10007 .build()); 10008 10009 List<Source> sources = List.of(source1, source2, source3); 10010 10011 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 10012 sources.forEach(source -> insertInDb(db, source)); 10013 10014 // Execution 10015 mDatastoreManager.runInTransaction( 10016 dao -> { 10017 Set<String> actualSources = 10018 dao.fetchFlexSourceIdsFor(Collections.singletonList("123456")); 10019 Set<String> expected = Set.of("source1", "source2", "source3"); 10020 assertEquals(expected, actualSources); 10021 }); 10022 10023 mDatastoreManager.runInTransaction( 10024 dao -> { 10025 Set<String> actualSources = 10026 dao.fetchFlexSourceIdsFor(Collections.singletonList("234567")); 10027 Set<String> expected = Set.of("source1", "source2"); 10028 assertEquals(expected, actualSources); 10029 }); 10030 10031 mDatastoreManager.runInTransaction( 10032 dao -> { 10033 Set<String> actualSources = 10034 dao.fetchFlexSourceIdsFor(Collections.singletonList("23456")); 10035 assertEquals(Collections.emptySet(), actualSources); 10036 }); 10037 10038 mDatastoreManager.runInTransaction( 10039 dao -> { 10040 Set<String> actualSources = dao.fetchFlexSourceIdsFor(new ArrayList<>()); 10041 assertEquals(Collections.emptySet(), actualSources); 10042 }); 10043 } 10044 10045 @Test fetchFlexSourceIdsFor_moreThanSqliteMaxCompoundSelect_expectedSourceReturned()10046 public void fetchFlexSourceIdsFor_moreThanSqliteMaxCompoundSelect_expectedSourceReturned() 10047 throws Exception { 10048 // Setup 10049 TriggerSpecs testTriggerSpecs = SourceFixture.getValidTriggerSpecsCountBased(); 10050 Source source1 = 10051 SourceFixture.getMinimalValidSourceBuilder() 10052 .setEventId(new UnsignedLong(1L)) 10053 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 10054 .setEventTime(5000) 10055 .setRegistrant(Uri.parse("android-app://com.registrant1")) 10056 .setId("source1") 10057 .setTriggerSpecsString(testTriggerSpecs.encodeToJson()) 10058 .setMaxEventLevelReports(testTriggerSpecs.getMaxReports()) 10059 .setPrivacyParameters( 10060 testTriggerSpecs.encodePrivacyParametersToJsonString()) 10061 .build(); 10062 source1.buildTriggerSpecs(); 10063 Source source2 = 10064 SourceFixture.getMinimalValidSourceBuilder() 10065 .setEventId(new UnsignedLong(2L)) 10066 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 10067 .setEventTime(10000) 10068 .setRegistrant(Uri.parse("android-app://com.registrant1")) 10069 .setId("source2") 10070 .setTriggerSpecsString(testTriggerSpecs.encodeToJson()) 10071 .setMaxEventLevelReports(testTriggerSpecs.getMaxReports()) 10072 .setPrivacyParameters( 10073 testTriggerSpecs.encodePrivacyParametersToJsonString()) 10074 .build(); 10075 source2.buildTriggerSpecs(); 10076 10077 List<TriggerSpecs> triggerSpecsList = List.of( 10078 source1.getTriggerSpecs(), 10079 source2.getTriggerSpecs()); 10080 10081 for (TriggerSpecs triggerSpecs : triggerSpecsList) { 10082 insertAttributedTrigger( 10083 triggerSpecs, 10084 EventReportFixture.getBaseEventReportBuild() 10085 .setTriggerId("123456") 10086 .setTriggerData(new UnsignedLong(2L)) 10087 .setTriggerPriority(1L) 10088 .setTriggerValue(1L) 10089 .build()); 10090 insertAttributedTrigger( 10091 triggerSpecs, 10092 EventReportFixture.getBaseEventReportBuild() 10093 .setTriggerId("234567") 10094 .setTriggerData(new UnsignedLong(2L)) 10095 .setTriggerPriority(1L) 10096 .setTriggerValue(1L) 10097 .build()); 10098 insertAttributedTrigger( 10099 triggerSpecs, 10100 EventReportFixture.getBaseEventReportBuild() 10101 .setTriggerId("345678") 10102 .setTriggerData(new UnsignedLong(2L)) 10103 .setTriggerPriority(1L) 10104 .setTriggerValue(1L) 10105 .build()); 10106 } 10107 10108 Source source3 = 10109 SourceFixture.getMinimalValidSourceBuilder() 10110 .setEventId(new UnsignedLong(2L)) 10111 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 10112 .setEventTime(10000) 10113 .setRegistrant(Uri.parse("android-app://com.registrant1")) 10114 .setId("source3") 10115 .setTriggerSpecsString(testTriggerSpecs.encodeToJson()) 10116 .setMaxEventLevelReports(testTriggerSpecs.getMaxReports()) 10117 .setPrivacyParameters( 10118 testTriggerSpecs.encodePrivacyParametersToJsonString()) 10119 .build(); 10120 source3.buildTriggerSpecs(); 10121 10122 insertAttributedTrigger( 10123 source3.getTriggerSpecs(), 10124 EventReportFixture.getBaseEventReportBuild() 10125 .setTriggerId("123456") 10126 .setTriggerData(new UnsignedLong(2L)) 10127 .setTriggerPriority(1L) 10128 .setTriggerValue(1L) 10129 .build()); 10130 10131 List<Source> sources = List.of(source1, source2, source3); 10132 10133 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 10134 sources.forEach(source -> insertInDb(db, source)); 10135 10136 // Execution 10137 mDatastoreManager.runInTransaction( 10138 dao -> { 10139 Set<String> actualSources = 10140 dao.fetchFlexSourceIdsFor(getRandomIdsWith(1000, "123456")); 10141 Set<String> expected = Set.of("source1", "source2", "source3"); 10142 assertEquals(expected, actualSources); 10143 }); 10144 10145 mDatastoreManager.runInTransaction( 10146 dao -> { 10147 Set<String> actualSources = 10148 dao.fetchFlexSourceIdsFor(getRandomIdsWith(1000, "234567")); 10149 Set<String> expected = Set.of("source1", "source2"); 10150 assertEquals(expected, actualSources); 10151 }); 10152 10153 mDatastoreManager.runInTransaction( 10154 dao -> { 10155 Set<String> actualSources = 10156 dao.fetchFlexSourceIdsFor(getRandomIdsWith(1000, "23456")); 10157 assertEquals(Collections.emptySet(), actualSources); 10158 }); 10159 10160 mDatastoreManager.runInTransaction( 10161 dao -> { 10162 Set<String> actualSources = dao.fetchFlexSourceIdsFor(new ArrayList<>()); 10163 assertEquals(Collections.emptySet(), actualSources); 10164 }); 10165 } 10166 10167 @Test fetchFlexSourceIdsFor_emptyInputSourceId_noSourceReturned()10168 public void fetchFlexSourceIdsFor_emptyInputSourceId_noSourceReturned() throws Exception { 10169 // Setup 10170 TriggerSpecs testTriggerSpecs = SourceFixture.getValidTriggerSpecsCountBased(); 10171 Source source1 = 10172 SourceFixture.getMinimalValidSourceBuilder() 10173 .setEventId(new UnsignedLong(1L)) 10174 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 10175 .setEventTime(5000) 10176 .setRegistrant(Uri.parse("android-app://com.registrant1")) 10177 .setId("source1") 10178 .setTriggerSpecsString(testTriggerSpecs.encodeToJson()) 10179 .setMaxEventLevelReports(testTriggerSpecs.getMaxReports()) 10180 .setPrivacyParameters( 10181 testTriggerSpecs.encodePrivacyParametersToJsonString()) 10182 .build(); 10183 source1.buildTriggerSpecs(); 10184 10185 Source source2 = 10186 SourceFixture.getMinimalValidSourceBuilder() 10187 .setEventId(new UnsignedLong(2L)) 10188 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 10189 .setEventTime(10000) 10190 .setRegistrant(Uri.parse("android-app://com.registrant1")) 10191 .setId("source2") 10192 .setTriggerSpecsString(testTriggerSpecs.encodeToJson()) 10193 .setMaxEventLevelReports(testTriggerSpecs.getMaxReports()) 10194 .setPrivacyParameters( 10195 testTriggerSpecs.encodePrivacyParametersToJsonString()) 10196 .build(); 10197 source2.buildTriggerSpecs(); 10198 10199 List<TriggerSpecs> triggerSpecsList = List.of( 10200 source1.getTriggerSpecs(), 10201 source2.getTriggerSpecs()); 10202 10203 for (TriggerSpecs triggerSpecs : triggerSpecsList) { 10204 insertAttributedTrigger( 10205 triggerSpecs, 10206 EventReportFixture.getBaseEventReportBuild() 10207 .setTriggerId("123456") 10208 .setTriggerData(new UnsignedLong(2L)) 10209 .setTriggerPriority(1L) 10210 .setTriggerValue(1L) 10211 .build()); 10212 insertAttributedTrigger( 10213 triggerSpecs, 10214 EventReportFixture.getBaseEventReportBuild() 10215 .setTriggerId("234567") 10216 .setTriggerData(new UnsignedLong(2L)) 10217 .setTriggerPriority(1L) 10218 .setTriggerValue(1L) 10219 .build()); 10220 insertAttributedTrigger( 10221 triggerSpecs, 10222 EventReportFixture.getBaseEventReportBuild() 10223 .setTriggerId("345678") 10224 .setTriggerData(new UnsignedLong(2L)) 10225 .setTriggerPriority(1L) 10226 .setTriggerValue(1L) 10227 .build()); 10228 } 10229 10230 Source source3 = 10231 SourceFixture.getMinimalValidSourceBuilder() 10232 .setEventId(new UnsignedLong(2L)) 10233 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 10234 .setEventTime(10000) 10235 .setRegistrant(Uri.parse("android-app://com.registrant1")) 10236 .setId("source3") 10237 .setTriggerSpecsString(testTriggerSpecs.encodeToJson()) 10238 .setMaxEventLevelReports(testTriggerSpecs.getMaxReports()) 10239 .setPrivacyParameters( 10240 testTriggerSpecs.encodePrivacyParametersToJsonString()) 10241 .build(); 10242 source3.buildTriggerSpecs(); 10243 10244 insertAttributedTrigger( 10245 source3.getTriggerSpecs(), 10246 EventReportFixture.getBaseEventReportBuild() 10247 .setTriggerId("123456") 10248 .setTriggerData(new UnsignedLong(2L)) 10249 .setTriggerPriority(1L) 10250 .setTriggerValue(1L) 10251 .build()); 10252 10253 List<Source> sources = List.of(source1, source2, source3); 10254 10255 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 10256 sources.forEach(source -> insertInDb(db, source)); 10257 10258 // Execution 10259 mDatastoreManager.runInTransaction( 10260 dao -> { 10261 Set<String> actualSources = 10262 dao.fetchFlexSourceIdsFor(Collections.singletonList("123456")); 10263 Set<String> expected = Set.of("source1", "source2", "source3"); 10264 assertEquals(expected, actualSources); 10265 }); 10266 10267 mDatastoreManager.runInTransaction( 10268 dao -> { 10269 Set<String> actualSources = 10270 dao.fetchFlexSourceIdsFor(Collections.singletonList("234567")); 10271 Set<String> expected = Set.of("source1", "source2"); 10272 assertEquals(expected, actualSources); 10273 }); 10274 10275 mDatastoreManager.runInTransaction( 10276 dao -> { 10277 Set<String> actualSources = 10278 dao.fetchFlexSourceIdsFor(Collections.singletonList("23456")); 10279 assertEquals(Collections.emptySet(), actualSources); 10280 }); 10281 } 10282 10283 @Test fetchFlexSourceIdsFor_doesNotMatchV1Sources()10284 public void fetchFlexSourceIdsFor_doesNotMatchV1Sources() throws Exception { 10285 // Setup 10286 EventReport eventReport1 = 10287 EventReportFixture.getBaseEventReportBuild() 10288 .setTriggerId("123456") 10289 .setTriggerData(new UnsignedLong(2L)) 10290 .setTriggerPriority(1L) 10291 .setTriggerValue(1L) 10292 .build(); 10293 EventReport eventReport2 = 10294 EventReportFixture.getBaseEventReportBuild() 10295 .setTriggerId("234567") 10296 .setTriggerData(new UnsignedLong(2L)) 10297 .setTriggerPriority(1L) 10298 .setTriggerValue(1L) 10299 .build(); 10300 EventReport eventReport3 = 10301 EventReportFixture.getBaseEventReportBuild() 10302 .setTriggerId("345678") 10303 .setTriggerData(new UnsignedLong(2L)) 10304 .setTriggerPriority(1L) 10305 .setTriggerValue(1L) 10306 .build(); 10307 10308 TriggerSpecs testTriggerSpecs = SourceFixture.getValidTriggerSpecsCountBased(); 10309 10310 // Flex source 10311 Source source1 = 10312 SourceFixture.getMinimalValidSourceBuilder() 10313 .setEventId(new UnsignedLong(1L)) 10314 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 10315 .setEventTime(5000) 10316 .setRegistrant(Uri.parse("android-app://com.registrant1")) 10317 .setId("source1") 10318 .setTriggerSpecsString(testTriggerSpecs.encodeToJson()) 10319 .setMaxEventLevelReports(testTriggerSpecs.getMaxReports()) 10320 .setPrivacyParameters( 10321 testTriggerSpecs.encodePrivacyParametersToJsonString()) 10322 .build(); 10323 source1.buildTriggerSpecs(); 10324 10325 insertAttributedTrigger(source1.getTriggerSpecs(), eventReport1); 10326 insertAttributedTrigger(source1.getTriggerSpecs(), eventReport2); 10327 insertAttributedTrigger(source1.getTriggerSpecs(), eventReport3); 10328 10329 // Non-flex (V1) source 10330 Source source2 = 10331 SourceFixture.getMinimalValidSourceBuilder() 10332 .setEventId(new UnsignedLong(2L)) 10333 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 10334 .setEventTime(10000) 10335 .setRegistrant(Uri.parse("android-app://com.registrant1")) 10336 .setId("source2") 10337 .build(); 10338 source2.buildAttributedTriggers(); 10339 10340 insertAttributedTrigger(source2.getAttributedTriggers(), eventReport1); 10341 insertAttributedTrigger(source2.getAttributedTriggers(), eventReport2); 10342 insertAttributedTrigger(source2.getAttributedTriggers(), eventReport3); 10343 10344 // Flex source 10345 Source source3 = 10346 SourceFixture.getMinimalValidSourceBuilder() 10347 .setEventId(new UnsignedLong(2L)) 10348 .setPublisher(WebUtil.validUri("https://subdomain1.site1.test")) 10349 .setEventTime(10000) 10350 .setRegistrant(Uri.parse("android-app://com.registrant1")) 10351 .setId("source3") 10352 .setTriggerSpecsString(testTriggerSpecs.encodeToJson()) 10353 .setMaxEventLevelReports(testTriggerSpecs.getMaxReports()) 10354 .setPrivacyParameters( 10355 testTriggerSpecs.encodePrivacyParametersToJsonString()) 10356 .build(); 10357 source3.buildTriggerSpecs(); 10358 10359 insertAttributedTrigger(source3.getTriggerSpecs(), eventReport1); 10360 10361 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 10362 List.of(source1, source2, source3).forEach(source -> insertInDb(db, source)); 10363 10364 // Execution 10365 mDatastoreManager.runInTransaction( 10366 dao -> { 10367 Set<String> actualSources = 10368 dao.fetchFlexSourceIdsFor(Collections.singletonList("123456")); 10369 Set<String> expected = Set.of("source1", "source3"); 10370 assertEquals(expected, actualSources); 10371 }); 10372 10373 mDatastoreManager.runInTransaction( 10374 dao -> { 10375 Set<String> actualSources = 10376 dao.fetchFlexSourceIdsFor( 10377 new ArrayList<>(Collections.singletonList("234567"))); 10378 Set<String> expected = Set.of("source1"); 10379 assertEquals(expected, actualSources); 10380 }); 10381 10382 mDatastoreManager.runInTransaction( 10383 dao -> { 10384 Set<String> actualSources = 10385 dao.fetchFlexSourceIdsFor(Collections.singletonList("23456")); 10386 assertEquals(Collections.emptySet(), actualSources); 10387 }); 10388 } 10389 10390 @Test fetchMatchingTriggers_bringsMatchingTriggers()10391 public void fetchMatchingTriggers_bringsMatchingTriggers() { 10392 // Setup 10393 Trigger trigger1 = 10394 TriggerFixture.getValidTriggerBuilder() 10395 .setAttributionDestination( 10396 WebUtil.validUri("https://subdomain1.site1.test")) 10397 .setTriggerTime(5000) 10398 .setRegistrant(Uri.parse("android-app://com.registrant1")) 10399 .setId("trigger1") 10400 .build(); 10401 Trigger trigger2 = 10402 TriggerFixture.getValidTriggerBuilder() 10403 .setAttributionDestination( 10404 WebUtil.validUri("https://subdomain1.site1.test")) 10405 .setTriggerTime(10000) 10406 .setRegistrant(Uri.parse("android-app://com.registrant1")) 10407 .setId("trigger2") 10408 .build(); 10409 Trigger trigger3 = 10410 TriggerFixture.getValidTriggerBuilder() 10411 .setAttributionDestination( 10412 WebUtil.validUri("https://subdomain2.site1.test")) 10413 .setTriggerTime(15000) 10414 .setRegistrant(Uri.parse("android-app://com.registrant1")) 10415 .setId("trigger3") 10416 .build(); 10417 Trigger trigger4 = 10418 TriggerFixture.getValidTriggerBuilder() 10419 .setAttributionDestination( 10420 WebUtil.validUri("https://subdomain2.site2.test")) 10421 .setTriggerTime(15000) 10422 .setRegistrant(Uri.parse("android-app://com.registrant1")) 10423 .setId("trigger4") 10424 .build(); 10425 Trigger trigger5 = 10426 TriggerFixture.getValidTriggerBuilder() 10427 .setAttributionDestination( 10428 WebUtil.validUri("https://subdomain2.site1.test")) 10429 .setTriggerTime(20000) 10430 .setRegistrant(Uri.parse("android-app://com.registrant2")) 10431 .setId("trigger5") 10432 .build(); 10433 List<Trigger> triggers = List.of(trigger1, trigger2, trigger3, trigger4, trigger5); 10434 10435 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 10436 triggers.forEach( 10437 trigger -> { 10438 ContentValues values = new ContentValues(); 10439 values.put(TriggerContract.ID, trigger.getId()); 10440 values.put( 10441 TriggerContract.ATTRIBUTION_DESTINATION, 10442 trigger.getAttributionDestination().toString()); 10443 values.put(TriggerContract.TRIGGER_TIME, trigger.getTriggerTime()); 10444 values.put(TriggerContract.ENROLLMENT_ID, trigger.getEnrollmentId()); 10445 values.put(TriggerContract.REGISTRANT, trigger.getRegistrant().toString()); 10446 values.put(TriggerContract.STATUS, trigger.getStatus()); 10447 db.insert(TriggerContract.TABLE, /* nullColumnHack */ null, values); 10448 }); 10449 10450 // Execution 10451 mDatastoreManager.runInTransaction( 10452 dao -> { 10453 // --- DELETE behaviour --- 10454 // Delete Nothing 10455 // No Matches 10456 Set<String> actualTriggers = 10457 dao.fetchMatchingTriggers( 10458 Uri.parse("android-app://com.registrant1"), 10459 Instant.ofEpochMilli(0), 10460 Instant.ofEpochMilli(50000), 10461 List.of(), 10462 List.of(), 10463 DeletionRequest.MATCH_BEHAVIOR_DELETE); 10464 assertEquals(0, actualTriggers.size()); 10465 10466 // 1 & 2 match registrant1 and "https://subdomain1.site1.test" 10467 // destination origin 10468 actualTriggers = 10469 dao.fetchMatchingTriggers( 10470 Uri.parse("android-app://com.registrant1"), 10471 Instant.ofEpochMilli(0), 10472 Instant.ofEpochMilli(50000), 10473 List.of(WebUtil.validUri("https://subdomain1.site1.test")), 10474 List.of(), 10475 DeletionRequest.MATCH_BEHAVIOR_DELETE); 10476 assertEquals(2, actualTriggers.size()); 10477 10478 // Only 2 matches registrant1 and "https://subdomain1.site1.test" 10479 // destination origin within the range 10480 actualTriggers = 10481 dao.fetchMatchingTriggers( 10482 Uri.parse("android-app://com.registrant1"), 10483 Instant.ofEpochMilli(8000), 10484 Instant.ofEpochMilli(50000), 10485 List.of(WebUtil.validUri("https://subdomain1.site1.test")), 10486 List.of(), 10487 DeletionRequest.MATCH_BEHAVIOR_DELETE); 10488 assertEquals(1, actualTriggers.size()); 10489 10490 // 1,2 & 3 matches registrant1 and "https://site1.test" destination 10491 // origin 10492 actualTriggers = 10493 dao.fetchMatchingTriggers( 10494 Uri.parse("android-app://com.registrant1"), 10495 Instant.ofEpochMilli(0), 10496 Instant.ofEpochMilli(50000), 10497 List.of(), 10498 List.of(WebUtil.validUri("https://site1.test")), 10499 DeletionRequest.MATCH_BEHAVIOR_DELETE); 10500 assertEquals(3, actualTriggers.size()); 10501 10502 // 3 matches origin and 4 matches domain URI 10503 actualTriggers = 10504 dao.fetchMatchingTriggers( 10505 Uri.parse("android-app://com.registrant1"), 10506 Instant.ofEpochMilli(10000), 10507 Instant.ofEpochMilli(20000), 10508 List.of(WebUtil.validUri("https://subdomain2.site1.test")), 10509 List.of(WebUtil.validUri("https://site2.test")), 10510 DeletionRequest.MATCH_BEHAVIOR_DELETE); 10511 assertEquals(2, actualTriggers.size()); 10512 10513 // --- PRESERVE (anti-match exception registrant) behaviour --- 10514 // Preserve Nothing 10515 // 1,2,3 & 4 are match registrant1 10516 actualTriggers = 10517 dao.fetchMatchingTriggers( 10518 Uri.parse("android-app://com.registrant1"), 10519 Instant.ofEpochMilli(0), 10520 Instant.ofEpochMilli(50000), 10521 List.of(), 10522 List.of(), 10523 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 10524 assertEquals(4, actualTriggers.size()); 10525 10526 // 3 & 4 match registrant1 and don't match 10527 // "https://subdomain1.site1.test" destination origin 10528 actualTriggers = 10529 dao.fetchMatchingTriggers( 10530 Uri.parse("android-app://com.registrant1"), 10531 Instant.ofEpochMilli(0), 10532 Instant.ofEpochMilli(50000), 10533 List.of(WebUtil.validUri("https://subdomain1.site1.test")), 10534 List.of(), 10535 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 10536 assertEquals(2, actualTriggers.size()); 10537 10538 // 3 & 4 match registrant1, in range and don't match 10539 // "https://subdomain1.site1.test" 10540 actualTriggers = 10541 dao.fetchMatchingTriggers( 10542 Uri.parse("android-app://com.registrant1"), 10543 Instant.ofEpochMilli(8000), 10544 Instant.ofEpochMilli(50000), 10545 List.of(WebUtil.validUri("https://subdomain1.site1.test")), 10546 List.of(), 10547 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 10548 assertEquals(2, actualTriggers.size()); 10549 10550 // Only 4 matches registrant1, in range and don't match 10551 // "https://site1.test" 10552 actualTriggers = 10553 dao.fetchMatchingTriggers( 10554 Uri.parse("android-app://com.registrant1"), 10555 Instant.ofEpochMilli(0), 10556 Instant.ofEpochMilli(50000), 10557 List.of(), 10558 List.of(WebUtil.validUri("https://site1.test")), 10559 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 10560 assertEquals(1, actualTriggers.size()); 10561 10562 // only 2 is registrant1 based, in range and does not match either 10563 // site2.test or subdomain2.site1.test 10564 actualTriggers = 10565 dao.fetchMatchingTriggers( 10566 Uri.parse("android-app://com.registrant1"), 10567 Instant.ofEpochMilli(10000), 10568 Instant.ofEpochMilli(20000), 10569 List.of(WebUtil.validUri("https://subdomain2.site1.test")), 10570 List.of(WebUtil.validUri("https://site2.test")), 10571 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 10572 assertEquals(1, actualTriggers.size()); 10573 }); 10574 } 10575 10576 @Test fetchMatchingAsyncRegistrations_bringsMatchingAsyncRegistrations()10577 public void fetchMatchingAsyncRegistrations_bringsMatchingAsyncRegistrations() { 10578 // Setup 10579 AsyncRegistration asyncRegistration1 = 10580 AsyncRegistrationFixture.getValidAsyncRegistrationBuilder() 10581 .setTopOrigin( 10582 WebUtil.validUri("https://subdomain1.site1.test")) 10583 .setRequestTime(5000) 10584 .setRegistrant(Uri.parse("android-app://com.registrant1")) 10585 .setId("asyncRegistration1") 10586 .build(); 10587 AsyncRegistration asyncRegistration2 = 10588 AsyncRegistrationFixture.getValidAsyncRegistrationBuilder() 10589 .setTopOrigin( 10590 WebUtil.validUri("https://subdomain1.site1.test")) 10591 .setRequestTime(10000) 10592 .setRegistrant(Uri.parse("android-app://com.registrant1")) 10593 .setId("asyncRegistration2") 10594 .build(); 10595 AsyncRegistration asyncRegistration3 = 10596 AsyncRegistrationFixture.getValidAsyncRegistrationBuilder() 10597 .setTopOrigin( 10598 WebUtil.validUri("https://subdomain2.site1.test")) 10599 .setRequestTime(15000) 10600 .setRegistrant(Uri.parse("android-app://com.registrant1")) 10601 .setId("asyncRegistration3") 10602 .build(); 10603 AsyncRegistration asyncRegistration4 = 10604 AsyncRegistrationFixture.getValidAsyncRegistrationBuilder() 10605 .setTopOrigin( 10606 WebUtil.validUri("https://subdomain2.site2.test")) 10607 .setRequestTime(15000) 10608 .setRegistrant(Uri.parse("android-app://com.registrant1")) 10609 .setId("asyncRegistration4") 10610 .build(); 10611 AsyncRegistration asyncRegistration5 = 10612 AsyncRegistrationFixture.getValidAsyncRegistrationBuilder() 10613 .setTopOrigin( 10614 WebUtil.validUri("https://subdomain2.site1.test")) 10615 .setRequestTime(20000) 10616 .setRegistrant(Uri.parse("android-app://com.registrant2")) 10617 .setId("asyncRegistration5") 10618 .build(); 10619 List<AsyncRegistration> asyncRegistrations = List.of( 10620 asyncRegistration1, asyncRegistration2, asyncRegistration3, asyncRegistration4, 10621 asyncRegistration5); 10622 10623 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 10624 asyncRegistrations.forEach( 10625 asyncRegistration -> { 10626 ContentValues values = new ContentValues(); 10627 values.put(AsyncRegistrationContract.ID, asyncRegistration.getId()); 10628 values.put( 10629 AsyncRegistrationContract.TOP_ORIGIN, 10630 asyncRegistration.getTopOrigin().toString()); 10631 values.put(AsyncRegistrationContract.REQUEST_TIME, 10632 asyncRegistration.getRequestTime()); 10633 values.put(AsyncRegistrationContract.REGISTRANT, 10634 asyncRegistration.getRegistrant().toString()); 10635 values.put(AsyncRegistrationContract.REGISTRATION_ID, 10636 UUID.randomUUID().toString()); 10637 db.insert(AsyncRegistrationContract.TABLE, /* nullColumnHack */ null, values); 10638 }); 10639 10640 // Execution 10641 mDatastoreManager.runInTransaction( 10642 dao -> { 10643 // --- DELETE behaviour --- 10644 // Delete Nothing 10645 // No Matches 10646 List<String> actualAsyncRegistrations = 10647 dao.fetchMatchingAsyncRegistrations( 10648 Uri.parse("android-app://com.registrant1"), 10649 Instant.ofEpochMilli(0), 10650 Instant.ofEpochMilli(50000), 10651 List.of(), 10652 List.of(), 10653 DeletionRequest.MATCH_BEHAVIOR_DELETE); 10654 assertEquals(0, actualAsyncRegistrations.size()); 10655 10656 // 1 & 2 match registrant1 and "https://subdomain1.site1.test" top- 10657 // origin origin 10658 actualAsyncRegistrations = 10659 dao.fetchMatchingAsyncRegistrations( 10660 Uri.parse("android-app://com.registrant1"), 10661 Instant.ofEpochMilli(0), 10662 Instant.ofEpochMilli(50000), 10663 List.of(WebUtil.validUri("https://subdomain1.site1.test")), 10664 List.of(), 10665 DeletionRequest.MATCH_BEHAVIOR_DELETE); 10666 assertEquals(2, actualAsyncRegistrations.size()); 10667 10668 // Only 2 matches registrant1 and "https://subdomain1.site1.test" 10669 // top-origin origin within the range 10670 actualAsyncRegistrations = 10671 dao.fetchMatchingAsyncRegistrations( 10672 Uri.parse("android-app://com.registrant1"), 10673 Instant.ofEpochMilli(8000), 10674 Instant.ofEpochMilli(50000), 10675 List.of(WebUtil.validUri("https://subdomain1.site1.test")), 10676 List.of(), 10677 DeletionRequest.MATCH_BEHAVIOR_DELETE); 10678 assertEquals(1, actualAsyncRegistrations.size()); 10679 10680 // 1,2 & 3 matches registrant1 and "https://site1.test" top-origin 10681 // origin 10682 actualAsyncRegistrations = 10683 dao.fetchMatchingAsyncRegistrations( 10684 Uri.parse("android-app://com.registrant1"), 10685 Instant.ofEpochMilli(0), 10686 Instant.ofEpochMilli(50000), 10687 List.of(), 10688 List.of(WebUtil.validUri("https://site1.test")), 10689 DeletionRequest.MATCH_BEHAVIOR_DELETE); 10690 assertEquals(3, actualAsyncRegistrations.size()); 10691 10692 // 3 matches origin and 4 matches domain URI 10693 actualAsyncRegistrations = 10694 dao.fetchMatchingAsyncRegistrations( 10695 Uri.parse("android-app://com.registrant1"), 10696 Instant.ofEpochMilli(10000), 10697 Instant.ofEpochMilli(20000), 10698 List.of(WebUtil.validUri("https://subdomain2.site1.test")), 10699 List.of(WebUtil.validUri("https://site2.test")), 10700 DeletionRequest.MATCH_BEHAVIOR_DELETE); 10701 assertEquals(2, actualAsyncRegistrations.size()); 10702 10703 // --- PRESERVE (anti-match exception registrant) behaviour --- 10704 // Preserve Nothing 10705 // 1,2,3 & 4 are match registrant1 10706 actualAsyncRegistrations = 10707 dao.fetchMatchingAsyncRegistrations( 10708 Uri.parse("android-app://com.registrant1"), 10709 Instant.ofEpochMilli(0), 10710 Instant.ofEpochMilli(50000), 10711 List.of(), 10712 List.of(), 10713 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 10714 assertEquals(4, actualAsyncRegistrations.size()); 10715 10716 // 3 & 4 match registrant1 and don't match 10717 // "https://subdomain1.site1.test" top-origin origin 10718 actualAsyncRegistrations = 10719 dao.fetchMatchingAsyncRegistrations( 10720 Uri.parse("android-app://com.registrant1"), 10721 Instant.ofEpochMilli(0), 10722 Instant.ofEpochMilli(50000), 10723 List.of(WebUtil.validUri("https://subdomain1.site1.test")), 10724 List.of(), 10725 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 10726 assertEquals(2, actualAsyncRegistrations.size()); 10727 10728 // 3 & 4 match registrant1, in range and don't match 10729 // "https://subdomain1.site1.test" 10730 actualAsyncRegistrations = 10731 dao.fetchMatchingAsyncRegistrations( 10732 Uri.parse("android-app://com.registrant1"), 10733 Instant.ofEpochMilli(8000), 10734 Instant.ofEpochMilli(50000), 10735 List.of(WebUtil.validUri("https://subdomain1.site1.test")), 10736 List.of(), 10737 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 10738 assertEquals(2, actualAsyncRegistrations.size()); 10739 10740 // Only 4 matches registrant1, in range and don't match 10741 // "https://site1.test" 10742 actualAsyncRegistrations = 10743 dao.fetchMatchingAsyncRegistrations( 10744 Uri.parse("android-app://com.registrant1"), 10745 Instant.ofEpochMilli(0), 10746 Instant.ofEpochMilli(50000), 10747 List.of(), 10748 List.of(WebUtil.validUri("https://site1.test")), 10749 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 10750 assertEquals(1, actualAsyncRegistrations.size()); 10751 10752 // only 2 is registrant1 based, in range and does not match either 10753 // site2.test or subdomain2.site1.test 10754 actualAsyncRegistrations = 10755 dao.fetchMatchingAsyncRegistrations( 10756 Uri.parse("android-app://com.registrant1"), 10757 Instant.ofEpochMilli(10000), 10758 Instant.ofEpochMilli(20000), 10759 List.of(WebUtil.validUri("https://subdomain2.site1.test")), 10760 List.of(WebUtil.validUri("https://site2.test")), 10761 DeletionRequest.MATCH_BEHAVIOR_PRESERVE); 10762 assertEquals(1, actualAsyncRegistrations.size()); 10763 }); 10764 } 10765 10766 @Test testUpdateSourceAggregateReportDedupKeys_updatesKeysInList()10767 public void testUpdateSourceAggregateReportDedupKeys_updatesKeysInList() { 10768 Source validSource = SourceFixture.getValidSource(); 10769 assertTrue(validSource.getAggregateReportDedupKeys().equals(new ArrayList<UnsignedLong>())); 10770 mDatastoreManager.runInTransaction((dao) -> dao.insertSource(validSource)); 10771 10772 String sourceId = getFirstSourceIdFromDatastore(); 10773 Source source = 10774 mDatastoreManager 10775 .runInTransactionWithResult( 10776 measurementDao -> measurementDao.getSource(sourceId)) 10777 .get(); 10778 10779 source.getAggregateReportDedupKeys().add(new UnsignedLong(10L)); 10780 mDatastoreManager.runInTransaction( 10781 (dao) -> dao.updateSourceAggregateReportDedupKeys(source)); 10782 10783 Source sourceAfterUpdate = 10784 mDatastoreManager 10785 .runInTransactionWithResult( 10786 measurementDao -> measurementDao.getSource(sourceId)) 10787 .get(); 10788 10789 assertTrue(sourceAfterUpdate.getAggregateReportDedupKeys().size() == 1); 10790 assertTrue(sourceAfterUpdate.getAggregateReportDedupKeys().get(0).getValue() == 10L); 10791 } 10792 10793 @Test testUpdateSourceAggregateReportDedupKeys_acceptsUnsignedLongValue()10794 public void testUpdateSourceAggregateReportDedupKeys_acceptsUnsignedLongValue() { 10795 Source validSource = SourceFixture.getValidSource(); 10796 assertTrue(validSource.getAggregateReportDedupKeys().equals(new ArrayList<UnsignedLong>())); 10797 mDatastoreManager.runInTransaction((dao) -> dao.insertSource(validSource)); 10798 10799 String sourceId = getFirstSourceIdFromDatastore(); 10800 Source source = 10801 mDatastoreManager 10802 .runInTransactionWithResult( 10803 measurementDao -> measurementDao.getSource(sourceId)) 10804 .get(); 10805 10806 UnsignedLong dedupKeyValue = new UnsignedLong("17293822569102704640"); 10807 source.getAggregateReportDedupKeys().add(dedupKeyValue); 10808 mDatastoreManager.runInTransaction( 10809 (dao) -> dao.updateSourceAggregateReportDedupKeys(source)); 10810 10811 Source sourceAfterUpdate = 10812 mDatastoreManager 10813 .runInTransactionWithResult( 10814 measurementDao -> measurementDao.getSource(sourceId)) 10815 .get(); 10816 10817 assertEquals(1, sourceAfterUpdate.getAggregateReportDedupKeys().size()); 10818 assertEquals(dedupKeyValue, sourceAfterUpdate.getAggregateReportDedupKeys().get(0)); 10819 } 10820 10821 @Test testUpdateSourceAggregatableNamedBudgetAndContribution_updatesBudgets()10822 public void testUpdateSourceAggregatableNamedBudgetAndContribution_updatesBudgets() { 10823 mocker.mockGetFlags(mMockFlags); 10824 when(mMockFlags.getMeasurementEnableAggregatableNamedBudgets()).thenReturn(true); 10825 when(mMockFlags.getMeasurementDbSizeLimit()).thenReturn(MEASUREMENT_DB_SIZE_LIMIT); 10826 10827 AggregatableNamedBudgets aggregatableNamedBudgets = new AggregatableNamedBudgets(); 10828 aggregatableNamedBudgets.createContributionBudget("budget1", 400); 10829 aggregatableNamedBudgets.setContribution("budget1", 150); 10830 Source validSource = 10831 insertSourceForAggregatableNamedBudgets( 10832 aggregatableNamedBudgets, 10833 SOURCE_EVENT_TIME, 10834 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 10835 List.of(APP_ONE_DESTINATION)); 10836 validSource.getAggregatableNamedBudgets().setContribution("budget1", 340); 10837 AggregatableNamedBudgets.BudgetAndContribution validSourceBudgetAndContribution = 10838 new AggregatableNamedBudgets.BudgetAndContribution(400); 10839 validSourceBudgetAndContribution.setAggregateContribution(340); 10840 mDatastoreManager.runInTransaction( 10841 (dao) -> 10842 dao.updateSourceAggregatableNamedBudgetAndContribution( 10843 validSource.getId(), "budget1", validSourceBudgetAndContribution)); 10844 10845 AggregatableNamedBudgets.BudgetAndContribution daoBudgetAndContribution = 10846 mDatastoreManager 10847 .runInTransactionWithResult( 10848 measurementDao -> 10849 measurementDao 10850 .getSourceAggregatableNamedBudgetAndContribution( 10851 validSource.getId(), "budget1")) 10852 .get(); 10853 AggregatableNamedBudgets.BudgetAndContribution expectedBudgetAndContribution = 10854 new AggregatableNamedBudgets.BudgetAndContribution(400); 10855 expectedBudgetAndContribution.setAggregateContribution(340); 10856 assertThat(daoBudgetAndContribution).isEqualTo(expectedBudgetAndContribution); 10857 } 10858 10859 @Test 10860 public void testUpdateSourceAggregatableNamedBudgetAndContribution_budgetDoesNotExistCreatesRow()10861 testUpdateSourceAggregatableNamedBudgetAndContribution_budgetDoesNotExistCreatesRow() { 10862 mocker.mockGetFlags(mMockFlags); 10863 when(mMockFlags.getMeasurementEnableAggregatableNamedBudgets()).thenReturn(true); 10864 when(mMockFlags.getMeasurementDbSizeLimit()).thenReturn(MEASUREMENT_DB_SIZE_LIMIT); 10865 10866 Source validSource = SourceFixture.getValidSource(); 10867 mDatastoreManager.runInTransaction((dao) -> dao.insertSource(validSource)); 10868 assertThat(validSource.getAggregatableNamedBudgets()).isNull(); 10869 10870 AggregatableNamedBudgets aggregatableNamedBudgets = new AggregatableNamedBudgets(); 10871 Source finalSource = 10872 Source.Builder.from(validSource) 10873 .setAggregatableNamedBudgets(aggregatableNamedBudgets) 10874 .build(); 10875 AggregatableNamedBudgets.BudgetAndContribution sourceBudgetAndContribution = 10876 new AggregatableNamedBudgets.BudgetAndContribution(100); 10877 sourceBudgetAndContribution.setAggregateContribution(70); 10878 mDatastoreManager.runInTransaction( 10879 (dao) -> 10880 dao.updateSourceAggregatableNamedBudgetAndContribution( 10881 finalSource.getId(), "budget1", sourceBudgetAndContribution)); 10882 10883 Source sourceAfterUpdate = 10884 mDatastoreManager 10885 .runInTransactionWithResult( 10886 measurementDao -> measurementDao.getSource(validSource.getId())) 10887 .get(); 10888 10889 AggregatableNamedBudgets.BudgetAndContribution daoBudgetAndContribution = 10890 mDatastoreManager 10891 .runInTransactionWithResult( 10892 measurementDao -> 10893 measurementDao 10894 .getSourceAggregatableNamedBudgetAndContribution( 10895 sourceAfterUpdate.getId(), "budget1")) 10896 .get(); 10897 assertThat(daoBudgetAndContribution).isEqualTo(sourceBudgetAndContribution); 10898 } 10899 10900 @Test testPersistAndRetrieveSource_handlesPreExistingNegativeValues()10901 public void testPersistAndRetrieveSource_handlesPreExistingNegativeValues() { 10902 // Setup 10903 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 10904 Source validSource = SourceFixture.getValidSource(); 10905 ContentValues values = new ContentValues(); 10906 values.put(SourceContract.ID, validSource.getId()); 10907 values.put(SourceContract.EVENT_ID, validSource.getEventId().toString()); 10908 values.put(SourceContract.ENROLLMENT_ID, validSource.getEnrollmentId()); 10909 values.put(SourceContract.REGISTRANT, validSource.getRegistrant().toString()); 10910 values.put(SourceContract.PUBLISHER, validSource.getPublisher().toString()); 10911 values.put( 10912 SourceContract.REGISTRATION_ORIGIN, validSource.getRegistrationOrigin().toString()); 10913 // Existing invalid values 10914 Long val1 = -123L; 10915 Long val2 = Long.MIN_VALUE; 10916 values.put( 10917 SourceContract.AGGREGATE_REPORT_DEDUP_KEYS, 10918 val1.toString() + "," + val2.toString()); 10919 db.insert(SourceContract.TABLE, /* nullColumnHack */ null, values); 10920 maybeInsertSourceDestinations(db, validSource, validSource.getId()); 10921 10922 // Execution 10923 Optional<Source> source = 10924 mDatastoreManager.runInTransactionWithResult( 10925 measurementDao -> measurementDao.getSource(validSource.getId())); 10926 assertTrue(source.isPresent()); 10927 List<UnsignedLong> aggReportDedupKeys = source.get().getAggregateReportDedupKeys(); 10928 assertEquals(2, aggReportDedupKeys.size()); 10929 assertEquals(val1, (Long) aggReportDedupKeys.get(0).getValue()); 10930 assertEquals(val2, (Long) aggReportDedupKeys.get(1).getValue()); 10931 } 10932 10933 @Test fetchTriggerMatchingSourcesForXna_filtersSourcesCorrectly()10934 public void fetchTriggerMatchingSourcesForXna_filtersSourcesCorrectly() { 10935 // Setup 10936 Uri matchingDestination = APP_ONE_DESTINATION; 10937 Uri nonMatchingDestination = APP_TWO_DESTINATION; 10938 String mmpMatchingEnrollmentId = "mmp1"; 10939 String mmpNonMatchingEnrollmentId = "mmpx"; 10940 String san1MatchingEnrollmentId = "san1EnrollmentId"; 10941 String san2MatchingEnrollmentId = "san2EnrollmentId"; 10942 String san3MatchingEnrollmentId = "san3EnrollmentId"; 10943 String san4NonMatchingEnrollmentId = "san4EnrollmentId"; 10944 String san5MatchingEnrollmentId = "san5EnrollmentId"; 10945 10946 Trigger trigger = 10947 TriggerFixture.getValidTriggerBuilder() 10948 .setAttributionDestination(matchingDestination) 10949 .setEnrollmentId(mmpMatchingEnrollmentId) 10950 .setRegistrant(TriggerFixture.ValidTriggerParams.REGISTRANT) 10951 .setTriggerTime(TriggerFixture.ValidTriggerParams.TRIGGER_TIME) 10952 .setEventTriggers(TriggerFixture.ValidTriggerParams.EVENT_TRIGGERS) 10953 .setAggregateTriggerData( 10954 TriggerFixture.ValidTriggerParams.AGGREGATE_TRIGGER_DATA) 10955 .setAggregateValuesString( 10956 TriggerFixture.ValidTriggerParams.AGGREGATE_VALUES_STRING) 10957 .setFilters(TriggerFixture.ValidTriggerParams.TOP_LEVEL_FILTERS_JSON_STRING) 10958 .setNotFilters( 10959 TriggerFixture.ValidTriggerParams.TOP_LEVEL_NOT_FILTERS_JSON_STRING) 10960 .build(); 10961 Source s1MmpMatchingWithDestinations = 10962 createSourceBuilder() 10963 .setId("s1") 10964 .setEnrollmentId(mmpMatchingEnrollmentId) 10965 .setAppDestinations(List.of(matchingDestination)) 10966 .build(); 10967 Source s1MmpMatchingWithoutDestinations = 10968 createSourceBuilder() 10969 .setId("s1") 10970 .setEnrollmentId(mmpMatchingEnrollmentId) 10971 .setAppDestinations(null) 10972 .setWebDestinations(null) 10973 .setRegistrationId(s1MmpMatchingWithDestinations.getRegistrationId()) 10974 .build(); 10975 Source s2MmpDiffDestination = 10976 createSourceBuilder() 10977 .setId("s2") 10978 .setEnrollmentId(mmpMatchingEnrollmentId) 10979 .setAppDestinations(List.of(nonMatchingDestination)) 10980 .build(); 10981 Source s3MmpExpired = 10982 createSourceBuilder() 10983 .setId("s3") 10984 .setEnrollmentId(mmpMatchingEnrollmentId) 10985 .setAppDestinations(List.of(nonMatchingDestination)) 10986 // expired before trigger time 10987 .setExpiryTime(trigger.getTriggerTime() - DAYS.toMillis(1)) 10988 .build(); 10989 Source s4NonMatchingMmp = 10990 createSourceBuilder() 10991 .setId("s4") 10992 .setEnrollmentId(mmpNonMatchingEnrollmentId) 10993 .setAppDestinations(List.of(matchingDestination)) 10994 .build(); 10995 Source s5MmpMatchingWithDestinations = 10996 createSourceBuilder() 10997 .setId("s5") 10998 .setEnrollmentId(mmpMatchingEnrollmentId) 10999 .setAppDestinations(List.of(matchingDestination)) 11000 .build(); 11001 Source s5MmpMatchingWithoutDestinations = 11002 createSourceBuilder() 11003 .setId("s5") 11004 .setEnrollmentId(mmpMatchingEnrollmentId) 11005 .setAppDestinations(null) 11006 .setWebDestinations(null) 11007 .setRegistrationId(s5MmpMatchingWithDestinations.getRegistrationId()) 11008 .build(); 11009 Source s6San1MatchingWithDestinations = 11010 createSourceBuilder() 11011 .setId("s6") 11012 .setEnrollmentId(san1MatchingEnrollmentId) 11013 .setAppDestinations(List.of(matchingDestination)) 11014 .setSharedAggregationKeys(SHARED_AGGREGATE_KEYS) 11015 .build(); 11016 Source s6San1MatchingWithoutDestinations = 11017 createSourceBuilder() 11018 .setId("s6") 11019 .setEnrollmentId(san1MatchingEnrollmentId) 11020 .setAppDestinations(null) 11021 .setWebDestinations(null) 11022 .setRegistrationId(s6San1MatchingWithDestinations.getRegistrationId()) 11023 .setSharedAggregationKeys(SHARED_AGGREGATE_KEYS) 11024 .build(); 11025 Source s7San1DiffDestination = 11026 createSourceBuilder() 11027 .setId("s7") 11028 .setEnrollmentId(san1MatchingEnrollmentId) 11029 .setAppDestinations(List.of(nonMatchingDestination)) 11030 .setSharedAggregationKeys(SHARED_AGGREGATE_KEYS) 11031 .build(); 11032 Source s8San2MatchingWithDestinations = 11033 createSourceBuilder() 11034 .setId("s8") 11035 .setEnrollmentId(san2MatchingEnrollmentId) 11036 .setAppDestinations(List.of(matchingDestination)) 11037 .setSharedAggregationKeys(SHARED_AGGREGATE_KEYS) 11038 .build(); 11039 Source s8San2MatchingWithoutDestinations = 11040 createSourceBuilder() 11041 .setId("s8") 11042 .setEnrollmentId(san2MatchingEnrollmentId) 11043 .setAppDestinations(null) 11044 .setWebDestinations(null) 11045 .setRegistrationId(s8San2MatchingWithDestinations.getRegistrationId()) 11046 .setSharedAggregationKeys(SHARED_AGGREGATE_KEYS) 11047 .build(); 11048 Source s9San3XnaIgnored = 11049 createSourceBuilder() 11050 .setId("s9") 11051 .setEnrollmentId(san3MatchingEnrollmentId) 11052 .setAppDestinations(List.of(matchingDestination)) 11053 .setSharedAggregationKeys(SHARED_AGGREGATE_KEYS) 11054 .build(); 11055 Source s10San3MatchingWithDestinations = 11056 createSourceBuilder() 11057 .setId("s10") 11058 .setEnrollmentId(san3MatchingEnrollmentId) 11059 .setAppDestinations(List.of(matchingDestination)) 11060 .setSharedAggregationKeys(SHARED_AGGREGATE_KEYS) 11061 .build(); 11062 Source s10San3MatchingWithoutDestinations = 11063 createSourceBuilder() 11064 .setId("s10") 11065 .setEnrollmentId(san3MatchingEnrollmentId) 11066 .setAppDestinations(null) 11067 .setWebDestinations(null) 11068 .setRegistrationId(s10San3MatchingWithDestinations.getRegistrationId()) 11069 .setSharedAggregationKeys(SHARED_AGGREGATE_KEYS) 11070 .build(); 11071 Source s11San4EnrollmentNonMatching = 11072 createSourceBuilder() 11073 .setId("s11") 11074 .setEnrollmentId(san4NonMatchingEnrollmentId) 11075 .setAppDestinations(List.of(matchingDestination)) 11076 .setSharedAggregationKeys(SHARED_AGGREGATE_KEYS) 11077 .build(); 11078 Source s12San1NullSharedAggregationKeys = 11079 createSourceBuilder() 11080 .setId("s12") 11081 .setEnrollmentId(san1MatchingEnrollmentId) 11082 .setAppDestinations(List.of(matchingDestination)) 11083 .setSharedAggregationKeys(null) 11084 .build(); 11085 Source s13San1Expired = 11086 createSourceBuilder() 11087 .setId("s13") 11088 .setEnrollmentId(san1MatchingEnrollmentId) 11089 .setAppDestinations(List.of(matchingDestination)) 11090 // expired before trigger time 11091 .setExpiryTime(trigger.getTriggerTime() - DAYS.toMillis(1)) 11092 .build(); 11093 String registrationIdForTriggerAndOtherRegistration = UUID.randomUUID().toString(); 11094 Source s14San5RegIdClasesWithMmp = 11095 createSourceBuilder() 11096 .setId("s14") 11097 .setEnrollmentId(san5MatchingEnrollmentId) 11098 .setAppDestinations(List.of(matchingDestination)) 11099 .setRegistrationId(registrationIdForTriggerAndOtherRegistration) 11100 .build(); 11101 Source s15MmpMatchingWithDestinations = 11102 createSourceBuilder() 11103 .setId("s15") 11104 .setEnrollmentId(mmpMatchingEnrollmentId) 11105 .setAppDestinations(List.of(matchingDestination)) 11106 .setRegistrationId(registrationIdForTriggerAndOtherRegistration) 11107 .build(); 11108 Source s15MmpMatchingWithoutDestinations = 11109 createSourceBuilder() 11110 .setId("s15") 11111 .setEnrollmentId(mmpMatchingEnrollmentId) 11112 .setAppDestinations(null) 11113 .setWebDestinations(null) 11114 .setRegistrationId(registrationIdForTriggerAndOtherRegistration) 11115 .build(); 11116 List<Source> sources = 11117 Arrays.asList( 11118 s1MmpMatchingWithDestinations, 11119 s2MmpDiffDestination, 11120 s3MmpExpired, 11121 s4NonMatchingMmp, 11122 s5MmpMatchingWithDestinations, 11123 s6San1MatchingWithDestinations, 11124 s7San1DiffDestination, 11125 s8San2MatchingWithDestinations, 11126 s9San3XnaIgnored, 11127 s10San3MatchingWithDestinations, 11128 s11San4EnrollmentNonMatching, 11129 s12San1NullSharedAggregationKeys, 11130 s13San1Expired, 11131 s14San5RegIdClasesWithMmp, 11132 s15MmpMatchingWithDestinations); 11133 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 11134 Objects.requireNonNull(db); 11135 // Insert all sources to the DB 11136 sources.forEach(source -> insertSource(source, source.getId())); 11137 11138 // Insert XNA ignored sources 11139 ContentValues values = new ContentValues(); 11140 values.put(XnaIgnoredSourcesContract.SOURCE_ID, s9San3XnaIgnored.getId()); 11141 values.put(XnaIgnoredSourcesContract.ENROLLMENT_ID, mmpMatchingEnrollmentId); 11142 long row = db.insert(XnaIgnoredSourcesContract.TABLE, null, values); 11143 assertEquals(1, row); 11144 11145 List<Source> expectedMatchingSources = 11146 Arrays.asList( 11147 s1MmpMatchingWithoutDestinations, 11148 s5MmpMatchingWithoutDestinations, 11149 s6San1MatchingWithoutDestinations, 11150 s8San2MatchingWithoutDestinations, 11151 s10San3MatchingWithoutDestinations, 11152 s15MmpMatchingWithoutDestinations); 11153 Comparator<Source> sortingComparator = Comparator.comparing(Source::getId); 11154 expectedMatchingSources.sort(sortingComparator); 11155 11156 // Execution 11157 mDatastoreManager.runInTransaction( 11158 dao -> { 11159 List<String> matchingSanEnrollmentIds = 11160 Arrays.asList( 11161 san1MatchingEnrollmentId, 11162 san2MatchingEnrollmentId, 11163 san3MatchingEnrollmentId, 11164 san5MatchingEnrollmentId); 11165 List<Source> actualMatchingSources = 11166 dao.fetchTriggerMatchingSourcesForXna( 11167 trigger, matchingSanEnrollmentIds); 11168 actualMatchingSources.sort(sortingComparator); 11169 // Assertion 11170 assertEquals(expectedMatchingSources, actualMatchingSources); 11171 }); 11172 } 11173 11174 @Test insertIgnoredSourceForEnrollment_success()11175 public void insertIgnoredSourceForEnrollment_success() { 11176 // Setup 11177 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 11178 // Need to insert sources before, to honor the foreign key constraint 11179 insertSource(createSourceBuilder().setId("s1").build(), "s1"); 11180 insertSource(createSourceBuilder().setId("s2").build(), "s2"); 11181 11182 Pair<String, String> entry11 = new Pair<>("s1", "e1"); 11183 Pair<String, String> entry21 = new Pair<>("s2", "e1"); 11184 Pair<String, String> entry22 = new Pair<>("s2", "e2"); 11185 11186 mDatastoreManager.runInTransaction( 11187 dao -> { 11188 // Execution 11189 dao.insertIgnoredSourceForEnrollment(entry11.first, entry11.second); 11190 dao.insertIgnoredSourceForEnrollment(entry21.first, entry21.second); 11191 dao.insertIgnoredSourceForEnrollment(entry22.first, entry22.second); 11192 11193 // Assertion 11194 queryAndAssertSourceEntries(db, "e1", Arrays.asList("s1", "s2")); 11195 queryAndAssertSourceEntries(db, "e2", Collections.singletonList("s2")); 11196 }); 11197 } 11198 11199 @Test countDistinctDebugAdIdsUsedByEnrollment_oneTriggerAndSource()11200 public void countDistinctDebugAdIdsUsedByEnrollment_oneTriggerAndSource() { 11201 // Setup 11202 Source.Builder sourceBuilder = 11203 new Source.Builder() 11204 .setPublisher(SourceFixture.ValidSourceParams.PUBLISHER) 11205 .setRegistrant(SourceFixture.ValidSourceParams.REGISTRANT) 11206 .setSourceType(SourceFixture.ValidSourceParams.SOURCE_TYPE) 11207 .setEventId(SourceFixture.ValidSourceParams.SOURCE_EVENT_ID) 11208 .setRegistrationOrigin(SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN); 11209 Source s1 = 11210 sourceBuilder 11211 .setId("s1") 11212 .setPublisherType(EventSurfaceType.WEB) 11213 .setDebugAdId("debug-ad-id-1") 11214 .setEnrollmentId("enrollment-id-1") 11215 .build(); 11216 mDatastoreManager.runInTransaction(dao -> dao.insertSource(s1)); 11217 11218 Trigger.Builder triggerBuilder = 11219 new Trigger.Builder() 11220 .setAttributionDestination( 11221 TriggerFixture.ValidTriggerParams.ATTRIBUTION_DESTINATION) 11222 .setRegistrant(TriggerFixture.ValidTriggerParams.REGISTRANT) 11223 .setRegistrationOrigin( 11224 TriggerFixture.ValidTriggerParams.REGISTRATION_ORIGIN) 11225 .setAggregatableSourceRegistrationTimeConfig( 11226 Trigger.SourceRegistrationTimeConfig.INCLUDE); 11227 Trigger t1 = 11228 triggerBuilder 11229 .setId("t1") 11230 .setDestinationType(EventSurfaceType.WEB) 11231 .setDebugAdId("debug-ad-id-1") 11232 .setEnrollmentId("enrollment-id-1") 11233 .build(); 11234 mDatastoreManager.runInTransaction(dao -> dao.insertTrigger(t1)); 11235 11236 // Assertion 11237 assertTrue( 11238 mDatastoreManager.runInTransaction( 11239 dao -> 11240 assertEquals( 11241 1, 11242 dao.countDistinctDebugAdIdsUsedByEnrollment( 11243 "enrollment-id-1")))); 11244 } 11245 11246 @Test countDistinctDebugAdIdsUsedByEnrollmentWithinWindow()11247 public void countDistinctDebugAdIdsUsedByEnrollmentWithinWindow() { 11248 // Setup 11249 long startTime = System.currentTimeMillis(); 11250 long endTime = System.currentTimeMillis() + DAYS.toMillis(7); 11251 Source.Builder webSourceBuilder = 11252 SourceFixture.getValidSourceBuilder().setPublisherType(EventSurfaceType.WEB); 11253 Trigger.Builder webTriggerBuilder = 11254 TriggerFixture.getValidTriggerBuilder().setDestinationType(EventSurfaceType.WEB); 11255 11256 // Not counted as debug ad id is null 11257 insertSource(webSourceBuilder.setDebugAdId(null).setEventTime(startTime).build(), "s1"); 11258 insertTrigger(webTriggerBuilder.setDebugAdId(null).setTriggerTime(startTime).build(), "t1"); 11259 11260 // Not counted as they are outside the window 11261 insertSource( 11262 webSourceBuilder.setDebugAdId("debug_ad_id_s2").setEventTime(startTime - 1).build(), 11263 "s2"); 11264 insertTrigger( 11265 webTriggerBuilder 11266 .setDebugAdId("debug_ad_id_t2") 11267 .setTriggerTime(startTime - 1) 11268 .build(), 11269 "t2"); 11270 11271 // count = 2 11272 insertSource( 11273 webSourceBuilder.setDebugAdId("debug_ad_id_s3").setEventTime(startTime).build(), 11274 "s3"); 11275 insertTrigger( 11276 webTriggerBuilder.setDebugAdId("debug_ad_id_t3").setTriggerTime(startTime).build(), 11277 "t3"); 11278 11279 // count = 4 11280 insertSource( 11281 webSourceBuilder 11282 .setDebugAdId("debug_ad_id_s4") 11283 .setEventTime(startTime + DAYS.toMillis(2)) 11284 .build(), 11285 "s4"); 11286 insertTrigger( 11287 webTriggerBuilder 11288 .setDebugAdId("debug_ad_id_t4") 11289 .setTriggerTime(startTime + DAYS.toMillis(2)) 11290 .build(), 11291 "t4"); 11292 11293 // count = 5; they share a common debug_ad_id value 11294 insertSource( 11295 webSourceBuilder.setDebugAdId("debug_ad_id_s5").setEventTime(endTime - 1).build(), 11296 "s5"); 11297 insertTrigger( 11298 webTriggerBuilder 11299 .setDebugAdId("debug_ad_id_s5") 11300 .setTriggerTime(endTime - 1) 11301 .build(), 11302 "t5"); 11303 11304 // Not counted as they fall outside the window 11305 insertSource( 11306 webSourceBuilder.setDebugAdId("debug_ad_id_s6").setEventTime(endTime).build(), 11307 "s6"); 11308 insertTrigger( 11309 webTriggerBuilder.setDebugAdId("debug_ad_id_s6").setTriggerTime(endTime).build(), 11310 "t6"); 11311 11312 // Not counted as they belong to a different enrollment 11313 insertSource( 11314 webSourceBuilder 11315 .setDebugAdId("debug_ad_id_s7") 11316 .setEventTime(startTime + DAYS.toMillis(2)) 11317 .setEnrollmentId("otherEnrollment") 11318 .build(), 11319 "s7"); 11320 insertTrigger( 11321 webTriggerBuilder 11322 .setDebugAdId("debug_ad_id_s7") 11323 .setTriggerTime(startTime + DAYS.toMillis(2)) 11324 .setEnrollmentId("otherEnrollment") 11325 .build(), 11326 "t7"); 11327 11328 // Not counted as they have the excluded debug AdId 11329 insertSource( 11330 webSourceBuilder 11331 .setDebugAdId("debug_ad_id_s8") 11332 .setEventTime(startTime + DAYS.toMillis(2)) 11333 .build(), 11334 "s8"); 11335 insertTrigger( 11336 webTriggerBuilder 11337 .setDebugAdId("debug_ad_id_s8") 11338 .setTriggerTime(startTime + DAYS.toMillis(2)) 11339 .build(), 11340 "t8"); 11341 11342 // Assertion 11343 assertTrue( 11344 mDatastoreManager.runInTransaction( 11345 dao -> 11346 assertEquals( 11347 5, 11348 dao.countDistinctDebugAdIdsUsedByEnrollmentInWindow( 11349 SourceFixture.ValidSourceParams.ENROLLMENT_ID, 11350 startTime, 11351 endTime, 11352 "debug_ad_id_s8")))); 11353 } 11354 11355 @Test countDistinctDebugAdIdsUsedByEnrollment_nullValuesPresent()11356 public void countDistinctDebugAdIdsUsedByEnrollment_nullValuesPresent() { 11357 // Setup 11358 Source.Builder sourceBuilder = 11359 new Source.Builder() 11360 .setPublisher(SourceFixture.ValidSourceParams.PUBLISHER) 11361 .setRegistrant(SourceFixture.ValidSourceParams.REGISTRANT) 11362 .setSourceType(SourceFixture.ValidSourceParams.SOURCE_TYPE) 11363 .setEventId(SourceFixture.ValidSourceParams.SOURCE_EVENT_ID) 11364 .setRegistrationOrigin(SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN); 11365 // Source with debug AdId present 11366 Source s1 = 11367 sourceBuilder 11368 .setId("s1") 11369 .setPublisherType(EventSurfaceType.WEB) 11370 .setDebugAdId("debug-ad-id-1") 11371 .setEnrollmentId("enrollment-id-1") 11372 .build(); 11373 mDatastoreManager.runInTransaction(dao -> dao.insertSource(s1)); 11374 // Source with no debug AdId 11375 Source s2 = 11376 sourceBuilder 11377 .setId("s2") 11378 .setPublisherType(EventSurfaceType.WEB) 11379 .setEnrollmentId("enrollment-id-1") 11380 .build(); 11381 mDatastoreManager.runInTransaction(dao -> dao.insertSource(s2)); 11382 11383 Trigger.Builder triggerBuilder = 11384 new Trigger.Builder() 11385 .setAttributionDestination( 11386 TriggerFixture.ValidTriggerParams.ATTRIBUTION_DESTINATION) 11387 .setRegistrant(TriggerFixture.ValidTriggerParams.REGISTRANT) 11388 .setRegistrationOrigin( 11389 TriggerFixture.ValidTriggerParams.REGISTRATION_ORIGIN) 11390 .setAggregatableSourceRegistrationTimeConfig( 11391 Trigger.SourceRegistrationTimeConfig.INCLUDE); 11392 // Trigger with debug AdId present 11393 Trigger t1 = 11394 triggerBuilder 11395 .setId("t1") 11396 .setDestinationType(EventSurfaceType.WEB) 11397 .setDebugAdId("debug-ad-id-1") 11398 .setEnrollmentId("enrollment-id-1") 11399 .build(); 11400 mDatastoreManager.runInTransaction(dao -> dao.insertTrigger(t1)); 11401 // Trigger with no debug AdId 11402 Trigger t2 = 11403 triggerBuilder 11404 .setId("t2") 11405 .setDestinationType(EventSurfaceType.WEB) 11406 .setEnrollmentId("enrollment-id-1") 11407 .build(); 11408 mDatastoreManager.runInTransaction(dao -> dao.insertTrigger(t2)); 11409 11410 // Assertion 11411 assertTrue( 11412 mDatastoreManager.runInTransaction( 11413 dao -> 11414 assertEquals( 11415 1, 11416 dao.countDistinctDebugAdIdsUsedByEnrollment( 11417 "enrollment-id-1")))); 11418 } 11419 11420 @Test countDistinctDebugAdIdsUsedByEnrollment_multipleSourcesAndTriggers()11421 public void countDistinctDebugAdIdsUsedByEnrollment_multipleSourcesAndTriggers() { 11422 // Setup 11423 Source.Builder sourceBuilder = 11424 new Source.Builder() 11425 .setPublisher(SourceFixture.ValidSourceParams.PUBLISHER) 11426 .setRegistrant(SourceFixture.ValidSourceParams.REGISTRANT) 11427 .setSourceType(SourceFixture.ValidSourceParams.SOURCE_TYPE) 11428 .setEventId(SourceFixture.ValidSourceParams.SOURCE_EVENT_ID) 11429 .setRegistrationOrigin(SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN); 11430 // Multiple sources with same AdId 11431 Source s1 = 11432 sourceBuilder 11433 .setId("s1") 11434 .setPublisherType(EventSurfaceType.WEB) 11435 .setDebugAdId("debug-ad-id-1") 11436 .setEnrollmentId("enrollment-id-1") 11437 .build(); 11438 mDatastoreManager.runInTransaction(dao -> dao.insertSource(s1)); 11439 Source s2 = 11440 sourceBuilder 11441 .setId("s2") 11442 .setPublisherType(EventSurfaceType.WEB) 11443 .setDebugAdId("debug-ad-id-1") 11444 .setEnrollmentId("enrollment-id-1") 11445 .build(); 11446 mDatastoreManager.runInTransaction(dao -> dao.insertSource(s2)); 11447 Source s3 = 11448 sourceBuilder 11449 .setId("s3") 11450 .setPublisherType(EventSurfaceType.WEB) 11451 .setDebugAdId("debug-ad-id-1") 11452 .setEnrollmentId("enrollment-id-1") 11453 .build(); 11454 mDatastoreManager.runInTransaction(dao -> dao.insertSource(s3)); 11455 11456 Trigger.Builder triggerBuilder = 11457 new Trigger.Builder() 11458 .setAttributionDestination( 11459 TriggerFixture.ValidTriggerParams.ATTRIBUTION_DESTINATION) 11460 .setRegistrant(TriggerFixture.ValidTriggerParams.REGISTRANT) 11461 .setRegistrationOrigin( 11462 TriggerFixture.ValidTriggerParams.REGISTRATION_ORIGIN) 11463 .setAggregatableSourceRegistrationTimeConfig( 11464 Trigger.SourceRegistrationTimeConfig.INCLUDE); 11465 // Multiple triggers with same AdId 11466 Trigger t1 = 11467 triggerBuilder 11468 .setId("t1") 11469 .setDestinationType(EventSurfaceType.WEB) 11470 .setDebugAdId("debug-ad-id-1") 11471 .setEnrollmentId("enrollment-id-1") 11472 .build(); 11473 mDatastoreManager.runInTransaction(dao -> dao.insertTrigger(t1)); 11474 Trigger t2 = 11475 triggerBuilder 11476 .setId("t2") 11477 .setDestinationType(EventSurfaceType.WEB) 11478 .setDebugAdId("debug-ad-id-1") 11479 .setEnrollmentId("enrollment-id-1") 11480 .build(); 11481 mDatastoreManager.runInTransaction(dao -> dao.insertTrigger(t2)); 11482 Trigger t3 = 11483 triggerBuilder 11484 .setId("t3") 11485 .setDestinationType(EventSurfaceType.WEB) 11486 .setDebugAdId("debug-ad-id-1") 11487 .setEnrollmentId("enrollment-id-1") 11488 .build(); 11489 mDatastoreManager.runInTransaction(dao -> dao.insertTrigger(t3)); 11490 11491 // Assertion 11492 assertTrue( 11493 mDatastoreManager.runInTransaction( 11494 dao -> 11495 assertEquals( 11496 1, 11497 dao.countDistinctDebugAdIdsUsedByEnrollment( 11498 "enrollment-id-1")))); 11499 } 11500 11501 @Test countDistinctDebugAdIdsUsedByEnrollment_multipleAdIdsPresent()11502 public void countDistinctDebugAdIdsUsedByEnrollment_multipleAdIdsPresent() { 11503 // Setup 11504 Source.Builder sourceBuilder = 11505 new Source.Builder() 11506 .setPublisher(SourceFixture.ValidSourceParams.PUBLISHER) 11507 .setRegistrant(SourceFixture.ValidSourceParams.REGISTRANT) 11508 .setSourceType(SourceFixture.ValidSourceParams.SOURCE_TYPE) 11509 .setEventId(SourceFixture.ValidSourceParams.SOURCE_EVENT_ID) 11510 .setRegistrationOrigin(SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN); 11511 // Multiple sources with different AdIds but the same enrollmentId 11512 Source s1 = 11513 sourceBuilder 11514 .setId("s1") 11515 .setPublisherType(EventSurfaceType.WEB) 11516 .setDebugAdId("debug-ad-id-1") 11517 .setEnrollmentId("enrollment-id-1") 11518 .build(); 11519 mDatastoreManager.runInTransaction(dao -> dao.insertSource(s1)); 11520 Source s2 = 11521 sourceBuilder 11522 .setId("s2") 11523 .setPublisherType(EventSurfaceType.WEB) 11524 .setDebugAdId("debug-ad-id-2") 11525 .setEnrollmentId("enrollment-id-1") 11526 .build(); 11527 mDatastoreManager.runInTransaction(dao -> dao.insertSource(s2)); 11528 Source s3 = 11529 sourceBuilder 11530 .setId("s3") 11531 .setPublisherType(EventSurfaceType.WEB) 11532 .setDebugAdId("debug-ad-id-3") 11533 .setEnrollmentId("enrollment-id-1") 11534 .build(); 11535 mDatastoreManager.runInTransaction(dao -> dao.insertSource(s3)); 11536 11537 Trigger.Builder triggerBuilder = 11538 new Trigger.Builder() 11539 .setAttributionDestination( 11540 TriggerFixture.ValidTriggerParams.ATTRIBUTION_DESTINATION) 11541 .setRegistrant(TriggerFixture.ValidTriggerParams.REGISTRANT) 11542 .setRegistrationOrigin( 11543 TriggerFixture.ValidTriggerParams.REGISTRATION_ORIGIN) 11544 .setAggregatableSourceRegistrationTimeConfig( 11545 Trigger.SourceRegistrationTimeConfig.INCLUDE); 11546 // Multiple triggers with different AdIds but the same enrollmentId 11547 Trigger t1 = 11548 triggerBuilder 11549 .setId("t1") 11550 .setDestinationType(EventSurfaceType.WEB) 11551 .setDebugAdId("debug-ad-id-4") 11552 .setEnrollmentId("enrollment-id-1") 11553 .build(); 11554 mDatastoreManager.runInTransaction(dao -> dao.insertTrigger(t1)); 11555 Trigger t2 = 11556 triggerBuilder 11557 .setId("t2") 11558 .setDestinationType(EventSurfaceType.WEB) 11559 .setDebugAdId("debug-ad-id-5") 11560 .setEnrollmentId("enrollment-id-1") 11561 .build(); 11562 mDatastoreManager.runInTransaction(dao -> dao.insertTrigger(t2)); 11563 Trigger t3 = 11564 triggerBuilder 11565 .setId("t3") 11566 .setDestinationType(EventSurfaceType.WEB) 11567 .setDebugAdId("debug-ad-id-6") 11568 .setEnrollmentId("enrollment-id-1") 11569 .build(); 11570 mDatastoreManager.runInTransaction(dao -> dao.insertTrigger(t3)); 11571 11572 // Assertion 11573 assertTrue( 11574 mDatastoreManager.runInTransaction( 11575 dao -> 11576 assertEquals( 11577 6, 11578 dao.countDistinctDebugAdIdsUsedByEnrollment( 11579 "enrollment-id-1")))); 11580 } 11581 11582 @Test countDistinctDebugAdIdsUsedByEnrollment_multipleEnrollmentIdsPresent()11583 public void countDistinctDebugAdIdsUsedByEnrollment_multipleEnrollmentIdsPresent() { 11584 // Setup 11585 Source.Builder sourceBuilder = 11586 new Source.Builder() 11587 .setPublisher(SourceFixture.ValidSourceParams.PUBLISHER) 11588 .setRegistrant(SourceFixture.ValidSourceParams.REGISTRANT) 11589 .setSourceType(SourceFixture.ValidSourceParams.SOURCE_TYPE) 11590 .setEventId(SourceFixture.ValidSourceParams.SOURCE_EVENT_ID) 11591 .setRegistrationOrigin(SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN); 11592 // Multiple sources with different AdIds and differing enrollmentIds 11593 Source s1 = 11594 sourceBuilder 11595 .setId("s1") 11596 .setPublisherType(EventSurfaceType.WEB) 11597 .setDebugAdId("debug-ad-id-1") 11598 .setEnrollmentId("enrollment-id-1") 11599 .build(); 11600 mDatastoreManager.runInTransaction(dao -> dao.insertSource(s1)); 11601 Source s2 = 11602 sourceBuilder 11603 .setId("s2") 11604 .setPublisherType(EventSurfaceType.WEB) 11605 .setDebugAdId("debug-ad-id-2") 11606 .setEnrollmentId("enrollment-id-2") 11607 .build(); 11608 mDatastoreManager.runInTransaction(dao -> dao.insertSource(s2)); 11609 Source s3 = 11610 sourceBuilder 11611 .setId("s3") 11612 .setPublisherType(EventSurfaceType.WEB) 11613 .setDebugAdId("debug-ad-id-3") 11614 .setEnrollmentId("enrollment-id-2") 11615 .build(); 11616 mDatastoreManager.runInTransaction(dao -> dao.insertSource(s3)); 11617 11618 Trigger.Builder triggerBuilder = 11619 new Trigger.Builder() 11620 .setAttributionDestination( 11621 TriggerFixture.ValidTriggerParams.ATTRIBUTION_DESTINATION) 11622 .setRegistrant(TriggerFixture.ValidTriggerParams.REGISTRANT) 11623 .setRegistrationOrigin( 11624 TriggerFixture.ValidTriggerParams.REGISTRATION_ORIGIN) 11625 .setAggregatableSourceRegistrationTimeConfig( 11626 Trigger.SourceRegistrationTimeConfig.INCLUDE); 11627 // Multiple triggers with different AdIds and differing enrollmentIds 11628 Trigger t1 = 11629 triggerBuilder 11630 .setId("t1") 11631 .setDestinationType(EventSurfaceType.WEB) 11632 .setDebugAdId("debug-ad-id-4") 11633 .setEnrollmentId("enrollment-id-1") 11634 .build(); 11635 mDatastoreManager.runInTransaction(dao -> dao.insertTrigger(t1)); 11636 Trigger t2 = 11637 triggerBuilder 11638 .setId("t2") 11639 .setDestinationType(EventSurfaceType.WEB) 11640 .setDebugAdId("debug-ad-id-5") 11641 .setEnrollmentId("enrollment-id-2") 11642 .build(); 11643 mDatastoreManager.runInTransaction(dao -> dao.insertTrigger(t2)); 11644 Trigger t3 = 11645 triggerBuilder 11646 .setId("t3") 11647 .setDestinationType(EventSurfaceType.WEB) 11648 .setDebugAdId("debug-ad-id-6") 11649 .setEnrollmentId("enrollment-id-2") 11650 .build(); 11651 mDatastoreManager.runInTransaction(dao -> dao.insertTrigger(t3)); 11652 11653 // Assertion 11654 assertTrue( 11655 mDatastoreManager.runInTransaction( 11656 dao -> 11657 assertEquals( 11658 2, 11659 dao.countDistinctDebugAdIdsUsedByEnrollment( 11660 "enrollment-id-1")))); 11661 assertTrue( 11662 mDatastoreManager.runInTransaction( 11663 dao -> 11664 assertEquals( 11665 4, 11666 dao.countDistinctDebugAdIdsUsedByEnrollment( 11667 "enrollment-id-2")))); 11668 } 11669 11670 @Test getPendingAggregateReportIdsByCoordinatorInWindow()11671 public void getPendingAggregateReportIdsByCoordinatorInWindow() { 11672 AggregateReport ar11 = 11673 AggregateReportFixture.getValidAggregateReportBuilder() 11674 .setId("11") 11675 .setAggregationCoordinatorOrigin(Uri.parse("https://url1.test")) 11676 .build(); 11677 AggregateReport ar12 = 11678 AggregateReportFixture.getValidAggregateReportBuilder() 11679 .setId("12") 11680 .setAggregationCoordinatorOrigin(Uri.parse("https://url1.test")) 11681 .build(); 11682 AggregateReport ar21 = 11683 AggregateReportFixture.getValidAggregateReportBuilder() 11684 .setId("21") 11685 .setAggregationCoordinatorOrigin(Uri.parse("https://url2.test")) 11686 .build(); 11687 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 11688 AbstractDbIntegrationTest.insertToDb(ar11, db); 11689 AbstractDbIntegrationTest.insertToDb(ar12, db); 11690 AbstractDbIntegrationTest.insertToDb(ar21, db); 11691 11692 Optional<Map<String, List<String>>> resOpt = 11693 mDatastoreManager.runInTransactionWithResult( 11694 (dao) -> 11695 dao.getPendingAggregateReportIdsByCoordinatorInWindow( 11696 AggregateReportFixture.ValidAggregateReportParams 11697 .TRIGGER_TIME, 11698 AggregateReportFixture.ValidAggregateReportParams 11699 .TRIGGER_TIME 11700 + DAYS.toMillis(30))); 11701 assertTrue(resOpt.isPresent()); 11702 Map<String, List<String>> res = resOpt.get(); 11703 assertEquals(2, res.size()); 11704 11705 // URL 1 11706 List<String> url1Ids = res.get("https://url1.test"); 11707 url1Ids.sort(String.CASE_INSENSITIVE_ORDER); 11708 assertEquals(2, url1Ids.size()); 11709 assertEquals("11", url1Ids.get(0)); 11710 assertEquals("12", url1Ids.get(1)); 11711 // URL 2 11712 List<String> url2Ids = res.get("https://url2.test"); 11713 url2Ids.sort(String.CASE_INSENSITIVE_ORDER); 11714 assertEquals(1, url2Ids.size()); 11715 assertEquals("21", url2Ids.get(0)); 11716 } 11717 11718 @Test getPendingAggregateReportIdsByCoordinatorInWindowWithRetryLimit()11719 public void getPendingAggregateReportIdsByCoordinatorInWindowWithRetryLimit() { 11720 // Mocking that the flags return a Max Retry of 1 11721 Flags mockFlags = Mockito.mock(Flags.class); 11722 ExtendedMockito.doReturn(mockFlags).when(FlagsFactory::getFlags); 11723 ExtendedMockito.doReturn(1).when(mockFlags).getMeasurementReportingRetryLimit(); 11724 ExtendedMockito.doReturn(true).when(mockFlags).getMeasurementReportingRetryLimitEnabled(); 11725 11726 AggregateReport ar11 = 11727 AggregateReportFixture.getValidAggregateReportBuilder() 11728 .setId("11") 11729 .setAggregationCoordinatorOrigin(Uri.parse("https://url1.test")) 11730 .build(); 11731 AggregateReport ar12 = 11732 AggregateReportFixture.getValidAggregateReportBuilder() 11733 .setId("12") 11734 .setAggregationCoordinatorOrigin(Uri.parse("https://url1.test")) 11735 .build(); 11736 AggregateReport ar21 = 11737 AggregateReportFixture.getValidAggregateReportBuilder() 11738 .setId("21") 11739 .setAggregationCoordinatorOrigin(Uri.parse("https://url2.test")) 11740 .build(); 11741 AggregateReport ar31 = 11742 AggregateReportFixture.getValidAggregateReportBuilder() 11743 .setId("31") 11744 .setAggregationCoordinatorOrigin(Uri.parse("https://url3.test")) 11745 .build(); 11746 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 11747 AbstractDbIntegrationTest.insertToDb(ar11, db); 11748 AbstractDbIntegrationTest.insertToDb(ar12, db); 11749 AbstractDbIntegrationTest.insertToDb(ar21, db); 11750 AbstractDbIntegrationTest.insertToDb(ar31, db); 11751 11752 Optional<Map<String, List<String>>> resOpt = 11753 mDatastoreManager.runInTransactionWithResult( 11754 (dao) -> { 11755 return dao.getPendingAggregateReportIdsByCoordinatorInWindow( 11756 AggregateReportFixture.ValidAggregateReportParams.TRIGGER_TIME, 11757 AggregateReportFixture.ValidAggregateReportParams.TRIGGER_TIME 11758 + DAYS.toMillis(30)); 11759 }); 11760 assertTrue(resOpt.isPresent()); 11761 Map<String, List<String>> res = resOpt.get(); 11762 assertEquals(3, res.size()); 11763 11764 // URL 1 11765 List<String> url1Ids = res.get("https://url1.test"); 11766 url1Ids.sort(String.CASE_INSENSITIVE_ORDER); 11767 assertEquals(2, url1Ids.size()); 11768 assertEquals("11", url1Ids.get(0)); 11769 assertEquals("12", url1Ids.get(1)); 11770 // URL 2 11771 List<String> url2Ids = res.get("https://url2.test"); 11772 url2Ids.sort(String.CASE_INSENSITIVE_ORDER); 11773 assertEquals(1, url2Ids.size()); 11774 assertEquals("21", url2Ids.get(0)); 11775 11776 resOpt = 11777 mDatastoreManager.runInTransactionWithResult( 11778 (dao) -> { 11779 // Adds records to KeyValueData table for Retry Count. 11780 dao.incrementAndGetReportingRetryCount( 11781 ar31.getId(), DataType.AGGREGATE_REPORT_RETRY_COUNT); 11782 return dao.getPendingAggregateReportIdsByCoordinatorInWindow( 11783 AggregateReportFixture.ValidAggregateReportParams.TRIGGER_TIME, 11784 AggregateReportFixture.ValidAggregateReportParams.TRIGGER_TIME 11785 + DAYS.toMillis(30)); 11786 }); 11787 res = resOpt.get(); 11788 11789 assertEquals(2, res.size()); 11790 11791 // URL 1 11792 url1Ids = res.get("https://url1.test"); 11793 url1Ids.sort(String.CASE_INSENSITIVE_ORDER); 11794 assertEquals(2, url1Ids.size()); 11795 assertEquals("11", url1Ids.get(0)); 11796 assertEquals("12", url1Ids.get(1)); 11797 // URL 2 11798 url2Ids = res.get("https://url2.test"); 11799 url2Ids.sort(String.CASE_INSENSITIVE_ORDER); 11800 assertEquals(1, url2Ids.size()); 11801 assertEquals("21", url2Ids.get(0)); 11802 } 11803 11804 @Test getPendingAggregateDebugReportIdsByCoordinator()11805 public void getPendingAggregateDebugReportIdsByCoordinator() { 11806 AggregateReport ar11 = 11807 AggregateReportFixture.getValidAggregateReportBuilder() 11808 .setId("11") 11809 .setAggregationCoordinatorOrigin(Uri.parse("https://url1.test")) 11810 .build(); 11811 AggregateReport ar12 = 11812 AggregateReportFixture.getValidAggregateReportBuilder() 11813 .setId("12") 11814 .setAggregationCoordinatorOrigin(Uri.parse("https://url1.test")) 11815 .build(); 11816 AggregateReport ar21 = 11817 AggregateReportFixture.getValidAggregateReportBuilder() 11818 .setId("21") 11819 .setAggregationCoordinatorOrigin(Uri.parse("https://url2.test")) 11820 .build(); 11821 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 11822 AbstractDbIntegrationTest.insertToDb(ar11, db); 11823 AbstractDbIntegrationTest.insertToDb(ar12, db); 11824 AbstractDbIntegrationTest.insertToDb(ar21, db); 11825 11826 Optional<Map<String, List<String>>> resOpt = 11827 mDatastoreManager.runInTransactionWithResult( 11828 (dao) -> 11829 dao.getPendingAggregateReportIdsByCoordinatorInWindow( 11830 AggregateReportFixture.ValidAggregateReportParams 11831 .TRIGGER_TIME, 11832 AggregateReportFixture.ValidAggregateReportParams 11833 .TRIGGER_TIME 11834 + DAYS.toMillis(30))); 11835 assertTrue(resOpt.isPresent()); 11836 Map<String, List<String>> res = resOpt.get(); 11837 assertEquals(2, res.size()); 11838 11839 // URL 1 11840 List<String> url1Ids = res.get("https://url1.test"); 11841 assertEquals(2, url1Ids.size()); 11842 url1Ids.sort(String.CASE_INSENSITIVE_ORDER); 11843 assertEquals("11", url1Ids.get(0)); 11844 assertEquals("12", url1Ids.get(1)); 11845 // URL 2 11846 List<String> url2Ids = res.get("https://url2.test"); 11847 url2Ids.sort(String.CASE_INSENSITIVE_ORDER); 11848 assertEquals(1, url2Ids.size()); 11849 assertEquals("21", url2Ids.get(0)); 11850 } 11851 11852 @Test getPendingAggregateDebugReportIdsByCoordinatorWithRetryLimit()11853 public void getPendingAggregateDebugReportIdsByCoordinatorWithRetryLimit() { 11854 // Mocking that the flags return a Max Retry of 1 11855 Flags mockFlags = Mockito.mock(Flags.class); 11856 ExtendedMockito.doReturn(mockFlags).when(FlagsFactory::getFlags); 11857 ExtendedMockito.doReturn(1).when(mockFlags).getMeasurementReportingRetryLimit(); 11858 ExtendedMockito.doReturn(true).when(mockFlags).getMeasurementReportingRetryLimitEnabled(); 11859 11860 AggregateReport ar11 = 11861 AggregateReportFixture.getValidAggregateReportBuilder() 11862 .setId("11") 11863 .setAggregationCoordinatorOrigin(Uri.parse("https://url1.test")) 11864 .setDebugReportStatus(AggregateReport.DebugReportStatus.PENDING) 11865 .build(); 11866 AggregateReport ar12 = 11867 AggregateReportFixture.getValidAggregateReportBuilder() 11868 .setId("12") 11869 .setAggregationCoordinatorOrigin(Uri.parse("https://url1.test")) 11870 .setDebugReportStatus(AggregateReport.DebugReportStatus.PENDING) 11871 .build(); 11872 AggregateReport ar21 = 11873 AggregateReportFixture.getValidAggregateReportBuilder() 11874 .setId("21") 11875 .setAggregationCoordinatorOrigin(Uri.parse("https://url2.test")) 11876 .setDebugReportStatus(AggregateReport.DebugReportStatus.PENDING) 11877 .build(); 11878 AggregateReport ar31 = 11879 AggregateReportFixture.getValidAggregateReportBuilder() 11880 .setId("31") 11881 .setAggregationCoordinatorOrigin(Uri.parse("https://url3.test")) 11882 .setDebugReportStatus(AggregateReport.DebugReportStatus.PENDING) 11883 .build(); 11884 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 11885 AbstractDbIntegrationTest.insertToDb(ar11, db); 11886 AbstractDbIntegrationTest.insertToDb(ar12, db); 11887 AbstractDbIntegrationTest.insertToDb(ar21, db); 11888 AbstractDbIntegrationTest.insertToDb(ar31, db); 11889 11890 Optional<Map<String, List<String>>> resOpt = 11891 mDatastoreManager.runInTransactionWithResult( 11892 (dao) -> dao.getPendingAggregateDebugReportIdsByCoordinator()); 11893 assertTrue(resOpt.isPresent()); 11894 Map<String, List<String>> res = resOpt.get(); 11895 assertEquals(3, res.size()); 11896 11897 resOpt = 11898 mDatastoreManager.runInTransactionWithResult( 11899 (dao) -> { 11900 // Adds records to KeyValueData table for Retry Count. 11901 dao.incrementAndGetReportingRetryCount( 11902 ar31.getId(), DataType.AGGREGATE_REPORT_RETRY_COUNT); 11903 return dao.getPendingAggregateDebugReportIdsByCoordinator(); 11904 }); 11905 assertTrue(resOpt.isPresent()); 11906 res = resOpt.get(); 11907 assertEquals(2, res.size()); 11908 11909 // URL 1 11910 List<String> url1Ids = res.get("https://url1.test"); 11911 assertEquals(2, url1Ids.size()); 11912 url1Ids.sort(String.CASE_INSENSITIVE_ORDER); 11913 assertEquals("11", url1Ids.get(0)); 11914 assertEquals("12", url1Ids.get(1)); 11915 // URL 2 11916 List<String> url2Ids = res.get("https://url2.test"); 11917 url2Ids.sort(String.CASE_INSENSITIVE_ORDER); 11918 assertEquals(1, url2Ids.size()); 11919 assertEquals("21", url2Ids.get(0)); 11920 } 11921 11922 @Test getPendingEventReportIdsInWindowWithRetry()11923 public void getPendingEventReportIdsInWindowWithRetry() { 11924 // Mocking that the flags return a Max Retry of 1 11925 Flags mockFlags = Mockito.mock(Flags.class); 11926 ExtendedMockito.doReturn(mockFlags).when(FlagsFactory::getFlags); 11927 ExtendedMockito.doReturn(1).when(mockFlags).getMeasurementReportingRetryLimit(); 11928 ExtendedMockito.doReturn(true).when(mockFlags).getMeasurementReportingRetryLimitEnabled(); 11929 11930 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 11931 11932 EventReport er1 = 11933 generateMockEventReport(WebUtil.validUrl("https://destination-1.test"), 1); 11934 EventReport er2 = 11935 generateMockEventReport(WebUtil.validUrl("https://destination-2.test"), 2); 11936 List.of(er1, er2) 11937 .forEach( 11938 eventReport -> { 11939 ContentValues values = new ContentValues(); 11940 values.put(EventReportContract.ID, eventReport.getId()); 11941 values.put( 11942 EventReportContract.ATTRIBUTION_DESTINATION, 11943 eventReport.getAttributionDestinations().get(0).toString()); 11944 values.put( 11945 EventReportContract.REPORT_TIME, 11946 EventReportFixture.ValidEventReportParams.TRIGGER_TIME 11947 + DAYS.toMillis(15)); 11948 values.put(EventReportContract.STATUS, EventReport.Status.PENDING); 11949 db.insert(EventReportContract.TABLE, null, values); 11950 }); 11951 Optional<List<String>> resOpt = 11952 mDatastoreManager.runInTransactionWithResult( 11953 (dao) -> 11954 dao.getPendingEventReportIdsInWindow( 11955 EventReportFixture.ValidEventReportParams.TRIGGER_TIME, 11956 EventReportFixture.ValidEventReportParams.TRIGGER_TIME 11957 + DAYS.toMillis(30))); 11958 assertTrue(resOpt.isPresent()); 11959 List<String> res = resOpt.get(); 11960 assertEquals(2, res.size()); 11961 assertTrue(res.containsAll(List.of("1", "2"))); 11962 resOpt = 11963 mDatastoreManager.runInTransactionWithResult( 11964 (dao) -> { 11965 // Adds records to KeyValueData table for Retry Count. 11966 dao.incrementAndGetReportingRetryCount( 11967 "1", DataType.EVENT_REPORT_RETRY_COUNT); 11968 return dao.getPendingEventReportIdsInWindow( 11969 EventReportFixture.ValidEventReportParams.TRIGGER_TIME, 11970 EventReportFixture.ValidEventReportParams.TRIGGER_TIME 11971 + DAYS.toMillis(30)); 11972 }); 11973 res = resOpt.get(); 11974 assertEquals(1, res.size()); 11975 assertEquals(List.of("2"), res); 11976 } 11977 11978 @Test getPendingEventDebugReportIdsWithRetryLimit()11979 public void getPendingEventDebugReportIdsWithRetryLimit() { 11980 // Mocking that the flags return a Max Retry of 1 11981 Flags mockFlags = Mockito.mock(Flags.class); 11982 ExtendedMockito.doReturn(mockFlags).when(FlagsFactory::getFlags); 11983 ExtendedMockito.doReturn(1).when(mockFlags).getMeasurementReportingRetryLimit(); 11984 ExtendedMockito.doReturn(true).when(mockFlags).getMeasurementReportingRetryLimitEnabled(); 11985 11986 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 11987 11988 EventReport er1 = 11989 generateMockEventReport(WebUtil.validUrl("https://destination-1.test"), 1); 11990 EventReport er2 = 11991 generateMockEventReport(WebUtil.validUrl("https://destination-2.test"), 2); 11992 List.of(er1, er2) 11993 .forEach( 11994 eventReport -> { 11995 ContentValues values = new ContentValues(); 11996 values.put(EventReportContract.ID, eventReport.getId()); 11997 values.put( 11998 EventReportContract.ATTRIBUTION_DESTINATION, 11999 eventReport.getAttributionDestinations().get(0).toString()); 12000 values.put( 12001 EventReportContract.DEBUG_REPORT_STATUS, 12002 EventReport.DebugReportStatus.PENDING); 12003 db.insert(EventReportContract.TABLE, null, values); 12004 }); 12005 12006 Optional<List<String>> resOpt = 12007 mDatastoreManager.runInTransactionWithResult( 12008 (dao) -> dao.getPendingDebugEventReportIds()); 12009 assertTrue(resOpt.isPresent()); 12010 List<String> res = resOpt.get(); 12011 assertEquals(2, res.size()); 12012 assertTrue(res.containsAll(List.of("1", "2"))); 12013 resOpt = 12014 mDatastoreManager.runInTransactionWithResult( 12015 (dao) -> { 12016 // Adds records to KeyValueData table for Retry Count. 12017 dao.incrementAndGetReportingRetryCount( 12018 "1", DataType.DEBUG_EVENT_REPORT_RETRY_COUNT); 12019 return dao.getPendingDebugEventReportIds(); 12020 }); 12021 res = resOpt.get(); 12022 assertEquals(1, res.size()); 12023 assertEquals(List.of("2"), res); 12024 } 12025 12026 @Test getNonExpiredAggregateEncryptionKeys()12027 public void getNonExpiredAggregateEncryptionKeys() { 12028 AggregateEncryptionKey ek11 = 12029 new AggregateEncryptionKey.Builder() 12030 .setId("11") 12031 .setKeyId("11") 12032 .setPublicKey("11") 12033 .setExpiry(11) 12034 .setAggregationCoordinatorOrigin(Uri.parse("https://1coordinator.test")) 12035 .build(); 12036 // ek12 will not be fetched because expiry (5) < 10 12037 AggregateEncryptionKey ek12 = 12038 new AggregateEncryptionKey.Builder() 12039 .setId("12") 12040 .setKeyId("12") 12041 .setPublicKey("12") 12042 .setExpiry(5) 12043 .setAggregationCoordinatorOrigin(Uri.parse("https://1coordinator.test")) 12044 .build(); 12045 12046 AggregateEncryptionKey ek21 = 12047 new AggregateEncryptionKey.Builder() 12048 .setId("21") 12049 .setKeyId("21") 12050 .setPublicKey("21") 12051 .setExpiry(10) 12052 .setAggregationCoordinatorOrigin(Uri.parse("https://2coordinator.test")) 12053 .build(); 12054 AggregateEncryptionKey ek22 = 12055 new AggregateEncryptionKey.Builder() 12056 .setId("22") 12057 .setKeyId("22") 12058 .setPublicKey("22") 12059 .setExpiry(15) 12060 .setAggregationCoordinatorOrigin(Uri.parse("https://2coordinator.test")) 12061 .build(); 12062 12063 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 12064 AbstractDbIntegrationTest.insertToDb(ek11, db); 12065 AbstractDbIntegrationTest.insertToDb(ek12, db); 12066 AbstractDbIntegrationTest.insertToDb(ek21, db); 12067 AbstractDbIntegrationTest.insertToDb(ek22, db); 12068 12069 List<AggregateEncryptionKey> res1 = 12070 mDatastoreManager 12071 .runInTransactionWithResult( 12072 (dao) -> { 12073 return dao.getNonExpiredAggregateEncryptionKeys( 12074 Uri.parse("https://1coordinator.test"), 10); 12075 }) 12076 .orElseThrow(); 12077 // ek12 will not be fetched because expiry (5) < 10 12078 assertEquals(1, res1.size()); 12079 assertEquals(ek11, res1.get(0)); 12080 12081 List<AggregateEncryptionKey> res2 = 12082 mDatastoreManager 12083 .runInTransactionWithResult( 12084 (dao) -> { 12085 return dao.getNonExpiredAggregateEncryptionKeys( 12086 Uri.parse("https://2coordinator.test"), 10); 12087 }) 12088 .orElseThrow(); 12089 res1.sort((x, y) -> String.CASE_INSENSITIVE_ORDER.compare(x.getId(), y.getId())); 12090 assertEquals(2, res2.size()); 12091 assertEquals(ek21, res2.get(0)); 12092 assertEquals(ek22, res2.get(1)); 12093 } 12094 12095 @Test incrementReportRetryIncrements()12096 public void incrementReportRetryIncrements() { 12097 final String eventId = "TestIdEvent"; 12098 final String aggregateId = "TestIdAggregate"; 12099 final String debugId = "TestIdDebug"; 12100 12101 mDatastoreManager.runInTransaction( 12102 (dao) -> { 12103 dao.incrementAndGetReportingRetryCount( 12104 eventId, DataType.EVENT_REPORT_RETRY_COUNT); 12105 dao.incrementAndGetReportingRetryCount( 12106 eventId, DataType.DEBUG_EVENT_REPORT_RETRY_COUNT); 12107 dao.incrementAndGetReportingRetryCount( 12108 aggregateId, DataType.AGGREGATE_REPORT_RETRY_COUNT); 12109 dao.incrementAndGetReportingRetryCount( 12110 aggregateId, DataType.DEBUG_AGGREGATE_REPORT_RETRY_COUNT); 12111 dao.incrementAndGetReportingRetryCount( 12112 debugId, DataType.DEBUG_REPORT_RETRY_COUNT); 12113 }); 12114 Optional<KeyValueData> eventCount = 12115 mDatastoreManager.runInTransactionWithResult( 12116 (dao) -> dao.getKeyValueData(eventId, DataType.EVENT_REPORT_RETRY_COUNT)); 12117 Optional<KeyValueData> debugEventCount = 12118 mDatastoreManager.runInTransactionWithResult( 12119 (dao) -> 12120 dao.getKeyValueData( 12121 eventId, DataType.DEBUG_EVENT_REPORT_RETRY_COUNT)); 12122 Optional<KeyValueData> aggregateCount = 12123 mDatastoreManager.runInTransactionWithResult( 12124 (dao) -> 12125 dao.getKeyValueData( 12126 aggregateId, DataType.AGGREGATE_REPORT_RETRY_COUNT)); 12127 Optional<KeyValueData> debugAggregateCount = 12128 mDatastoreManager.runInTransactionWithResult( 12129 (dao) -> 12130 dao.getKeyValueData( 12131 aggregateId, DataType.DEBUG_AGGREGATE_REPORT_RETRY_COUNT)); 12132 Optional<KeyValueData> debugCount = 12133 mDatastoreManager.runInTransactionWithResult( 12134 (dao) -> dao.getKeyValueData(debugId, DataType.DEBUG_REPORT_RETRY_COUNT)); 12135 12136 assertTrue(eventCount.isPresent()); 12137 assertEquals(1, (eventCount.get().getReportRetryCount())); 12138 assertTrue(debugEventCount.isPresent()); 12139 assertEquals(1, (debugEventCount.get().getReportRetryCount())); 12140 assertTrue(aggregateCount.isPresent()); 12141 assertEquals(1, (aggregateCount.get().getReportRetryCount())); 12142 assertTrue(debugAggregateCount.isPresent()); 12143 assertEquals(1, (debugAggregateCount.get().getReportRetryCount())); 12144 assertTrue(debugCount.isPresent()); 12145 assertEquals(1, (debugCount.get().getReportRetryCount())); 12146 12147 mDatastoreManager.runInTransaction( 12148 (dao) -> { 12149 dao.incrementAndGetReportingRetryCount( 12150 eventId, DataType.EVENT_REPORT_RETRY_COUNT); 12151 dao.incrementAndGetReportingRetryCount( 12152 aggregateId, DataType.AGGREGATE_REPORT_RETRY_COUNT); 12153 }); 12154 eventCount = 12155 mDatastoreManager.runInTransactionWithResult( 12156 (dao) -> dao.getKeyValueData(eventId, DataType.EVENT_REPORT_RETRY_COUNT)); 12157 aggregateCount = 12158 mDatastoreManager.runInTransactionWithResult( 12159 (dao) -> 12160 dao.getKeyValueData( 12161 aggregateId, DataType.AGGREGATE_REPORT_RETRY_COUNT)); 12162 debugEventCount = 12163 mDatastoreManager.runInTransactionWithResult( 12164 (dao) -> 12165 dao.getKeyValueData( 12166 eventId, DataType.DEBUG_EVENT_REPORT_RETRY_COUNT)); 12167 debugAggregateCount = 12168 mDatastoreManager.runInTransactionWithResult( 12169 (dao) -> 12170 dao.getKeyValueData( 12171 aggregateId, DataType.DEBUG_AGGREGATE_REPORT_RETRY_COUNT)); 12172 debugCount = 12173 mDatastoreManager.runInTransactionWithResult( 12174 (dao) -> dao.getKeyValueData(debugId, DataType.DEBUG_REPORT_RETRY_COUNT)); 12175 12176 assertTrue(eventCount.isPresent()); 12177 assertEquals(2, (eventCount.get().getReportRetryCount())); 12178 assertTrue(debugEventCount.isPresent()); 12179 assertEquals(1, (debugEventCount.get().getReportRetryCount())); 12180 assertTrue(aggregateCount.isPresent()); 12181 assertEquals(2, (aggregateCount.get().getReportRetryCount())); 12182 assertTrue(debugAggregateCount.isPresent()); 12183 assertEquals(1, (debugAggregateCount.get().getReportRetryCount())); 12184 assertTrue(debugCount.isPresent()); 12185 assertEquals(1, (debugCount.get().getReportRetryCount())); 12186 12187 mDatastoreManager.runInTransaction( 12188 (dao) -> { 12189 dao.incrementAndGetReportingRetryCount( 12190 eventId, DataType.DEBUG_EVENT_REPORT_RETRY_COUNT); 12191 dao.incrementAndGetReportingRetryCount( 12192 aggregateId, DataType.DEBUG_AGGREGATE_REPORT_RETRY_COUNT); 12193 dao.incrementAndGetReportingRetryCount( 12194 debugId, DataType.DEBUG_REPORT_RETRY_COUNT); 12195 }); 12196 eventCount = 12197 mDatastoreManager.runInTransactionWithResult( 12198 (dao) -> dao.getKeyValueData(eventId, DataType.EVENT_REPORT_RETRY_COUNT)); 12199 aggregateCount = 12200 mDatastoreManager.runInTransactionWithResult( 12201 (dao) -> 12202 dao.getKeyValueData( 12203 aggregateId, DataType.AGGREGATE_REPORT_RETRY_COUNT)); 12204 debugEventCount = 12205 mDatastoreManager.runInTransactionWithResult( 12206 (dao) -> 12207 dao.getKeyValueData( 12208 eventId, DataType.DEBUG_EVENT_REPORT_RETRY_COUNT)); 12209 debugAggregateCount = 12210 mDatastoreManager.runInTransactionWithResult( 12211 (dao) -> 12212 dao.getKeyValueData( 12213 aggregateId, DataType.DEBUG_AGGREGATE_REPORT_RETRY_COUNT)); 12214 debugCount = 12215 mDatastoreManager.runInTransactionWithResult( 12216 (dao) -> dao.getKeyValueData(debugId, DataType.DEBUG_REPORT_RETRY_COUNT)); 12217 12218 assertTrue(eventCount.isPresent()); 12219 assertEquals(2, (eventCount.get().getReportRetryCount())); 12220 assertTrue(debugEventCount.isPresent()); 12221 assertEquals(2, (debugEventCount.get().getReportRetryCount())); 12222 assertTrue(aggregateCount.isPresent()); 12223 assertEquals(2, (aggregateCount.get().getReportRetryCount())); 12224 assertTrue(debugAggregateCount.isPresent()); 12225 assertEquals(2, (debugAggregateCount.get().getReportRetryCount())); 12226 assertTrue(debugCount.isPresent()); 12227 assertEquals(2, (debugCount.get().getReportRetryCount())); 12228 } 12229 12230 @Test countNavigationSourcesPerReportingOriginQuery()12231 public void countNavigationSourcesPerReportingOriginQuery() { 12232 final String registrationId1 = "registrationId1"; 12233 final String registrationId2 = "registrationId2"; 12234 Source source1 = 12235 SourceFixture.getValidSourceBuilder() 12236 .setRegistrationId(registrationId1) 12237 .setSourceType(Source.SourceType.NAVIGATION) 12238 .setRegistrationOrigin(REGISTRATION_ORIGIN) 12239 .build(); 12240 Source source2 = 12241 SourceFixture.getValidSourceBuilder() 12242 .setRegistrationId(registrationId1) 12243 .setSourceType(Source.SourceType.EVENT) 12244 .setRegistrationOrigin(REGISTRATION_ORIGIN) 12245 .build(); 12246 Source source3 = 12247 SourceFixture.getValidSourceBuilder() 12248 .setRegistrationId(registrationId1) 12249 .setSourceType(Source.SourceType.NAVIGATION) 12250 .setRegistrationOrigin(REGISTRATION_ORIGIN) 12251 .build(); 12252 Source source4 = 12253 SourceFixture.getValidSourceBuilder() 12254 .setRegistrationId(registrationId2) 12255 .setSourceType(Source.SourceType.EVENT) 12256 .setRegistrationOrigin(REGISTRATION_ORIGIN_2) 12257 .build(); 12258 Source source5 = 12259 SourceFixture.getValidSourceBuilder() 12260 .setRegistrationId(registrationId2) 12261 .setSourceType(Source.SourceType.NAVIGATION) 12262 .setRegistrationOrigin(REGISTRATION_ORIGIN_2) 12263 .build(); 12264 Arrays.asList(source1, source2, source3, source4, source5).stream() 12265 .forEach(source -> insertSource(source)); 12266 assertThat( 12267 mDatastoreManager.runInTransactionWithResult( 12268 (dao) -> 12269 dao.countNavigationSourcesPerReportingOrigin( 12270 REGISTRATION_ORIGIN, registrationId1))) 12271 .isEqualTo(Optional.of(2L)); 12272 assertThat( 12273 mDatastoreManager.runInTransactionWithResult( 12274 (dao) -> 12275 dao.countNavigationSourcesPerReportingOrigin( 12276 REGISTRATION_ORIGIN_2, registrationId2))) 12277 .isEqualTo(Optional.of(1L)); 12278 assertThat( 12279 mDatastoreManager.runInTransactionWithResult( 12280 (dao) -> 12281 dao.countNavigationSourcesPerReportingOrigin( 12282 REGISTRATION_ORIGIN, registrationId2))) 12283 .isEqualTo(Optional.of(0L)); 12284 } 12285 verifySourceStatus(@onNull Source source, @Source.Status int status)12286 private void verifySourceStatus(@NonNull Source source, @Source.Status int status) { 12287 assertThat( 12288 mDatastoreManager 12289 .runInTransactionWithResult( 12290 measurementDao -> measurementDao.getSource(source.getId())) 12291 .get() 12292 .getStatus()) 12293 .isEqualTo(status); 12294 } 12295 12296 @Test 12297 public void testUpdateSourcesForAttributionScope_diffMaxViewStates_ignoresSourcesDeletesReports()12298 testUpdateSourcesForAttributionScope_diffMaxViewStates_ignoresSourcesDeletesReports() { 12299 mLegacyFlags = mMockFlags; 12300 mocker.mockGetFlags(mMockFlags); 12301 doReturn(true).when(mMockFlags).getMeasurementEnableAttributionScope(); 12302 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mMockFlags).getMeasurementDbSizeLimit(); 12303 12304 Source source1 = 12305 insertSourceForAttributionScope( 12306 List.of("1"), 12307 ATTRIBUTION_SCOPE_LIMIT, 12308 MAX_EVENT_STATES, 12309 SOURCE_EVENT_TIME, 12310 List.of(WEB_ONE_DESTINATION), 12311 null); 12312 EventReport pastFakeEventReport = 12313 new EventReport.Builder() 12314 .setId("1") 12315 .setSourceId(source1.getId()) 12316 .setSourceEventId(source1.getEventId()) 12317 .setReportTime(SOURCE_EVENT_TIME) 12318 .setAttributionDestinations(List.of(WEB_ONE_DESTINATION)) 12319 .setTriggerTime(SOURCE_EVENT_TIME) 12320 .setSourceType(source1.getSourceType()) 12321 .setStatus(EventReport.Status.PENDING) 12322 .setRegistrationOrigin(source1.getRegistrationOrigin()) 12323 .build(); 12324 EventReport fakeEventReport1 = 12325 new EventReport.Builder() 12326 .setId("2") 12327 .setSourceId(source1.getId()) 12328 .setSourceEventId(source1.getEventId()) 12329 .setReportTime(SOURCE_EVENT_TIME + 1000) 12330 .setAttributionDestinations(List.of(WEB_ONE_DESTINATION)) 12331 .setTriggerTime(SOURCE_EVENT_TIME + 1000) 12332 .setSourceType(source1.getSourceType()) 12333 .setStatus(EventReport.Status.PENDING) 12334 .setRegistrationOrigin(source1.getRegistrationOrigin()) 12335 .build(); 12336 // Deleted fake event report for comparison. 12337 EventReport deletedFakeEventReport1 = 12338 new EventReport.Builder() 12339 .setId("3") 12340 .setSourceId(source1.getId()) 12341 .setSourceEventId(source1.getEventId()) 12342 .setReportTime(SOURCE_EVENT_TIME + 1000) 12343 .setAttributionDestinations(List.of(WEB_ONE_DESTINATION)) 12344 .setTriggerTime(SOURCE_EVENT_TIME + 1000) 12345 .setSourceType(source1.getSourceType()) 12346 .setStatus(EventReport.Status.MARKED_TO_DELETE) 12347 .setRegistrationOrigin(source1.getRegistrationOrigin()) 12348 .build(); 12349 mDatastoreManager.runInTransaction( 12350 (dao) -> { 12351 dao.insertEventReport(pastFakeEventReport); 12352 dao.insertEventReport(fakeEventReport1); 12353 dao.updateSourcesForAttributionScope(source1); 12354 }); 12355 Arrays.asList(source1).stream() 12356 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12357 assertThat( 12358 mDatastoreManager 12359 .runInTransactionWithResult( 12360 measurementDao -> 12361 measurementDao.getSourceEventReports(source1)) 12362 .get()) 12363 .containsExactly(pastFakeEventReport, fakeEventReport1); 12364 12365 Source source2 = 12366 insertSourceForAttributionScope( 12367 List.of("2"), 12368 ATTRIBUTION_SCOPE_LIMIT, 12369 MAX_EVENT_STATES + 1, 12370 SOURCE_EVENT_TIME + 1, 12371 List.of(WEB_ONE_DESTINATION), 12372 null); 12373 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source2)); 12374 Arrays.asList(source1).stream() 12375 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12376 Arrays.asList(source2).stream() 12377 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12378 assertThat( 12379 mDatastoreManager 12380 .runInTransactionWithResult( 12381 measurementDao -> 12382 measurementDao.getSourceEventReports(source1)) 12383 .get()) 12384 .containsExactly(pastFakeEventReport, deletedFakeEventReport1); 12385 12386 Source source3 = 12387 insertSourceForAttributionScope( 12388 List.of("3"), 12389 ATTRIBUTION_SCOPE_LIMIT, 12390 MAX_EVENT_STATES, 12391 SOURCE_EVENT_TIME + 2, 12392 List.of(WEB_TWO_DESTINATION), 12393 null); 12394 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source3)); 12395 Arrays.asList(source1).stream() 12396 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12397 Arrays.asList(source2, source3).stream() 12398 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12399 12400 Source source4 = 12401 insertSourceForAttributionScope( 12402 List.of("5"), 12403 ATTRIBUTION_SCOPE_LIMIT, 12404 MAX_EVENT_STATES, 12405 SOURCE_EVENT_TIME + 3, 12406 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 12407 null); 12408 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source4)); 12409 Arrays.asList(source1, source2).stream() 12410 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12411 Arrays.asList(source3, source4).stream() 12412 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12413 12414 Source source5 = 12415 insertSourceForAttributionScope( 12416 List.of("4"), 12417 ATTRIBUTION_SCOPE_LIMIT, 12418 MAX_EVENT_STATES + 1, 12419 SOURCE_EVENT_TIME + 4, 12420 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 12421 null); 12422 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source5)); 12423 Arrays.asList(source1, source2, source3, source4).stream() 12424 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12425 Arrays.asList(source5).stream() 12426 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12427 12428 // Sources for different reporting origin with different max event states. 12429 Source source6 = 12430 insertSourceForAttributionScope( 12431 List.of("4"), 12432 ATTRIBUTION_SCOPE_LIMIT, 12433 MAX_EVENT_STATES, 12434 SOURCE_EVENT_TIME + 5, 12435 List.of(WEB_ONE_DESTINATION), 12436 null, 12437 REGISTRATION_ORIGIN_2); 12438 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source6)); 12439 Arrays.asList(source1, source2, source3, source4).stream() 12440 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12441 Arrays.asList(source5, source6).stream() 12442 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12443 12444 // Sources for different reporting origin with the same max event states. 12445 Source source7 = 12446 insertSourceForAttributionScope( 12447 List.of("4"), 12448 ATTRIBUTION_SCOPE_LIMIT + 1, 12449 MAX_EVENT_STATES, 12450 SOURCE_EVENT_TIME + 5, 12451 List.of(WEB_TWO_DESTINATION), 12452 null, 12453 REGISTRATION_ORIGIN_2); 12454 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source7)); 12455 Arrays.asList(source1, source2, source3, source4).stream() 12456 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12457 Arrays.asList(source5, source6, source7).stream() 12458 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12459 12460 Source source8 = 12461 insertSourceForAttributionScope( 12462 List.of("4"), 12463 ATTRIBUTION_SCOPE_LIMIT, 12464 MAX_EVENT_STATES + 1, 12465 SOURCE_EVENT_TIME + 5, 12466 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 12467 null); 12468 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source8)); 12469 Arrays.asList(source1, source2, source3, source4).stream() 12470 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12471 Arrays.asList(source5, source6, source7, source8).stream() 12472 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12473 } 12474 12475 @Test testUpdateSourcesForAttributionScope_smallerLimit_ignoresSourcesDeletesReports()12476 public void testUpdateSourcesForAttributionScope_smallerLimit_ignoresSourcesDeletesReports() { 12477 mLegacyFlags = mMockFlags; 12478 mocker.mockGetFlags(mMockFlags); 12479 doReturn(true).when(mMockFlags).getMeasurementEnableAttributionScope(); 12480 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mMockFlags).getMeasurementDbSizeLimit(); 12481 Source source1 = 12482 insertSourceForAttributionScope( 12483 List.of("1"), 12484 ATTRIBUTION_SCOPE_LIMIT, 12485 MAX_EVENT_STATES, 12486 SOURCE_EVENT_TIME, 12487 List.of(WEB_ONE_DESTINATION), 12488 null); 12489 EventReport pastFakeEventReport = 12490 new EventReport.Builder() 12491 .setId("1") 12492 .setSourceId(source1.getId()) 12493 .setSourceEventId(source1.getEventId()) 12494 .setReportTime(SOURCE_EVENT_TIME) 12495 .setAttributionDestinations(List.of(WEB_ONE_DESTINATION)) 12496 .setTriggerTime(SOURCE_EVENT_TIME) 12497 .setSourceType(source1.getSourceType()) 12498 .setStatus(EventReport.Status.PENDING) 12499 .setRegistrationOrigin(source1.getRegistrationOrigin()) 12500 .build(); 12501 EventReport fakeEventReport1 = 12502 new EventReport.Builder() 12503 .setId("2") 12504 .setSourceId(source1.getId()) 12505 .setSourceEventId(source1.getEventId()) 12506 .setReportTime(SOURCE_EVENT_TIME + 1000) 12507 .setAttributionDestinations(List.of(WEB_ONE_DESTINATION)) 12508 .setTriggerTime(SOURCE_EVENT_TIME + 1000) 12509 .setSourceType(source1.getSourceType()) 12510 .setStatus(EventReport.Status.PENDING) 12511 .setRegistrationOrigin(source1.getRegistrationOrigin()) 12512 .build(); 12513 // Delete fake event report for comparison. 12514 EventReport deletedFakeEventReport1 = 12515 new EventReport.Builder() 12516 .setId("3") 12517 .setSourceId(source1.getId()) 12518 .setSourceEventId(source1.getEventId()) 12519 .setReportTime(SOURCE_EVENT_TIME + 1000) 12520 .setAttributionDestinations(List.of(WEB_ONE_DESTINATION)) 12521 .setTriggerTime(SOURCE_EVENT_TIME + 1000) 12522 .setSourceType(source1.getSourceType()) 12523 .setStatus(EventReport.Status.MARKED_TO_DELETE) 12524 .setRegistrationOrigin(source1.getRegistrationOrigin()) 12525 .build(); 12526 mDatastoreManager.runInTransaction( 12527 (dao) -> { 12528 dao.insertEventReport(pastFakeEventReport); 12529 dao.insertEventReport(fakeEventReport1); 12530 dao.updateSourcesForAttributionScope(source1); 12531 }); 12532 Arrays.asList(source1).stream() 12533 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12534 assertThat( 12535 mDatastoreManager 12536 .runInTransactionWithResult( 12537 measurementDao -> 12538 measurementDao.getSourceEventReports(source1)) 12539 .get()) 12540 .containsExactly(pastFakeEventReport, fakeEventReport1); 12541 12542 Source source2 = 12543 insertSourceForAttributionScope( 12544 List.of("2"), 12545 /* attributionScopeLimit= */ 8L, 12546 MAX_EVENT_STATES, 12547 SOURCE_EVENT_TIME + 1, 12548 List.of(WEB_ONE_DESTINATION), 12549 null); 12550 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source2)); 12551 Arrays.asList(source1).stream() 12552 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12553 Arrays.asList(source2).stream() 12554 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12555 assertThat( 12556 mDatastoreManager 12557 .runInTransactionWithResult( 12558 measurementDao -> 12559 measurementDao.getSourceEventReports(source1)) 12560 .get()) 12561 .containsExactly(pastFakeEventReport, deletedFakeEventReport1); 12562 12563 Source source3 = 12564 insertSourceForAttributionScope( 12565 List.of("3"), 12566 /* attributionScopeLimit= */ 4L, 12567 MAX_EVENT_STATES, 12568 SOURCE_EVENT_TIME + 2, 12569 List.of(WEB_TWO_DESTINATION), 12570 null); 12571 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source3)); 12572 Arrays.asList(source1).stream() 12573 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12574 Arrays.asList(source2, source3).stream() 12575 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12576 12577 Source source4 = 12578 insertSourceForAttributionScope( 12579 List.of("3"), 12580 /* attributionScopeLimit= */ 7L, 12581 MAX_EVENT_STATES, 12582 SOURCE_EVENT_TIME + 3, 12583 List.of(WEB_TWO_DESTINATION), 12584 null); 12585 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source4)); 12586 Arrays.asList(source1, source3).stream() 12587 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12588 Arrays.asList(source2, source4).stream() 12589 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12590 12591 Source source5 = 12592 insertSourceForAttributionScope( 12593 List.of("3"), 12594 /* attributionScopeLimit= */ 4L, 12595 MAX_EVENT_STATES, 12596 SOURCE_EVENT_TIME + 2, 12597 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 12598 null); 12599 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source5)); 12600 Arrays.asList(source1, source3).stream() 12601 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12602 Arrays.asList(source2, source4, source5).stream() 12603 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12604 12605 Source source6 = 12606 insertSourceForAttributionScope( 12607 List.of("3"), 12608 /* attributionScopeLimit= */ 6L, 12609 MAX_EVENT_STATES, 12610 SOURCE_EVENT_TIME + 2, 12611 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 12612 null); 12613 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source6)); 12614 Arrays.asList(source1, source3, source5).stream() 12615 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12616 Arrays.asList(source2, source4, source6).stream() 12617 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12618 12619 // Sources for different reporting origin with different max event states. 12620 Source source7 = 12621 insertSourceForAttributionScope( 12622 List.of("4"), 12623 /* attributionScopeLimit= */ 6L, 12624 MAX_EVENT_STATES, 12625 SOURCE_EVENT_TIME + 3, 12626 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 12627 null, 12628 REGISTRATION_ORIGIN_2); 12629 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source7)); 12630 Arrays.asList(source1, source3, source5).stream() 12631 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12632 Arrays.asList(source2, source4, source6, source7).stream() 12633 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12634 12635 // Sources for different reporting origin with the same max event states. 12636 Source source8 = 12637 insertSourceForAttributionScope( 12638 List.of("4"), 12639 /* attributionScopeLimit= */ 4L, 12640 MAX_EVENT_STATES, 12641 SOURCE_EVENT_TIME + 3, 12642 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 12643 null, 12644 REGISTRATION_ORIGIN_2); 12645 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source8)); 12646 Arrays.asList(source1, source3, source5).stream() 12647 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12648 Arrays.asList(source2, source4, source6, source7, source8).stream() 12649 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12650 12651 Source source9 = 12652 insertSourceForAttributionScope( 12653 List.of("4"), 12654 /* attributionScopeLimit= */ 5L, 12655 MAX_EVENT_STATES, 12656 SOURCE_EVENT_TIME + 4, 12657 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION), 12658 null); 12659 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source9)); 12660 Arrays.stream(new Source[] {source1, source3, source5}) 12661 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12662 Arrays.stream(new Source[] {source2, source4, source6, source7, source8, source9}) 12663 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12664 } 12665 12666 @Test 12667 public void testUpdateSourcesForAttributionScope_scopedSource_ignoresNonScopedAndDeletesReports()12668 testUpdateSourcesForAttributionScope_scopedSource_ignoresNonScopedAndDeletesReports() { 12669 mLegacyFlags = mMockFlags; 12670 mocker.mockGetFlags(mMockFlags); 12671 doReturn(true).when(mMockFlags).getMeasurementEnableAttributionScope(); 12672 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mMockFlags).getMeasurementDbSizeLimit(); 12673 12674 Source source1 = 12675 insertSourceForAttributionScope( 12676 null, null, null, SOURCE_EVENT_TIME, List.of(WEB_ONE_DESTINATION), null); 12677 EventReport pastFakeEventReport = 12678 new EventReport.Builder() 12679 .setId("1") 12680 .setSourceId(source1.getId()) 12681 .setSourceEventId(source1.getEventId()) 12682 .setReportTime(SOURCE_EVENT_TIME) 12683 .setAttributionDestinations(List.of(WEB_ONE_DESTINATION)) 12684 .setTriggerTime(SOURCE_EVENT_TIME) 12685 .setSourceType(source1.getSourceType()) 12686 .setStatus(EventReport.Status.PENDING) 12687 .setRegistrationOrigin(source1.getRegistrationOrigin()) 12688 .build(); 12689 EventReport fakeEventReport1 = 12690 new EventReport.Builder() 12691 .setId("2") 12692 .setSourceId(source1.getId()) 12693 .setSourceEventId(source1.getEventId()) 12694 .setReportTime(SOURCE_EVENT_TIME + 1000) 12695 .setAttributionDestinations(List.of(WEB_ONE_DESTINATION)) 12696 .setTriggerTime(SOURCE_EVENT_TIME + 1000) 12697 .setSourceType(source1.getSourceType()) 12698 .setStatus(EventReport.Status.PENDING) 12699 .setRegistrationOrigin(source1.getRegistrationOrigin()) 12700 .build(); 12701 // Deleted fake event report for comparison. 12702 EventReport deletedFakeEventReport1 = 12703 new EventReport.Builder() 12704 .setId("3") 12705 .setSourceId(source1.getId()) 12706 .setSourceEventId(source1.getEventId()) 12707 .setReportTime(SOURCE_EVENT_TIME + 1000) 12708 .setAttributionDestinations(List.of(WEB_ONE_DESTINATION)) 12709 .setTriggerTime(SOURCE_EVENT_TIME + 1000) 12710 .setSourceType(source1.getSourceType()) 12711 .setStatus(EventReport.Status.MARKED_TO_DELETE) 12712 .setRegistrationOrigin(source1.getRegistrationOrigin()) 12713 .build(); 12714 mDatastoreManager.runInTransaction( 12715 (dao) -> { 12716 dao.insertEventReport(pastFakeEventReport); 12717 dao.insertEventReport(fakeEventReport1); 12718 dao.updateSourcesForAttributionScope(source1); 12719 }); 12720 Arrays.asList(source1).stream() 12721 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12722 assertThat( 12723 mDatastoreManager 12724 .runInTransactionWithResult( 12725 measurementDao -> 12726 measurementDao.getSourceEventReports(source1)) 12727 .get()) 12728 .containsExactly(pastFakeEventReport, fakeEventReport1); 12729 12730 Source source2 = 12731 insertSourceForAttributionScope( 12732 List.of("2"), 12733 ATTRIBUTION_SCOPE_LIMIT, 12734 MAX_EVENT_STATES, 12735 SOURCE_EVENT_TIME + 1, 12736 List.of(WEB_ONE_DESTINATION), 12737 null); 12738 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source2)); 12739 Arrays.asList(source1).stream() 12740 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12741 Arrays.asList(source2).stream() 12742 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12743 assertThat( 12744 mDatastoreManager 12745 .runInTransactionWithResult( 12746 measurementDao -> 12747 measurementDao.getSourceEventReports(source1)) 12748 .get()) 12749 .containsExactly(pastFakeEventReport, deletedFakeEventReport1); 12750 12751 Source source3 = 12752 insertSourceForAttributionScope( 12753 List.of("3"), 12754 ATTRIBUTION_SCOPE_LIMIT, 12755 MAX_EVENT_STATES, 12756 SOURCE_EVENT_TIME + 2, 12757 List.of(WEB_TWO_DESTINATION), 12758 null); 12759 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source3)); 12760 Arrays.asList(source1).stream() 12761 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12762 Arrays.asList(source2, source3).stream() 12763 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12764 } 12765 12766 @Test testUpdateSourcesForAttributionScope_newNonScopedSource_removesScopes()12767 public void testUpdateSourcesForAttributionScope_newNonScopedSource_removesScopes() { 12768 mLegacyFlags = mMockFlags; 12769 mocker.mockGetFlags(mMockFlags); 12770 doReturn(true).when(mMockFlags).getMeasurementEnableAttributionScope(); 12771 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mMockFlags).getMeasurementDbSizeLimit(); 12772 Consumer<? super Source> verifyAttributionScopeEmptyFn = 12773 source -> { 12774 assertThat( 12775 mDatastoreManager 12776 .runInTransactionWithResult( 12777 measurementDao -> 12778 measurementDao 12779 .getSourceAttributionScopes( 12780 source.getId())) 12781 .get()) 12782 .isEmpty(); 12783 Source savedSource = 12784 mDatastoreManager 12785 .runInTransactionWithResult( 12786 measurementDao -> 12787 measurementDao.getSource(source.getId())) 12788 .get(); 12789 assertThat(savedSource.getAttributionScopeLimit()).isNull(); 12790 assertThat(savedSource.getMaxEventStates()).isNull(); 12791 }; 12792 12793 Consumer<? super Source> verifyAttributionScopeUnchangedFn = 12794 source -> { 12795 assertThat( 12796 mDatastoreManager 12797 .runInTransactionWithResult( 12798 measurementDao -> 12799 measurementDao 12800 .getSourceAttributionScopes( 12801 source.getId())) 12802 .get()) 12803 .containsExactlyElementsIn(source.getAttributionScopes()); 12804 Source savedSource = 12805 mDatastoreManager 12806 .runInTransactionWithResult( 12807 measurementDao -> 12808 measurementDao.getSource(source.getId())) 12809 .get(); 12810 assertThat(savedSource.getAttributionScopeLimit()) 12811 .isEqualTo(savedSource.getAttributionScopeLimit()); 12812 assertThat(savedSource.getMaxEventStates()) 12813 .isEqualTo(savedSource.getMaxEventStates()); 12814 }; 12815 12816 Source source1 = 12817 insertSourceForAttributionScope( 12818 List.of("1"), 12819 ATTRIBUTION_SCOPE_LIMIT, 12820 MAX_EVENT_STATES, 12821 SOURCE_EVENT_TIME, 12822 List.of(WEB_ONE_DESTINATION), 12823 null); 12824 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source1)); 12825 Arrays.asList(source1).stream() 12826 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12827 Arrays.asList(source1).stream().forEach(verifyAttributionScopeUnchangedFn); 12828 12829 Source source2 = 12830 insertSourceForAttributionScope( 12831 List.of("2"), 12832 ATTRIBUTION_SCOPE_LIMIT, 12833 MAX_EVENT_STATES, 12834 SOURCE_EVENT_TIME + 1, 12835 List.of(WEB_ONE_DESTINATION), 12836 null); 12837 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source2)); 12838 Arrays.asList(source1, source2).stream() 12839 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12840 Arrays.asList(source1, source2).stream().forEach(verifyAttributionScopeUnchangedFn); 12841 12842 Source source3 = 12843 insertSourceForAttributionScope( 12844 null, 12845 null, 12846 null, 12847 SOURCE_EVENT_TIME + 2, 12848 List.of(WEB_TWO_DESTINATION), 12849 null); 12850 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source3)); 12851 Arrays.asList(source1, source2, source3).stream() 12852 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12853 // Source3 is for a different destination, attribution scopes should not be cleared. 12854 Arrays.asList(source1, source2).stream().forEach(verifyAttributionScopeUnchangedFn); 12855 12856 Source source4 = 12857 insertSourceForAttributionScope( 12858 null, 12859 null, 12860 null, 12861 SOURCE_EVENT_TIME + 3, 12862 List.of(WEB_ONE_DESTINATION), 12863 null); 12864 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source4)); 12865 Arrays.asList(source1, source2, source3, source4).stream() 12866 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12867 // Source4 is the same destination, clear attribution scopes for older sources. 12868 Arrays.asList(source1, source2).stream().forEach(verifyAttributionScopeEmptyFn); 12869 } 12870 12871 @Test testUpdateSourcesForAttributionScope_scopesNotSelected_ignoreSources()12872 public void testUpdateSourcesForAttributionScope_scopesNotSelected_ignoreSources() { 12873 mLegacyFlags = mMockFlags; 12874 mocker.mockGetFlags(mMockFlags); 12875 doReturn(true).when(mMockFlags).getMeasurementEnableAttributionScope(); 12876 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mMockFlags).getMeasurementDbSizeLimit(); 12877 // Below are the sources registered with attribution scopes and destinations. 12878 // For each destination, two sources are registered, and only one's scopes are to be 12879 // deleted. 12880 // For registration R1: 12881 // S1: attribution scopes -> [0, ""], destinations -> [D1] 12882 // S2: attribution scopes -> [3, 4, 5], destinations -> [D1] 12883 // S3: attribution scopes -> [0, 1], destinations -> [D2] 12884 // S4: attribution scopes -> [1, 3], destinations -> [D2] 12885 // S5: attribution scopes -> [1, 2], destinations -> [D3] 12886 // S6: attribution scopes -> [2, 3, 4], destinations -> [D3] 12887 // S7: attribution scopes -> [2], destinations -> [D4] 12888 // S8: attribution scopes -> [1, 2], destinations -> [D4], shares same timestamp as S7. 12889 // S12: attribution scopes -> [3, 4], destinations -> [D1, D2, D3, D4] 12890 // For registration R2 to test interplay cross reporting origin: 12891 // If the reporting origin were R1, the attribution scopes for S9, S10, and S11 would 12892 // have been removed. 12893 // S9: attribution scopes -> [0], destinations -> [D1] 12894 // S10: attribution scopes -> [0, 1], destinations -> [D2] 12895 // S11: attribution scopes -> [1, 2], destinations -> [D3] 12896 // The selected attribution scopes for each destination are: 12897 // D1: [3, 4, 5] => Scope for S1 to be removed. 12898 // D2: [1, 3, 4] => Scope for S3 to be removed. 12899 // D3: [2, 3, 4] => Scope for S5 to be removed. 12900 // D4: [2, 3, 4] => Scope for S8 to be removed. S7 and S8 share the same timestamp; the 12901 // attribution scope with the higher value will be selected. 12902 12903 // S1: attribution scopes -> [0], destinations -> [D1] 12904 Source source1 = 12905 insertSourceForAttributionScope( 12906 List.of("0", ""), 12907 ATTRIBUTION_SCOPE_LIMIT, 12908 MAX_EVENT_STATES, 12909 SOURCE_EVENT_TIME, 12910 null, 12911 List.of(APP_ONE_DESTINATION)); 12912 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source1)); 12913 Arrays.stream(new Source[] {source1}) 12914 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12915 12916 // S2: attribution scopes -> [3, 4, 5], destinations -> [D1] 12917 Source source2 = 12918 insertSourceForAttributionScope( 12919 List.of("3", "4", "5"), 12920 ATTRIBUTION_SCOPE_LIMIT, 12921 MAX_EVENT_STATES, 12922 SOURCE_EVENT_TIME + 1, 12923 null, 12924 List.of(APP_ONE_DESTINATION)); 12925 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source2)); 12926 Arrays.stream(new Source[] {source1}) 12927 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12928 Arrays.stream(new Source[] {source2}) 12929 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12930 12931 // S3: attribution scopes -> [0, 1], destinations -> [D2] 12932 Source source3 = 12933 insertSourceForAttributionScope( 12934 List.of("0", "1"), 12935 ATTRIBUTION_SCOPE_LIMIT, 12936 MAX_EVENT_STATES, 12937 SOURCE_EVENT_TIME + 2, 12938 List.of(WEB_ONE_DESTINATION), 12939 null); 12940 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source3)); 12941 Arrays.stream(new Source[] {source1}) 12942 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12943 Arrays.stream(new Source[] {source2, source3}) 12944 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12945 12946 // S4: attribution scopes -> [1, 3], destinations -> [D2] 12947 Source source4 = 12948 insertSourceForAttributionScope( 12949 List.of("1", "3"), 12950 ATTRIBUTION_SCOPE_LIMIT, 12951 MAX_EVENT_STATES, 12952 SOURCE_EVENT_TIME + 3, 12953 List.of(WEB_ONE_DESTINATION), 12954 null); 12955 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source4)); 12956 Arrays.stream(new Source[] {source1}) 12957 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12958 Arrays.stream(new Source[] {source2, source3, source4}) 12959 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12960 12961 // S5: attribution scopes -> [1, 2], destinations -> [D3] 12962 Source source5 = 12963 insertSourceForAttributionScope( 12964 List.of("1", "2"), 12965 ATTRIBUTION_SCOPE_LIMIT, 12966 MAX_EVENT_STATES, 12967 SOURCE_EVENT_TIME + 4, 12968 List.of(WEB_TWO_DESTINATION), 12969 null); 12970 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source5)); 12971 Arrays.stream(new Source[] {source1}) 12972 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12973 Arrays.stream(new Source[] {source2, source3, source4, source5}) 12974 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12975 12976 // S6: attribution scopes -> [2, 3, 4], destinations -> [D3] 12977 Source source6 = 12978 insertSourceForAttributionScope( 12979 List.of("2", "3", "4"), 12980 ATTRIBUTION_SCOPE_LIMIT, 12981 MAX_EVENT_STATES, 12982 SOURCE_EVENT_TIME + 5, 12983 List.of(WEB_TWO_DESTINATION), 12984 null); 12985 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source6)); 12986 Arrays.stream(new Source[] {source1, source5}) 12987 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 12988 Arrays.stream(new Source[] {source2, source3, source4, source6}) 12989 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 12990 12991 // S7: attribution scopes -> [2], destinations -> [D4] 12992 Source source7 = 12993 insertSourceForAttributionScope( 12994 List.of("2"), 12995 ATTRIBUTION_SCOPE_LIMIT, 12996 MAX_EVENT_STATES, 12997 SOURCE_EVENT_TIME + 6, 12998 List.of(WEB_THREE_DESTINATION), 12999 null); 13000 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source7)); 13001 Arrays.stream(new Source[] {source1, source5}) 13002 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 13003 Arrays.stream(new Source[] {source2, source3, source4, source6, source7}) 13004 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 13005 13006 // S8: attribution scopes -> [1, 2], destinations -> [D4], shares same timestamp as S7. 13007 Source source8 = 13008 insertSourceForAttributionScope( 13009 List.of("1", "2"), 13010 ATTRIBUTION_SCOPE_LIMIT, 13011 MAX_EVENT_STATES, 13012 SOURCE_EVENT_TIME + 6, 13013 List.of(WEB_THREE_DESTINATION), 13014 null); 13015 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source8)); 13016 Arrays.stream(new Source[] {source1, source5}) 13017 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 13018 Arrays.stream(new Source[] {source2, source3, source4, source6, source7, source8}) 13019 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 13020 13021 // S9: attribution scopes -> [0], destinations -> [D1], reporting origin -> R2 13022 Source source9 = 13023 insertSourceForAttributionScope( 13024 List.of("0"), 13025 ATTRIBUTION_SCOPE_LIMIT, 13026 MAX_EVENT_STATES, 13027 SOURCE_EVENT_TIME, 13028 null, 13029 List.of(APP_ONE_DESTINATION), 13030 REGISTRATION_ORIGIN_2); 13031 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source9)); 13032 Arrays.stream(new Source[] {source1, source5}) 13033 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 13034 Arrays.stream(new Source[] {source2, source3, source4, source6, source7, source8, source9}) 13035 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 13036 13037 // S10: attribution scopes -> [0, 1], destinations -> [D2], reporting origin -> R2 13038 Source source10 = 13039 insertSourceForAttributionScope( 13040 List.of("0", "1"), 13041 ATTRIBUTION_SCOPE_LIMIT, 13042 MAX_EVENT_STATES, 13043 SOURCE_EVENT_TIME + 2, 13044 List.of(WEB_ONE_DESTINATION), 13045 null, 13046 REGISTRATION_ORIGIN_2); 13047 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source10)); 13048 Arrays.stream(new Source[] {source1, source5}) 13049 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 13050 Arrays.stream( 13051 new Source[] { 13052 source2, source3, source4, source6, source7, source8, source9, source10 13053 }) 13054 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 13055 13056 // S11: attribution scopes -> [1, 2], destinations -> [D3], reporting origin -> R2 13057 Source source11 = 13058 insertSourceForAttributionScope( 13059 List.of("1", "2"), 13060 ATTRIBUTION_SCOPE_LIMIT, 13061 MAX_EVENT_STATES, 13062 SOURCE_EVENT_TIME + 4, 13063 List.of(WEB_TWO_DESTINATION), 13064 null, 13065 REGISTRATION_ORIGIN_2); 13066 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source11)); 13067 Arrays.stream(new Source[] {source1, source5}) 13068 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 13069 Arrays.stream( 13070 new Source[] { 13071 source2, source3, source4, source6, source7, source8, source9, source10, 13072 source11 13073 }) 13074 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 13075 13076 // S12: attribution scopes -> [3, 4], destinations -> [D1, D2, D3, D4] 13077 Source source12 = 13078 insertSourceForAttributionScope( 13079 List.of("3", "4"), 13080 ATTRIBUTION_SCOPE_LIMIT, 13081 MAX_EVENT_STATES, 13082 SOURCE_EVENT_TIME + 8, 13083 List.of(WEB_ONE_DESTINATION, WEB_TWO_DESTINATION, WEB_THREE_DESTINATION), 13084 List.of(APP_ONE_DESTINATION)); 13085 mDatastoreManager.runInTransaction((dao) -> dao.updateSourcesForAttributionScope(source12)); 13086 Arrays.stream(new Source[] {source1, source3, source5, source8}) 13087 .forEach(source -> verifySourceStatus(source, Source.Status.IGNORED)); 13088 Arrays.stream( 13089 new Source[] { 13090 source2, source4, source6, source7, source9, source10, source11, 13091 source12 13092 }) 13093 .forEach(source -> verifySourceStatus(source, Source.Status.ACTIVE)); 13094 } 13095 13096 @Test getLatestReportTimeInBatchWindow_singleAggregateReport_returnsSingleReportTime()13097 public void getLatestReportTimeInBatchWindow_singleAggregateReport_returnsSingleReportTime() { 13098 Source source = 13099 SourceFixture.getMinimalValidSourceBuilder() 13100 .setEventId(new UnsignedLong(1L)) 13101 .setId("source1") 13102 .build(); 13103 13104 long scheduledReportTime = 1L; 13105 AggregateReport report = 13106 generateMockAggregateReport( 13107 WebUtil.validUrl("https://destination-1.test"), 13108 1, 13109 "source1", 13110 scheduledReportTime); 13111 13112 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 13113 Objects.requireNonNull(db); 13114 insertSource(source, source.getId()); 13115 getAggregateReportConsumer(db).accept(report); 13116 13117 Long result = 13118 mDatastoreManager 13119 .runInTransactionWithResult( 13120 measurementDao -> 13121 measurementDao.getLatestReportTimeInBatchWindow( 13122 mLegacyFlags 13123 .getMeasurementReportingJobServiceBatchWindowMillis())) 13124 .orElseThrow(); 13125 13126 assertEquals(scheduledReportTime, result.longValue()); 13127 } 13128 13129 @Test testGetLatestReportTimeInBatchWindow_singleEventReport_returnsSingleReportTime()13130 public void testGetLatestReportTimeInBatchWindow_singleEventReport_returnsSingleReportTime() { 13131 long reportTime = 1L; 13132 EventReport report = 13133 generateMockEventReport( 13134 WebUtil.validUrl("https://destination-1.test"), 1, reportTime); 13135 13136 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 13137 Objects.requireNonNull(db); 13138 getEventReportConsumer(db).accept(report); 13139 13140 Long result = 13141 mDatastoreManager 13142 .runInTransactionWithResult( 13143 measurementDao -> 13144 measurementDao.getLatestReportTimeInBatchWindow( 13145 mLegacyFlags 13146 .getMeasurementReportingJobServiceBatchWindowMillis())) 13147 .orElseThrow(); 13148 13149 assertEquals(reportTime, result.longValue()); 13150 } 13151 13152 @Test 13153 public void testGetLatestReportTimeInBatchWindow_twoAggregateReport_bothInBatchWindow_returnsTwoReportTimes()13154 testGetLatestReportTimeInBatchWindow_twoAggregateReport_bothInBatchWindow_returnsTwoReportTimes() { 13155 String sourceId = "source1"; 13156 Source source = 13157 SourceFixture.getMinimalValidSourceBuilder() 13158 .setEventId(new UnsignedLong(1L)) 13159 .setId(sourceId) 13160 .build(); 13161 13162 long firstScheduledReportTime = 1L; 13163 long secondScheduledReportTime = 13164 firstScheduledReportTime 13165 + MEASUREMENT_REPORTING_JOB_SERVICE_BATCH_WINDOW_MILLIS 13166 - 1L; 13167 13168 String destination = WebUtil.validUrl("https://destination-1.test"); 13169 List<AggregateReport> reports = 13170 Arrays.asList( 13171 generateMockAggregateReport( 13172 destination, 1, sourceId, firstScheduledReportTime), 13173 generateMockAggregateReport( 13174 destination, 2, sourceId, secondScheduledReportTime)); 13175 13176 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 13177 Objects.requireNonNull(db); 13178 insertSource(source, source.getId()); 13179 reports.forEach(getAggregateReportConsumer(db)); 13180 13181 Long result = 13182 mDatastoreManager 13183 .runInTransactionWithResult( 13184 measurementDao -> 13185 measurementDao.getLatestReportTimeInBatchWindow( 13186 mLegacyFlags 13187 .getMeasurementReportingJobServiceBatchWindowMillis())) 13188 .orElseThrow(); 13189 13190 assertEquals(secondScheduledReportTime, result.longValue()); 13191 } 13192 13193 @Test 13194 public void testGetLatestReportTimeInBatchWindow_twoAggReport_oneAfterBatchWindow_returnOneReportTime()13195 testGetLatestReportTimeInBatchWindow_twoAggReport_oneAfterBatchWindow_returnOneReportTime() { 13196 String sourceId = "source1"; 13197 Source source = 13198 SourceFixture.getMinimalValidSourceBuilder() 13199 .setEventId(new UnsignedLong(1L)) 13200 .setId(sourceId) 13201 .build(); 13202 13203 long firstScheduledReportTime = 1L; 13204 long secondScheduledReportTime = 13205 firstScheduledReportTime 13206 + MEASUREMENT_REPORTING_JOB_SERVICE_BATCH_WINDOW_MILLIS 13207 + 1L; 13208 13209 String destination = WebUtil.validUrl("https://destination-1.test"); 13210 List<AggregateReport> reports = 13211 Arrays.asList( 13212 generateMockAggregateReport( 13213 destination, 1, sourceId, firstScheduledReportTime), 13214 generateMockAggregateReport( 13215 destination, 2, sourceId, secondScheduledReportTime)); 13216 13217 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 13218 Objects.requireNonNull(db); 13219 insertSource(source, source.getId()); 13220 reports.forEach(getAggregateReportConsumer(db)); 13221 13222 Long result = 13223 mDatastoreManager 13224 .runInTransactionWithResult( 13225 measurementDao -> 13226 measurementDao.getLatestReportTimeInBatchWindow( 13227 mLegacyFlags 13228 .getMeasurementReportingJobServiceBatchWindowMillis())) 13229 .orElseThrow(); 13230 13231 // secondScheduledReportTime should not be returned as it was outside the batch window. 13232 assertEquals(firstScheduledReportTime, result.longValue()); 13233 } 13234 13235 @Test 13236 public void testGetLatestReportTimeInBatchWindow_twoEventReport_bothInBatchWindow_returnSecondReportTime()13237 testGetLatestReportTimeInBatchWindow_twoEventReport_bothInBatchWindow_returnSecondReportTime() { 13238 long firstScheduledReportTime = 1L; 13239 long secondScheduledReportTime = 13240 firstScheduledReportTime 13241 + MEASUREMENT_REPORTING_JOB_SERVICE_BATCH_WINDOW_MILLIS 13242 - 1L; 13243 13244 String destination = WebUtil.validUrl("https://destination-1.test"); 13245 List<EventReport> reports = 13246 Arrays.asList( 13247 generateMockEventReport(destination, 1, firstScheduledReportTime), 13248 generateMockEventReport(destination, 2, secondScheduledReportTime)); 13249 13250 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 13251 Objects.requireNonNull(db); 13252 13253 Consumer<EventReport> eventReportConsumer = getEventReportConsumer(db); 13254 reports.forEach(eventReportConsumer); 13255 13256 Long result = 13257 mDatastoreManager 13258 .runInTransactionWithResult( 13259 measurementDao -> 13260 measurementDao.getLatestReportTimeInBatchWindow( 13261 mLegacyFlags 13262 .getMeasurementReportingJobServiceBatchWindowMillis())) 13263 .orElseThrow(); 13264 13265 assertEquals(secondScheduledReportTime, result.longValue()); 13266 } 13267 13268 @Test 13269 public void testGetLatestReportTimeInBatchWindow_twoEventReport_oneAfterBatchWindow_returnFirstReportTime()13270 testGetLatestReportTimeInBatchWindow_twoEventReport_oneAfterBatchWindow_returnFirstReportTime() { 13271 long firstScheduledReportTime = 1L; 13272 long secondScheduledReportTime = 13273 firstScheduledReportTime 13274 + MEASUREMENT_REPORTING_JOB_SERVICE_BATCH_WINDOW_MILLIS 13275 + 1L; 13276 13277 String destination = WebUtil.validUrl("https://destination-1.test"); 13278 List<EventReport> reports = 13279 Arrays.asList( 13280 generateMockEventReport(destination, 1, firstScheduledReportTime), 13281 generateMockEventReport(destination, 2, secondScheduledReportTime)); 13282 13283 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 13284 Objects.requireNonNull(db); 13285 13286 Consumer<EventReport> eventReportConsumer = getEventReportConsumer(db); 13287 reports.forEach(eventReportConsumer); 13288 13289 Long result = 13290 mDatastoreManager 13291 .runInTransactionWithResult( 13292 measurementDao -> 13293 measurementDao.getLatestReportTimeInBatchWindow( 13294 mLegacyFlags 13295 .getMeasurementReportingJobServiceBatchWindowMillis())) 13296 .orElseThrow(); 13297 13298 // secondScheduledReportTime should not be returned as it was outside the batch window. 13299 assertEquals(firstScheduledReportTime, result.longValue()); 13300 } 13301 13302 @Test 13303 public void testGetLatestReportTimeInBatchWindow_oneAggReport_oneEventReport_bothInBatchWindow_returnSecondReportTime()13304 testGetLatestReportTimeInBatchWindow_oneAggReport_oneEventReport_bothInBatchWindow_returnSecondReportTime() { 13305 long firstScheduledReportTime = 1L; 13306 long secondScheduledReportTime = 13307 firstScheduledReportTime 13308 + MEASUREMENT_REPORTING_JOB_SERVICE_BATCH_WINDOW_MILLIS 13309 - 1L; 13310 13311 Source source = 13312 SourceFixture.getMinimalValidSourceBuilder() 13313 .setEventId(new UnsignedLong(1L)) 13314 .setId("source1") 13315 .build(); 13316 13317 String destination = WebUtil.validUrl("https://destination-1.test"); 13318 AggregateReport aggregateReport = 13319 generateMockAggregateReport(destination, 1, "source1", firstScheduledReportTime); 13320 EventReport eventReport = 13321 generateMockEventReport(destination, 1, secondScheduledReportTime); 13322 13323 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 13324 Objects.requireNonNull(db); 13325 insertSource(source, source.getId()); 13326 getEventReportConsumer(db).accept(eventReport); 13327 getAggregateReportConsumer(db).accept(aggregateReport); 13328 13329 Long result = 13330 mDatastoreManager 13331 .runInTransactionWithResult( 13332 measurementDao -> 13333 measurementDao.getLatestReportTimeInBatchWindow( 13334 mLegacyFlags 13335 .getMeasurementReportingJobServiceBatchWindowMillis())) 13336 .orElseThrow(); 13337 13338 assertEquals(secondScheduledReportTime, result.longValue()); 13339 } 13340 13341 @Test 13342 public void testGetLatestReportTimeInBatchWindow_oneAggReport_oneEventReport_oneAfterBatchWindow_returnFirstReportTime()13343 testGetLatestReportTimeInBatchWindow_oneAggReport_oneEventReport_oneAfterBatchWindow_returnFirstReportTime() { 13344 long firstScheduledReportTime = 1L; 13345 long secondScheduledReportTime = 13346 firstScheduledReportTime 13347 + MEASUREMENT_REPORTING_JOB_SERVICE_BATCH_WINDOW_MILLIS 13348 + 1L; 13349 13350 String sourceId = "source1"; 13351 Source source = 13352 SourceFixture.getMinimalValidSourceBuilder() 13353 .setEventId(new UnsignedLong(1L)) 13354 .setId(sourceId) 13355 .build(); 13356 13357 String destination = WebUtil.validUrl("https://destination-1.test"); 13358 AggregateReport aggregateReport = 13359 generateMockAggregateReport(destination, 1, sourceId, firstScheduledReportTime); 13360 EventReport eventReport = 13361 generateMockEventReport(destination, 1, secondScheduledReportTime); 13362 13363 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 13364 Objects.requireNonNull(db); 13365 insertSource(source, source.getId()); 13366 getEventReportConsumer(db).accept(eventReport); 13367 getAggregateReportConsumer(db).accept(aggregateReport); 13368 13369 Long result = 13370 mDatastoreManager 13371 .runInTransactionWithResult( 13372 measurementDao -> 13373 measurementDao.getLatestReportTimeInBatchWindow( 13374 mLegacyFlags 13375 .getMeasurementReportingJobServiceBatchWindowMillis())) 13376 .orElseThrow(); 13377 13378 assertEquals(firstScheduledReportTime, result.longValue()); 13379 } 13380 13381 @Test 13382 public void testGetLatestReportTimeInBatchWindow_manyAggReport_manyEventReport_returnLatestReportTime()13383 testGetLatestReportTimeInBatchWindow_manyAggReport_manyEventReport_returnLatestReportTime() { 13384 String sourceId = "source1"; 13385 Source source = 13386 SourceFixture.getMinimalValidSourceBuilder() 13387 .setEventId(new UnsignedLong(1L)) 13388 .setId(sourceId) 13389 .build(); 13390 List<EventReport> eventReports = new ArrayList<>(); 13391 List<AggregateReport> aggregateReports = new ArrayList<>(); 13392 int firstReportTime = 1; 13393 String destination = WebUtil.validUrl("https://destination-1.test"); 13394 for (int i = firstReportTime; i < MEASUREMENT_MAX_EVENT_REPORTS_PER_DESTINATION; i++) { 13395 eventReports.add( 13396 generateMockEventReport(destination, /* id= */ i, /* reportTime= */ i)); 13397 } 13398 13399 for (int i = firstReportTime; i < MEASUREMENT_MAX_AGGREGATE_REPORTS_PER_SOURCE; i++) { 13400 aggregateReports.add( 13401 generateMockAggregateReport( 13402 destination, /* id= */ i, sourceId, /* reportTime= */ i)); 13403 } 13404 13405 // Add one more aggregate report that is scheduled at the very edge of the batch window. 13406 // The report time for this report should be returned as the report time. 13407 long lastReportTime = MEASUREMENT_REPORTING_JOB_SERVICE_BATCH_WINDOW_MILLIS; 13408 int lastId = MEASUREMENT_MAX_AGGREGATE_REPORTS_PER_SOURCE; 13409 aggregateReports.add( 13410 generateMockAggregateReport(destination, lastId, sourceId, lastReportTime)); 13411 13412 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 13413 Objects.requireNonNull(db); 13414 insertSource(source, source.getId()); 13415 eventReports.forEach(getEventReportConsumer(db)); 13416 aggregateReports.forEach(getAggregateReportConsumer(db)); 13417 13418 Long result = 13419 mDatastoreManager 13420 .runInTransactionWithResult( 13421 measurementDao -> 13422 measurementDao.getLatestReportTimeInBatchWindow( 13423 mLegacyFlags 13424 .getMeasurementReportingJobServiceBatchWindowMillis())) 13425 .orElseThrow(); 13426 13427 assertEquals(lastReportTime, result.longValue()); 13428 } 13429 13430 @Test testGetLatestReportTimeInBatchWindow_noReports_returnNull()13431 public void testGetLatestReportTimeInBatchWindow_noReports_returnNull() { 13432 Optional<Long> results = 13433 mDatastoreManager.runInTransactionWithResult( 13434 measurementDao -> 13435 measurementDao.getLatestReportTimeInBatchWindow( 13436 mLegacyFlags 13437 .getMeasurementReportingJobServiceBatchWindowMillis())); 13438 13439 assertTrue(results.isEmpty()); 13440 } 13441 13442 @Test testInsertSource_withDestinationLimitPriorityEnabled_fetchesTheSetValue()13443 public void testInsertSource_withDestinationLimitPriorityEnabled_fetchesTheSetValue() { 13444 // Setup 13445 mocker.mockGetFlags(mMockFlags); 13446 doReturn(true).when(mMockFlags).getMeasurementEnableSourceDestinationLimitPriority(); 13447 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mMockFlags).getMeasurementDbSizeLimit(); 13448 13449 // Execution 13450 Source validSource = 13451 SourceFixture.getValidSourceBuilder() 13452 .setDestinationLimitPriority( 13453 SourceFixture.ValidSourceParams.DESTINATION_LIMIT_PRIORITY) 13454 .build(); 13455 mDatastoreManager.runInTransaction((dao) -> dao.insertSource(validSource)); 13456 13457 // Assertion 13458 String sourceId = getFirstSourceIdFromDatastore(); 13459 Source source = 13460 mDatastoreManager 13461 .runInTransactionWithResult( 13462 measurementDao -> measurementDao.getSource(sourceId)) 13463 .orElseThrow(() -> new IllegalStateException("Source is null")); 13464 assertEquals( 13465 SourceFixture.ValidSourceParams.DESTINATION_LIMIT_PRIORITY, 13466 source.getDestinationLimitPriority()); 13467 } 13468 13469 @Test testInsertAggregateDebugReportRecords_sourceAndTriggerIdPresent_succeeds()13470 public void testInsertAggregateDebugReportRecords_sourceAndTriggerIdPresent_succeeds() { 13471 // insert source & trigger to honor the foreign key constraint 13472 Source source = 13473 SourceFixture.getMinimalValidSourceBuilder() 13474 .setId("S1") 13475 .setEventId(new UnsignedLong(3L)) 13476 .setEnrollmentId("1") 13477 .build(); 13478 Trigger trigger = 13479 TriggerFixture.getValidTriggerBuilder().setId("T1").setEnrollmentId("2").build(); 13480 AggregateDebugReportRecord validAggregateDebugReportRecord = 13481 new AggregateDebugReportRecord.Builder( 13482 /* reportGenerationTime= */ 1000L, 13483 /* topLevelRegistrant= */ TOP_LEVEL_REGISTRANT_1, 13484 /* registrantApp= */ Uri.parse("com.test1.myapp"), 13485 /* registrationOrigin= */ Uri.parse("https://destination.test"), 13486 /* contributions= */ 9) 13487 .setSourceId("S1") 13488 .setTriggerId("T1") 13489 .build(); 13490 13491 // Execution 13492 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 13493 Objects.requireNonNull(db); 13494 insertSource(source, source.getId()); 13495 AbstractDbIntegrationTest.insertToDb(trigger, db); 13496 mDatastoreManager.runInTransaction( 13497 (dao) -> dao.insertAggregateDebugReportRecord(validAggregateDebugReportRecord)); 13498 13499 // Assertion 13500 try (Cursor cursor = 13501 MeasurementDbHelper.getInstance() 13502 .getReadableDatabase() 13503 .query( 13504 AggregatableDebugReportBudgetTrackerContract.TABLE, 13505 null, 13506 null, 13507 null, 13508 null, 13509 null, 13510 null)) { 13511 assertThat(cursor.moveToNext()).isTrue(); 13512 assertThat(validAggregateDebugReportRecord.getReportGenerationTime()) 13513 .isEqualTo( 13514 cursor.getInt( 13515 cursor.getColumnIndex( 13516 AggregatableDebugReportBudgetTrackerContract 13517 .REPORT_GENERATION_TIME))); 13518 assertThat(validAggregateDebugReportRecord.getTopLevelRegistrant().toString()) 13519 .isEqualTo( 13520 cursor.getString( 13521 cursor.getColumnIndex( 13522 AggregatableDebugReportBudgetTrackerContract 13523 .TOP_LEVEL_REGISTRANT))); 13524 assertThat(validAggregateDebugReportRecord.getRegistrantApp().toString()) 13525 .isEqualTo( 13526 cursor.getString( 13527 cursor.getColumnIndex( 13528 AggregatableDebugReportBudgetTrackerContract 13529 .REGISTRANT_APP))); 13530 assertThat(validAggregateDebugReportRecord.getRegistrationOrigin().toString()) 13531 .isEqualTo( 13532 cursor.getString( 13533 cursor.getColumnIndex( 13534 AggregatableDebugReportBudgetTrackerContract 13535 .REGISTRATION_ORIGIN))); 13536 assertThat(validAggregateDebugReportRecord.getSourceId()) 13537 .isEqualTo( 13538 cursor.getString( 13539 cursor.getColumnIndex( 13540 AggregatableDebugReportBudgetTrackerContract 13541 .SOURCE_ID))); 13542 assertThat(validAggregateDebugReportRecord.getTriggerId()) 13543 .isEqualTo( 13544 cursor.getString( 13545 cursor.getColumnIndex( 13546 AggregatableDebugReportBudgetTrackerContract 13547 .TRIGGER_ID))); 13548 assertThat(validAggregateDebugReportRecord.getContributions()) 13549 .isEqualTo( 13550 cursor.getInt( 13551 cursor.getColumnIndex( 13552 AggregatableDebugReportBudgetTrackerContract 13553 .CONTRIBUTIONS))); 13554 } 13555 } 13556 13557 @Test testInsertAggregateDebugReportRecords_nullTriggerId_succeeds()13558 public void testInsertAggregateDebugReportRecords_nullTriggerId_succeeds() { 13559 // insert source & trigger to honor the foreign key constraint 13560 Source source = 13561 SourceFixture.getMinimalValidSourceBuilder() 13562 .setId("S1") 13563 .setEventId(new UnsignedLong(3L)) 13564 .setEnrollmentId("1") 13565 .build(); 13566 AggregateDebugReportRecord validAggregateDebugReportRecord = 13567 new AggregateDebugReportRecord.Builder( 13568 /* reportGenerationTime= */ 1000L, 13569 /* topLevelRegistrant= */ TOP_LEVEL_REGISTRANT_1, 13570 /* registrantApp= */ Uri.parse("com.test1.myapp"), 13571 /* registrationOrigin= */ Uri.parse("https://destination.test"), 13572 /* contributions= */ 9) 13573 .setSourceId("S1") 13574 .build(); 13575 13576 // Execution 13577 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 13578 Objects.requireNonNull(db); 13579 insertSource(source, source.getId()); 13580 mDatastoreManager.runInTransaction( 13581 (dao) -> dao.insertAggregateDebugReportRecord(validAggregateDebugReportRecord)); 13582 13583 // Assertion 13584 try (Cursor cursor = 13585 MeasurementDbHelper.getInstance() 13586 .getReadableDatabase() 13587 .query( 13588 AggregatableDebugReportBudgetTrackerContract.TABLE, 13589 null, 13590 null, 13591 null, 13592 null, 13593 null, 13594 null)) { 13595 assertThat(cursor.moveToNext()).isTrue(); 13596 assertThat(validAggregateDebugReportRecord.getReportGenerationTime()) 13597 .isEqualTo( 13598 cursor.getInt( 13599 cursor.getColumnIndex( 13600 AggregatableDebugReportBudgetTrackerContract 13601 .REPORT_GENERATION_TIME))); 13602 assertThat(validAggregateDebugReportRecord.getTopLevelRegistrant().toString()) 13603 .isEqualTo( 13604 cursor.getString( 13605 cursor.getColumnIndex( 13606 AggregatableDebugReportBudgetTrackerContract 13607 .TOP_LEVEL_REGISTRANT))); 13608 assertThat(validAggregateDebugReportRecord.getRegistrantApp().toString()) 13609 .isEqualTo( 13610 cursor.getString( 13611 cursor.getColumnIndex( 13612 AggregatableDebugReportBudgetTrackerContract 13613 .REGISTRANT_APP))); 13614 assertThat(validAggregateDebugReportRecord.getRegistrationOrigin().toString()) 13615 .isEqualTo( 13616 cursor.getString( 13617 cursor.getColumnIndex( 13618 AggregatableDebugReportBudgetTrackerContract 13619 .REGISTRATION_ORIGIN))); 13620 assertThat(validAggregateDebugReportRecord.getSourceId()) 13621 .isEqualTo( 13622 cursor.getString( 13623 cursor.getColumnIndex( 13624 AggregatableDebugReportBudgetTrackerContract 13625 .SOURCE_ID))); 13626 assertThat(validAggregateDebugReportRecord.getTriggerId()) 13627 .isEqualTo( 13628 cursor.getString( 13629 cursor.getColumnIndex( 13630 AggregatableDebugReportBudgetTrackerContract 13631 .TRIGGER_ID))); 13632 assertThat(validAggregateDebugReportRecord.getContributions()) 13633 .isEqualTo( 13634 cursor.getInt( 13635 cursor.getColumnIndex( 13636 AggregatableDebugReportBudgetTrackerContract 13637 .CONTRIBUTIONS))); 13638 } 13639 } 13640 13641 @Test testInsertAggregateDebugReportRecords_nullSourceAndTriggerId_succeeds()13642 public void testInsertAggregateDebugReportRecords_nullSourceAndTriggerId_succeeds() { 13643 AggregateDebugReportRecord validAggregateDebugReportRecord = 13644 new AggregateDebugReportRecord.Builder( 13645 /* reportGenerationTime= */ 1000L, 13646 /* topLevelRegistrant= */ TOP_LEVEL_REGISTRANT_1, 13647 /* registrantApp= */ Uri.parse("com.test1.myapp"), 13648 /* registrationOrigin= */ Uri.parse("https://destination.test"), 13649 /* contributions= */ 9) 13650 .build(); 13651 13652 // Execution 13653 mDatastoreManager.runInTransaction( 13654 (dao) -> dao.insertAggregateDebugReportRecord(validAggregateDebugReportRecord)); 13655 13656 // Assertion 13657 try (Cursor cursor = 13658 MeasurementDbHelper.getInstance() 13659 .getReadableDatabase() 13660 .query( 13661 AggregatableDebugReportBudgetTrackerContract.TABLE, 13662 null, 13663 null, 13664 null, 13665 null, 13666 null, 13667 null)) { 13668 assertThat(cursor.moveToNext()).isTrue(); 13669 assertThat(validAggregateDebugReportRecord.getReportGenerationTime()) 13670 .isEqualTo( 13671 cursor.getInt( 13672 cursor.getColumnIndex( 13673 AggregatableDebugReportBudgetTrackerContract 13674 .REPORT_GENERATION_TIME))); 13675 assertThat(validAggregateDebugReportRecord.getTopLevelRegistrant().toString()) 13676 .isEqualTo( 13677 cursor.getString( 13678 cursor.getColumnIndex( 13679 AggregatableDebugReportBudgetTrackerContract 13680 .TOP_LEVEL_REGISTRANT))); 13681 assertThat(validAggregateDebugReportRecord.getRegistrantApp().toString()) 13682 .isEqualTo( 13683 cursor.getString( 13684 cursor.getColumnIndex( 13685 AggregatableDebugReportBudgetTrackerContract 13686 .REGISTRANT_APP))); 13687 assertThat(validAggregateDebugReportRecord.getRegistrationOrigin().toString()) 13688 .isEqualTo( 13689 cursor.getString( 13690 cursor.getColumnIndex( 13691 AggregatableDebugReportBudgetTrackerContract 13692 .REGISTRATION_ORIGIN))); 13693 assertThat(validAggregateDebugReportRecord.getSourceId()) 13694 .isEqualTo( 13695 cursor.getString( 13696 cursor.getColumnIndex( 13697 AggregatableDebugReportBudgetTrackerContract 13698 .SOURCE_ID))); 13699 assertThat(validAggregateDebugReportRecord.getTriggerId()) 13700 .isEqualTo( 13701 cursor.getString( 13702 cursor.getColumnIndex( 13703 AggregatableDebugReportBudgetTrackerContract 13704 .TRIGGER_ID))); 13705 assertThat(validAggregateDebugReportRecord.getContributions()) 13706 .isEqualTo( 13707 cursor.getInt( 13708 cursor.getColumnIndex( 13709 AggregatableDebugReportBudgetTrackerContract 13710 .CONTRIBUTIONS))); 13711 } 13712 } 13713 13714 @Test testGetTotalAggregateDebugReportBudget()13715 public void testGetTotalAggregateDebugReportBudget() { 13716 // insert source & trigger to honor the foreign key constraint 13717 Source source = 13718 SourceFixture.getMinimalValidSourceBuilder() 13719 .setId("S1") 13720 .setEventId(new UnsignedLong(3L)) 13721 .setEnrollmentId("1") 13722 .build(); 13723 Trigger trigger = 13724 TriggerFixture.getValidTriggerBuilder().setId("T1").setEnrollmentId("2").build(); 13725 SQLiteDatabase db = MeasurementDbHelper.getInstance().getWritableDatabase(); 13726 Objects.requireNonNull(db); 13727 insertSource(source, source.getId()); 13728 AbstractDbIntegrationTest.insertToDb(trigger, db); 13729 13730 // test case 1 (publisher query): outside time window + same publisher 13731 AggregateDebugReportRecord validAggregateDebugReportRecord1 = 13732 new AggregateDebugReportRecord.Builder( 13733 /* reportGenerationTime= */ 1000L, 13734 /* topLevelRegistrant= */ TOP_LEVEL_REGISTRANT_1, 13735 /* registrantApp= */ Uri.parse("com.test.myapp"), 13736 /* registrationOrigin= */ Uri.parse("https://destination0.test"), 13737 /* contributions= */ 9) 13738 .setSourceId("S1") 13739 .setTriggerId("T1") 13740 .build(); 13741 13742 // test case 2 (publisher query): inside time window + same publisher + different origin 13743 AggregateDebugReportRecord validAggregateDebugReportRecord2 = 13744 new AggregateDebugReportRecord.Builder( 13745 /* reportGenerationTime= */ 2000L, 13746 /* topLevelRegistrant= */ TOP_LEVEL_REGISTRANT_1, 13747 /* registrantApp= */ Uri.parse("com.test.myapp"), 13748 /* registrationOrigin= */ Uri.parse("https://destination1.test"), 13749 /* contributions= */ 16) 13750 .setSourceId("S1") 13751 .setTriggerId("T1") 13752 .build(); 13753 AggregateDebugReportRecord validAggregateDebugReportRecord3 = 13754 new AggregateDebugReportRecord.Builder( 13755 /* reportGenerationTime= */ 3000L, 13756 /* topLevelRegistrant= */ TOP_LEVEL_REGISTRANT_1, 13757 /* registrantApp= */ Uri.parse("com.test.myapp"), 13758 /* registrationOrigin= */ Uri.parse("https://destination2.test"), 13759 /* contributions= */ 25) 13760 .setSourceId("S1") 13761 .setTriggerId("T1") 13762 .build(); 13763 AggregateDebugReportRecord validAggregateDebugReportRecord4 = 13764 new AggregateDebugReportRecord.Builder( 13765 /* reportGenerationTime= */ 4000L, 13766 /* topLevelRegistrant= */ TOP_LEVEL_REGISTRANT_1, 13767 /* registrantApp= */ Uri.parse("com.test.myapp"), 13768 /* registrationOrigin= */ Uri.parse("https://destination3.test"), 13769 /* contributions= */ 36) 13770 .setSourceId("S1") 13771 .setTriggerId("T1") 13772 .build(); 13773 13774 // test case 3 (publisher + origin query): inside time window + same publisher + same origin 13775 AggregateDebugReportRecord validAggregateDebugReportRecord5 = 13776 new AggregateDebugReportRecord.Builder( 13777 /* reportGenerationTime= */ 5000L, 13778 /* topLevelRegistrant= */ TOP_LEVEL_REGISTRANT_2, 13779 /* registrantApp= */ Uri.parse("com.test.myapp"), 13780 /* registrationOrigin= */ Uri.parse("https://destination4.test"), 13781 /* contributions= */ 49) 13782 .setSourceId("S1") 13783 .setTriggerId("T1") 13784 .build(); 13785 AggregateDebugReportRecord validAggregateDebugReportRecord6 = 13786 new AggregateDebugReportRecord.Builder( 13787 /* reportGenerationTime= */ 6000L, 13788 /* topLevelRegistrant= */ TOP_LEVEL_REGISTRANT_2, 13789 /* registrantApp= */ Uri.parse("com.test.myapp"), 13790 /* registrationOrigin= */ Uri.parse("https://destination4.test"), 13791 /* contributions= */ 64) 13792 .setSourceId("S1") 13793 .setTriggerId("T1") 13794 .build(); 13795 List<AggregateDebugReportRecord> validAggregateDebugReportRecords = 13796 Arrays.asList( 13797 validAggregateDebugReportRecord1, 13798 validAggregateDebugReportRecord2, 13799 validAggregateDebugReportRecord3, 13800 validAggregateDebugReportRecord4, 13801 validAggregateDebugReportRecord5, 13802 validAggregateDebugReportRecord6); 13803 13804 for (AggregateDebugReportRecord validAggregateDebugReportRecord : 13805 validAggregateDebugReportRecords) { 13806 mDatastoreManager.runInTransaction( 13807 (dao) -> dao.insertAggregateDebugReportRecord(validAggregateDebugReportRecord)); 13808 } 13809 13810 // test case 1 13811 int budget1 = 13812 mDatastoreManager 13813 .runInTransactionWithResult( 13814 (dao) -> 13815 dao.sumAggregateDebugReportBudgetXPublisherXWindow( 13816 /* publisher= */ TOP_LEVEL_REGISTRANT_1, 13817 /* publisherType= */ EventSurfaceType.APP, 13818 /* windowStartTime= */ 1000L)) 13819 .get(); 13820 assertThat(budget1).isEqualTo((16 + 25 + 36)); 13821 13822 // test case 2 13823 int budget2 = 13824 mDatastoreManager 13825 .runInTransactionWithResult( 13826 (dao) -> 13827 dao.sumAggregateDebugReportBudgetXPublisherXWindow( 13828 /* publisher= */ TOP_LEVEL_REGISTRANT_1, 13829 /* publisherType= */ EventSurfaceType.APP, 13830 /* windowStartTime= */ 1001L)) 13831 .get(); 13832 assertThat(budget2).isEqualTo((16 + 25 + 36)); 13833 13834 // test case 3 13835 int budget3 = 13836 mDatastoreManager 13837 .runInTransactionWithResult( 13838 (dao) -> 13839 dao.sumAggregateDebugReportBudgetXOriginXPublisherXWindow( 13840 /* publisher= */ TOP_LEVEL_REGISTRANT_2, 13841 /* publisherType= */ EventSurfaceType.APP, 13842 /* origin= */ Uri.parse( 13843 "https://destination4.test"), 13844 /* windowStartTime= */ 1001L)) 13845 .get(); 13846 assertThat(budget3).isEqualTo((49 + 64)); 13847 } 13848 13849 @Test updateSourceAggregateDebugContributions_updateFromPreValue_success()13850 public void updateSourceAggregateDebugContributions_updateFromPreValue_success() { 13851 // Setup 13852 mocker.mockGetFlags(mMockFlags); 13853 doReturn(true).when(mMockFlags).getMeasurementEnableAggregateDebugReporting(); 13854 doReturn(MEASUREMENT_DB_SIZE_LIMIT).when(mMockFlags).getMeasurementDbSizeLimit(); 13855 int initialContributions = 1024; 13856 Source source = 13857 SourceFixture.getValidSourceBuilder() 13858 .setAggregateDebugReportContributions(initialContributions) 13859 .build(); 13860 insertSource(source, source.getId()); 13861 13862 // Verify persisted value 13863 assertThat(getFirstSourceFromDb().getAggregateDebugReportContributions()) 13864 .isEqualTo(initialContributions); 13865 13866 // Execution 13867 // Update the value 13868 int expectedUpdatedContributions = 65536; 13869 source.setAggregateDebugContributions(expectedUpdatedContributions); 13870 mDatastoreManager.runInTransaction( 13871 measurementDao -> measurementDao.updateSourceAggregateDebugContributions(source)); 13872 13873 // Verification 13874 assertThat(getFirstSourceFromDb().getAggregateDebugReportContributions()) 13875 .isEqualTo(expectedUpdatedContributions); 13876 } 13877 13878 /** Test that records in SourceContract Table are fetched properly. */ 13879 @Test testFetchAllSourceRegistrations_pass()13880 public void testFetchAllSourceRegistrations_pass() { 13881 Source source1 = 13882 SourceFixture.getMinimalValidSourceBuilder() 13883 .setId("1") 13884 .setEventTime(8640000000L) 13885 .setExpiryTime(8640000010L) 13886 .setDebugKey(new UnsignedLong(7834690L)) 13887 .build(); 13888 insertSource(source1, source1.getId()); 13889 13890 Source source2 = 13891 SourceFixture.getMinimalValidSourceBuilder() 13892 .setId("2") 13893 .setEventTime(8640000000L) 13894 .setExpiryTime(8640000010L) 13895 .setDebugKey(new UnsignedLong(7834690L)) 13896 .build(); 13897 insertSource(source2, source2.getId()); 13898 13899 List<Source> fetchedAllSourceRegistration = 13900 mDatastoreManager 13901 .runInTransactionWithResult(dao -> dao.fetchAllSourceRegistrations()) 13902 .orElseThrow(); 13903 13904 assertThat(fetchedAllSourceRegistration).isNotNull(); 13905 assertThat(fetchedAllSourceRegistration.size()).isEqualTo(2); 13906 13907 assertThat(fetchedAllSourceRegistration.get(0)).isEqualTo(source1); 13908 assertThat(fetchedAllSourceRegistration.get(1)).isEqualTo(source2); 13909 } 13910 13911 @Test testFetchAllSourceRegistrations_passMultipleDestinations()13912 public void testFetchAllSourceRegistrations_passMultipleDestinations() { 13913 List<Uri> webDestinations1 = 13914 List.of( 13915 Uri.parse("https://first-place.test"), 13916 Uri.parse("https://second-place.test"), 13917 Uri.parse("https://third-place.test")); 13918 13919 Source source1 = 13920 SourceFixture.getMinimalValidSourceBuilder() 13921 .setId("1") 13922 .setWebDestinations(webDestinations1) 13923 .build(); 13924 insertSource(source1, source1.getId()); 13925 13926 List<Uri> webDestinations2 = 13927 List.of( 13928 Uri.parse("https://not-first-place.test"), 13929 Uri.parse("https://not-second-place.test"), 13930 Uri.parse("https://third-place.test")); 13931 Source source2 = 13932 SourceFixture.getMinimalValidSourceBuilder() 13933 .setId("2") 13934 .setWebDestinations(webDestinations2) 13935 .build(); 13936 insertSource(source2, source2.getId()); 13937 13938 List<Source> fetchedAllSourceRegistration = 13939 mDatastoreManager 13940 .runInTransactionWithResult(dao -> dao.fetchAllSourceRegistrations()) 13941 .orElseThrow(); 13942 13943 assertThat(fetchedAllSourceRegistration).isNotNull(); 13944 assertThat(fetchedAllSourceRegistration.size()).isEqualTo(2); 13945 13946 assertThat(fetchedAllSourceRegistration.get(0)).isEqualTo(source1); 13947 assertThat(fetchedAllSourceRegistration.get(1)).isEqualTo(source2); 13948 } 13949 13950 /** Test that records in TriggerContract Table are fetched properly. */ 13951 @Test testFetchAllTriggerRegistrations_pass()13952 public void testFetchAllTriggerRegistrations_pass() { 13953 Trigger trigger1 = 13954 TriggerFixture.getValidTriggerBuilder() 13955 .setId("trigger1") 13956 .setTriggerTime(TriggerFixture.ValidTriggerParams.TRIGGER_TIME) 13957 .setDebugKey(TriggerFixture.ValidTriggerParams.DEBUG_KEY) 13958 .build(); 13959 13960 insertTrigger(trigger1, "trigger1"); 13961 13962 Trigger trigger2 = 13963 TriggerFixture.getValidTriggerBuilder() 13964 .setId("trigger2") 13965 .setTriggerTime(TriggerFixture.ValidTriggerParams.TRIGGER_TIME) 13966 .setDebugKey(TriggerFixture.ValidTriggerParams.DEBUG_KEY) 13967 .build(); 13968 13969 insertTrigger(trigger2, "trigger2"); 13970 13971 List<Trigger> fetchedAllTriggerRegistration = 13972 mDatastoreManager 13973 .runInTransactionWithResult(dao -> dao.fetchAllTriggerRegistrations()) 13974 .orElseThrow(); 13975 13976 assertThat(fetchedAllTriggerRegistration).isNotNull(); 13977 assertThat(fetchedAllTriggerRegistration.size()).isEqualTo(2); 13978 13979 assertThat(fetchedAllTriggerRegistration.get(0)).isEqualTo(trigger1); 13980 assertThat(fetchedAllTriggerRegistration.get(1)).isEqualTo(trigger2); 13981 } 13982 13983 /** Test that records in EventReport Table are fetched properly. */ 13984 @Test testFetchAllEventReports_pass()13985 public void testFetchAllEventReports_pass() { 13986 Source source1 = 13987 SourceFixture.getMinimalValidSourceBuilder() 13988 .setId("S1") 13989 .setAttributionMode(Source.AttributionMode.FALSELY) 13990 .build(); 13991 insertSource(source1, source1.getId()); 13992 Source source2 = 13993 SourceFixture.getMinimalValidSourceBuilder() 13994 .setId("S2") 13995 .setAttributionMode(Source.AttributionMode.TRUTHFULLY) 13996 .build(); 13997 insertSource(source2, source2.getId()); 13998 13999 Trigger trigger1 = TriggerFixture.getValidTriggerBuilder().setId("T1").build(); 14000 insertTrigger(trigger1, trigger1.getId()); 14001 14002 EventReport eventReport1 = 14003 EventReportFixture.getBaseEventReportBuild() 14004 .setId("Event1") 14005 .setSourceId("S1") 14006 .setTriggerId(null) 14007 .build(); 14008 14009 EventReport eventReport2 = 14010 EventReportFixture.getBaseEventReportBuild() 14011 .setId("Event2") 14012 .setSourceId("S2") 14013 .setTriggerId("T1") 14014 .build(); 14015 14016 mDatastoreManager.runInTransaction( 14017 (dao) -> { 14018 dao.insertEventReport(eventReport1); 14019 dao.insertEventReport(eventReport2); 14020 }); 14021 14022 List<EventReport> fetchedAllEventReports = 14023 mDatastoreManager 14024 .runInTransactionWithResult(dao -> dao.fetchAllEventReports()) 14025 .orElseThrow(); 14026 ; 14027 14028 assertThat(fetchedAllEventReports).isNotNull(); 14029 assertThat(fetchedAllEventReports.size()).isEqualTo(2); 14030 14031 assertThat(fetchedAllEventReports.get(0)).isEqualTo(eventReport1); 14032 assertThat(fetchedAllEventReports.get(1)).isEqualTo(eventReport2); 14033 } 14034 14035 /** Test that records in AggregateReport Table are fetched properly. */ 14036 @Test testFetchAllAggregatableReports_pass()14037 public void testFetchAllAggregatableReports_pass() { 14038 AggregateReport aggregatableReport1 = 14039 AggregateReportFixture.getValidAggregateReportBuilder() 14040 .setId("report1") 14041 .setPublisher(Uri.parse("android-app://com.registrant1")) 14042 .setAttributionDestination(Uri.parse("android-app://com.destination1")) 14043 .build(); 14044 14045 AggregateReport aggregatableReport2 = 14046 AggregateReportFixture.getValidAggregateReportBuilder() 14047 .setId("report2") 14048 .setPublisher(Uri.parse("android-app://com.registrant2")) 14049 .setAttributionDestination( 14050 Uri.parse("android-app://com.destination2")) 14051 .build(); 14052 14053 mDatastoreManager.runInTransaction( 14054 (dao) -> { 14055 dao.insertAggregateReport(aggregatableReport1); 14056 dao.insertAggregateReport(aggregatableReport2); 14057 }); 14058 14059 List<AggregateReport> fetchedAllAggregatableReports = 14060 mDatastoreManager 14061 .runInTransactionWithResult(dao -> dao.fetchAllAggregatableReports()) 14062 .orElseThrow(); 14063 14064 assertThat(fetchedAllAggregatableReports).isNotNull(); 14065 assertThat(fetchedAllAggregatableReports.size()).isEqualTo(2); 14066 14067 assertThat(fetchedAllAggregatableReports.get(0)).isEqualTo(aggregatableReport1); 14068 assertThat(fetchedAllAggregatableReports.get(1)).isEqualTo(aggregatableReport2); 14069 } 14070 14071 @Test testFetchAllDebugReports_pass()14072 public void testFetchAllDebugReports_pass() { 14073 DebugReport debugReport1 = 14074 createDebugReport(/* id= */ "1", Uri.parse("android-app://debug1"), 1701206853050L); 14075 14076 DebugReport debugReport2 = 14077 createDebugReport(/* id= */ "2", Uri.parse("android-app://debug2"), 1701206853050L); 14078 14079 mDatastoreManager.runInTransaction( 14080 (dao) -> { 14081 dao.insertDebugReport(debugReport1); 14082 dao.insertDebugReport(debugReport2); 14083 }); 14084 14085 List<DebugReport> fetchedAllDebugReports = 14086 mDatastoreManager 14087 .runInTransactionWithResult(dao -> dao.fetchAllDebugReports()) 14088 .orElseThrow(); 14089 14090 assertThat(fetchedAllDebugReports).isNotNull(); 14091 assertThat(fetchedAllDebugReports.size()).isEqualTo(2); 14092 14093 assertThat(fetchedAllDebugReports.get(0)).isEqualTo(debugReport1); 14094 assertThat(fetchedAllDebugReports.get(1)).isEqualTo(debugReport2); 14095 } 14096 14097 @Test testGetPendingCountUniqueReportIds_getsPendingReports()14098 public void testGetPendingCountUniqueReportIds_getsPendingReports() { 14099 CountUniqueReport report1 = 14100 CountUniqueReportFixture.getValidCountUniqueReportBuilder() 14101 .setReportId("CU1") 14102 .setStatus(CountUniqueReport.ReportDeliveryStatus.PENDING) 14103 .build(); 14104 CountUniqueReport report2 = 14105 CountUniqueReportFixture.getValidCountUniqueReportBuilder() 14106 .setReportId("CU2") 14107 .setStatus(CountUniqueReport.ReportDeliveryStatus.PENDING) 14108 .build(); 14109 14110 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 14111 14112 AbstractDbIntegrationTest.insertToDb(report1, db); 14113 AbstractDbIntegrationTest.insertToDb(report2, db); 14114 14115 Optional<List<String>> idsOpt = 14116 mDatastoreManager.runInTransactionWithResult( 14117 (dao) -> dao.getPendingCountUniqueReportIds()); 14118 14119 assertThat(idsOpt.isPresent()).isTrue(); 14120 List<String> ids = idsOpt.get(); 14121 assertThat(ids.size()).isEqualTo(2); 14122 14123 assertThat(ids.contains(report1.getReportId())).isTrue(); 14124 assertThat(ids.contains(report2.getReportId())).isTrue(); 14125 } 14126 14127 @Test testGetPendingCountUniqueReportIds_ignoresDeliveredReports()14128 public void testGetPendingCountUniqueReportIds_ignoresDeliveredReports() { 14129 CountUniqueReport report1 = 14130 CountUniqueReportFixture.getValidCountUniqueReportBuilder() 14131 .setReportId("CU1") 14132 .setStatus(CountUniqueReport.ReportDeliveryStatus.DELIVERED) 14133 .build(); 14134 CountUniqueReport report2 = 14135 CountUniqueReportFixture.getValidCountUniqueReportBuilder() 14136 .setReportId("CU2") 14137 .setStatus(CountUniqueReport.ReportDeliveryStatus.PENDING) 14138 .build(); 14139 14140 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 14141 14142 AbstractDbIntegrationTest.insertToDb(report1, db); 14143 AbstractDbIntegrationTest.insertToDb(report2, db); 14144 14145 Optional<List<String>> idsOpt = 14146 mDatastoreManager.runInTransactionWithResult( 14147 (dao) -> dao.getPendingCountUniqueReportIds()); 14148 14149 assertThat(idsOpt.isPresent()).isTrue(); 14150 List<String> ids = idsOpt.get(); 14151 assertThat(ids.size()).isEqualTo(1); 14152 14153 assertThat(ids.contains(report1.getReportId())).isFalse(); 14154 assertThat(ids.contains(report2.getReportId())).isTrue(); 14155 } 14156 getFirstSourceFromDb()14157 private Source getFirstSourceFromDb() { 14158 return mDatastoreManager 14159 .runInTransactionWithResult( 14160 measurementDao -> 14161 measurementDao.getSource( 14162 getFirstIdFromDatastore( 14163 SourceContract.TABLE, SourceContract.ID))) 14164 .orElseThrow(); 14165 } 14166 getAggregateReportConsumer(SQLiteDatabase db)14167 private static Consumer<AggregateReport> getAggregateReportConsumer(SQLiteDatabase db) { 14168 Consumer<AggregateReport> aggregateReportConsumer = 14169 aggregateReport -> { 14170 ContentValues values = new ContentValues(); 14171 values.put(MeasurementTables.AggregateReport.ID, aggregateReport.getId()); 14172 values.put( 14173 MeasurementTables.AggregateReport.SOURCE_ID, 14174 aggregateReport.getSourceId()); 14175 values.put( 14176 MeasurementTables.AggregateReport.ATTRIBUTION_DESTINATION, 14177 aggregateReport.getAttributionDestination().toString()); 14178 values.put( 14179 MeasurementTables.AggregateReport.SCHEDULED_REPORT_TIME, 14180 aggregateReport.getScheduledReportTime()); 14181 values.put( 14182 MeasurementTables.AggregateReport.STATUS, aggregateReport.getStatus()); 14183 db.insert(MeasurementTables.AggregateReport.TABLE, null, values); 14184 }; 14185 return aggregateReportConsumer; 14186 } 14187 getEventReportConsumer(SQLiteDatabase db)14188 private static Consumer<EventReport> getEventReportConsumer(SQLiteDatabase db) { 14189 Consumer<EventReport> eventReportConsumer = 14190 eventReport -> { 14191 ContentValues values = new ContentValues(); 14192 values.put(EventReportContract.ID, eventReport.getId()); 14193 values.put( 14194 EventReportContract.ATTRIBUTION_DESTINATION, 14195 eventReport.getAttributionDestinations().get(0).toString()); 14196 values.put(EventReportContract.REPORT_TIME, eventReport.getReportTime()); 14197 values.put(EventReportContract.STATUS, EventReport.Status.PENDING); 14198 14199 db.insert(EventReportContract.TABLE, null, values); 14200 }; 14201 return eventReportConsumer; 14202 } 14203 insertInDb(SQLiteDatabase db, Source source)14204 private void insertInDb(SQLiteDatabase db, Source source) { 14205 ContentValues values = new ContentValues(); 14206 values.put(SourceContract.ID, source.getId()); 14207 values.put(SourceContract.STATUS, Source.Status.ACTIVE); 14208 values.put(SourceContract.EVENT_TIME, source.getEventTime()); 14209 values.put(SourceContract.EXPIRY_TIME, source.getExpiryTime()); 14210 values.put(SourceContract.ENROLLMENT_ID, source.getEnrollmentId()); 14211 values.put(SourceContract.PUBLISHER, source.getPublisher().toString()); 14212 values.put(SourceContract.REGISTRANT, source.getRegistrant().toString()); 14213 values.put(SourceContract.REGISTRATION_ORIGIN, source.getRegistrationOrigin().toString()); 14214 if (source.getAttributedTriggers() != null) { 14215 values.put(SourceContract.EVENT_ATTRIBUTION_STATUS, source.attributedTriggersToJson()); 14216 } 14217 if (source.getTriggerSpecs() != null) { 14218 values.put(SourceContract.TRIGGER_SPECS, source.getTriggerSpecs().encodeToJson()); 14219 } 14220 db.insert(SourceContract.TABLE, null, values); 14221 14222 // Insert source destinations 14223 if (source.getAppDestinations() != null) { 14224 for (Uri appDestination : source.getAppDestinations()) { 14225 ContentValues destinationValues = new ContentValues(); 14226 destinationValues.put( 14227 MeasurementTables.SourceDestination.SOURCE_ID, source.getId()); 14228 destinationValues.put( 14229 MeasurementTables.SourceDestination.DESTINATION_TYPE, EventSurfaceType.APP); 14230 destinationValues.put( 14231 MeasurementTables.SourceDestination.DESTINATION, appDestination.toString()); 14232 db.insert(MeasurementTables.SourceDestination.TABLE, null, destinationValues); 14233 } 14234 } 14235 14236 if (source.getWebDestinations() != null) { 14237 for (Uri webDestination : source.getWebDestinations()) { 14238 ContentValues destinationValues = new ContentValues(); 14239 destinationValues.put( 14240 MeasurementTables.SourceDestination.SOURCE_ID, source.getId()); 14241 destinationValues.put( 14242 MeasurementTables.SourceDestination.DESTINATION_TYPE, EventSurfaceType.WEB); 14243 destinationValues.put( 14244 MeasurementTables.SourceDestination.DESTINATION, webDestination.toString()); 14245 db.insert(MeasurementTables.SourceDestination.TABLE, null, destinationValues); 14246 } 14247 } 14248 } 14249 queryAndAssertSourceEntries( SQLiteDatabase db, String enrollmentId, List<String> expectedSourceIds)14250 private void queryAndAssertSourceEntries( 14251 SQLiteDatabase db, String enrollmentId, List<String> expectedSourceIds) { 14252 try (Cursor cursor = 14253 db.query( 14254 XnaIgnoredSourcesContract.TABLE, 14255 new String[] {XnaIgnoredSourcesContract.SOURCE_ID}, 14256 XnaIgnoredSourcesContract.ENROLLMENT_ID + " = ?", 14257 new String[] {enrollmentId}, 14258 null, 14259 null, 14260 null)) { 14261 assertEquals(expectedSourceIds.size(), cursor.getCount()); 14262 for (int i = 0; i < expectedSourceIds.size() && cursor.moveToNext(); i++) { 14263 assertEquals(expectedSourceIds.get(i), cursor.getString(0)); 14264 } 14265 } 14266 } 14267 createSourceBuilder()14268 private Source.Builder createSourceBuilder() { 14269 return new Source.Builder() 14270 .setEventId(SourceFixture.ValidSourceParams.SOURCE_EVENT_ID) 14271 .setPublisher(SourceFixture.ValidSourceParams.PUBLISHER) 14272 .setAppDestinations(SourceFixture.ValidSourceParams.ATTRIBUTION_DESTINATIONS) 14273 .setWebDestinations(SourceFixture.ValidSourceParams.WEB_DESTINATIONS) 14274 .setEnrollmentId(SourceFixture.ValidSourceParams.ENROLLMENT_ID) 14275 .setRegistrant(SourceFixture.ValidSourceParams.REGISTRANT) 14276 .setEventTime(SOURCE_EVENT_TIME) 14277 .setExpiryTime(SourceFixture.ValidSourceParams.EXPIRY_TIME) 14278 .setPriority(SourceFixture.ValidSourceParams.PRIORITY) 14279 .setSourceType(SourceFixture.ValidSourceParams.SOURCE_TYPE) 14280 .setInstallAttributionWindow( 14281 SourceFixture.ValidSourceParams.INSTALL_ATTRIBUTION_WINDOW) 14282 .setInstallCooldownWindow(SourceFixture.ValidSourceParams.INSTALL_COOLDOWN_WINDOW) 14283 .setAttributionMode(SourceFixture.ValidSourceParams.ATTRIBUTION_MODE) 14284 .setAggregateSource(SourceFixture.ValidSourceParams.buildAggregateSource()) 14285 .setFilterDataString(SourceFixture.ValidSourceParams.buildFilterDataString()) 14286 .setSharedFilterDataKeys(SourceFixture.ValidSourceParams.SHARED_FILTER_DATA_KEYS) 14287 .setIsDebugReporting(true) 14288 .setRegistrationId(UUID.randomUUID().toString()) 14289 .setSharedAggregationKeys(SHARED_AGGREGATE_KEYS) 14290 .setInstallTime(SourceFixture.ValidSourceParams.INSTALL_TIME) 14291 .setRegistrationOrigin(SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN); 14292 } 14293 createAggregateReportForSourceAndTrigger( Source source, Trigger trigger)14294 private AggregateReport createAggregateReportForSourceAndTrigger( 14295 Source source, Trigger trigger) { 14296 return createAggregateReportForSourceAndTrigger( 14297 UUID.randomUUID().toString(), source, trigger); 14298 } 14299 createEventReportForSourceAndTrigger(Source source, Trigger trigger)14300 private EventReport createEventReportForSourceAndTrigger(Source source, Trigger trigger) 14301 throws JSONException { 14302 return createEventReportForSourceAndTrigger(UUID.randomUUID().toString(), source, trigger); 14303 } 14304 createAggregateReportForSourceAndTrigger( String reportId, Source source, Trigger trigger)14305 private AggregateReport createAggregateReportForSourceAndTrigger( 14306 String reportId, Source source, Trigger trigger) { 14307 return AggregateReportFixture.getValidAggregateReportBuilder() 14308 .setId(reportId) 14309 .setSourceId(source.getId()) 14310 .setTriggerId(trigger.getId()) 14311 .setTriggerTime(trigger.getTriggerTime()) 14312 .build(); 14313 } 14314 createEventReportForSourceAndTrigger( String reportId, Source source, Trigger trigger)14315 private EventReport createEventReportForSourceAndTrigger( 14316 String reportId, Source source, Trigger trigger) throws JSONException { 14317 EventTrigger eventTrigger = trigger.parseEventTriggers(mFakeFlags).get(0); 14318 return new EventReport.Builder() 14319 .populateFromSourceAndTrigger( 14320 source, 14321 trigger, 14322 eventTrigger.getTriggerData(), 14323 eventTrigger, 14324 new Pair<>(null, null), 14325 new EventReportWindowCalcDelegate(mLegacyFlags), 14326 new SourceNoiseHandler(mLegacyFlags), 14327 source.getAttributionDestinations(trigger.getDestinationType())) 14328 .setId(reportId) 14329 .setSourceEventId(source.getEventId()) 14330 .setSourceId(source.getId()) 14331 .setTriggerId(trigger.getId()) 14332 .build(); 14333 } 14334 createEventReportForSourceAndTriggerForUninstall( String reportId, Source source, Trigger trigger)14335 private EventReport createEventReportForSourceAndTriggerForUninstall( 14336 String reportId, Source source, Trigger trigger) throws JSONException { 14337 EventTrigger eventTrigger = trigger.parseEventTriggers(mFakeFlags).get(0); 14338 return new EventReport.Builder() 14339 .setTriggerTime(trigger.getTriggerTime()) 14340 .setSourceEventId(source.getEventId()) 14341 .setEnrollmentId(source.getEnrollmentId()) 14342 .setStatus(EventReport.Status.PENDING) 14343 .setSourceType(source.getSourceType()) 14344 .setDebugReportStatus(EventReport.DebugReportStatus.NONE) 14345 .setRegistrationOrigin(trigger.getRegistrationOrigin()) 14346 .setTriggerPriority(eventTrigger.getTriggerPriority()) 14347 .setTriggerData(eventTrigger.getTriggerData()) 14348 .setId(reportId) 14349 .setAttributionDestinations( 14350 source.getAttributionDestinations(trigger.getDestinationType())) 14351 .setSourceId(source.getId()) 14352 .setTriggerId(trigger.getId()) 14353 .build(); 14354 } 14355 createDebugReport()14356 private DebugReport createDebugReport() { 14357 return createDebugReport(UUID.randomUUID().toString(), REGISTRANT, INSERTION_TIME); 14358 } 14359 createDebugReport(String id, Uri registrant, long insertionTime)14360 private DebugReport createDebugReport(String id, Uri registrant, long insertionTime) { 14361 return new DebugReport.Builder() 14362 .setId(id) 14363 .setType("trigger-event-deduplicated") 14364 .setBody( 14365 " {\n" 14366 + " \"attribution_destination\":" 14367 + " \"https://destination.example\",\n" 14368 + " \"source_event_id\": \"45623\"\n" 14369 + " }") 14370 .setEnrollmentId("1") 14371 .setRegistrationOrigin(REGISTRATION_ORIGIN) 14372 .setRegistrant(registrant) 14373 .setInsertionTime(insertionTime) 14374 .build(); 14375 } 14376 buildDebugReportWithInstalledRegistrant(String id)14377 private DebugReport buildDebugReportWithInstalledRegistrant(String id) { 14378 return new DebugReport.Builder() 14379 .setId(id) 14380 .setType("trigger-event-deduplicated") 14381 .setBody( 14382 " {\n" 14383 + " \"attribution_destination\":" 14384 + " \"https://destination.example\",\n" 14385 + " \"source_event_id\": \"45623\"\n" 14386 + " }") 14387 .setEnrollmentId("1") 14388 .setRegistrationOrigin(REGISTRATION_ORIGIN) 14389 .setRegistrant(INSTALLED_REGISTRANT) 14390 .setInsertionTime(INSERTION_TIME) 14391 .build(); 14392 } 14393 buildDebugReportWithNotInstalledRegistrant(String id)14394 private DebugReport buildDebugReportWithNotInstalledRegistrant(String id) { 14395 return new DebugReport.Builder() 14396 .setId(id) 14397 .setType("trigger-event-deduplicated") 14398 .setBody( 14399 " {\n" 14400 + " \"attribution_destination\":" 14401 + " \"https://destination.example\",\n" 14402 + " \"source_event_id\": \"45623\"\n" 14403 + " }") 14404 .setEnrollmentId("1") 14405 .setRegistrationOrigin(REGISTRATION_ORIGIN) 14406 .setRegistrant(NOT_INSTALLED_REGISTRANT) 14407 .setInsertionTime(INSERTION_TIME) 14408 .build(); 14409 } 14410 buildRegistrant(String appName)14411 private Uri buildRegistrant(String appName) { 14412 return Uri.parse("android-app://" + appName); 14413 } 14414 insertAsyncRecordForPackageName(Uri... registrants)14415 private void insertAsyncRecordForPackageName(Uri... registrants) { 14416 for (Uri registrant : registrants) { 14417 AsyncRegistration validRecord = 14418 AsyncRegistrationFixture.getValidAsyncRegistrationBuilder() 14419 .setRegistrant(registrant) 14420 .build(); 14421 14422 mDatastoreManager.runInTransaction((dao) -> dao.insertAsyncRegistration(validRecord)); 14423 } 14424 } 14425 insertSourceForPackageName(Uri... registrants)14426 private void insertSourceForPackageName(Uri... registrants) { 14427 for (Uri registrant : registrants) { 14428 Source validSource = 14429 SourceFixture.getValidSourceBuilder().setRegistrant(registrant).build(); 14430 14431 mDatastoreManager.runInTransaction((dao) -> dao.insertSource(validSource)); 14432 } 14433 } 14434 insertSourceForAttributionScope( List<String> attributionScopes, Long attributionScopeLimit, Long maxEventStates, long eventTime, List<Uri> webDestinations, List<Uri> appDestinations)14435 private Source insertSourceForAttributionScope( 14436 List<String> attributionScopes, 14437 Long attributionScopeLimit, 14438 Long maxEventStates, 14439 long eventTime, 14440 List<Uri> webDestinations, 14441 List<Uri> appDestinations) { 14442 return insertSourceForAttributionScope( 14443 attributionScopes, 14444 attributionScopeLimit, 14445 maxEventStates, 14446 eventTime, 14447 webDestinations, 14448 appDestinations, 14449 SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN); 14450 } 14451 insertSourceForAttributionScope( List<String> attributionScopes, Long attributionScopeLimit, Long maxEventStates, long eventTime, List<Uri> webDestinations, List<Uri> appDestinations, @NonNull Uri reportingOrigin)14452 private Source insertSourceForAttributionScope( 14453 List<String> attributionScopes, 14454 Long attributionScopeLimit, 14455 Long maxEventStates, 14456 long eventTime, 14457 List<Uri> webDestinations, 14458 List<Uri> appDestinations, 14459 @NonNull Uri reportingOrigin) { 14460 return insertSourceForAttributionScope( 14461 attributionScopes, 14462 attributionScopeLimit, 14463 maxEventStates, 14464 eventTime, 14465 webDestinations, 14466 appDestinations, 14467 reportingOrigin, 14468 SourceFixture.ValidSourceParams.REGISTRATION_ID, 14469 Source.SourceType.EVENT, 14470 Source.Status.ACTIVE); 14471 } 14472 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)14473 private Source insertSourceForAttributionScope( 14474 List<String> attributionScopes, 14475 Long attributionScopeLimit, 14476 Long maxEventStates, 14477 long eventTime, 14478 List<Uri> webDestinations, 14479 List<Uri> appDestinations, 14480 @NonNull Uri reportingOrigin, 14481 String registrationId, 14482 Source.SourceType sourceType, 14483 @Source.Status int status) { 14484 Source validSource = 14485 SourceFixture.getValidSourceBuilder() 14486 .setEventTime(eventTime) 14487 .setAttributionScopeLimit(attributionScopeLimit) 14488 .setMaxEventStates(maxEventStates) 14489 .setWebDestinations(webDestinations) 14490 .setAppDestinations(appDestinations) 14491 .setAttributionScopes(attributionScopes) 14492 .setRegistrationOrigin(reportingOrigin) 14493 .setSourceType(sourceType) 14494 .setStatus(status) 14495 .setRegistrationId(registrationId) 14496 .build(); 14497 14498 mDatastoreManager.runInTransaction( 14499 (dao) -> { 14500 dao.insertSource(validSource); 14501 Source insertedSource = dao.getSource(validSource.getId()); 14502 boolean attributionScopeEnabled = 14503 mLegacyFlags.getMeasurementEnableAttributionScope(); 14504 assertThat(insertedSource.getMaxEventStates()) 14505 .isEqualTo(attributionScopeEnabled ? maxEventStates : null); 14506 assertThat(insertedSource.getAttributionScopeLimit()) 14507 .isEqualTo(attributionScopeEnabled ? attributionScopeLimit : null); 14508 assertThat(dao.getSourceAttributionScopes(validSource.getId())) 14509 .containsExactlyElementsIn( 14510 (!attributionScopeEnabled || attributionScopes == null) 14511 ? List.of() 14512 : attributionScopes); 14513 }); 14514 return validSource; 14515 } 14516 insertSourceForAggregatableNamedBudgets( AggregatableNamedBudgets aggregatableNamedBudgets, long eventTime, List<Uri> webDestinations, List<Uri> appDestinations)14517 private Source insertSourceForAggregatableNamedBudgets( 14518 AggregatableNamedBudgets aggregatableNamedBudgets, 14519 long eventTime, 14520 List<Uri> webDestinations, 14521 List<Uri> appDestinations) { 14522 Source validSource = 14523 SourceFixture.getValidSourceBuilder() 14524 .setEventTime(eventTime) 14525 .setWebDestinations(webDestinations) 14526 .setAppDestinations(appDestinations) 14527 .setRegistrationOrigin(SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN) 14528 .setSourceType(Source.SourceType.EVENT) 14529 .setStatus(Source.Status.ACTIVE) 14530 .setRegistrationId(SourceFixture.ValidSourceParams.REGISTRATION_ID) 14531 .setAggregatableNamedBudgets(aggregatableNamedBudgets) 14532 .build(); 14533 14534 mDatastoreManager.runInTransaction((dao) -> dao.insertSource(validSource)); 14535 return validSource; 14536 } 14537 insertSourceForPopulatingAggregatableNamedBudgets( long eventTime, List<Uri> webDestinations, List<Uri> appDestinations)14538 private Source insertSourceForPopulatingAggregatableNamedBudgets( 14539 long eventTime, List<Uri> webDestinations, List<Uri> appDestinations) { 14540 Source validSource = 14541 SourceFixture.getValidSourceBuilder() 14542 .setEventTime(eventTime) 14543 .setWebDestinations(webDestinations) 14544 .setAppDestinations(appDestinations) 14545 .setRegistrationOrigin(SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN) 14546 .setSourceType(Source.SourceType.EVENT) 14547 .setStatus(Source.Status.ACTIVE) 14548 .setRegistrationId(SourceFixture.ValidSourceParams.REGISTRATION_ID) 14549 .build(); 14550 14551 mDatastoreManager.runInTransaction((dao) -> dao.insertSource(validSource)); 14552 return validSource; 14553 } 14554 insertTriggerForPackageName(Uri... registrants)14555 private void insertTriggerForPackageName(Uri... registrants) { 14556 for (Uri registrant : registrants) { 14557 Trigger validTrigger = 14558 TriggerFixture.getValidTriggerBuilder() 14559 .setRegistrant(registrant) 14560 .setId(UUID.randomUUID().toString()) 14561 .build(); 14562 14563 mDatastoreManager.runInTransaction((dao) -> dao.insertTrigger(validTrigger)); 14564 } 14565 } 14566 setupSourceAndTriggerData()14567 private void setupSourceAndTriggerData() { 14568 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 14569 List<Source> sourcesList = new ArrayList<>(); 14570 sourcesList.add( 14571 SourceFixture.getMinimalValidSourceBuilder() 14572 .setId("S1") 14573 .setRegistrant(APP_TWO_SOURCES) 14574 .setPublisher(APP_TWO_PUBLISHER) 14575 .setPublisherType(EventSurfaceType.APP) 14576 .build()); 14577 sourcesList.add( 14578 SourceFixture.getMinimalValidSourceBuilder() 14579 .setId("S2") 14580 .setRegistrant(APP_TWO_SOURCES) 14581 .setPublisher(APP_TWO_PUBLISHER) 14582 .setPublisherType(EventSurfaceType.APP) 14583 .build()); 14584 sourcesList.add( 14585 SourceFixture.getMinimalValidSourceBuilder() 14586 .setId("S3") 14587 .setRegistrant(APP_ONE_SOURCE) 14588 .setPublisher(APP_ONE_PUBLISHER) 14589 .setPublisherType(EventSurfaceType.APP) 14590 .build()); 14591 sourcesList.add( 14592 SourceFixture.getMinimalValidSourceBuilder() 14593 .setId("S4") 14594 .setRegistrant(APP_ONE_SOURCE) 14595 .setPublisher(APP_ONE_PUBLISHER) 14596 .setPublisherType(EventSurfaceType.APP) 14597 .setStatus(Source.Status.MARKED_TO_DELETE) 14598 .build()); 14599 sourcesList.add( 14600 SourceFixture.getMinimalValidSourceBuilder() 14601 .setId("S5") 14602 .setRegistrant(APP_TWO_SOURCES) 14603 .setPublisher(APP_TWO_PUBLISHER) 14604 .setPublisherType(EventSurfaceType.APP) 14605 .setStatus(Source.Status.MARKED_TO_DELETE) 14606 .build()); 14607 sourcesList.forEach(source -> insertSource(source, source.getId())); 14608 List<Trigger> triggersList = new ArrayList<>(); 14609 triggersList.add( 14610 TriggerFixture.getValidTriggerBuilder() 14611 .setId("T1") 14612 .setRegistrant(APP_TWO_DESTINATION) 14613 .build()); 14614 triggersList.add( 14615 TriggerFixture.getValidTriggerBuilder() 14616 .setId("T2") 14617 .setRegistrant(APP_TWO_DESTINATION) 14618 .build()); 14619 triggersList.add( 14620 TriggerFixture.getValidTriggerBuilder() 14621 .setId("T3") 14622 .setRegistrant(APP_ONE_DESTINATION) 14623 .build()); 14624 14625 // Add web triggers. 14626 triggersList.add( 14627 TriggerFixture.getValidTriggerBuilder() 14628 .setId("T4") 14629 .setRegistrant(APP_BROWSER) 14630 .setAttributionDestination(WEB_ONE_DESTINATION) 14631 .build()); 14632 triggersList.add( 14633 TriggerFixture.getValidTriggerBuilder() 14634 .setId("T5") 14635 .setRegistrant(APP_BROWSER) 14636 .setAttributionDestination(WEB_ONE_DESTINATION_DIFFERENT_SUBDOMAIN) 14637 .build()); 14638 triggersList.add( 14639 TriggerFixture.getValidTriggerBuilder() 14640 .setId("T7") 14641 .setRegistrant(APP_BROWSER) 14642 .setAttributionDestination(WEB_ONE_DESTINATION_DIFFERENT_SUBDOMAIN_2) 14643 .build()); 14644 triggersList.add( 14645 TriggerFixture.getValidTriggerBuilder() 14646 .setId("T8") 14647 .setRegistrant(APP_BROWSER) 14648 .setAttributionDestination(WEB_TWO_DESTINATION) 14649 .build()); 14650 triggersList.add( 14651 TriggerFixture.getValidTriggerBuilder() 14652 .setId("T9") 14653 .setRegistrant(APP_BROWSER) 14654 .setAttributionDestination(WEB_TWO_DESTINATION_WITH_PATH) 14655 .build()); 14656 14657 for (Trigger trigger : triggersList) { 14658 ContentValues values = new ContentValues(); 14659 values.put("_id", trigger.getId()); 14660 values.put("registrant", trigger.getRegistrant().toString()); 14661 values.put("attribution_destination", trigger.getAttributionDestination().toString()); 14662 long row = db.insert("msmt_trigger", null, values); 14663 assertNotEquals("Trigger insertion failed", -1, row); 14664 } 14665 } 14666 createWebTrigger(Uri attributionDestination)14667 private Trigger createWebTrigger(Uri attributionDestination) { 14668 return TriggerFixture.getValidTriggerBuilder() 14669 .setId("ID" + mValueId++) 14670 .setAttributionDestination(attributionDestination) 14671 .setRegistrant(APP_BROWSER) 14672 .build(); 14673 } 14674 createAppTrigger(Uri registrant, Uri destination)14675 private Trigger createAppTrigger(Uri registrant, Uri destination) { 14676 return TriggerFixture.getValidTriggerBuilder() 14677 .setId("ID" + mValueId++) 14678 .setAttributionDestination(destination) 14679 .setRegistrant(registrant) 14680 .build(); 14681 } 14682 addTriggersToDatabase(List<Trigger> triggersList)14683 private void addTriggersToDatabase(List<Trigger> triggersList) { 14684 SQLiteDatabase db = MeasurementDbHelper.getInstance().safeGetWritableDatabase(); 14685 14686 for (Trigger trigger : triggersList) { 14687 ContentValues values = new ContentValues(); 14688 values.put("_id", trigger.getId()); 14689 values.put("registrant", trigger.getRegistrant().toString()); 14690 values.put("attribution_destination", trigger.getAttributionDestination().toString()); 14691 long row = db.insert("msmt_trigger", null, values); 14692 assertNotEquals("Trigger insertion failed", -1, row); 14693 } 14694 } 14695 setupSourceDataForPublisherTypeWeb()14696 private void setupSourceDataForPublisherTypeWeb() { 14697 List<Source> sourcesList = new ArrayList<>(); 14698 sourcesList.add( 14699 SourceFixture.getMinimalValidSourceBuilder() 14700 .setId("W1") 14701 .setPublisher(WEB_PUBLISHER_ONE) 14702 .setPublisherType(EventSurfaceType.WEB) 14703 .build()); 14704 sourcesList.add( 14705 SourceFixture.getMinimalValidSourceBuilder() 14706 .setId("W21") 14707 .setPublisher(WEB_PUBLISHER_TWO) 14708 .setPublisherType(EventSurfaceType.WEB) 14709 .build()); 14710 sourcesList.add( 14711 SourceFixture.getMinimalValidSourceBuilder() 14712 .setId("W22") 14713 .setPublisher(WEB_PUBLISHER_TWO) 14714 .setPublisherType(EventSurfaceType.WEB) 14715 .build()); 14716 sourcesList.add( 14717 SourceFixture.getMinimalValidSourceBuilder() 14718 .setId("W23") 14719 .setPublisher(WEB_PUBLISHER_TWO) 14720 .setPublisherType(EventSurfaceType.WEB) 14721 .setStatus(Source.Status.MARKED_TO_DELETE) 14722 .build()); 14723 sourcesList.add( 14724 SourceFixture.getMinimalValidSourceBuilder() 14725 .setId("S3") 14726 .setPublisher(WEB_PUBLISHER_THREE) 14727 .setPublisherType(EventSurfaceType.WEB) 14728 .build()); 14729 sourcesList.forEach(source -> insertSource(source, source.getId())); 14730 } 14731 createSourceForIATest( String id, long currentTime, long priority, int eventTimePastDays, boolean expiredIAWindow, String enrollmentId)14732 private Source.Builder createSourceForIATest( 14733 String id, 14734 long currentTime, 14735 long priority, 14736 int eventTimePastDays, 14737 boolean expiredIAWindow, 14738 String enrollmentId) { 14739 return createSourceForIATest( 14740 id, 14741 currentTime, 14742 priority, 14743 eventTimePastDays, 14744 expiredIAWindow, 14745 enrollmentId, 14746 REGISTRATION_ORIGIN); 14747 } 14748 createSourceForIATest( String id, long currentTime, long priority, int eventTimePastDays, boolean expiredIAWindow, String enrollmentId, Uri registrationOrigin)14749 private Source.Builder createSourceForIATest( 14750 String id, 14751 long currentTime, 14752 long priority, 14753 int eventTimePastDays, 14754 boolean expiredIAWindow, 14755 String enrollmentId, 14756 Uri registrationOrigin) { 14757 return new Source.Builder() 14758 .setId(id) 14759 .setPublisher(Uri.parse("android-app://com.example.sample")) 14760 .setRegistrant(Uri.parse("android-app://com.example.sample")) 14761 .setEnrollmentId(enrollmentId) 14762 .setExpiryTime(currentTime + DAYS.toMillis(30)) 14763 .setInstallAttributionWindow(DAYS.toMillis(expiredIAWindow ? 0 : 30)) 14764 .setAppDestinations(List.of(INSTALLED_PACKAGE)) 14765 .setEventTime( 14766 currentTime 14767 - DAYS.toMillis(eventTimePastDays == -1 ? 10 : eventTimePastDays)) 14768 .setPriority(priority == -1 ? 100 : priority) 14769 .setRegistrationOrigin(registrationOrigin); 14770 } 14771 generateMockAggregateReport(String attributionDestination, int id)14772 private AggregateReport generateMockAggregateReport(String attributionDestination, int id) { 14773 return new AggregateReport.Builder() 14774 .setId(String.valueOf(id)) 14775 .setAttributionDestination(Uri.parse(attributionDestination)) 14776 .build(); 14777 } 14778 generateMockAggregateReportBuilder( String attributionDestination, int id, String sourceId, String api)14779 private AggregateReport.Builder generateMockAggregateReportBuilder( 14780 String attributionDestination, int id, String sourceId, String api) { 14781 return new AggregateReport.Builder() 14782 .setId(String.valueOf(id)) 14783 .setSourceId(sourceId) 14784 .setAttributionDestination(Uri.parse(attributionDestination)) 14785 .setApi(api); 14786 } 14787 generateMockAggregateReport( String attributionDestination, int id, String sourceId, String api)14788 private AggregateReport generateMockAggregateReport( 14789 String attributionDestination, int id, String sourceId, String api) { 14790 return generateMockAggregateReportBuilder( 14791 attributionDestination, id, sourceId, api).build(); 14792 } 14793 generateMockAggregateReport( String attributionDestination, int id, String sourceId, long reportTime)14794 private AggregateReport generateMockAggregateReport( 14795 String attributionDestination, int id, String sourceId, long reportTime) { 14796 return new AggregateReport.Builder() 14797 .setId(String.valueOf(id)) 14798 .setSourceId(sourceId) 14799 .setAttributionDestination(Uri.parse(attributionDestination)) 14800 .setScheduledReportTime(reportTime) 14801 .build(); 14802 } 14803 generateMockEventReport(String attributionDestination, int id)14804 private EventReport generateMockEventReport(String attributionDestination, int id) { 14805 return new EventReport.Builder() 14806 .setId(String.valueOf(id)) 14807 .setAttributionDestinations(List.of(Uri.parse(attributionDestination))) 14808 .build(); 14809 } 14810 generateMockEventReport( String attributionDestination, int id, long reportTime)14811 private EventReport generateMockEventReport( 14812 String attributionDestination, int id, long reportTime) { 14813 return new EventReport.Builder() 14814 .setId(String.valueOf(id)) 14815 .setAttributionDestinations(List.of(Uri.parse(attributionDestination))) 14816 .setReportTime(reportTime) 14817 .build(); 14818 } 14819 assertAggregateReportCount( List<String> attributionDestinations, int destinationType, List<Integer> expectedCounts)14820 private void assertAggregateReportCount( 14821 List<String> attributionDestinations, 14822 int destinationType, 14823 List<Integer> expectedCounts) { 14824 IntStream.range(0, attributionDestinations.size()) 14825 .forEach( 14826 i -> { 14827 DatastoreManager.ThrowingCheckedFunction<Integer> 14828 aggregateReportCountPerDestination = 14829 measurementDao -> 14830 measurementDao 14831 .getNumAggregateReportsPerDestination( 14832 Uri.parse( 14833 attributionDestinations 14834 .get(i)), 14835 destinationType); 14836 assertEquals( 14837 expectedCounts.get(i), 14838 mDatastoreManager 14839 .runInTransactionWithResult( 14840 aggregateReportCountPerDestination) 14841 .orElseThrow()); 14842 }); 14843 } 14844 assertEventReportCount( List<String> attributionDestinations, int destinationType, List<Integer> expectedCounts)14845 private void assertEventReportCount( 14846 List<String> attributionDestinations, 14847 int destinationType, 14848 List<Integer> expectedCounts) { 14849 IntStream.range(0, attributionDestinations.size()) 14850 .forEach( 14851 i -> { 14852 DatastoreManager.ThrowingCheckedFunction<Integer> 14853 numEventReportsPerDestination = 14854 measurementDao -> 14855 measurementDao.getNumEventReportsPerDestination( 14856 Uri.parse( 14857 attributionDestinations.get(i)), 14858 destinationType); 14859 assertEquals( 14860 expectedCounts.get(i), 14861 mDatastoreManager 14862 .runInTransactionWithResult( 14863 numEventReportsPerDestination) 14864 .orElseThrow()); 14865 }); 14866 } 14867 createAppDestinationVariants(int destinationNum)14868 private List<String> createAppDestinationVariants(int destinationNum) { 14869 return Arrays.asList( 14870 "android-app://subdomain.destination-" + destinationNum + ".app/abcd", 14871 "android-app://subdomain.destination-" + destinationNum + ".app", 14872 "android-app://destination-" + destinationNum + ".app/abcd", 14873 "android-app://destination-" + destinationNum + ".app", 14874 "android-app://destination-" + destinationNum + ".ap"); 14875 } 14876 createWebDestinationVariants(int destinationNum)14877 private List<String> createWebDestinationVariants(int destinationNum) { 14878 return Arrays.asList( 14879 "https://subdomain.destination-" + destinationNum + ".com/abcd", 14880 "https://subdomain.destination-" + destinationNum + ".com", 14881 "https://destination-" + destinationNum + ".com/abcd", 14882 "https://destination-" + destinationNum + ".com", 14883 "https://destination-" + destinationNum + ".co"); 14884 } 14885 getInstallAttributionStatus(String sourceDbId, SQLiteDatabase db)14886 private boolean getInstallAttributionStatus(String sourceDbId, SQLiteDatabase db) { 14887 Cursor cursor = 14888 db.query( 14889 SourceContract.TABLE, 14890 new String[] {SourceContract.IS_INSTALL_ATTRIBUTED}, 14891 SourceContract.ID + " = ? ", 14892 new String[] {sourceDbId}, 14893 null, 14894 null, 14895 null, 14896 null); 14897 assertTrue(cursor.moveToFirst()); 14898 return cursor.getInt(0) == 1; 14899 } 14900 getInstallAttributionInstallTime(String sourceDbId, SQLiteDatabase db)14901 private Long getInstallAttributionInstallTime(String sourceDbId, SQLiteDatabase db) { 14902 Cursor cursor = 14903 db.query( 14904 SourceContract.TABLE, 14905 new String[] {SourceContract.INSTALL_TIME}, 14906 SourceContract.ID + " = ? ", 14907 new String[] {sourceDbId}, 14908 null, 14909 null, 14910 null, 14911 null); 14912 assertTrue(cursor.moveToFirst()); 14913 if (!cursor.isNull(0)) { 14914 return cursor.getLong(0); 14915 } 14916 return null; 14917 } 14918 removeSources(List<String> dbIds, SQLiteDatabase db)14919 private void removeSources(List<String> dbIds, SQLiteDatabase db) { 14920 db.delete( 14921 SourceContract.TABLE, 14922 SourceContract.ID + " IN ( ? )", 14923 new String[] {String.join(",", dbIds)}); 14924 } 14925 maybeInsertSourceDestinations( SQLiteDatabase db, Source source, String sourceId)14926 private static void maybeInsertSourceDestinations( 14927 SQLiteDatabase db, Source source, String sourceId) { 14928 if (source.getAppDestinations() != null) { 14929 for (Uri appDestination : source.getAppDestinations()) { 14930 ContentValues values = new ContentValues(); 14931 values.put(MeasurementTables.SourceDestination.SOURCE_ID, sourceId); 14932 values.put( 14933 MeasurementTables.SourceDestination.DESTINATION_TYPE, EventSurfaceType.APP); 14934 values.put( 14935 MeasurementTables.SourceDestination.DESTINATION, appDestination.toString()); 14936 long row = db.insert(MeasurementTables.SourceDestination.TABLE, null, values); 14937 assertNotEquals("Source app destination insertion failed", -1, row); 14938 } 14939 } 14940 if (source.getWebDestinations() != null) { 14941 for (Uri webDestination : source.getWebDestinations()) { 14942 ContentValues values = new ContentValues(); 14943 values.put(MeasurementTables.SourceDestination.SOURCE_ID, sourceId); 14944 values.put( 14945 MeasurementTables.SourceDestination.DESTINATION_TYPE, EventSurfaceType.WEB); 14946 values.put( 14947 MeasurementTables.SourceDestination.DESTINATION, webDestination.toString()); 14948 long row = db.insert(MeasurementTables.SourceDestination.TABLE, null, values); 14949 assertNotEquals("Source web destination insertion failed", -1, row); 14950 } 14951 } 14952 } 14953 getAttributionBuilder(Source source, Trigger trigger)14954 private static Attribution.Builder getAttributionBuilder(Source source, Trigger trigger) { 14955 return new Attribution.Builder() 14956 .setEnrollmentId(source.getEnrollmentId()) 14957 .setDestinationOrigin(source.getWebDestinations().get(0).toString()) 14958 .setDestinationSite(source.getAppDestinations().get(0).toString()) 14959 .setSourceOrigin(source.getPublisher().toString()) 14960 .setSourceSite(source.getPublisher().toString()) 14961 .setRegistrant(source.getRegistrant().toString()) 14962 .setTriggerTime( 14963 trigger.getTriggerTime() - MEASUREMENT_RATE_LIMIT_WINDOW_MILLISECONDS + 1) 14964 .setRegistrationOrigin(trigger.getRegistrationOrigin()); 14965 } 14966 14967 /** Create {@link Attribution} object from SQLite datastore. */ constructAttributionFromCursor(Cursor cursor)14968 private static Attribution constructAttributionFromCursor(Cursor cursor) { 14969 Attribution.Builder builder = new Attribution.Builder(); 14970 int index = cursor.getColumnIndex(AttributionContract.ID); 14971 if (index > -1 && !cursor.isNull(index)) { 14972 builder.setId(cursor.getString(index)); 14973 } 14974 index = cursor.getColumnIndex(AttributionContract.SCOPE); 14975 if (index > -1 && !cursor.isNull(index)) { 14976 builder.setScope(cursor.getInt(index)); 14977 } 14978 index = cursor.getColumnIndex(AttributionContract.SOURCE_SITE); 14979 if (index > -1 && !cursor.isNull(index)) { 14980 builder.setSourceSite(cursor.getString(index)); 14981 } 14982 index = cursor.getColumnIndex(AttributionContract.SOURCE_ORIGIN); 14983 if (index > -1 && !cursor.isNull(index)) { 14984 builder.setSourceOrigin(cursor.getString(index)); 14985 } 14986 index = cursor.getColumnIndex(AttributionContract.DESTINATION_SITE); 14987 if (index > -1 && !cursor.isNull(index)) { 14988 builder.setDestinationSite(cursor.getString(index)); 14989 } 14990 index = cursor.getColumnIndex(AttributionContract.DESTINATION_ORIGIN); 14991 if (index > -1 && !cursor.isNull(index)) { 14992 builder.setDestinationOrigin(cursor.getString(index)); 14993 } 14994 index = cursor.getColumnIndex(AttributionContract.ENROLLMENT_ID); 14995 if (index > -1 && !cursor.isNull(index)) { 14996 builder.setEnrollmentId(cursor.getString(index)); 14997 } 14998 index = cursor.getColumnIndex(AttributionContract.TRIGGER_TIME); 14999 if (index > -1 && !cursor.isNull(index)) { 15000 builder.setTriggerTime(cursor.getLong(index)); 15001 } 15002 index = cursor.getColumnIndex(AttributionContract.REGISTRANT); 15003 if (index > -1 && !cursor.isNull(index)) { 15004 builder.setRegistrant(cursor.getString(index)); 15005 } 15006 index = cursor.getColumnIndex(AttributionContract.REGISTRATION_ORIGIN); 15007 if (index > -1 && !cursor.isNull(index)) { 15008 builder.setRegistrationOrigin(Uri.parse(cursor.getString(index))); 15009 } 15010 return builder.build(); 15011 } 15012 getAttribution(String attributionId, SQLiteDatabase db)15013 private Attribution getAttribution(String attributionId, SQLiteDatabase db) { 15014 try (Cursor cursor = 15015 db.query( 15016 AttributionContract.TABLE, 15017 /* columns= */ null, 15018 AttributionContract.ID + " = ? ", 15019 new String[] {attributionId}, 15020 /* groupBy= */ null, 15021 /* having= */ null, 15022 /* orderBy= */ null, 15023 /* limit= */ null)) { 15024 if (cursor.getCount() == 0) { 15025 return null; 15026 } 15027 cursor.moveToNext(); 15028 return constructAttributionFromCursor(cursor); 15029 } 15030 } 15031 insertAttributedTrigger(TriggerSpecs triggerSpecs, EventReport eventReport)15032 private static void insertAttributedTrigger(TriggerSpecs triggerSpecs, 15033 EventReport eventReport) { 15034 triggerSpecs.getAttributedTriggers().add( 15035 new AttributedTrigger( 15036 eventReport.getTriggerId(), 15037 eventReport.getTriggerPriority(), 15038 eventReport.getTriggerData(), 15039 eventReport.getTriggerValue(), 15040 eventReport.getTriggerTime(), 15041 eventReport.getTriggerDedupKey(), 15042 eventReport.getTriggerDebugKey(), 15043 false)); 15044 } 15045 insertAttributedTrigger(List<AttributedTrigger> attributedTriggers, EventReport eventReport)15046 private static void insertAttributedTrigger(List<AttributedTrigger> attributedTriggers, 15047 EventReport eventReport) { 15048 attributedTriggers.add( 15049 new AttributedTrigger( 15050 eventReport.getTriggerId(), 15051 eventReport.getTriggerData(), 15052 eventReport.getTriggerDedupKey())); 15053 } 15054 getRandomIdsWith(int numIds, String idToInclude)15055 private static List<String> getRandomIdsWith(int numIds, String idToInclude) { 15056 List<String> result = new ArrayList<>(); 15057 result.add(idToInclude); 15058 for (int i = 0; i < numIds; i++) { 15059 result.add(UUID.randomUUID().toString()); 15060 } 15061 return result; 15062 } 15063 getFirstSourceIdFromDatastore()15064 private static String getFirstSourceIdFromDatastore() { 15065 return getFirstIdFromDatastore(SourceContract.TABLE, SourceContract.ID); 15066 } 15067 getFirstIdFromDatastore(String tableName, String idColumn)15068 private static String getFirstIdFromDatastore(String tableName, String idColumn) { 15069 try (Cursor cursor = 15070 MeasurementDbHelper.getInstance() 15071 .getReadableDatabase() 15072 .query(tableName, new String[] {idColumn}, null, null, null, null, null)) { 15073 assertTrue(cursor.moveToNext()); 15074 return cursor.getString(cursor.getColumnIndex(idColumn)); 15075 } 15076 } 15077 getNullableUriList(List<Uri> uris)15078 private static List<Uri> getNullableUriList(List<Uri> uris) { 15079 return uris == null ? null : uris; 15080 } 15081 getMatchingSourceIds(Trigger trigger)15082 private List<String> getMatchingSourceIds(Trigger trigger) { 15083 List<Source> result = 15084 mDatastoreManager 15085 .runInTransactionWithResult( 15086 measurementDao -> measurementDao.getMatchingActiveSources(trigger)) 15087 .orElseThrow(); 15088 return result.stream().map(Source::getId).collect(Collectors.toList()); 15089 } 15090 getMatchingSources(Trigger trigger)15091 private List<Source> getMatchingSources(Trigger trigger) { 15092 return mDatastoreManager 15093 .runInTransactionWithResult( 15094 measurementDao -> measurementDao.getMatchingActiveSources(trigger)) 15095 .orElseThrow(); 15096 } 15097 createCountUniqueReport( String reportId, String payload, Uri reportingOrigin, int status, int debugStatus, Long scheduledReportTime, String version, String debugKey, String contextId, String enrollmentId, int contributionValue, long contributionTime)15098 private CountUniqueReport createCountUniqueReport( 15099 String reportId, 15100 String payload, 15101 Uri reportingOrigin, 15102 int status, 15103 int debugStatus, 15104 Long scheduledReportTime, 15105 String version, 15106 String debugKey, 15107 String contextId, 15108 String enrollmentId, 15109 int contributionValue, 15110 long contributionTime) { 15111 CountUniqueReport.Builder builder = new CountUniqueReport.Builder(); 15112 builder.setReportId(reportId); 15113 builder.setPayload(payload); 15114 builder.setReportingOrigin(reportingOrigin); 15115 builder.setStatus(status); 15116 builder.setDebugReportStatus(debugStatus); 15117 builder.setScheduledReportTime(scheduledReportTime); 15118 builder.setApiVersion(version); 15119 builder.setDebugKey(debugKey); 15120 builder.setContextId(contextId); 15121 builder.setEnrollmentId(enrollmentId); 15122 builder.setContributionValue(contributionValue); 15123 builder.setContributionTime(contributionTime); 15124 return builder.build(); 15125 } 15126 } 15127