1 /* 2 * Copyright (C) 2024 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 package com.android.adservices.service.measurement.aggregation; 17 18 import android.annotation.Nullable; 19 20 import com.android.adservices.service.Flags; 21 import com.android.adservices.service.measurement.FilterMap; 22 import com.android.adservices.service.measurement.util.Filter; 23 import com.android.adservices.service.measurement.util.Filter.FilterContract; 24 import com.android.adservices.service.measurement.util.UnsignedLong; 25 26 import org.json.JSONArray; 27 import org.json.JSONException; 28 import org.json.JSONObject; 29 30 import java.util.HashMap; 31 import java.util.Iterator; 32 import java.util.List; 33 import java.util.Map; 34 import java.util.Objects; 35 36 /** POJO for AggregatableValuesConfig */ 37 public class AggregatableValuesConfig { 38 private final Map<String, AggregatableKeyValue> mValues; 39 @Nullable private final List<FilterMap> mFilterSet; 40 @Nullable private final List<FilterMap> mNotFilterSet; 41 AggregatableValuesConfig(AggregatableValuesConfig.Builder builder)42 private AggregatableValuesConfig(AggregatableValuesConfig.Builder builder) { 43 mValues = builder.mValues; 44 mFilterSet = builder.mFilterSet; 45 mNotFilterSet = builder.mNotFilterSet; 46 } 47 48 /** 49 * Returns a map of key and aggregatable_value's values. Example: {"campaignCounts": 50 * AggregatableKeyValue{"value":1664}} 51 */ getValues()52 public Map<String, AggregatableKeyValue> getValues() { 53 return mValues; 54 } 55 56 /** 57 * Returns a map of key and int value of aggregatable_values's values. Example: 58 * {"campaignCounts": 1664} 59 */ getConfigValuesMap()60 public Map<String, Integer> getConfigValuesMap() { 61 Map<String, Integer> configValuesMap = new HashMap<>(); 62 for (String key : mValues.keySet()) { 63 configValuesMap.put(key, mValues.get(key).getValue()); 64 } 65 return configValuesMap; 66 } 67 68 /** Returns AggregateKeyValue filters. Example: [{"category": ["filter_1", "filter_2"]}] */ 69 @Nullable getFilterSet()70 public List<FilterMap> getFilterSet() { 71 return mFilterSet; 72 } 73 74 /** Returns AggregateKeyValue not_filters. Example: [{"category": ["filter_2", "filter_3"]}] */ 75 @Nullable getNotFilterSet()76 public List<FilterMap> getNotFilterSet() { 77 return mNotFilterSet; 78 } 79 80 @Override equals(Object o)81 public boolean equals(Object o) { 82 if (this == o) return true; 83 if (!(o instanceof AggregatableValuesConfig)) return false; 84 AggregatableValuesConfig that = (AggregatableValuesConfig) o; 85 return Objects.equals(mValues, that.mValues) 86 && Objects.equals(mFilterSet, that.mFilterSet) 87 && Objects.equals(mNotFilterSet, that.mNotFilterSet); 88 } 89 90 @Override hashCode()91 public int hashCode() { 92 return Objects.hash(mValues, mFilterSet, mNotFilterSet); 93 } 94 95 public static final class Builder { 96 private Map<String, AggregatableKeyValue> mValues; 97 @Nullable private List<FilterMap> mFilterSet; 98 @Nullable private List<FilterMap> mNotFilterSet; 99 Builder(Map<String, AggregatableKeyValue> values)100 public Builder(Map<String, AggregatableKeyValue> values) { 101 mValues = values; 102 } 103 Builder(JSONObject aggregateKeyValuesJson, Flags flags)104 public Builder(JSONObject aggregateKeyValuesJson, Flags flags) throws JSONException { 105 if (!aggregateKeyValuesJson.isNull(AggregatableValuesConfigContract.VALUES)) { 106 mValues = 107 buildValues( 108 aggregateKeyValuesJson.getJSONObject( 109 AggregatableValuesConfigContract.VALUES), 110 flags); 111 } else { 112 mValues = buildValues(aggregateKeyValuesJson, flags); 113 } 114 Filter filter = new Filter(flags); 115 if (!aggregateKeyValuesJson.isNull(FilterContract.FILTERS)) { 116 JSONArray filterSet = 117 Filter.maybeWrapFilters(aggregateKeyValuesJson, FilterContract.FILTERS); 118 mFilterSet = filter.deserializeFilterSet(filterSet); 119 } 120 if (!aggregateKeyValuesJson.isNull(FilterContract.NOT_FILTERS)) { 121 JSONArray notFilterSet = 122 Filter.maybeWrapFilters(aggregateKeyValuesJson, FilterContract.NOT_FILTERS); 123 mNotFilterSet = filter.deserializeFilterSet(notFilterSet); 124 } 125 } 126 127 /** 128 * Create {@link AggregatableValuesConfigContract#getValues} from a JSONObject that has 129 * Integer typed values. 130 */ buildValues( JSONObject aggregateKeyValuesJson, Flags flags)131 private Map<String, AggregatableKeyValue> buildValues( 132 JSONObject aggregateKeyValuesJson, Flags flags) throws JSONException { 133 Iterator<String> valuesKeySet = aggregateKeyValuesJson.keys(); 134 Map<String, AggregatableKeyValue> mValues = new HashMap<>(); 135 while (valuesKeySet.hasNext()) { 136 String key = valuesKeySet.next(); 137 AggregatableKeyValue.Builder aggregatableKeyValueBuilder; 138 if (flags.getMeasurementEnableFlexibleContributionFiltering()) { 139 int value; 140 UnsignedLong filteringId = UnsignedLong.ZERO; 141 if (aggregateKeyValuesJson.get(key) instanceof JSONObject) { 142 JSONObject obj = aggregateKeyValuesJson.getJSONObject(key); 143 value = obj.getInt(AggregatableKeyValue.AggregatableKeyValueContract.VALUE); 144 if (!obj.isNull( 145 AggregatableKeyValue.AggregatableKeyValueContract.FILTERING_ID)) { 146 filteringId = 147 new UnsignedLong( 148 obj.getString( 149 AggregatableKeyValue 150 .AggregatableKeyValueContract 151 .FILTERING_ID)); 152 } 153 } else { 154 value = aggregateKeyValuesJson.getInt(key); 155 } 156 aggregatableKeyValueBuilder = 157 new AggregatableKeyValue.Builder(value).setFilteringId(filteringId); 158 } else { 159 // This will throw JSONException if a JSONObject was previously persisted, and 160 // flag MEASUREMENT_ENABLE_FLEXIBLE_CONTRIBUTION_FILTERING gets set to false. 161 aggregatableKeyValueBuilder = 162 new AggregatableKeyValue.Builder(aggregateKeyValuesJson.getInt(key)); 163 } 164 mValues.put(key, aggregatableKeyValueBuilder.build()); 165 } 166 return mValues; 167 } 168 169 /** See {@link AggregatableValuesConfig#getFilterSet()} */ setFilterSet(@ullable List<FilterMap> filterSet)170 public Builder setFilterSet(@Nullable List<FilterMap> filterSet) { 171 mFilterSet = filterSet; 172 return this; 173 } 174 175 /** See {@link AggregatableValuesConfig#getNotFilterSet()} */ setNotFilterSet(@ullable List<FilterMap> notFilterSet)176 public Builder setNotFilterSet(@Nullable List<FilterMap> notFilterSet) { 177 mNotFilterSet = notFilterSet; 178 return this; 179 } 180 181 /** Build the {@link AggregatableValuesConfig}. */ build()182 public AggregatableValuesConfig build() throws JSONException { 183 Objects.requireNonNull(mValues); 184 return new AggregatableValuesConfig(this); 185 } 186 } 187 188 public interface AggregatableValuesConfigContract { 189 String VALUES = "values"; 190 } 191 } 192