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 24 import com.android.adservices.service.Flags; 25 import com.android.adservices.service.common.WebAddresses; 26 import com.android.adservices.service.measurement.aggregation.AggregatableAttributionTrigger; 27 import com.android.adservices.service.measurement.aggregation.AggregatableNamedBudget; 28 import com.android.adservices.service.measurement.aggregation.AggregatableValuesConfig; 29 import com.android.adservices.service.measurement.aggregation.AggregateDebugReporting; 30 import com.android.adservices.service.measurement.aggregation.AggregateDeduplicationKey; 31 import com.android.adservices.service.measurement.aggregation.AggregateReport; 32 import com.android.adservices.service.measurement.aggregation.AggregateTriggerData; 33 import com.android.adservices.service.measurement.util.Filter; 34 import com.android.adservices.service.measurement.util.Filter.FilterContract; 35 import com.android.adservices.service.measurement.util.JsonUtil; 36 import com.android.adservices.service.measurement.util.UnsignedLong; 37 import com.android.adservices.service.measurement.util.Validation; 38 39 import org.json.JSONArray; 40 import org.json.JSONException; 41 import org.json.JSONObject; 42 43 import java.lang.annotation.Retention; 44 import java.lang.annotation.RetentionPolicy; 45 import java.math.BigInteger; 46 import java.util.ArrayList; 47 import java.util.HashMap; 48 import java.util.HashSet; 49 import java.util.Iterator; 50 import java.util.List; 51 import java.util.Map; 52 import java.util.Objects; 53 import java.util.Optional; 54 import java.util.Set; 55 56 /** POJO for Trigger. */ 57 public class Trigger { 58 59 private String mId; 60 private Uri mAttributionDestination; 61 @EventSurfaceType private int mDestinationType; 62 private String mEnrollmentId; 63 private long mTriggerTime; 64 private @NonNull String mEventTriggers; 65 @Status private int mStatus; 66 private Uri mRegistrant; 67 private String mAggregateTriggerData; 68 private String mAggregateValuesString; 69 private String mAggregateDeduplicationKeys; 70 private boolean mIsDebugReporting; 71 private Optional<AggregatableAttributionTrigger> mAggregatableAttributionTrigger; 72 private String mFilters; 73 private String mNotFilters; 74 @Nullable private UnsignedLong mDebugKey; 75 private boolean mAdIdPermission; 76 private boolean mArDebugPermission; 77 @Nullable private String mAttributionConfig; 78 @Nullable private String mAdtechKeyMapping; 79 @Nullable private String mDebugJoinKey; 80 @Nullable private String mPlatformAdId; 81 @Nullable private String mDebugAdId; 82 private Uri mRegistrationOrigin; 83 @Nullable private Uri mAggregationCoordinatorOrigin; 84 private SourceRegistrationTimeConfig mAggregatableSourceRegistrationTimeConfig; 85 @Nullable private String mTriggerContextId; 86 @Nullable private String mAttributionScopesString; 87 @Nullable private Integer mAggregatableFilteringIdMaxBytes; 88 @Nullable private String mAggregateDebugReportingString; 89 @Nullable private AggregateDebugReporting mAggregateDebugReporting; 90 @Nullable private String mNamedBudgetsString; 91 92 @IntDef(value = {Status.PENDING, Status.IGNORED, Status.ATTRIBUTED, Status.MARKED_TO_DELETE}) 93 @Retention(RetentionPolicy.SOURCE) 94 public @interface Status { 95 int PENDING = 0; 96 int IGNORED = 1; 97 int ATTRIBUTED = 2; 98 int MARKED_TO_DELETE = 3; 99 } 100 101 public enum SourceRegistrationTimeConfig { 102 INCLUDE, 103 EXCLUDE 104 } 105 Trigger()106 private Trigger() { 107 mStatus = Status.PENDING; 108 // Making this default explicit since it anyway occur on an uninitialised int field. 109 mDestinationType = EventSurfaceType.APP; 110 mIsDebugReporting = false; 111 } 112 113 @Override equals(Object obj)114 public boolean equals(Object obj) { 115 if (!(obj instanceof Trigger)) { 116 return false; 117 } 118 Trigger trigger = (Trigger) obj; 119 return Objects.equals(mId, trigger.getId()) 120 && Objects.equals(mAttributionDestination, trigger.mAttributionDestination) 121 && mDestinationType == trigger.mDestinationType 122 && Objects.equals(mEnrollmentId, trigger.mEnrollmentId) 123 && mTriggerTime == trigger.mTriggerTime 124 && Objects.equals(mDebugKey, trigger.mDebugKey) 125 && Objects.equals(mEventTriggers, trigger.mEventTriggers) 126 && mStatus == trigger.mStatus 127 && mIsDebugReporting == trigger.mIsDebugReporting 128 && mAdIdPermission == trigger.mAdIdPermission 129 && mArDebugPermission == trigger.mArDebugPermission 130 && mAggregatableSourceRegistrationTimeConfig 131 == trigger.mAggregatableSourceRegistrationTimeConfig 132 && Objects.equals(mRegistrant, trigger.mRegistrant) 133 && Objects.equals(mAggregateTriggerData, trigger.mAggregateTriggerData) 134 && Objects.equals(mAggregateValuesString, trigger.mAggregateValuesString) 135 && Objects.equals( 136 mAggregatableAttributionTrigger, trigger.mAggregatableAttributionTrigger) 137 && Objects.equals(mFilters, trigger.mFilters) 138 && Objects.equals(mNotFilters, trigger.mNotFilters) 139 && Objects.equals(mAttributionConfig, trigger.mAttributionConfig) 140 && Objects.equals(mAdtechKeyMapping, trigger.mAdtechKeyMapping) 141 && Objects.equals(mAggregateDeduplicationKeys, trigger.mAggregateDeduplicationKeys) 142 && Objects.equals(mDebugJoinKey, trigger.mDebugJoinKey) 143 && Objects.equals(mPlatformAdId, trigger.mPlatformAdId) 144 && Objects.equals(mDebugAdId, trigger.mDebugAdId) 145 && Objects.equals(mRegistrationOrigin, trigger.mRegistrationOrigin) 146 && Objects.equals(mTriggerContextId, trigger.mTriggerContextId) 147 && Objects.equals(mAttributionScopesString, trigger.mAttributionScopesString) 148 && Objects.equals( 149 mAggregatableFilteringIdMaxBytes, trigger.mAggregatableFilteringIdMaxBytes) 150 && Objects.equals( 151 mAggregateDebugReportingString, trigger.mAggregateDebugReportingString) 152 && Objects.equals(mNamedBudgetsString, trigger.mNamedBudgetsString); 153 } 154 155 @Override hashCode()156 public int hashCode() { 157 return Objects.hash( 158 mId, 159 mAttributionDestination, 160 mDestinationType, 161 mEnrollmentId, 162 mTriggerTime, 163 mEventTriggers, 164 mStatus, 165 mAggregateTriggerData, 166 mAggregateValuesString, 167 mAggregatableAttributionTrigger, 168 mFilters, 169 mNotFilters, 170 mDebugKey, 171 mAdIdPermission, 172 mArDebugPermission, 173 mAttributionConfig, 174 mAdtechKeyMapping, 175 mAggregateDeduplicationKeys, 176 mDebugJoinKey, 177 mPlatformAdId, 178 mDebugAdId, 179 mRegistrationOrigin, 180 mAggregatableSourceRegistrationTimeConfig, 181 mTriggerContextId, 182 mAttributionScopesString, 183 mAggregatableFilteringIdMaxBytes, 184 mAggregateDebugReportingString, 185 mNamedBudgetsString); 186 } 187 188 /** Unique identifier for the {@link Trigger}. */ getId()189 public String getId() { 190 return mId; 191 } 192 193 /** 194 * Destination where {@link Trigger} occurred. 195 */ getAttributionDestination()196 public Uri getAttributionDestination() { 197 return mAttributionDestination; 198 } 199 200 /** Destination type of the {@link Trigger}. */ 201 @EventSurfaceType getDestinationType()202 public int getDestinationType() { 203 return mDestinationType; 204 } 205 206 /** 207 * AdTech enrollment ID. 208 */ getEnrollmentId()209 public String getEnrollmentId() { 210 return mEnrollmentId; 211 } 212 213 /** 214 * Time when the event occurred. 215 */ getTriggerTime()216 public long getTriggerTime() { 217 return mTriggerTime; 218 } 219 220 /** 221 * Event triggers containing priority, de-dup key, trigger data and event-level filters info. 222 */ getEventTriggers()223 public String getEventTriggers() { 224 return mEventTriggers; 225 } 226 227 /** Current state of the {@link Trigger}. */ 228 @Status getStatus()229 public int getStatus() { 230 return mStatus; 231 } 232 233 /** 234 * Set the status. 235 */ setStatus(@tatus int status)236 public void setStatus(@Status int status) { 237 mStatus = status; 238 } 239 240 /** 241 * Registrant of this trigger, primarily an App. 242 */ getRegistrant()243 public Uri getRegistrant() { 244 return mRegistrant; 245 } 246 247 /** 248 * Returns aggregate trigger data string used for aggregation. aggregate trigger data json is a 249 * JSONArray. example: [ // Each dict independently adds pieces to multiple source keys. { // 250 * Conversion type purchase = 2 at a 9 bit key_offset, i.e. 2 << 9. // A 9 bit key_offset is 251 * needed because there are 511 possible campaigns, which // will take up 9 bits in the 252 * resulting key. "key_piece": "0x400", // Apply this key piece to: "source_keys": 253 * ["campaignCounts"] }, { // Purchase category shirts = 21 at a 7 bit key_offset, i.e. 21 << 7. 254 * // A 7 bit key_offset is needed because there are ~100 regions for the geo key, // which will 255 * take up 7 bits of space in the resulting key. "key_piece": "0xA80", // Apply this key piece 256 * to: "source_keys": ["geoValue", "nonMatchingKeyIdsAreIgnored"] } ] 257 */ getAggregateTriggerData()258 public String getAggregateTriggerData() { 259 return mAggregateTriggerData; 260 } 261 262 /** 263 * Returns aggregate value string used for aggregation. 264 * Aggregate value can be either JSONObject or JSONArray. 265 * example: {"campaignCounts": 32768, "geoValue": 1664} 266 * example: [{ 267 * "values": {"campaignCounts" :32768, "geoValue": 1664}, 268 * "filters": {"category": ["filter_1", "filter_2"]}, 269 * "not_filters": {"category": ["filter_3", "filter_4"]} 270 * }] 271 */ getAggregateValuesString()272 public String getAggregateValuesString() { 273 return mAggregateValuesString; 274 } 275 276 /** 277 * Returns a list of aggregate deduplication keys. aggregate deduplication key is a JSONObject. 278 * example: { "deduplication_key": "32768", "filters": [ {type: [filter_1, filter_2]} ], 279 * "not_filters": [ {type: [not_filter_1, not_filter_2]} ] } 280 */ getAggregateDeduplicationKeys()281 public String getAggregateDeduplicationKeys() { 282 return mAggregateDeduplicationKeys; 283 } 284 285 /** 286 * Returns the JSON representation of the list of named budgets. named budget is a JSONObject. 287 * example: {"name": "biddable", "filters": { "2": ["11102626635", "11081876753"]}, 288 * "not_filters": { "2": ["11102626635", "11081876753"]}} 289 */ 290 @Nullable getNamedBudgetsString()291 public String getNamedBudgetsString() { 292 return mNamedBudgetsString; 293 } 294 295 /** 296 * Returns the AggregatableAttributionTrigger object, which is constructed using the aggregate 297 * trigger data string and aggregate values string in Trigger. 298 */ getAggregatableAttributionTrigger(Flags flags)299 public Optional<AggregatableAttributionTrigger> getAggregatableAttributionTrigger(Flags flags) 300 throws JSONException { 301 if (mAggregatableAttributionTrigger != null) { 302 return mAggregatableAttributionTrigger; 303 } 304 305 mAggregatableAttributionTrigger = parseAggregateTrigger(flags); 306 return mAggregatableAttributionTrigger; 307 } 308 309 /** 310 * Returns top level filters. The value is in json format. 311 * 312 * <p>Will be used for deciding if the trigger can be attributed to the source. If the source 313 * fails the filtering against these filters then no reports(event/aggregate) are generated. 314 * example: { "key1" : ["value11", "value12"], "key2" : ["value21", "value22"] } 315 */ getFilters()316 public String getFilters() { 317 return mFilters; 318 } 319 320 /** Is Ad Tech Opt-in to Debug Reporting {@link Trigger}. */ isDebugReporting()321 public boolean isDebugReporting() { 322 return mIsDebugReporting; 323 } 324 325 /** Is Ad ID Permission Enabled. */ hasAdIdPermission()326 public boolean hasAdIdPermission() { 327 return mAdIdPermission; 328 } 329 330 /** Is Ar Debug Permission Enabled. */ hasArDebugPermission()331 public boolean hasArDebugPermission() { 332 return mArDebugPermission; 333 } 334 335 /** 336 * Returns top level not-filters. The value is in json format. 337 */ getNotFilters()338 public String getNotFilters() { 339 return mNotFilters; 340 } 341 342 /** Debug key of {@link Trigger}. */ 343 @Nullable getDebugKey()344 public UnsignedLong getDebugKey() { 345 return mDebugKey; 346 } 347 348 /** 349 * Returns field attribution config JSONArray as String. example: [{ "source_network": 350 * "AdTech1-Ads", "source_priority_range": { “start”: 100, “end”: 1000 }, "source_filters": { 351 * "campaign_type": ["install"], "source_type": ["navigation"], }, "priority": "99", "expiry": 352 * "604800", "filter_data":{ "campaign_type": ["install"], } }] 353 */ 354 @Nullable getAttributionConfig()355 public String getAttributionConfig() { 356 return mAttributionConfig; 357 } 358 359 /** 360 * Returns adtech bit mapping JSONObject as String. example: "x_network_key_mapping": { 361 * "AdTechA-enrollment_id": "0x1", "AdTechB-enrollment_id": "0x2", } 362 */ 363 @Nullable getAdtechKeyMapping()364 public String getAdtechKeyMapping() { 365 return mAdtechKeyMapping; 366 } 367 368 /** 369 * Returns join key that should be matched with source's join key at the time of generating 370 * reports. 371 */ 372 @Nullable getDebugJoinKey()373 public String getDebugJoinKey() { 374 return mDebugJoinKey; 375 } 376 377 /** 378 * Returns actual platform AdID from getAdId() on app trigger registration, to be matched with a 379 * web source's {@link Trigger#getDebugAdId()} value at the time of generating reports. 380 */ 381 @Nullable getPlatformAdId()382 public String getPlatformAdId() { 383 return mPlatformAdId; 384 } 385 386 /** 387 * Returns SHA256 hash of AdID from registration response on web registration concatenated with 388 * enrollment ID, to be matched with an app source's {@link Source#getPlatformAdId()} value at 389 * the time of generating reports. 390 */ 391 @Nullable getDebugAdId()392 public String getDebugAdId() { 393 return mDebugAdId; 394 } 395 396 /** Returns registration origin used to register the source */ getRegistrationOrigin()397 public Uri getRegistrationOrigin() { 398 return mRegistrationOrigin; 399 } 400 401 /** Returns coordinator origin for aggregatable reports */ 402 @Nullable getAggregationCoordinatorOrigin()403 public Uri getAggregationCoordinatorOrigin() { 404 return mAggregationCoordinatorOrigin; 405 } 406 407 /** 408 * Return {@link SourceRegistrationTimeConfig#EXCLUDE} if the {@link AggregateReport} should not 409 * include the attributed {@link Source} registration time during attribution reporting. Returns 410 * {@link SourceRegistrationTimeConfig#INCLUDE} otherwise. 411 */ getAggregatableSourceRegistrationTimeConfig()412 public SourceRegistrationTimeConfig getAggregatableSourceRegistrationTimeConfig() { 413 return mAggregatableSourceRegistrationTimeConfig; 414 } 415 416 /** Returns the context id */ 417 @Nullable getTriggerContextId()418 public String getTriggerContextId() { 419 return mTriggerContextId; 420 } 421 422 /** Returns the aggregatable filtering id max bytes. */ 423 @Nullable getAggregatableFilteringIdMaxBytes()424 public Integer getAggregatableFilteringIdMaxBytes() { 425 return mAggregatableFilteringIdMaxBytes; 426 } 427 428 /** Returns if the report for this Trigger should be sent immediately or with a delay. */ shouldAddDelay(Flags flags)429 public boolean shouldAddDelay(Flags flags) { 430 boolean isNonNullTriggerContextId = mTriggerContextId != null; 431 boolean isNonNullFilteringIdAndNonDefaultFilteringIdMaxBytes = 432 flags.getMeasurementEnableFlexibleContributionFiltering() 433 && mAggregatableFilteringIdMaxBytes != null 434 && mAggregatableFilteringIdMaxBytes 435 != Flags.MEASUREMENT_DEFAULT_FILTERING_ID_MAX_BYTES; 436 if (isNonNullTriggerContextId || isNonNullFilteringIdAndNonDefaultFilteringIdMaxBytes) { 437 return false; 438 } 439 return true; 440 } 441 442 /** 443 * Generates AggregatableAttributionTrigger from aggregate trigger data string and aggregate 444 * values string in Trigger. 445 */ parseAggregateTrigger(Flags flags)446 private Optional<AggregatableAttributionTrigger> parseAggregateTrigger(Flags flags) 447 throws JSONException, NumberFormatException { 448 if (mAggregateValuesString == null) { 449 return Optional.empty(); 450 } 451 JSONArray triggerDataArray = 452 mAggregateTriggerData == null 453 ? new JSONArray() 454 : new JSONArray(mAggregateTriggerData); 455 List<AggregateTriggerData> triggerDataList = new ArrayList<>(); 456 Filter filter = new Filter(flags); 457 for (int i = 0; i < triggerDataArray.length(); i++) { 458 JSONObject triggerDatum = triggerDataArray.getJSONObject(i); 459 // Remove "0x" prefix. 460 String hexString = triggerDatum.getString("key_piece").substring(2); 461 BigInteger bigInteger = new BigInteger(hexString, 16); 462 JSONArray sourceKeys = triggerDatum.getJSONArray("source_keys"); 463 Set<String> sourceKeySet = new HashSet<>(); 464 for (int j = 0; j < sourceKeys.length(); j++) { 465 sourceKeySet.add(sourceKeys.getString(j)); 466 } 467 AggregateTriggerData.Builder builder = 468 new AggregateTriggerData.Builder() 469 .setKey(bigInteger) 470 .setSourceKeys(sourceKeySet); 471 if (triggerDatum.has(FilterContract.FILTERS) 472 && !triggerDatum.isNull(FilterContract.FILTERS)) { 473 List<FilterMap> filterSet = 474 filter.deserializeFilterSet( 475 triggerDatum.getJSONArray(FilterContract.FILTERS)); 476 builder.setFilterSet(filterSet); 477 } 478 if (triggerDatum.has(FilterContract.NOT_FILTERS) 479 && !triggerDatum.isNull(FilterContract.NOT_FILTERS)) { 480 List<FilterMap> notFilterSet = 481 filter.deserializeFilterSet(triggerDatum.getJSONArray("not_filters")); 482 builder.setNotFilterSet(notFilterSet); 483 } 484 if (!triggerDatum.isNull("x_network_data")) { 485 JSONObject xNetworkDataJson = triggerDatum.getJSONObject("x_network_data"); 486 XNetworkData xNetworkData = new XNetworkData.Builder(xNetworkDataJson).build(); 487 builder.setXNetworkData(xNetworkData); 488 } 489 triggerDataList.add(builder.build()); 490 } 491 List<AggregateDeduplicationKey> dedupKeyList = new ArrayList<>(); 492 if (getAggregateDeduplicationKeys() != null) { 493 JSONArray dedupKeyObjects = new JSONArray(getAggregateDeduplicationKeys()); 494 for (int i = 0; i < dedupKeyObjects.length(); i++) { 495 JSONObject dedupKeyObject = dedupKeyObjects.getJSONObject(i); 496 AggregateDeduplicationKey.Builder builder = new AggregateDeduplicationKey.Builder(); 497 if (dedupKeyObject.has("deduplication_key") 498 && !dedupKeyObject.isNull("deduplication_key")) { 499 builder.setDeduplicationKey( 500 new UnsignedLong(dedupKeyObject.getString("deduplication_key"))); 501 } 502 if (dedupKeyObject.has(FilterContract.FILTERS) 503 && !dedupKeyObject.isNull(FilterContract.FILTERS)) { 504 List<FilterMap> filterSet = 505 filter.deserializeFilterSet( 506 dedupKeyObject.getJSONArray(FilterContract.FILTERS)); 507 builder.setFilterSet(filterSet); 508 } 509 if (dedupKeyObject.has(FilterContract.NOT_FILTERS) 510 && !dedupKeyObject.isNull(FilterContract.NOT_FILTERS)) { 511 List<FilterMap> notFilterSet = 512 filter.deserializeFilterSet( 513 dedupKeyObject.getJSONArray(FilterContract.NOT_FILTERS)); 514 builder.setNotFilterSet(notFilterSet); 515 } 516 dedupKeyList.add(builder.build()); 517 } 518 } 519 AggregatableAttributionTrigger.Builder aggregatableAttributionTriggerBuilder = 520 new AggregatableAttributionTrigger.Builder() 521 .setTriggerData(triggerDataList) 522 .setAggregateDeduplicationKeys(dedupKeyList); 523 Optional<JSONArray> maybeAggregateValuesArr = 524 JsonUtil.maybeGetJsonArray(mAggregateValuesString); 525 if (maybeAggregateValuesArr.isPresent()) { 526 if (!flags.getMeasurementEnableAggregateValueFilters()) { 527 return Optional.empty(); 528 } 529 List<AggregatableValuesConfig> aggregatableValuesConfigList = new ArrayList<>(); 530 for (int i = 0; i < maybeAggregateValuesArr.get().length(); i++) { 531 JSONObject valuesObj = maybeAggregateValuesArr.get().getJSONObject(i); 532 AggregatableValuesConfig aggregatableValuesConfig = 533 new AggregatableValuesConfig.Builder(valuesObj, flags).build(); 534 aggregatableValuesConfigList.add(aggregatableValuesConfig); 535 } 536 aggregatableAttributionTriggerBuilder.setValueConfigs(aggregatableValuesConfigList); 537 } else { 538 // Default case: Convert value from integer to AggregatableKeyValue. 539 AggregatableValuesConfig aggregatableValuesConfig = 540 new AggregatableValuesConfig.Builder( 541 new JSONObject(mAggregateValuesString), flags) 542 .build(); 543 aggregatableAttributionTriggerBuilder.setValueConfigs( 544 List.of(aggregatableValuesConfig)); 545 } 546 547 if (flags.getMeasurementEnableAggregatableNamedBudgets() 548 && getNamedBudgetsString() != null) { 549 JSONArray budgetObjects = new JSONArray(getNamedBudgetsString()); 550 List<AggregatableNamedBudget> aggregatableNamedBudgets = new ArrayList<>(); 551 for (int i = 0; i < budgetObjects.length(); i++) { 552 aggregatableNamedBudgets.add( 553 new AggregatableNamedBudget(budgetObjects.getJSONObject(i), flags)); 554 } 555 aggregatableAttributionTriggerBuilder.setNamedBudgets(aggregatableNamedBudgets); 556 } 557 558 return Optional.of(aggregatableAttributionTriggerBuilder.build()); 559 } 560 561 /** 562 * Parses the json array under {@link #mEventTriggers} to form a list of {@link EventTrigger}s. 563 * 564 * @return list of {@link EventTrigger}s 565 * @throws JSONException if JSON parsing fails 566 */ parseEventTriggers(Flags flags)567 public List<EventTrigger> parseEventTriggers(Flags flags) throws JSONException { 568 JSONArray jsonArray = new JSONArray(mEventTriggers); 569 List<EventTrigger> eventTriggers = new ArrayList<>(); 570 boolean readValue = flags.getMeasurementFlexibleEventReportingApiEnabled(); 571 for (int i = 0; i < jsonArray.length(); i++) { 572 JSONObject eventTrigger = jsonArray.getJSONObject(i); 573 574 EventTrigger.Builder eventTriggerBuilder = 575 new EventTrigger.Builder( 576 new UnsignedLong( 577 eventTrigger.getString( 578 EventTriggerContract.TRIGGER_DATA))); 579 580 if (!eventTrigger.isNull(EventTriggerContract.PRIORITY)) { 581 eventTriggerBuilder.setTriggerPriority( 582 eventTrigger.getLong(EventTriggerContract.PRIORITY)); 583 } 584 585 if (readValue && !eventTrigger.isNull(EventTriggerContract.VALUE)) { 586 eventTriggerBuilder.setTriggerValue( 587 eventTrigger.getLong(EventTriggerContract.VALUE)); 588 } else { 589 eventTriggerBuilder.setTriggerValue(1L); 590 } 591 592 if (!eventTrigger.isNull(EventTriggerContract.DEDUPLICATION_KEY)) { 593 eventTriggerBuilder.setDedupKey(new UnsignedLong( 594 eventTrigger.getString(EventTriggerContract.DEDUPLICATION_KEY))); 595 } 596 597 if (!eventTrigger.isNull(FilterContract.FILTERS)) { 598 List<FilterMap> filterSet = 599 new Filter(flags) 600 .deserializeFilterSet( 601 eventTrigger.getJSONArray(FilterContract.FILTERS)); 602 eventTriggerBuilder.setFilterSet(filterSet); 603 } 604 605 if (!eventTrigger.isNull(FilterContract.NOT_FILTERS)) { 606 List<FilterMap> notFilterSet = 607 new Filter(flags) 608 .deserializeFilterSet( 609 eventTrigger.getJSONArray(FilterContract.NOT_FILTERS)); 610 eventTriggerBuilder.setNotFilterSet(notFilterSet); 611 } 612 eventTriggers.add(eventTriggerBuilder.build()); 613 } 614 615 return eventTriggers; 616 } 617 618 /** 619 * Parses the json object under {@link #mAdtechKeyMapping} to create a mapping of adtechs to 620 * their bits. 621 * 622 * @return mapping of String to BigInteger 623 * @throws JSONException if JSON parsing fails 624 * @throws NumberFormatException if BigInteger parsing fails 625 */ 626 @Nullable parseAdtechKeyMapping()627 public Map<String, BigInteger> parseAdtechKeyMapping() 628 throws JSONException, NumberFormatException { 629 if (mAdtechKeyMapping == null) { 630 return null; 631 } 632 Map<String, BigInteger> adtechBitMapping = new HashMap<>(); 633 JSONObject jsonObject = new JSONObject(mAdtechKeyMapping); 634 Iterator<String> keys = jsonObject.keys(); 635 while (keys.hasNext()) { 636 String key = keys.next(); 637 // Remove "0x" prefix. 638 String hexString = jsonObject.getString(key).substring(2); 639 BigInteger bigInteger = new BigInteger(hexString, 16); 640 adtechBitMapping.put(key, bigInteger); 641 } 642 return adtechBitMapping; 643 } 644 645 /** 646 * Returns a {@code Uri} with scheme and (1) public suffix + 1 in case of a web destination, or 647 * (2) the Android package name in case of an app destination. Returns null if extracting the 648 * public suffix + 1 fails. 649 */ 650 @Nullable getAttributionDestinationBaseUri()651 public Uri getAttributionDestinationBaseUri() { 652 if (mDestinationType == EventSurfaceType.APP) { 653 return mAttributionDestination; 654 } else { 655 Optional<Uri> uri = WebAddresses.topPrivateDomainAndScheme(mAttributionDestination); 656 return uri.orElse(null); 657 } 658 } 659 660 /** Returns attribution scope string for the trigger. */ 661 @Nullable getAttributionScopesString()662 public String getAttributionScopesString() { 663 return mAttributionScopesString; 664 } 665 666 /** Returns attribution scopes for the trigger. */ 667 @Nullable getAttributionScopes()668 public List<String> getAttributionScopes() throws JSONException { 669 if (mAttributionScopesString == null) { 670 return null; 671 } 672 JSONArray jsonArray = new JSONArray(mAttributionScopesString); 673 List<String> attributionScopes = new ArrayList<>(); 674 for (int i = 0; i < jsonArray.length(); ++i) { 675 attributionScopes.add(jsonArray.getString(i)); 676 } 677 return attributionScopes; 678 } 679 680 /** Returns the aggregate debug reporting object as a string */ 681 @Nullable getAggregateDebugReportingString()682 public String getAggregateDebugReportingString() { 683 return mAggregateDebugReportingString; 684 } 685 686 /** Returns the aggregate debug reporting object as a string */ 687 @Nullable getAggregateDebugReportingObject()688 public AggregateDebugReporting getAggregateDebugReportingObject() throws JSONException { 689 if (mAggregateDebugReportingString == null) { 690 return null; 691 } 692 if (mAggregateDebugReporting == null) { 693 mAggregateDebugReporting = 694 new AggregateDebugReporting.Builder( 695 new JSONObject(mAggregateDebugReportingString)) 696 .build(); 697 } 698 return mAggregateDebugReporting; 699 } 700 701 /** Returns if the trigger has non-empty aggregatable values or non-empty aggregatable data */ hasAggregatableData(Flags flags)702 public boolean hasAggregatableData(Flags flags) throws JSONException { 703 Optional<AggregatableAttributionTrigger> aggregatableAttributionTriggerOpt = 704 getAggregatableAttributionTrigger(flags); 705 if (aggregatableAttributionTriggerOpt.isEmpty()) { 706 return false; 707 } 708 709 AggregatableAttributionTrigger aggregatableAttributionTrigger = 710 aggregatableAttributionTriggerOpt.get(); 711 for (AggregatableValuesConfig aggValuesConfig : 712 aggregatableAttributionTrigger.getValueConfigs()) { 713 if (!aggValuesConfig.getValues().isEmpty()) { 714 return true; 715 } 716 } 717 return !aggregatableAttributionTrigger.getTriggerData().isEmpty(); 718 } 719 720 /** Builder for {@link Trigger}. */ 721 public static final class Builder { 722 723 private final Trigger mBuilding; 724 Builder()725 public Builder() { 726 mBuilding = new Trigger(); 727 } 728 729 /** See {@link Trigger#getId()}. */ 730 @NonNull setId(String id)731 public Builder setId(String id) { 732 mBuilding.mId = id; 733 return this; 734 } 735 736 /** See {@link Trigger#getAttributionDestination()}. */ 737 @NonNull setAttributionDestination(Uri attributionDestination)738 public Builder setAttributionDestination(Uri attributionDestination) { 739 Validation.validateUri(attributionDestination); 740 mBuilding.mAttributionDestination = attributionDestination; 741 return this; 742 } 743 744 /** See {@link Trigger#getDestinationType()}. */ 745 @NonNull setDestinationType(@ventSurfaceType int destinationType)746 public Builder setDestinationType(@EventSurfaceType int destinationType) { 747 mBuilding.mDestinationType = destinationType; 748 return this; 749 } 750 751 /** See {@link Trigger#getEnrollmentId()}. */ 752 @NonNull setEnrollmentId(String enrollmentId)753 public Builder setEnrollmentId(String enrollmentId) { 754 mBuilding.mEnrollmentId = enrollmentId; 755 return this; 756 } 757 758 /** See {@link Trigger#getStatus()}. */ 759 @NonNull setStatus(@tatus int status)760 public Builder setStatus(@Status int status) { 761 mBuilding.mStatus = status; 762 return this; 763 } 764 765 /** See {@link Trigger#getTriggerTime()}. */ 766 @NonNull setTriggerTime(long triggerTime)767 public Builder setTriggerTime(long triggerTime) { 768 mBuilding.mTriggerTime = triggerTime; 769 return this; 770 } 771 772 /** See {@link Trigger#getEventTriggers()}. */ 773 @NonNull setEventTriggers(@onNull String eventTriggers)774 public Builder setEventTriggers(@NonNull String eventTriggers) { 775 Validation.validateNonNull(eventTriggers); 776 mBuilding.mEventTriggers = eventTriggers; 777 return this; 778 } 779 780 /** See {@link Trigger#getRegistrant()} */ 781 @NonNull setRegistrant(@onNull Uri registrant)782 public Builder setRegistrant(@NonNull Uri registrant) { 783 Validation.validateUri(registrant); 784 mBuilding.mRegistrant = registrant; 785 return this; 786 } 787 788 /** See {@link Trigger#getAggregateTriggerData()}. */ 789 @NonNull setAggregateTriggerData(@ullable String aggregateTriggerData)790 public Builder setAggregateTriggerData(@Nullable String aggregateTriggerData) { 791 mBuilding.mAggregateTriggerData = aggregateTriggerData; 792 return this; 793 } 794 795 /** See {@link Trigger#getAggregateValuesString()} */ 796 @NonNull setAggregateValuesString(@ullable String aggregateValuesString)797 public Builder setAggregateValuesString(@Nullable String aggregateValuesString) { 798 mBuilding.mAggregateValuesString = aggregateValuesString; 799 return this; 800 } 801 802 /** See {@link Trigger#getAggregateDeduplicationKeys()} */ 803 @NonNull setAggregateDeduplicationKeys(@onNull String aggregateDeduplicationKeys)804 public Builder setAggregateDeduplicationKeys(@NonNull String aggregateDeduplicationKeys) { 805 mBuilding.mAggregateDeduplicationKeys = aggregateDeduplicationKeys; 806 return this; 807 } 808 809 /** See {@link Trigger#getNamedBudgetsString()} ()} */ setNamedBudgetsString(String namedBudgetsString)810 public Builder setNamedBudgetsString(String namedBudgetsString) { 811 mBuilding.mNamedBudgetsString = namedBudgetsString; 812 return this; 813 } 814 815 /** See {@link Trigger#getFilters()} */ 816 @NonNull setFilters(@ullable String filters)817 public Builder setFilters(@Nullable String filters) { 818 mBuilding.mFilters = filters; 819 return this; 820 } 821 822 /** See {@link Trigger#isDebugReporting()} */ setIsDebugReporting(boolean isDebugReporting)823 public Builder setIsDebugReporting(boolean isDebugReporting) { 824 mBuilding.mIsDebugReporting = isDebugReporting; 825 return this; 826 } 827 828 /** See {@link Trigger#hasAdIdPermission()} */ setAdIdPermission(boolean adIdPermission)829 public Builder setAdIdPermission(boolean adIdPermission) { 830 mBuilding.mAdIdPermission = adIdPermission; 831 return this; 832 } 833 834 /** See {@link Trigger#hasArDebugPermission()} */ setArDebugPermission(boolean arDebugPermission)835 public Builder setArDebugPermission(boolean arDebugPermission) { 836 mBuilding.mArDebugPermission = arDebugPermission; 837 return this; 838 } 839 840 /** See {@link Trigger#getNotFilters()} */ 841 @NonNull setNotFilters(@ullable String notFilters)842 public Builder setNotFilters(@Nullable String notFilters) { 843 mBuilding.mNotFilters = notFilters; 844 return this; 845 } 846 847 /** See {@link Trigger#getDebugKey()} */ setDebugKey(@ullable UnsignedLong debugKey)848 public Builder setDebugKey(@Nullable UnsignedLong debugKey) { 849 mBuilding.mDebugKey = debugKey; 850 return this; 851 } 852 853 /** See {@link Trigger#getAttributionConfig()} */ setAttributionConfig(@ullable String attributionConfig)854 public Builder setAttributionConfig(@Nullable String attributionConfig) { 855 mBuilding.mAttributionConfig = attributionConfig; 856 return this; 857 } 858 859 /** See {@link Trigger#getAdtechKeyMapping()} */ setAdtechBitMapping(@ullable String adtechBitMapping)860 public Builder setAdtechBitMapping(@Nullable String adtechBitMapping) { 861 mBuilding.mAdtechKeyMapping = adtechBitMapping; 862 return this; 863 } 864 865 /** See {@link Trigger#getAggregatableAttributionTrigger(Flags)} */ 866 @NonNull setAggregatableAttributionTrigger( @ullable AggregatableAttributionTrigger aggregatableAttributionTrigger)867 public Builder setAggregatableAttributionTrigger( 868 @Nullable AggregatableAttributionTrigger aggregatableAttributionTrigger) { 869 mBuilding.mAggregatableAttributionTrigger = 870 Optional.ofNullable(aggregatableAttributionTrigger); 871 return this; 872 } 873 874 /** See {@link Trigger#getDebugJoinKey()} */ 875 @NonNull setDebugJoinKey(@ullable String debugJoinKey)876 public Builder setDebugJoinKey(@Nullable String debugJoinKey) { 877 mBuilding.mDebugJoinKey = debugJoinKey; 878 return this; 879 } 880 881 /** See {@link Trigger#getPlatformAdId()} */ 882 @NonNull setPlatformAdId(@ullable String platformAdId)883 public Builder setPlatformAdId(@Nullable String platformAdId) { 884 mBuilding.mPlatformAdId = platformAdId; 885 return this; 886 } 887 888 /** See {@link Trigger#getDebugAdId()} */ 889 @NonNull setDebugAdId(@ullable String debugAdId)890 public Builder setDebugAdId(@Nullable String debugAdId) { 891 mBuilding.mDebugAdId = debugAdId; 892 return this; 893 } 894 895 /** See {@link Trigger#getRegistrationOrigin()} */ 896 @NonNull setRegistrationOrigin(Uri registrationOrigin)897 public Builder setRegistrationOrigin(Uri registrationOrigin) { 898 mBuilding.mRegistrationOrigin = registrationOrigin; 899 return this; 900 } 901 902 /** See {@link Trigger#getAggregationCoordinatorOrigin()} */ setAggregationCoordinatorOrigin(Uri aggregationCoordinatorOrigin)903 public Builder setAggregationCoordinatorOrigin(Uri aggregationCoordinatorOrigin) { 904 mBuilding.mAggregationCoordinatorOrigin = aggregationCoordinatorOrigin; 905 return this; 906 } 907 908 /** See {@link Trigger#getAggregatableSourceRegistrationTimeConfig()}. */ 909 @NonNull setAggregatableSourceRegistrationTimeConfig( SourceRegistrationTimeConfig config)910 public Builder setAggregatableSourceRegistrationTimeConfig( 911 SourceRegistrationTimeConfig config) { 912 mBuilding.mAggregatableSourceRegistrationTimeConfig = config; 913 return this; 914 } 915 /** See {@link Trigger#getTriggerContextId()}. */ setTriggerContextId(@ullable String triggerContextId)916 public Builder setTriggerContextId(@Nullable String triggerContextId) { 917 mBuilding.mTriggerContextId = triggerContextId; 918 return this; 919 } 920 921 /** See {@link Trigger#getAttributionScopesString()}. */ 922 @NonNull setAttributionScopesString(@ullable String attributionScopesString)923 public Builder setAttributionScopesString(@Nullable String attributionScopesString) { 924 mBuilding.mAttributionScopesString = attributionScopesString; 925 return this; 926 } 927 928 /** See {@link Trigger#getAggregatableFilteringIdMaxBytes()} */ setAggregatableFilteringIdMaxBytes( @ullable Integer aggregatableFilteringIdMaxBytes)929 public Builder setAggregatableFilteringIdMaxBytes( 930 @Nullable Integer aggregatableFilteringIdMaxBytes) { 931 mBuilding.mAggregatableFilteringIdMaxBytes = aggregatableFilteringIdMaxBytes; 932 return this; 933 } 934 935 /** See {@link Trigger#getAggregateDebugReportingString()}. */ 936 @NonNull setAggregateDebugReportingString( @ullable String aggregateDebugReportingString)937 public Builder setAggregateDebugReportingString( 938 @Nullable String aggregateDebugReportingString) { 939 mBuilding.mAggregateDebugReportingString = aggregateDebugReportingString; 940 return this; 941 } 942 943 /** Build the {@link Trigger}. */ 944 @NonNull build()945 public Trigger build() { 946 Validation.validateNonNull( 947 mBuilding.mAttributionDestination, 948 mBuilding.mEnrollmentId, 949 mBuilding.mRegistrant, 950 mBuilding.mRegistrationOrigin, 951 mBuilding.mAggregatableSourceRegistrationTimeConfig); 952 953 return mBuilding; 954 } 955 } 956 957 /** Event trigger field keys. */ 958 public interface EventTriggerContract { 959 String TRIGGER_DATA = "trigger_data"; 960 String PRIORITY = "priority"; 961 String VALUE = "value"; 962 String DEDUPLICATION_KEY = "deduplication_key"; 963 } 964 } 965