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