• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 org.json.JSONArray;
20 import org.json.JSONException;
21 import org.json.JSONObject;
22 
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.Objects;
26 
27 /**
28  * A class wrapper for the trigger specification from the input argument during source registration
29  */
30 public class TriggerSpec {
31     private List<Integer> mTriggerData;
32     private int mEventReportWindowsStart;
33     private List<Long> mEventReportWindowsEnd;
34     private SummaryOperatorType mSummaryWindowOperator;
35     private List<Integer> mSummaryBucket;
36 
37     @Override
equals(Object obj)38     public boolean equals(Object obj) {
39         if (!(obj instanceof TriggerSpec)) {
40             return false;
41         }
42         TriggerSpec t = (TriggerSpec) obj;
43         return mTriggerData.equals(t.mTriggerData)
44                 && mEventReportWindowsStart == t.mEventReportWindowsStart
45                 && mEventReportWindowsEnd.equals(t.mEventReportWindowsEnd)
46                 && mSummaryWindowOperator == t.mSummaryWindowOperator
47                 && mSummaryBucket.equals(t.mSummaryBucket);
48     }
49 
50     @Override
hashCode()51     public int hashCode() {
52         return Objects.hash(
53                 mTriggerData,
54                 mEventReportWindowsStart,
55                 mEventReportWindowsEnd,
56                 mSummaryWindowOperator,
57                 mSummaryBucket);
58     }
59 
60     /** @return Trigger Data */
getTriggerData()61     public List<Integer> getTriggerData() {
62         return mTriggerData;
63     }
64 
65     /** @return Event Report Windows Start */
getEventReportWindowsStart()66     public int getEventReportWindowsStart() {
67         return mEventReportWindowsStart;
68     }
69 
70     /** @return Event Report Windows End */
getEventReportWindowsEnd()71     public List<Long> getEventReportWindowsEnd() {
72         return mEventReportWindowsEnd;
73     }
74 
75     /** @return Summary Window Operator */
getSummaryWindowOperator()76     public SummaryOperatorType getSummaryWindowOperator() {
77         return mSummaryWindowOperator;
78     }
79 
80     /** @return Summary Bucket */
getSummaryBucket()81     public List<Integer> getSummaryBucket() {
82         return mSummaryBucket;
83     }
84 
85     /**
86      * Encode the parameter to JSON
87      *
88      * @return json object encode this class
89      */
encodeJSON()90     public JSONObject encodeJSON() throws JSONException {
91         JSONObject json = new JSONObject();
92         json.put("trigger_data", new JSONArray(mTriggerData));
93         JSONObject windows = new JSONObject();
94         windows.put("start_time", mEventReportWindowsStart);
95         windows.put("end_times", new JSONArray(mEventReportWindowsEnd));
96         json.put("event_report_windows", windows);
97         json.put("summary_window_operator", mSummaryWindowOperator.name().toLowerCase());
98         json.put("summary_buckets", new JSONArray(mSummaryBucket));
99         return json;
100     }
101 
isStrictIncreasing(List<T> list)102     private static <T extends Comparable<T>> boolean isStrictIncreasing(List<T> list) {
103         for (int i = 1; i < list.size(); i++) {
104             if (list.get(i).compareTo(list.get(i - 1)) <= 0) {
105                 return false;
106             }
107         }
108         return true;
109     }
110 
111     /** The choice of the summary operator with the reporting window */
112     public enum SummaryOperatorType {
113         COUNT,
114         VALUE_SUM
115     }
116 
getIntArrayFromJSON(JSONObject json, String key)117     private static ArrayList<Integer> getIntArrayFromJSON(JSONObject json, String key)
118             throws JSONException {
119         ArrayList<Integer> result = new ArrayList<>();
120         JSONArray valueArray = json.getJSONArray(key);
121         for (int i = 0; i < valueArray.length(); i++) {
122             result.add(valueArray.getInt(i));
123         }
124         return result;
125     }
126 
validateParameters()127     private void validateParameters() {
128         if (!isStrictIncreasing(mEventReportWindowsEnd)) {
129             throw new IllegalArgumentException(FieldsKey.EVENT_REPORT_WINDOWS + " not increasing");
130         }
131         if (!isStrictIncreasing(mSummaryBucket)) {
132             throw new IllegalArgumentException(FieldsKey.SUMMARY_BUCKETS + " not increasing");
133         }
134         if (mEventReportWindowsStart < 0) {
135             mEventReportWindowsStart = 0;
136         }
137     }
138 
139     /** */
140     public static final class Builder {
141         private final TriggerSpec mBuilding;
142 
Builder(JSONObject jsonObject)143         public Builder(JSONObject jsonObject) throws JSONException {
144             mBuilding = new TriggerSpec();
145             mBuilding.mSummaryWindowOperator = SummaryOperatorType.COUNT;
146             mBuilding.mEventReportWindowsStart = 0;
147             mBuilding.mSummaryBucket = new ArrayList<>();
148             mBuilding.mEventReportWindowsEnd = new ArrayList<>();
149             this.setTriggerData(getIntArrayFromJSON(jsonObject, FieldsKey.TRIGGER_DATA));
150             if (mBuilding.mTriggerData.size()
151                     > PrivacyParams.getMaxFlexibleEventTriggerDataCardinality()) {
152                 throw new IllegalArgumentException(
153                         "Trigger Data Cardinality Exceeds "
154                                 + PrivacyParams.getMaxFlexibleEventTriggerDataCardinality());
155             }
156             JSONObject jsonReportWindows = jsonObject.getJSONObject(FieldsKey.EVENT_REPORT_WINDOWS);
157             if (!jsonReportWindows.isNull(FieldsKey.START_TIME)) {
158                 this.setEventReportWindowsStart(jsonReportWindows.getInt(FieldsKey.START_TIME));
159             }
160 
161             JSONArray jsonArray = jsonReportWindows.getJSONArray(FieldsKey.END_TIME);
162             if (jsonArray.length() > PrivacyParams.getMaxFlexibleEventReportingWindows()) {
163                 throw new IllegalArgumentException(
164                         "Number of Reporting Windows Exceeds "
165                                 + PrivacyParams.getMaxFlexibleEventReportingWindows());
166             }
167             List<Long> data = new ArrayList<>();
168             for (int i = 0; i < jsonArray.length(); i++) {
169                 data.add(jsonArray.getLong(i));
170             }
171             this.setEventReportWindowsEnd(data);
172 
173             if (!jsonObject.isNull(FieldsKey.SUMMARY_WINDOW_OPERATOR)) {
174                 try {
175                     SummaryOperatorType op =
176                             SummaryOperatorType.valueOf(
177                                     jsonObject
178                                             .getString(FieldsKey.SUMMARY_WINDOW_OPERATOR)
179                                             .toUpperCase());
180                     this.setSummaryWindowOperator(op);
181                 } catch (IllegalArgumentException e) {
182                     // if a summary window operator is defined, but not in the pre-defined list, it
183                     // will throw to exception.
184                     throw new IllegalArgumentException(
185                             FieldsKey.SUMMARY_WINDOW_OPERATOR + " invalid");
186                 }
187             }
188             this.setSummaryBucket(getIntArrayFromJSON(jsonObject, FieldsKey.SUMMARY_BUCKETS));
189             mBuilding.validateParameters();
190         }
191 
192         /** See {@link TriggerSpec#getTriggerData()} ()}. */
setTriggerData(List<Integer> triggerData)193         public Builder setTriggerData(List<Integer> triggerData) {
194             mBuilding.mTriggerData = triggerData;
195             return this;
196         }
197 
198         /** See {@link TriggerSpec#getEventReportWindowsStart()} ()}. */
setEventReportWindowsStart(int eventReportWindowsStart)199         public Builder setEventReportWindowsStart(int eventReportWindowsStart) {
200             mBuilding.mEventReportWindowsStart = eventReportWindowsStart;
201             return this;
202         }
203 
204         /** See {@link TriggerSpec#getEventReportWindowsEnd()} ()}. */
setEventReportWindowsEnd(List<Long> eventReportWindowsEnd)205         public Builder setEventReportWindowsEnd(List<Long> eventReportWindowsEnd) {
206             mBuilding.mEventReportWindowsEnd = eventReportWindowsEnd;
207             return this;
208         }
209 
210         /** See {@link TriggerSpec#getSummaryWindowOperator()} ()}. */
setSummaryWindowOperator(SummaryOperatorType summaryWindowOperator)211         public Builder setSummaryWindowOperator(SummaryOperatorType summaryWindowOperator) {
212             mBuilding.mSummaryWindowOperator = summaryWindowOperator;
213             return this;
214         }
215 
216         /** See {@link TriggerSpec#getSummaryBucket()} ()}. */
setSummaryBucket(List<Integer> summaryBucket)217         public Builder setSummaryBucket(List<Integer> summaryBucket) {
218             mBuilding.mSummaryBucket = summaryBucket;
219             return this;
220         }
221 
222         /** Build the {@link TriggerSpec}. */
build()223         public TriggerSpec build() {
224             return mBuilding;
225         }
226     }
227 
228     private interface FieldsKey {
229         String END_TIME = "end_times";
230         String START_TIME = "start_time";
231         String SUMMARY_WINDOW_OPERATOR = "summary_window_operator";
232         String EVENT_REPORT_WINDOWS = "event_report_windows";
233         String TRIGGER_DATA = "trigger_data";
234         String SUMMARY_BUCKETS = "summary_buckets";
235     }
236 }
237