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