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