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