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; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.net.Uri; 22 import android.util.Pair; 23 24 import androidx.annotation.Nullable; 25 26 import com.android.adservices.service.measurement.noising.SourceNoiseHandler; 27 import com.android.adservices.service.measurement.reporting.EventReportWindowCalcDelegate; 28 import com.android.adservices.service.measurement.util.UnsignedLong; 29 30 import com.google.common.collect.ImmutableMultiset; 31 32 import java.lang.annotation.Retention; 33 import java.lang.annotation.RetentionPolicy; 34 import java.util.ArrayList; 35 import java.util.List; 36 import java.util.Objects; 37 import java.util.UUID; 38 39 /** 40 * POJO for EventReport. 41 */ 42 public class EventReport { 43 44 private String mId; 45 private UnsignedLong mSourceEventId; 46 private long mReportTime; 47 private long mTriggerTime; 48 private long mTriggerPriority; 49 private List<Uri> mAttributionDestinations; 50 private String mEnrollmentId; 51 private UnsignedLong mTriggerData; 52 private UnsignedLong mTriggerDedupKey; 53 private double mRandomizedTriggerRate; 54 private @Status int mStatus; 55 private @DebugReportStatus int mDebugReportStatus; 56 private Source.SourceType mSourceType; 57 @Nullable private UnsignedLong mSourceDebugKey; 58 @Nullable private UnsignedLong mTriggerDebugKey; 59 @NonNull private List<UnsignedLong> mTriggerDebugKeys; 60 private String mSourceId; 61 private String mTriggerId; 62 private Uri mRegistrationOrigin; 63 private long mTriggerValue; 64 private Pair<Long, Long> mTriggerSummaryBucket; 65 66 @IntDef(value = {Status.PENDING, Status.DELIVERED, Status.MARKED_TO_DELETE}) 67 @Retention(RetentionPolicy.SOURCE) 68 public @interface Status { 69 int PENDING = 0; 70 int DELIVERED = 1; 71 int MARKED_TO_DELETE = 2; 72 } 73 74 @IntDef( 75 value = { 76 DebugReportStatus.NONE, 77 DebugReportStatus.PENDING, 78 DebugReportStatus.DELIVERED, 79 }) 80 @Retention(RetentionPolicy.SOURCE) 81 public @interface DebugReportStatus { 82 int NONE = 0; 83 int PENDING = 1; 84 int DELIVERED = 2; 85 } 86 EventReport()87 private EventReport() { 88 mTriggerDedupKey = null; 89 mTriggerDebugKeys = new ArrayList<>(); 90 } 91 92 @Override equals(Object obj)93 public boolean equals(Object obj) { 94 if (!(obj instanceof EventReport)) { 95 return false; 96 } 97 EventReport eventReport = (EventReport) obj; 98 return mStatus == eventReport.mStatus 99 && mDebugReportStatus == eventReport.mDebugReportStatus 100 && mReportTime == eventReport.mReportTime 101 && Objects.equals(mAttributionDestinations, eventReport.mAttributionDestinations) 102 && ImmutableMultiset.copyOf(mAttributionDestinations) 103 .equals(ImmutableMultiset.copyOf(eventReport.mAttributionDestinations)) 104 && Objects.equals(mEnrollmentId, eventReport.mEnrollmentId) 105 && mTriggerTime == eventReport.mTriggerTime 106 && Objects.equals(mTriggerData, eventReport.mTriggerData) 107 && Objects.equals(mSourceEventId, eventReport.mSourceEventId) 108 && mTriggerPriority == eventReport.mTriggerPriority 109 && Objects.equals(mTriggerDedupKey, eventReport.mTriggerDedupKey) 110 && mSourceType == eventReport.mSourceType 111 && mRandomizedTriggerRate == eventReport.mRandomizedTriggerRate 112 && Objects.equals(mSourceDebugKey, eventReport.mSourceDebugKey) 113 && Objects.equals(mTriggerDebugKey, eventReport.mTriggerDebugKey) 114 && Objects.equals(mTriggerDebugKeys, eventReport.mTriggerDebugKeys) 115 && Objects.equals(mSourceId, eventReport.mSourceId) 116 && Objects.equals(mTriggerId, eventReport.mTriggerId) 117 && Objects.equals(mRegistrationOrigin, eventReport.mRegistrationOrigin) 118 && mTriggerValue == eventReport.mTriggerValue 119 && Objects.equals(mTriggerSummaryBucket, eventReport.mTriggerSummaryBucket); 120 } 121 122 @Override hashCode()123 public int hashCode() { 124 return Objects.hash( 125 mStatus, 126 mDebugReportStatus, 127 mReportTime, 128 mAttributionDestinations, 129 mEnrollmentId, 130 mTriggerTime, 131 mTriggerData, 132 mSourceEventId, 133 mTriggerPriority, 134 mTriggerDedupKey, 135 mSourceType, 136 mRandomizedTriggerRate, 137 mSourceDebugKey, 138 mTriggerDebugKey, 139 mTriggerDebugKeys, 140 mSourceId, 141 mTriggerId, 142 mRegistrationOrigin, 143 mTriggerValue, 144 mTriggerSummaryBucket); 145 } 146 147 /** Unique identifier for the report. */ getId()148 public String getId() { 149 return mId; 150 } 151 152 /** Identifier of the associated {@link Source} event. */ getSourceEventId()153 public UnsignedLong getSourceEventId() { 154 return mSourceEventId; 155 } 156 157 /** 158 * Scheduled time for the report to be sent. 159 */ getReportTime()160 public long getReportTime() { 161 return mReportTime; 162 } 163 164 /** 165 * TriggerTime of the associated {@link Trigger}. 166 */ getTriggerTime()167 public long getTriggerTime() { 168 return mTriggerTime; 169 } 170 171 /** 172 * Priority of the associated {@link Trigger}. 173 */ getTriggerPriority()174 public long getTriggerPriority() { 175 return mTriggerPriority; 176 } 177 178 /** 179 * AttributionDestinations of the {@link Source} and {@link Trigger}. 180 */ getAttributionDestinations()181 public List<Uri> getAttributionDestinations() { 182 return mAttributionDestinations; 183 } 184 185 /** 186 * Ad Tech enrollment ID. 187 */ getEnrollmentId()188 public String getEnrollmentId() { 189 return mEnrollmentId; 190 } 191 192 /** 193 * Metadata for the report. 194 */ getTriggerData()195 public UnsignedLong getTriggerData() { 196 return mTriggerData; 197 } 198 199 /** 200 * Deduplication key of the associated {@link Trigger} 201 */ getTriggerDedupKey()202 public UnsignedLong getTriggerDedupKey() { 203 return mTriggerDedupKey; 204 } 205 206 /** 207 * Current {@link Status} of the report. 208 */ getStatus()209 public @Status int getStatus() { 210 return mStatus; 211 } 212 213 /** Current {@link DebugReportStatus} of the report. */ getDebugReportStatus()214 public @DebugReportStatus int getDebugReportStatus() { 215 return mDebugReportStatus; 216 } 217 218 /** 219 * SourceType of the event's source. 220 */ getSourceType()221 public Source.SourceType getSourceType() { 222 return mSourceType; 223 } 224 225 /** 226 * Randomized trigger rate for noising 227 */ getRandomizedTriggerRate()228 public double getRandomizedTriggerRate() { 229 return mRandomizedTriggerRate; 230 } 231 232 /** Source Debug Key */ 233 @Nullable getSourceDebugKey()234 public UnsignedLong getSourceDebugKey() { 235 return mSourceDebugKey; 236 } 237 238 /** Trigger Debug Key */ 239 @Nullable getTriggerDebugKey()240 public UnsignedLong getTriggerDebugKey() { 241 return mTriggerDebugKey; 242 } 243 244 /** Trigger Debug Keys */ 245 @NonNull getTriggerDebugKeys()246 public List<UnsignedLong> getTriggerDebugKeys() { 247 return mTriggerDebugKeys; 248 } 249 250 /** Source ID */ getSourceId()251 public String getSourceId() { 252 return mSourceId; 253 } 254 255 /** Trigger ID */ getTriggerId()256 public String getTriggerId() { 257 return mTriggerId; 258 } 259 260 /** Trigger summary bucket */ getTriggerSummaryBucket()261 public Pair<Long, Long> getTriggerSummaryBucket() { 262 return mTriggerSummaryBucket; 263 } 264 265 /** Returns registration origin used to register the source and trigger */ getRegistrationOrigin()266 public Uri getRegistrationOrigin() { 267 return mRegistrationOrigin; 268 } 269 270 /** Trigger Value */ getTriggerValue()271 public long getTriggerValue() { 272 return mTriggerValue; 273 } 274 275 /** Get Summary Bucket As String */ getStringEncodedTriggerSummaryBucket()276 public String getStringEncodedTriggerSummaryBucket() { 277 if (mTriggerSummaryBucket == null) { 278 return null; 279 } else { 280 return mTriggerSummaryBucket.first + "," + mTriggerSummaryBucket.second; 281 } 282 } 283 284 /** Builder for {@link EventReport} */ 285 public static final class Builder { 286 287 private final EventReport mBuilding; 288 Builder()289 public Builder() { 290 mBuilding = new EventReport(); 291 } 292 293 /** See {@link EventReport#getId()} */ setId(String id)294 public Builder setId(String id) { 295 mBuilding.mId = id; 296 return this; 297 } 298 299 /** See {@link EventReport#getSourceEventId()} */ setSourceEventId(UnsignedLong sourceEventId)300 public Builder setSourceEventId(UnsignedLong sourceEventId) { 301 mBuilding.mSourceEventId = sourceEventId; 302 return this; 303 } 304 305 /** See {@link EventReport#getEnrollmentId()} */ setEnrollmentId(String enrollmentId)306 public Builder setEnrollmentId(String enrollmentId) { 307 mBuilding.mEnrollmentId = enrollmentId; 308 return this; 309 } 310 311 /** See {@link EventReport#getAttributionDestinations()} */ setAttributionDestinations(List<Uri> attributionDestinations)312 public Builder setAttributionDestinations(List<Uri> attributionDestinations) { 313 mBuilding.mAttributionDestinations = attributionDestinations; 314 return this; 315 } 316 317 /** 318 * See {@link EventReport#getTriggerTime()} 319 */ setTriggerTime(long triggerTime)320 public Builder setTriggerTime(long triggerTime) { 321 mBuilding.mTriggerTime = triggerTime; 322 return this; 323 } 324 325 /** 326 * See {@link EventReport#getTriggerData()} 327 */ setTriggerData(UnsignedLong triggerData)328 public Builder setTriggerData(UnsignedLong triggerData) { 329 mBuilding.mTriggerData = triggerData; 330 return this; 331 } 332 333 /** 334 * See {@link EventReport#getTriggerPriority()} 335 */ setTriggerPriority(long triggerPriority)336 public Builder setTriggerPriority(long triggerPriority) { 337 mBuilding.mTriggerPriority = triggerPriority; 338 return this; 339 } 340 341 /** 342 * See {@link EventReport#getTriggerDedupKey()} 343 */ setTriggerDedupKey(UnsignedLong triggerDedupKey)344 public Builder setTriggerDedupKey(UnsignedLong triggerDedupKey) { 345 mBuilding.mTriggerDedupKey = triggerDedupKey; 346 return this; 347 } 348 349 /** 350 * See {@link EventReport#getReportTime()} 351 */ setReportTime(long reportTime)352 public Builder setReportTime(long reportTime) { 353 mBuilding.mReportTime = reportTime; 354 return this; 355 } 356 357 /** 358 * See {@link EventReport#getStatus()} 359 */ setStatus(@tatus int status)360 public Builder setStatus(@Status int status) { 361 mBuilding.mStatus = status; 362 return this; 363 } 364 365 /** See {@link EventReport#getDebugReportStatus()}} */ setDebugReportStatus(@ebugReportStatus int debugReportStatus)366 public Builder setDebugReportStatus(@DebugReportStatus int debugReportStatus) { 367 mBuilding.mDebugReportStatus = debugReportStatus; 368 return this; 369 } 370 371 /** 372 * See {@link EventReport#getSourceType()} 373 */ setSourceType(Source.SourceType sourceType)374 public Builder setSourceType(Source.SourceType sourceType) { 375 mBuilding.mSourceType = sourceType; 376 return this; 377 } 378 379 /** See {@link EventReport#getRandomizedTriggerRate()}} */ setRandomizedTriggerRate(double randomizedTriggerRate)380 public Builder setRandomizedTriggerRate(double randomizedTriggerRate) { 381 mBuilding.mRandomizedTriggerRate = randomizedTriggerRate; 382 return this; 383 } 384 385 /** See {@link EventReport#getSourceDebugKey()}} */ setSourceDebugKey(UnsignedLong sourceDebugKey)386 public Builder setSourceDebugKey(UnsignedLong sourceDebugKey) { 387 mBuilding.mSourceDebugKey = sourceDebugKey; 388 return this; 389 } 390 391 /** See {@link EventReport#getTriggerDebugKey()}} */ setTriggerDebugKey(UnsignedLong triggerDebugKey)392 public Builder setTriggerDebugKey(UnsignedLong triggerDebugKey) { 393 mBuilding.mTriggerDebugKey = triggerDebugKey; 394 return this; 395 } 396 397 /** See {@link EventReport#getTriggerDebugKeys()}} */ setTriggerDebugKeys(List<UnsignedLong> triggerDebugKeys)398 public Builder setTriggerDebugKeys(List<UnsignedLong> triggerDebugKeys) { 399 mBuilding.mTriggerDebugKeys = triggerDebugKeys; 400 return this; 401 } 402 403 /** See {@link EventReport#getSourceId()} */ setSourceId(String sourceId)404 public Builder setSourceId(String sourceId) { 405 mBuilding.mSourceId = sourceId; 406 return this; 407 } 408 409 /** See {@link EventReport#getTriggerId()} */ setTriggerId(String triggerId)410 public Builder setTriggerId(String triggerId) { 411 mBuilding.mTriggerId = triggerId; 412 return this; 413 } 414 415 /** 416 * set the summary bucket from input DB text encode summary bucket 417 * 418 * @param summaryBucket the string encoded summary bucket 419 * @return builder 420 */ setTriggerSummaryBucket(@ullable String summaryBucket)421 public Builder setTriggerSummaryBucket(@Nullable String summaryBucket) { 422 if (summaryBucket == null || summaryBucket.isEmpty()) { 423 mBuilding.mTriggerSummaryBucket = null; 424 return this; 425 } 426 String[] numbers = summaryBucket.split(","); 427 428 Long firstNumber = Long.parseLong(numbers[0].trim()); 429 Long secondNumber = Long.parseLong(numbers[1].trim()); 430 mBuilding.mTriggerSummaryBucket = new Pair<>(firstNumber, secondNumber); 431 432 return this; 433 } 434 435 /** See {@link EventReport#getTriggerSummaryBucket()} */ setTriggerSummaryBucket(@ullable Pair<Long, Long> summaryBucket)436 public Builder setTriggerSummaryBucket(@Nullable Pair<Long, Long> summaryBucket) { 437 mBuilding.mTriggerSummaryBucket = summaryBucket; 438 return this; 439 } 440 441 /** See {@link EventReport#getTriggerId()} */ setTriggerValue(long triggerValue)442 public Builder setTriggerValue(long triggerValue) { 443 mBuilding.mTriggerValue = triggerValue; 444 return this; 445 } 446 447 /** See {@link Source#getRegistrationOrigin()} ()} */ 448 @NonNull setRegistrationOrigin(Uri registrationOrigin)449 public Builder setRegistrationOrigin(Uri registrationOrigin) { 450 mBuilding.mRegistrationOrigin = registrationOrigin; 451 return this; 452 } 453 454 // TODO (b/285607306): cleanup since this doesn't just do "populateFromSourceAndTrigger" 455 /** Populates fields using {@link Source}, {@link Trigger} and {@link EventTrigger}. */ populateFromSourceAndTrigger( @onNull Source source, @NonNull Trigger trigger, @NonNull EventTrigger eventTrigger, @NonNull Pair<UnsignedLong, UnsignedLong> debugKeyPair, @NonNull EventReportWindowCalcDelegate eventReportWindowCalcDelegate, @NonNull SourceNoiseHandler sourceNoiseHandler, List<Uri> eventReportDestinations)456 public Builder populateFromSourceAndTrigger( 457 @NonNull Source source, 458 @NonNull Trigger trigger, 459 @NonNull EventTrigger eventTrigger, 460 @NonNull Pair<UnsignedLong, UnsignedLong> debugKeyPair, 461 @NonNull EventReportWindowCalcDelegate eventReportWindowCalcDelegate, 462 @NonNull SourceNoiseHandler sourceNoiseHandler, 463 List<Uri> eventReportDestinations) { 464 mBuilding.mId = UUID.randomUUID().toString(); 465 mBuilding.mTriggerDedupKey = eventTrigger.getDedupKey(); 466 mBuilding.mTriggerTime = trigger.getTriggerTime(); 467 mBuilding.mSourceEventId = source.getEventId(); 468 mBuilding.mEnrollmentId = source.getEnrollmentId(); 469 mBuilding.mStatus = Status.PENDING; 470 mBuilding.mAttributionDestinations = eventReportDestinations; 471 mBuilding.mSourceType = source.getSourceType(); 472 mBuilding.mSourceDebugKey = debugKeyPair.first; 473 mBuilding.mTriggerDebugKey = debugKeyPair.second; 474 mBuilding.mDebugReportStatus = DebugReportStatus.NONE; 475 if (mBuilding.mSourceDebugKey != null && mBuilding.mTriggerDebugKey != null) { 476 mBuilding.mDebugReportStatus = DebugReportStatus.PENDING; 477 } 478 mBuilding.mSourceId = source.getId(); 479 mBuilding.mTriggerId = trigger.getId(); 480 mBuilding.mRegistrationOrigin = trigger.getRegistrationOrigin(); 481 mBuilding.mTriggerPriority = eventTrigger.getTriggerPriority(); 482 // truncate trigger data to 3-bit or 1-bit based on {@link Source.SourceType} 483 mBuilding.mTriggerData = getTruncatedTriggerData(source, eventTrigger); 484 mBuilding.mReportTime = 485 eventReportWindowCalcDelegate.getReportingTime( 486 source, trigger.getTriggerTime(), trigger.getDestinationType()); 487 mBuilding.mRandomizedTriggerRate = 488 sourceNoiseHandler.getRandomizedTriggerRate(source); 489 return this; 490 } 491 492 /** Provides event report builder for flexible event-level reports */ getForFlex( @onNull Source source, @NonNull Trigger trigger, @NonNull AttributedTrigger attributedTrigger, long reportTime, @NonNull Pair<Long, Long> triggerSummaryBucket, @Nullable UnsignedLong sourceDebugKey, @NonNull List<UnsignedLong> debugKeys, double flipProbability, List<Uri> eventReportDestinations)493 public Builder getForFlex( 494 @NonNull Source source, 495 @NonNull Trigger trigger, 496 @NonNull AttributedTrigger attributedTrigger, 497 long reportTime, 498 @NonNull Pair<Long, Long> triggerSummaryBucket, 499 @Nullable UnsignedLong sourceDebugKey, 500 @NonNull List<UnsignedLong> debugKeys, 501 double flipProbability, 502 List<Uri> eventReportDestinations) { 503 mBuilding.mId = UUID.randomUUID().toString(); 504 mBuilding.mTriggerTime = trigger.getTriggerTime(); 505 mBuilding.mSourceEventId = source.getEventId(); 506 mBuilding.mEnrollmentId = source.getEnrollmentId(); 507 mBuilding.mStatus = Status.PENDING; 508 mBuilding.mAttributionDestinations = eventReportDestinations; 509 mBuilding.mSourceType = source.getSourceType(); 510 mBuilding.mSourceDebugKey = sourceDebugKey; 511 mBuilding.mTriggerDebugKeys = debugKeys; 512 mBuilding.mDebugReportStatus = DebugReportStatus.NONE; 513 if (mBuilding.mSourceDebugKey != null && debugKeys.size() > 0) { 514 mBuilding.mDebugReportStatus = DebugReportStatus.PENDING; 515 } 516 mBuilding.mSourceId = source.getId(); 517 mBuilding.mTriggerId = trigger.getId(); 518 mBuilding.mRegistrationOrigin = trigger.getRegistrationOrigin(); 519 mBuilding.mRandomizedTriggerRate = flipProbability; 520 mBuilding.mTriggerData = attributedTrigger.getTriggerData(); 521 mBuilding.mReportTime = reportTime; 522 mBuilding.mTriggerSummaryBucket = triggerSummaryBucket; 523 return this; 524 } 525 getTruncatedTriggerData(Source source, EventTrigger eventTrigger)526 private UnsignedLong getTruncatedTriggerData(Source source, EventTrigger eventTrigger) { 527 UnsignedLong triggerData = eventTrigger.getTriggerData(); 528 return triggerData.mod(source.getTriggerDataCardinality()); 529 } 530 531 /** 532 * Build the {@link EventReport}. 533 */ build()534 public EventReport build() { 535 return mBuilding; 536 } 537 } 538 } 539