• 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.net.Uri;
20 
21 import com.android.adservices.LogUtil;
22 import com.android.adservices.LoggerFactory;
23 import com.android.adservices.common.WebUtil;
24 import com.android.adservices.service.FakeFlagsFactory;
25 import com.android.adservices.service.measurement.aggregation.AggregatableAttributionSource;
26 import com.android.adservices.service.measurement.noising.Combinatorics;
27 import com.android.adservices.service.measurement.util.UnsignedLong;
28 
29 import org.json.JSONArray;
30 import org.json.JSONException;
31 import org.json.JSONObject;
32 
33 import java.math.BigInteger;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.Collections;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.TreeMap;
40 import java.util.UUID;
41 import java.util.concurrent.TimeUnit;
42 
43 public final class SourceFixture {
SourceFixture()44     private SourceFixture() { }
45 
46     // Assume the field values in this Source.Builder have no relation to the field values in
47     // {@link ValidSourceParams}
getMinimalValidSourceBuilder()48     public static Source.Builder getMinimalValidSourceBuilder() {
49         return new Source.Builder()
50                 .setPublisher(ValidSourceParams.PUBLISHER)
51                 .setAppDestinations(ValidSourceParams.ATTRIBUTION_DESTINATIONS)
52                 .setEnrollmentId(ValidSourceParams.ENROLLMENT_ID)
53                 .setRegistrant(ValidSourceParams.REGISTRANT)
54                 .setRegistrationOrigin(ValidSourceParams.REGISTRATION_ORIGIN);
55     }
56 
57     /**
58      * @return The minimum valid source with attribiton scopes.
59      */
getMinimalValidSourceWithAttributionScope()60     public static Source.Builder getMinimalValidSourceWithAttributionScope() {
61         return getMinimalValidSourceBuilder()
62                 .setAttributionScopes(ValidSourceParams.ATTRIBUTION_SCOPES)
63                 .setAttributionScopeLimit(ValidSourceParams.ATTRIBUTION_SCOPE_LIMIT)
64                 .setMaxEventStates(ValidSourceParams.MAX_NUM_VIEW_STATES);
65     }
66 
67     // Assume the field values in this Source have no relation to the field values in
68     // {@link ValidSourceParams}
getValidSource()69     public static Source getValidSource() {
70         return getValidSourceBuilder().build();
71     }
72 
getValidSourceBuilder()73     public static Source.Builder getValidSourceBuilder() {
74         return new Source.Builder()
75                 .setId(UUID.randomUUID().toString())
76                 .setEventId(ValidSourceParams.SOURCE_EVENT_ID)
77                 .setPublisher(ValidSourceParams.PUBLISHER)
78                 .setAppDestinations(ValidSourceParams.ATTRIBUTION_DESTINATIONS)
79                 .setWebDestinations(ValidSourceParams.WEB_DESTINATIONS)
80                 .setEnrollmentId(ValidSourceParams.ENROLLMENT_ID)
81                 .setRegistrant(ValidSourceParams.REGISTRANT)
82                 .setEventTime(ValidSourceParams.SOURCE_EVENT_TIME)
83                 .setExpiryTime(ValidSourceParams.EXPIRY_TIME)
84                 .setEventReportWindow(ValidSourceParams.EXPIRY_TIME)
85                 .setAggregatableReportWindow(ValidSourceParams.EXPIRY_TIME)
86                 .setPriority(ValidSourceParams.PRIORITY)
87                 .setSourceType(ValidSourceParams.SOURCE_TYPE)
88                 .setInstallAttributionWindow(ValidSourceParams.INSTALL_ATTRIBUTION_WINDOW)
89                 .setInstallCooldownWindow(ValidSourceParams.INSTALL_COOLDOWN_WINDOW)
90                 .setReinstallReattributionWindow(ValidSourceParams.REINSTALL_REATTRIBUTION_WINDOW)
91                 .setAttributionMode(ValidSourceParams.ATTRIBUTION_MODE)
92                 .setAggregateSource(ValidSourceParams.buildAggregateSource())
93                 .setFilterDataString(ValidSourceParams.buildFilterDataString())
94                 .setSharedFilterDataKeys(ValidSourceParams.SHARED_FILTER_DATA_KEYS)
95                 .setIsDebugReporting(true)
96                 .setRegistrationId(ValidSourceParams.REGISTRATION_ID)
97                 .setSharedAggregationKeys(ValidSourceParams.SHARED_AGGREGATE_KEYS)
98                 .setInstallTime(ValidSourceParams.INSTALL_TIME)
99                 .setPlatformAdId(ValidSourceParams.PLATFORM_AD_ID)
100                 .setDebugAdId(ValidSourceParams.DEBUG_AD_ID)
101                 .setRegistrationOrigin(ValidSourceParams.REGISTRATION_ORIGIN)
102                 .setCoarseEventReportDestinations(true)
103                 .setSharedDebugKey(ValidSourceParams.SHARED_DEBUG_KEY)
104                 .setAttributionScopes(ValidSourceParams.ATTRIBUTION_SCOPES)
105                 .setAttributionScopeLimit(ValidSourceParams.ATTRIBUTION_SCOPE_LIMIT)
106                 .setMaxEventStates(ValidSourceParams.MAX_NUM_VIEW_STATES)
107                 .setDestinationLimitPriority(ValidSourceParams.DESTINATION_LIMIT_PRIORITY)
108                 .setDestinationLimitAlgorithm(ValidSourceParams.DESTINATION_LIMIT_ALGORITHM)
109                 .setAttributedTriggers(new ArrayList<>())
110                 .setEventLevelEpsilon(ValidSourceParams.EVENT_LEVEL_EPSILON)
111                 .setAggregateDebugReportingString(ValidSourceParams.AGGREGATE_DEBUG_REPORT)
112                 .setAggregateDebugReportContributions(
113                         ValidSourceParams.AGGREGATE_DEBUG_REPORT_CONTRIBUTIONS);
114     }
115 
116     public static class ValidSourceParams {
117 
118         public static final Long EXPIRY_TIME = 8640000010L;
119         public static final Long PRIORITY = 100L;
120         public static final UnsignedLong SOURCE_EVENT_ID = new UnsignedLong(1L);
121         public static final Long SOURCE_EVENT_TIME = 8640000000L;
122         public static final List<Uri> ATTRIBUTION_DESTINATIONS =
123                 List.of(Uri.parse("android-app://com.destination"));
124         public static List<Uri> WEB_DESTINATIONS = List.of(Uri.parse("https://destination.com"));
125         public static final Uri PUBLISHER = Uri.parse("android-app://com.publisher");
126         public static final Uri WEB_PUBLISHER = Uri.parse("https://publisher.com");
127         public static final Uri REGISTRANT = Uri.parse("android-app://com.registrant");
128         public static final String ENROLLMENT_ID = "enrollment-id";
129         public static final Source.SourceType SOURCE_TYPE = Source.SourceType.EVENT;
130         public static final Long INSTALL_ATTRIBUTION_WINDOW = 841839879274L;
131         public static final Long INSTALL_COOLDOWN_WINDOW = 8418398274L;
132         public static final UnsignedLong DEBUG_KEY = new UnsignedLong(7834690L);
133 
134         @Source.AttributionMode
135         public static final int ATTRIBUTION_MODE = Source.AttributionMode.TRUTHFULLY;
136 
137         public static final int AGGREGATE_CONTRIBUTIONS = 0;
138         public static final String REGISTRATION_ID = "R1";
139         public static final String SHARED_AGGREGATE_KEYS = "[\"key1\"]";
140         public static final String SHARED_FILTER_DATA_KEYS =
141                 "[\"conversion_subdomain\", \"product\"]";
142         public static final Long INSTALL_TIME = 100L;
143         public static final String PLATFORM_AD_ID = "test-platform-ad-id";
144         public static final String DEBUG_AD_ID = "test-debug-ad-id";
145         public static final Uri REGISTRATION_ORIGIN =
146                 WebUtil.validUri("https://subdomain.example.test");
147         public static final UnsignedLong SHARED_DEBUG_KEY = new UnsignedLong(834690L);
148         public static final List<String> ATTRIBUTION_SCOPES = List.of("1", "2", "3");
149         public static final Long ATTRIBUTION_SCOPE_LIMIT = 5L;
150         public static final Long MAX_NUM_VIEW_STATES = 10L;
151         public static final Long REINSTALL_REATTRIBUTION_WINDOW = 841839879274L;
152         public static final long DESTINATION_LIMIT_PRIORITY = 841849879274L;
153         public static final Source.DestinationLimitAlgorithm DESTINATION_LIMIT_ALGORITHM =
154                 Source.DestinationLimitAlgorithm.FIFO;
155         public static final Double EVENT_LEVEL_EPSILON = 12D;
156         public static final int STATUS = Source.Status.ACTIVE;
157         public static final String AGGREGATE_DEBUG_REPORT =
158                 "{\"budget\":1024,"
159                         + "\"key_piece\":\"0x100\","
160                         + "\"debug_data\":["
161                         + "{"
162                         + "\"types\": [\"source-destination-limit\"],"
163                         + "\"key_piece\": \"0x111\","
164                         + "\"value\": 111"
165                         + "},"
166                         + "{"
167                         + "\"types\": [\"unspecified\"],"
168                         + "\"key_piece\": \"0x222\","
169                         + "\"value\": 222"
170                         + "}"
171                         + "]}";
172         public static final int AGGREGATE_DEBUG_REPORT_CONTRIBUTIONS = 100;
173         public static final int MAX_EVENT_LEVEL_REPORTS = 5;
174         public static final Source.TriggerDataMatching TRIGGER_DATA_MATCHING =
175                 Source.TriggerDataMatching.MODULUS;
176 
buildAggregateSource()177         public static final String buildAggregateSource() {
178             try {
179                 JSONObject jsonObject = new JSONObject();
180                 jsonObject.put("campaignCounts", "0x456");
181                 jsonObject.put("geoValue", "0x159");
182                 return jsonObject.toString();
183             } catch (JSONException e) {
184                 LogUtil.e("JSONException when building aggregate source.");
185             }
186             return null;
187         }
188 
189         /** Creates a filter data string */
buildFilterDataString()190         public static final String buildFilterDataString() {
191             try {
192                 JSONObject filterMap = new JSONObject();
193                 filterMap.put(
194                         "conversion_subdomain",
195                         new JSONArray(Collections.singletonList("electronics.megastore")));
196                 filterMap.put("product", new JSONArray(Arrays.asList("1234", "2345")));
197                 return filterMap.toString();
198             } catch (JSONException e) {
199                 LogUtil.e("JSONException when building aggregate filter data.");
200             }
201             return null;
202         }
203 
buildAggregatableAttributionSource()204         public static final AggregatableAttributionSource buildAggregatableAttributionSource() {
205             TreeMap<String, BigInteger> aggregateSourceMap = new TreeMap<>();
206             aggregateSourceMap.put("5", new BigInteger("345"));
207             return new AggregatableAttributionSource.Builder()
208                     .setAggregatableSource(aggregateSourceMap)
209                     .setFilterMap(
210                             new FilterMap.Builder()
211                                     .setAttributionFilterMap(
212                                             Map.of(
213                                                     "product",
214                                                     List.of("1234", "4321"),
215                                                     "conversion_subdomain",
216                                                     List.of("electronics.megastore")))
217                                     .build())
218                     .build();
219         }
220     }
221 
222     /** Provides a count-based valid TriggerSpecs. */
getValidTriggerSpecsCountBased()223     public static TriggerSpecs getValidTriggerSpecsCountBased() throws JSONException {
224         String triggerSpecsString =
225                 "[{\"trigger_data\": [1, 2],"
226                         + "\"event_report_windows\": { "
227                         + "\"start_time\": 0, "
228                         + String.format(
229                                 "\"end_times\": [%s, %s]}, ",
230                                 TimeUnit.DAYS.toMillis(2), TimeUnit.DAYS.toMillis(7))
231                         + "\"summary_operator\": \"count\", "
232                         + "\"summary_buckets\": [1, 2]}]";
233         Source source =
234                 getMinimalValidSourceBuilder()
235                         .setAttributedTriggers(new ArrayList<>())
236                         .build();
237         TriggerSpecs triggerSpecs = new TriggerSpecs(
238                 triggerSpecArrayFrom(triggerSpecsString), 3, source);
239         // Oblige building privacy parameters for the trigger specs
240         triggerSpecs.getInformationGain(source, FakeFlagsFactory.getFlagsForTest());
241         return triggerSpecs;
242     }
243 
244     /** Provides a count-based valid TriggerSpecs with smaller state space. */
getValidTriggerSpecsCountBasedWithFewerState()245     public static TriggerSpecs getValidTriggerSpecsCountBasedWithFewerState() throws JSONException {
246         String triggerSpecsString =
247                 "[{\"trigger_data\": [1],"
248                         + "\"event_report_windows\": { "
249                         + "\"start_time\": 0, "
250                         + String.format("\"end_times\": [%s]}, ", TimeUnit.DAYS.toMillis(2))
251                         + "\"summary_operator\": \"count\", "
252                         + "\"summary_buckets\": [1]}]";
253         Source source = getMinimalValidSourceBuilder().build();
254         TriggerSpecs triggerSpecs = new TriggerSpecs(
255                 triggerSpecArrayFrom(triggerSpecsString), 1, source);
256         // Oblige building privacy parameters for the trigger specs
257         triggerSpecs.getInformationGain(source, FakeFlagsFactory.getFlagsForTest());
258         return triggerSpecs;
259     }
260 
261     /** Provides a valid TriggerSpecs with hardcoded epsilon. */
getValidTriggerSpecsWithNonDefaultEpsilon()262     public static TriggerSpecs getValidTriggerSpecsWithNonDefaultEpsilon() throws JSONException {
263         String triggerSpecsString =
264                 "[{\"trigger_data\": [1],"
265                         + "\"event_report_windows\": { "
266                         + "\"start_time\": 0, "
267                         + String.format("\"end_times\": [%s]}, ", TimeUnit.DAYS.toMillis(2))
268                         + "\"summary_operator\": \"count\", "
269                         + "\"summary_buckets\": [1]}]";
270         Source source = getMinimalValidSourceBuilder().build();
271         double mockFlipProbability = Combinatorics.getFlipProbability(5, 3);
272         String privacyParametersString = "{\"flip_probability\": " + mockFlipProbability + "}";
273         TriggerSpecs triggerSpecs =
274                 new TriggerSpecs(triggerSpecsString, "1", source, privacyParametersString);
275         return triggerSpecs;
276     }
277 
278     /** Provides a value-sum-based valid TriggerSpecs. */
getValidTriggerSpecsValueSum()279     public static TriggerSpecs getValidTriggerSpecsValueSum() throws JSONException {
280         return getValidTriggerSpecsValueSum(3);
281     }
282 
283     /** Provides a value-sum-based valid TriggerSpecs. */
getValidTriggerSpecsValueSum(int maxReports)284     public static TriggerSpecs getValidTriggerSpecsValueSum(int maxReports) throws JSONException {
285         Source source =
286                 getMinimalValidSourceBuilder()
287                         .setAttributedTriggers(new ArrayList<>())
288                         .build();
289         TriggerSpecs triggerSpecs = new TriggerSpecs(
290                 getTriggerSpecValueSumArrayValidBaseline(),
291                 maxReports,
292                 source);
293         // Oblige building privacy parameters for the trigger specs
294         triggerSpecs.getInformationGain(source, FakeFlagsFactory.getFlagsForTest());
295         return triggerSpecs;
296     }
297 
298     /** Provides a value-sum-based valid TriggerSpecs. */
getValidTriggerSpecsValueSumWithStartTime(long startTime)299     public static TriggerSpecs getValidTriggerSpecsValueSumWithStartTime(long startTime)
300             throws JSONException {
301         Source source =
302                 getMinimalValidSourceBuilder()
303                         .setAttributedTriggers(new ArrayList<>())
304                         .build();
305         TriggerSpecs triggerSpecs = new TriggerSpecs(
306                 getTriggerSpecValueSumArrayValidBaseline(startTime),
307                 /* maxReports */ 3,
308                 source);
309         // Oblige building privacy parameters for the trigger specs
310         triggerSpecs.getInformationGain(source, FakeFlagsFactory.getFlagsForTest());
311         return triggerSpecs;
312     }
313 
getValidSourceWithFlexEventReport()314     public static Source getValidSourceWithFlexEventReport() {
315         try {
316             return getValidSourceBuilder()
317                     .setAttributedTriggers(new ArrayList<>())
318                     .setTriggerSpecs(getValidTriggerSpecsCountBased())
319                     .setMaxEventLevelReports(getValidTriggerSpecsCountBased().getMaxReports())
320                     .build();
321         } catch (JSONException e) {
322             return null;
323         }
324     }
325 
getValidSourceWithFlexEventReportWithFewerState()326     public static Source getValidSourceWithFlexEventReportWithFewerState() {
327         try {
328             return getMinimalValidSourceBuilder()
329                     .setAttributedTriggers(new ArrayList<>())
330                     .setTriggerSpecs(getValidTriggerSpecsCountBasedWithFewerState())
331                     .setMaxEventLevelReports(
332                             getValidTriggerSpecsCountBasedWithFewerState().getMaxReports())
333                     .build();
334         } catch (JSONException e) {
335             return null;
336         }
337     }
338 
339     /** Provide a Source with hardcoded epsilon in TriggerSpecs. */
getValidFullFlexSourceWithNonDefaultEpsilon()340     public static Source getValidFullFlexSourceWithNonDefaultEpsilon() {
341         try {
342             return getMinimalValidSourceBuilder()
343                     .setAttributedTriggers(new ArrayList<>())
344                     .setTriggerSpecs(getValidTriggerSpecsWithNonDefaultEpsilon())
345                     .setMaxEventLevelReports(
346                             getValidTriggerSpecsCountBasedWithFewerState().getMaxReports())
347                     .build();
348         } catch (JSONException e) {
349             LoggerFactory.getMeasurementLogger()
350                     .e(e, "Unable to build Source with non default epsilon.");
351             return null;
352         }
353     }
354 
getValidFullSourceBuilderWithFlexEventReportValueSum()355     public static Source.Builder getValidFullSourceBuilderWithFlexEventReportValueSum() {
356         try {
357             return getValidSourceBuilder()
358                     .setAttributedTriggers(new ArrayList<>())
359                     .setTriggerSpecs(getValidTriggerSpecsValueSum());
360         } catch (JSONException e) {
361             return null;
362         }
363     }
364 
getValidSourceBuilderWithFlexEventReportValueSum()365     public static Source.Builder getValidSourceBuilderWithFlexEventReportValueSum()
366             throws JSONException {
367         TriggerSpecs triggerSpecs = getValidTriggerSpecsValueSum();
368         return getMinimalValidSourceBuilder()
369                 .setId(UUID.randomUUID().toString())
370                 .setTriggerSpecsString(triggerSpecs.encodeToJson())
371                 .setMaxEventLevelReports(triggerSpecs.getMaxReports())
372                 .setEventAttributionStatus(null)
373                 .setPrivacyParameters(triggerSpecs.encodePrivacyParametersToJsonString());
374     }
375 
getValidSourceBuilderWithFlexEventReport()376     public static Source.Builder getValidSourceBuilderWithFlexEventReport() throws JSONException {
377         TriggerSpecs triggerSpecs = getValidTriggerSpecsCountBased();
378         return getMinimalValidSourceBuilder()
379                 .setId(UUID.randomUUID().toString())
380                 .setTriggerSpecsString(triggerSpecs.encodeToJson())
381                 .setMaxEventLevelReports(triggerSpecs.getMaxReports())
382                 .setEventAttributionStatus(null)
383                 .setPrivacyParameters(triggerSpecs.encodePrivacyParametersToJsonString());
384     }
385 
getTriggerSpecCountEncodedJsonValidBaseline()386     public static String getTriggerSpecCountEncodedJsonValidBaseline() {
387         return "[{\"trigger_data\": [1, 2, 3],"
388                 + "\"event_report_windows\": { "
389                 + "\"start_time\": 0, "
390                 + String.format(
391                         "\"end_times\": [%s, %s, %s]}, ",
392                         TimeUnit.DAYS.toMillis(2),
393                         TimeUnit.DAYS.toMillis(7),
394                         TimeUnit.DAYS.toMillis(30))
395                 + "\"summary_operator\": \"count\", "
396                 + "\"summary_buckets\": [1, 2, 3, 4]}]";
397     }
398 
getTriggerSpecArrayCountValidBaseline()399     public static TriggerSpec[] getTriggerSpecArrayCountValidBaseline() {
400         return triggerSpecArrayFrom(getTriggerSpecCountEncodedJsonValidBaseline());
401     }
402 
getTriggerSpecValueSumEncodedJsonValidBaseline()403     public static String getTriggerSpecValueSumEncodedJsonValidBaseline() {
404         return getTriggerSpecValueSumEncodedJsonValidBaseline(0L);
405     }
406 
407     /** Provides baseline trigger specs JSON given a start time. */
getTriggerSpecValueSumEncodedJsonValidBaseline(long startTime)408     public static String getTriggerSpecValueSumEncodedJsonValidBaseline(long startTime) {
409         return "[{\"trigger_data\": [1, 2],"
410                 + "\"event_report_windows\": { "
411                 + "\"start_time\": " + String.valueOf(startTime) + ", "
412                 + String.format(
413                         "\"end_times\": [%s, %s]}, ",
414                         TimeUnit.DAYS.toMillis(2), TimeUnit.DAYS.toMillis(7))
415                 + "\"summary_operator\": \"value_sum\", "
416                 + "\"summary_buckets\": [10, 100]}]";
417     }
418 
getTriggerSpecValueSumArrayValidBaseline()419     public static TriggerSpec[] getTriggerSpecValueSumArrayValidBaseline() {
420         return triggerSpecArrayFrom(getTriggerSpecValueSumEncodedJsonValidBaseline(0L));
421     }
422 
423     /** Provides value-sum trigger specs given a start time. */
getTriggerSpecValueSumArrayValidBaseline(long startTime)424     public static TriggerSpec[] getTriggerSpecValueSumArrayValidBaseline(long startTime) {
425         return triggerSpecArrayFrom(getTriggerSpecValueSumEncodedJsonValidBaseline(startTime));
426     }
427 
getTriggerSpecValueCountJsonTwoTriggerSpecs()428     public static TriggerSpec[] getTriggerSpecValueCountJsonTwoTriggerSpecs() {
429         return triggerSpecArrayFrom(
430                 "[{\"trigger_data\": [1, 2, 3],"
431                         + "\"event_report_windows\": { "
432                         + "\"start_time\": 0, "
433                         + String.format(
434                                 "\"end_times\": [%s, %s, %s]}, ",
435                                 TimeUnit.DAYS.toMillis(2),
436                                 TimeUnit.DAYS.toMillis(7),
437                                 TimeUnit.DAYS.toMillis(30))
438                         + "\"summary_operator\": \"count\", "
439                         + "\"summary_buckets\": [1, 2, 3, 4]}, "
440                         + "{\"trigger_data\": [4, 5, 6, 7],"
441                         + "\"event_report_windows\": { "
442                         + "\"start_time\": 0, "
443                         + String.format("\"end_times\": [%s]}, ", TimeUnit.DAYS.toMillis(3))
444                         + "\"summary_operator\": \"count\", "
445                         + "\"summary_buckets\": [1,5,7]} "
446                         + "]");
447     }
448 
triggerSpecArrayFrom(String json)449     private static TriggerSpec[] triggerSpecArrayFrom(String json) {
450         try {
451             JSONArray jsonArray = new JSONArray(json);
452             TriggerSpec[] triggerSpecArray = new TriggerSpec[jsonArray.length()];
453             for (int i = 0; i < jsonArray.length(); i++) {
454                 triggerSpecArray[i] = new TriggerSpec.Builder(jsonArray.getJSONObject(i)).build();
455             }
456             return triggerSpecArray;
457         } catch (JSONException ignored) {
458             return null;
459         }
460     }
461 }
462