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.test.scenario.adservices.utils; 18 19 import android.adservices.adselection.AdSelectionConfig; 20 import android.adservices.common.AdData; 21 import android.adservices.common.AdSelectionSignals; 22 import android.adservices.common.AdTechIdentifier; 23 import android.adservices.customaudience.CustomAudience; 24 import android.adservices.customaudience.TrustedBiddingData; 25 import android.net.Uri; 26 import android.util.Log; 27 28 import com.google.common.collect.ImmutableList; 29 30 import java.io.IOException; 31 import java.net.HttpURLConnection; 32 import java.net.MalformedURLException; 33 import java.net.ProtocolException; 34 import java.net.URL; 35 import java.time.Duration; 36 import java.time.Instant; 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.HashMap; 40 import java.util.List; 41 import java.util.Map; 42 43 public class StaticAdTechServerUtils { 44 private static final String TAG = "StaticAdTechServerUtils"; 45 46 private static final String SERVER_BASE_ADDRESS_FORMAT = "https://%s"; 47 private static final List<String> BUYER_BASE_DOMAINS = 48 ImmutableList.of( 49 "performance-fledge-static-5jyy5ulagq-uc.a.run.app", 50 "performance-fledge-static-2-5jyy5ulagq-uc.a.run.app", 51 "performance-fledge-static-3-5jyy5ulagq-uc.a.run.app", 52 "performance-fledge-static-4-5jyy5ulagq-uc.a.run.app", 53 "performance-fledge-static-5-5jyy5ulagq-uc.a.run.app"); 54 55 // All details needed to create AdSelectionConfig 56 private static final String SELLER_BASE_DOMAIN = 57 "performance-fledge-static-5jyy5ulagq-uc.a.run.app"; 58 private static final AdTechIdentifier SELLER = AdTechIdentifier.fromString(SELLER_BASE_DOMAIN); 59 private static final String DECISION_LOGIC_PATH = "/seller/decision/simple_logic"; 60 private static final String TRUSTED_SCORING_SIGNALS_PATH = "/trusted/scoringsignals/simple"; 61 private static final AdSelectionSignals AD_SELECTION_SIGNALS = 62 AdSelectionSignals.fromString("{\"ad_selection_signals\":1}"); 63 private static final AdSelectionSignals SELLER_SIGNALS = 64 AdSelectionSignals.fromString("{\"test_seller_signals\":1}"); 65 66 // All details needed to create custom audiences 67 private static final AdSelectionSignals VALID_USER_BIDDING_SIGNALS = 68 AdSelectionSignals.fromString("{'valid': 'yep', 'opaque': 'definitely'}"); 69 private static final String AD_URI_PATH_FORMAT = "/render/%s/%s"; 70 private static final String DAILY_UPDATE_PATH_FORMAT = "/dailyupdate/%s"; 71 private static final String BIDDING_LOGIC_PATH = "/buyer/bidding/simple_logic"; 72 private static final String TRUSTED_BIDDING_SIGNALS_PATH = "/trusted/biddingsignals/simple"; 73 private static final String CUSTOM_AUDIENCE_PREFIX = "GENERIC_CA_"; 74 private static final Duration CUSTOM_AUDIENCE_EXPIRE_IN = Duration.ofDays(1); 75 private static final Instant VALID_ACTIVATION_TIME = Instant.now(); 76 private static final Instant VALID_EXPIRATION_TIME = 77 VALID_ACTIVATION_TIME.plus(CUSTOM_AUDIENCE_EXPIRE_IN); 78 private static final ArrayList<String> VALID_TRUSTED_BIDDING_KEYS = 79 new ArrayList<>(Arrays.asList("example", "valid", "list", "of", "keys")); 80 81 private final List<AdTechIdentifier> mCustomAudienceBuyers; 82 private final Map<AdTechIdentifier, AdSelectionSignals> mPerBuyerSignals; 83 private final int mNumberOfBuyers; 84 StaticAdTechServerUtils( int numberOfBuyers, List<AdTechIdentifier> customAudienceBuyers, Map<AdTechIdentifier, AdSelectionSignals> perBuyerSignals)85 private StaticAdTechServerUtils( 86 int numberOfBuyers, 87 List<AdTechIdentifier> customAudienceBuyers, 88 Map<AdTechIdentifier, AdSelectionSignals> perBuyerSignals) { 89 this.mNumberOfBuyers = numberOfBuyers; 90 this.mCustomAudienceBuyers = customAudienceBuyers; 91 this.mPerBuyerSignals = perBuyerSignals; 92 } 93 94 /** 95 * Makes a warmup call to all the servers so that servers don't have cold start latency during 96 * test runs. 97 */ warmupServers()98 public static void warmupServers() { 99 for (String domain : BUYER_BASE_DOMAINS) { 100 String buyerBaseAddress = String.format("https://%s", domain); 101 try { 102 URL url = new URL(buyerBaseAddress); 103 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 104 connection.setRequestMethod("GET"); 105 connection.getInputStream(); 106 } catch (MalformedURLException e) { 107 Log.e(TAG, "Parsing ad render url failed", e); 108 } catch (ProtocolException e) { 109 Log.e(TAG, "Invalid protocol for http call", e); 110 } catch (IOException e) { 111 Log.e(TAG, "Ad rendering call failed with exception", e); 112 } 113 } 114 } 115 withNumberOfBuyers(int numberOfBuyers)116 public static StaticAdTechServerUtils withNumberOfBuyers(int numberOfBuyers) { 117 if (numberOfBuyers > BUYER_BASE_DOMAINS.size()) { 118 throw new IllegalArgumentException( 119 "Number of buyers should be less than available domains : " 120 + BUYER_BASE_DOMAINS.size()); 121 } 122 123 List<AdTechIdentifier> customAudienceBuyers = new ArrayList<>(numberOfBuyers); 124 Map<AdTechIdentifier, AdSelectionSignals> perBuyerSignals = new HashMap<>(numberOfBuyers); 125 for (int i = 0; i < numberOfBuyers; i++) { 126 AdTechIdentifier buyer = AdTechIdentifier.fromString(BUYER_BASE_DOMAINS.get(i)); 127 customAudienceBuyers.add(buyer); 128 perBuyerSignals.put(buyer, AdSelectionSignals.fromString("{\"buyer_signals\":1}")); 129 } 130 131 return new StaticAdTechServerUtils(numberOfBuyers, customAudienceBuyers, perBuyerSignals); 132 } 133 createAndGetAdSelectionConfig()134 public AdSelectionConfig createAndGetAdSelectionConfig() { 135 String sellerBaseAddress = String.format(SERVER_BASE_ADDRESS_FORMAT, SELLER_BASE_DOMAIN); 136 137 return new AdSelectionConfig.Builder() 138 .setSeller(SELLER) 139 .setDecisionLogicUri(Uri.parse(sellerBaseAddress + DECISION_LOGIC_PATH)) 140 .setCustomAudienceBuyers(mCustomAudienceBuyers) 141 .setAdSelectionSignals(AD_SELECTION_SIGNALS) 142 .setSellerSignals(SELLER_SIGNALS) 143 .setPerBuyerSignals(mPerBuyerSignals) 144 .setTrustedScoringSignalsUri( 145 Uri.parse(sellerBaseAddress + TRUSTED_SCORING_SIGNALS_PATH)) 146 .build(); 147 } 148 createAndGetCustomAudiences( int numberOfCustomAudiencesPerBuyer, int numberOfAdsPerCustomAudiences)149 public List<CustomAudience> createAndGetCustomAudiences( 150 int numberOfCustomAudiencesPerBuyer, int numberOfAdsPerCustomAudiences) { 151 List<CustomAudience> customAudiences = new ArrayList<>(); 152 List<Double> bidsForBuyer = new ArrayList<>(); 153 154 for (int i = 1; i <= numberOfAdsPerCustomAudiences; i++) { 155 bidsForBuyer.add(i + 0.1); 156 } 157 158 for (int buyerIndex = 0; buyerIndex < mNumberOfBuyers; buyerIndex++) { 159 for (int customAudienceIndex = 1; 160 customAudienceIndex <= numberOfCustomAudiencesPerBuyer; 161 customAudienceIndex++) { 162 CustomAudience customAudience = 163 createCustomAudience( 164 buyerIndex, 165 customAudienceIndex, 166 bidsForBuyer, 167 VALID_ACTIVATION_TIME, 168 VALID_EXPIRATION_TIME); 169 customAudiences.add(customAudience); 170 } 171 } 172 173 return customAudiences; 174 } 175 createCustomAudience( int buyerIndex, int customAudienceIndex, List<Double> bids, Instant activationTime, Instant expirationTime)176 private CustomAudience createCustomAudience( 177 int buyerIndex, 178 int customAudienceIndex, 179 List<Double> bids, 180 Instant activationTime, 181 Instant expirationTime) { 182 // Generate ads for with bids provided 183 List<AdData> ads = new ArrayList<>(); 184 String customAudienceName = CUSTOM_AUDIENCE_PREFIX + customAudienceIndex; 185 186 // Create ads with the custom audience name and bid number as the ad URI 187 // Add the bid value to the metadata 188 for (int i = 0; i < bids.size(); i++) { 189 String adRenderUri = getAdRenderUri(buyerIndex, customAudienceName, i + 1); 190 191 ads.add( 192 new AdData.Builder() 193 .setRenderUri(Uri.parse(adRenderUri)) 194 .setMetadata("{\"bid\":" + (bids.get(i) + buyerIndex * 0.01) + "}") 195 .build()); 196 } 197 198 AdTechIdentifier buyerIdentifier = mCustomAudienceBuyers.get(buyerIndex); 199 String dailyUpdatePath = String.format(DAILY_UPDATE_PATH_FORMAT, customAudienceName); 200 String buyerBaseAddress = String.format("https://%s", BUYER_BASE_DOMAINS.get(buyerIndex)); 201 return new CustomAudience.Builder() 202 .setBuyer(buyerIdentifier) 203 .setName(customAudienceName) 204 .setActivationTime(activationTime) 205 .setExpirationTime(expirationTime) 206 .setDailyUpdateUri(Uri.parse(buyerBaseAddress + dailyUpdatePath)) 207 .setUserBiddingSignals(VALID_USER_BIDDING_SIGNALS) 208 .setTrustedBiddingData( 209 getValidTrustedBiddingDataByBuyer( 210 Uri.parse(buyerBaseAddress + TRUSTED_BIDDING_SIGNALS_PATH))) 211 .setBiddingLogicUri(Uri.parse(buyerBaseAddress + BIDDING_LOGIC_PATH)) 212 .setAds(ads) 213 .build(); 214 } 215 getAdRenderUri(int buyerIndex, String ca, int adId)216 public static String getAdRenderUri(int buyerIndex, String ca, int adId) { 217 String adPath = String.format(AD_URI_PATH_FORMAT, ca, adId); 218 String adRenderUri = 219 String.format(SERVER_BASE_ADDRESS_FORMAT, BUYER_BASE_DOMAINS.get(buyerIndex)) 220 + adPath; 221 return adRenderUri; 222 } 223 getValidTrustedBiddingDataByBuyer( Uri validTrustedBiddingUri)224 private static TrustedBiddingData getValidTrustedBiddingDataByBuyer( 225 Uri validTrustedBiddingUri) { 226 return new TrustedBiddingData.Builder() 227 .setTrustedBiddingKeys(getValidTrustedBiddingKeys()) 228 .setTrustedBiddingUri(validTrustedBiddingUri) 229 .build(); 230 } 231 getValidTrustedBiddingKeys()232 private static ImmutableList<String> getValidTrustedBiddingKeys() { 233 return ImmutableList.copyOf(VALID_TRUSTED_BIDDING_KEYS); 234 } 235 } 236