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 android.adservices.customaudience; 18 19 import static com.android.adservices.flags.Flags.FLAG_FLEDGE_GET_AD_SELECTION_DATA_SELLER_CONFIGURATION_ENABLED; 20 21 import android.adservices.adselection.GetAdSelectionDataRequest; 22 import android.adservices.common.AdData; 23 import android.adservices.common.AdSelectionSignals; 24 import android.adservices.common.AdTechIdentifier; 25 import android.annotation.FlaggedApi; 26 import android.annotation.IntDef; 27 import android.annotation.NonNull; 28 import android.annotation.Nullable; 29 import android.net.Uri; 30 import android.os.OutcomeReceiver; 31 import android.os.Parcel; 32 import android.os.Parcelable; 33 34 import com.android.adservices.AdServicesParcelableUtil; 35 36 import java.lang.annotation.Retention; 37 import java.lang.annotation.RetentionPolicy; 38 import java.time.Instant; 39 import java.util.List; 40 import java.util.Objects; 41 import java.util.concurrent.Executor; 42 43 /** 44 * Represents the information necessary for a custom audience to participate in ad selection. 45 * 46 * <p>A custom audience is an abstract grouping of users with similar demonstrated interests. This 47 * class is a collection of some data stored on a device that is necessary to serve advertisements 48 * targeting a single custom audience. 49 */ 50 public final class CustomAudience implements Parcelable { 51 /** @hide */ 52 public static final int FLAG_AUCTION_SERVER_REQUEST_DEFAULT = 0; 53 54 /** 55 * This auction server request flag indicates to the service that ads for this {@link 56 * CustomAudience} can be omitted in the server auction payload. 57 */ 58 @FlaggedApi( 59 "com.android.adservices.flags.fledge_custom_audience_auction_server_request_flags_enabled") 60 public static final int FLAG_AUCTION_SERVER_REQUEST_OMIT_ADS = 1 << 0; 61 62 @NonNull private final AdTechIdentifier mBuyer; 63 @NonNull private final String mName; 64 @Nullable private final Instant mActivationTime; 65 @Nullable private final Instant mExpirationTime; 66 @NonNull private final Uri mDailyUpdateUri; 67 @Nullable private final AdSelectionSignals mUserBiddingSignals; 68 @Nullable private final TrustedBiddingData mTrustedBiddingData; 69 @NonNull private final Uri mBiddingLogicUri; 70 @NonNull private final List<AdData> mAds; 71 @AuctionServerRequestFlag private final int mAuctionServerRequestFlags; 72 private final double mPriority; 73 74 /** @hide */ 75 @IntDef( 76 flag = true, 77 prefix = {"FLAG_AUCTION_SERVER_REQUEST"}, 78 value = {FLAG_AUCTION_SERVER_REQUEST_DEFAULT, FLAG_AUCTION_SERVER_REQUEST_OMIT_ADS}) 79 @Retention(RetentionPolicy.SOURCE) 80 public @interface AuctionServerRequestFlag {} 81 82 @NonNull 83 public static final Creator<CustomAudience> CREATOR = new Creator<CustomAudience>() { 84 @Override 85 public CustomAudience createFromParcel(@NonNull Parcel in) { 86 Objects.requireNonNull(in); 87 88 return new CustomAudience(in); 89 } 90 91 @Override 92 public CustomAudience[] newArray(int size) { 93 return new CustomAudience[size]; 94 } 95 }; 96 CustomAudience(@onNull CustomAudience.Builder builder)97 private CustomAudience(@NonNull CustomAudience.Builder builder) { 98 Objects.requireNonNull(builder); 99 100 mBuyer = builder.mBuyer; 101 mName = builder.mName; 102 mActivationTime = builder.mActivationTime; 103 mExpirationTime = builder.mExpirationTime; 104 mDailyUpdateUri = builder.mDailyUpdateUri; 105 mUserBiddingSignals = builder.mUserBiddingSignals; 106 mTrustedBiddingData = builder.mTrustedBiddingData; 107 mBiddingLogicUri = builder.mBiddingLogicUri; 108 mAds = builder.mAds; 109 mAuctionServerRequestFlags = builder.mAuctionServerRequestFlags; 110 mPriority = builder.mPriority; 111 } 112 CustomAudience(@onNull Parcel in)113 private CustomAudience(@NonNull Parcel in) { 114 Objects.requireNonNull(in); 115 116 mBuyer = AdTechIdentifier.CREATOR.createFromParcel(in); 117 mName = in.readString(); 118 mActivationTime = 119 AdServicesParcelableUtil.readNullableFromParcel( 120 in, (sourceParcel) -> Instant.ofEpochMilli(sourceParcel.readLong())); 121 mExpirationTime = 122 AdServicesParcelableUtil.readNullableFromParcel( 123 in, (sourceParcel) -> Instant.ofEpochMilli(sourceParcel.readLong())); 124 mDailyUpdateUri = Uri.CREATOR.createFromParcel(in); 125 mUserBiddingSignals = 126 AdServicesParcelableUtil.readNullableFromParcel( 127 in, AdSelectionSignals.CREATOR::createFromParcel); 128 mTrustedBiddingData = 129 AdServicesParcelableUtil.readNullableFromParcel( 130 in, TrustedBiddingData.CREATOR::createFromParcel); 131 mBiddingLogicUri = Uri.CREATOR.createFromParcel(in); 132 mAds = in.createTypedArrayList(AdData.CREATOR); 133 mAuctionServerRequestFlags = in.readInt(); 134 mPriority = in.readDouble(); 135 } 136 137 @Override writeToParcel(@onNull Parcel dest, int flags)138 public void writeToParcel(@NonNull Parcel dest, int flags) { 139 Objects.requireNonNull(dest); 140 141 mBuyer.writeToParcel(dest, flags); 142 dest.writeString(mName); 143 AdServicesParcelableUtil.writeNullableToParcel( 144 dest, 145 mActivationTime, 146 (targetParcel, sourceInstant) -> 147 targetParcel.writeLong(sourceInstant.toEpochMilli())); 148 AdServicesParcelableUtil.writeNullableToParcel( 149 dest, 150 mExpirationTime, 151 (targetParcel, sourceInstant) -> 152 targetParcel.writeLong(sourceInstant.toEpochMilli())); 153 mDailyUpdateUri.writeToParcel(dest, flags); 154 AdServicesParcelableUtil.writeNullableToParcel( 155 dest, 156 mUserBiddingSignals, 157 (targetParcel, sourceSignals) -> sourceSignals.writeToParcel(targetParcel, flags)); 158 AdServicesParcelableUtil.writeNullableToParcel( 159 dest, 160 mTrustedBiddingData, 161 (targetParcel, sourceData) -> sourceData.writeToParcel(targetParcel, flags)); 162 mBiddingLogicUri.writeToParcel(dest, flags); 163 dest.writeTypedList(mAds); 164 dest.writeInt(mAuctionServerRequestFlags); 165 dest.writeDouble(mPriority); 166 } 167 168 @Override toString()169 public String toString() { 170 return "CustomAudience{" 171 + "mBuyer=" 172 + mBuyer 173 + ", mName='" 174 + mName 175 + ", mActivationTime=" 176 + mActivationTime 177 + ", mExpirationTime=" 178 + mExpirationTime 179 + ", mDailyUpdateUri=" 180 + mDailyUpdateUri 181 + ", mUserBiddingSignals=" 182 + mUserBiddingSignals 183 + ", mTrustedBiddingData=" 184 + mTrustedBiddingData 185 + ", mBiddingLogicUri=" 186 + mBiddingLogicUri 187 + ", mAds=" 188 + mAds 189 + ", mAuctionServerRequestFlags=" 190 + mAuctionServerRequestFlags 191 + ", mPriority=" 192 + mPriority 193 + '}'; 194 } 195 196 /** @hide */ 197 @Override describeContents()198 public int describeContents() { 199 return 0; 200 } 201 202 /** 203 * A buyer is identified by a domain in the form "buyerexample.com". 204 * 205 * @return an {@link AdTechIdentifier} containing the custom audience's buyer's domain 206 */ 207 @NonNull getBuyer()208 public AdTechIdentifier getBuyer() { 209 return mBuyer; 210 } 211 212 /** 213 * The custom audience's name is an arbitrary string provided by the owner and buyer on creation 214 * of the {@link CustomAudience} object. 215 * 216 * <p>The overall size of the CA is limited and the size of this field is considered using 217 * {@link String#getBytes()} in {@code UTF-8} encoding. 218 * 219 * @return the String name of the custom audience 220 */ 221 @NonNull getName()222 public String getName() { 223 return mName; 224 } 225 226 /** 227 * On creation of the {@link CustomAudience} object, an optional activation time may be set in 228 * the future, in order to serve a delayed activation. If the field is not set, the {@link 229 * CustomAudience} will be activated at the time of joining. 230 * 231 * <p>For example, a custom audience for lapsed users may not activate until a threshold of 232 * inactivity is reached, at which point the custom audience's ads will participate in the ad 233 * selection process, potentially redirecting lapsed users to the original owner application. 234 * 235 * <p>The maximum delay in activation is 60 days from initial creation. 236 * 237 * <p>If specified, the activation time must be an earlier instant than the expiration time. 238 * 239 * @return the timestamp {@link Instant}, truncated to milliseconds, after which the custom 240 * audience is active 241 */ 242 @Nullable getActivationTime()243 public Instant getActivationTime() { 244 return mActivationTime; 245 } 246 247 /** 248 * Once the expiration time has passed, a custom audience is no longer eligible for daily 249 * ad/bidding data updates or participation in the ad selection process. The custom audience 250 * will then be deleted from memory by the next daily update. 251 * 252 * <p>If no expiration time is provided on creation of the {@link CustomAudience}, expiry will 253 * default to 60 days from activation. 254 * 255 * <p>The maximum expiry is 60 days from initial activation. 256 * 257 * @return the timestamp {@link Instant}, truncated to milliseconds, after which the custom 258 * audience should be removed 259 */ 260 @Nullable getExpirationTime()261 public Instant getExpirationTime() { 262 return mExpirationTime; 263 } 264 265 /** 266 * This URI points to a buyer-operated server that hosts updated bidding data and ads metadata 267 * to be used in the on-device ad selection process. The URI must use HTTPS. 268 * 269 * @return the custom audience's daily update URI 270 */ 271 @NonNull getDailyUpdateUri()272 public Uri getDailyUpdateUri() { 273 return mDailyUpdateUri; 274 } 275 276 /** 277 * User bidding signals are optionally provided by buyers to be consumed by buyer-provided 278 * JavaScript during ad selection in an isolated execution environment. 279 * 280 * <p>If the user bidding signals are not a valid JSON object that can be consumed by the 281 * buyer's JS, the custom audience will not be eligible for ad selection. 282 * 283 * <p>If not specified, the {@link CustomAudience} will not participate in ad selection until 284 * user bidding signals are provided via the daily update for the custom audience. 285 * 286 * @return an {@link AdSelectionSignals} object representing the user bidding signals for the 287 * custom audience 288 */ 289 @Nullable getUserBiddingSignals()290 public AdSelectionSignals getUserBiddingSignals() { 291 return mUserBiddingSignals; 292 } 293 294 /** 295 * Trusted bidding data consists of a URI pointing to a trusted server for buyers' bidding data 296 * and a list of keys to query the server with. Note that the keys are arbitrary identifiers 297 * that will only be used to query the trusted server for a buyer's bidding logic during ad 298 * selection. 299 * 300 * <p>If not specified, the {@link CustomAudience} will not participate in ad selection until 301 * trusted bidding data are provided via the daily update for the custom audience. 302 * 303 * @return a {@link TrustedBiddingData} object containing the custom audience's trusted bidding 304 * data 305 */ 306 @Nullable getTrustedBiddingData()307 public TrustedBiddingData getTrustedBiddingData() { 308 return mTrustedBiddingData; 309 } 310 311 /** 312 * Returns the target URI used to fetch bidding logic when a custom audience participates in the 313 * ad selection process. The URI must use HTTPS. 314 * 315 * @return the URI for fetching buyer bidding logic 316 */ 317 @NonNull getBiddingLogicUri()318 public Uri getBiddingLogicUri() { 319 return mBiddingLogicUri; 320 } 321 322 /** 323 * This list of {@link AdData} objects is a full and complete list of the ads that will be 324 * served by this {@link CustomAudience} during the ad selection process. 325 * 326 * <p>If not specified, or if an empty list is provided, the {@link CustomAudience} will not 327 * participate in ad selection until a valid list of ads are provided via the daily update for 328 * the custom audience. 329 * 330 * <p>The combined ads size of the CA is limited and the sizes of each ad's string fields are 331 * considered using {@link String#getBytes()} in {@code UTF-8} encoding. 332 * 333 * @return a {@link List} of {@link AdData} objects representing ads currently served by the 334 * custom audience 335 */ 336 @NonNull getAds()337 public List<AdData> getAds() { 338 return mAds; 339 } 340 341 /** 342 * Returns the bitfield of auction server request flags. These are flags that influence the 343 * creation of the payload generated by the {@link 344 * android.adservices.adselection.AdSelectionManager#getAdSelectionData(GetAdSelectionDataRequest, 345 * Executor, OutcomeReceiver)} API. 346 * 347 * <p>To create this bitfield, place an {@code |} bitwise operator between each {@link 348 * AuctionServerRequestFlag} to be enabled. 349 */ 350 @FlaggedApi( 351 "com.android.adservices.flags.fledge_custom_audience_auction_server_request_flags_enabled") 352 @AuctionServerRequestFlag getAuctionServerRequestFlags()353 public int getAuctionServerRequestFlags() { 354 return mAuctionServerRequestFlags; 355 } 356 357 /** 358 * Returns the priority of this CustomAudience with respect to other CustomAudiences for this 359 * buyer. 360 * 361 * <p>This means if there is insufficient space during buyer input generation in the {@link 362 * android.adservices.adselection.AdSelectionManager#getAdSelectionData(GetAdSelectionDataRequest, 363 * Executor, OutcomeReceiver)} call, the service will attempt to include the highest priority 364 * custom audiences first. 365 * 366 * <p>The default value if this field is not set is 0. 367 */ 368 @FlaggedApi(FLAG_FLEDGE_GET_AD_SELECTION_DATA_SELLER_CONFIGURATION_ENABLED) getPriority()369 public double getPriority() { 370 return mPriority; 371 } 372 373 /** 374 * Checks whether two {@link CustomAudience} objects contain the same information. 375 */ 376 @Override equals(Object o)377 public boolean equals(Object o) { 378 if (this == o) return true; 379 if (!(o instanceof CustomAudience)) return false; 380 CustomAudience that = (CustomAudience) o; 381 return mBuyer.equals(that.mBuyer) 382 && mName.equals(that.mName) 383 && Objects.equals(mActivationTime, that.mActivationTime) 384 && Objects.equals(mExpirationTime, that.mExpirationTime) 385 && mDailyUpdateUri.equals(that.mDailyUpdateUri) 386 && Objects.equals(mUserBiddingSignals, that.mUserBiddingSignals) 387 && Objects.equals(mTrustedBiddingData, that.mTrustedBiddingData) 388 && mBiddingLogicUri.equals(that.mBiddingLogicUri) 389 && mAds.equals(that.mAds) 390 && mAuctionServerRequestFlags == that.mAuctionServerRequestFlags 391 && mPriority == that.mPriority; 392 } 393 394 /** 395 * Returns the hash of the {@link CustomAudience} object's data. 396 */ 397 @Override hashCode()398 public int hashCode() { 399 return Objects.hash( 400 mBuyer, 401 mName, 402 mActivationTime, 403 mExpirationTime, 404 mDailyUpdateUri, 405 mUserBiddingSignals, 406 mTrustedBiddingData, 407 mBiddingLogicUri, 408 mAds, 409 mAuctionServerRequestFlags, 410 mPriority); 411 } 412 413 /** Builder for {@link CustomAudience} objects. */ 414 public static final class Builder { 415 @Nullable private AdTechIdentifier mBuyer; 416 @Nullable private String mName; 417 @Nullable private Instant mActivationTime; 418 @Nullable private Instant mExpirationTime; 419 @Nullable private Uri mDailyUpdateUri; 420 @Nullable private AdSelectionSignals mUserBiddingSignals; 421 @Nullable private TrustedBiddingData mTrustedBiddingData; 422 @Nullable private Uri mBiddingLogicUri; 423 @Nullable private List<AdData> mAds; 424 @AuctionServerRequestFlag private int mAuctionServerRequestFlags; 425 private double mPriority; 426 427 // TODO(b/232883403): We may need to add @NonNUll members as args. Builder()428 public Builder() { 429 } 430 431 /** 432 * Sets the buyer {@link AdTechIdentifier}. 433 * 434 * <p>See {@link #getBuyer()} for more information. 435 */ 436 @NonNull setBuyer(@onNull AdTechIdentifier buyer)437 public CustomAudience.Builder setBuyer(@NonNull AdTechIdentifier buyer) { 438 Objects.requireNonNull(buyer); 439 mBuyer = buyer; 440 return this; 441 } 442 443 /** 444 * Sets the {@link CustomAudience} object's name. 445 * <p> 446 * See {@link #getName()} for more information. 447 */ 448 @NonNull setName(@onNull String name)449 public CustomAudience.Builder setName(@NonNull String name) { 450 Objects.requireNonNull(name); 451 mName = name; 452 return this; 453 } 454 455 /** 456 * Sets the time, truncated to milliseconds, after which the {@link CustomAudience} will 457 * serve ads. 458 * 459 * <p>Set to {@code null} in order for this {@link CustomAudience} to be immediately active 460 * and participate in ad selection. 461 * 462 * <p>See {@link #getActivationTime()} for more information. 463 */ 464 @NonNull setActivationTime(@ullable Instant activationTime)465 public CustomAudience.Builder setActivationTime(@Nullable Instant activationTime) { 466 mActivationTime = activationTime; 467 return this; 468 } 469 470 /** 471 * Sets the time, truncated to milliseconds, after which the {@link CustomAudience} should 472 * be removed. 473 * <p> 474 * See {@link #getExpirationTime()} for more information. 475 */ 476 @NonNull setExpirationTime(@ullable Instant expirationTime)477 public CustomAudience.Builder setExpirationTime(@Nullable Instant expirationTime) { 478 mExpirationTime = expirationTime; 479 return this; 480 } 481 482 /** 483 * Sets the daily update URI. The URI must use HTTPS. 484 * 485 * <p>See {@link #getDailyUpdateUri()} for more information. 486 */ 487 @NonNull setDailyUpdateUri(@onNull Uri dailyUpdateUri)488 public CustomAudience.Builder setDailyUpdateUri(@NonNull Uri dailyUpdateUri) { 489 Objects.requireNonNull(dailyUpdateUri); 490 mDailyUpdateUri = dailyUpdateUri; 491 return this; 492 } 493 494 /** 495 * Sets the user bidding signals used in the ad selection process. 496 * 497 * <p>See {@link #getUserBiddingSignals()} for more information. 498 */ 499 @NonNull setUserBiddingSignals( @ullable AdSelectionSignals userBiddingSignals)500 public CustomAudience.Builder setUserBiddingSignals( 501 @Nullable AdSelectionSignals userBiddingSignals) { 502 mUserBiddingSignals = userBiddingSignals; 503 return this; 504 } 505 506 /** 507 * Sets the trusted bidding data to be queried and used in the ad selection process. 508 * <p> 509 * See {@link #getTrustedBiddingData()} for more information. 510 */ 511 @NonNull setTrustedBiddingData( @ullable TrustedBiddingData trustedBiddingData)512 public CustomAudience.Builder setTrustedBiddingData( 513 @Nullable TrustedBiddingData trustedBiddingData) { 514 mTrustedBiddingData = trustedBiddingData; 515 return this; 516 } 517 518 /** 519 * Sets the URI to fetch bidding logic from for use in the ad selection process. The URI 520 * must use HTTPS. 521 * 522 * <p>See {@link #getBiddingLogicUri()} for more information. 523 */ 524 @NonNull setBiddingLogicUri(@onNull Uri biddingLogicUri)525 public CustomAudience.Builder setBiddingLogicUri(@NonNull Uri biddingLogicUri) { 526 Objects.requireNonNull(biddingLogicUri); 527 mBiddingLogicUri = biddingLogicUri; 528 return this; 529 } 530 531 /** 532 * Sets the initial remarketing ads served by the custom audience. Will be assigned with an 533 * empty list if not provided. 534 * 535 * <p>See {@link #getAds()} for more information. 536 */ 537 @NonNull setAds(@ullable List<AdData> ads)538 public CustomAudience.Builder setAds(@Nullable List<AdData> ads) { 539 mAds = ads; 540 return this; 541 } 542 543 /** 544 * Sets the bitfield of auction server request flags. 545 * 546 * <p>See {@link #getAuctionServerRequestFlags()} for more information. 547 */ 548 @FlaggedApi( 549 "com.android.adservices.flags.fledge_custom_audience_auction_server_request_flags_enabled") 550 @NonNull setAuctionServerRequestFlags( @uctionServerRequestFlag int auctionServerRequestFlags)551 public CustomAudience.Builder setAuctionServerRequestFlags( 552 @AuctionServerRequestFlag int auctionServerRequestFlags) { 553 mAuctionServerRequestFlags = auctionServerRequestFlags; 554 return this; 555 } 556 557 /** 558 * Sets the priority for this custom audience. 559 * 560 * <p>See {@link #getPriority()} for further details. 561 */ 562 @FlaggedApi(FLAG_FLEDGE_GET_AD_SELECTION_DATA_SELLER_CONFIGURATION_ENABLED) 563 @NonNull setPriority(double priority)564 public CustomAudience.Builder setPriority(double priority) { 565 mPriority = priority; 566 return this; 567 } 568 569 /** 570 * Builds an instance of a {@link CustomAudience}. 571 * 572 * @throws NullPointerException if any non-null parameter is null 573 * @throws IllegalArgumentException if the expiration time occurs before activation time 574 * @throws IllegalArgumentException if the expiration time is set before the current time 575 */ 576 @NonNull build()577 public CustomAudience build() { 578 Objects.requireNonNull(mBuyer, "The buyer has not been provided"); 579 Objects.requireNonNull(mName, "The name has not been provided"); 580 Objects.requireNonNull(mDailyUpdateUri, "The daily update URI has not been provided"); 581 Objects.requireNonNull(mBiddingLogicUri, "The bidding logic URI has not been provided"); 582 583 // To pass the API lint, we should not allow null Collection. 584 if (mAds == null) { 585 mAds = List.of(); 586 } 587 588 return new CustomAudience(this); 589 } 590 } 591 } 592