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 given domains. */ createCustomAudienceWithSubdomains( final AdTechIdentifier buyer, List<Double> bids)182 public CustomAudience createCustomAudienceWithSubdomains( 183 final AdTechIdentifier buyer, List<Double> bids) { 184 // Generate ads for with bids provided 185 List<AdData> ads = new ArrayList<>(); 186 187 // Create ads with the buyer name and bid number as the ad URI 188 // Add the bid value to the metadata 189 for (int i = 0; i < bids.size(); i++) { 190 ads.add( 191 new AdData.Builder() 192 .setRenderUri( 193 CommonFixture.getUriWithValidSubdomain( 194 buyer.toString(), AD_URI_PREFIX + "/ad" + (i + 1))) 195 .setMetadata("{\"result\":" + bids.get(i) + "}") 196 .build()); 197 } 198 199 return CustomAudienceFixture.getValidBuilderWithSubdomainsForBuyer(buyer) 200 .setAds(ads) 201 .build(); 202 } 203 204 /** Join a custom audience. */ joinCustomAudience(CustomAudience customAudience)205 public void joinCustomAudience(CustomAudience customAudience) 206 throws ExecutionException, InterruptedException, TimeoutException { 207 mCustomAudiencesToCleanUp.add(customAudience); 208 Log.i( 209 TAG, 210 "Joining custom audience " 211 + customAudience.getName() 212 + " for buyer " 213 + customAudience.getBuyer()); 214 mCustomAudienceClient 215 .joinCustomAudience(customAudience) 216 .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); 217 } 218 219 /** Leave a custom audience. */ leaveCustomAudience(CustomAudience customAudience)220 public void leaveCustomAudience(CustomAudience customAudience) 221 throws ExecutionException, InterruptedException, TimeoutException { 222 mCustomAudienceClient 223 .leaveCustomAudience(customAudience.getBuyer(), customAudience.getName()) 224 .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); 225 Log.d(TAG, "Left Custom Audience: " + customAudience.getName()); 226 } 227 228 /** Leave a custom audience. */ leaveJoinedCustomAudiences()229 public void leaveJoinedCustomAudiences() 230 throws ExecutionException, InterruptedException, TimeoutException { 231 try { 232 for (CustomAudience customAudience : mCustomAudiencesToCleanUp) { 233 Log.i( 234 TAG, 235 "Cleanup: leaving custom audience " 236 + customAudience.getName() 237 + " for buyer" 238 + customAudience.getBuyer()); 239 mCustomAudienceClient 240 .leaveCustomAudience(customAudience.getBuyer(), customAudience.getName()) 241 .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); 242 } 243 } finally { 244 mCustomAudiencesToCleanUp.clear(); 245 } 246 } 247 } 248