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.common;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.net.Uri;
22 import android.os.Parcel;
23 import android.os.Parcelable;
24 
25 import com.android.adservices.AdServicesParcelableUtil;
26 import com.android.internal.util.Preconditions;
27 
28 import java.util.HashSet;
29 import java.util.Objects;
30 import java.util.Set;
31 
32 /** Represents data specific to an ad that is necessary for ad selection and rendering. */
33 public final class AdData implements Parcelable {
34     /** @hide */
35     public static final String NUM_AD_COUNTER_KEYS_EXCEEDED_FORMAT =
36             "AdData should have no more than %d ad counter keys";
37     /** @hide */
38     public static final int MAX_NUM_AD_COUNTER_KEYS = 10;
39 
40     @NonNull private final Uri mRenderUri;
41     @NonNull private final String mMetadata;
42     @NonNull private final Set<Integer> mAdCounterKeys;
43     @Nullable private final AdFilters mAdFilters;
44     @Nullable private final String mAdRenderId;
45 
46     @NonNull
47     public static final Creator<AdData> CREATOR =
48             new Creator<AdData>() {
49                 @Override
50                 public AdData createFromParcel(@NonNull Parcel in) {
51                     Objects.requireNonNull(in);
52 
53                     return new AdData(in);
54                 }
55 
56                 @Override
57                 public AdData[] newArray(int size) {
58                     return new AdData[size];
59                 }
60             };
61 
AdData(@onNull AdData.Builder builder)62     private AdData(@NonNull AdData.Builder builder) {
63         Objects.requireNonNull(builder);
64 
65         mRenderUri = builder.mRenderUri;
66         mMetadata = builder.mMetadata;
67         mAdCounterKeys = builder.mAdCounterKeys;
68         mAdFilters = builder.mAdFilters;
69         mAdRenderId = builder.mAdRenderId;
70     }
71 
AdData(@onNull Parcel in)72     private AdData(@NonNull Parcel in) {
73         Objects.requireNonNull(in);
74 
75         mRenderUri = Uri.CREATOR.createFromParcel(in);
76         mMetadata = in.readString();
77         mAdCounterKeys =
78                 AdServicesParcelableUtil.readNullableFromParcel(
79                         in, AdServicesParcelableUtil::readIntegerSetFromParcel);
80         mAdFilters =
81                 AdServicesParcelableUtil.readNullableFromParcel(
82                         in, AdFilters.CREATOR::createFromParcel);
83         mAdRenderId = in.readString();
84     }
85 
86     @Override
writeToParcel(@onNull Parcel dest, int flags)87     public void writeToParcel(@NonNull Parcel dest, int flags) {
88         Objects.requireNonNull(dest);
89 
90         mRenderUri.writeToParcel(dest, flags);
91         dest.writeString(mMetadata);
92         AdServicesParcelableUtil.writeNullableToParcel(
93                 dest, mAdCounterKeys, AdServicesParcelableUtil::writeIntegerSetToParcel);
94         AdServicesParcelableUtil.writeNullableToParcel(
95                 dest,
96                 mAdFilters,
97                 (targetParcel, sourceFilters) -> sourceFilters.writeToParcel(targetParcel, flags));
98         dest.writeString(mAdRenderId);
99     }
100 
101     /** @hide */
102     @Override
describeContents()103     public int describeContents() {
104         return 0;
105     }
106 
107     /** Gets the URI that points to the ad's rendering assets. The URI must use HTTPS. */
108     @NonNull
getRenderUri()109     public Uri getRenderUri() {
110         return mRenderUri;
111     }
112 
113     /**
114      * Gets the buyer ad metadata used during the ad selection process.
115      *
116      * <p>The metadata should be a valid JSON object serialized as a string. Metadata represents
117      * ad-specific bidding information that will be used during ad selection as part of bid
118      * generation and used in buyer JavaScript logic, which is executed in an isolated execution
119      * environment.
120      *
121      * <p>If the metadata is not a valid JSON object that can be consumed by the buyer's JS, the ad
122      * will not be eligible for ad selection.
123      */
124     @NonNull
getMetadata()125     public String getMetadata() {
126         return mMetadata;
127     }
128 
129     /**
130      * Gets the set of keys used in counting events.
131      *
132      * <p>No more than 10 ad counter keys may be associated with an ad.
133      *
134      * <p>The keys and counts per key are used in frequency cap filtering during ad selection to
135      * disqualify associated ads from being submitted to bidding.
136      *
137      * <p>Note that these keys can be overwritten along with the ads and other bidding data for a
138      * custom audience during the custom audience's daily update.
139      */
140     @NonNull
getAdCounterKeys()141     public Set<Integer> getAdCounterKeys() {
142         return mAdCounterKeys;
143     }
144 
145     /**
146      * Gets all {@link AdFilters} associated with the ad.
147      *
148      * <p>The filters, if met or exceeded, exclude the associated ad from participating in ad
149      * selection. They are optional and if {@code null} specify that no filters apply to this ad.
150      */
151     @Nullable
getAdFilters()152     public AdFilters getAdFilters() {
153         return mAdFilters;
154     }
155 
156     /**
157      * Gets the ad render id for server auctions.
158      *
159      * <p>Ad render id is collected for each {@link AdData} when server auction request is received.
160      *
161      * <p>Any {@link AdData} without ad render id will be ineligible for server-side auction.
162      *
163      * <p>The overall size of the CA is limited. The size of this field is considered using
164      * {@link String#getBytes()} in {@code UTF-8} encoding.
165      */
166     @Nullable
getAdRenderId()167     public String getAdRenderId() {
168         return mAdRenderId;
169     }
170 
171     /** Checks whether two {@link AdData} objects contain the same information. */
172     @Override
equals(Object o)173     public boolean equals(Object o) {
174         if (this == o) return true;
175         if (!(o instanceof AdData)) return false;
176         AdData adData = (AdData) o;
177         return mRenderUri.equals(adData.mRenderUri)
178                 && mMetadata.equals(adData.mMetadata)
179                 && mAdCounterKeys.equals(adData.mAdCounterKeys)
180                 && Objects.equals(mAdFilters, adData.mAdFilters)
181                 && Objects.equals(mAdRenderId, adData.mAdRenderId);
182     }
183 
184     /** Returns the hash of the {@link AdData} object's data. */
185     @Override
hashCode()186     public int hashCode() {
187         return Objects.hash(mRenderUri, mMetadata, mAdCounterKeys, mAdFilters);
188     }
189 
190     @Override
toString()191     public String toString() {
192         return "AdData{"
193                 + "mRenderUri="
194                 + mRenderUri
195                 + ", mMetadata='"
196                 + mMetadata
197                 + '\''
198                 + ", mAdCounterKeys="
199                 + mAdCounterKeys
200                 + ", mAdFilters="
201                 + mAdFilters
202                 + ", mAdRenderId='"
203                 + mAdRenderId
204                 + '\''
205                 + '}';
206     }
207 
208     /** Builder for {@link AdData} objects. */
209     public static final class Builder {
210         @Nullable private Uri mRenderUri;
211         @Nullable private String mMetadata;
212         @NonNull private Set<Integer> mAdCounterKeys = new HashSet<>();
213         @Nullable private AdFilters mAdFilters;
214         @Nullable private String mAdRenderId;
215 
216         // TODO(b/232883403): We may need to add @NonNUll members as args.
Builder()217         public Builder() {}
218 
219         /**
220          * Sets the URI that points to the ad's rendering assets. The URI must use HTTPS.
221          *
222          * <p>See {@link #getRenderUri()} for detail.
223          */
224         @NonNull
setRenderUri(@onNull Uri renderUri)225         public AdData.Builder setRenderUri(@NonNull Uri renderUri) {
226             Objects.requireNonNull(renderUri);
227             mRenderUri = renderUri;
228             return this;
229         }
230 
231         /**
232          * Sets the buyer ad metadata used during the ad selection process.
233          *
234          * <p>The metadata should be a valid JSON object serialized as a string. Metadata represents
235          * ad-specific bidding information that will be used during ad selection as part of bid
236          * generation and used in buyer JavaScript logic, which is executed in an isolated execution
237          * environment.
238          *
239          * <p>If the metadata is not a valid JSON object that can be consumed by the buyer's JS, the
240          * ad will not be eligible for ad selection.
241          *
242          * <p>See {@link #getMetadata()} for detail.
243          */
244         @NonNull
setMetadata(@onNull String metadata)245         public AdData.Builder setMetadata(@NonNull String metadata) {
246             Objects.requireNonNull(metadata);
247             mMetadata = metadata;
248             return this;
249         }
250 
251         /**
252          * Sets the set of keys used in counting events.
253          *
254          * <p>No more than 10 ad counter keys may be associated with an ad.
255          *
256          * <p>See {@link #getAdCounterKeys()} for more information.
257          */
258         @NonNull
setAdCounterKeys(@onNull Set<Integer> adCounterKeys)259         public AdData.Builder setAdCounterKeys(@NonNull Set<Integer> adCounterKeys) {
260             Objects.requireNonNull(adCounterKeys);
261             Preconditions.checkArgument(
262                     !adCounterKeys.contains(null), "Ad counter keys must not contain null value");
263             Preconditions.checkArgument(
264                     adCounterKeys.size() <= MAX_NUM_AD_COUNTER_KEYS,
265                     NUM_AD_COUNTER_KEYS_EXCEEDED_FORMAT,
266                     MAX_NUM_AD_COUNTER_KEYS);
267             mAdCounterKeys = adCounterKeys;
268             return this;
269         }
270 
271         /**
272          * Sets all {@link AdFilters} associated with the ad.
273          *
274          * <p>See {@link #getAdFilters()} for more information.
275          */
276         @NonNull
setAdFilters(@ullable AdFilters adFilters)277         public AdData.Builder setAdFilters(@Nullable AdFilters adFilters) {
278             mAdFilters = adFilters;
279             return this;
280         }
281 
282         /**
283          * Sets the ad render id for server auction
284          *
285          * <p>See {@link AdData#getAdRenderId()} for more information.
286          */
287         @NonNull
setAdRenderId(@ullable String adRenderId)288         public AdData.Builder setAdRenderId(@Nullable String adRenderId) {
289             mAdRenderId = adRenderId;
290             return this;
291         }
292 
293         /**
294          * Builds the {@link AdData} object.
295          *
296          * @throws NullPointerException if any required parameters are {@code null} when built
297          */
298         @NonNull
build()299         public AdData build() {
300             Objects.requireNonNull(mRenderUri, "The render URI has not been provided");
301             // TODO(b/231997523): Add JSON field validation.
302             Objects.requireNonNull(mMetadata, "The metadata has not been provided");
303 
304             return new AdData(this);
305         }
306     }
307 }
308