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 package android.adservices.adselection;
17 
18 import android.adservices.common.AdSelectionSignals;
19 import android.adservices.common.AdTechIdentifier;
20 import android.annotation.FlaggedApi;
21 import android.annotation.NonNull;
22 import android.net.Uri;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 
26 import com.android.adservices.AdServicesParcelableUtil;
27 import com.android.adservices.flags.Flags;
28 
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Objects;
35 
36 /**
37  * Contains the configuration of the ad selection process.
38  *
39  * <p>Instances of this class are created by SDKs to be provided as arguments to the {@link
40  * AdSelectionManager#selectAds} and {@link AdSelectionManager#reportImpression} methods in {@link
41  * AdSelectionManager}.
42  */
43 // TODO(b/233280314): investigate on adSelectionConfig optimization by merging mCustomAudienceBuyers
44 //  and mPerBuyerSignals.
45 public final class AdSelectionConfig implements Parcelable {
46     /**
47      * {@link AdSelectionConfig} with empty values for each field.
48      *
49      * @hide
50      */
51     @NonNull public static final AdSelectionConfig EMPTY = new AdSelectionConfig();
52 
53     @NonNull private final AdTechIdentifier mSeller;
54     @NonNull private final Uri mDecisionLogicUri;
55     @NonNull private final List<AdTechIdentifier> mCustomAudienceBuyers;
56     @NonNull private final AdSelectionSignals mAdSelectionSignals;
57     @NonNull private final AdSelectionSignals mSellerSignals;
58     @NonNull private final Map<AdTechIdentifier, AdSelectionSignals> mPerBuyerSignals;
59     @NonNull private final Map<AdTechIdentifier, SignedContextualAds> mBuyerSignedContextualAds;
60     @NonNull private final Uri mTrustedScoringSignalsUri;
61 
62     @NonNull
63     public static final Creator<AdSelectionConfig> CREATOR =
64             new Creator<AdSelectionConfig>() {
65                 @Override
66                 public AdSelectionConfig createFromParcel(@NonNull Parcel in) {
67                     Objects.requireNonNull(in);
68                     return new AdSelectionConfig(in);
69                 }
70 
71                 @Override
72                 public AdSelectionConfig[] newArray(int size) {
73                     return new AdSelectionConfig[size];
74                 }
75             };
76 
AdSelectionConfig()77     private AdSelectionConfig() {
78         this.mSeller = AdTechIdentifier.fromString("");
79         this.mDecisionLogicUri = Uri.EMPTY;
80         this.mCustomAudienceBuyers = Collections.emptyList();
81         this.mAdSelectionSignals = AdSelectionSignals.EMPTY;
82         this.mSellerSignals = AdSelectionSignals.EMPTY;
83         this.mPerBuyerSignals = Collections.emptyMap();
84         this.mBuyerSignedContextualAds = Collections.emptyMap();
85         this.mTrustedScoringSignalsUri = Uri.EMPTY;
86     }
87 
AdSelectionConfig( @onNull AdTechIdentifier seller, @NonNull Uri decisionLogicUri, @NonNull List<AdTechIdentifier> customAudienceBuyers, @NonNull AdSelectionSignals adSelectionSignals, @NonNull AdSelectionSignals sellerSignals, @NonNull Map<AdTechIdentifier, AdSelectionSignals> perBuyerSignals, @NonNull Map<AdTechIdentifier, SignedContextualAds> perBuyerSignedContextualAds, @NonNull Uri trustedScoringSignalsUri)88     private AdSelectionConfig(
89             @NonNull AdTechIdentifier seller,
90             @NonNull Uri decisionLogicUri,
91             @NonNull List<AdTechIdentifier> customAudienceBuyers,
92             @NonNull AdSelectionSignals adSelectionSignals,
93             @NonNull AdSelectionSignals sellerSignals,
94             @NonNull Map<AdTechIdentifier, AdSelectionSignals> perBuyerSignals,
95             @NonNull Map<AdTechIdentifier, SignedContextualAds> perBuyerSignedContextualAds,
96             @NonNull Uri trustedScoringSignalsUri) {
97         this.mSeller = seller;
98         this.mDecisionLogicUri = decisionLogicUri;
99         this.mCustomAudienceBuyers = customAudienceBuyers;
100         this.mAdSelectionSignals = adSelectionSignals;
101         this.mSellerSignals = sellerSignals;
102         this.mPerBuyerSignals = perBuyerSignals;
103         this.mBuyerSignedContextualAds = perBuyerSignedContextualAds;
104         this.mTrustedScoringSignalsUri = trustedScoringSignalsUri;
105     }
106 
AdSelectionConfig(@onNull Parcel in)107     private AdSelectionConfig(@NonNull Parcel in) {
108         Objects.requireNonNull(in);
109         mSeller = AdTechIdentifier.CREATOR.createFromParcel(in);
110         mDecisionLogicUri = Uri.CREATOR.createFromParcel(in);
111         mCustomAudienceBuyers = in.createTypedArrayList(AdTechIdentifier.CREATOR);
112         mAdSelectionSignals = AdSelectionSignals.CREATOR.createFromParcel(in);
113         mSellerSignals = AdSelectionSignals.CREATOR.createFromParcel(in);
114         mPerBuyerSignals =
115                 AdServicesParcelableUtil.readMapFromParcel(
116                         in, AdTechIdentifier::fromString, AdSelectionSignals.class);
117         mBuyerSignedContextualAds =
118                 AdServicesParcelableUtil.readMapFromParcel(
119                         in, AdTechIdentifier::fromString, SignedContextualAds.class);
120         mTrustedScoringSignalsUri = Uri.CREATOR.createFromParcel(in);
121     }
122 
123     @Override
describeContents()124     public int describeContents() {
125         return 0;
126     }
127 
128     @Override
writeToParcel(@onNull Parcel dest, int flags)129     public void writeToParcel(@NonNull Parcel dest, int flags) {
130         Objects.requireNonNull(dest);
131 
132         mSeller.writeToParcel(dest, flags);
133         mDecisionLogicUri.writeToParcel(dest, flags);
134         dest.writeTypedList(mCustomAudienceBuyers);
135         mAdSelectionSignals.writeToParcel(dest, flags);
136         mSellerSignals.writeToParcel(dest, flags);
137         AdServicesParcelableUtil.writeMapToParcel(dest, mPerBuyerSignals);
138         AdServicesParcelableUtil.writeMapToParcel(dest, mBuyerSignedContextualAds);
139         mTrustedScoringSignalsUri.writeToParcel(dest, flags);
140     }
141 
142     @Override
equals(Object o)143     public boolean equals(Object o) {
144         if (this == o) return true;
145         if (!(o instanceof AdSelectionConfig)) return false;
146         AdSelectionConfig that = (AdSelectionConfig) o;
147         return Objects.equals(mSeller, that.mSeller)
148                 && Objects.equals(mDecisionLogicUri, that.mDecisionLogicUri)
149                 && Objects.equals(mCustomAudienceBuyers, that.mCustomAudienceBuyers)
150                 && Objects.equals(mAdSelectionSignals, that.mAdSelectionSignals)
151                 && Objects.equals(mSellerSignals, that.mSellerSignals)
152                 && Objects.equals(mPerBuyerSignals, that.mPerBuyerSignals)
153                 && Objects.equals(mBuyerSignedContextualAds, that.mBuyerSignedContextualAds)
154                 && Objects.equals(mTrustedScoringSignalsUri, that.mTrustedScoringSignalsUri);
155     }
156 
157     @Override
hashCode()158     public int hashCode() {
159         return Objects.hash(
160                 mSeller,
161                 mDecisionLogicUri,
162                 mCustomAudienceBuyers,
163                 mAdSelectionSignals,
164                 mSellerSignals,
165                 mPerBuyerSignals,
166                 mBuyerSignedContextualAds,
167                 mTrustedScoringSignalsUri);
168     }
169 
170     /**
171      * @return a new builder instance created from this object's cloned data
172      * @hide
173      */
174     @NonNull
cloneToBuilder()175     public AdSelectionConfig.Builder cloneToBuilder() {
176         return new AdSelectionConfig.Builder()
177                 .setSeller(this.getSeller())
178                 .setPerBuyerSignedContextualAds(this.getPerBuyerSignedContextualAds())
179                 .setAdSelectionSignals(this.getAdSelectionSignals())
180                 .setCustomAudienceBuyers(this.getCustomAudienceBuyers())
181                 .setDecisionLogicUri(this.getDecisionLogicUri())
182                 .setPerBuyerSignals(this.getPerBuyerSignals())
183                 .setSellerSignals(this.getSellerSignals())
184                 .setTrustedScoringSignalsUri(this.getTrustedScoringSignalsUri());
185     }
186 
187     /** @return a AdTechIdentifier of the seller, for example "www.example-ssp.com" */
188     @NonNull
getSeller()189     public AdTechIdentifier getSeller() {
190         return mSeller;
191     }
192 
193     /**
194      * @return the URI used to retrieve the JS code containing the seller/SSP scoreAd function used
195      *     during the ad selection and reporting processes
196      */
197     @NonNull
getDecisionLogicUri()198     public Uri getDecisionLogicUri() {
199         return mDecisionLogicUri;
200     }
201 
202     /**
203      * @return a list of custom audience buyers allowed by the SSP to participate in the ad
204      *     selection process
205      */
206     @NonNull
getCustomAudienceBuyers()207     public List<AdTechIdentifier> getCustomAudienceBuyers() {
208         return new ArrayList<>(mCustomAudienceBuyers);
209     }
210 
211 
212     /**
213      * @return JSON in an AdSelectionSignals object, fetched from the AdSelectionConfig and consumed
214      *     by the JS logic fetched from the DSP, represents signals given to the participating
215      *     buyers in the ad selection and reporting processes.
216      */
217     @NonNull
getAdSelectionSignals()218     public AdSelectionSignals getAdSelectionSignals() {
219         return mAdSelectionSignals;
220     }
221 
222     /**
223      * @return JSON in an AdSelectionSignals object, provided by the SSP and consumed by the JS
224      *     logic fetched from the SSP, represents any information that the SSP used in the ad
225      *     scoring process to tweak the results of the ad selection process (e.g. brand safety
226      *     checks, excluded contextual ads).
227      */
228     @NonNull
getSellerSignals()229     public AdSelectionSignals getSellerSignals() {
230         return mSellerSignals;
231     }
232 
233     /**
234      * @return a Map of buyers and AdSelectionSignals, fetched from the AdSelectionConfig and
235      *     consumed by the JS logic fetched from the DSP, representing any information that each
236      *     buyer would provide during ad selection to participants (such as bid floor, ad selection
237      *     type, etc.)
238      */
239     @NonNull
getPerBuyerSignals()240     public Map<AdTechIdentifier, AdSelectionSignals> getPerBuyerSignals() {
241         return new HashMap<>(mPerBuyerSignals);
242     }
243 
244 
245     /**
246      * @return a Map of buyers and corresponding Contextual Ads, these ads are expected to be
247      *     pre-downloaded from the contextual path and injected into Ad Selection.
248      */
249     @FlaggedApi(Flags.FLAG_FLEDGE_AD_SELECTION_FILTERING_ENABLED)
250     @NonNull
getPerBuyerSignedContextualAds()251     public Map<AdTechIdentifier, SignedContextualAds> getPerBuyerSignedContextualAds() {
252         return new HashMap<>(mBuyerSignedContextualAds);
253     }
254 
255     /**
256      * @return URI endpoint of sell-side trusted signal from which creative specific realtime
257      *     information can be fetched from.
258      */
259     @NonNull
getTrustedScoringSignalsUri()260     public Uri getTrustedScoringSignalsUri() {
261         return mTrustedScoringSignalsUri;
262     }
263 
264     /** Builder for {@link AdSelectionConfig} object. */
265     public static final class Builder {
266         private AdTechIdentifier mSeller;
267         private Uri mDecisionLogicUri;
268         private List<AdTechIdentifier> mCustomAudienceBuyers;
269         private AdSelectionSignals mAdSelectionSignals = AdSelectionSignals.EMPTY;
270         private AdSelectionSignals mSellerSignals = AdSelectionSignals.EMPTY;
271         private Map<AdTechIdentifier, AdSelectionSignals> mPerBuyerSignals = Collections.emptyMap();
272         private Map<AdTechIdentifier, SignedContextualAds> mBuyerSignedContextualAds =
273                 Collections.emptyMap();
274         private Uri mTrustedScoringSignalsUri;
275 
Builder()276         public Builder() {}
277 
278         /**
279          * Sets the seller identifier.
280          *
281          * <p>See {@link #getSeller()} for more details.
282          */
283         @NonNull
setSeller(@onNull AdTechIdentifier seller)284         public AdSelectionConfig.Builder setSeller(@NonNull AdTechIdentifier seller) {
285             Objects.requireNonNull(seller);
286 
287             this.mSeller = seller;
288             return this;
289         }
290 
291         /**
292          * Sets the URI used to fetch decision logic for use in the ad selection process. Decision
293          * URI could be either of the two schemas:
294          *
295          * <ul>
296          *   <li><b>HTTPS:</b> HTTPS URIs have to be absolute URIs where the host matches the {@code
297          *       seller}
298          *   <li><b>Ad Selection Prebuilt:</b> Ad Selection Service URIs follow {@code
299          *       ad-selection-prebuilt://ad-selection/<name>?<script-generation-parameters>} format.
300          *       FLEDGE generates the appropriate JS script without the need for a network call.
301          *       <p>Available prebuilt scripts:
302          *       <ul>
303          *         <li><b>{@code highest-bid-wins} for {@code scoreAds} and {@code
304          *             reportResult}:</b> This JS picks the ad with the highest bid for scoring. For
305          *             reporting, the given URI is parameterized with {@code render_uri} and {@code
306          *             bid}. Below parameter(s) are required to use this prebuilt:
307          *             <ul>
308          *               <li><b>{@code reportingUrl}:</b> Base reporting uri that will be
309          *                   parameterized later with {@code render_uri} and {@code bid}
310          *             </ul>
311          *             <p>Ex. If your base reporting URL is "https://www.ssp.com" then, {@code
312          *             ad-selection-prebuilt://ad-selection/highest-bid-wins/?reportingUrl=https://www.ssp.com}
313          *       </ul>
314          * </ul>
315          *
316          * <p>See {@link #getDecisionLogicUri()} for more details.
317          */
318         @NonNull
setDecisionLogicUri(@onNull Uri decisionLogicUri)319         public AdSelectionConfig.Builder setDecisionLogicUri(@NonNull Uri decisionLogicUri) {
320             Objects.requireNonNull(decisionLogicUri);
321 
322             this.mDecisionLogicUri = decisionLogicUri;
323             return this;
324         }
325 
326         /**
327          * Sets the list of allowed buyers.
328          *
329          * <p>See {@link #getCustomAudienceBuyers()} for more details.
330          */
331         @NonNull
setCustomAudienceBuyers( @onNull List<AdTechIdentifier> customAudienceBuyers)332         public AdSelectionConfig.Builder setCustomAudienceBuyers(
333                 @NonNull List<AdTechIdentifier> customAudienceBuyers) {
334             Objects.requireNonNull(customAudienceBuyers);
335 
336             this.mCustomAudienceBuyers = customAudienceBuyers;
337             return this;
338         }
339 
340         /**
341          * Sets the signals provided to buyers during ad selection bid generation.
342          *
343          * <p>If not set, defaults to the empty JSON.
344          *
345          * <p>See {@link #getAdSelectionSignals()} for more details.
346          */
347         @NonNull
setAdSelectionSignals( @onNull AdSelectionSignals adSelectionSignals)348         public AdSelectionConfig.Builder setAdSelectionSignals(
349                 @NonNull AdSelectionSignals adSelectionSignals) {
350             Objects.requireNonNull(adSelectionSignals);
351 
352             this.mAdSelectionSignals = adSelectionSignals;
353             return this;
354         }
355 
356         /**
357          * Set the signals used to modify ad selection results.
358          *
359          * <p>If not set, defaults to the empty JSON.
360          *
361          * <p>See {@link #getSellerSignals()} for more details.
362          */
363         @NonNull
setSellerSignals( @onNull AdSelectionSignals sellerSignals)364         public AdSelectionConfig.Builder setSellerSignals(
365                 @NonNull AdSelectionSignals sellerSignals) {
366             Objects.requireNonNull(sellerSignals);
367 
368             this.mSellerSignals = sellerSignals;
369             return this;
370         }
371 
372         /**
373          * Sets the signals provided by each buyer during ad selection.
374          *
375          * <p>If not set, defaults to an empty map.
376          *
377          * <p>See {@link #getPerBuyerSignals()} for more details.
378          */
379         @NonNull
setPerBuyerSignals( @onNull Map<AdTechIdentifier, AdSelectionSignals> perBuyerSignals)380         public AdSelectionConfig.Builder setPerBuyerSignals(
381                 @NonNull Map<AdTechIdentifier, AdSelectionSignals> perBuyerSignals) {
382             Objects.requireNonNull(perBuyerSignals);
383 
384             this.mPerBuyerSignals = perBuyerSignals;
385             return this;
386         }
387 
388         /**
389          * Sets the contextual Ads corresponding to each buyer during ad selection.
390          *
391          * <p>If not set, defaults to an empty map.
392          *
393          * <p>See {@link #getPerBuyerSignedContextualAds()} for more details.
394          */
395         @FlaggedApi(Flags.FLAG_FLEDGE_AD_SELECTION_FILTERING_ENABLED)
396         @NonNull
setPerBuyerSignedContextualAds( @onNull Map<AdTechIdentifier, SignedContextualAds> buyerSignedContextualAds)397         public AdSelectionConfig.Builder setPerBuyerSignedContextualAds(
398                 @NonNull Map<AdTechIdentifier, SignedContextualAds> buyerSignedContextualAds) {
399             Objects.requireNonNull(buyerSignedContextualAds);
400 
401             this.mBuyerSignedContextualAds = buyerSignedContextualAds;
402             return this;
403         }
404 
405         /**
406          * Sets the URI endpoint of sell-side trusted signal from which creative specific realtime
407          * information can be fetched from.
408          *
409          * <p>If {@link Uri#EMPTY} is passed then network call will be skipped and {@link
410          * AdSelectionSignals#EMPTY} will be passed to ad selection.
411          *
412          * <p>See {@link #getTrustedScoringSignalsUri()} for more details.
413          */
414         @NonNull
setTrustedScoringSignalsUri( @onNull Uri trustedScoringSignalsUri)415         public AdSelectionConfig.Builder setTrustedScoringSignalsUri(
416                 @NonNull Uri trustedScoringSignalsUri) {
417             Objects.requireNonNull(trustedScoringSignalsUri);
418 
419             this.mTrustedScoringSignalsUri = trustedScoringSignalsUri;
420             return this;
421         }
422 
423         /**
424          * Builds an {@link AdSelectionConfig} instance.
425          *
426          * @throws NullPointerException if any required params are null
427          */
428         @NonNull
build()429         public AdSelectionConfig build() {
430             Objects.requireNonNull(mSeller, "The seller has not been provided");
431             Objects.requireNonNull(
432                 mDecisionLogicUri, "The decision logic URI has not been provided");
433             Objects.requireNonNull(
434                 mCustomAudienceBuyers, "The custom audience buyers have not been provided");
435             Objects.requireNonNull(
436                 mAdSelectionSignals, "The ad selection signals have not been provided");
437             Objects.requireNonNull(mSellerSignals, "The seller signals have not been provided");
438             Objects.requireNonNull(
439                 mPerBuyerSignals, "The per buyer signals have not been provided");
440             Objects.requireNonNull(
441                 mBuyerSignedContextualAds,
442                 "The buyer signed contextual ads have not been provided");
443             Objects.requireNonNull(
444                 mTrustedScoringSignalsUri,
445                 "The trusted scoring signals URI have not been provided");
446             return new AdSelectionConfig(
447                     mSeller,
448                     mDecisionLogicUri,
449                     mCustomAudienceBuyers,
450                     mAdSelectionSignals,
451                     mSellerSignals,
452                     mPerBuyerSignals,
453                     mBuyerSignedContextualAds,
454                     mTrustedScoringSignalsUri);
455         }
456     }
457 }
458