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