• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.util;
18 
19 import android.annotation.NonNull;
20 
21 import com.android.adservices.service.measurement.FilterMap;
22 
23 import org.json.JSONArray;
24 import org.json.JSONException;
25 import org.json.JSONObject;
26 
27 import java.util.ArrayList;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Set;
31 
32 /** Filtering utilities for measurement. */
33 public final class Filter {
Filter()34     private Filter() { }
35 
36     /**
37      * Checks whether source filter and trigger filter are matched. When a key is only present in
38      * source or trigger, ignore that key. When a key is present both in source and trigger, the key
39      * matches if the intersection of values is not empty.
40      *
41      * @param sourceFilter the {@code FilterMap} in attribution source.
42      * @param triggerFilters a list of {@code FilterMap}, the trigger filter set.
43      * @param isFilter true for filters, false for not_filters.
44      * @return return true when all keys shared by source filter and trigger filter are matched.
45      */
isFilterMatch( FilterMap sourceFilter, List<FilterMap> triggerFilters, boolean isFilter)46     public static boolean isFilterMatch(
47             FilterMap sourceFilter, List<FilterMap> triggerFilters, boolean isFilter) {
48         if (sourceFilter.getAttributionFilterMap().isEmpty() || triggerFilters.isEmpty()) {
49             return true;
50         }
51         for (FilterMap filterMap : triggerFilters) {
52             if (isFilterMatch(sourceFilter, filterMap, isFilter)) {
53                 return true;
54             }
55         }
56         return false;
57     }
58 
isFilterMatch( FilterMap sourceFilter, FilterMap triggerFilter, boolean isFilter)59     private static boolean isFilterMatch(
60             FilterMap sourceFilter, FilterMap triggerFilter, boolean isFilter) {
61         for (String key : triggerFilter.getAttributionFilterMap().keySet()) {
62             if (!sourceFilter.getAttributionFilterMap().containsKey(key)) {
63                 continue;
64             }
65             // Finds the intersection of two value lists.
66             List<String> sourceValues = sourceFilter.getAttributionFilterMap().get(key);
67             List<String> triggerValues = triggerFilter.getAttributionFilterMap().get(key);
68             if (!matchFilterValues(sourceValues, triggerValues, isFilter)) {
69                 return false;
70             }
71         }
72         return true;
73     }
74 
matchFilterValues(List<String> sourceValues, List<String> triggerValues, boolean isFilter)75     private static boolean matchFilterValues(List<String> sourceValues, List<String> triggerValues,
76             boolean isFilter) {
77         if (triggerValues.isEmpty()) {
78             return isFilter ? sourceValues.isEmpty() : !sourceValues.isEmpty();
79         }
80         Set<String> intersection = new HashSet<>(sourceValues);
81         intersection.retainAll(triggerValues);
82         return isFilter ? !intersection.isEmpty() : intersection.isEmpty();
83     }
84 
85     /**
86      * Deserializes the provided {@link JSONArray} of filters into filter set.
87      *
88      * @param filters serialized filter set
89      * @return deserialized filter set
90      * @throws JSONException if the deserialization fails
91      */
92     @NonNull
deserializeFilterSet(@onNull JSONArray filters)93     public static List<FilterMap> deserializeFilterSet(@NonNull JSONArray filters)
94             throws JSONException {
95         List<FilterMap> filterSet = new ArrayList<>();
96         for (int i = 0; i < filters.length(); i++) {
97             FilterMap filterMap =
98                     new FilterMap.Builder().buildFilterData(filters.getJSONObject(i)).build();
99             filterSet.add(filterMap);
100         }
101         return filterSet;
102     }
103 
104     /**
105      * Builds {@link JSONArray} our of the list of {@link List<FilterMap>} provided by serializing
106      * it recursively.
107      *
108      * @param filterMaps to be serialized
109      * @return serialized filter maps
110      */
111     @NonNull
serializeFilterSet(@onNull List<FilterMap> filterMaps)112     public static JSONArray serializeFilterSet(@NonNull List<FilterMap> filterMaps) {
113         JSONArray serializedFilterMaps = new JSONArray();
114         for (FilterMap sourceNotFilter : filterMaps) {
115             serializedFilterMaps.put(sourceNotFilter.serializeAsJson());
116         }
117         return serializedFilterMaps;
118     }
119 
120     /**
121      * Filters can be available in either {@link JSONObject} format or {@link JSONArray} format. For
122      * consistency across the board, this method wraps the {@link JSONObject} into {@link
123      * JSONArray}.
124      *
125      * @param json json where to look for the filter object
126      * @param key key with which the filter object is associated
127      * @return wrapped {@link JSONArray}
128      * @throws JSONException when creation of {@link JSONArray} fails
129      */
130     @NonNull
maybeWrapFilters(@onNull JSONObject json, @NonNull String key)131     public static JSONArray maybeWrapFilters(@NonNull JSONObject json, @NonNull String key)
132             throws JSONException {
133         JSONObject maybeFilterMap = json.optJSONObject(key);
134         if (maybeFilterMap != null) {
135             JSONArray filterSet = new JSONArray();
136             filterSet.put(maybeFilterMap);
137             return filterSet;
138         }
139         return json.getJSONArray(key);
140     }
141 }
142