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.annotation.Nullable; 22 import android.net.Uri; 23 import android.util.Pair; 24 25 import com.android.adservices.LoggerFactory; 26 import com.android.adservices.service.Flags; 27 import com.android.adservices.service.FlagsFactory; 28 import com.android.adservices.service.measurement.aggregation.AggregatableAttributionSource; 29 import com.android.adservices.service.measurement.noising.Combinatorics; 30 import com.android.adservices.service.measurement.noising.SourceNoiseHandler; 31 import com.android.adservices.service.measurement.reporting.EventReportWindowCalcDelegate; 32 import com.android.adservices.service.measurement.util.UnsignedLong; 33 import com.android.adservices.service.measurement.util.Validation; 34 import com.android.internal.annotations.VisibleForTesting; 35 36 import com.google.common.collect.ImmutableMultiset; 37 38 import org.json.JSONArray; 39 import org.json.JSONException; 40 import org.json.JSONObject; 41 42 import java.lang.annotation.Retention; 43 import java.lang.annotation.RetentionPolicy; 44 import java.math.BigInteger; 45 import java.util.ArrayList; 46 import java.util.Collections; 47 import java.util.HashMap; 48 import java.util.Iterator; 49 import java.util.List; 50 import java.util.Map; 51 import java.util.Objects; 52 import java.util.Optional; 53 import java.util.TreeMap; 54 import java.util.concurrent.TimeUnit; 55 56 /** 57 * POJO for Source. 58 */ 59 public class Source { 60 61 public static final long DEFAULT_MAX_EVENT_STATES = 3L; 62 63 private String mId; 64 private UnsignedLong mEventId; 65 private Uri mPublisher; 66 @EventSurfaceType private int mPublisherType; 67 private List<Uri> mAppDestinations; 68 private List<Uri> mWebDestinations; 69 private String mEnrollmentId; 70 private Uri mRegistrant; 71 private SourceType mSourceType; 72 private long mPriority; 73 @Status private int mStatus; 74 private long mEventTime; 75 private long mExpiryTime; 76 @Nullable private Long mEventReportWindow; 77 @Nullable private String mEventReportWindows; 78 private long mAggregatableReportWindow; 79 private long mReinstallReattributionWindow; 80 private List<UnsignedLong> mAggregateReportDedupKeys; 81 private List<UnsignedLong> mEventReportDedupKeys; 82 @AttributionMode private int mAttributionMode; 83 private long mInstallAttributionWindow; 84 private long mInstallCooldownWindow; 85 @Nullable private UnsignedLong mDebugKey; 86 private boolean mIsInstallAttributed; 87 private boolean mIsDebugReporting; 88 private String mFilterDataString; 89 @Nullable private String mSharedFilterDataKeys; 90 private FilterMap mFilterData; 91 private String mAggregateSource; 92 private int mAggregateContributions; 93 private Optional<AggregatableAttributionSource> mAggregatableAttributionSource; 94 private boolean mAdIdPermission; 95 private boolean mArDebugPermission; 96 @Nullable private String mRegistrationId; 97 @Nullable private String mSharedAggregationKeys; 98 @Nullable private Long mInstallTime; 99 @Nullable private String mParentId; 100 @Nullable private String mDebugJoinKey; 101 @Nullable private List<AttributedTrigger> mAttributedTriggers; 102 @Nullable private TriggerSpecs mTriggerSpecs; 103 @Nullable private String mTriggerSpecsString; 104 @Nullable private Long mNumStates; 105 @Nullable private Double mFlipProbability; 106 @Nullable private Integer mMaxEventLevelReports; 107 @Nullable private String mEventAttributionStatusString; 108 @Nullable private String mPrivacyParametersString = null; 109 private TriggerDataMatching mTriggerDataMatching; 110 @Nullable private String mPlatformAdId; 111 @Nullable private String mDebugAdId; 112 private Uri mRegistrationOrigin; 113 private boolean mCoarseEventReportDestinations; 114 @Nullable private UnsignedLong mSharedDebugKey; 115 private List<Pair<Long, Long>> mParsedEventReportWindows; 116 private boolean mDropSourceIfInstalled; 117 @Nullable private List<String> mAttributionScopes; 118 @Nullable private Long mAttributionScopeLimit; 119 @Nullable private Long mMaxEventStates; 120 121 /** 122 * Parses and returns the event_report_windows Returns null if parsing fails or if there is no 123 * windows provided by user. 124 */ 125 @Nullable parsedProcessedEventReportWindows()126 public List<Pair<Long, Long>> parsedProcessedEventReportWindows() { 127 if (mParsedEventReportWindows != null) { 128 return mParsedEventReportWindows; 129 } 130 131 if (mEventReportWindows == null) { 132 return null; 133 } 134 135 List<Pair<Long, Long>> rawWindows = parseEventReportWindows(mEventReportWindows); 136 if (rawWindows == null) { 137 return null; 138 } 139 // Append Event Time 140 mParsedEventReportWindows = new ArrayList<>(); 141 142 for (Pair<Long, Long> window : rawWindows) { 143 mParsedEventReportWindows.add( 144 new Pair<>(mEventTime + window.first, mEventTime + window.second)); 145 } 146 return mParsedEventReportWindows; 147 } 148 149 /** 150 * Returns parsed or default value of event report windows (can be used during {@code Source} 151 * construction since the method does not require a {@code Source} object). 152 * 153 * @param eventReportWindows string to be parsed 154 * @param sourceType Source's Type 155 * @param expiryDelta relative expiry value 156 * @param flags Flags 157 * @return parsed or default value 158 */ 159 @Nullable getOrDefaultEventReportWindowsForFlex( @ullable JSONObject eventReportWindows, @NonNull SourceType sourceType, long expiryDelta, @NonNull Flags flags)160 public static List<Pair<Long, Long>> getOrDefaultEventReportWindowsForFlex( 161 @Nullable JSONObject eventReportWindows, 162 @NonNull SourceType sourceType, 163 long expiryDelta, 164 @NonNull Flags flags) { 165 if (eventReportWindows == null) { 166 return getDefaultEventReportWindowsForFlex(expiryDelta, sourceType, flags); 167 } 168 return parseEventReportWindows(eventReportWindows); 169 } 170 171 /** Parses the provided eventReportWindows. Returns null if parsing fails */ 172 @Nullable parseEventReportWindows( @onNull String eventReportWindows)173 public static List<Pair<Long, Long>> parseEventReportWindows( 174 @NonNull String eventReportWindows) { 175 try { 176 JSONObject jsonObject = new JSONObject(eventReportWindows); 177 return parseEventReportWindows(jsonObject); 178 } catch (JSONException e) { 179 LoggerFactory.getMeasurementLogger() 180 .e(e, "Invalid JSON encountered: event_report_windows"); 181 return null; 182 } 183 } 184 185 /** Parses the provided eventReportWindows. Returns null if parsing fails */ 186 @Nullable parseEventReportWindows( @onNull JSONObject jsonObject)187 public static List<Pair<Long, Long>> parseEventReportWindows( 188 @NonNull JSONObject jsonObject) { 189 List<Pair<Long, Long>> result = new ArrayList<>(); 190 try { 191 long startTime = 0L; 192 if (!jsonObject.isNull("start_time")) { 193 startTime = jsonObject.getLong("start_time"); 194 } 195 JSONArray endTimesJSON = jsonObject.getJSONArray("end_times"); 196 197 for (int i = 0; i < endTimesJSON.length(); i++) { 198 long endTime = endTimesJSON.getLong(i); 199 Pair<Long, Long> window = Pair.create(startTime, endTime); 200 result.add(window); 201 startTime = endTime; 202 } 203 } catch (JSONException e) { 204 LoggerFactory.getMeasurementLogger() 205 .e(e, "Invalid JSON encountered: event_report_windows"); 206 return null; 207 } 208 return result; 209 } 210 getDefaultEventReportWindowsForFlex( long expiryDelta, SourceType sourceType, Flags flags)211 private static List<Pair<Long, Long>> getDefaultEventReportWindowsForFlex( 212 long expiryDelta, SourceType sourceType, Flags flags) { 213 List<Pair<Long, Long>> result = new ArrayList<>(); 214 // Obtain default early report windows without regard to install-related behaviour. 215 List<Long> defaultEarlyWindowEnds = 216 EventReportWindowCalcDelegate.getDefaultEarlyReportingWindowEnds(sourceType, false); 217 List<Long> earlyWindowEnds = 218 new EventReportWindowCalcDelegate(flags) 219 .getConfiguredOrDefaultEarlyReportingWindowEnds( 220 sourceType, defaultEarlyWindowEnds); 221 long windowStart = 0; 222 for (long earlyWindowEnd : earlyWindowEnds) { 223 if (earlyWindowEnd >= expiryDelta) { 224 break; 225 } 226 result.add(Pair.create(windowStart, earlyWindowEnd)); 227 windowStart = earlyWindowEnd; 228 } 229 result.add(Pair.create(windowStart, expiryDelta)); 230 return result; 231 } 232 233 /** 234 * Checks if the source has a valid number of report states and sets the value. mNumStates will 235 * not be set if invalid. 236 * 237 * @param flags flag values 238 */ validateAndSetNumReportStates(Flags flags)239 public boolean validateAndSetNumReportStates(Flags flags) { 240 if (mTriggerSpecs == null) { 241 long reportStateCountLimit = flags.getMeasurementMaxReportStatesPerSourceRegistration(); 242 EventReportWindowCalcDelegate eventReportWindowCalcDelegate = 243 new EventReportWindowCalcDelegate(flags); 244 int reportingWindowCountForNoising = 245 eventReportWindowCalcDelegate.getReportingWindowCountForNoising(this); 246 247 int numStars = eventReportWindowCalcDelegate.getMaxReportCount(this); 248 int numBars = getTriggerDataCardinality() 249 * reportingWindowCountForNoising 250 * getDestinationTypeMultiplier(flags); 251 252 long numStates = Combinatorics.getNumberOfStarsAndBarsSequences(numStars, numBars); 253 if (numStates > reportStateCountLimit) { 254 return false; 255 } 256 257 setNumStates(numStates); 258 259 return true; 260 } else { 261 return mTriggerSpecs.hasValidReportStateCount(this, flags); 262 } 263 } 264 265 /** 266 * Verifies whether the source contains a valid maximum event states value and assigns the 267 * default value if it's not specified in certain instances. 268 * 269 * @param flags flag values 270 */ validateAndSetMaxEventStates(Flags flags)271 public boolean validateAndSetMaxEventStates(Flags flags) { 272 if (!flags.getMeasurementEnableAttributionScope() || getAttributionScopeLimit() == null) { 273 return true; 274 } 275 Long numStates = 276 mTriggerSpecs == null 277 ? getNumStates(flags) 278 : mTriggerSpecs.getNumStates(this, flags); 279 if (numStates == null || numStates == 0L) { 280 throw new IllegalStateException( 281 "Num states should be validated before validating max event states"); 282 } 283 if (mMaxEventStates == null) { 284 // Fallback to default max event states. 285 setMaxEventStates(DEFAULT_MAX_EVENT_STATES); 286 } 287 if (getSourceType() == SourceType.EVENT && numStates > getMaxEventStates()) { 288 return false; 289 } 290 return true; 291 } 292 293 /** 294 * Checks if the source has valid information gain 295 * 296 * @param flags flag values 297 */ hasValidInformationGain(@onNull Flags flags)298 public boolean hasValidInformationGain(@NonNull Flags flags) { 299 if (mTriggerSpecs != null) { 300 return isFlexEventApiValueValid(flags); 301 } 302 return isFlexLiteApiValueValid(flags); 303 } 304 getInformationGainThreshold(Flags flags)305 private double getInformationGainThreshold(Flags flags) { 306 if (getDestinationTypeMultiplier(flags) == 2) { 307 return mSourceType == SourceType.EVENT 308 ? flags.getMeasurementFlexApiMaxInformationGainDualDestinationEvent() 309 : flags.getMeasurementFlexApiMaxInformationGainDualDestinationNavigation(); 310 } 311 return mSourceType == SourceType.EVENT 312 ? flags.getMeasurementFlexApiMaxInformationGainEvent() 313 : flags.getMeasurementFlexApiMaxInformationGainNavigation(); 314 } 315 316 /** 317 * Compute information gain for the source given the numTriggerStates. Attribution scope limit 318 * and max event states will also be considered if attribution scope is enabled. 319 * 320 * @param flags flag values. 321 * @param flipProbability the flip probability, used only if attribution scope is not enabled. 322 * @param numTriggerStates num of trigger states. 323 */ getInformationGain(Flags flags, long numTriggerStates, double flipProbability)324 double getInformationGain(Flags flags, long numTriggerStates, double flipProbability) { 325 if (flags.getMeasurementEnableAttributionScope()) { 326 long attributionScopeLimit = 327 getAttributionScopeLimit() == null ? 1L : getAttributionScopeLimit(); 328 long maxEventStates = getMaxEventStates() == null ? 1L : getMaxEventStates(); 329 return Combinatorics.getMaxInformationGainWithAttributionScope( 330 numTriggerStates, 331 attributionScopeLimit, 332 maxEventStates, 333 flags.getMeasurementPrivacyEpsilon()); 334 } 335 return Combinatorics.getInformationGain(numTriggerStates, flipProbability); 336 } 337 isFlexLiteApiValueValid(Flags flags)338 private boolean isFlexLiteApiValueValid(Flags flags) { 339 return getInformationGain(flags, getNumStates(flags), getFlipProbability(flags)) 340 <= getInformationGainThreshold(flags); 341 } 342 buildPrivacyParameters(Flags flags)343 private void buildPrivacyParameters(Flags flags) { 344 if (mTriggerSpecs != null) { 345 // Flex source has num states set during registration but not during attribution; also 346 // num states is only needed for information gain calculation, which is handled during 347 // registration. We set flip probability here for use in noising during registration and 348 // availability for debug reporting during attribution. 349 setFlipProbability(mTriggerSpecs.getFlipProbability(this, flags)); 350 return; 351 } 352 double epsilon = (double) flags.getMeasurementPrivacyEpsilon(); 353 setFlipProbability(Combinatorics.getFlipProbability(getNumStates(flags), epsilon)); 354 } 355 356 /** Should source report coarse destinations */ shouldReportCoarseDestinations(Flags flags)357 public boolean shouldReportCoarseDestinations(Flags flags) { 358 return flags.getMeasurementEnableCoarseEventReportDestinations() 359 && hasCoarseEventReportDestinations(); 360 } 361 362 /** 363 * Returns the number of destination types to use in privacy computations. 364 */ getDestinationTypeMultiplier(Flags flags)365 public int getDestinationTypeMultiplier(Flags flags) { 366 return !shouldReportCoarseDestinations(flags) && hasAppDestinations() 367 && hasWebDestinations() 368 ? SourceNoiseHandler.DUAL_DESTINATION_IMPRESSION_NOISE_MULTIPLIER 369 : SourceNoiseHandler.SINGLE_DESTINATION_IMPRESSION_NOISE_MULTIPLIER; 370 } 371 372 /** Returns true is manual event reporting windows are set otherwise false; */ hasManualEventReportWindows()373 public boolean hasManualEventReportWindows() { 374 return getEventReportWindows() != null; 375 } 376 377 @IntDef(value = {Status.ACTIVE, Status.IGNORED, Status.MARKED_TO_DELETE}) 378 @Retention(RetentionPolicy.SOURCE) 379 public @interface Status { 380 int ACTIVE = 0; 381 int IGNORED = 1; 382 int MARKED_TO_DELETE = 2; 383 } 384 385 @IntDef(value = { 386 AttributionMode.UNASSIGNED, 387 AttributionMode.TRUTHFULLY, 388 AttributionMode.NEVER, 389 AttributionMode.FALSELY, 390 }) 391 @Retention(RetentionPolicy.SOURCE) 392 public @interface AttributionMode { 393 int UNASSIGNED = 0; 394 int TRUTHFULLY = 1; 395 int NEVER = 2; 396 int FALSELY = 3; 397 } 398 399 /** The choice of the summary operator with the reporting window */ 400 public enum TriggerDataMatching { 401 MODULUS, 402 EXACT 403 } 404 405 public enum SourceType { 406 EVENT("event"), 407 NAVIGATION("navigation"); 408 409 private final String mValue; 410 SourceType(String value)411 SourceType(String value) { 412 mValue = value; 413 } 414 getValue()415 public String getValue() { 416 return mValue; 417 } 418 getIntValue()419 public int getIntValue() { 420 return this.equals(SourceType.NAVIGATION) ? 1 : 0; 421 } 422 } 423 Source()424 private Source() { 425 mEventReportDedupKeys = new ArrayList<>(); 426 mAggregateReportDedupKeys = new ArrayList<>(); 427 mStatus = Status.ACTIVE; 428 mSourceType = SourceType.EVENT; 429 // Making this default explicit since it anyway would occur on an uninitialised int field. 430 mPublisherType = EventSurfaceType.APP; 431 mAttributionMode = AttributionMode.UNASSIGNED; 432 mTriggerDataMatching = TriggerDataMatching.MODULUS; 433 mIsInstallAttributed = false; 434 mIsDebugReporting = false; 435 } 436 437 /** Class for storing fake report data. */ 438 public static class FakeReport { 439 private final UnsignedLong mTriggerData; 440 private final long mReportingTime; 441 private final long mTriggerTime; 442 private final List<Uri> mDestinations; 443 private final Pair<Long, Long> mTriggerSummaryBucket; 444 FakeReport( UnsignedLong triggerData, long reportingTime, long triggerTime, List<Uri> destinations, @Nullable Pair<Long, Long> triggerSummaryBucket)445 public FakeReport( 446 UnsignedLong triggerData, 447 long reportingTime, 448 long triggerTime, 449 List<Uri> destinations, 450 @Nullable Pair<Long, Long> triggerSummaryBucket) { 451 mTriggerData = triggerData; 452 mReportingTime = reportingTime; 453 mDestinations = destinations; 454 mTriggerTime = triggerTime; 455 mTriggerSummaryBucket = triggerSummaryBucket; 456 } 457 458 @Override equals(Object o)459 public boolean equals(Object o) { 460 if (this == o) return true; 461 if (!(o instanceof FakeReport)) return false; 462 FakeReport that = (FakeReport) o; 463 return Objects.equals(mTriggerData, that.mTriggerData) 464 && mReportingTime == that.mReportingTime 465 && mTriggerTime == that.mTriggerTime 466 && Objects.equals(mDestinations, that.mDestinations) 467 && Objects.equals(mTriggerSummaryBucket, that.mTriggerSummaryBucket); 468 } 469 470 @Override hashCode()471 public int hashCode() { 472 return Objects.hash( 473 mTriggerData, 474 mReportingTime, 475 mTriggerTime, 476 mDestinations, 477 mTriggerSummaryBucket); 478 } 479 getReportingTime()480 public long getReportingTime() { 481 return mReportingTime; 482 } 483 getTriggerTime()484 public long getTriggerTime() { 485 return mTriggerTime; 486 } 487 getTriggerData()488 public UnsignedLong getTriggerData() { 489 return mTriggerData; 490 } 491 getDestinations()492 public List<Uri> getDestinations() { 493 return mDestinations; 494 } 495 496 @Nullable getTriggerSummaryBucket()497 public Pair<Long, Long> getTriggerSummaryBucket() { 498 return mTriggerSummaryBucket; 499 } 500 } 501 502 /** 503 * Range of trigger metadata: [0, cardinality). 504 * 505 * @return Cardinality of {@link Trigger} metadata 506 */ getTriggerDataCardinality()507 public int getTriggerDataCardinality() { 508 if (getTriggerSpecs() != null) { 509 return getTriggerSpecs().getTriggerDataCardinality(); 510 } 511 return mSourceType == SourceType.EVENT 512 ? PrivacyParams.EVENT_TRIGGER_DATA_CARDINALITY 513 : PrivacyParams.getNavigationTriggerDataCardinality(); 514 } 515 516 /** 517 * @return the list of attributed triggers 518 */ 519 @Nullable getAttributedTriggers()520 public List<AttributedTrigger> getAttributedTriggers() { 521 return mAttributedTriggers; 522 } 523 524 /** 525 * @return all the attributed trigger IDs 526 */ getAttributedTriggerIds()527 public List<String> getAttributedTriggerIds() { 528 List<String> result = new ArrayList<>(); 529 for (AttributedTrigger attributedTrigger : mAttributedTriggers) { 530 result.add(attributedTrigger.getTriggerId()); 531 } 532 return result; 533 } 534 535 /** 536 * @return the JSON encoded current trigger status 537 */ 538 @NonNull attributedTriggersToJson()539 public String attributedTriggersToJson() { 540 JSONArray jsonArray = new JSONArray(); 541 for (AttributedTrigger trigger : mAttributedTriggers) { 542 jsonArray.put(trigger.encodeToJson()); 543 } 544 return jsonArray.toString(); 545 } 546 547 /** 548 * @return the JSON encoded current trigger status 549 */ 550 @NonNull attributedTriggersToJsonFlexApi()551 public String attributedTriggersToJsonFlexApi() { 552 JSONArray jsonArray = new JSONArray(); 553 for (AttributedTrigger trigger : mAttributedTriggers) { 554 jsonArray.put(trigger.encodeToJsonFlexApi()); 555 } 556 return jsonArray.toString(); 557 } 558 559 /** 560 * @return the flex event trigger specification 561 */ 562 @Nullable getTriggerSpecs()563 public TriggerSpecs getTriggerSpecs() { 564 return mTriggerSpecs; 565 } 566 567 @Override equals(Object obj)568 public boolean equals(Object obj) { 569 if (!(obj instanceof Source)) { 570 return false; 571 } 572 Source source = (Source) obj; 573 return Objects.equals(mPublisher, source.mPublisher) 574 && mPublisherType == source.mPublisherType 575 && areEqualNullableDestinations(mAppDestinations, source.mAppDestinations) 576 && areEqualNullableDestinations(mWebDestinations, source.mWebDestinations) 577 && Objects.equals(mEnrollmentId, source.mEnrollmentId) 578 && mPriority == source.mPriority 579 && mStatus == source.mStatus 580 && mExpiryTime == source.mExpiryTime 581 && Objects.equals(mEventReportWindow, source.mEventReportWindow) 582 && Objects.equals(mEventReportWindows, source.mEventReportWindows) 583 && Objects.equals(mAggregatableReportWindow, source.mAggregatableReportWindow) 584 && mEventTime == source.mEventTime 585 && mReinstallReattributionWindow == source.mReinstallReattributionWindow 586 && mAdIdPermission == source.mAdIdPermission 587 && mArDebugPermission == source.mArDebugPermission 588 && Objects.equals(mEventId, source.mEventId) 589 && Objects.equals(mDebugKey, source.mDebugKey) 590 && mSourceType == source.mSourceType 591 && Objects.equals(mEventReportDedupKeys, source.mEventReportDedupKeys) 592 && Objects.equals(mAggregateReportDedupKeys, source.mAggregateReportDedupKeys) 593 && Objects.equals(mRegistrant, source.mRegistrant) 594 && mAttributionMode == source.mAttributionMode 595 && mIsDebugReporting == source.mIsDebugReporting 596 && Objects.equals(mFilterDataString, source.mFilterDataString) 597 && Objects.equals(mSharedFilterDataKeys, source.mSharedFilterDataKeys) 598 && Objects.equals(mAggregateSource, source.mAggregateSource) 599 && mAggregateContributions == source.mAggregateContributions 600 && Objects.equals( 601 mAggregatableAttributionSource, source.mAggregatableAttributionSource) 602 && Objects.equals(mRegistrationId, source.mRegistrationId) 603 && Objects.equals(mSharedAggregationKeys, source.mSharedAggregationKeys) 604 && Objects.equals(mParentId, source.mParentId) 605 && Objects.equals(mInstallTime, source.mInstallTime) 606 && Objects.equals(mDebugJoinKey, source.mDebugJoinKey) 607 && Objects.equals(mPlatformAdId, source.mPlatformAdId) 608 && Objects.equals(mDebugAdId, source.mDebugAdId) 609 && Objects.equals(mRegistrationOrigin, source.mRegistrationOrigin) 610 && mCoarseEventReportDestinations == source.mCoarseEventReportDestinations 611 && Objects.equals(mAttributedTriggers, source.mAttributedTriggers) 612 && Objects.equals(mTriggerSpecs, source.mTriggerSpecs) 613 && Objects.equals(mTriggerSpecsString, source.mTriggerSpecsString) 614 && Objects.equals(mMaxEventLevelReports, source.mMaxEventLevelReports) 615 && Objects.equals( 616 mEventAttributionStatusString, source.mEventAttributionStatusString) 617 && Objects.equals(mPrivacyParametersString, source.mPrivacyParametersString) 618 && Objects.equals(mTriggerDataMatching, source.mTriggerDataMatching) 619 && Objects.equals(mSharedDebugKey, source.mSharedDebugKey) 620 && mDropSourceIfInstalled == source.mDropSourceIfInstalled 621 && Objects.equals(mAttributionScopes, source.mAttributionScopes) 622 && Objects.equals(mAttributionScopeLimit, source.mAttributionScopeLimit) 623 && Objects.equals(mMaxEventStates, source.mMaxEventStates); 624 } 625 626 @Override hashCode()627 public int hashCode() { 628 return Objects.hash( 629 mId, 630 mPublisher, 631 mPublisherType, 632 mAppDestinations, 633 mWebDestinations, 634 mEnrollmentId, 635 mPriority, 636 mStatus, 637 mExpiryTime, 638 mEventReportWindow, 639 mEventReportWindows, 640 mAggregatableReportWindow, 641 mReinstallReattributionWindow, 642 mEventTime, 643 mEventId, 644 mSourceType, 645 mEventReportDedupKeys, 646 mAggregateReportDedupKeys, 647 mFilterDataString, 648 mSharedFilterDataKeys, 649 mAggregateSource, 650 mAggregateContributions, 651 mAggregatableAttributionSource, 652 mDebugKey, 653 mAdIdPermission, 654 mArDebugPermission, 655 mRegistrationId, 656 mSharedAggregationKeys, 657 mInstallTime, 658 mDebugJoinKey, 659 mPlatformAdId, 660 mDebugAdId, 661 mRegistrationOrigin, 662 mDebugJoinKey, 663 mAttributedTriggers, 664 mTriggerSpecs, 665 mTriggerSpecsString, 666 mTriggerDataMatching, 667 mMaxEventLevelReports, 668 mEventAttributionStatusString, 669 mPrivacyParametersString, 670 mCoarseEventReportDestinations, 671 mSharedDebugKey, 672 mDropSourceIfInstalled, 673 mAttributionScopes, 674 mAttributionScopeLimit, 675 mMaxEventStates); 676 } 677 setAttributionMode(@ttributionMode int attributionMode)678 public void setAttributionMode(@AttributionMode int attributionMode) { 679 mAttributionMode = attributionMode; 680 } 681 682 /** 683 * Retrieve the attribution destinations corresponding to their destination type. 684 * 685 * @return a list of Uris. 686 */ getAttributionDestinations(@ventSurfaceType int destinationType)687 public List<Uri> getAttributionDestinations(@EventSurfaceType int destinationType) { 688 return destinationType == EventSurfaceType.APP ? mAppDestinations : mWebDestinations; 689 } 690 691 /** 692 * Unique identifier for the {@link Source}. 693 */ getId()694 public String getId() { 695 return mId; 696 } 697 698 /** 699 * Identifier provided by the registrant. 700 */ getEventId()701 public UnsignedLong getEventId() { 702 return mEventId; 703 } 704 705 /** 706 * Priority of the {@link Source}. 707 */ getPriority()708 public long getPriority() { 709 return mPriority; 710 } 711 712 /** 713 * Ad Tech enrollment ID 714 */ getEnrollmentId()715 public String getEnrollmentId() { 716 return mEnrollmentId; 717 } 718 719 /** Uri which registered the {@link Source}. */ getPublisher()720 public Uri getPublisher() { 721 return mPublisher; 722 } 723 724 /** The publisher type (e.g., app or web) {@link Source}. */ 725 @EventSurfaceType getPublisherType()726 public int getPublisherType() { 727 return mPublisherType; 728 } 729 730 /** Uris for the {@link Trigger}'s app destinations. */ 731 @Nullable getAppDestinations()732 public List<Uri> getAppDestinations() { 733 return mAppDestinations; 734 } 735 736 /** Uris for the {@link Trigger}'s web destinations. */ 737 @Nullable getWebDestinations()738 public List<Uri> getWebDestinations() { 739 return mWebDestinations; 740 } 741 742 /** Returns combined list of web and app attribution destinations. */ getAllAttributionDestinations()743 public List<Pair<Integer, String>> getAllAttributionDestinations() { 744 List<Pair<Integer, String>> destinations = new ArrayList<>(); 745 if (hasAppDestinations()) { 746 for (Uri appDestination : getAppDestinations()) { 747 destinations.add(Pair.create(EventSurfaceType.APP, appDestination.toString())); 748 } 749 } 750 if (hasWebDestinations()) { 751 for (Uri webDestination : getWebDestinations()) { 752 destinations.add(Pair.create(EventSurfaceType.WEB, webDestination.toString())); 753 } 754 } 755 return destinations; 756 } 757 758 /** 759 * Type of {@link Source}. Values: Event, Navigation. 760 */ getSourceType()761 public SourceType getSourceType() { 762 return mSourceType; 763 } 764 765 /** Time when {@link Source} will expire. */ getExpiryTime()766 public long getExpiryTime() { 767 return mExpiryTime; 768 } 769 770 /** Returns Event report window */ getEventReportWindow()771 public Long getEventReportWindow() { 772 return mEventReportWindow; 773 } 774 775 /** Returns reinstall reattribution window */ getReinstallReattributionWindow()776 public long getReinstallReattributionWindow() { 777 return mReinstallReattributionWindow; 778 } 779 780 /** 781 * Time when {@link Source} event report window will expire. (Appends the Event Time to window) 782 */ getEffectiveEventReportWindow()783 public long getEffectiveEventReportWindow() { 784 if (mEventReportWindow == null) { 785 return getExpiryTime(); 786 } 787 // TODO(b/290098169): Cleanup after a few releases 788 // Handling cases where ReportWindow is already stored as mEventTime + mEventReportWindow 789 if (mEventReportWindow > mEventTime) { 790 return mEventReportWindow; 791 } else { 792 return mEventTime + mEventReportWindow; 793 } 794 } 795 796 /** JSON string for event report windows */ getEventReportWindows()797 public String getEventReportWindows() { 798 return mEventReportWindows; 799 } 800 801 /** Time when {@link Source} aggregate report window will expire. */ getAggregatableReportWindow()802 public long getAggregatableReportWindow() { 803 return mAggregatableReportWindow; 804 } 805 806 /** Debug key of {@link Source}. */ 807 @Nullable getDebugKey()808 public UnsignedLong getDebugKey() { 809 return mDebugKey; 810 } 811 812 /** 813 * Time the event occurred. 814 */ getEventTime()815 public long getEventTime() { 816 return mEventTime; 817 } 818 819 /** Is Ad ID Permission Enabled. */ hasAdIdPermission()820 public boolean hasAdIdPermission() { 821 return mAdIdPermission; 822 } 823 824 /** Is Ar Debug Permission Enabled. */ hasArDebugPermission()825 public boolean hasArDebugPermission() { 826 return mArDebugPermission; 827 } 828 829 /** List of dedup keys for the attributed {@link Trigger}. */ getEventReportDedupKeys()830 public List<UnsignedLong> getEventReportDedupKeys() { 831 return mEventReportDedupKeys; 832 } 833 834 /** List of dedup keys used for generating Aggregate Reports. */ getAggregateReportDedupKeys()835 public List<UnsignedLong> getAggregateReportDedupKeys() { 836 return mAggregateReportDedupKeys; 837 } 838 839 /** Current status of the {@link Source}. */ 840 @Status getStatus()841 public int getStatus() { 842 return mStatus; 843 } 844 845 /** 846 * Registrant of this source, primarily an App. 847 */ getRegistrant()848 public Uri getRegistrant() { 849 return mRegistrant; 850 } 851 852 /** Selected mode for attribution. Values: Truthfully, Never, Falsely. */ 853 @AttributionMode getAttributionMode()854 public int getAttributionMode() { 855 return mAttributionMode; 856 } 857 858 /** Specification for trigger matching behaviour. Values: Modulus, Exact. */ getTriggerDataMatching()859 public TriggerDataMatching getTriggerDataMatching() { 860 return mTriggerDataMatching; 861 } 862 863 /** 864 * Attribution window for install events. 865 */ getInstallAttributionWindow()866 public long getInstallAttributionWindow() { 867 return mInstallAttributionWindow; 868 } 869 870 /** 871 * Cooldown for attributing post-install {@link Trigger} events. 872 */ getInstallCooldownWindow()873 public long getInstallCooldownWindow() { 874 return mInstallCooldownWindow; 875 } 876 877 /** Check if install detection is enabled for the source. */ isInstallDetectionEnabled()878 public boolean isInstallDetectionEnabled() { 879 return getInstallCooldownWindow() > 0 && hasAppDestinations(); 880 } 881 882 /** 883 * Is an App-install attributed to the {@link Source}. 884 */ isInstallAttributed()885 public boolean isInstallAttributed() { 886 return mIsInstallAttributed; 887 } 888 889 /** Is Ad Tech Opt-in to Debug Reporting {@link Source}. */ isDebugReporting()890 public boolean isDebugReporting() { 891 return mIsDebugReporting; 892 } 893 894 /** 895 * Check whether the parameter of flexible event API is valid or not. Currently, only max 896 * information gain is check because of the computation is complicated. Other straightforward 897 * value errors will be show in the debug log using LogUtil 898 * 899 * @return whether the parameters of flexible are valid 900 */ 901 @VisibleForTesting isFlexEventApiValueValid(Flags flags)902 public boolean isFlexEventApiValueValid(Flags flags) { 903 return mTriggerSpecs.getInformationGain(this, flags) <= getInformationGainThreshold(flags); 904 } 905 906 /** 907 * Returns aggregate filter data string used for aggregation. aggregate filter data json is a 908 * JSONObject in Attribution-Reporting-Register-Source header. Example: 909 * Attribution-Reporting-Register-Source: { // some other fields. "filter_data" : { 910 * "conversion_subdomain": ["electronics.megastore"], "product": ["1234", "2345"], "ctid": 911 * ["id"], ...... } } 912 */ getFilterDataString()913 public String getFilterDataString() { 914 return mFilterDataString; 915 } 916 917 /** 918 * Returns the shared filter data keys of the source as a unique list of strings. Example: 919 * ["click_duration", "campaign_type"] 920 */ 921 @Nullable getSharedFilterDataKeys()922 public String getSharedFilterDataKeys() { 923 return mSharedFilterDataKeys; 924 } 925 926 /** 927 * Returns aggregate source string used for aggregation. aggregate source json is a JSONArray. 928 * Example: [{ // Generates a "0x159" key piece (low order bits of the key) named // 929 * "campaignCounts" "id": "campaignCounts", "key_piece": "0x159", // User saw ad from campaign 930 * 345 (out of 511) }, { // Generates a "0x5" key piece (low order bits of the key) named 931 * "geoValue" "id": "geoValue", // Source-side geo region = 5 (US), out of a possible ~100 932 * regions. "key_piece": "0x5", }] 933 */ getAggregateSource()934 public String getAggregateSource() { 935 return mAggregateSource; 936 } 937 938 /** 939 * Returns the current sum of values the source contributed to aggregatable reports. 940 */ getAggregateContributions()941 public int getAggregateContributions() { 942 return mAggregateContributions; 943 } 944 945 /** 946 * Returns the AggregatableAttributionSource object, which is constructed using the aggregate 947 * source string and aggregate filter data string in Source. 948 */ getAggregatableAttributionSource( @onNull Trigger trigger, Flags flags)949 public Optional<AggregatableAttributionSource> getAggregatableAttributionSource( 950 @NonNull Trigger trigger, Flags flags) throws JSONException { 951 return flags.getMeasurementEnableLookbackWindowFilter() 952 ? getAggregatableAttributionSourceV2(trigger) 953 : getAggregatableAttributionSource(); 954 } 955 getAggregatableAttributionSource()956 private Optional<AggregatableAttributionSource> getAggregatableAttributionSource() 957 throws JSONException { 958 if (mAggregatableAttributionSource == null) { 959 if (mAggregateSource == null) { 960 mAggregatableAttributionSource = Optional.empty(); 961 return mAggregatableAttributionSource; 962 } 963 JSONObject jsonObject = new JSONObject(mAggregateSource); 964 TreeMap<String, BigInteger> aggregateSourceMap = new TreeMap<>(); 965 Iterator<String> keys = jsonObject.keys(); 966 while (keys.hasNext()) { 967 String key = keys.next(); 968 // Remove "0x" prefix. 969 String hexString = jsonObject.getString(key).substring(2); 970 BigInteger bigInteger = new BigInteger(hexString, 16); 971 aggregateSourceMap.put(key, bigInteger); 972 } 973 AggregatableAttributionSource aggregatableAttributionSource = 974 new AggregatableAttributionSource.Builder() 975 .setAggregatableSource(aggregateSourceMap) 976 .setFilterMap(getFilterData()) 977 .build(); 978 mAggregatableAttributionSource = Optional.of(aggregatableAttributionSource); 979 } 980 981 return mAggregatableAttributionSource; 982 } 983 getAggregatableAttributionSourceV2( @onNull Trigger trigger)984 private Optional<AggregatableAttributionSource> getAggregatableAttributionSourceV2( 985 @NonNull Trigger trigger) throws JSONException { 986 if (mAggregateSource == null) { 987 return Optional.empty(); 988 } 989 JSONObject jsonObject = new JSONObject(mAggregateSource); 990 TreeMap<String, BigInteger> aggregateSourceMap = new TreeMap<>(); 991 for (String key : jsonObject.keySet()) { 992 // Remove "0x" prefix. 993 String hexString = jsonObject.getString(key).substring(2); 994 BigInteger bigInteger = new BigInteger(hexString, 16); 995 aggregateSourceMap.put(key, bigInteger); 996 } 997 return Optional.of( 998 new AggregatableAttributionSource.Builder() 999 .setAggregatableSource(aggregateSourceMap) 1000 .setFilterMap(getFilterData(trigger)) 1001 .build()); 1002 } 1003 1004 /** Returns the registration id. */ 1005 @Nullable getRegistrationId()1006 public String getRegistrationId() { 1007 return mRegistrationId; 1008 } 1009 1010 /** 1011 * Returns the shared aggregation keys of the source as a unique list of strings. Example: 1012 * [“campaignCounts”] 1013 */ 1014 @Nullable getSharedAggregationKeys()1015 public String getSharedAggregationKeys() { 1016 return mSharedAggregationKeys; 1017 } 1018 1019 /** Returns the install time of the source which is the same value as event time. */ 1020 @Nullable getInstallTime()1021 public Long getInstallTime() { 1022 return mInstallTime; 1023 } 1024 1025 /** 1026 * Returns join key that should be matched with trigger's join key at the time of generating 1027 * reports. 1028 */ 1029 @Nullable getDebugJoinKey()1030 public String getDebugJoinKey() { 1031 return mDebugJoinKey; 1032 } 1033 1034 /** 1035 * Returns actual platform AdID from getAdId() on app source registration, to be matched with a 1036 * web trigger's {@link Trigger#getDebugAdId()} value at the time of generating reports. 1037 */ 1038 @Nullable getPlatformAdId()1039 public String getPlatformAdId() { 1040 return mPlatformAdId; 1041 } 1042 1043 /** 1044 * Returns SHA256 hash of AdID from registration response on web registration concatenated with 1045 * enrollment ID, to be matched with an app trigger's {@link Trigger#getPlatformAdId()} value at 1046 * the time of generating reports. 1047 */ 1048 @Nullable getDebugAdId()1049 public String getDebugAdId() { 1050 return mDebugAdId; 1051 } 1052 1053 /** 1054 * Indicates whether event report for this source should be generated with the destinations 1055 * where the conversion occurred or merge app and web destinations. Set to true of both app and 1056 * web destination should be merged into the array of event report. 1057 */ hasCoarseEventReportDestinations()1058 public boolean hasCoarseEventReportDestinations() { 1059 return mCoarseEventReportDestinations; 1060 } 1061 1062 /** Returns registration origin used to register the source */ getRegistrationOrigin()1063 public Uri getRegistrationOrigin() { 1064 return mRegistrationOrigin; 1065 } 1066 1067 /** Returns trigger specs */ getTriggerSpecsString()1068 public String getTriggerSpecsString() { 1069 return mTriggerSpecsString; 1070 } 1071 1072 /** 1073 * Returns the number of report states for the source (used only for computation and not 1074 * stored in the datastore) 1075 */ getNumStates(Flags flags)1076 private Long getNumStates(Flags flags) { 1077 if (mNumStates == null) { 1078 validateAndSetNumReportStates(flags); 1079 } 1080 return mNumStates; 1081 } 1082 1083 /** Returns flip probability (used only for computation and not stored in the datastore) */ getFlipProbability(Flags flags)1084 public Double getFlipProbability(Flags flags) { 1085 if (mFlipProbability == null) { 1086 buildPrivacyParameters(flags); 1087 } 1088 return mFlipProbability; 1089 } 1090 1091 /** Returns max bucket increments */ getMaxEventLevelReports()1092 public Integer getMaxEventLevelReports() { 1093 return mMaxEventLevelReports; 1094 } 1095 1096 /** 1097 * Returns the RBR provided or default value for max_event_level_reports 1098 * 1099 * @param sourceType Source's Type 1100 * @param maxEventLevelReports RBR parsed value for max_event_level_reports 1101 * @param flags Flag values 1102 */ 1103 @NonNull getOrDefaultMaxEventLevelReports( @onNull SourceType sourceType, @Nullable Integer maxEventLevelReports, @NonNull Flags flags)1104 public static Integer getOrDefaultMaxEventLevelReports( 1105 @NonNull SourceType sourceType, 1106 @Nullable Integer maxEventLevelReports, 1107 @NonNull Flags flags) { 1108 if (maxEventLevelReports == null) { 1109 return sourceType == Source.SourceType.NAVIGATION 1110 ? PrivacyParams.NAVIGATION_SOURCE_MAX_REPORTS 1111 : flags.getMeasurementVtcConfigurableMaxEventReportsCount(); 1112 } 1113 return maxEventLevelReports; 1114 } 1115 1116 /** Returns event attribution status of current source */ getEventAttributionStatus()1117 public String getEventAttributionStatus() { 1118 return mEventAttributionStatusString; 1119 } 1120 1121 /** Returns privacy parameters */ getPrivacyParameters()1122 public String getPrivacyParameters() { 1123 return mPrivacyParametersString; 1124 } 1125 1126 /** See {@link Source#getAppDestinations()} */ setAppDestinations(@ullable List<Uri> appDestinations)1127 public void setAppDestinations(@Nullable List<Uri> appDestinations) { 1128 mAppDestinations = appDestinations; 1129 } 1130 1131 /** See {@link Source#getWebDestinations()} */ setWebDestinations(@ullable List<Uri> webDestinations)1132 public void setWebDestinations(@Nullable List<Uri> webDestinations) { 1133 mWebDestinations = webDestinations; 1134 } 1135 1136 /** Set app install attribution to the {@link Source}. */ setInstallAttributed(boolean isInstallAttributed)1137 public void setInstallAttributed(boolean isInstallAttributed) { 1138 mIsInstallAttributed = isInstallAttributed; 1139 } 1140 1141 /** Set the number of report states for the {@link Source}. */ setNumStates(long numStates)1142 private void setNumStates(long numStates) { 1143 mNumStates = numStates; 1144 } 1145 1146 /** Set max event states for the {@link Source}. */ setMaxEventStates(long maxEventStates)1147 private void setMaxEventStates(long maxEventStates) { 1148 mMaxEventStates = maxEventStates; 1149 } 1150 1151 /** Set flip probability for the {@link Source}. */ setFlipProbability(double flipProbability)1152 private void setFlipProbability(double flipProbability) { 1153 mFlipProbability = flipProbability; 1154 } 1155 1156 /** 1157 * @return if it's a derived source, returns the ID of the source it was created from. If it is 1158 * null, it is an original source. 1159 */ 1160 @Nullable getParentId()1161 public String getParentId() { 1162 return mParentId; 1163 } 1164 1165 /** 1166 * Set the status. 1167 */ setStatus(@tatus int status)1168 public void setStatus(@Status int status) { 1169 mStatus = status; 1170 } 1171 1172 /** 1173 * Set the aggregate contributions value. 1174 */ setAggregateContributions(int aggregateContributions)1175 public void setAggregateContributions(int aggregateContributions) { 1176 mAggregateContributions = aggregateContributions; 1177 } 1178 1179 /** 1180 * Generates AggregatableFilterData from aggregate filter string in Source, including entries 1181 * for source type and duration from source to trigger if lookback window filter is enabled. 1182 */ getFilterData(@onNull Trigger trigger, Flags flags)1183 public FilterMap getFilterData(@NonNull Trigger trigger, Flags flags) throws JSONException { 1184 return flags.getMeasurementEnableLookbackWindowFilter() 1185 ? getFilterData(trigger) 1186 : getFilterData(); 1187 } 1188 getFilterData()1189 private FilterMap getFilterData() throws JSONException { 1190 if (mFilterData != null) { 1191 return mFilterData; 1192 } 1193 1194 if (mFilterDataString == null || mFilterDataString.isEmpty()) { 1195 mFilterData = new FilterMap.Builder().build(); 1196 } else { 1197 mFilterData = 1198 new FilterMap.Builder() 1199 .buildFilterData(new JSONObject(mFilterDataString)) 1200 .build(); 1201 } 1202 mFilterData 1203 .getAttributionFilterMap() 1204 .put("source_type", Collections.singletonList(mSourceType.getValue())); 1205 return mFilterData; 1206 } 1207 getFilterData(@onNull Trigger trigger)1208 private FilterMap getFilterData(@NonNull Trigger trigger) throws JSONException { 1209 FilterMap.Builder builder = new FilterMap.Builder(); 1210 if (mFilterDataString != null && !mFilterDataString.isEmpty()) { 1211 builder.buildFilterDataV2(new JSONObject(mFilterDataString)); 1212 } 1213 builder.addStringListValue("source_type", Collections.singletonList(mSourceType.getValue())) 1214 .addLongValue( 1215 FilterMap.LOOKBACK_WINDOW, 1216 TimeUnit.MILLISECONDS.toSeconds(trigger.getTriggerTime() - mEventTime)); 1217 return builder.build(); 1218 } 1219 extractSharedFilterMapFromJson(Map<String, V> attributionFilterMap)1220 private <V> Map<String, V> extractSharedFilterMapFromJson(Map<String, V> attributionFilterMap) 1221 throws JSONException { 1222 Map<String, V> sharedAttributionFilterMap = new HashMap<>(); 1223 JSONArray sharedFilterDataKeysArray = new JSONArray(mSharedFilterDataKeys); 1224 for (int i = 0; i < sharedFilterDataKeysArray.length(); ++i) { 1225 String filterKey = sharedFilterDataKeysArray.getString(i); 1226 if (attributionFilterMap.containsKey(filterKey)) { 1227 sharedAttributionFilterMap.put(filterKey, attributionFilterMap.get(filterKey)); 1228 } 1229 } 1230 return sharedAttributionFilterMap; 1231 } 1232 1233 /** 1234 * Generates AggregatableFilterData from aggregate filter string in Source, including entries 1235 * for source type and duration from source to trigger if lookback window filter is enabled. 1236 */ getSharedFilterData(@onNull Trigger trigger, Flags flags)1237 public FilterMap getSharedFilterData(@NonNull Trigger trigger, Flags flags) 1238 throws JSONException { 1239 FilterMap filterMap = getFilterData(trigger, flags); 1240 if (mSharedFilterDataKeys == null) { 1241 return filterMap; 1242 } 1243 if (flags.getMeasurementEnableLookbackWindowFilter()) { 1244 return new FilterMap.Builder() 1245 .setAttributionFilterMapWithLongValue( 1246 extractSharedFilterMapFromJson( 1247 filterMap.getAttributionFilterMapWithLongValue())) 1248 .build(); 1249 } else { 1250 return new FilterMap.Builder() 1251 .setAttributionFilterMap( 1252 extractSharedFilterMapFromJson(filterMap.getAttributionFilterMap())) 1253 .build(); 1254 } 1255 } 1256 1257 @Nullable getSharedDebugKey()1258 public UnsignedLong getSharedDebugKey() { 1259 return mSharedDebugKey; 1260 } 1261 1262 /** Returns true if the source should be dropped when the app is already installed. */ shouldDropSourceIfInstalled()1263 public boolean shouldDropSourceIfInstalled() { 1264 return mDropSourceIfInstalled; 1265 } 1266 1267 /** Returns true if the source has app destination(s), false otherwise. */ hasAppDestinations()1268 public boolean hasAppDestinations() { 1269 return mAppDestinations != null && mAppDestinations.size() > 0; 1270 } 1271 1272 /** Returns true if the source has web destination(s), false otherwise. */ hasWebDestinations()1273 public boolean hasWebDestinations() { 1274 return mWebDestinations != null && mWebDestinations.size() > 0; 1275 } 1276 areEqualNullableDestinations( List<Uri> destinations, List<Uri> otherDestinations)1277 private static boolean areEqualNullableDestinations( 1278 List<Uri> destinations, List<Uri> otherDestinations) { 1279 if (destinations == null && otherDestinations == null) { 1280 return true; 1281 } else if (destinations == null || otherDestinations == null) { 1282 return false; 1283 } else { 1284 return ImmutableMultiset.copyOf(destinations) 1285 .equals(ImmutableMultiset.copyOf(otherDestinations)); 1286 } 1287 } 1288 1289 /** Parses the event attribution status string. */ 1290 @VisibleForTesting parseEventAttributionStatus()1291 public void parseEventAttributionStatus() throws JSONException { 1292 JSONArray eventAttributionStatus = new JSONArray(mEventAttributionStatusString); 1293 for (int i = 0; i < eventAttributionStatus.length(); i++) { 1294 JSONObject json = eventAttributionStatus.getJSONObject(i); 1295 mAttributedTriggers.add(new AttributedTrigger(json)); 1296 } 1297 } 1298 1299 /** Build the attributed triggers list from the raw string */ buildAttributedTriggers()1300 public void buildAttributedTriggers() throws JSONException { 1301 if (mAttributedTriggers == null) { 1302 mAttributedTriggers = new ArrayList<>(); 1303 if (mEventAttributionStatusString != null && !mEventAttributionStatusString.isEmpty()) { 1304 parseEventAttributionStatus(); 1305 } 1306 } 1307 } 1308 1309 /** Build the trigger specs from the raw string */ buildTriggerSpecs()1310 public void buildTriggerSpecs() throws JSONException { 1311 buildAttributedTriggers(); 1312 if (mTriggerSpecs == null) { 1313 mTriggerSpecs = 1314 new TriggerSpecs( 1315 mTriggerSpecsString, 1316 getOrDefaultMaxEventLevelReports( 1317 mSourceType, mMaxEventLevelReports, FlagsFactory.getFlags()), 1318 this, 1319 mPrivacyParametersString); 1320 } 1321 } 1322 1323 /** Returns the attribution scopes attached to the source. */ 1324 @Nullable getAttributionScopes()1325 public List<String> getAttributionScopes() { 1326 return mAttributionScopes; 1327 } 1328 1329 /** Returns the attribution scope limit for the source. It should be positive. */ 1330 @Nullable getAttributionScopeLimit()1331 public Long getAttributionScopeLimit() { 1332 return mAttributionScopeLimit; 1333 } 1334 1335 /** Returns max number of event states. It should be positive. */ 1336 @Nullable getMaxEventStates()1337 public Long getMaxEventStates() { 1338 return mMaxEventStates; 1339 } 1340 1341 /** Builder for {@link Source}. */ 1342 public static final class Builder { 1343 private final Source mBuilding; 1344 Builder()1345 public Builder() { 1346 mBuilding = new Source(); 1347 } 1348 1349 /** 1350 * Copy builder. 1351 * 1352 * @param copyFrom copy from source 1353 * @return copied source 1354 */ from(Source copyFrom)1355 public static Builder from(Source copyFrom) { 1356 Builder builder = new Builder(); 1357 builder.setId(copyFrom.mId); 1358 builder.setRegistrationId(copyFrom.mRegistrationId); 1359 builder.setAggregateSource(copyFrom.mAggregateSource); 1360 builder.setExpiryTime(copyFrom.mExpiryTime); 1361 builder.setAppDestinations(copyFrom.mAppDestinations); 1362 builder.setWebDestinations(copyFrom.mWebDestinations); 1363 builder.setSharedAggregationKeys(copyFrom.mSharedAggregationKeys); 1364 builder.setEventId(copyFrom.mEventId); 1365 builder.setRegistrant(copyFrom.mRegistrant); 1366 builder.setEventTime(copyFrom.mEventTime); 1367 builder.setPublisher(copyFrom.mPublisher); 1368 builder.setPublisherType(copyFrom.mPublisherType); 1369 builder.setInstallCooldownWindow(copyFrom.mInstallCooldownWindow); 1370 builder.setInstallAttributed(copyFrom.mIsInstallAttributed); 1371 builder.setInstallAttributionWindow(copyFrom.mInstallAttributionWindow); 1372 builder.setReinstallReattributionWindow(copyFrom.mReinstallReattributionWindow); 1373 builder.setSourceType(copyFrom.mSourceType); 1374 builder.setAdIdPermission(copyFrom.mAdIdPermission); 1375 builder.setAggregateContributions(copyFrom.mAggregateContributions); 1376 builder.setArDebugPermission(copyFrom.mArDebugPermission); 1377 builder.setAttributionMode(copyFrom.mAttributionMode); 1378 builder.setDebugKey(copyFrom.mDebugKey); 1379 builder.setEventReportDedupKeys(copyFrom.mEventReportDedupKeys); 1380 builder.setAggregateReportDedupKeys(copyFrom.mAggregateReportDedupKeys); 1381 builder.setEventReportWindow(copyFrom.mEventReportWindow); 1382 builder.setEventReportWindows(copyFrom.mEventReportWindows); 1383 builder.setMaxEventLevelReports(copyFrom.mMaxEventLevelReports); 1384 builder.setAggregatableReportWindow(copyFrom.mAggregatableReportWindow); 1385 builder.setEnrollmentId(copyFrom.mEnrollmentId); 1386 builder.setFilterDataString(copyFrom.mFilterDataString); 1387 builder.setSharedFilterDataKeys(copyFrom.mSharedFilterDataKeys); 1388 builder.setInstallTime(copyFrom.mInstallTime); 1389 builder.setIsDebugReporting(copyFrom.mIsDebugReporting); 1390 builder.setPriority(copyFrom.mPriority); 1391 builder.setStatus(copyFrom.mStatus); 1392 builder.setDebugJoinKey(copyFrom.mDebugJoinKey); 1393 builder.setPlatformAdId(copyFrom.mPlatformAdId); 1394 builder.setDebugAdId(copyFrom.mDebugAdId); 1395 builder.setRegistrationOrigin(copyFrom.mRegistrationOrigin); 1396 builder.setAttributedTriggers(copyFrom.mAttributedTriggers); 1397 builder.setTriggerSpecs(copyFrom.mTriggerSpecs); 1398 builder.setTriggerDataMatching(copyFrom.mTriggerDataMatching); 1399 builder.setCoarseEventReportDestinations(copyFrom.mCoarseEventReportDestinations); 1400 builder.setSharedDebugKey(copyFrom.mSharedDebugKey); 1401 builder.setDropSourceIfInstalled(copyFrom.mDropSourceIfInstalled); 1402 builder.setAttributionScopes(copyFrom.mAttributionScopes); 1403 builder.setAttributionScopeLimit(copyFrom.mAttributionScopeLimit); 1404 builder.setMaxEventStates(copyFrom.mMaxEventStates); 1405 return builder; 1406 } 1407 1408 /** See {@link Source#getId()}. */ 1409 @NonNull setId(@onNull String id)1410 public Builder setId(@NonNull String id) { 1411 mBuilding.mId = id; 1412 return this; 1413 } 1414 1415 /** See {@link Source#getEventId()}. */ 1416 @NonNull setEventId(UnsignedLong eventId)1417 public Builder setEventId(UnsignedLong eventId) { 1418 mBuilding.mEventId = eventId; 1419 return this; 1420 } 1421 1422 /** See {@link Source#getPublisher()}. */ 1423 @NonNull setPublisher(@onNull Uri publisher)1424 public Builder setPublisher(@NonNull Uri publisher) { 1425 Validation.validateUri(publisher); 1426 mBuilding.mPublisher = publisher; 1427 return this; 1428 } 1429 1430 /** See {@link Source#getPublisherType()}. */ 1431 @NonNull setPublisherType(@ventSurfaceType int publisherType)1432 public Builder setPublisherType(@EventSurfaceType int publisherType) { 1433 mBuilding.mPublisherType = publisherType; 1434 return this; 1435 } 1436 1437 /** See {@link Source#getAppDestinations()}. */ 1438 @NonNull setAppDestinations(@ullable List<Uri> appDestinations)1439 public Builder setAppDestinations(@Nullable List<Uri> appDestinations) { 1440 Optional.ofNullable(appDestinations).ifPresent(uris -> { 1441 Validation.validateNotEmpty(uris); 1442 if (uris.size() > 1) { 1443 throw new IllegalArgumentException("Received more than one app destination"); 1444 } 1445 Validation.validateUri(uris.toArray(new Uri[0])); 1446 }); 1447 mBuilding.mAppDestinations = appDestinations; 1448 return this; 1449 } 1450 1451 /** See {@link Source#getWebDestinations()}. */ 1452 @NonNull setWebDestinations(@ullable List<Uri> webDestinations)1453 public Builder setWebDestinations(@Nullable List<Uri> webDestinations) { 1454 Optional.ofNullable(webDestinations).ifPresent(uris -> { 1455 Validation.validateNotEmpty(uris); 1456 Validation.validateUri(uris.toArray(new Uri[0])); 1457 }); 1458 mBuilding.mWebDestinations = webDestinations; 1459 return this; 1460 } 1461 1462 /** See {@link Source#getEnrollmentId()}. */ 1463 @NonNull setEnrollmentId(@onNull String enrollmentId)1464 public Builder setEnrollmentId(@NonNull String enrollmentId) { 1465 mBuilding.mEnrollmentId = enrollmentId; 1466 return this; 1467 } 1468 1469 /** See {@link Source#hasAdIdPermission()} */ setAdIdPermission(boolean adIdPermission)1470 public Source.Builder setAdIdPermission(boolean adIdPermission) { 1471 mBuilding.mAdIdPermission = adIdPermission; 1472 return this; 1473 } 1474 1475 /** See {@link Source#hasArDebugPermission()} */ setArDebugPermission(boolean arDebugPermission)1476 public Source.Builder setArDebugPermission(boolean arDebugPermission) { 1477 mBuilding.mArDebugPermission = arDebugPermission; 1478 return this; 1479 } 1480 1481 /** See {@link Source#getEventId()}. */ 1482 @NonNull setEventTime(long eventTime)1483 public Builder setEventTime(long eventTime) { 1484 mBuilding.mEventTime = eventTime; 1485 return this; 1486 } 1487 1488 /** 1489 * See {@link Source#getExpiryTime()}. 1490 */ setExpiryTime(long expiryTime)1491 public Builder setExpiryTime(long expiryTime) { 1492 mBuilding.mExpiryTime = expiryTime; 1493 return this; 1494 } 1495 1496 /** See {@link Source#getEventReportWindow()}. */ setEventReportWindow(Long eventReportWindow)1497 public Builder setEventReportWindow(Long eventReportWindow) { 1498 mBuilding.mEventReportWindow = eventReportWindow; 1499 return this; 1500 } 1501 1502 /** See {@link Source#getEventReportWindows()} ()}. */ setEventReportWindows(String eventReportWindows)1503 public Builder setEventReportWindows(String eventReportWindows) { 1504 mBuilding.mEventReportWindows = eventReportWindows; 1505 return this; 1506 } 1507 1508 /** See {@link Source#getAggregatableReportWindow()}. */ setAggregatableReportWindow(Long aggregateReportWindow)1509 public Builder setAggregatableReportWindow(Long aggregateReportWindow) { 1510 mBuilding.mAggregatableReportWindow = aggregateReportWindow; 1511 return this; 1512 } 1513 1514 /** See {@link Source#getReinstallReattributionWindow()}. */ setReinstallReattributionWindow(Long reinstallReattributionWindow)1515 public Builder setReinstallReattributionWindow(Long reinstallReattributionWindow) { 1516 mBuilding.mReinstallReattributionWindow = reinstallReattributionWindow; 1517 return this; 1518 } 1519 1520 /** See {@link Source#getPriority()}. */ 1521 @NonNull setPriority(long priority)1522 public Builder setPriority(long priority) { 1523 mBuilding.mPriority = priority; 1524 return this; 1525 } 1526 1527 /** See {@link Source#getDebugKey()}. */ setDebugKey(@ullable UnsignedLong debugKey)1528 public Builder setDebugKey(@Nullable UnsignedLong debugKey) { 1529 mBuilding.mDebugKey = debugKey; 1530 return this; 1531 } 1532 1533 /** See {@link Source#isDebugReporting()}. */ setIsDebugReporting(boolean isDebugReporting)1534 public Builder setIsDebugReporting(boolean isDebugReporting) { 1535 mBuilding.mIsDebugReporting = isDebugReporting; 1536 return this; 1537 } 1538 1539 /** See {@link Source#getSourceType()}. */ 1540 @NonNull setSourceType(@onNull SourceType sourceType)1541 public Builder setSourceType(@NonNull SourceType sourceType) { 1542 Validation.validateNonNull(sourceType); 1543 mBuilding.mSourceType = sourceType; 1544 return this; 1545 } 1546 1547 /** See {@link Source#getEventReportDedupKeys()}. */ 1548 @NonNull setEventReportDedupKeys(@ullable List<UnsignedLong> mEventReportDedupKeys)1549 public Builder setEventReportDedupKeys(@Nullable List<UnsignedLong> mEventReportDedupKeys) { 1550 mBuilding.mEventReportDedupKeys = mEventReportDedupKeys; 1551 return this; 1552 } 1553 1554 /** See {@link Source#getAggregateReportDedupKeys()}. */ 1555 @NonNull setAggregateReportDedupKeys( @onNull List<UnsignedLong> mAggregateReportDedupKeys)1556 public Builder setAggregateReportDedupKeys( 1557 @NonNull List<UnsignedLong> mAggregateReportDedupKeys) { 1558 mBuilding.mAggregateReportDedupKeys = mAggregateReportDedupKeys; 1559 return this; 1560 } 1561 1562 /** See {@link Source#getStatus()}. */ 1563 @NonNull setStatus(@tatus int status)1564 public Builder setStatus(@Status int status) { 1565 mBuilding.mStatus = status; 1566 return this; 1567 } 1568 1569 /** See {@link Source#getRegistrant()} */ 1570 @NonNull setRegistrant(@onNull Uri registrant)1571 public Builder setRegistrant(@NonNull Uri registrant) { 1572 Validation.validateUri(registrant); 1573 mBuilding.mRegistrant = registrant; 1574 return this; 1575 } 1576 1577 /** See {@link Source#getAttributionMode()} */ 1578 @NonNull setAttributionMode(@ttributionMode int attributionMode)1579 public Builder setAttributionMode(@AttributionMode int attributionMode) { 1580 mBuilding.mAttributionMode = attributionMode; 1581 return this; 1582 } 1583 1584 /** See {@link Source#getTriggerDataMatching()} */ 1585 @NonNull setTriggerDataMatching(TriggerDataMatching triggerDataMatching)1586 public Builder setTriggerDataMatching(TriggerDataMatching triggerDataMatching) { 1587 mBuilding.mTriggerDataMatching = triggerDataMatching; 1588 return this; 1589 } 1590 1591 /** See {@link Source#getInstallAttributionWindow()} */ 1592 @NonNull setInstallAttributionWindow(long installAttributionWindow)1593 public Builder setInstallAttributionWindow(long installAttributionWindow) { 1594 mBuilding.mInstallAttributionWindow = installAttributionWindow; 1595 return this; 1596 } 1597 1598 /** See {@link Source#getInstallCooldownWindow()} */ 1599 @NonNull setInstallCooldownWindow(long installCooldownWindow)1600 public Builder setInstallCooldownWindow(long installCooldownWindow) { 1601 mBuilding.mInstallCooldownWindow = installCooldownWindow; 1602 return this; 1603 } 1604 1605 /** See {@link Source#isInstallAttributed()} */ 1606 @NonNull setInstallAttributed(boolean installAttributed)1607 public Builder setInstallAttributed(boolean installAttributed) { 1608 mBuilding.mIsInstallAttributed = installAttributed; 1609 return this; 1610 } 1611 1612 /** See {@link Source#getFilterDataString()}. */ setFilterDataString(@ullable String filterMap)1613 public Builder setFilterDataString(@Nullable String filterMap) { 1614 mBuilding.mFilterDataString = filterMap; 1615 return this; 1616 } 1617 1618 /** See {@link Source#getSharedFilterDataKeys()}. */ setSharedFilterDataKeys(@ullable String sharedFilterDataKeys)1619 public Builder setSharedFilterDataKeys(@Nullable String sharedFilterDataKeys) { 1620 mBuilding.mSharedFilterDataKeys = sharedFilterDataKeys; 1621 return this; 1622 } 1623 1624 /** See {@link Source#getAggregateSource()} */ 1625 @NonNull setAggregateSource(@ullable String aggregateSource)1626 public Builder setAggregateSource(@Nullable String aggregateSource) { 1627 mBuilding.mAggregateSource = aggregateSource; 1628 return this; 1629 } 1630 1631 /** See {@link Source#getAggregateContributions()} */ 1632 @NonNull setAggregateContributions(int aggregateContributions)1633 public Builder setAggregateContributions(int aggregateContributions) { 1634 mBuilding.mAggregateContributions = aggregateContributions; 1635 return this; 1636 } 1637 1638 /** See {@link Source#getRegistrationId()} */ 1639 @NonNull setRegistrationId(@ullable String registrationId)1640 public Builder setRegistrationId(@Nullable String registrationId) { 1641 mBuilding.mRegistrationId = registrationId; 1642 return this; 1643 } 1644 1645 /** See {@link Source#getSharedAggregationKeys()} */ 1646 @NonNull setSharedAggregationKeys(@ullable String sharedAggregationKeys)1647 public Builder setSharedAggregationKeys(@Nullable String sharedAggregationKeys) { 1648 mBuilding.mSharedAggregationKeys = sharedAggregationKeys; 1649 return this; 1650 } 1651 1652 /** See {@link Source#getInstallTime()} */ 1653 @NonNull setInstallTime(@ullable Long installTime)1654 public Builder setInstallTime(@Nullable Long installTime) { 1655 mBuilding.mInstallTime = installTime; 1656 return this; 1657 } 1658 1659 /** See {@link Source#getParentId()} */ 1660 @NonNull setParentId(@ullable String parentId)1661 public Builder setParentId(@Nullable String parentId) { 1662 mBuilding.mParentId = parentId; 1663 return this; 1664 } 1665 1666 /** See {@link Source#getAggregatableAttributionSource()} */ 1667 @NonNull setAggregatableAttributionSource( @ullable AggregatableAttributionSource aggregatableAttributionSource)1668 public Builder setAggregatableAttributionSource( 1669 @Nullable AggregatableAttributionSource aggregatableAttributionSource) { 1670 mBuilding.mAggregatableAttributionSource = 1671 Optional.ofNullable(aggregatableAttributionSource); 1672 return this; 1673 } 1674 1675 /** See {@link Source#getDebugJoinKey()} */ 1676 @NonNull setDebugJoinKey(@ullable String debugJoinKey)1677 public Builder setDebugJoinKey(@Nullable String debugJoinKey) { 1678 mBuilding.mDebugJoinKey = debugJoinKey; 1679 return this; 1680 } 1681 1682 /** See {@link Source#getPlatformAdId()} */ 1683 @NonNull setPlatformAdId(@ullable String platformAdId)1684 public Builder setPlatformAdId(@Nullable String platformAdId) { 1685 mBuilding.mPlatformAdId = platformAdId; 1686 return this; 1687 } 1688 1689 /** See {@link Source#getDebugAdId()} */ 1690 @NonNull setDebugAdId(@ullable String debugAdId)1691 public Builder setDebugAdId(@Nullable String debugAdId) { 1692 mBuilding.mDebugAdId = debugAdId; 1693 return this; 1694 } 1695 1696 /** See {@link Source#getRegistrationOrigin()} ()} */ 1697 @NonNull setRegistrationOrigin(Uri registrationOrigin)1698 public Builder setRegistrationOrigin(Uri registrationOrigin) { 1699 mBuilding.mRegistrationOrigin = registrationOrigin; 1700 return this; 1701 } 1702 1703 /** See {@link Source#getAttributedTriggers()} */ 1704 @NonNull setAttributedTriggers(@onNull List<AttributedTrigger> attributedTriggers)1705 public Builder setAttributedTriggers(@NonNull List<AttributedTrigger> attributedTriggers) { 1706 mBuilding.mAttributedTriggers = attributedTriggers; 1707 return this; 1708 } 1709 1710 /** See {@link Source#getTriggerSpecs()} */ 1711 @NonNull setTriggerSpecs(@ullable TriggerSpecs triggerSpecs)1712 public Builder setTriggerSpecs(@Nullable TriggerSpecs triggerSpecs) { 1713 mBuilding.mTriggerSpecs = triggerSpecs; 1714 return this; 1715 } 1716 1717 /** See {@link Source#hasCoarseEventReportDestinations()} */ 1718 @NonNull setCoarseEventReportDestinations(boolean coarseEventReportDestinations)1719 public Builder setCoarseEventReportDestinations(boolean coarseEventReportDestinations) { 1720 mBuilding.mCoarseEventReportDestinations = coarseEventReportDestinations; 1721 return this; 1722 } 1723 1724 /** See {@link Source#getTriggerSpecsString()} */ 1725 @NonNull setTriggerSpecsString(@ullable String triggerSpecsString)1726 public Builder setTriggerSpecsString(@Nullable String triggerSpecsString) { 1727 mBuilding.mTriggerSpecsString = triggerSpecsString; 1728 return this; 1729 } 1730 1731 /** See {@link Source#getMaxEventLevelReports()} */ 1732 @NonNull setMaxEventLevelReports(@ullable Integer maxEventLevelReports)1733 public Builder setMaxEventLevelReports(@Nullable Integer maxEventLevelReports) { 1734 mBuilding.mMaxEventLevelReports = maxEventLevelReports; 1735 return this; 1736 } 1737 1738 /** See {@link Source#getEventAttributionStatus()} */ 1739 @NonNull setEventAttributionStatus(@ullable String eventAttributionStatus)1740 public Builder setEventAttributionStatus(@Nullable String eventAttributionStatus) { 1741 mBuilding.mEventAttributionStatusString = eventAttributionStatus; 1742 return this; 1743 } 1744 1745 /** See {@link Source#getPrivacyParameters()} */ 1746 @NonNull setPrivacyParameters(@ullable String privacyParameters)1747 public Builder setPrivacyParameters(@Nullable String privacyParameters) { 1748 mBuilding.mPrivacyParametersString = privacyParameters; 1749 return this; 1750 } 1751 1752 /** See {@link Source#getSharedDebugKey()}. */ 1753 @NonNull setSharedDebugKey(@ullable UnsignedLong sharedDebugKey)1754 public Builder setSharedDebugKey(@Nullable UnsignedLong sharedDebugKey) { 1755 mBuilding.mSharedDebugKey = sharedDebugKey; 1756 return this; 1757 } 1758 1759 /** See {@link Source#shouldDropSourceIfInstalled()}. */ 1760 @NonNull setDropSourceIfInstalled(boolean dropSourceIfInstalled)1761 public Builder setDropSourceIfInstalled(boolean dropSourceIfInstalled) { 1762 mBuilding.mDropSourceIfInstalled = dropSourceIfInstalled; 1763 return this; 1764 } 1765 1766 /** See {@link Source#getAttributionScopes()}. */ 1767 @NonNull setAttributionScopes(@ullable List<String> attributionScopes)1768 public Builder setAttributionScopes(@Nullable List<String> attributionScopes) { 1769 mBuilding.mAttributionScopes = attributionScopes; 1770 return this; 1771 } 1772 1773 /** See {@link Source#getAttributionScopeLimit()}. */ 1774 @NonNull setAttributionScopeLimit(@ullable Long attributionScopeLimit)1775 public Builder setAttributionScopeLimit(@Nullable Long attributionScopeLimit) { 1776 mBuilding.mAttributionScopeLimit = attributionScopeLimit; 1777 return this; 1778 } 1779 1780 /** See {@link Source#getMaxEventStates()}. */ 1781 @NonNull setMaxEventStates(@ullable Long maxEventStates)1782 public Builder setMaxEventStates(@Nullable Long maxEventStates) { 1783 mBuilding.mMaxEventStates = maxEventStates; 1784 return this; 1785 } 1786 1787 /** Build the {@link Source}. */ 1788 @NonNull build()1789 public Source build() { 1790 Validation.validateNonNull( 1791 mBuilding.mPublisher, 1792 mBuilding.mEnrollmentId, 1793 mBuilding.mRegistrant, 1794 mBuilding.mSourceType, 1795 mBuilding.mAggregateReportDedupKeys, 1796 mBuilding.mEventReportDedupKeys, 1797 mBuilding.mRegistrationOrigin); 1798 1799 return mBuilding; 1800 } 1801 } 1802 } 1803