/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.adservices.customaudience; import android.adservices.adselection.GetAdSelectionDataRequest; import android.adservices.common.AdData; import android.adservices.common.AdSelectionSignals; import android.adservices.common.AdTechIdentifier; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.net.Uri; import android.os.OutcomeReceiver; import android.os.Parcel; import android.os.Parcelable; import com.android.adservices.AdServicesParcelableUtil; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.time.Instant; import java.util.List; import java.util.Objects; import java.util.concurrent.Executor; /** * Represents the information necessary for a custom audience to participate in ad selection. * *
A custom audience is an abstract grouping of users with similar demonstrated interests. This
* class is a collection of some data stored on a device that is necessary to serve advertisements
* targeting a single custom audience.
*/
public final class CustomAudience implements Parcelable {
/** @hide */
public static final int FLAG_AUCTION_SERVER_REQUEST_DEFAULT = 0;
/**
* This auction server request flag indicates to the service that ads for this {@link
* CustomAudience} can be omitted in the server auction payload.
*/
@FlaggedApi(
"com.android.adservices.flags.fledge_custom_audience_auction_server_request_flags_enabled")
public static final int FLAG_AUCTION_SERVER_REQUEST_OMIT_ADS = 1 << 0;
@NonNull private final AdTechIdentifier mBuyer;
@NonNull private final String mName;
@Nullable private final Instant mActivationTime;
@Nullable private final Instant mExpirationTime;
@NonNull private final Uri mDailyUpdateUri;
@Nullable private final AdSelectionSignals mUserBiddingSignals;
@Nullable private final TrustedBiddingData mTrustedBiddingData;
@NonNull private final Uri mBiddingLogicUri;
@NonNull private final List The overall size of the CA is limited and the size of this field is considered using
* {@link String#getBytes()} in {@code UTF-8} encoding.
*
* @return the String name of the custom audience
*/
@NonNull
public String getName() {
return mName;
}
/**
* On creation of the {@link CustomAudience} object, an optional activation time may be set in
* the future, in order to serve a delayed activation. If the field is not set, the {@link
* CustomAudience} will be activated at the time of joining.
*
* For example, a custom audience for lapsed users may not activate until a threshold of
* inactivity is reached, at which point the custom audience's ads will participate in the ad
* selection process, potentially redirecting lapsed users to the original owner application.
*
* The maximum delay in activation is 60 days from initial creation.
*
* If specified, the activation time must be an earlier instant than the expiration time.
*
* @return the timestamp {@link Instant}, truncated to milliseconds, after which the custom
* audience is active
*/
@Nullable
public Instant getActivationTime() {
return mActivationTime;
}
/**
* Once the expiration time has passed, a custom audience is no longer eligible for daily
* ad/bidding data updates or participation in the ad selection process. The custom audience
* will then be deleted from memory by the next daily update.
*
* If no expiration time is provided on creation of the {@link CustomAudience}, expiry will
* default to 60 days from activation.
*
* The maximum expiry is 60 days from initial activation.
*
* @return the timestamp {@link Instant}, truncated to milliseconds, after which the custom
* audience should be removed
*/
@Nullable
public Instant getExpirationTime() {
return mExpirationTime;
}
/**
* This URI points to a buyer-operated server that hosts updated bidding data and ads metadata
* to be used in the on-device ad selection process. The URI must use HTTPS.
*
* @return the custom audience's daily update URI
*/
@NonNull
public Uri getDailyUpdateUri() {
return mDailyUpdateUri;
}
/**
* User bidding signals are optionally provided by buyers to be consumed by buyer-provided
* JavaScript during ad selection in an isolated execution environment.
*
* If the user bidding signals are not a valid JSON object that can be consumed by the
* buyer's JS, the custom audience will not be eligible for ad selection.
*
* If not specified, the {@link CustomAudience} will not participate in ad selection until
* user bidding signals are provided via the daily update for the custom audience.
*
* @return an {@link AdSelectionSignals} object representing the user bidding signals for the
* custom audience
*/
@Nullable
public AdSelectionSignals getUserBiddingSignals() {
return mUserBiddingSignals;
}
/**
* Trusted bidding data consists of a URI pointing to a trusted server for buyers' bidding data
* and a list of keys to query the server with. Note that the keys are arbitrary identifiers
* that will only be used to query the trusted server for a buyer's bidding logic during ad
* selection.
*
* If not specified, the {@link CustomAudience} will not participate in ad selection until
* trusted bidding data are provided via the daily update for the custom audience.
*
* @return a {@link TrustedBiddingData} object containing the custom audience's trusted bidding
* data
*/
@Nullable
public TrustedBiddingData getTrustedBiddingData() {
return mTrustedBiddingData;
}
/**
* Returns the target URI used to fetch bidding logic when a custom audience participates in the
* ad selection process. The URI must use HTTPS.
*
* @return the URI for fetching buyer bidding logic
*/
@NonNull
public Uri getBiddingLogicUri() {
return mBiddingLogicUri;
}
/**
* This list of {@link AdData} objects is a full and complete list of the ads that will be
* served by this {@link CustomAudience} during the ad selection process.
*
* If not specified, or if an empty list is provided, the {@link CustomAudience} will not
* participate in ad selection until a valid list of ads are provided via the daily update for
* the custom audience.
*
* The combined ads size of the CA is limited and the sizes of each ad's string fields are
* considered using {@link String#getBytes()} in {@code UTF-8} encoding.
*
* @return a {@link List} of {@link AdData} objects representing ads currently served by the
* custom audience
*/
@NonNull
public List To create this bitfield, place an {@code |} bitwise operator between each {@link
* AuctionServerRequestFlag} to be enabled.
*/
@FlaggedApi(
"com.android.adservices.flags.fledge_custom_audience_auction_server_request_flags_enabled")
@AuctionServerRequestFlag
public int getAuctionServerRequestFlags() {
return mAuctionServerRequestFlags;
}
/**
* Checks whether two {@link CustomAudience} objects contain the same information.
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof CustomAudience)) return false;
CustomAudience that = (CustomAudience) o;
return mBuyer.equals(that.mBuyer)
&& mName.equals(that.mName)
&& Objects.equals(mActivationTime, that.mActivationTime)
&& Objects.equals(mExpirationTime, that.mExpirationTime)
&& mDailyUpdateUri.equals(that.mDailyUpdateUri)
&& Objects.equals(mUserBiddingSignals, that.mUserBiddingSignals)
&& Objects.equals(mTrustedBiddingData, that.mTrustedBiddingData)
&& mBiddingLogicUri.equals(that.mBiddingLogicUri)
&& mAds.equals(that.mAds)
&& mAuctionServerRequestFlags == that.mAuctionServerRequestFlags;
}
/**
* Returns the hash of the {@link CustomAudience} object's data.
*/
@Override
public int hashCode() {
return Objects.hash(
mBuyer,
mName,
mActivationTime,
mExpirationTime,
mDailyUpdateUri,
mUserBiddingSignals,
mTrustedBiddingData,
mBiddingLogicUri,
mAds,
mAuctionServerRequestFlags);
}
/** Builder for {@link CustomAudience} objects. */
public static final class Builder {
@Nullable private AdTechIdentifier mBuyer;
@Nullable private String mName;
@Nullable private Instant mActivationTime;
@Nullable private Instant mExpirationTime;
@Nullable private Uri mDailyUpdateUri;
@Nullable private AdSelectionSignals mUserBiddingSignals;
@Nullable private TrustedBiddingData mTrustedBiddingData;
@Nullable private Uri mBiddingLogicUri;
@Nullable private List See {@link #getBuyer()} for more information.
*/
@NonNull
public CustomAudience.Builder setBuyer(@NonNull AdTechIdentifier buyer) {
Objects.requireNonNull(buyer);
mBuyer = buyer;
return this;
}
/**
* Sets the {@link CustomAudience} object's name.
*
* See {@link #getName()} for more information.
*/
@NonNull
public CustomAudience.Builder setName(@NonNull String name) {
Objects.requireNonNull(name);
mName = name;
return this;
}
/**
* Sets the time, truncated to milliseconds, after which the {@link CustomAudience} will
* serve ads.
*
* Set to {@code null} in order for this {@link CustomAudience} to be immediately active
* and participate in ad selection.
*
* See {@link #getActivationTime()} for more information.
*/
@NonNull
public CustomAudience.Builder setActivationTime(@Nullable Instant activationTime) {
mActivationTime = activationTime;
return this;
}
/**
* Sets the time, truncated to milliseconds, after which the {@link CustomAudience} should
* be removed.
*
* See {@link #getExpirationTime()} for more information.
*/
@NonNull
public CustomAudience.Builder setExpirationTime(@Nullable Instant expirationTime) {
mExpirationTime = expirationTime;
return this;
}
/**
* Sets the daily update URI. The URI must use HTTPS.
*
* See {@link #getDailyUpdateUri()} for more information.
*/
@NonNull
public CustomAudience.Builder setDailyUpdateUri(@NonNull Uri dailyUpdateUri) {
Objects.requireNonNull(dailyUpdateUri);
mDailyUpdateUri = dailyUpdateUri;
return this;
}
/**
* Sets the user bidding signals used in the ad selection process.
*
* See {@link #getUserBiddingSignals()} for more information.
*/
@NonNull
public CustomAudience.Builder setUserBiddingSignals(
@Nullable AdSelectionSignals userBiddingSignals) {
mUserBiddingSignals = userBiddingSignals;
return this;
}
/**
* Sets the trusted bidding data to be queried and used in the ad selection process.
*
* See {@link #getTrustedBiddingData()} for more information.
*/
@NonNull
public CustomAudience.Builder setTrustedBiddingData(
@Nullable TrustedBiddingData trustedBiddingData) {
mTrustedBiddingData = trustedBiddingData;
return this;
}
/**
* Sets the URI to fetch bidding logic from for use in the ad selection process. The URI
* must use HTTPS.
*
* See {@link #getBiddingLogicUri()} for more information.
*/
@NonNull
public CustomAudience.Builder setBiddingLogicUri(@NonNull Uri biddingLogicUri) {
Objects.requireNonNull(biddingLogicUri);
mBiddingLogicUri = biddingLogicUri;
return this;
}
/**
* Sets the initial remarketing ads served by the custom audience. Will be assigned with an
* empty list if not provided.
*
* See {@link #getAds()} for more information.
*/
@NonNull
public CustomAudience.Builder setAds(@Nullable List See {@link #getAuctionServerRequestFlags()} for more information.
*/
@FlaggedApi(
"com.android.adservices.flags.fledge_custom_audience_auction_server_request_flags_enabled")
@NonNull
public CustomAudience.Builder setAuctionServerRequestFlags(
@AuctionServerRequestFlag int auctionServerRequestFlags) {
mAuctionServerRequestFlags = auctionServerRequestFlags;
return this;
}
/**
* Builds an instance of a {@link CustomAudience}.
*
* @throws NullPointerException if any non-null parameter is null
* @throws IllegalArgumentException if the expiration time occurs before activation time
* @throws IllegalArgumentException if the expiration time is set before the current time
*/
@NonNull
public CustomAudience build() {
Objects.requireNonNull(mBuyer, "The buyer has not been provided");
Objects.requireNonNull(mName, "The name has not been provided");
Objects.requireNonNull(mDailyUpdateUri, "The daily update URI has not been provided");
Objects.requireNonNull(mBiddingLogicUri, "The bidding logic URI has not been provided");
// To pass the API lint, we should not allow null Collection.
if (mAds == null) {
mAds = List.of();
}
return new CustomAudience(this);
}
}
}