• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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