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.service.measurement.aggregation; 18 19 import static com.android.adservices.service.measurement.aggregation.AggregatePayloadGenerator.POST_FLEXIBLE_CONTRIBUTION_FILTERING_API_VERSION; 20 21 import android.annotation.IntDef; 22 import android.annotation.NonNull; 23 import android.net.Uri; 24 25 import androidx.annotation.Nullable; 26 27 import com.android.adservices.LoggerFactory; 28 import com.android.adservices.service.AdServicesConfig; 29 import com.android.adservices.service.Flags; 30 import com.android.adservices.service.measurement.EventSurfaceType; 31 import com.android.adservices.service.measurement.Trigger; 32 import com.android.adservices.service.measurement.util.UnsignedLong; 33 34 import org.json.JSONArray; 35 import org.json.JSONException; 36 import org.json.JSONObject; 37 38 import java.lang.annotation.Retention; 39 import java.lang.annotation.RetentionPolicy; 40 import java.util.ArrayList; 41 import java.util.Collections; 42 import java.util.List; 43 import java.util.Objects; 44 import java.util.UUID; 45 46 /** 47 * Class that contains all the real data needed after aggregation, it is not encrypted. 48 */ 49 public class AggregateReport { 50 static final String OPERATION = "operation"; 51 static final String HISTOGRAM = "histogram"; 52 static final String DATA = "data"; 53 54 private String mId; 55 private Uri mPublisher; 56 private Uri mAttributionDestination; 57 @Nullable private Long mSourceRegistrationTime; 58 private long mScheduledReportTime; // triggerTime + random([10min, 1hour]) 59 private String mEnrollmentId; 60 private String mDebugCleartextPayload; 61 private AggregateAttributionData mAggregateAttributionData; 62 private @Status int mStatus; 63 private @DebugReportStatus int mDebugReportStatus; 64 private String mApiVersion; 65 @Nullable private UnsignedLong mSourceDebugKey; 66 @Nullable private UnsignedLong mTriggerDebugKey; 67 private String mSourceId; 68 private String mTriggerId; 69 private UnsignedLong mDedupKey; 70 private Uri mRegistrationOrigin; 71 private Uri mAggregationCoordinatorOrigin; 72 private boolean mIsFakeReport; 73 @Nullable private String mTriggerContextId; 74 private long mTriggerTime; 75 private String mApi; 76 @Nullable private Integer mAggregatableFilteringIdMaxBytes; 77 78 @IntDef(value = {Status.PENDING, Status.DELIVERED, Status.MARKED_TO_DELETE}) 79 @Retention(RetentionPolicy.SOURCE) 80 public @interface Status { 81 int PENDING = 0; 82 int DELIVERED = 1; 83 int MARKED_TO_DELETE = 2; 84 } 85 86 @IntDef( 87 value = { 88 DebugReportStatus.NONE, 89 DebugReportStatus.PENDING, 90 DebugReportStatus.DELIVERED, 91 }) 92 @Retention(RetentionPolicy.SOURCE) 93 public @interface DebugReportStatus { 94 int NONE = 0; 95 int PENDING = 1; 96 int DELIVERED = 2; 97 } 98 AggregateReport()99 private AggregateReport() { 100 mId = null; 101 mPublisher = null; 102 mAttributionDestination = null; 103 mSourceRegistrationTime = null; 104 mScheduledReportTime = 0L; 105 mEnrollmentId = null; 106 mDebugCleartextPayload = null; 107 mAggregateAttributionData = null; 108 mStatus = Status.PENDING; 109 mDebugReportStatus = DebugReportStatus.NONE; 110 mSourceDebugKey = null; 111 mTriggerDebugKey = null; 112 mDedupKey = null; 113 mRegistrationOrigin = null; 114 mAggregationCoordinatorOrigin = null; 115 mTriggerContextId = null; 116 mTriggerTime = 0L; 117 mApi = null; 118 mAggregatableFilteringIdMaxBytes = null; 119 } 120 121 @Override equals(Object obj)122 public boolean equals(Object obj) { 123 if (!(obj instanceof AggregateReport)) { 124 return false; 125 } 126 AggregateReport aggregateReport = (AggregateReport) obj; 127 return Objects.equals(mPublisher, aggregateReport.mPublisher) 128 && Objects.equals(mAttributionDestination, aggregateReport.mAttributionDestination) 129 && Objects.equals(mSourceRegistrationTime, aggregateReport.mSourceRegistrationTime) 130 && mScheduledReportTime == aggregateReport.mScheduledReportTime 131 && Objects.equals(mEnrollmentId, aggregateReport.mEnrollmentId) 132 && Objects.equals(mDebugCleartextPayload, aggregateReport.mDebugCleartextPayload) 133 && Objects.equals( 134 mAggregateAttributionData, aggregateReport.mAggregateAttributionData) 135 && mStatus == aggregateReport.mStatus 136 && mDebugReportStatus == aggregateReport.mDebugReportStatus 137 && Objects.equals(mApiVersion, aggregateReport.mApiVersion) 138 && Objects.equals(mSourceDebugKey, aggregateReport.mSourceDebugKey) 139 && Objects.equals(mTriggerDebugKey, aggregateReport.mTriggerDebugKey) 140 && Objects.equals(mSourceId, aggregateReport.mSourceId) 141 && Objects.equals(mTriggerId, aggregateReport.mTriggerId) 142 && Objects.equals(mDedupKey, aggregateReport.mDedupKey) 143 && Objects.equals(mRegistrationOrigin, aggregateReport.mRegistrationOrigin) 144 && Objects.equals( 145 mAggregationCoordinatorOrigin, 146 aggregateReport.mAggregationCoordinatorOrigin) 147 && mIsFakeReport == aggregateReport.mIsFakeReport 148 && Objects.equals(mTriggerContextId, aggregateReport.mTriggerContextId) 149 && mTriggerTime == aggregateReport.mTriggerTime 150 && Objects.equals(mApi, aggregateReport.mApi) 151 && Objects.equals( 152 mAggregatableFilteringIdMaxBytes, 153 aggregateReport.mAggregatableFilteringIdMaxBytes); 154 } 155 156 @Override hashCode()157 public int hashCode() { 158 return Objects.hash( 159 mId, 160 mPublisher, 161 mAttributionDestination, 162 mSourceRegistrationTime, 163 mScheduledReportTime, 164 mEnrollmentId, 165 mDebugCleartextPayload, 166 mAggregateAttributionData, 167 mStatus, 168 mDebugReportStatus, 169 mSourceDebugKey, 170 mTriggerDebugKey, 171 mSourceId, 172 mTriggerId, 173 mDedupKey, 174 mRegistrationOrigin, 175 mAggregationCoordinatorOrigin, 176 mIsFakeReport, 177 mTriggerContextId, 178 mTriggerTime, 179 mApi, 180 mAggregatableFilteringIdMaxBytes); 181 } 182 183 /** 184 * Unique identifier for the {@link AggregateReport}. 185 */ getId()186 public String getId() { 187 return mId; 188 } 189 190 /** 191 * Uri for publisher of this source, primarily an App. 192 */ getPublisher()193 public Uri getPublisher() { 194 return mPublisher; 195 } 196 197 /** 198 * Uri for attribution destination of source. 199 */ getAttributionDestination()200 public Uri getAttributionDestination() { 201 return mAttributionDestination; 202 } 203 204 /** Source registration time. */ 205 @Nullable getSourceRegistrationTime()206 public Long getSourceRegistrationTime() { 207 return mSourceRegistrationTime; 208 } 209 210 /** 211 * Scheduled report time for aggregate report . 212 */ getScheduledReportTime()213 public long getScheduledReportTime() { 214 return mScheduledReportTime; 215 } 216 217 /** 218 * Ad-tech enrollment ID. 219 */ getEnrollmentId()220 public String getEnrollmentId() { 221 return mEnrollmentId; 222 } 223 224 /** 225 * Unencrypted aggregate payload string, convert from List of AggregateHistogramContribution. 226 */ getDebugCleartextPayload()227 public String getDebugCleartextPayload() { 228 return mDebugCleartextPayload; 229 } 230 231 /** Source Debug Key */ 232 @Nullable getSourceDebugKey()233 public UnsignedLong getSourceDebugKey() { 234 return mSourceDebugKey; 235 } 236 237 /** Trigger Debug Key */ 238 @Nullable getTriggerDebugKey()239 public UnsignedLong getTriggerDebugKey() { 240 return mTriggerDebugKey; 241 } 242 243 /** 244 * Contains the data specific to the aggregate report. 245 * 246 * @deprecated use {@link #getDebugCleartextPayload()} instead 247 */ 248 @Deprecated getAggregateAttributionData()249 public AggregateAttributionData getAggregateAttributionData() { 250 return mAggregateAttributionData; 251 } 252 253 /** 254 * Current {@link Status} of the report. 255 */ getStatus()256 public @Status int getStatus() { 257 return mStatus; 258 } 259 260 /** Current {@link DebugReportStatus} of the report. */ getDebugReportStatus()261 public @DebugReportStatus int getDebugReportStatus() { 262 return mDebugReportStatus; 263 } 264 265 /** 266 * Api version when the report was issued. 267 */ getApiVersion()268 public String getApiVersion() { 269 return mApiVersion; 270 } 271 272 /** Deduplication key assigned to theis aggregate report. */ 273 @Nullable getDedupKey()274 public UnsignedLong getDedupKey() { 275 return mDedupKey; 276 } 277 278 /** Returns registration origin used to register the source */ getRegistrationOrigin()279 public Uri getRegistrationOrigin() { 280 return mRegistrationOrigin; 281 } 282 283 /** Returns coordinator origin for aggregatable reports */ getAggregationCoordinatorOrigin()284 public Uri getAggregationCoordinatorOrigin() { 285 return mAggregationCoordinatorOrigin; 286 } 287 288 /** Is the report a null report. */ isFakeReport()289 public boolean isFakeReport() { 290 return mIsFakeReport; 291 } 292 293 /** Returns the trigger's context id. */ 294 @Nullable getTriggerContextId()295 public String getTriggerContextId() { 296 return mTriggerContextId; 297 } 298 299 /** TriggerTime of the associated {@link Trigger}. */ getTriggerTime()300 public long getTriggerTime() { 301 return mTriggerTime; 302 } 303 304 /** Returns the aggregate report api. */ getApi()305 public String getApi() { 306 return mApi; 307 } 308 309 /** Returns the aggregatable filtering id max bytes. */ 310 @Nullable getAggregatableFilteringIdMaxBytes()311 public Integer getAggregatableFilteringIdMaxBytes() { 312 return mAggregatableFilteringIdMaxBytes; 313 } 314 315 /** Returns if the report for this Trigger should be sent immediately or with a delay. */ isDelayed(Trigger trigger, Flags flags)316 public static boolean isDelayed(Trigger trigger, Flags flags) { 317 boolean isNonNullTriggerContextId = trigger.getTriggerContextId() != null; 318 boolean isNonNullFilteringIdAndNonDefaultFilteringIdMaxBytes = 319 flags.getMeasurementEnableFlexibleContributionFiltering() 320 && trigger.getAggregatableFilteringIdMaxBytes() != null 321 && trigger.getAggregatableFilteringIdMaxBytes() 322 != Flags.MEASUREMENT_DEFAULT_FILTERING_ID_MAX_BYTES; 323 if (isNonNullTriggerContextId || isNonNullFilteringIdAndNonDefaultFilteringIdMaxBytes) { 324 return false; 325 } 326 return true; 327 } 328 329 /** 330 * Generates String for debugCleartextPayload. JSON for format : { "operation": "histogram", 331 * "data": [{ "bucket": 1369, "value": 32768 }, { "bucket": 3461, "value": 1664 }] } 332 */ 333 @NonNull generateDebugPayload(List<AggregateHistogramContribution> contributions)334 public static String generateDebugPayload(List<AggregateHistogramContribution> contributions) 335 throws JSONException { 336 JSONArray jsonArray = new JSONArray(); 337 for (AggregateHistogramContribution contribution : contributions) { 338 jsonArray.put(contribution.toJSONObject()); 339 } 340 JSONObject debugPayload = new JSONObject(); 341 debugPayload.put(OPERATION, HISTOGRAM); 342 debugPayload.put(DATA, jsonArray); 343 return debugPayload.toString(); 344 } 345 346 /** 347 * It deserializes the debug cleartext payload into {@link AggregateHistogramContribution}s. 348 * 349 * @return list of {@link AggregateHistogramContribution}s 350 */ 351 @NonNull extractAggregateHistogramContributions()352 public List<AggregateHistogramContribution> extractAggregateHistogramContributions() { 353 try { 354 ArrayList<AggregateHistogramContribution> aggregateHistogramContributions = 355 new ArrayList<>(); 356 JSONObject debugCleartextPayload = new JSONObject(mDebugCleartextPayload); 357 JSONArray contributionsArray = debugCleartextPayload.getJSONArray(DATA); 358 for (int i = 0; i < contributionsArray.length(); i++) { 359 AggregateHistogramContribution aggregateHistogramContribution = 360 new AggregateHistogramContribution.Builder() 361 .fromJsonObject(contributionsArray.getJSONObject(i)); 362 aggregateHistogramContributions.add(aggregateHistogramContribution); 363 } 364 return aggregateHistogramContributions; 365 } catch (JSONException e) { 366 LoggerFactory.getMeasurementLogger() 367 .e(e, "Failed to parse contributions on Aggregate report."); 368 return Collections.emptyList(); 369 } 370 } 371 372 /** 373 * Source ID. Can be null for trigger verbose aggregate debug reports, where no source is 374 * involved, e.g. trigger-no-matching-source or source-destination-limit (no source in DB). 375 */ 376 @Nullable getSourceId()377 public String getSourceId() { 378 return mSourceId; 379 } 380 381 /** 382 * Trigger ID. Can be null for source verbose aggregate debug reports, where no trigger is 383 * involved, e.g. source-success. 384 */ 385 @Nullable getTriggerId()386 public String getTriggerId() { 387 return mTriggerId; 388 } 389 390 /** 391 * Builder for {@link AggregateReport}. 392 */ 393 public static final class Builder { 394 private final AggregateReport mAttributionReport; 395 Builder()396 public Builder() { 397 mAttributionReport = new AggregateReport(); 398 } 399 400 /** 401 * See {@link AggregateReport#getId()}. 402 */ setId(String id)403 public Builder setId(String id) { 404 mAttributionReport.mId = id; 405 return this; 406 } 407 408 /** 409 * See {@link AggregateReport#getPublisher()}. 410 */ setPublisher(Uri publisher)411 public Builder setPublisher(Uri publisher) { 412 mAttributionReport.mPublisher = publisher; 413 return this; 414 } 415 416 /** 417 * See {@link AggregateReport#getAttributionDestination()}. 418 */ setAttributionDestination(Uri attributionDestination)419 public Builder setAttributionDestination(Uri attributionDestination) { 420 mAttributionReport.mAttributionDestination = attributionDestination; 421 return this; 422 } 423 424 /** See {@link AggregateReport#getSourceRegistrationTime()}. */ setSourceRegistrationTime(@ullable Long sourceRegistrationTime)425 public Builder setSourceRegistrationTime(@Nullable Long sourceRegistrationTime) { 426 mAttributionReport.mSourceRegistrationTime = sourceRegistrationTime; 427 return this; 428 } 429 430 /** 431 * See {@link AggregateReport#getScheduledReportTime()}. 432 */ setScheduledReportTime(long scheduledReportTime)433 public Builder setScheduledReportTime(long scheduledReportTime) { 434 mAttributionReport.mScheduledReportTime = scheduledReportTime; 435 return this; 436 } 437 438 /** 439 * See {@link AggregateReport#getEnrollmentId()}. 440 */ setEnrollmentId(String enrollmentId)441 public Builder setEnrollmentId(String enrollmentId) { 442 mAttributionReport.mEnrollmentId = enrollmentId; 443 return this; 444 } 445 446 /** 447 * See {@link AggregateReport#getDebugCleartextPayload()}. 448 */ setDebugCleartextPayload(String debugCleartextPayload)449 public Builder setDebugCleartextPayload(String debugCleartextPayload) { 450 mAttributionReport.mDebugCleartextPayload = debugCleartextPayload; 451 return this; 452 } 453 454 /** 455 * See {@link AggregateReport#getAggregateAttributionData()}. 456 * 457 * @deprecated use {@link #getDebugCleartextPayload()} instead 458 */ 459 @Deprecated setAggregateAttributionData( AggregateAttributionData aggregateAttributionData)460 public Builder setAggregateAttributionData( 461 AggregateAttributionData aggregateAttributionData) { 462 mAttributionReport.mAggregateAttributionData = aggregateAttributionData; 463 return this; 464 } 465 466 /** 467 * See {@link AggregateReport#getStatus()} 468 */ setStatus(@tatus int status)469 public Builder setStatus(@Status int status) { 470 mAttributionReport.mStatus = status; 471 return this; 472 } 473 474 /** See {@link AggregateReport#getDebugReportStatus()} */ setDebugReportStatus(@ebugReportStatus int debugReportStatus)475 public Builder setDebugReportStatus(@DebugReportStatus int debugReportStatus) { 476 mAttributionReport.mDebugReportStatus = debugReportStatus; 477 return this; 478 } 479 480 /** 481 * See {@link AggregateReport#getApiVersion()} 482 */ setApiVersion(String version)483 public Builder setApiVersion(String version) { 484 mAttributionReport.mApiVersion = version; 485 return this; 486 } 487 488 /** See {@link AggregateReport#getSourceDebugKey()} */ setSourceDebugKey(UnsignedLong sourceDebugKey)489 public Builder setSourceDebugKey(UnsignedLong sourceDebugKey) { 490 mAttributionReport.mSourceDebugKey = sourceDebugKey; 491 return this; 492 } 493 494 /** See {@link AggregateReport#getTriggerDebugKey()} */ setTriggerDebugKey(UnsignedLong triggerDebugKey)495 public Builder setTriggerDebugKey(UnsignedLong triggerDebugKey) { 496 mAttributionReport.mTriggerDebugKey = triggerDebugKey; 497 return this; 498 } 499 500 /** See {@link AggregateReport#getSourceId()} */ setSourceId(String sourceId)501 public Builder setSourceId(String sourceId) { 502 mAttributionReport.mSourceId = sourceId; 503 return this; 504 } 505 506 /** See {@link AggregateReport#getTriggerId()} */ setTriggerId(String triggerId)507 public Builder setTriggerId(String triggerId) { 508 mAttributionReport.mTriggerId = triggerId; 509 return this; 510 } 511 512 /** See {@link AggregateReport#getDedupKey()} */ 513 @NonNull setDedupKey(@ullable UnsignedLong dedupKey)514 public Builder setDedupKey(@Nullable UnsignedLong dedupKey) { 515 mAttributionReport.mDedupKey = dedupKey; 516 return this; 517 } 518 519 /** See {@link AggregateReport#getRegistrationOrigin()} */ 520 @NonNull setRegistrationOrigin(Uri registrationOrigin)521 public Builder setRegistrationOrigin(Uri registrationOrigin) { 522 mAttributionReport.mRegistrationOrigin = registrationOrigin; 523 return this; 524 } 525 526 /** See {@link AggregateReport#getAggregationCoordinatorOrigin()} */ setAggregationCoordinatorOrigin(Uri aggregationCoordinatorOrigin)527 public Builder setAggregationCoordinatorOrigin(Uri aggregationCoordinatorOrigin) { 528 mAttributionReport.mAggregationCoordinatorOrigin = aggregationCoordinatorOrigin; 529 return this; 530 } 531 532 /** See {@link AggregateReport#isFakeReport()} */ setIsFakeReport(boolean isFakeReport)533 public Builder setIsFakeReport(boolean isFakeReport) { 534 mAttributionReport.mIsFakeReport = isFakeReport; 535 return this; 536 } 537 538 /** See {@link AggregateReport#getTriggerContextId()} */ setTriggerContextId(@ullable String triggerContextId)539 public Builder setTriggerContextId(@Nullable String triggerContextId) { 540 mAttributionReport.mTriggerContextId = triggerContextId; 541 return this; 542 } 543 544 /** See {@link AggregateReport#getTriggerTime()} */ setTriggerTime(long triggerTime)545 public Builder setTriggerTime(long triggerTime) { 546 mAttributionReport.mTriggerTime = triggerTime; 547 return this; 548 } 549 550 /** See {@link AggregateReport#getApi()} */ setApi(@onNull String api)551 public Builder setApi(@NonNull String api) { 552 mAttributionReport.mApi = api; 553 return this; 554 } 555 556 /** See {@link AggregateReport#getAggregatableFilteringIdMaxBytes()} */ setAggregatableFilteringIdMaxBytes( @ullable Integer aggregatableFilteringIdMaxBytes)557 public Builder setAggregatableFilteringIdMaxBytes( 558 @Nullable Integer aggregatableFilteringIdMaxBytes) { 559 mAttributionReport.mAggregatableFilteringIdMaxBytes = aggregatableFilteringIdMaxBytes; 560 return this; 561 } 562 563 /** 564 * Given a {@link Trigger} trigger, source registration time, reporting delay, and the api 565 * version, initialize an {@link AggregateReport.Builder} builder that builds a null 566 * aggregate report. A null aggregate report is used to obscure the number of real aggregate 567 * reports. 568 * 569 * @param trigger the trigger 570 * @param fakeSourceTime a fake source registration time 571 * @param delay amount of delay in ms to wait before sending out report. Only applicable if 572 * the trigger's trigger context id is null 573 * @param apiVersion api version string that is sent to aggregate service 574 * @return builder initialized to build a null aggregate report 575 * @throws JSONException thrown if fake contributions create invalid JSON. Fake 576 * contributions are hardcoded, so this should not be thrown. 577 */ getNullAggregateReportBuilder( Trigger trigger, @Nullable Long fakeSourceTime, long delay, String apiVersion, String api, Flags flags)578 public Builder getNullAggregateReportBuilder( 579 Trigger trigger, 580 @Nullable Long fakeSourceTime, 581 long delay, 582 String apiVersion, 583 String api, 584 Flags flags) 585 throws JSONException { 586 mAttributionReport.mId = UUID.randomUUID().toString(); 587 long reportTime = trigger.getTriggerTime(); 588 if (isDelayed(trigger, flags)) { 589 reportTime += delay; 590 } 591 592 AggregateHistogramContribution.Builder paddingContributionBuilder = 593 new AggregateHistogramContribution.Builder(); 594 if (apiVersion.equals(POST_FLEXIBLE_CONTRIBUTION_FILTERING_API_VERSION)) { 595 paddingContributionBuilder.setPaddingContributionWithFilteringId(); 596 } else { 597 paddingContributionBuilder.setPaddingContribution(); 598 } 599 600 String debugPayload = 601 AggregateReport.generateDebugPayload( 602 List.of(paddingContributionBuilder.build())); 603 604 mAttributionReport.mApiVersion = apiVersion; 605 mAttributionReport.mPublisher = Uri.EMPTY; 606 mAttributionReport.mRegistrationOrigin = trigger.getRegistrationOrigin(); 607 mAttributionReport.mAttributionDestination = trigger.getAttributionDestinationBaseUri(); 608 mAttributionReport.mSourceRegistrationTime = fakeSourceTime; 609 mAttributionReport.mScheduledReportTime = reportTime; 610 mAttributionReport.mDebugCleartextPayload = debugPayload; 611 mAttributionReport.mSourceDebugKey = null; 612 mAttributionReport.mIsFakeReport = true; 613 mAttributionReport.mTriggerId = trigger.getId(); 614 mAttributionReport.mTriggerContextId = trigger.getTriggerContextId(); 615 mAttributionReport.mTriggerTime = trigger.getTriggerTime(); 616 mAttributionReport.mApi = api; 617 mAttributionReport.mAggregatableFilteringIdMaxBytes = 618 trigger.getAggregatableFilteringIdMaxBytes(); 619 620 if (trigger.getAggregationCoordinatorOrigin() != null) { 621 mAttributionReport.mAggregationCoordinatorOrigin = 622 trigger.getAggregationCoordinatorOrigin(); 623 } else { 624 mAttributionReport.mAggregationCoordinatorOrigin = 625 Uri.parse( 626 AdServicesConfig 627 .getMeasurementDefaultAggregationCoordinatorOrigin()); 628 } 629 630 if (flags.getMeasurementEnableBothSideDebugKeysInReports()) { 631 mAttributionReport.mTriggerDebugKey = null; 632 } else if ((trigger.getDestinationType() == EventSurfaceType.APP 633 && trigger.hasAdIdPermission()) 634 || (trigger.getDestinationType() == EventSurfaceType.WEB 635 && trigger.hasArDebugPermission())) { 636 mAttributionReport.mTriggerDebugKey = trigger.getDebugKey(); 637 } 638 639 return this; 640 } 641 642 /** Build the {@link AggregateReport}. */ build()643 public AggregateReport build() { 644 return mAttributionReport; 645 } 646 } 647 } 648