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 com.android.adservices.tests.permissions; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.junit.Assert.assertThrows; 22 23 import android.adservices.adselection.AdSelectionConfig; 24 import android.adservices.adselection.AdSelectionConfigFixture; 25 import android.adservices.adselection.ReportImpressionRequest; 26 import android.adservices.clients.adselection.AdSelectionClient; 27 import android.adservices.clients.customaudience.AdvertisingCustomAudienceClient; 28 import android.adservices.clients.topics.AdvertisingTopicsClient; 29 import android.adservices.common.AdTechIdentifier; 30 import android.adservices.common.CommonFixture; 31 import android.adservices.customaudience.CustomAudience; 32 import android.content.Context; 33 import android.net.Uri; 34 import android.util.Log; 35 36 import androidx.test.core.app.ApplicationProvider; 37 import androidx.test.ext.junit.runners.AndroidJUnit4; 38 39 import com.android.adservices.common.AdservicesTestHelper; 40 import com.android.adservices.common.CompatAdServicesTestUtils; 41 import com.android.adservices.service.PhFlagsFixture; 42 import com.android.adservices.service.js.JSScriptEngine; 43 import com.android.compatibility.common.util.ShellUtils; 44 import com.android.modules.utils.build.SdkLevel; 45 46 import org.junit.After; 47 import org.junit.Assume; 48 import org.junit.Before; 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 52 import java.util.concurrent.ExecutionException; 53 import java.util.concurrent.Executor; 54 import java.util.concurrent.Executors; 55 56 /** In the ad_services_config.xml file, API access is revoked for all but a few, for this test. */ 57 @RunWith(AndroidJUnit4.class) 58 // TODO: Add tests for measurement (b/238194122). 59 public class PermissionsAppOptOutTest { 60 private static final Executor CALLBACK_EXECUTOR = Executors.newCachedThreadPool(); 61 private static final Context sContext = ApplicationProvider.getApplicationContext(); 62 private static final String CALLER_NOT_AUTHORIZED = 63 "java.lang.SecurityException: Caller is not authorized to call this API. " 64 + "Caller is not allowed."; 65 66 private String mPreviousAppAllowList; 67 68 @Before setup()69 public void setup() { 70 // Skip the test if it runs on unsupported platforms 71 Assume.assumeTrue(AdservicesTestHelper.isDeviceSupported()); 72 73 if (!SdkLevel.isAtLeastT()) { 74 overridePpapiAppAllowList(); 75 CompatAdServicesTestUtils.setFlags(); 76 } 77 78 // TODO: Remove once b/277790129 has been resolved 79 final String flags = ShellUtils.runShellCommand("device_config list adservices"); 80 Log.d("Adservices", flags); 81 } 82 83 @After tearDown()84 public void tearDown() throws Exception { 85 if (!AdservicesTestHelper.isDeviceSupported()) { 86 return; 87 } 88 89 if (!SdkLevel.isAtLeastT()) { 90 setPpapiAppAllowList(mPreviousAppAllowList); 91 CompatAdServicesTestUtils.resetFlagsToDefault(); 92 } 93 // TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE 94 CommonFixture.doSleep(PhFlagsFixture.DEFAULT_API_RATE_LIMIT_SLEEP_MS); 95 } 96 setPpapiAppAllowList(String allowList)97 private void setPpapiAppAllowList(String allowList) { 98 ShellUtils.runShellCommand( 99 "device_config put adservices ppapi_app_allow_list " + allowList); 100 } 101 overridePpapiAppAllowList()102 private void overridePpapiAppAllowList() { 103 mPreviousAppAllowList = 104 ShellUtils.runShellCommand("device_config get adservices ppapi_app_allow_list"); 105 setPpapiAppAllowList(mPreviousAppAllowList + "," + sContext.getPackageName()); 106 } 107 108 @Test testAppOptOut_topics()109 public void testAppOptOut_topics() { 110 AdvertisingTopicsClient advertisingTopicsClient1 = 111 new AdvertisingTopicsClient.Builder() 112 .setContext(sContext) 113 .setSdkName("sdk1") 114 .setExecutor(CALLBACK_EXECUTOR) 115 .build(); 116 117 ExecutionException exception = 118 assertThrows( 119 ExecutionException.class, () -> advertisingTopicsClient1.getTopics().get()); 120 assertThat(exception.getMessage()).isEqualTo(CALLER_NOT_AUTHORIZED); 121 } 122 123 @Test testNoEnrollment_fledgeJoinCustomAudience()124 public void testNoEnrollment_fledgeJoinCustomAudience() { 125 AdvertisingCustomAudienceClient customAudienceClient = 126 new AdvertisingCustomAudienceClient.Builder() 127 .setContext(sContext) 128 .setExecutor(CALLBACK_EXECUTOR) 129 .build(); 130 131 CustomAudience customAudience = 132 new CustomAudience.Builder() 133 .setBuyer(AdTechIdentifier.fromString("buyer.example.com")) 134 .setName("exampleCustomAudience") 135 .setDailyUpdateUri(Uri.parse("https://buyer.example.com/daily-update")) 136 .setBiddingLogicUri(Uri.parse("https://buyer.example.com/bidding-logic")) 137 .build(); 138 139 ExecutionException exception = 140 assertThrows( 141 ExecutionException.class, 142 () -> customAudienceClient.joinCustomAudience(customAudience).get()); 143 assertThat(exception.getMessage()).isEqualTo(CALLER_NOT_AUTHORIZED); 144 } 145 146 @Test testWithEnrollment_fledgeJoinCustomAudience()147 public void testWithEnrollment_fledgeJoinCustomAudience() 148 throws ExecutionException, InterruptedException { 149 AdvertisingCustomAudienceClient customAudienceClient = 150 new AdvertisingCustomAudienceClient.Builder() 151 .setContext(sContext) 152 .setExecutor(CALLBACK_EXECUTOR) 153 .build(); 154 155 // The "test.com" buyer is a pre-seeded enrolled ad tech 156 CustomAudience customAudience = 157 new CustomAudience.Builder() 158 .setBuyer(AdTechIdentifier.fromString("test.com")) 159 .setName("exampleCustomAudience") 160 .setDailyUpdateUri(Uri.parse("https://test.com/daily-update")) 161 .setBiddingLogicUri(Uri.parse("https://test.com/bidding-logic")) 162 .build(); 163 164 // When the ad tech is properly enrolled, just verify that no error is thrown 165 customAudienceClient.joinCustomAudience(customAudience).get(); 166 } 167 168 @Test testNoEnrollment_fledgeLeaveCustomAudience()169 public void testNoEnrollment_fledgeLeaveCustomAudience() { 170 AdvertisingCustomAudienceClient customAudienceClient = 171 new AdvertisingCustomAudienceClient.Builder() 172 .setContext(sContext) 173 .setExecutor(CALLBACK_EXECUTOR) 174 .build(); 175 176 ExecutionException exception = 177 assertThrows( 178 ExecutionException.class, 179 () -> 180 customAudienceClient 181 .leaveCustomAudience( 182 AdTechIdentifier.fromString("buyer.example.com"), 183 "exampleCustomAudience") 184 .get()); 185 assertThat(exception.getMessage()).isEqualTo(CALLER_NOT_AUTHORIZED); 186 } 187 188 @Test testWithEnrollment_fledgeLeaveCustomAudience()189 public void testWithEnrollment_fledgeLeaveCustomAudience() 190 throws ExecutionException, InterruptedException { 191 AdvertisingCustomAudienceClient customAudienceClient = 192 new AdvertisingCustomAudienceClient.Builder() 193 .setContext(sContext) 194 .setExecutor(CALLBACK_EXECUTOR) 195 .build(); 196 197 // The "test.com" buyer is a pre-seeded enrolled ad tech 198 // When the ad tech is properly enrolled, just verify that no error is thrown 199 customAudienceClient 200 .leaveCustomAudience( 201 AdTechIdentifier.fromString("test.com"), "exampleCustomAudience") 202 .get(); 203 } 204 205 @Test testNoEnrollment_selectAds_adSelectionConfig()206 public void testNoEnrollment_selectAds_adSelectionConfig() { 207 Assume.assumeTrue(JSScriptEngine.AvailabilityChecker.isJSSandboxAvailable()); 208 AdSelectionConfig adSelectionConfig = 209 AdSelectionConfigFixture.anAdSelectionConfig( 210 AdTechIdentifier.fromString("seller.example.com")); 211 212 AdSelectionClient mAdSelectionClient = 213 new AdSelectionClient.Builder() 214 .setContext(sContext) 215 .setExecutor(CALLBACK_EXECUTOR) 216 .build(); 217 218 ExecutionException exception = 219 assertThrows( 220 ExecutionException.class, 221 () -> mAdSelectionClient.selectAds(adSelectionConfig).get()); 222 assertThat(exception.getMessage()).isEqualTo(CALLER_NOT_AUTHORIZED); 223 } 224 225 @Test testWithEnrollment_selectAds_adSelectionConfig()226 public void testWithEnrollment_selectAds_adSelectionConfig() { 227 Assume.assumeTrue(JSScriptEngine.AvailabilityChecker.isJSSandboxAvailable()); 228 // The "test.com" buyer is a pre-seeded enrolled ad tech 229 AdSelectionConfig adSelectionConfig = 230 AdSelectionConfigFixture.anAdSelectionConfig( 231 AdTechIdentifier.fromString("test.com")); 232 233 AdSelectionClient mAdSelectionClient = 234 new AdSelectionClient.Builder() 235 .setContext(sContext) 236 .setExecutor(CALLBACK_EXECUTOR) 237 .build(); 238 239 // When the ad tech is properly enrolled, just verify that the error thrown is not due to 240 // enrollment 241 ExecutionException exception = 242 assertThrows( 243 ExecutionException.class, 244 () -> mAdSelectionClient.selectAds(adSelectionConfig).get()); 245 assertThat(exception.getMessage()).isNotEqualTo(CALLER_NOT_AUTHORIZED); 246 } 247 248 @Test testNoEnrollment_reportImpression()249 public void testNoEnrollment_reportImpression() { 250 Assume.assumeTrue(JSScriptEngine.AvailabilityChecker.isJSSandboxAvailable()); 251 AdSelectionConfig adSelectionConfig = 252 AdSelectionConfigFixture.anAdSelectionConfig( 253 AdTechIdentifier.fromString("seller.example.com")); 254 255 long adSelectionId = 1; 256 257 AdSelectionClient mAdSelectionClient = 258 new AdSelectionClient.Builder() 259 .setContext(sContext) 260 .setExecutor(CALLBACK_EXECUTOR) 261 .build(); 262 263 ReportImpressionRequest request = 264 new ReportImpressionRequest(adSelectionId, adSelectionConfig); 265 266 ExecutionException exception = 267 assertThrows( 268 ExecutionException.class, 269 () -> mAdSelectionClient.reportImpression(request).get()); 270 assertThat(exception.getMessage()).isEqualTo(CALLER_NOT_AUTHORIZED); 271 } 272 273 @Test testWithEnrollment_reportImpression()274 public void testWithEnrollment_reportImpression() { 275 Assume.assumeTrue(JSScriptEngine.AvailabilityChecker.isJSSandboxAvailable()); 276 // The "test.com" buyer is a pre-seeded enrolled ad tech 277 AdSelectionConfig adSelectionConfig = 278 AdSelectionConfigFixture.anAdSelectionConfig( 279 AdTechIdentifier.fromString("test.com")); 280 281 long adSelectionId = 1; 282 283 AdSelectionClient mAdSelectionClient = 284 new AdSelectionClient.Builder() 285 .setContext(sContext) 286 .setExecutor(CALLBACK_EXECUTOR) 287 .build(); 288 289 ReportImpressionRequest request = 290 new ReportImpressionRequest(adSelectionId, adSelectionConfig); 291 292 // When the ad tech is properly enrolled, just verify that the error thrown is not due to 293 // enrollment 294 ExecutionException exception = 295 assertThrows( 296 ExecutionException.class, 297 () -> mAdSelectionClient.reportImpression(request).get()); 298 assertThat(exception.getMessage()).isNotEqualTo(CALLER_NOT_AUTHORIZED); 299 } 300 301 // TODO(b/221876775): Unhide for frequency cap mainline promotion 302 /* 303 @Test 304 public void testNoEnrollment_updateAdCounterHistogram() { 305 long adSelectionId = 1; 306 307 AdSelectionClient mAdSelectionClient = 308 new AdSelectionClient.Builder() 309 .setContext(sContext) 310 .setExecutor(CALLBACK_EXECUTOR) 311 .build(); 312 313 UpdateAdCounterHistogramRequest request = 314 new UpdateAdCounterHistogramRequest.Builder() 315 .setAdSelectionId(adSelectionId) 316 .setAdEventType(FrequencyCapFilters.AD_EVENT_TYPE_VIEW) 317 .setCallerAdTech(AdTechIdentifier.fromString("seller.example.com")) 318 .build(); 319 320 ExecutionException exception = 321 assertThrows( 322 ExecutionException.class, 323 () -> mAdSelectionClient.updateAdCounterHistogram(request).get()); 324 assertThat(exception.getMessage()).isEqualTo(CALLER_NOT_AUTHORIZED); 325 } 326 327 @Test 328 public void testWithEnrollment_updateAdCounterHistogram() 329 throws ExecutionException, InterruptedException { 330 long adSelectionId = 1; 331 332 AdSelectionClient mAdSelectionClient = 333 new AdSelectionClient.Builder() 334 .setContext(sContext) 335 .setExecutor(CALLBACK_EXECUTOR) 336 .build(); 337 338 UpdateAdCounterHistogramRequest request = 339 new UpdateAdCounterHistogramRequest.Builder() 340 .setAdSelectionId(adSelectionId) 341 .setAdEventType(FrequencyCapFilters.AD_EVENT_TYPE_VIEW) 342 .setCallerAdTech(AdTechIdentifier.fromString("test.com")) 343 .build(); 344 345 mAdSelectionClient.updateAdCounterHistogram(request).get(); 346 } 347 */ 348 } 349