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