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.Nullable; 20 21 import com.android.adservices.LoggerFactory; 22 import com.android.adservices.service.Flags; 23 import com.android.adservices.service.measurement.registration.FetcherUtil; 24 25 import org.json.JSONArray; 26 import org.json.JSONException; 27 import org.json.JSONObject; 28 29 import java.math.BigInteger; 30 import java.util.ArrayList; 31 import java.util.HashMap; 32 import java.util.Iterator; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.Objects; 36 37 /** POJO for FilterMap. */ 38 public class FilterMap { 39 40 private Map<String, List<String>> mAttributionFilterMap; 41 private Map<String, FilterValue> mAttributionFilterMapWithLongValue; 42 43 public static final String RESERVED_PREFIX = "_"; 44 public static final String LOOKBACK_WINDOW = "_lookback_window"; 45 FilterMap()46 FilterMap() { 47 mAttributionFilterMap = new HashMap<>(); 48 mAttributionFilterMapWithLongValue = new HashMap<>(); 49 } 50 51 @Override equals(Object obj)52 public boolean equals(Object obj) { 53 if (!(obj instanceof FilterMap)) { 54 return false; 55 } 56 FilterMap attributionFilterMap = (FilterMap) obj; 57 return Objects.equals(mAttributionFilterMap, attributionFilterMap.mAttributionFilterMap) 58 && Objects.equals( 59 mAttributionFilterMapWithLongValue, 60 attributionFilterMap.mAttributionFilterMapWithLongValue); 61 } 62 63 @Override hashCode()64 public int hashCode() { 65 return Objects.hash(mAttributionFilterMap, mAttributionFilterMapWithLongValue); 66 } 67 68 /** 69 * Returns the attribution filter map. 70 * 71 * @deprecated use {@link #getAttributionFilterMapWithLongValue()} instead. 72 */ 73 @Deprecated getAttributionFilterMap()74 public Map<String, List<String>> getAttributionFilterMap() { 75 return mAttributionFilterMap; 76 } 77 78 /** Returns the attribution filter map with lookback window included. */ getAttributionFilterMapWithLongValue()79 public Map<String, FilterValue> getAttributionFilterMapWithLongValue() { 80 return mAttributionFilterMapWithLongValue; 81 } 82 83 /** 84 * Returns the long value given the key. {@code key} must be present and the value kind must be 85 * {@link FilterValue.Kind#LONG_VALUE}. 86 */ getLongValue(String key)87 public long getLongValue(String key) { 88 return mAttributionFilterMapWithLongValue.get(key).longValue(); 89 } 90 91 /** Returns whether the attribution filter map is empty. */ isEmpty(Flags flags)92 public boolean isEmpty(Flags flags) { 93 return flags.getMeasurementEnableLookbackWindowFilter() 94 ? mAttributionFilterMapWithLongValue.isEmpty() 95 : mAttributionFilterMap.isEmpty(); 96 } 97 98 /** 99 * Returns the string list value given the key. {@code key} must be present and the value kind 100 * must be {@link FilterValue.Kind#STRING_LIST_VALUE}. 101 */ getStringListValue(String key)102 public List<String> getStringListValue(String key) { 103 return mAttributionFilterMapWithLongValue.get(key).stringListValue(); 104 } 105 106 /** 107 * Serializes the object into a {@link JSONObject}. 108 * 109 * @return serialized {@link JSONObject}. 110 */ 111 @Nullable serializeAsJson(Flags flags)112 public JSONObject serializeAsJson(Flags flags) { 113 return flags.getMeasurementEnableLookbackWindowFilter() 114 ? serializeAsJsonV2() 115 : serializeAsJson(); 116 } 117 118 @Nullable serializeAsJson()119 private JSONObject serializeAsJson() { 120 if (mAttributionFilterMap == null) { 121 return null; 122 } 123 124 try { 125 JSONObject result = new JSONObject(); 126 for (String key : mAttributionFilterMap.keySet()) { 127 result.put(key, new JSONArray(mAttributionFilterMap.get(key))); 128 } 129 130 return result; 131 } catch (JSONException e) { 132 LoggerFactory.getMeasurementLogger().d(e, "Failed to serialize filtermap."); 133 return null; 134 } 135 } 136 137 @Nullable serializeAsJsonV2()138 private JSONObject serializeAsJsonV2() { 139 if (mAttributionFilterMapWithLongValue == null) { 140 return null; 141 } 142 143 try { 144 JSONObject result = new JSONObject(); 145 for (String key : mAttributionFilterMapWithLongValue.keySet()) { 146 FilterValue value = mAttributionFilterMapWithLongValue.get(key); 147 switch (value.kind()) { 148 case LONG_VALUE: 149 result.put(key, value.longValue()); 150 break; 151 case STRING_LIST_VALUE: 152 result.put(key, new JSONArray(value.stringListValue())); 153 break; 154 } 155 } 156 return result; 157 } catch (JSONException e) { 158 LoggerFactory.getMeasurementLogger().d(e, "Failed to serialize filtermap."); 159 return null; 160 } 161 } 162 163 /** Builder for {@link FilterMap}. */ 164 public static final class Builder { 165 private final FilterMap mBuilding; 166 Builder()167 public Builder() { 168 mBuilding = new FilterMap(); 169 } 170 171 /** See {@link FilterMap#getAttributionFilterMapWithLongValue()}. */ setAttributionFilterMapWithLongValue( Map<String, FilterValue> attributionFilterMap)172 public Builder setAttributionFilterMapWithLongValue( 173 Map<String, FilterValue> attributionFilterMap) { 174 mBuilding.mAttributionFilterMapWithLongValue = attributionFilterMap; 175 return this; 176 } 177 178 /** Adds filter with long value. */ addLongValue(String key, long value)179 public Builder addLongValue(String key, long value) { 180 mBuilding.mAttributionFilterMapWithLongValue.put(key, FilterValue.ofLong(value)); 181 return this; 182 } 183 184 /** Adds filter with string list value. */ addStringListValue(String key, List<String> value)185 public Builder addStringListValue(String key, List<String> value) { 186 mBuilding.mAttributionFilterMapWithLongValue.put(key, FilterValue.ofStringList(value)); 187 return this; 188 } 189 190 /** 191 * See {@link FilterMap#getAttributionFilterMap()}. 192 * 193 * @deprecated use {@link #setAttributionFilterMapWithLongValue} instead. 194 */ 195 @Deprecated setAttributionFilterMap(Map<String, List<String>> attributionFilterMap)196 public Builder setAttributionFilterMap(Map<String, List<String>> attributionFilterMap) { 197 mBuilding.mAttributionFilterMap = attributionFilterMap; 198 return this; 199 } 200 201 /** Builds FilterMap from JSONObject. */ buildFilterData(JSONObject jsonObject, Flags flags)202 public Builder buildFilterData(JSONObject jsonObject, Flags flags) throws JSONException { 203 return flags.getMeasurementEnableLookbackWindowFilter() 204 ? buildFilterDataV2(jsonObject) 205 : buildFilterData(jsonObject); 206 } 207 208 /** 209 * Builds FilterMap from JSONObject. 210 * 211 * @deprecated use {@link #buildFilterDataV2} instead. 212 */ 213 @Deprecated buildFilterData(JSONObject jsonObject)214 public Builder buildFilterData(JSONObject jsonObject) throws JSONException { 215 Map<String, List<String>> filterMap = new HashMap<>(); 216 Iterator<String> keys = jsonObject.keys(); 217 while (keys.hasNext()) { 218 String key = keys.next(); 219 JSONArray jsonArray = jsonObject.getJSONArray(key); 220 List<String> filterMapList = new ArrayList<>(); 221 for (int i = 0; i < jsonArray.length(); i++) { 222 filterMapList.add(jsonArray.getString(i)); 223 } 224 filterMap.put(key, filterMapList); 225 } 226 mBuilding.mAttributionFilterMap = filterMap; 227 return this; 228 } 229 230 /** Builds FilterMap from JSONObject with long filter values. */ buildFilterDataV2(JSONObject jsonObject)231 public Builder buildFilterDataV2(JSONObject jsonObject) throws JSONException { 232 Map<String, FilterValue> filterMap = new HashMap<>(); 233 Iterator<String> keys = jsonObject.keys(); 234 while (keys.hasNext()) { 235 String key = keys.next(); 236 if (LOOKBACK_WINDOW.equals(key)) { 237 String value = jsonObject.getString(key); 238 try { 239 BigInteger lookbackWindowValue = new BigInteger(value); 240 filterMap.put( 241 key, 242 FilterValue.ofLong( 243 lookbackWindowValue.compareTo( 244 FetcherUtil 245 .BIG_INTEGER_LONG_MAX_VALUE) 246 > 0 247 ? Long.MAX_VALUE 248 : Long.parseLong(value))); 249 } catch (NumberFormatException e) { 250 throw new JSONException( 251 String.format( 252 "Failed to parse long value: %s for key: %s", value, key)); 253 } 254 } else { 255 JSONArray jsonArray = jsonObject.getJSONArray(key); 256 List<String> filterMapList = new ArrayList<>(); 257 for (int i = 0; i < jsonArray.length(); i++) { 258 filterMapList.add(jsonArray.getString(i)); 259 } 260 filterMap.put(key, FilterValue.ofStringList(filterMapList)); 261 } 262 } 263 mBuilding.mAttributionFilterMapWithLongValue = filterMap; 264 return this; 265 } 266 267 /** Build the {@link FilterMap}. */ build()268 public FilterMap build() { 269 return mBuilding; 270 } 271 } 272 } 273