| /* |
| * Copyright (C) 2023 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.adselection; |
| |
| import android.adservices.common.AdTechIdentifier; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.net.Uri; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.Objects; |
| |
| /** |
| * Contains a buyer supplied {@link ContextualAds} bundle and its signature. |
| * |
| * <p>Instances of this class are created by SDKs to be injected as part of {@link |
| * AdSelectionConfig} and passed to {@link AdSelectionManager#selectAds} |
| * |
| * <p>SignedContextualAds are signed using ECDSA algorithm with SHA256 hashing algorithm (aka |
| * SHA256withECDSA). Keys used should belong to P-256 curve (aka “secp256r1” or “prime256v1”). |
| * |
| * <p>Signature should include the buyer, decisionLogicUri and adsWithBid fields. |
| * |
| * <p>While creating the signature a specific serialization rules must be followed as it's outlined |
| * here: |
| * |
| * <ul> |
| * <li>{@code Objects} concatenate the serialized values of their fields with the {@code |} (pipe) |
| * in between each field |
| * <li>{@code All fields} are sorted by alphabetical order within the object |
| * <li>{@code Nullable fields} are serialized as ”null” if they are null |
| * <li>{@code Doubles} are converted to String preserving precision |
| * <li>{@code Integers} are converted to string values |
| * <li>{@code Sets} are sorted alphabetically |
| * <li>{@code Lists} keep the same order |
| * <li>{@code Strings} get encoded into byte[] using UTF-8 encoding |
| * </ul> |
| * |
| * @hide |
| */ |
| public final class SignedContextualAds implements Parcelable { |
| private static final String BUYER_CANNOT_BE_NULL = "Buyer cannot be null."; |
| private static final String DECISION_LOGIC_URI_CANNOT_BE_NULL = |
| "DecisionLogicUri cannot be null."; |
| private static final String ADS_WITH_BID_CANNOT_BE_NULL = "AdsWithBid cannot be null."; |
| private static final String SIGNATURE_CANNOT_BE_NULL = "Signature cannot be null."; |
| @NonNull private final AdTechIdentifier mBuyer; |
| @NonNull private final Uri mDecisionLogicUri; |
| @NonNull private final List<AdWithBid> mAdsWithBid; |
| @NonNull private final byte[] mSignature; |
| |
| @NonNull |
| public static final Creator<SignedContextualAds> CREATOR = |
| new Creator<>() { |
| @Override |
| public SignedContextualAds createFromParcel(@NonNull Parcel in) { |
| Objects.requireNonNull(in); |
| return new SignedContextualAds(in); |
| } |
| |
| @Override |
| public SignedContextualAds[] newArray(int size) { |
| return new SignedContextualAds[0]; |
| } |
| }; |
| |
| private SignedContextualAds( |
| @NonNull AdTechIdentifier buyer, |
| @NonNull Uri decisionLogicUri, |
| @NonNull List<AdWithBid> adsWithBid, |
| @NonNull byte[] signature) { |
| this.mBuyer = buyer; |
| this.mDecisionLogicUri = decisionLogicUri; |
| this.mAdsWithBid = adsWithBid; |
| this.mSignature = signature; |
| } |
| |
| private SignedContextualAds(@NonNull Parcel in) { |
| Objects.requireNonNull(in); |
| mBuyer = AdTechIdentifier.CREATOR.createFromParcel(in); |
| mDecisionLogicUri = Uri.CREATOR.createFromParcel(in); |
| mAdsWithBid = in.createTypedArrayList(AdWithBid.CREATOR); |
| mSignature = in.createByteArray(); |
| } |
| |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| @Override |
| public void writeToParcel(@NonNull Parcel dest, int flags) { |
| Objects.requireNonNull(dest); |
| |
| mBuyer.writeToParcel(dest, flags); |
| mDecisionLogicUri.writeToParcel(dest, flags); |
| dest.writeTypedList(mAdsWithBid); |
| dest.writeByteArray(mSignature); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) return true; |
| if (!(o instanceof SignedContextualAds)) return false; |
| SignedContextualAds that = (SignedContextualAds) o; |
| return Objects.equals(mBuyer, that.mBuyer) |
| && Objects.equals(mDecisionLogicUri, that.mDecisionLogicUri) |
| && Objects.equals(mAdsWithBid, that.mAdsWithBid) |
| && Arrays.equals(mSignature, that.mSignature); |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(mBuyer, mDecisionLogicUri, mAdsWithBid, Arrays.hashCode(mSignature)); |
| } |
| |
| /** |
| * @return a new builder from this SignedContextualAds instance |
| * @hide |
| */ |
| @NonNull |
| public SignedContextualAds.Builder cloneToBuilder() { |
| return new SignedContextualAds.Builder() |
| .setBuyer(mBuyer) |
| .setDecisionLogicUri(mDecisionLogicUri) |
| .setAdsWithBid(mAdsWithBid) |
| .setSignature(mSignature); |
| } |
| |
| /** |
| * @return the Ad tech identifier from which this contextual Ad would have been downloaded |
| */ |
| @NonNull |
| public AdTechIdentifier getBuyer() { |
| return mBuyer; |
| } |
| |
| /** |
| * @return the URI used to retrieve the updateBid() and reportWin() function used during the ad |
| * selection and reporting process |
| */ |
| @NonNull |
| public Uri getDecisionLogicUri() { |
| return mDecisionLogicUri; |
| } |
| |
| /** |
| * @return the Ad data with bid value associated with this ad |
| */ |
| @NonNull |
| public List<AdWithBid> getAdsWithBid() { |
| return mAdsWithBid; |
| } |
| |
| /** |
| * Returns a copy of the signature for the contextual ads object. |
| * |
| * <p>See {@link SignedContextualAds} for more details. |
| * |
| * @return the signature |
| */ |
| public byte[] getSignature() { |
| return Arrays.copyOf(mSignature, mSignature.length); |
| } |
| |
| @Override |
| public String toString() { |
| return "SignedContextualAds{" |
| + "mBuyer=" |
| + mBuyer |
| + ", mDecisionLogicUri=" |
| + mDecisionLogicUri |
| + ", mAdsWithBid=" |
| + mAdsWithBid |
| + ", mSignature=" |
| + Arrays.toString(mSignature) |
| + '}'; |
| } |
| |
| /** Builder for {@link SignedContextualAds} object */ |
| public static final class Builder { |
| @Nullable private AdTechIdentifier mBuyer; |
| @Nullable private Uri mDecisionLogicUri; |
| @Nullable private List<AdWithBid> mAdsWithBid; |
| @Nullable private byte[] mSignature; |
| |
| public Builder() {} |
| |
| /** |
| * Sets the buyer Ad tech Identifier |
| * |
| * <p>See {@link #getBuyer()} for more details |
| */ |
| @NonNull |
| public SignedContextualAds.Builder setBuyer(@NonNull AdTechIdentifier buyer) { |
| Objects.requireNonNull(buyer, BUYER_CANNOT_BE_NULL); |
| |
| this.mBuyer = buyer; |
| return this; |
| } |
| |
| /** |
| * Sets the URI to fetch the decision logic used in ad selection and reporting |
| * |
| * <p>See {@link #getDecisionLogicUri()} for more details |
| */ |
| @NonNull |
| public SignedContextualAds.Builder setDecisionLogicUri(@NonNull Uri decisionLogicUri) { |
| Objects.requireNonNull(decisionLogicUri, DECISION_LOGIC_URI_CANNOT_BE_NULL); |
| |
| this.mDecisionLogicUri = decisionLogicUri; |
| return this; |
| } |
| |
| /** |
| * Sets the Ads with pre-defined bid values |
| * |
| * <p>See {@link #getAdsWithBid()} for more details |
| */ |
| @NonNull |
| public SignedContextualAds.Builder setAdsWithBid(@NonNull List<AdWithBid> adsWithBid) { |
| Objects.requireNonNull(adsWithBid, ADS_WITH_BID_CANNOT_BE_NULL); |
| |
| this.mAdsWithBid = adsWithBid; |
| return this; |
| } |
| |
| /** Sets the copied signature */ |
| @NonNull |
| public SignedContextualAds.Builder setSignature(@NonNull byte[] signature) { |
| Objects.requireNonNull(signature, SIGNATURE_CANNOT_BE_NULL); |
| |
| this.mSignature = Arrays.copyOf(signature, signature.length); |
| return this; |
| } |
| |
| /** |
| * Builds a {@link SignedContextualAds} instance. |
| * |
| * @throws NullPointerException if any required params are null |
| */ |
| @NonNull |
| public SignedContextualAds build() { |
| Objects.requireNonNull(mBuyer, BUYER_CANNOT_BE_NULL); |
| Objects.requireNonNull(mDecisionLogicUri, DECISION_LOGIC_URI_CANNOT_BE_NULL); |
| Objects.requireNonNull(mAdsWithBid, ADS_WITH_BID_CANNOT_BE_NULL); |
| Objects.requireNonNull(mSignature, SIGNATURE_CANNOT_BE_NULL); |
| |
| return new SignedContextualAds(mBuyer, mDecisionLogicUri, mAdsWithBid, mSignature); |
| } |
| } |
| } |