• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 static android.adservices.adselection.ReportEventRequest.FLAG_REPORTING_DESTINATION_BUYER;
20 import static android.adservices.adselection.ReportEventRequest.FLAG_REPORTING_DESTINATION_SELLER;
21 
22 import static com.android.adservices.service.FlagsConstants.KEY_AD_SERVICES_RETRY_STRATEGY_ENABLED;
23 import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_AD_SELECTION_BIDDING_TIMEOUT_PER_CA_MS;
24 import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_AD_SELECTION_OVERALL_TIMEOUT_MS;
25 import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_AD_SELECTION_SCORING_TIMEOUT_MS;
26 
27 import android.Manifest;
28 import android.adservices.adselection.AdSelectionConfig;
29 import android.adservices.adselection.AdSelectionOutcome;
30 import android.adservices.adselection.ReportEventRequest;
31 import android.adservices.adselection.ReportImpressionRequest;
32 import android.adservices.clients.adselection.AdSelectionClient;
33 import android.adservices.clients.customaudience.AdvertisingCustomAudienceClient;
34 import android.adservices.common.AdData;
35 import android.adservices.common.AdSelectionSignals;
36 import android.adservices.common.AdTechIdentifier;
37 import android.adservices.common.CommonFixture;
38 import android.adservices.customaudience.CustomAudience;
39 import android.adservices.customaudience.FetchAndJoinCustomAudienceRequest;
40 import android.adservices.customaudience.JoinCustomAudienceRequest;
41 import android.adservices.customaudience.ScheduleCustomAudienceUpdateRequest;
42 import android.adservices.customaudience.TrustedBiddingData;
43 import android.net.Uri;
44 import android.util.Log;
45 
46 import androidx.test.platform.app.InstrumentationRegistry;
47 
48 import com.android.adservices.common.AdServicesCtsTestCase;
49 import com.android.adservices.common.AdservicesTestHelper;
50 import com.android.adservices.common.annotations.EnableAllApis;
51 import com.android.adservices.common.annotations.SetCompatModeFlags;
52 import com.android.adservices.common.annotations.SetPpapiAppAllowList;
53 import com.android.adservices.shared.testing.SupportedByConditionRule;
54 import com.android.adservices.shared.testing.annotations.SetFlagEnabled;
55 import com.android.adservices.shared.testing.annotations.SetIntegerFlag;
56 import com.android.modules.utils.build.SdkLevel;
57 
58 import com.google.common.collect.ImmutableList;
59 import com.google.common.collect.ImmutableMap;
60 
61 import org.json.JSONException;
62 import org.json.JSONObject;
63 import org.junit.After;
64 import org.junit.Before;
65 import org.junit.Rule;
66 
67 import java.net.URL;
68 import java.time.Instant;
69 import java.time.temporal.ChronoUnit;
70 import java.util.Locale;
71 import java.util.concurrent.ExecutionException;
72 import java.util.concurrent.ExecutorService;
73 import java.util.concurrent.Executors;
74 import java.util.concurrent.TimeUnit;
75 import java.util.concurrent.TimeoutException;
76 
77 /** Abstract class for FLEDGE scenario tests using local servers. */
78 @EnableAllApis
79 @SetCompatModeFlags
80 @SetFlagEnabled(KEY_AD_SERVICES_RETRY_STRATEGY_ENABLED)
81 @SetIntegerFlag(name = KEY_FLEDGE_AD_SELECTION_BIDDING_TIMEOUT_PER_CA_MS, value = 5_000)
82 @SetIntegerFlag(name = KEY_FLEDGE_AD_SELECTION_OVERALL_TIMEOUT_MS, value = 10_000)
83 @SetIntegerFlag(name = KEY_FLEDGE_AD_SELECTION_SCORING_TIMEOUT_MS, value = 5_000)
84 @SetPpapiAppAllowList
85 public abstract class FledgeScenarioTest extends AdServicesCtsTestCase {
86     protected static final int TIMEOUT = 120;
87     protected static final int TIMEOUT_TES_SECONDS = 10;
88     protected static final String SHOES_CA = "shoes";
89     protected static final String SHIRTS_CA = "shirts";
90     protected static final String HATS_CA = "hats";
91     protected static final long AD_ID_FETCHER_TIMEOUT = 1000;
92     private static final int NUM_ADS_PER_AUDIENCE = 4;
93     private static final String PACKAGE_NAME = CommonFixture.TEST_PACKAGE_NAME;
94 
95     protected AdvertisingCustomAudienceClient mCustomAudienceClient;
96     protected AdvertisingCustomAudienceClient mCustomAudienceClientUsingGetMethod;
97     protected AdSelectionClient mAdSelectionClient;
98     protected AdSelectionClient mAdSelectionClientUsingGetMethod;
99 
100     private AdTechIdentifier mBuyer;
101     private AdTechIdentifier mSeller;
102     private String mServerBaseAddress;
103 
104     @Rule(order = 1)
105     public final SupportedByConditionRule devOptionsEnabled =
106             DevContextUtils.createDevOptionsAvailableRule(mContext, LOGCAT_TAG_FLEDGE);
107 
108     @Rule(order = 2)
109     public final SupportedByConditionRule webViewSupportsJSSandbox =
110             CtsWebViewSupportUtil.createJSSandboxAvailableRule(mContext);
111 
112     @Rule(order = 3)
113     public MockWebServerRule mMockWebServerRule =
114             MockWebServerRule.forHttps(
115                     mContext, "adservices_untrusted_test_server.p12", "adservices_test");
116 
makeAdSelectionSignals()117     protected static AdSelectionSignals makeAdSelectionSignals() {
118         return AdSelectionSignals.fromString(
119                 String.format("{\"valid\": true, \"publisher\": \"%s\"}", PACKAGE_NAME));
120     }
121 
122     @Before
setUp()123     public void setUp() throws Exception {
124         String[] deviceConfigPermissions;
125         if (SdkLevel.isAtLeastU()) {
126             deviceConfigPermissions =
127                     new String[] {
128                         Manifest.permission.WRITE_DEVICE_CONFIG,
129                         Manifest.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG
130                     };
131         } else {
132             deviceConfigPermissions = new String[] {Manifest.permission.WRITE_DEVICE_CONFIG};
133         }
134         InstrumentationRegistry.getInstrumentation()
135                 .getUiAutomation()
136                 .adoptShellPermissionIdentity(deviceConfigPermissions);
137 
138         AdservicesTestHelper.killAdservicesProcess(mContext);
139         ExecutorService executor = Executors.newCachedThreadPool();
140         mCustomAudienceClient =
141                 new AdvertisingCustomAudienceClient.Builder()
142                         .setContext(mContext)
143                         .setExecutor(executor)
144                         .build();
145         mCustomAudienceClientUsingGetMethod =
146                 new AdvertisingCustomAudienceClient.Builder()
147                         .setContext(mContext)
148                         .setExecutor(executor)
149                         .setUseGetMethodToCreateManagerInstance(true)
150                         .build();
151         mAdSelectionClient =
152                 new AdSelectionClient.Builder().setContext(mContext).setExecutor(executor).build();
153         mAdSelectionClientUsingGetMethod =
154                 new AdSelectionClient.Builder()
155                         .setContext(mContext)
156                         .setExecutor(executor)
157                         .setUseGetMethodToCreateManagerInstance(true)
158                         .build();
159     }
160 
161     @After
tearDown()162     public void tearDown() throws Exception {
163         try {
164             leaveCustomAudience(SHOES_CA);
165             leaveCustomAudience(SHIRTS_CA);
166             leaveCustomAudience(HATS_CA);
167         } catch (Exception e) {
168             // No-op catch here, these are only for cleaning up
169             Log.w(LOGCAT_TAG_FLEDGE, "Failed while cleaning up custom audiences", e);
170         }
171     }
172 
doSelectAds(AdSelectionConfig adSelectionConfig)173     protected AdSelectionOutcome doSelectAds(AdSelectionConfig adSelectionConfig)
174             throws ExecutionException, InterruptedException, TimeoutException {
175         return doSelectAds(mAdSelectionClient, adSelectionConfig);
176     }
177 
doSelectAds( AdSelectionClient adSelectionClient, AdSelectionConfig adSelectionConfig)178     protected AdSelectionOutcome doSelectAds(
179             AdSelectionClient adSelectionClient, AdSelectionConfig adSelectionConfig)
180             throws ExecutionException, InterruptedException, TimeoutException {
181         AdSelectionOutcome result =
182                 adSelectionClient.selectAds(adSelectionConfig).get(TIMEOUT, TimeUnit.SECONDS);
183         Log.d(LOGCAT_TAG_FLEDGE, "Ran ad selection.");
184         return result;
185     }
186 
doReportEvent(long adSelectionId, String eventName)187     protected void doReportEvent(long adSelectionId, String eventName)
188             throws JSONException, ExecutionException, InterruptedException, TimeoutException {
189         doReportEvent(mAdSelectionClient, adSelectionId, eventName);
190     }
191 
doReportEvent( AdSelectionClient adSelectionClient, long adSelectionId, String eventName)192     protected void doReportEvent(
193             AdSelectionClient adSelectionClient, long adSelectionId, String eventName)
194             throws JSONException, ExecutionException, InterruptedException, TimeoutException {
195         adSelectionClient
196                 .reportEvent(
197                         new ReportEventRequest.Builder(
198                                         adSelectionId,
199                                         eventName,
200                                         new JSONObject().put("key", "value").toString(),
201                                         FLAG_REPORTING_DESTINATION_SELLER
202                                                 | FLAG_REPORTING_DESTINATION_BUYER)
203                                 .build())
204                 .get(TIMEOUT, TimeUnit.SECONDS);
205         Log.d(LOGCAT_TAG_FLEDGE, "Ran report ad click for ad selection id: " + adSelectionId);
206     }
207 
doReportImpression(long adSelectionId, AdSelectionConfig adSelectionConfig)208     protected void doReportImpression(long adSelectionId, AdSelectionConfig adSelectionConfig)
209             throws ExecutionException, InterruptedException, TimeoutException {
210         doReportImpression(mAdSelectionClient, adSelectionId, adSelectionConfig);
211     }
212 
doReportImpression( AdSelectionClient adSelectionClient, long adSelectionId, AdSelectionConfig adSelectionConfig)213     protected void doReportImpression(
214             AdSelectionClient adSelectionClient,
215             long adSelectionId,
216             AdSelectionConfig adSelectionConfig)
217             throws ExecutionException, InterruptedException, TimeoutException {
218         adSelectionClient
219                 .reportImpression(new ReportImpressionRequest(adSelectionId, adSelectionConfig))
220                 .get(TIMEOUT, TimeUnit.SECONDS);
221         Log.d(LOGCAT_TAG_FLEDGE, "Ran report impression for ad selection id: " + adSelectionId);
222     }
223 
joinCustomAudience(String customAudienceName)224     protected void joinCustomAudience(String customAudienceName)
225             throws ExecutionException, InterruptedException, TimeoutException {
226         joinCustomAudience(mCustomAudienceClient, customAudienceName);
227     }
228 
joinCustomAudience( AdvertisingCustomAudienceClient client, String customAudienceName)229     protected void joinCustomAudience(
230             AdvertisingCustomAudienceClient client, String customAudienceName)
231             throws ExecutionException, InterruptedException, TimeoutException {
232         JoinCustomAudienceRequest joinCustomAudienceRequest =
233                 makeJoinCustomAudienceRequest(customAudienceName);
234         client.joinCustomAudience(joinCustomAudienceRequest.getCustomAudience())
235                 .get(TIMEOUT_TES_SECONDS, TimeUnit.SECONDS);
236         Log.d(LOGCAT_TAG_FLEDGE, "Joined Custom Audience: " + customAudienceName);
237     }
238 
joinCustomAudience(CustomAudience customAudience)239     protected void joinCustomAudience(CustomAudience customAudience)
240             throws ExecutionException, InterruptedException, TimeoutException {
241         mCustomAudienceClient
242                 .joinCustomAudience(customAudience)
243                 .get(TIMEOUT_TES_SECONDS, TimeUnit.SECONDS);
244         Log.d(LOGCAT_TAG_FLEDGE, "Joined Custom Audience: " + customAudience.getName());
245     }
246 
leaveCustomAudience(String customAudienceName)247     protected void leaveCustomAudience(String customAudienceName)
248             throws ExecutionException, InterruptedException, TimeoutException {
249         leaveCustomAudience(mCustomAudienceClient, customAudienceName);
250     }
251 
leaveCustomAudience( AdvertisingCustomAudienceClient client, String customAudienceName)252     protected void leaveCustomAudience(
253             AdvertisingCustomAudienceClient client, String customAudienceName)
254             throws ExecutionException, InterruptedException, TimeoutException {
255         CustomAudience customAudience = makeCustomAudience(customAudienceName).build();
256         client.leaveCustomAudience(customAudience.getBuyer(), customAudience.getName())
257                 .get(TIMEOUT_TES_SECONDS, TimeUnit.SECONDS);
258         Log.d(LOGCAT_TAG_FLEDGE, "Left Custom Audience: " + customAudienceName);
259     }
260 
doScheduleCustomAudienceUpdate(ScheduleCustomAudienceUpdateRequest request)261     protected void doScheduleCustomAudienceUpdate(ScheduleCustomAudienceUpdateRequest request)
262             throws ExecutionException, InterruptedException, TimeoutException {
263         doScheduleCustomAudienceUpdate(mCustomAudienceClient, request);
264     }
265 
doScheduleCustomAudienceUpdate( AdvertisingCustomAudienceClient client, ScheduleCustomAudienceUpdateRequest request)266     protected void doScheduleCustomAudienceUpdate(
267             AdvertisingCustomAudienceClient client, ScheduleCustomAudienceUpdateRequest request)
268             throws ExecutionException, InterruptedException, TimeoutException {
269         client.scheduleCustomAudienceUpdate(request).get(TIMEOUT, TimeUnit.SECONDS);
270         Log.d(LOGCAT_TAG_FLEDGE, "Scheduled Custom Audience Update: " + request);
271     }
272 
getServerBaseAddress()273     protected String getServerBaseAddress() {
274         return mServerBaseAddress;
275     }
276 
makeAdSelectionConfig(URL serverBaseAddressWithPrefix)277     protected AdSelectionConfig makeAdSelectionConfig(URL serverBaseAddressWithPrefix) {
278         AdSelectionSignals signals = FledgeScenarioTest.makeAdSelectionSignals();
279         Log.d(LOGCAT_TAG_FLEDGE, "Ad tech buyer: " + mBuyer);
280         Log.d(LOGCAT_TAG_FLEDGE, "Ad tech seller: " + mSeller);
281         return new AdSelectionConfig.Builder()
282                 .setSeller(mSeller)
283                 .setPerBuyerSignals(ImmutableMap.of(mBuyer, signals))
284                 .setCustomAudienceBuyers(ImmutableList.of(mBuyer))
285                 .setAdSelectionSignals(signals)
286                 .setSellerSignals(signals)
287                 .setDecisionLogicUri(
288                         Uri.parse(serverBaseAddressWithPrefix + Scenarios.SCORING_LOGIC_PATH))
289                 .setTrustedScoringSignalsUri(
290                         Uri.parse(serverBaseAddressWithPrefix + Scenarios.SCORING_SIGNALS_PATH))
291                 .build();
292     }
293 
setupDispatcher( ScenarioDispatcherFactory scenarioDispatcherFactory)294     protected ScenarioDispatcher setupDispatcher(
295             ScenarioDispatcherFactory scenarioDispatcherFactory) throws Exception {
296         ScenarioDispatcher scenarioDispatcher =
297                 mMockWebServerRule.startMockWebServer(scenarioDispatcherFactory);
298         mServerBaseAddress = scenarioDispatcher.getBaseAddressWithPrefix().toString();
299         mBuyer =
300                 AdTechIdentifier.fromString(
301                         scenarioDispatcher.getBaseAddressWithPrefix().getHost());
302         mSeller =
303                 AdTechIdentifier.fromString(
304                         scenarioDispatcher.getBaseAddressWithPrefix().getHost());
305         Log.d(LOGCAT_TAG_FLEDGE, "Started default MockWebServer.");
306         return scenarioDispatcher;
307     }
308 
309 
makeJoinCustomAudienceRequest(String customAudienceName)310     private JoinCustomAudienceRequest makeJoinCustomAudienceRequest(String customAudienceName) {
311         return new JoinCustomAudienceRequest.Builder()
312                 .setCustomAudience(makeCustomAudience(customAudienceName).build())
313                 .build();
314     }
315 
makeCustomAudience(String customAudienceName)316     protected CustomAudience.Builder makeCustomAudience(String customAudienceName) {
317         Uri trustedBiddingUri = Uri.parse(mServerBaseAddress + Scenarios.BIDDING_SIGNALS_PATH);
318         Uri dailyUpdateUri =
319                 Uri.parse(mServerBaseAddress + Scenarios.getDailyUpdatePath(customAudienceName));
320         return new CustomAudience.Builder()
321                 .setName(customAudienceName)
322                 .setDailyUpdateUri(dailyUpdateUri)
323                 .setTrustedBiddingData(
324                         new TrustedBiddingData.Builder()
325                                 .setTrustedBiddingKeys(ImmutableList.of())
326                                 .setTrustedBiddingUri(trustedBiddingUri)
327                                 .build())
328                 .setUserBiddingSignals(AdSelectionSignals.fromString("{}"))
329                 .setAds(makeAds(customAudienceName))
330                 .setBiddingLogicUri(
331                         Uri.parse(String.format(mServerBaseAddress + Scenarios.BIDDING_LOGIC_PATH)))
332                 .setBuyer(mBuyer)
333                 .setActivationTime(Instant.now())
334                 .setExpirationTime(Instant.now().plus(5, ChronoUnit.DAYS));
335     }
336 
makeAds(String customAudienceName)337     private ImmutableList<AdData> makeAds(String customAudienceName) {
338         ImmutableList.Builder<AdData> ads = new ImmutableList.Builder<>();
339         for (int i = 0; i < NUM_ADS_PER_AUDIENCE; i++) {
340             ads.add(makeAd(/* adNumber= */ i, customAudienceName));
341         }
342         return ads.build();
343     }
344 
makeAd(int adNumber, String customAudienceName)345     private AdData makeAd(int adNumber, String customAudienceName) {
346         return new AdData.Builder()
347                 .setMetadata(
348                         String.format(
349                                 Locale.ENGLISH,
350                                 "{\"bid\": 5, \"ad_number\": %d, \"target\": \"%s\"}",
351                                 adNumber,
352                                 PACKAGE_NAME))
353                 .setRenderUri(
354                         Uri.parse(
355                                 String.format(
356                                         "%s/render/%s/%s",
357                                         mServerBaseAddress, customAudienceName, adNumber)))
358                 .build();
359     }
360 
makeFetchAndJoinCustomAudienceRequest()361     protected FetchAndJoinCustomAudienceRequest.Builder makeFetchAndJoinCustomAudienceRequest() {
362         return new FetchAndJoinCustomAudienceRequest.Builder(
363                         Uri.parse(mServerBaseAddress + Scenarios.FETCH_CA_PATH))
364                 .setName(HATS_CA);
365     }
366 
doFetchAndJoinCustomAudience(FetchAndJoinCustomAudienceRequest request)367     protected void doFetchAndJoinCustomAudience(FetchAndJoinCustomAudienceRequest request)
368             throws Exception {
369         doFetchAndJoinCustomAudience(mCustomAudienceClient, request);
370     }
371 
doFetchAndJoinCustomAudience( AdvertisingCustomAudienceClient client, FetchAndJoinCustomAudienceRequest request)372     protected void doFetchAndJoinCustomAudience(
373             AdvertisingCustomAudienceClient client, FetchAndJoinCustomAudienceRequest request)
374             throws Exception {
375         client.fetchAndJoinCustomAudience(request).get(TIMEOUT_TES_SECONDS, TimeUnit.SECONDS);
376     }
377 }
378