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