1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.adservices.service.measurement.registration; 18 19 20 import android.content.ContentProviderClient; 21 import android.content.ContentResolver; 22 import android.content.Context; 23 import android.net.Uri; 24 import android.os.RemoteException; 25 26 import com.android.adservices.LogUtil; 27 import com.android.adservices.data.measurement.DatastoreException; 28 import com.android.adservices.data.measurement.DatastoreManager; 29 import com.android.adservices.data.measurement.DatastoreManagerFactory; 30 import com.android.adservices.data.measurement.IMeasurementDao; 31 import com.android.adservices.service.Flags; 32 import com.android.adservices.service.FlagsFactory; 33 import com.android.adservices.service.measurement.Attribution; 34 import com.android.adservices.service.measurement.EventReport; 35 import com.android.adservices.service.measurement.EventSurfaceType; 36 import com.android.adservices.service.measurement.KeyValueData; 37 import com.android.adservices.service.measurement.KeyValueData.DataType; 38 import com.android.adservices.service.measurement.PrivacyParams; 39 import com.android.adservices.service.measurement.Source; 40 import com.android.adservices.service.measurement.SystemHealthParams; 41 import com.android.adservices.service.measurement.Trigger; 42 import com.android.adservices.service.measurement.attribution.TriggerContentProvider; 43 import com.android.adservices.service.measurement.noising.SourceNoiseHandler; 44 import com.android.adservices.service.measurement.reporting.DebugReportApi; 45 import com.android.adservices.service.measurement.util.BaseUriExtractor; 46 import com.android.adservices.service.measurement.util.Web; 47 import com.android.adservices.service.stats.AdServicesLoggerImpl; 48 import com.android.internal.annotations.VisibleForTesting; 49 50 import java.util.HashSet; 51 import java.util.List; 52 import java.util.Objects; 53 import java.util.Optional; 54 import java.util.Set; 55 import java.util.UUID; 56 import java.util.stream.Collectors; 57 58 /** Runner for servicing queued registration requests */ 59 public class AsyncRegistrationQueueRunner { 60 private static AsyncRegistrationQueueRunner sAsyncRegistrationQueueRunner; 61 private final DatastoreManager mDatastoreManager; 62 private final AsyncSourceFetcher mAsyncSourceFetcher; 63 private final AsyncTriggerFetcher mAsyncTriggerFetcher; 64 private final ContentResolver mContentResolver; 65 private final DebugReportApi mDebugReportApi; 66 private final SourceNoiseHandler mSourceNoiseHandler; 67 AsyncRegistrationQueueRunner(Context context)68 private AsyncRegistrationQueueRunner(Context context) { 69 mDatastoreManager = DatastoreManagerFactory.getDatastoreManager(context); 70 mAsyncSourceFetcher = new AsyncSourceFetcher(context); 71 mAsyncTriggerFetcher = new AsyncTriggerFetcher(context); 72 mContentResolver = context.getContentResolver(); 73 mDebugReportApi = new DebugReportApi(context, FlagsFactory.getFlags()); 74 mSourceNoiseHandler = new SourceNoiseHandler(FlagsFactory.getFlags()); 75 } 76 77 @VisibleForTesting AsyncRegistrationQueueRunner( ContentResolver contentResolver, AsyncSourceFetcher asyncSourceFetcher, AsyncTriggerFetcher asyncTriggerFetcher, DatastoreManager datastoreManager, DebugReportApi debugReportApi, SourceNoiseHandler sourceNoiseHandler)78 public AsyncRegistrationQueueRunner( 79 ContentResolver contentResolver, 80 AsyncSourceFetcher asyncSourceFetcher, 81 AsyncTriggerFetcher asyncTriggerFetcher, 82 DatastoreManager datastoreManager, 83 DebugReportApi debugReportApi, 84 SourceNoiseHandler sourceNoiseHandler) { 85 mAsyncSourceFetcher = asyncSourceFetcher; 86 mAsyncTriggerFetcher = asyncTriggerFetcher; 87 mDatastoreManager = datastoreManager; 88 mContentResolver = contentResolver; 89 mDebugReportApi = debugReportApi; 90 mSourceNoiseHandler = sourceNoiseHandler; 91 } 92 93 /** 94 * Returns an instance of AsyncRegistrationQueueRunner. 95 * 96 * @param context the current {@link Context}. 97 */ getInstance(Context context)98 public static synchronized AsyncRegistrationQueueRunner getInstance(Context context) { 99 Objects.requireNonNull(context); 100 if (sAsyncRegistrationQueueRunner == null) { 101 sAsyncRegistrationQueueRunner = new AsyncRegistrationQueueRunner(context); 102 } 103 return sAsyncRegistrationQueueRunner; 104 } 105 106 /** Processes records in the AsyncRegistration Queue table. */ runAsyncRegistrationQueueWorker()107 public void runAsyncRegistrationQueueWorker() { 108 Flags flags = FlagsFactory.getFlags(); 109 int recordServiceLimit = flags.getMeasurementMaxRegistrationsPerJobInvocation(); 110 int retryLimit = flags.getMeasurementMaxRetriesPerRegistrationRequest(); 111 112 Set<Uri> failedOrigins = new HashSet<>(); 113 for (int i = 0; i < recordServiceLimit; i++) { 114 Optional<AsyncRegistration> optAsyncRegistration = 115 mDatastoreManager.runInTransactionWithResult( 116 (dao) -> 117 dao.fetchNextQueuedAsyncRegistration( 118 retryLimit, failedOrigins)); 119 120 AsyncRegistration asyncRegistration; 121 if (optAsyncRegistration.isPresent()) { 122 asyncRegistration = optAsyncRegistration.get(); 123 } else { 124 LogUtil.d("AsyncRegistrationQueueRunner: no async registration fetched."); 125 return; 126 } 127 128 if (asyncRegistration.isSourceRequest()) { 129 LogUtil.d("AsyncRegistrationQueueRunner:" + " processing source"); 130 processSourceRegistration(asyncRegistration, failedOrigins); 131 } else { 132 LogUtil.d("AsyncRegistrationQueueRunner:" + " processing trigger"); 133 processTriggerRegistration(asyncRegistration, failedOrigins); 134 } 135 } 136 } 137 processSourceRegistration( AsyncRegistration asyncRegistration, Set<Uri> failedOrigins)138 private void processSourceRegistration( 139 AsyncRegistration asyncRegistration, Set<Uri> failedOrigins) { 140 AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); 141 AsyncRedirect asyncRedirect = new AsyncRedirect(); 142 long startTime = asyncRegistration.getRequestTime(); 143 Optional<Source> resultSource = 144 mAsyncSourceFetcher.fetchSource(asyncRegistration, asyncFetchStatus, asyncRedirect); 145 long endTime = System.currentTimeMillis(); 146 asyncFetchStatus.setRegistrationDelay(endTime - startTime); 147 148 boolean transactionResult = 149 mDatastoreManager.runInTransaction( 150 (dao) -> { 151 if (asyncFetchStatus.isRequestSuccess()) { 152 if (resultSource.isPresent()) { 153 storeSource(resultSource.get(), asyncRegistration, dao); 154 } 155 handleSuccess( 156 asyncRegistration, asyncFetchStatus, asyncRedirect, dao); 157 } else { 158 handleFailure( 159 asyncRegistration, asyncFetchStatus, failedOrigins, dao); 160 } 161 }); 162 163 if (!transactionResult) { 164 asyncFetchStatus.setEntityStatus(AsyncFetchStatus.EntityStatus.STORAGE_ERROR); 165 } 166 167 FetcherUtil.emitHeaderMetrics( 168 FlagsFactory.getFlags(), 169 AdServicesLoggerImpl.getInstance(), 170 asyncRegistration, 171 asyncFetchStatus); 172 } 173 174 /** Visible only for testing. */ 175 @VisibleForTesting storeSource( Source source, AsyncRegistration asyncRegistration, IMeasurementDao dao)176 public void storeSource( 177 Source source, AsyncRegistration asyncRegistration, IMeasurementDao dao) 178 throws DatastoreException { 179 Uri topOrigin = 180 asyncRegistration.getType() == AsyncRegistration.RegistrationType.WEB_SOURCE 181 ? asyncRegistration.getTopOrigin() 182 : getPublisher(asyncRegistration); 183 @EventSurfaceType 184 int publisherType = 185 asyncRegistration.getType() == AsyncRegistration.RegistrationType.WEB_SOURCE 186 ? EventSurfaceType.WEB 187 : EventSurfaceType.APP; 188 if (isSourceAllowedToInsert(source, topOrigin, publisherType, dao, mDebugReportApi)) { 189 insertSourceFromTransaction(source, dao); 190 mDebugReportApi.scheduleSourceSuccessDebugReport(source, dao); 191 } 192 } 193 processTriggerRegistration( AsyncRegistration asyncRegistration, Set<Uri> failedOrigins)194 private void processTriggerRegistration( 195 AsyncRegistration asyncRegistration, Set<Uri> failedOrigins) { 196 AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); 197 AsyncRedirect asyncRedirect = new AsyncRedirect(); 198 long startTime = asyncRegistration.getRequestTime(); 199 Optional<Trigger> resultTrigger = mAsyncTriggerFetcher.fetchTrigger( 200 asyncRegistration, asyncFetchStatus, asyncRedirect); 201 long endTime = System.currentTimeMillis(); 202 asyncFetchStatus.setRegistrationDelay(endTime - startTime); 203 204 boolean transactionResult = 205 mDatastoreManager.runInTransaction( 206 (dao) -> { 207 if (asyncFetchStatus.isRequestSuccess()) { 208 if (resultTrigger.isPresent()) { 209 storeTrigger(resultTrigger.get(), dao); 210 } 211 handleSuccess( 212 asyncRegistration, asyncFetchStatus, asyncRedirect, dao); 213 } else { 214 handleFailure( 215 asyncRegistration, asyncFetchStatus, failedOrigins, dao); 216 } 217 }); 218 219 if (!transactionResult) { 220 asyncFetchStatus.setEntityStatus(AsyncFetchStatus.EntityStatus.STORAGE_ERROR); 221 } 222 223 FetcherUtil.emitHeaderMetrics( 224 FlagsFactory.getFlags(), 225 AdServicesLoggerImpl.getInstance(), 226 asyncRegistration, 227 asyncFetchStatus); 228 } 229 230 /** Visible only for testing. */ 231 @VisibleForTesting storeTrigger(Trigger trigger, IMeasurementDao dao)232 public void storeTrigger(Trigger trigger, IMeasurementDao dao) throws DatastoreException { 233 if (isTriggerAllowedToInsert(dao, trigger)) { 234 try { 235 dao.insertTrigger(trigger); 236 } catch (DatastoreException e) { 237 mDebugReportApi.scheduleTriggerNoMatchingSourceDebugReport( 238 trigger, dao, DebugReportApi.Type.TRIGGER_UNKNOWN_ERROR); 239 LogUtil.e(e, "Insert trigger to DB error, generate trigger-unknown-error report"); 240 throw new DatastoreException( 241 "Insert trigger to DB error, generate trigger-unknown-error report"); 242 } 243 notifyTriggerContentProvider(); 244 } 245 } 246 247 /** Visible only for testing. */ 248 @VisibleForTesting isSourceAllowedToInsert( Source source, Uri topOrigin, @EventSurfaceType int publisherType, IMeasurementDao dao, DebugReportApi debugReportApi)249 public static boolean isSourceAllowedToInsert( 250 Source source, 251 Uri topOrigin, 252 @EventSurfaceType int publisherType, 253 IMeasurementDao dao, 254 DebugReportApi debugReportApi) 255 throws DatastoreException { 256 long windowStartTime = source.getEventTime() - PrivacyParams.RATE_LIMIT_WINDOW_MILLISECONDS; 257 Optional<Uri> publisher = getTopLevelPublisher(topOrigin, publisherType); 258 if (!publisher.isPresent()) { 259 LogUtil.d("insertSources: getTopLevelPublisher failed", topOrigin); 260 return false; 261 } 262 long numOfSourcesPerPublisher = 263 dao.getNumSourcesPerPublisher( 264 BaseUriExtractor.getBaseUri(topOrigin), publisherType); 265 266 if (numOfSourcesPerPublisher >= SystemHealthParams.getMaxSourcesPerPublisher()) { 267 LogUtil.d( 268 "insertSources: Max limit of %s sources for publisher - %s reached.", 269 SystemHealthParams.getMaxSourcesPerPublisher(), publisher); 270 debugReportApi.scheduleSourceStorageLimitDebugReport( 271 source, String.valueOf(numOfSourcesPerPublisher), dao); 272 return false; 273 } 274 int numOfOriginExcludingRegistrationOrigin = 275 dao.countSourcesPerPublisherXEnrollmentExcludingRegOrigin( 276 source.getRegistrationOrigin(), 277 publisher.get(), 278 publisherType, 279 source.getEnrollmentId(), 280 source.getEventTime(), 281 PrivacyParams.MIN_REPORTING_ORIGIN_UPDATE_WINDOW); 282 if (numOfOriginExcludingRegistrationOrigin > 0) { 283 LogUtil.d( 284 "insertSources: Max limit of 1 reporting origin for publisher - %s and" 285 + " enrollment - %s reached.", 286 publisher, source.getEnrollmentId()); 287 return false; 288 } 289 if (source.getAppDestinations() != null 290 && !isDestinationWithinBounds( 291 debugReportApi, 292 source, 293 publisher.get(), 294 publisherType, 295 source.getEnrollmentId(), 296 source.getAppDestinations(), 297 EventSurfaceType.APP, 298 windowStartTime, 299 source.getEventTime(), 300 dao)) { 301 return false; 302 } 303 304 if (source.getWebDestinations() != null 305 && !isDestinationWithinBounds( 306 debugReportApi, 307 source, 308 publisher.get(), 309 publisherType, 310 source.getEnrollmentId(), 311 source.getWebDestinations(), 312 EventSurfaceType.WEB, 313 windowStartTime, 314 source.getEventTime(), 315 dao)) { 316 return false; 317 } 318 return true; 319 } 320 isDestinationWithinBounds( DebugReportApi debugReportApi, Source source, Uri publisher, @EventSurfaceType int publisherType, String enrollmentId, List<Uri> destinations, @EventSurfaceType int destinationType, long windowStartTime, long requestTime, IMeasurementDao dao)321 private static boolean isDestinationWithinBounds( 322 DebugReportApi debugReportApi, 323 Source source, 324 Uri publisher, 325 @EventSurfaceType int publisherType, 326 String enrollmentId, 327 List<Uri> destinations, 328 @EventSurfaceType int destinationType, 329 long windowStartTime, 330 long requestTime, 331 IMeasurementDao dao) 332 throws DatastoreException { 333 int destinationCount = 334 dao.countDistinctDestinationsPerPublisherXEnrollmentInActiveSource( 335 publisher, 336 publisherType, 337 enrollmentId, 338 destinations, 339 destinationType, 340 windowStartTime, 341 requestTime); 342 int maxDistinctDestinations = 343 FlagsFactory.getFlags().getMeasurementMaxDistinctDestinationsInActiveSource(); 344 if (destinationCount + destinations.size() > maxDistinctDestinations) { 345 LogUtil.d( 346 "AsyncRegistrationQueueRunner: " 347 + (destinationType == EventSurfaceType.APP ? "App" : "Web") 348 + " destination count >= " 349 + "MaxDistinctDestinationsPerPublisherXEnrollmentInActiveSource"); 350 debugReportApi.scheduleSourceDestinationLimitDebugReport( 351 source, String.valueOf(maxDistinctDestinations), dao); 352 return false; 353 } 354 355 int destinationEnrollmentCount = 356 dao.countDistinctEnrollmentsPerPublisherXDestinationInSource( 357 publisher, 358 publisherType, 359 destinations, 360 enrollmentId, 361 windowStartTime, 362 requestTime); 363 if (destinationEnrollmentCount 364 >= PrivacyParams.getMaxDistinctEnrollmentsPerPublisherXDestinationInSource()) { 365 debugReportApi.scheduleSourceSuccessDebugReport(source, dao); 366 LogUtil.d( 367 "AsyncRegistrationQueueRunner: " 368 + (destinationType == EventSurfaceType.APP ? "App" : "Web") 369 + " enrollment count >= " 370 + "MaxDistinctEnrollmentsPerPublisherXDestinationInSource"); 371 return false; 372 } 373 return true; 374 } 375 376 @VisibleForTesting isTriggerAllowedToInsert(IMeasurementDao dao, Trigger trigger)377 static boolean isTriggerAllowedToInsert(IMeasurementDao dao, Trigger trigger) { 378 long triggerInsertedPerDestination; 379 try { 380 triggerInsertedPerDestination = 381 dao.getNumTriggersPerDestination( 382 trigger.getAttributionDestination(), trigger.getDestinationType()); 383 } catch (DatastoreException e) { 384 LogUtil.e("Unable to fetch number of triggers currently registered per destination."); 385 return false; 386 } 387 388 return triggerInsertedPerDestination < SystemHealthParams.getMaxTriggersPerDestination(); 389 } 390 createAsyncRegistrationFromRedirect( AsyncRegistration asyncRegistration, Uri redirectUri)391 private static AsyncRegistration createAsyncRegistrationFromRedirect( 392 AsyncRegistration asyncRegistration, Uri redirectUri) { 393 return new AsyncRegistration.Builder() 394 .setId(UUID.randomUUID().toString()) 395 .setRegistrationUri(redirectUri) 396 .setWebDestination(asyncRegistration.getWebDestination()) 397 .setOsDestination(asyncRegistration.getOsDestination()) 398 .setRegistrant(asyncRegistration.getRegistrant()) 399 .setVerifiedDestination(asyncRegistration.getVerifiedDestination()) 400 .setTopOrigin(asyncRegistration.getTopOrigin()) 401 .setType(asyncRegistration.getType()) 402 .setSourceType(asyncRegistration.getSourceType()) 403 .setRequestTime(asyncRegistration.getRequestTime()) 404 .setRetryCount(0) 405 .setDebugKeyAllowed(asyncRegistration.getDebugKeyAllowed()) 406 .setAdIdPermission(asyncRegistration.hasAdIdPermission()) 407 .setRegistrationId(asyncRegistration.getRegistrationId()) 408 .build(); 409 } 410 generateFakeEventReports(Source source)411 private List<EventReport> generateFakeEventReports(Source source) { 412 List<Source.FakeReport> fakeReports = 413 mSourceNoiseHandler.assignAttributionModeAndGenerateFakeReports(source); 414 return fakeReports.stream() 415 .map( 416 fakeReport -> 417 new EventReport.Builder() 418 .setSourceEventId(source.getEventId()) 419 .setReportTime(fakeReport.getReportingTime()) 420 .setTriggerData(fakeReport.getTriggerData()) 421 .setAttributionDestinations(fakeReport.getDestinations()) 422 .setEnrollmentId(source.getEnrollmentId()) 423 // The query for attribution check is from 424 // (triggerTime - 30 days) to triggerTime and max expiry is 425 // 30 days, so it's safe to choose triggerTime as source 426 // event time so that it gets considered when the query is 427 // fired for attribution rate limit check. 428 .setTriggerTime(source.getEventTime()) 429 .setTriggerPriority(0L) 430 .setTriggerDedupKey(null) 431 .setSourceType(source.getSourceType()) 432 .setStatus(EventReport.Status.PENDING) 433 .setRandomizedTriggerRate( 434 mSourceNoiseHandler.getRandomAttributionProbability( 435 source)) 436 .setRegistrationOrigin(source.getRegistrationOrigin()) 437 .build()) 438 .collect(Collectors.toList()); 439 } 440 441 @VisibleForTesting insertSourceFromTransaction(Source source, IMeasurementDao dao)442 void insertSourceFromTransaction(Source source, IMeasurementDao dao) throws DatastoreException { 443 List<EventReport> eventReports = generateFakeEventReports(source); 444 if (!eventReports.isEmpty()) { 445 mDebugReportApi.scheduleSourceNoisedDebugReport(source, dao); 446 } 447 try { 448 dao.insertSource(source); 449 } catch (DatastoreException e) { 450 mDebugReportApi.scheduleSourceUnknownErrorDebugReport(source, dao); 451 LogUtil.e(e, "Insert source to DB error, generate source-unknown-error report"); 452 throw new DatastoreException( 453 "Insert source to DB error, generate source-unknown-error report"); 454 } 455 for (EventReport report : eventReports) { 456 dao.insertEventReport(report); 457 } 458 // We want to account for attribution if fake report generation was considered 459 // based on the probability. In that case the attribution mode will be NEVER 460 // (empty fake reports state) or FALSELY (non-empty fake reports). 461 if (source.getAttributionMode() != Source.AttributionMode.TRUTHFULLY) { 462 // Attribution rate limits for app and web destinations are counted 463 // separately, so add a fake report entry for each type of destination if 464 // non-null. 465 if (!Objects.isNull(source.getAppDestinations())) { 466 for (Uri destination : source.getAppDestinations()) { 467 dao.insertAttribution(createFakeAttributionRateLimit(source, destination)); 468 } 469 } 470 471 if (!Objects.isNull(source.getWebDestinations())) { 472 for (Uri destination : source.getWebDestinations()) { 473 dao.insertAttribution(createFakeAttributionRateLimit(source, destination)); 474 } 475 } 476 } 477 } 478 handleSuccess( AsyncRegistration asyncRegistration, AsyncFetchStatus asyncFetchStatus, AsyncRedirect asyncRedirect, IMeasurementDao dao)479 private void handleSuccess( 480 AsyncRegistration asyncRegistration, 481 AsyncFetchStatus asyncFetchStatus, 482 AsyncRedirect asyncRedirect, 483 IMeasurementDao dao) 484 throws DatastoreException { 485 // deleteAsyncRegistration will throw an exception & rollback the transaction if the record 486 // is already deleted. This can happen if both fallback & regular job are running at the 487 // same time or if deletion job deletes the records. 488 dao.deleteAsyncRegistration(asyncRegistration.getId()); 489 if (asyncRedirect.getRedirects().isEmpty()) { 490 return; 491 } 492 int maxRedirects = FlagsFactory.getFlags().getMeasurementMaxRegistrationRedirects(); 493 KeyValueData keyValueData = 494 dao.getKeyValueData( 495 asyncRegistration.getRegistrationId(), 496 DataType.REGISTRATION_REDIRECT_COUNT); 497 int currentCount = keyValueData.getRegistrationRedirectCount(); 498 if (currentCount == maxRedirects) { 499 asyncFetchStatus.setRedirectError(true); 500 return; 501 } 502 for (Uri uri : asyncRedirect.getRedirects()) { 503 if (currentCount >= maxRedirects) { 504 break; 505 } 506 dao.insertAsyncRegistration( 507 createAsyncRegistrationFromRedirect(asyncRegistration, uri)); 508 currentCount++; 509 } 510 keyValueData.setRegistrationRedirectCount(currentCount); 511 dao.insertOrUpdateKeyValueData(keyValueData); 512 } 513 handleFailure( AsyncRegistration asyncRegistration, AsyncFetchStatus asyncFetchStatus, Set<Uri> failedOrigins, IMeasurementDao dao)514 private void handleFailure( 515 AsyncRegistration asyncRegistration, 516 AsyncFetchStatus asyncFetchStatus, 517 Set<Uri> failedOrigins, 518 IMeasurementDao dao) 519 throws DatastoreException { 520 if (asyncFetchStatus.canRetry()) { 521 LogUtil.d( 522 "AsyncRegistrationQueueRunner: " 523 + "async " 524 + asyncRegistration.getType() 525 + " registration will be queued for retry " 526 + "Fetch Status : " 527 + asyncFetchStatus.getResponseStatus()); 528 failedOrigins.add(BaseUriExtractor.getBaseUri(asyncRegistration.getRegistrationUri())); 529 asyncRegistration.incrementRetryCount(); 530 dao.updateRetryCount(asyncRegistration); 531 } else { 532 LogUtil.d( 533 "AsyncRegistrationQueueRunner: " 534 + "async " 535 + asyncRegistration.getType() 536 + " registration will not be queued for retry. " 537 + "Fetch Status : " 538 + asyncFetchStatus.getResponseStatus()); 539 dao.deleteAsyncRegistration(asyncRegistration.getId()); 540 } 541 } 542 543 /** 544 * {@link Attribution} generated from here will only be used for fake report attribution. 545 * 546 * @param source source to derive parameters from 547 * @param destination destination for attribution 548 * @return a fake {@link Attribution} 549 */ createFakeAttributionRateLimit(Source source, Uri destination)550 private Attribution createFakeAttributionRateLimit(Source source, Uri destination) { 551 Optional<Uri> topLevelPublisher = 552 getTopLevelPublisher(source.getPublisher(), source.getPublisherType()); 553 554 if (!topLevelPublisher.isPresent()) { 555 throw new IllegalArgumentException( 556 String.format( 557 "insertAttributionRateLimit: getSourceAndDestinationTopPrivateDomains" 558 + " failed. Publisher: %s; Attribution destination: %s", 559 source.getPublisher(), destination)); 560 } 561 562 return new Attribution.Builder() 563 .setSourceSite(topLevelPublisher.get().toString()) 564 .setSourceOrigin(source.getPublisher().toString()) 565 .setDestinationSite(destination.toString()) 566 .setDestinationOrigin(destination.toString()) 567 .setEnrollmentId(source.getEnrollmentId()) 568 .setTriggerTime(source.getEventTime()) 569 .setRegistrant(source.getRegistrant().toString()) 570 .setSourceId(source.getId()) 571 // Intentionally kept it as null because it's a fake attribution 572 .setTriggerId(null) 573 .build(); 574 } 575 getTopLevelPublisher( Uri topOrigin, @EventSurfaceType int publisherType)576 private static Optional<Uri> getTopLevelPublisher( 577 Uri topOrigin, @EventSurfaceType int publisherType) { 578 return publisherType == EventSurfaceType.APP 579 ? Optional.of(topOrigin) 580 : Web.topPrivateDomainAndScheme(topOrigin); 581 } 582 getPublisher(AsyncRegistration request)583 private Uri getPublisher(AsyncRegistration request) { 584 return request.getRegistrant(); 585 } 586 notifyTriggerContentProvider()587 private void notifyTriggerContentProvider() { 588 try (ContentProviderClient contentProviderClient = 589 mContentResolver.acquireContentProviderClient(TriggerContentProvider.TRIGGER_URI)) { 590 if (contentProviderClient != null) { 591 contentProviderClient.insert(TriggerContentProvider.TRIGGER_URI, null); 592 } 593 } catch (RemoteException e) { 594 LogUtil.e(e, "Trigger Content Provider invocation failed."); 595 } 596 } 597 } 598