1 /* 2 * Copyright (C) 2024 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.utils; 18 19 import android.adservices.clients.customaudience.AdvertisingCustomAudienceClient; 20 import android.adservices.common.AdData; 21 import android.adservices.common.AdTechIdentifier; 22 import android.adservices.common.CommonFixture; 23 import android.adservices.customaudience.CustomAudience; 24 import android.adservices.customaudience.CustomAudienceFixture; 25 import android.adservices.customaudience.TrustedBiddingDataFixture; 26 import android.content.Context; 27 import android.util.Log; 28 29 import java.time.Instant; 30 import java.util.ArrayList; 31 import java.util.List; 32 import java.util.concurrent.ExecutionException; 33 import java.util.concurrent.ExecutorService; 34 import java.util.concurrent.Executors; 35 import java.util.concurrent.TimeUnit; 36 import java.util.concurrent.TimeoutException; 37 38 public class CustomAudienceTestFixture { 39 private static final String TAG = CustomAudienceTestFixture.class.getPackageName(); 40 // Timeout for joining or leaving a custom audience. 41 private static final int API_RESPONSE_TIMEOUT_SECONDS = 5; 42 public static final String AD_URI_PREFIX = "/adverts/123/"; 43 public static final String BUYER_BIDDING_LOGIC_URI_PATH = "/buyer/bidding/logic/"; 44 45 private final ArrayList<CustomAudience> mCustomAudiencesToCleanUp = new ArrayList<>(); 46 private AdvertisingCustomAudienceClient mCustomAudienceClient; 47 CustomAudienceTestFixture(Context context)48 public CustomAudienceTestFixture(Context context) { 49 ExecutorService executor = Executors.newCachedThreadPool(); 50 mCustomAudienceClient = 51 new AdvertisingCustomAudienceClient.Builder() 52 .setContext(context) 53 .setExecutor(executor) 54 .build(); 55 } 56 CustomAudienceTestFixture(AdvertisingCustomAudienceClient customAudienceClient)57 public CustomAudienceTestFixture(AdvertisingCustomAudienceClient customAudienceClient) { 58 mCustomAudienceClient = customAudienceClient; 59 } 60 61 /** Return the custom audience client being used. */ getClient()62 public AdvertisingCustomAudienceClient getClient() { 63 return mCustomAudienceClient; 64 } 65 66 /** 67 * @param buyer The name of the buyer for this Custom Audience 68 * @param bids these bids, are added to its metadata. Our JS logic then picks this value and 69 * creates ad with the provided value as bid 70 * @return a real Custom Audience object that can be persisted and used in bidding and scoring 71 */ createCustomAudience(final AdTechIdentifier buyer, List<Double> bids)72 public CustomAudience createCustomAudience(final AdTechIdentifier buyer, List<Double> bids) { 73 return createCustomAudience( 74 buyer, 75 bids, 76 CustomAudienceFixture.VALID_ACTIVATION_TIME, 77 CustomAudienceFixture.VALID_EXPIRATION_TIME); 78 } 79 80 /** 81 * @param buyer The name of the buyer for this Custom Audience 82 * @param bids these bids, are added to its metadata. Our JS logic then picks this value and 83 * creates ad with the provided value as bid 84 * @return a real Custom Audience object that can be persisted and used in bidding and scoring 85 */ createCustomAudienceWithAdCost( final AdTechIdentifier buyer, List<Double> bids, double adCost)86 public CustomAudience createCustomAudienceWithAdCost( 87 final AdTechIdentifier buyer, List<Double> bids, double adCost) { 88 return createCustomAudienceWithAdCost( 89 buyer, 90 bids, 91 CustomAudienceFixture.VALID_ACTIVATION_TIME, 92 CustomAudienceFixture.VALID_EXPIRATION_TIME, 93 adCost); 94 } 95 96 /** Create a custom audience. */ createCustomAudience( final AdTechIdentifier buyer, List<Double> bids, Instant activationTime, Instant expirationTime)97 public CustomAudience createCustomAudience( 98 final AdTechIdentifier buyer, 99 List<Double> bids, 100 Instant activationTime, 101 Instant expirationTime) { 102 return createCustomAudience( 103 buyer + CustomAudienceFixture.VALID_NAME, 104 buyer, 105 bids, 106 activationTime, 107 expirationTime); 108 } 109 110 /** Create a custom audience. */ createCustomAudience( String name, final AdTechIdentifier buyer, List<Double> bids, Instant activationTime, Instant expirationTime)111 public CustomAudience createCustomAudience( 112 String name, 113 final AdTechIdentifier buyer, 114 List<Double> bids, 115 Instant activationTime, 116 Instant expirationTime) { 117 // Generate ads for with bids provided 118 List<AdData> ads = new ArrayList<>(); 119 120 // Create ads with the buyer name and bid number as the ad URI 121 // Add the bid value to the metadata 122 for (int i = 0; i < bids.size(); i++) { 123 ads.add( 124 new AdData.Builder() 125 .setRenderUri( 126 CommonFixture.getUri(buyer, AD_URI_PREFIX + "/ad" + (i + 1))) 127 .setMetadata("{\"result\":" + bids.get(i) + "}") 128 .build()); 129 } 130 131 return new CustomAudience.Builder() 132 .setBuyer(buyer) 133 .setName(name) 134 .setActivationTime(activationTime) 135 .setExpirationTime(expirationTime) 136 .setDailyUpdateUri(CustomAudienceFixture.getValidDailyUpdateUriByBuyer(buyer)) 137 .setUserBiddingSignals(CustomAudienceFixture.VALID_USER_BIDDING_SIGNALS) 138 .setTrustedBiddingData( 139 TrustedBiddingDataFixture.getValidTrustedBiddingDataByBuyer(buyer)) 140 .setBiddingLogicUri(CommonFixture.getUri(buyer, BUYER_BIDDING_LOGIC_URI_PATH)) 141 .setAds(ads) 142 .build(); 143 } 144 145 /** Create a custom audience with a given ad cost. */ createCustomAudienceWithAdCost( final AdTechIdentifier buyer, List<Double> bids, Instant activationTime, Instant expirationTime, double adCost)146 public CustomAudience createCustomAudienceWithAdCost( 147 final AdTechIdentifier buyer, 148 List<Double> bids, 149 Instant activationTime, 150 Instant expirationTime, 151 double adCost) { 152 // Generate ads for with bids provided 153 List<AdData> ads = new ArrayList<>(); 154 155 // Create ads with the buyer name and bid number as the ad URI 156 // Add the bid value to the metadata 157 for (int i = 0; i < bids.size(); i++) { 158 ads.add( 159 new AdData.Builder() 160 .setRenderUri( 161 CommonFixture.getUri(buyer, AD_URI_PREFIX + "/ad" + (i + 1))) 162 .setMetadata( 163 "{\"result\":" + bids.get(i) + ",\"adCost\":" + adCost + "}") 164 .build()); 165 } 166 167 return new CustomAudience.Builder() 168 .setBuyer(buyer) 169 .setName(buyer + CustomAudienceFixture.VALID_NAME) 170 .setActivationTime(activationTime) 171 .setExpirationTime(expirationTime) 172 .setDailyUpdateUri(CustomAudienceFixture.getValidDailyUpdateUriByBuyer(buyer)) 173 .setUserBiddingSignals(CustomAudienceFixture.VALID_USER_BIDDING_SIGNALS) 174 .setTrustedBiddingData( 175 TrustedBiddingDataFixture.getValidTrustedBiddingDataByBuyer(buyer)) 176 .setBiddingLogicUri(CommonFixture.getUri(buyer, BUYER_BIDDING_LOGIC_URI_PATH)) 177 .setAds(ads) 178 .build(); 179 } 180 181 /** Create a custom audience with a list of ads with a generated ad render id. */ createCustomAudienceWithAdRenderId( String name, final AdTechIdentifier buyer, List<Double> bids, Instant activationTime, Instant expirationTime)182 public CustomAudience createCustomAudienceWithAdRenderId( 183 String name, 184 final AdTechIdentifier buyer, 185 List<Double> bids, 186 Instant activationTime, 187 Instant expirationTime) { 188 // Generate ads for with bids provided 189 List<AdData> ads = new ArrayList<>(); 190 191 // Create ads with the buyer name and bid number as the ad URI 192 // Add the bid value to the metadata 193 for (int i = 0; i < bids.size(); i++) { 194 ads.add( 195 new AdData.Builder() 196 .setRenderUri( 197 CommonFixture.getUri(buyer, AD_URI_PREFIX + "/ad" + (i + 1))) 198 .setMetadata("{\"result\":" + bids.get(i) + "}") 199 .setAdRenderId(String.format("%s", i)) 200 .build()); 201 } 202 203 return new CustomAudience.Builder() 204 .setBuyer(buyer) 205 .setName(name) 206 .setActivationTime(activationTime) 207 .setExpirationTime(expirationTime) 208 .setDailyUpdateUri(CustomAudienceFixture.getValidDailyUpdateUriByBuyer(buyer)) 209 .setUserBiddingSignals(CustomAudienceFixture.VALID_USER_BIDDING_SIGNALS) 210 .setTrustedBiddingData( 211 TrustedBiddingDataFixture.getValidTrustedBiddingDataByBuyer(buyer)) 212 .setBiddingLogicUri(CommonFixture.getUri(buyer, BUYER_BIDDING_LOGIC_URI_PATH)) 213 .setAds(ads) 214 .build(); 215 } 216 217 /** Create a custom audience with given domains. */ createCustomAudienceWithSubdomains( final AdTechIdentifier buyer, List<Double> bids)218 public CustomAudience createCustomAudienceWithSubdomains( 219 final AdTechIdentifier buyer, List<Double> bids) { 220 // Generate ads for with bids provided 221 List<AdData> ads = new ArrayList<>(); 222 223 // Create ads with the buyer name and bid number as the ad URI 224 // Add the bid value to the metadata 225 for (int i = 0; i < bids.size(); i++) { 226 ads.add( 227 new AdData.Builder() 228 .setRenderUri( 229 CommonFixture.getUriWithValidSubdomain( 230 buyer.toString(), AD_URI_PREFIX + "/ad" + (i + 1))) 231 .setMetadata("{\"result\":" + bids.get(i) + "}") 232 .build()); 233 } 234 235 return CustomAudienceFixture.getValidBuilderWithSubdomainsForBuyer(buyer) 236 .setAds(ads) 237 .build(); 238 } 239 240 /** Join a custom audience. */ joinCustomAudience(CustomAudience customAudience)241 public void joinCustomAudience(CustomAudience customAudience) 242 throws ExecutionException, InterruptedException, TimeoutException { 243 mCustomAudiencesToCleanUp.add(customAudience); 244 Log.i( 245 TAG, 246 "Joining custom audience " 247 + customAudience.getName() 248 + " for buyer " 249 + customAudience.getBuyer()); 250 mCustomAudienceClient 251 .joinCustomAudience(customAudience) 252 .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); 253 } 254 255 /** Leave a custom audience. */ leaveCustomAudience(CustomAudience customAudience)256 public void leaveCustomAudience(CustomAudience customAudience) 257 throws ExecutionException, InterruptedException, TimeoutException { 258 mCustomAudienceClient 259 .leaveCustomAudience(customAudience.getBuyer(), customAudience.getName()) 260 .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); 261 Log.d(TAG, "Left Custom Audience: " + customAudience.getName()); 262 } 263 264 /** Leave a custom audience. */ leaveJoinedCustomAudiences()265 public void leaveJoinedCustomAudiences() 266 throws ExecutionException, InterruptedException, TimeoutException { 267 try { 268 for (CustomAudience customAudience : mCustomAudiencesToCleanUp) { 269 Log.i( 270 TAG, 271 "Cleanup: leaving custom audience " 272 + customAudience.getName() 273 + " for buyer" 274 + customAudience.getBuyer()); 275 mCustomAudienceClient 276 .leaveCustomAudience(customAudience.getBuyer(), customAudience.getName()) 277 .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); 278 } 279 } finally { 280 mCustomAudiencesToCleanUp.clear(); 281 } 282 } 283 } 284