• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.adservices.common;
18 
19 import android.adservices.adselection.ReportImpressionRequest;
20 import android.annotation.IntDef;
21 import android.annotation.NonNull;
22 import android.os.OutcomeReceiver;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 
26 import com.android.adservices.AdServicesParcelableUtil;
27 import com.android.internal.annotations.VisibleForTesting;
28 
29 import org.json.JSONArray;
30 import org.json.JSONException;
31 import org.json.JSONObject;
32 
33 import java.lang.annotation.Retention;
34 import java.lang.annotation.RetentionPolicy;
35 import java.util.HashSet;
36 import java.util.Objects;
37 import java.util.Set;
38 import java.util.concurrent.Executor;
39 
40 /**
41  * A container for the ad filters that are based on frequency caps.
42  *
43  * <p>Frequency caps filters combine an event type with a set of {@link KeyedFrequencyCap} objects
44  * to define a set of ad filters. If any of these frequency caps are exceeded for a given ad, the ad
45  * will be removed from the group of ads submitted to a buyer adtech's bidding function.
46  *
47  * @hide
48  */
49 // TODO(b/221876775): Unhide for frequency cap API review
50 public final class FrequencyCapFilters implements Parcelable {
51     /**
52      * Event types which are used to update ad counter histograms, which inform frequency cap
53      * filtering in FLEDGE.
54      *
55      * @hide
56      */
57     @IntDef(
58             prefix = {"AD_EVENT_TYPE_"},
59             value = {
60                 AD_EVENT_TYPE_INVALID,
61                 AD_EVENT_TYPE_WIN,
62                 AD_EVENT_TYPE_IMPRESSION,
63                 AD_EVENT_TYPE_VIEW,
64                 AD_EVENT_TYPE_CLICK
65             })
66     @Retention(RetentionPolicy.SOURCE)
67     public @interface AdEventType {}
68 
69     /** @hide */
70     public static final int AD_EVENT_TYPE_INVALID = -1;
71 
72     /**
73      * The WIN ad event type is automatically populated within the FLEDGE service for any winning ad
74      * which is returned from FLEDGE ad selection.
75      *
76      * <p>It should not be used to manually update an ad counter histogram.
77      */
78     public static final int AD_EVENT_TYPE_WIN = 0;
79 
80     public static final int AD_EVENT_TYPE_IMPRESSION = 1;
81     public static final int AD_EVENT_TYPE_VIEW = 2;
82     public static final int AD_EVENT_TYPE_CLICK = 3;
83     /** @hide */
84     @VisibleForTesting public static final String WIN_EVENTS_FIELD_NAME = "win";
85     /** @hide */
86     @VisibleForTesting public static final String IMPRESSION_EVENTS_FIELD_NAME = "impression";
87     /** @hide */
88     @VisibleForTesting public static final String VIEW_EVENTS_FIELD_NAME = "view";
89     /** @hide */
90     @VisibleForTesting public static final String CLICK_EVENTS_FIELD_NAME = "click";
91 
92     @NonNull private final Set<KeyedFrequencyCap> mKeyedFrequencyCapsForWinEvents;
93     @NonNull private final Set<KeyedFrequencyCap> mKeyedFrequencyCapsForImpressionEvents;
94     @NonNull private final Set<KeyedFrequencyCap> mKeyedFrequencyCapsForViewEvents;
95     @NonNull private final Set<KeyedFrequencyCap> mKeyedFrequencyCapsForClickEvents;
96 
97     @NonNull
98     public static final Creator<FrequencyCapFilters> CREATOR =
99             new Creator<FrequencyCapFilters>() {
100                 @Override
101                 public FrequencyCapFilters createFromParcel(@NonNull Parcel in) {
102                     Objects.requireNonNull(in);
103                     return new FrequencyCapFilters(in);
104                 }
105 
106                 @Override
107                 public FrequencyCapFilters[] newArray(int size) {
108                     return new FrequencyCapFilters[size];
109                 }
110             };
111 
FrequencyCapFilters(@onNull Builder builder)112     private FrequencyCapFilters(@NonNull Builder builder) {
113         Objects.requireNonNull(builder);
114 
115         mKeyedFrequencyCapsForWinEvents = builder.mKeyedFrequencyCapsForWinEvents;
116         mKeyedFrequencyCapsForImpressionEvents = builder.mKeyedFrequencyCapsForImpressionEvents;
117         mKeyedFrequencyCapsForViewEvents = builder.mKeyedFrequencyCapsForViewEvents;
118         mKeyedFrequencyCapsForClickEvents = builder.mKeyedFrequencyCapsForClickEvents;
119     }
120 
FrequencyCapFilters(@onNull Parcel in)121     private FrequencyCapFilters(@NonNull Parcel in) {
122         Objects.requireNonNull(in);
123 
124         mKeyedFrequencyCapsForWinEvents =
125                 AdServicesParcelableUtil.readSetFromParcel(in, KeyedFrequencyCap.CREATOR);
126         mKeyedFrequencyCapsForImpressionEvents =
127                 AdServicesParcelableUtil.readSetFromParcel(in, KeyedFrequencyCap.CREATOR);
128         mKeyedFrequencyCapsForViewEvents =
129                 AdServicesParcelableUtil.readSetFromParcel(in, KeyedFrequencyCap.CREATOR);
130         mKeyedFrequencyCapsForClickEvents =
131                 AdServicesParcelableUtil.readSetFromParcel(in, KeyedFrequencyCap.CREATOR);
132     }
133 
134     /**
135      * Gets the set of {@link KeyedFrequencyCap} objects that will filter on the {@link
136      * #AD_EVENT_TYPE_WIN} event type.
137      *
138      * <p>These frequency caps apply to events for ads that were selected as winners in ad
139      * selection. Winning ads are used to automatically increment the associated counter keys on the
140      * win event type.
141      */
142     @NonNull
getKeyedFrequencyCapsForWinEvents()143     public Set<KeyedFrequencyCap> getKeyedFrequencyCapsForWinEvents() {
144         return mKeyedFrequencyCapsForWinEvents;
145     }
146 
147     /**
148      * Gets the set of {@link KeyedFrequencyCap} objects that will filter on the {@link
149      * #AD_EVENT_TYPE_IMPRESSION} event type.
150      *
151      * <p>These frequency caps apply to events which correlate to an impression as interpreted by an
152      * adtech. Note that events are not automatically counted when calling {@link
153      * android.adservices.adselection.AdSelectionManager#reportImpression(ReportImpressionRequest,
154      * Executor, OutcomeReceiver)}.
155      */
156     @NonNull
getKeyedFrequencyCapsForImpressionEvents()157     public Set<KeyedFrequencyCap> getKeyedFrequencyCapsForImpressionEvents() {
158         return mKeyedFrequencyCapsForImpressionEvents;
159     }
160 
161     /**
162      * Gets the set of {@link KeyedFrequencyCap} objects that will filter on the {@link
163      * #AD_EVENT_TYPE_VIEW} event type.
164      *
165      * <p>These frequency caps apply to events which correlate to a view as interpreted by an
166      * adtech.
167      */
168     @NonNull
getKeyedFrequencyCapsForViewEvents()169     public Set<KeyedFrequencyCap> getKeyedFrequencyCapsForViewEvents() {
170         return mKeyedFrequencyCapsForViewEvents;
171     }
172 
173     /**
174      * Gets the set of {@link KeyedFrequencyCap} objects that will filter on the {@link
175      * #AD_EVENT_TYPE_CLICK} event type.
176      *
177      * <p>These frequency caps apply to events which correlate to a click as interpreted by an
178      * adtech.
179      */
180     @NonNull
getKeyedFrequencyCapsForClickEvents()181     public Set<KeyedFrequencyCap> getKeyedFrequencyCapsForClickEvents() {
182         return mKeyedFrequencyCapsForClickEvents;
183     }
184 
185     /**
186      * @return The estimated size of this object, in bytes.
187      * @hide
188      */
getSizeInBytes()189     public int getSizeInBytes() {
190         return getSizeInBytesOfFcapSet(mKeyedFrequencyCapsForWinEvents)
191                 + getSizeInBytesOfFcapSet(mKeyedFrequencyCapsForImpressionEvents)
192                 + getSizeInBytesOfFcapSet(mKeyedFrequencyCapsForViewEvents)
193                 + getSizeInBytesOfFcapSet(mKeyedFrequencyCapsForClickEvents);
194     }
195 
getSizeInBytesOfFcapSet(Set<KeyedFrequencyCap> fcaps)196     private int getSizeInBytesOfFcapSet(Set<KeyedFrequencyCap> fcaps) {
197         int toReturn = 0;
198         for (final KeyedFrequencyCap fcap : fcaps) {
199             toReturn += fcap.getSizeInBytes();
200         }
201         return toReturn;
202     }
203 
204     /**
205      * A JSON serializer.
206      *
207      * @return A JSON serialization of this object.
208      * @hide
209      */
toJson()210     public JSONObject toJson() throws JSONException {
211         JSONObject toReturn = new JSONObject();
212         toReturn.put(WIN_EVENTS_FIELD_NAME, fcapSetToJsonArray(mKeyedFrequencyCapsForWinEvents));
213         toReturn.put(
214                 IMPRESSION_EVENTS_FIELD_NAME,
215                 fcapSetToJsonArray(mKeyedFrequencyCapsForImpressionEvents));
216         toReturn.put(VIEW_EVENTS_FIELD_NAME, fcapSetToJsonArray(mKeyedFrequencyCapsForViewEvents));
217         toReturn.put(
218                 CLICK_EVENTS_FIELD_NAME, fcapSetToJsonArray(mKeyedFrequencyCapsForClickEvents));
219         return toReturn;
220     }
221 
fcapSetToJsonArray(Set<KeyedFrequencyCap> fcapSet)222     private static JSONArray fcapSetToJsonArray(Set<KeyedFrequencyCap> fcapSet)
223             throws JSONException {
224         JSONArray toReturn = new JSONArray();
225         for (KeyedFrequencyCap fcap : fcapSet) {
226             toReturn.put(fcap.toJson());
227         }
228         return toReturn;
229     }
230 
231     /**
232      * A JSON de-serializer.
233      *
234      * @param json A JSON representation of an {@link FrequencyCapFilters} object as would be
235      *     generated by {@link #toJson()}.
236      * @return An {@link FrequencyCapFilters} object generated from the given JSON.
237      * @hide
238      */
fromJson(JSONObject json)239     public static FrequencyCapFilters fromJson(JSONObject json) throws JSONException {
240         Builder builder = new Builder();
241         if (json.has(WIN_EVENTS_FIELD_NAME)) {
242             builder.setKeyedFrequencyCapsForWinEvents(
243                     jsonArrayToFcapSet(json.getJSONArray(WIN_EVENTS_FIELD_NAME)));
244         }
245         if (json.has(IMPRESSION_EVENTS_FIELD_NAME)) {
246             builder.setKeyedFrequencyCapsForImpressionEvents(
247                     jsonArrayToFcapSet(json.getJSONArray(IMPRESSION_EVENTS_FIELD_NAME)));
248         }
249         if (json.has(VIEW_EVENTS_FIELD_NAME)) {
250             builder.setKeyedFrequencyCapsForViewEvents(
251                     jsonArrayToFcapSet(json.getJSONArray(VIEW_EVENTS_FIELD_NAME)));
252         }
253         if (json.has(CLICK_EVENTS_FIELD_NAME)) {
254             builder.setKeyedFrequencyCapsForClickEvents(
255                     jsonArrayToFcapSet(json.getJSONArray(CLICK_EVENTS_FIELD_NAME)));
256         }
257         return builder.build();
258     }
259 
jsonArrayToFcapSet(JSONArray json)260     private static Set<KeyedFrequencyCap> jsonArrayToFcapSet(JSONArray json) throws JSONException {
261         Set<KeyedFrequencyCap> toReturn = new HashSet<>();
262         for (int i = 0; i < json.length(); i++) {
263             toReturn.add(KeyedFrequencyCap.fromJson(json.getJSONObject(i)));
264         }
265         return toReturn;
266     }
267 
268     @Override
writeToParcel(@onNull Parcel dest, int flags)269     public void writeToParcel(@NonNull Parcel dest, int flags) {
270         Objects.requireNonNull(dest);
271         AdServicesParcelableUtil.writeSetToParcel(dest, mKeyedFrequencyCapsForWinEvents);
272         AdServicesParcelableUtil.writeSetToParcel(dest, mKeyedFrequencyCapsForImpressionEvents);
273         AdServicesParcelableUtil.writeSetToParcel(dest, mKeyedFrequencyCapsForViewEvents);
274         AdServicesParcelableUtil.writeSetToParcel(dest, mKeyedFrequencyCapsForClickEvents);
275     }
276 
277     /** @hide */
278     @Override
describeContents()279     public int describeContents() {
280         return 0;
281     }
282 
283     /** Checks whether the {@link FrequencyCapFilters} objects contain the same information. */
284     @Override
equals(Object o)285     public boolean equals(Object o) {
286         if (this == o) return true;
287         if (!(o instanceof FrequencyCapFilters)) return false;
288         FrequencyCapFilters that = (FrequencyCapFilters) o;
289         return mKeyedFrequencyCapsForWinEvents.equals(that.mKeyedFrequencyCapsForWinEvents)
290                 && mKeyedFrequencyCapsForImpressionEvents.equals(
291                         that.mKeyedFrequencyCapsForImpressionEvents)
292                 && mKeyedFrequencyCapsForViewEvents.equals(that.mKeyedFrequencyCapsForViewEvents)
293                 && mKeyedFrequencyCapsForClickEvents.equals(that.mKeyedFrequencyCapsForClickEvents);
294     }
295 
296     /** Returns the hash of the {@link FrequencyCapFilters} object's data. */
297     @Override
hashCode()298     public int hashCode() {
299         return Objects.hash(
300                 mKeyedFrequencyCapsForWinEvents,
301                 mKeyedFrequencyCapsForImpressionEvents,
302                 mKeyedFrequencyCapsForViewEvents,
303                 mKeyedFrequencyCapsForClickEvents);
304     }
305 
306     @Override
toString()307     public String toString() {
308         return "FrequencyCapFilters{"
309                 + "mKeyedFrequencyCapsForWinEvents="
310                 + mKeyedFrequencyCapsForWinEvents
311                 + ", mKeyedFrequencyCapsForImpressionEvents="
312                 + mKeyedFrequencyCapsForImpressionEvents
313                 + ", mKeyedFrequencyCapsForViewEvents="
314                 + mKeyedFrequencyCapsForViewEvents
315                 + ", mKeyedFrequencyCapsForClickEvents="
316                 + mKeyedFrequencyCapsForClickEvents
317                 + '}';
318     }
319 
320     /** Builder for creating {@link FrequencyCapFilters} objects. */
321     public static final class Builder {
322         @NonNull private Set<KeyedFrequencyCap> mKeyedFrequencyCapsForWinEvents = new HashSet<>();
323 
324         @NonNull
325         private Set<KeyedFrequencyCap> mKeyedFrequencyCapsForImpressionEvents = new HashSet<>();
326 
327         @NonNull private Set<KeyedFrequencyCap> mKeyedFrequencyCapsForViewEvents = new HashSet<>();
328         @NonNull private Set<KeyedFrequencyCap> mKeyedFrequencyCapsForClickEvents = new HashSet<>();
329 
Builder()330         public Builder() {}
331 
332         /**
333          * Sets the set of {@link KeyedFrequencyCap} objects that will filter on the {@link
334          * #AD_EVENT_TYPE_WIN} event type.
335          *
336          * <p>See {@link #getKeyedFrequencyCapsForWinEvents()} for more information.
337          */
338         @NonNull
setKeyedFrequencyCapsForWinEvents( @onNull Set<KeyedFrequencyCap> keyedFrequencyCapsForWinEvents)339         public Builder setKeyedFrequencyCapsForWinEvents(
340                 @NonNull Set<KeyedFrequencyCap> keyedFrequencyCapsForWinEvents) {
341             Objects.requireNonNull(keyedFrequencyCapsForWinEvents);
342             mKeyedFrequencyCapsForWinEvents = keyedFrequencyCapsForWinEvents;
343             return this;
344         }
345 
346         /**
347          * Sets the set of {@link KeyedFrequencyCap} objects that will filter on the {@link
348          * #AD_EVENT_TYPE_IMPRESSION} event type.
349          *
350          * <p>See {@link #getKeyedFrequencyCapsForImpressionEvents()} for more information.
351          */
352         @NonNull
setKeyedFrequencyCapsForImpressionEvents( @onNull Set<KeyedFrequencyCap> keyedFrequencyCapsForImpressionEvents)353         public Builder setKeyedFrequencyCapsForImpressionEvents(
354                 @NonNull Set<KeyedFrequencyCap> keyedFrequencyCapsForImpressionEvents) {
355             Objects.requireNonNull(keyedFrequencyCapsForImpressionEvents);
356             mKeyedFrequencyCapsForImpressionEvents = keyedFrequencyCapsForImpressionEvents;
357             return this;
358         }
359 
360         /**
361          * Sets the set of {@link KeyedFrequencyCap} objects that will filter on the {@link
362          * #AD_EVENT_TYPE_VIEW} event type.
363          *
364          * <p>See {@link #getKeyedFrequencyCapsForViewEvents()} for more information.
365          */
366         @NonNull
setKeyedFrequencyCapsForViewEvents( @onNull Set<KeyedFrequencyCap> keyedFrequencyCapsForViewEvents)367         public Builder setKeyedFrequencyCapsForViewEvents(
368                 @NonNull Set<KeyedFrequencyCap> keyedFrequencyCapsForViewEvents) {
369             Objects.requireNonNull(keyedFrequencyCapsForViewEvents);
370             mKeyedFrequencyCapsForViewEvents = keyedFrequencyCapsForViewEvents;
371             return this;
372         }
373 
374         /**
375          * Sets the set of {@link KeyedFrequencyCap} objects that will filter on the {@link
376          * #AD_EVENT_TYPE_CLICK} event type.
377          *
378          * <p>See {@link #getKeyedFrequencyCapsForClickEvents()} for more information.
379          */
380         @NonNull
setKeyedFrequencyCapsForClickEvents( @onNull Set<KeyedFrequencyCap> keyedFrequencyCapsForClickEvents)381         public Builder setKeyedFrequencyCapsForClickEvents(
382                 @NonNull Set<KeyedFrequencyCap> keyedFrequencyCapsForClickEvents) {
383             Objects.requireNonNull(keyedFrequencyCapsForClickEvents);
384             mKeyedFrequencyCapsForClickEvents = keyedFrequencyCapsForClickEvents;
385             return this;
386         }
387 
388         /** Builds and returns a {@link FrequencyCapFilters} instance. */
389         @NonNull
build()390         public FrequencyCapFilters build() {
391             return new FrequencyCapFilters(this);
392         }
393     }
394 }
395