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 com.android.adservices.service.customaudience; 18 19 import static android.adservices.common.AdServicesStatusUtils.ILLEGAL_STATE_BACKGROUND_CALLER_ERROR_MESSAGE; 20 import static android.adservices.common.AdServicesStatusUtils.RATE_LIMIT_REACHED_ERROR_MESSAGE; 21 import static android.adservices.common.AdServicesStatusUtils.SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_ERROR_MESSAGE; 22 import static android.adservices.common.AdServicesStatusUtils.SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_ON_BEHALF_ERROR_MESSAGE; 23 import static android.adservices.common.AdServicesStatusUtils.STATUS_BACKGROUND_CALLER; 24 import static android.adservices.common.AdServicesStatusUtils.STATUS_CALLBACK_SHUTDOWN; 25 import static android.adservices.common.AdServicesStatusUtils.STATUS_CALLER_NOT_ALLOWED; 26 import static android.adservices.common.AdServicesStatusUtils.STATUS_INTERNAL_ERROR; 27 import static android.adservices.common.AdServicesStatusUtils.STATUS_INVALID_ARGUMENT; 28 import static android.adservices.common.AdServicesStatusUtils.STATUS_INVALID_OBJECT; 29 import static android.adservices.common.AdServicesStatusUtils.STATUS_RATE_LIMIT_REACHED; 30 import static android.adservices.common.AdServicesStatusUtils.STATUS_SERVER_RATE_LIMIT_REACHED; 31 import static android.adservices.common.AdServicesStatusUtils.STATUS_SUCCESS; 32 import static android.adservices.common.AdServicesStatusUtils.STATUS_UNAUTHORIZED; 33 import static android.adservices.common.AdServicesStatusUtils.STATUS_USER_CONSENT_REVOKED; 34 import static android.adservices.common.CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI; 35 import static android.adservices.common.CommonFixture.TEST_PACKAGE_NAME; 36 import static android.adservices.common.CommonFixture.VALID_BUYER_1; 37 import static android.adservices.common.CommonFixture.VALID_BUYER_2; 38 import static android.adservices.customaudience.CustomAudience.FLAG_AUCTION_SERVER_REQUEST_OMIT_ADS; 39 import static android.adservices.customaudience.CustomAudienceFixture.CUSTOM_AUDIENCE_MAX_ACTIVATION_DELAY_IN; 40 import static android.adservices.customaudience.CustomAudienceFixture.INVALID_BEYOND_MAX_EXPIRATION_TIME; 41 import static android.adservices.customaudience.CustomAudienceFixture.INVALID_DELAYED_ACTIVATION_TIME; 42 import static android.adservices.customaudience.CustomAudienceFixture.VALID_EXPIRATION_TIME; 43 import static android.adservices.customaudience.CustomAudienceFixture.VALID_NAME; 44 import static android.adservices.customaudience.CustomAudienceFixture.VALID_OWNER; 45 import static android.adservices.customaudience.CustomAudienceFixture.VALID_USER_BIDDING_SIGNALS; 46 import static android.adservices.customaudience.CustomAudienceFixture.getValidDailyUpdateUriByBuyer; 47 import static android.adservices.exceptions.RetryableAdServicesNetworkException.DEFAULT_RETRY_AFTER_VALUE; 48 49 import static com.android.adservices.service.common.AdTechUriValidator.IDENTIFIER_AND_URI_ARE_INCONSISTENT; 50 import static com.android.adservices.service.common.Validator.EXCEPTION_MESSAGE_FORMAT; 51 import static com.android.adservices.service.common.ValidatorUtil.AD_TECH_ROLE_BUYER; 52 import static com.android.adservices.service.customaudience.CustomAudienceBlob.BUYER_KEY; 53 import static com.android.adservices.service.customaudience.CustomAudienceBlob.OWNER_KEY; 54 import static com.android.adservices.service.customaudience.CustomAudienceBlobFixture.addDailyUpdateUri; 55 import static com.android.adservices.service.customaudience.CustomAudienceBlobFixture.addName; 56 import static com.android.adservices.service.customaudience.CustomAudienceBlobValidator.CLASS_NAME; 57 import static com.android.adservices.service.customaudience.CustomAudienceExpirationTimeValidatorTest.CUSTOM_AUDIENCE_MAX_EXPIRE_IN; 58 import static com.android.adservices.service.customaudience.CustomAudienceFieldSizeValidator.VIOLATION_NAME_TOO_LONG; 59 import static com.android.adservices.service.customaudience.CustomAudienceFieldSizeValidator.VIOLATION_USER_BIDDING_SIGNAL_TOO_BIG; 60 import static com.android.adservices.service.customaudience.CustomAudienceQuantityChecker.CUSTOM_AUDIENCE_QUANTITY_CHECK_FAILED; 61 import static com.android.adservices.service.customaudience.CustomAudienceQuantityChecker.THE_MAX_NUMBER_OF_CUSTOM_AUDIENCE_FOR_THE_DEVICE_HAD_REACHED; 62 import static com.android.adservices.service.customaudience.CustomAudienceQuantityChecker.THE_MAX_NUMBER_OF_CUSTOM_AUDIENCE_FOR_THE_OWNER_HAD_REACHED; 63 import static com.android.adservices.service.customaudience.CustomAudienceTimestampValidator.VIOLATION_ACTIVATE_AFTER_MAX_ACTIVATE; 64 import static com.android.adservices.service.customaudience.CustomAudienceTimestampValidator.VIOLATION_EXPIRE_AFTER_MAX_EXPIRE_TIME; 65 import static com.android.adservices.service.customaudience.CustomAudienceTimestampValidator.VIOLATION_EXPIRE_BEFORE_ACTIVATION; 66 import static com.android.adservices.service.customaudience.CustomAudienceUpdatableDataReader.ADS_KEY; 67 import static com.android.adservices.service.customaudience.CustomAudienceUpdatableDataReader.USER_BIDDING_SIGNALS_KEY; 68 import static com.android.adservices.service.customaudience.CustomAudienceValidator.DAILY_UPDATE_URI_FIELD_NAME; 69 import static com.android.adservices.service.customaudience.FetchCustomAudienceFixture.getFullJsonResponseStringWithInvalidAdRenderId; 70 import static com.android.adservices.service.customaudience.FetchCustomAudienceFixture.getFullSuccessfulJsonResponse; 71 import static com.android.adservices.service.customaudience.FetchCustomAudienceFixture.getFullSuccessfulJsonResponseString; 72 import static com.android.adservices.service.customaudience.FetchCustomAudienceFixture.getFullSuccessfulJsonResponseStringWithAdRenderId; 73 import static com.android.adservices.service.customaudience.FetchCustomAudienceImpl.FUSED_CUSTOM_AUDIENCE_EXCEEDS_SIZE_LIMIT_MESSAGE; 74 import static com.android.adservices.service.customaudience.FetchCustomAudienceImpl.FUSED_CUSTOM_AUDIENCE_INCOMPLETE_MESSAGE; 75 import static com.android.adservices.service.customaudience.FetchCustomAudienceImpl.REQUEST_CUSTOM_HEADER_EXCEEDS_SIZE_LIMIT_MESSAGE; 76 import static com.android.adservices.service.customaudience.FetchCustomAudienceReader.ACTIVATION_TIME_KEY; 77 import static com.android.adservices.service.customaudience.FetchCustomAudienceReader.DAILY_UPDATE_URI_KEY; 78 import static com.android.adservices.service.customaudience.FetchCustomAudienceReader.EXPIRATION_TIME_KEY; 79 import static com.android.adservices.service.customaudience.FetchCustomAudienceReader.NAME_KEY; 80 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__FETCH_AND_JOIN_CUSTOM_AUDIENCE; 81 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; 82 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; 83 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 84 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow; 85 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; 86 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; 87 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; 88 89 import static org.junit.Assert.assertEquals; 90 import static org.junit.Assert.assertFalse; 91 import static org.junit.Assert.assertTrue; 92 import static org.mockito.ArgumentMatchers.any; 93 import static org.mockito.ArgumentMatchers.anyBoolean; 94 import static org.mockito.ArgumentMatchers.anyLong; 95 import static org.mockito.Mockito.times; 96 97 import android.adservices.common.AdTechIdentifier; 98 import android.adservices.common.CallingAppUidSupplierProcessImpl; 99 import android.adservices.common.CommonFixture; 100 import android.adservices.common.FledgeErrorResponse; 101 import android.adservices.customaudience.CustomAudienceFixture; 102 import android.adservices.customaudience.FetchAndJoinCustomAudienceCallback; 103 import android.adservices.customaudience.FetchAndJoinCustomAudienceInput; 104 import android.adservices.http.MockWebServerRule; 105 import android.net.Uri; 106 import android.os.LimitExceededException; 107 import android.os.Process; 108 import android.os.RemoteException; 109 110 import com.android.adservices.LoggerFactory; 111 import com.android.adservices.MockWebServerRuleFactory; 112 import com.android.adservices.concurrency.AdServicesExecutors; 113 import com.android.adservices.data.adselection.AppInstallDao; 114 import com.android.adservices.data.adselection.FrequencyCapDao; 115 import com.android.adservices.data.customaudience.AdDataConversionStrategy; 116 import com.android.adservices.data.customaudience.AdDataConversionStrategyFactory; 117 import com.android.adservices.data.customaudience.CustomAudienceDao; 118 import com.android.adservices.data.customaudience.CustomAudienceStats; 119 import com.android.adservices.data.customaudience.DBCustomAudience; 120 import com.android.adservices.data.customaudience.DBCustomAudienceQuarantine; 121 import com.android.adservices.service.Flags; 122 import com.android.adservices.service.adselection.AdFilteringFeatureFactory; 123 import com.android.adservices.service.common.AdRenderIdValidator; 124 import com.android.adservices.service.common.AppImportanceFilter; 125 import com.android.adservices.service.common.CustomAudienceServiceFilter; 126 import com.android.adservices.service.common.FledgeAllowListsFilter; 127 import com.android.adservices.service.common.FledgeAuthorizationFilter; 128 import com.android.adservices.service.common.Throttler; 129 import com.android.adservices.service.common.cache.CacheProviderFactory; 130 import com.android.adservices.service.common.httpclient.AdServicesHttpsClient; 131 import com.android.adservices.service.consent.ConsentManager; 132 import com.android.adservices.service.devapi.DevContext; 133 import com.android.adservices.service.stats.AdServicesLogger; 134 import com.android.adservices.service.stats.AdServicesLoggerImpl; 135 import com.android.adservices.shared.testing.SdkLevelSupportRule; 136 import com.android.dx.mockito.inline.extended.ExtendedMockito; 137 138 import com.google.common.collect.ImmutableList; 139 import com.google.common.util.concurrent.MoreExecutors; 140 import com.google.mockwebserver.Dispatcher; 141 import com.google.mockwebserver.MockResponse; 142 import com.google.mockwebserver.MockWebServer; 143 import com.google.mockwebserver.RecordedRequest; 144 145 import org.json.JSONObject; 146 import org.junit.After; 147 import org.junit.Assert; 148 import org.junit.Before; 149 import org.junit.Rule; 150 import org.junit.Test; 151 import org.junit.runner.RunWith; 152 import org.mockito.ArgumentCaptor; 153 import org.mockito.Mock; 154 import org.mockito.MockitoSession; 155 import org.mockito.Spy; 156 import org.mockito.junit.MockitoJUnitRunner; 157 import org.mockito.quality.Strictness; 158 import org.mockito.stubbing.Answer; 159 160 import java.nio.charset.StandardCharsets; 161 import java.time.Clock; 162 import java.util.List; 163 import java.util.Locale; 164 import java.util.concurrent.CountDownLatch; 165 import java.util.concurrent.ExecutorService; 166 import java.util.concurrent.atomic.AtomicInteger; 167 168 @RunWith(MockitoJUnitRunner.class) 169 public class FetchCustomAudienceImplTest { 170 private static final int API_NAME = 171 AD_SERVICES_API_CALLED__API_NAME__FETCH_AND_JOIN_CUSTOM_AUDIENCE; 172 private static final ExecutorService DIRECT_EXECUTOR = MoreExecutors.newDirectExecutorService(); 173 private static final AdDataConversionStrategy AD_DATA_CONVERSION_STRATEGY = 174 AdDataConversionStrategyFactory.getAdDataConversionStrategy(true, true, true); 175 private static final Clock CLOCK = CommonFixture.FIXED_CLOCK_TRUNCATED_TO_MILLI; 176 private final AdServicesLogger mAdServicesLoggerMock = 177 ExtendedMockito.mock(AdServicesLoggerImpl.class); 178 @Mock private CustomAudienceServiceFilter mCustomAudienceServiceFilterMock; 179 @Mock private CustomAudienceDao mCustomAudienceDaoMock; 180 @Mock private AppInstallDao mAppInstallDaoMock; 181 @Mock private FrequencyCapDao mFrequencyCapDaoMock; 182 private final AdRenderIdValidator mAdRenderIdValidator = 183 AdRenderIdValidator.createEnabledInstance(100); 184 private AdFilteringFeatureFactory mAdFilteringFeatureFactory; 185 186 @Spy 187 private final AdServicesHttpsClient mHttpClientSpy = 188 new AdServicesHttpsClient( 189 AdServicesExecutors.getBlockingExecutor(), 190 CacheProviderFactory.createNoOpCache()); 191 192 @Rule public MockWebServerRule mMockWebServerRule = MockWebServerRuleFactory.createForHttps(); 193 private static final AdTechIdentifier BUYER = AdTechIdentifier.fromString("localhost"); 194 private int mCallingAppUid; 195 private Uri mFetchUri; 196 private FetchCustomAudienceImpl mFetchCustomAudienceImpl; 197 private FetchAndJoinCustomAudienceInput.Builder mInputBuilder; 198 private MockitoSession mStaticMockSession = null; 199 200 @Rule(order = 0) 201 public final SdkLevelSupportRule sdkLevel = SdkLevelSupportRule.forAtLeastS(); 202 203 @Before setup()204 public void setup() { 205 mStaticMockSession = 206 ExtendedMockito.mockitoSession() 207 .strictness(Strictness.LENIENT) 208 .initMocks(this) 209 .mockStatic(ConsentManager.class) 210 .startMocking(); 211 212 mCallingAppUid = CallingAppUidSupplierProcessImpl.create().getCallingAppUid(); 213 214 mFetchUri = mMockWebServerRule.uriForPath("/fetch"); 215 216 mInputBuilder = 217 new FetchAndJoinCustomAudienceInput.Builder(mFetchUri, VALID_OWNER) 218 .setName(CustomAudienceFixture.VALID_NAME) 219 .setActivationTime(CustomAudienceFixture.VALID_ACTIVATION_TIME) 220 .setExpirationTime(VALID_EXPIRATION_TIME) 221 .setUserBiddingSignals(CustomAudienceFixture.VALID_USER_BIDDING_SIGNALS); 222 223 Flags flags = new FetchCustomAudienceFlags(); 224 225 mAdFilteringFeatureFactory = 226 new AdFilteringFeatureFactory(mAppInstallDaoMock, mFrequencyCapDaoMock, flags); 227 228 mFetchCustomAudienceImpl = getImplWithFlags(flags); 229 230 doReturn(BUYER) 231 .when(mCustomAudienceServiceFilterMock) 232 .filterRequestAndExtractIdentifier( 233 mFetchUri, 234 VALID_OWNER, 235 false, 236 true, 237 true, 238 Process.myUid(), 239 API_NAME, 240 Throttler.ApiKey.FLEDGE_API_FETCH_CUSTOM_AUDIENCE, 241 DevContext.createForDevOptionsDisabled()); 242 doReturn(BUYER) 243 .when(mCustomAudienceServiceFilterMock) 244 .filterRequestAndExtractIdentifier( 245 mFetchUri, 246 VALID_OWNER, 247 false, 248 true, 249 true, 250 Process.myUid(), 251 API_NAME, 252 Throttler.ApiKey.FLEDGE_API_FETCH_CUSTOM_AUDIENCE, 253 DevContext.builder().setDevOptionsEnabled(true).build()); 254 doReturn( 255 CustomAudienceStats.builder() 256 .setTotalCustomAudienceCount(1) 257 .setBuyer(BUYER) 258 .setOwner(VALID_OWNER) 259 .setPerOwnerCustomAudienceCount(1) 260 .setPerBuyerCustomAudienceCount(1) 261 .setTotalBuyerCount(1) 262 .setTotalOwnerCount(1) 263 .build()) 264 .when(mCustomAudienceDaoMock) 265 .getCustomAudienceStats(eq(VALID_OWNER)); 266 267 doReturn(false) 268 .when(mCustomAudienceDaoMock) 269 .doesCustomAudienceQuarantineExist(VALID_OWNER, BUYER); 270 } 271 272 @After tearDown()273 public void tearDown() { 274 if (mStaticMockSession != null) { 275 mStaticMockSession.finishMocking(); 276 } 277 } 278 279 @Test testImpl_disabled()280 public void testImpl_disabled() throws Exception { 281 // Use flag value to disable the API. 282 mFetchCustomAudienceImpl = 283 getImplWithFlags( 284 new FetchCustomAudienceFlags() { 285 @Override 286 public boolean getFledgeFetchCustomAudienceEnabled() { 287 return false; 288 } 289 }); 290 291 MockWebServer mockWebServer = 292 mMockWebServerRule.startMockWebServer( 293 List.of( 294 new MockResponse() 295 .setBody( 296 getFullSuccessfulJsonResponseString( 297 VALID_BUYER_1)))); 298 299 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 300 301 assertEquals(0, mockWebServer.getRequestCount()); 302 assertFalse(callback.mIsSuccess); 303 verify(mAdServicesLoggerMock) 304 .logFledgeApiCallStats( 305 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_INTERNAL_ERROR), anyInt()); 306 } 307 308 @Test testImpl_invalidPackageName_throws()309 public void testImpl_invalidPackageName_throws() throws Exception { 310 String otherPackageName = VALID_OWNER + "incorrectPackage"; 311 312 doThrow(new FledgeAuthorizationFilter.CallerMismatchException()) 313 .when(mCustomAudienceServiceFilterMock) 314 .filterRequestAndExtractIdentifier( 315 mFetchUri, 316 otherPackageName, 317 false, 318 true, 319 true, 320 Process.myUid(), 321 API_NAME, 322 Throttler.ApiKey.FLEDGE_API_FETCH_CUSTOM_AUDIENCE, 323 DevContext.createForDevOptionsDisabled()); 324 325 FetchCustomAudienceTestCallback callback = 326 callFetchCustomAudience( 327 mInputBuilder.setCallerPackageName(otherPackageName).build()); 328 329 assertFalse(callback.mIsSuccess); 330 assertEquals(STATUS_UNAUTHORIZED, callback.mFledgeErrorResponse.getStatusCode()); 331 assertEquals( 332 SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_ON_BEHALF_ERROR_MESSAGE, 333 callback.mFledgeErrorResponse.getErrorMessage()); 334 335 // Confirm a duplicate log entry does not exist. 336 // CustomAudienceServiceFilter ensures the failing assertion is logged internally. 337 verify(mAdServicesLoggerMock, never()) 338 .logFledgeApiCallStats( 339 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_UNAUTHORIZED), anyInt()); 340 } 341 342 @Test testImpl_throttled_throws()343 public void testImpl_throttled_throws() throws Exception { 344 doThrow(new LimitExceededException(RATE_LIMIT_REACHED_ERROR_MESSAGE)) 345 .when(mCustomAudienceServiceFilterMock) 346 .filterRequestAndExtractIdentifier( 347 mFetchUri, 348 VALID_OWNER, 349 false, 350 true, 351 true, 352 Process.myUid(), 353 API_NAME, 354 Throttler.ApiKey.FLEDGE_API_FETCH_CUSTOM_AUDIENCE, 355 DevContext.createForDevOptionsDisabled()); 356 357 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 358 359 assertFalse(callback.mIsSuccess); 360 assertEquals(STATUS_RATE_LIMIT_REACHED, callback.mFledgeErrorResponse.getStatusCode()); 361 assertEquals( 362 RATE_LIMIT_REACHED_ERROR_MESSAGE, callback.mFledgeErrorResponse.getErrorMessage()); 363 364 // Confirm a duplicate log entry does not exist. 365 // CustomAudienceServiceFilter ensures the failing assertion is logged internally. 366 verify(mAdServicesLoggerMock, never()) 367 .logFledgeApiCallStats( 368 eq(API_NAME), 369 eq(TEST_PACKAGE_NAME), 370 eq(STATUS_RATE_LIMIT_REACHED), 371 anyInt()); 372 } 373 374 @Test testImpl_failedForegroundCheck_throws()375 public void testImpl_failedForegroundCheck_throws() throws Exception { 376 doThrow(new AppImportanceFilter.WrongCallingApplicationStateException()) 377 .when(mCustomAudienceServiceFilterMock) 378 .filterRequestAndExtractIdentifier( 379 mFetchUri, 380 VALID_OWNER, 381 false, 382 true, 383 true, 384 Process.myUid(), 385 API_NAME, 386 Throttler.ApiKey.FLEDGE_API_FETCH_CUSTOM_AUDIENCE, 387 DevContext.createForDevOptionsDisabled()); 388 389 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 390 391 assertFalse(callback.mIsSuccess); 392 assertEquals(STATUS_BACKGROUND_CALLER, callback.mFledgeErrorResponse.getStatusCode()); 393 assertEquals( 394 ILLEGAL_STATE_BACKGROUND_CALLER_ERROR_MESSAGE, 395 callback.mFledgeErrorResponse.getErrorMessage()); 396 397 // Confirm a duplicate log entry does not exist. 398 // CustomAudienceServiceFilter ensures the failing assertion is logged internally. 399 verify(mAdServicesLoggerMock, never()) 400 .logFledgeApiCallStats( 401 eq(API_NAME), 402 eq(TEST_PACKAGE_NAME), 403 eq(STATUS_BACKGROUND_CALLER), 404 anyInt()); 405 } 406 407 @Test testImpl_failedEnrollmentCheck_throws()408 public void testImpl_failedEnrollmentCheck_throws() throws Exception { 409 doThrow(new FledgeAuthorizationFilter.AdTechNotAllowedException()) 410 .when(mCustomAudienceServiceFilterMock) 411 .filterRequestAndExtractIdentifier( 412 mFetchUri, 413 VALID_OWNER, 414 false, 415 true, 416 true, 417 Process.myUid(), 418 API_NAME, 419 Throttler.ApiKey.FLEDGE_API_FETCH_CUSTOM_AUDIENCE, 420 DevContext.createForDevOptionsDisabled()); 421 422 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 423 424 assertFalse(callback.mIsSuccess); 425 assertEquals(STATUS_CALLER_NOT_ALLOWED, callback.mFledgeErrorResponse.getStatusCode()); 426 assertEquals( 427 SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_ERROR_MESSAGE, 428 callback.mFledgeErrorResponse.getErrorMessage()); 429 430 // Confirm a duplicate log entry does not exist. 431 // CustomAudienceServiceFilter ensures the failing assertion is logged internally. 432 verify(mAdServicesLoggerMock, never()) 433 .logFledgeApiCallStats( 434 eq(API_NAME), 435 eq(TEST_PACKAGE_NAME), 436 eq(STATUS_CALLER_NOT_ALLOWED), 437 anyInt()); 438 } 439 440 @Test testImpl_appCannotUsePPAPI_throws()441 public void testImpl_appCannotUsePPAPI_throws() throws Exception { 442 doThrow(new FledgeAllowListsFilter.AppNotAllowedException()) 443 .when(mCustomAudienceServiceFilterMock) 444 .filterRequestAndExtractIdentifier( 445 mFetchUri, 446 VALID_OWNER, 447 false, 448 true, 449 true, 450 Process.myUid(), 451 API_NAME, 452 Throttler.ApiKey.FLEDGE_API_FETCH_CUSTOM_AUDIENCE, 453 DevContext.createForDevOptionsDisabled()); 454 455 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 456 457 assertFalse(callback.mIsSuccess); 458 assertEquals(STATUS_CALLER_NOT_ALLOWED, callback.mFledgeErrorResponse.getStatusCode()); 459 assertEquals( 460 SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_ERROR_MESSAGE, 461 callback.mFledgeErrorResponse.getErrorMessage()); 462 463 // Confirm a duplicate log entry does not exist. 464 // CustomAudienceServiceFilter ensures the failing assertion is logged internally. 465 verify(mAdServicesLoggerMock, never()) 466 .logFledgeApiCallStats( 467 eq(API_NAME), 468 eq(TEST_PACKAGE_NAME), 469 eq(STATUS_CALLER_NOT_ALLOWED), 470 anyInt()); 471 } 472 473 @Test testImpl_revokedConsent_failsSilently()474 public void testImpl_revokedConsent_failsSilently() throws Exception { 475 doThrow(new ConsentManager.RevokedConsentException()) 476 .when(mCustomAudienceServiceFilterMock) 477 .filterRequestAndExtractIdentifier( 478 mFetchUri, 479 VALID_OWNER, 480 false, 481 true, 482 true, 483 Process.myUid(), 484 API_NAME, 485 Throttler.ApiKey.FLEDGE_API_FETCH_CUSTOM_AUDIENCE, 486 DevContext.createForDevOptionsDisabled()); 487 488 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 489 490 assertTrue(callback.mIsSuccess); 491 492 // Confirm a duplicate log entry does not exist. 493 // CustomAudienceServiceFilter ensures the failing assertion is logged internally. 494 verify(mAdServicesLoggerMock, never()) 495 .logFledgeApiCallStats( 496 eq(API_NAME), 497 eq(TEST_PACKAGE_NAME), 498 eq(STATUS_USER_CONSENT_REVOKED), 499 anyInt()); 500 } 501 502 @Test testImpl_invalidRequest_quotaExhausted_throws()503 public void testImpl_invalidRequest_quotaExhausted_throws() throws Exception { 504 // Use flag values with a clearly small quota limits. 505 mFetchCustomAudienceImpl = 506 getImplWithFlags( 507 new FetchCustomAudienceFlags() { 508 @Override 509 public long getFledgeCustomAudienceMaxOwnerCount() { 510 return 0; 511 } 512 513 @Override 514 public long getFledgeCustomAudienceMaxCount() { 515 return 0; 516 } 517 518 @Override 519 public long getFledgeCustomAudiencePerAppMaxCount() { 520 return 0; 521 } 522 }); 523 524 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 525 526 assertFalse(callback.mIsSuccess); 527 assertEquals(STATUS_INVALID_ARGUMENT, callback.mFledgeErrorResponse.getStatusCode()); 528 assertEquals( 529 String.format( 530 Locale.ENGLISH, 531 CUSTOM_AUDIENCE_QUANTITY_CHECK_FAILED, 532 ImmutableList.of( 533 THE_MAX_NUMBER_OF_CUSTOM_AUDIENCE_FOR_THE_DEVICE_HAD_REACHED, 534 THE_MAX_NUMBER_OF_CUSTOM_AUDIENCE_FOR_THE_OWNER_HAD_REACHED)), 535 callback.mFledgeErrorResponse.getErrorMessage()); 536 537 // Assert failure due to the invalid argument is logged. 538 verify(mAdServicesLoggerMock) 539 .logFledgeApiCallStats( 540 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_INVALID_ARGUMENT), anyInt()); 541 } 542 543 @Test testImpl_invalidRequest_invalidName_throws()544 public void testImpl_invalidRequest_invalidName_throws() throws Exception { 545 // Use flag value with a clearly small size limit. 546 mFetchCustomAudienceImpl = 547 getImplWithFlags( 548 new FetchCustomAudienceFlags() { 549 @Override 550 public int getFledgeCustomAudienceMaxNameSizeB() { 551 return 1; 552 } 553 }); 554 555 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 556 557 assertFalse(callback.mIsSuccess); 558 assertEquals(STATUS_INVALID_ARGUMENT, callback.mFledgeErrorResponse.getStatusCode()); 559 assertEquals( 560 String.format( 561 Locale.ENGLISH, 562 EXCEPTION_MESSAGE_FORMAT, 563 CLASS_NAME, 564 ImmutableList.of( 565 String.format( 566 Locale.ENGLISH, 567 VIOLATION_NAME_TOO_LONG, 568 1, 569 VALID_NAME.getBytes(StandardCharsets.UTF_8).length))), 570 callback.mFledgeErrorResponse.getErrorMessage()); 571 572 // Assert failure due to the invalid argument is logged. 573 verify(mAdServicesLoggerMock) 574 .logFledgeApiCallStats( 575 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_INVALID_ARGUMENT), anyInt()); 576 } 577 578 @Test testImpl_invalidRequest_invalidActivationTime_throws()579 public void testImpl_invalidRequest_invalidActivationTime_throws() throws Exception { 580 mInputBuilder.setActivationTime(INVALID_DELAYED_ACTIVATION_TIME); 581 582 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 583 584 assertFalse(callback.mIsSuccess); 585 assertEquals(STATUS_INVALID_ARGUMENT, callback.mFledgeErrorResponse.getStatusCode()); 586 assertEquals( 587 String.format( 588 Locale.ENGLISH, 589 EXCEPTION_MESSAGE_FORMAT, 590 CLASS_NAME, 591 ImmutableList.of( 592 String.format( 593 Locale.ENGLISH, 594 VIOLATION_ACTIVATE_AFTER_MAX_ACTIVATE, 595 CUSTOM_AUDIENCE_MAX_ACTIVATION_DELAY_IN, 596 FIXED_NOW_TRUNCATED_TO_MILLI, 597 INVALID_DELAYED_ACTIVATION_TIME), 598 String.format( 599 VIOLATION_EXPIRE_BEFORE_ACTIVATION, 600 INVALID_DELAYED_ACTIVATION_TIME, 601 VALID_EXPIRATION_TIME))), 602 callback.mFledgeErrorResponse.getErrorMessage()); 603 604 // Assert failure due to the invalid argument is logged. 605 verify(mAdServicesLoggerMock) 606 .logFledgeApiCallStats( 607 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_INVALID_ARGUMENT), anyInt()); 608 } 609 610 @Test testImpl_invalidRequest_invalidExpirationTime_throws()611 public void testImpl_invalidRequest_invalidExpirationTime_throws() throws Exception { 612 mInputBuilder.setExpirationTime(INVALID_BEYOND_MAX_EXPIRATION_TIME); 613 614 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 615 616 assertFalse(callback.mIsSuccess); 617 assertEquals(STATUS_INVALID_ARGUMENT, callback.mFledgeErrorResponse.getStatusCode()); 618 assertEquals( 619 String.format( 620 Locale.ENGLISH, 621 EXCEPTION_MESSAGE_FORMAT, 622 CLASS_NAME, 623 ImmutableList.of( 624 String.format( 625 Locale.ENGLISH, 626 VIOLATION_EXPIRE_AFTER_MAX_EXPIRE_TIME, 627 CUSTOM_AUDIENCE_MAX_EXPIRE_IN, 628 FIXED_NOW_TRUNCATED_TO_MILLI, 629 FIXED_NOW_TRUNCATED_TO_MILLI, 630 INVALID_BEYOND_MAX_EXPIRATION_TIME))), 631 callback.mFledgeErrorResponse.getErrorMessage()); 632 633 // Assert failure due to the invalid argument is logged. 634 verify(mAdServicesLoggerMock) 635 .logFledgeApiCallStats( 636 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_INVALID_ARGUMENT), anyInt()); 637 } 638 639 @Test testImpl_invalidRequest_invalidUserBiddingSignals_throws()640 public void testImpl_invalidRequest_invalidUserBiddingSignals_throws() throws Exception { 641 // Use flag value with a clearly small size limit. 642 mFetchCustomAudienceImpl = 643 getImplWithFlags( 644 new FetchCustomAudienceFlags() { 645 @Override 646 public int getFledgeFetchCustomAudienceMaxUserBiddingSignalsSizeB() { 647 return 1; 648 } 649 }); 650 651 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 652 653 assertFalse(callback.mIsSuccess); 654 assertEquals(STATUS_INVALID_ARGUMENT, callback.mFledgeErrorResponse.getStatusCode()); 655 assertEquals( 656 String.format( 657 Locale.ENGLISH, 658 EXCEPTION_MESSAGE_FORMAT, 659 CLASS_NAME, 660 ImmutableList.of( 661 String.format( 662 Locale.ENGLISH, 663 VIOLATION_USER_BIDDING_SIGNAL_TOO_BIG, 664 1, 665 VALID_USER_BIDDING_SIGNALS.getSizeInBytes()))), 666 callback.mFledgeErrorResponse.getErrorMessage()); 667 668 // Assert failure due to the invalid argument is logged. 669 verify(mAdServicesLoggerMock) 670 .logFledgeApiCallStats( 671 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_INVALID_ARGUMENT), anyInt()); 672 } 673 674 @Test testImpl_customHeaderExceedsLimit_throws()675 public void testImpl_customHeaderExceedsLimit_throws() throws Exception { 676 // Use flag value with a clearly small size limit. 677 mFetchCustomAudienceImpl = 678 getImplWithFlags( 679 new FetchCustomAudienceFlags() { 680 @Override 681 public int getFledgeFetchCustomAudienceMaxRequestCustomHeaderSizeB() { 682 return 1; 683 } 684 }); 685 686 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 687 688 assertFalse(callback.mIsSuccess); 689 assertEquals(STATUS_INVALID_ARGUMENT, callback.mFledgeErrorResponse.getStatusCode()); 690 assertEquals( 691 REQUEST_CUSTOM_HEADER_EXCEEDS_SIZE_LIMIT_MESSAGE, 692 callback.mFledgeErrorResponse.getErrorMessage()); 693 694 // Assert failure due to the invalid argument is logged. 695 verify(mAdServicesLoggerMock) 696 .logFledgeApiCallStats( 697 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_INVALID_ARGUMENT), anyInt()); 698 } 699 700 @Test testImpl_invalidResponse_invalidJSONObject()701 public void testImpl_invalidResponse_invalidJSONObject() throws Exception { 702 String jsonString = "Not[A]VALID[JSON]"; 703 MockWebServer mockWebServer = 704 mMockWebServerRule.startMockWebServer( 705 List.of(new MockResponse().setBody("Not[A]VALID[JSON]"))); 706 707 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 708 709 assertEquals(1, mockWebServer.getRequestCount()); 710 assertFalse(callback.mIsSuccess); 711 assertEquals(STATUS_INVALID_OBJECT, callback.mFledgeErrorResponse.getStatusCode()); 712 assertEquals( 713 "Value Not of type " 714 + jsonString.getClass().getName() 715 + " cannot be converted to JSONObject", 716 callback.mFledgeErrorResponse.getErrorMessage()); 717 718 // Assert failure due to the invalid response is logged. 719 verify(mAdServicesLoggerMock) 720 .logFledgeApiCallStats( 721 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_INVALID_OBJECT), anyInt()); 722 } 723 724 @Test testImpl_invalidFused_missingField()725 public void testImpl_invalidFused_missingField() throws Exception { 726 // Remove ads from the response, resulting in an incomplete fused custom audience. 727 JSONObject validResponse = 728 getFullSuccessfulJsonResponse(BUYER, /* auctionServerRequestFlagsEnabled= */ false); 729 validResponse.remove(ADS_KEY); 730 731 MockWebServer mockWebServer = 732 mMockWebServerRule.startMockWebServer( 733 List.of(new MockResponse().setBody(validResponse.toString()))); 734 735 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 736 737 assertEquals(1, mockWebServer.getRequestCount()); 738 assertFalse(callback.mIsSuccess); 739 assertEquals(STATUS_INVALID_OBJECT, callback.mFledgeErrorResponse.getStatusCode()); 740 assertEquals( 741 FUSED_CUSTOM_AUDIENCE_INCOMPLETE_MESSAGE, 742 callback.mFledgeErrorResponse.getErrorMessage()); 743 744 // Assert failure due to the invalid response is logged. 745 verify(mAdServicesLoggerMock) 746 .logFledgeApiCallStats( 747 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_INVALID_OBJECT), anyInt()); 748 } 749 750 @Test testImpl_invalidFused_missingFieldAuctionServerFlagsEnabled()751 public void testImpl_invalidFused_missingFieldAuctionServerFlagsEnabled() throws Exception { 752 enableAuctionServerRequestFlags(); 753 754 // Remove adsKey from the response, resulting in an incomplete fused custom audience. 755 JSONObject validResponse = 756 getFullSuccessfulJsonResponse(BUYER, /* auctionServerRequestFlagsEnabled= */ true); 757 validResponse.remove(ADS_KEY); 758 759 MockWebServer mockWebServer = 760 mMockWebServerRule.startMockWebServer( 761 List.of(new MockResponse().setBody(validResponse.toString()))); 762 763 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 764 765 assertEquals(1, mockWebServer.getRequestCount()); 766 assertFalse(callback.mIsSuccess); 767 assertEquals(STATUS_INVALID_OBJECT, callback.mFledgeErrorResponse.getStatusCode()); 768 assertEquals( 769 FUSED_CUSTOM_AUDIENCE_INCOMPLETE_MESSAGE, 770 callback.mFledgeErrorResponse.getErrorMessage()); 771 772 // Assert failure due to the invalid response is logged. 773 verify(mAdServicesLoggerMock) 774 .logFledgeApiCallStats( 775 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_INVALID_OBJECT), anyInt()); 776 } 777 778 @Test testImpl_invalidFused_invalidField()779 public void testImpl_invalidFused_invalidField() throws Exception { 780 // Replace buyer to cause mismatch. 781 JSONObject validResponse = getFullSuccessfulJsonResponse(BUYER, false); 782 validResponse.remove(DAILY_UPDATE_URI_KEY); 783 JSONObject invalidResponse = 784 addDailyUpdateUri( 785 validResponse, getValidDailyUpdateUriByBuyer(VALID_BUYER_2), false); 786 787 MockWebServer mockWebServer = 788 mMockWebServerRule.startMockWebServer( 789 List.of(new MockResponse().setBody(invalidResponse.toString()))); 790 791 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 792 793 assertEquals(1, mockWebServer.getRequestCount()); 794 assertFalse(callback.mIsSuccess); 795 assertEquals(STATUS_INVALID_ARGUMENT, callback.mFledgeErrorResponse.getStatusCode()); 796 assertEquals( 797 String.format( 798 Locale.ENGLISH, 799 EXCEPTION_MESSAGE_FORMAT, 800 CLASS_NAME, 801 ImmutableList.of( 802 String.format( 803 Locale.ENGLISH, 804 IDENTIFIER_AND_URI_ARE_INCONSISTENT, 805 AD_TECH_ROLE_BUYER, 806 BUYER, 807 AD_TECH_ROLE_BUYER, 808 DAILY_UPDATE_URI_FIELD_NAME, 809 VALID_BUYER_2))), 810 callback.mFledgeErrorResponse.getErrorMessage()); 811 812 // Assert failure due to the invalid response is logged. 813 verify(mAdServicesLoggerMock) 814 .logFledgeApiCallStats( 815 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_INVALID_ARGUMENT), anyInt()); 816 } 817 818 @Test testImpl_invalidFused_exceedsSizeLimit()819 public void testImpl_invalidFused_exceedsSizeLimit() throws Exception { 820 // Use flag value with a clearly small size limit. 821 mFetchCustomAudienceImpl = 822 getImplWithFlags( 823 new FetchCustomAudienceFlags() { 824 @Override 825 public int getFledgeFetchCustomAudienceMaxCustomAudienceSizeB() { 826 return 1; 827 } 828 }); 829 830 MockWebServer mockWebServer = 831 mMockWebServerRule.startMockWebServer( 832 List.of( 833 new MockResponse() 834 .setBody(getFullSuccessfulJsonResponseString(BUYER)))); 835 836 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 837 838 assertEquals(1, mockWebServer.getRequestCount()); 839 assertFalse(callback.mIsSuccess); 840 assertEquals(STATUS_INVALID_OBJECT, callback.mFledgeErrorResponse.getStatusCode()); 841 assertEquals( 842 FUSED_CUSTOM_AUDIENCE_EXCEEDS_SIZE_LIMIT_MESSAGE, 843 callback.mFledgeErrorResponse.getErrorMessage()); 844 845 // Assert failure due to the invalid response is logged. 846 verify(mAdServicesLoggerMock) 847 .logFledgeApiCallStats( 848 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_INVALID_OBJECT), anyInt()); 849 } 850 851 @Test testImpl_runNormally_partialResponse()852 public void testImpl_runNormally_partialResponse() throws Exception { 853 // Remove all fields from the response that the request itself has. 854 JSONObject partialResponse = getFullSuccessfulJsonResponse(BUYER, false); 855 partialResponse.remove(OWNER_KEY); 856 partialResponse.remove(BUYER_KEY); 857 partialResponse.remove(NAME_KEY); 858 partialResponse.remove(ACTIVATION_TIME_KEY); 859 partialResponse.remove(EXPIRATION_TIME_KEY); 860 partialResponse.remove(USER_BIDDING_SIGNALS_KEY); 861 862 MockWebServer mockWebServer = 863 mMockWebServerRule.startMockWebServer( 864 List.of(new MockResponse().setBody(partialResponse.toString()))); 865 866 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 867 868 assertEquals(1, mockWebServer.getRequestCount()); 869 assertTrue(callback.mIsSuccess); 870 verify(mCustomAudienceDaoMock) 871 .insertOrOverwriteCustomAudience( 872 FetchCustomAudienceFixture.getFullSuccessfulDBCustomAudience(), 873 getValidDailyUpdateUriByBuyer(BUYER), 874 DevContext.createForDevOptionsDisabled().getDevOptionsEnabled()); 875 verify(mAdServicesLoggerMock) 876 .logFledgeApiCallStats( 877 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_SUCCESS), anyInt()); 878 } 879 880 @Test testImpl_runNormally_discardedResponseValues()881 public void testImpl_runNormally_discardedResponseValues() throws Exception { 882 // Replace response name with a valid but different name from the original request. 883 JSONObject validResponse = getFullSuccessfulJsonResponse(BUYER, false); 884 validResponse.remove(NAME_KEY); 885 String validNameFromTheServer = VALID_NAME + "FromTheServer"; 886 validResponse = addName(validResponse, validNameFromTheServer, false); 887 888 MockWebServer mockWebServer = 889 mMockWebServerRule.startMockWebServer( 890 List.of(new MockResponse().setBody(validResponse.toString()))); 891 892 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 893 894 assertEquals(1, mockWebServer.getRequestCount()); 895 assertTrue(callback.mIsSuccess); 896 // Assert the response value is in fact discarded in favor of the request value. 897 verify(mCustomAudienceDaoMock) 898 .insertOrOverwriteCustomAudience( 899 FetchCustomAudienceFixture.getFullSuccessfulDBCustomAudience(), 900 getValidDailyUpdateUriByBuyer(BUYER), 901 DevContext.createForDevOptionsDisabled().getDevOptionsEnabled()); 902 verify(mAdServicesLoggerMock) 903 .logFledgeApiCallStats( 904 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_SUCCESS), anyInt()); 905 } 906 907 @Test testImpl_runNormally_completeResponse()908 public void testImpl_runNormally_completeResponse() throws Exception { 909 // Respond with a complete custom audience including the request values as is. 910 MockWebServer mockWebServer = 911 mMockWebServerRule.startMockWebServer( 912 List.of( 913 new MockResponse() 914 .setBody(getFullSuccessfulJsonResponseString(BUYER)))); 915 916 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 917 918 assertEquals(1, mockWebServer.getRequestCount()); 919 assertTrue(callback.mIsSuccess); 920 verify(mCustomAudienceDaoMock) 921 .insertOrOverwriteCustomAudience( 922 FetchCustomAudienceFixture.getFullSuccessfulDBCustomAudience(), 923 getValidDailyUpdateUriByBuyer(BUYER), 924 DevContext.createForDevOptionsDisabled().getDevOptionsEnabled()); 925 verify(mAdServicesLoggerMock) 926 .logFledgeApiCallStats( 927 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_SUCCESS), anyInt()); 928 } 929 930 @Test testImpl_runNormally_CallbackThrowsException()931 public void testImpl_runNormally_CallbackThrowsException() throws Exception { 932 // Respond with a complete custom audience including the request values as is. 933 MockWebServer mockWebServer = 934 mMockWebServerRule.startMockWebServer( 935 List.of( 936 new MockResponse() 937 .setBody(getFullSuccessfulJsonResponseString(BUYER)))); 938 939 FetchCustomAudienceTestCallback callback = 940 callFetchCustomAudienceWithErrorCallback(mInputBuilder.build(), 3); 941 942 assertEquals(1, mockWebServer.getRequestCount()); 943 assertTrue(callback.mIsSuccess); 944 verify(mAdServicesLoggerMock) 945 .logFledgeApiCallStats( 946 eq(API_NAME), 947 eq(TEST_PACKAGE_NAME), 948 eq(STATUS_CALLBACK_SHUTDOWN), 949 anyInt()); 950 } 951 952 @Test testImpl_runWithFailure_CallbackThrowsException()953 public void testImpl_runWithFailure_CallbackThrowsException() throws Exception { 954 955 // Just want the service to throw an exception so we trigger the failure callback 956 doThrow(new FledgeAuthorizationFilter.AdTechNotAllowedException()) 957 .when(mCustomAudienceServiceFilterMock) 958 .filterRequestAndExtractIdentifier( 959 mFetchUri, 960 VALID_OWNER, 961 false, 962 true, 963 true, 964 Process.myUid(), 965 API_NAME, 966 Throttler.ApiKey.FLEDGE_API_FETCH_CUSTOM_AUDIENCE, 967 DevContext.createForDevOptionsDisabled()); 968 969 FetchCustomAudienceTestCallback callback = 970 callFetchCustomAudienceWithErrorCallback(mInputBuilder.build(), 1); 971 972 assertFalse(callback.mIsSuccess); 973 verify(mAdServicesLoggerMock) 974 .logFledgeApiCallStats( 975 eq(API_NAME), 976 eq(TEST_PACKAGE_NAME), 977 eq(STATUS_CALLBACK_SHUTDOWN), 978 anyInt()); 979 } 980 981 @Test testImpl_runNormally_completeResponseWithAuctionServerFlagsEnabled()982 public void testImpl_runNormally_completeResponseWithAuctionServerFlagsEnabled() 983 throws Exception { 984 enableAuctionServerRequestFlags(); 985 986 // Respond with a complete custom audience including the request values as is and auction 987 // request flags 988 MockWebServer mockWebServer = 989 mMockWebServerRule.startMockWebServer( 990 List.of( 991 new MockResponse() 992 .setBody( 993 getFullSuccessfulJsonResponse(BUYER, true) 994 .toString()))); 995 996 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 997 998 assertEquals(1, mockWebServer.getRequestCount()); 999 assertTrue(callback.mIsSuccess); 1000 verify(mCustomAudienceDaoMock) 1001 .insertOrOverwriteCustomAudience( 1002 FetchCustomAudienceFixture 1003 .getFullSuccessfulDBCustomAudienceWithAuctionServerRequestFlags( 1004 FLAG_AUCTION_SERVER_REQUEST_OMIT_ADS), 1005 getValidDailyUpdateUriByBuyer(BUYER), 1006 DevContext.createForDevOptionsDisabled().getDevOptionsEnabled()); 1007 verify(mAdServicesLoggerMock) 1008 .logFledgeApiCallStats( 1009 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_SUCCESS), anyInt()); 1010 } 1011 1012 @Test testImpl_runNormally_completeResponseWithAuctionServerFlagsDisabled()1013 public void testImpl_runNormally_completeResponseWithAuctionServerFlagsDisabled() 1014 throws Exception { 1015 // Respond with a complete custom audience including the request values as is and auction 1016 // request flags 1017 MockWebServer mockWebServer = 1018 mMockWebServerRule.startMockWebServer( 1019 List.of( 1020 new MockResponse() 1021 .setBody( 1022 getFullSuccessfulJsonResponse(BUYER, true) 1023 .toString()))); 1024 1025 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 1026 1027 assertEquals(1, mockWebServer.getRequestCount()); 1028 assertTrue(callback.mIsSuccess); 1029 // Expect a CA without auction server request flags 1030 verify(mCustomAudienceDaoMock) 1031 .insertOrOverwriteCustomAudience( 1032 FetchCustomAudienceFixture.getFullSuccessfulDBCustomAudience(), 1033 getValidDailyUpdateUriByBuyer(BUYER), 1034 DevContext.createForDevOptionsDisabled().getDevOptionsEnabled()); 1035 verify(mAdServicesLoggerMock) 1036 .logFledgeApiCallStats( 1037 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_SUCCESS), anyInt()); 1038 } 1039 1040 @Test 1041 public void testImpl_runNormally_completeResponseWithAuctionServerFlagsEnabledButNoFlagsInResponse()1042 testImpl_runNormally_completeResponseWithAuctionServerFlagsEnabledButNoFlagsInResponse() 1043 throws Exception { 1044 enableAuctionServerRequestFlags(); 1045 1046 // Respond with a complete custom audience including the request values as is 1047 MockWebServer mockWebServer = 1048 mMockWebServerRule.startMockWebServer( 1049 List.of( 1050 new MockResponse() 1051 .setBody( 1052 getFullSuccessfulJsonResponse(BUYER, false) 1053 .toString()))); 1054 1055 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 1056 1057 assertEquals(1, mockWebServer.getRequestCount()); 1058 assertTrue(callback.mIsSuccess); 1059 // Expect a CA without auction server request flags 1060 verify(mCustomAudienceDaoMock) 1061 .insertOrOverwriteCustomAudience( 1062 FetchCustomAudienceFixture.getFullSuccessfulDBCustomAudience(), 1063 getValidDailyUpdateUriByBuyer(BUYER), 1064 DevContext.createForDevOptionsDisabled().getDevOptionsEnabled()); 1065 verify(mAdServicesLoggerMock) 1066 .logFledgeApiCallStats( 1067 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_SUCCESS), anyInt()); 1068 } 1069 1070 @Test testImpl_runNormally_withDevOptionsEnabled()1071 public void testImpl_runNormally_withDevOptionsEnabled() throws Exception { 1072 // Respond with a complete custom audience including the request values as is. 1073 MockWebServer mockWebServer = 1074 mMockWebServerRule.startMockWebServer( 1075 List.of( 1076 new MockResponse() 1077 .setBody(getFullSuccessfulJsonResponseString(BUYER)))); 1078 1079 CountDownLatch resultLatch = new CountDownLatch(1); 1080 FetchCustomAudienceTestCallback callback = new FetchCustomAudienceTestCallback(resultLatch); 1081 mFetchCustomAudienceImpl.doFetchCustomAudience( 1082 mInputBuilder.build(), 1083 callback, 1084 DevContext.builder().setDevOptionsEnabled(true).build()); 1085 resultLatch.await(); 1086 1087 assertEquals(1, mockWebServer.getRequestCount()); 1088 assertTrue(callback.mIsSuccess); 1089 verify(mCustomAudienceDaoMock) 1090 .insertOrOverwriteCustomAudience( 1091 FetchCustomAudienceFixture.getFullSuccessfulDBCustomAudience(), 1092 getValidDailyUpdateUriByBuyer(BUYER), 1093 /*debuggable=*/ true); 1094 verify(mAdServicesLoggerMock) 1095 .logFledgeApiCallStats( 1096 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_SUCCESS), anyInt()); 1097 } 1098 1099 @Test testImpl_runNormally_withAdRenderId()1100 public void testImpl_runNormally_withAdRenderId() throws Exception { 1101 // Enable server auction Ad Render Ids 1102 mFetchCustomAudienceImpl = 1103 getImplWithFlags( 1104 new FetchCustomAudienceFlags() { 1105 @Override 1106 public boolean getFledgeAuctionServerAdRenderIdEnabled() { 1107 return true; 1108 } 1109 }); 1110 1111 // Respond with a complete custom audience with ad render ids including the request values 1112 // as is. 1113 MockWebServer mockWebServer = 1114 mMockWebServerRule.startMockWebServer( 1115 List.of( 1116 new MockResponse() 1117 .setBody( 1118 getFullSuccessfulJsonResponseStringWithAdRenderId( 1119 BUYER)))); 1120 1121 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 1122 1123 assertEquals(1, mockWebServer.getRequestCount()); 1124 assertTrue(callback.mIsSuccess); 1125 verify(mCustomAudienceDaoMock) 1126 .insertOrOverwriteCustomAudience( 1127 FetchCustomAudienceFixture 1128 .getFullSuccessfulDBCustomAudienceWithAdRenderId(), 1129 getValidDailyUpdateUriByBuyer(BUYER), 1130 DevContext.createForDevOptionsDisabled().getDevOptionsEnabled()); 1131 verify(mAdServicesLoggerMock) 1132 .logFledgeApiCallStats( 1133 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_SUCCESS), anyInt()); 1134 } 1135 1136 @Test testImpl_withAdRenderId_onlyInvalidAdRenderIds_fail()1137 public void testImpl_withAdRenderId_onlyInvalidAdRenderIds_fail() throws Exception { 1138 // Enable server auction Ad Render Ids 1139 mFetchCustomAudienceImpl = 1140 getImplWithFlags( 1141 new FetchCustomAudienceFlags() { 1142 @Override 1143 public boolean getFledgeAuctionServerAdRenderIdEnabled() { 1144 return true; 1145 } 1146 1147 @Override 1148 public long getFledgeAuctionServerAdRenderIdMaxLength() { 1149 return 1; 1150 } 1151 }); 1152 1153 // Respond with a complete custom audience with ad render ids including the request values 1154 // as is. 1155 MockWebServer mockWebServer = 1156 mMockWebServerRule.startMockWebServer( 1157 List.of( 1158 new MockResponse() 1159 .setBody( 1160 getFullJsonResponseStringWithInvalidAdRenderId( 1161 BUYER)))); 1162 1163 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 1164 1165 assertEquals(1, mockWebServer.getRequestCount()); 1166 assertFalse(callback.mIsSuccess); 1167 assertEquals(STATUS_INVALID_ARGUMENT, callback.mFledgeErrorResponse.getStatusCode()); 1168 1169 // Assert failure due to the invalid response is logged. 1170 verify(mAdServicesLoggerMock) 1171 .logFledgeApiCallStats( 1172 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_INVALID_ARGUMENT), anyInt()); 1173 } 1174 1175 @Test testImpl_runNormally_withAdRenderId_AdRenderIdFlagDisabled()1176 public void testImpl_runNormally_withAdRenderId_AdRenderIdFlagDisabled() throws Exception { 1177 // Disable server auction Ad Render Ids 1178 mFetchCustomAudienceImpl = 1179 getImplWithFlags( 1180 new FetchCustomAudienceFlags() { 1181 @Override 1182 public boolean getFledgeAuctionServerAdRenderIdEnabled() { 1183 return false; 1184 } 1185 }); 1186 1187 // Respond with a complete custom audience with ad render ids including the request values 1188 // as is, but expect that ad render ids to not be read 1189 MockWebServer mockWebServer = 1190 mMockWebServerRule.startMockWebServer( 1191 List.of( 1192 new MockResponse() 1193 .setBody( 1194 getFullSuccessfulJsonResponseStringWithAdRenderId( 1195 BUYER)))); 1196 1197 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 1198 1199 ArgumentCaptor<DBCustomAudience> argumentDBCustomAudience = 1200 ArgumentCaptor.forClass(DBCustomAudience.class); 1201 1202 assertEquals(1, mockWebServer.getRequestCount()); 1203 assertTrue(callback.mIsSuccess); 1204 verify(mCustomAudienceDaoMock) 1205 .insertOrOverwriteCustomAudience( 1206 argumentDBCustomAudience.capture(), 1207 eq(getValidDailyUpdateUriByBuyer(BUYER)), 1208 eq(DevContext.createForDevOptionsDisabled().getDevOptionsEnabled())); 1209 DBCustomAudience dbCustomAudience = argumentDBCustomAudience.getValue(); 1210 Assert.assertNotEquals( 1211 FetchCustomAudienceFixture.getFullSuccessfulDBCustomAudienceWithAdRenderId(), 1212 dbCustomAudience); 1213 assertTrue(dbCustomAudience.getAds().stream().allMatch(ad -> ad.getAdRenderId() == null)); 1214 verify(mAdServicesLoggerMock) 1215 .logFledgeApiCallStats( 1216 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_SUCCESS), anyInt()); 1217 } 1218 1219 @Test testImpl_runNormally_differentResponsesToSameFetchUri()1220 public void testImpl_runNormally_differentResponsesToSameFetchUri() throws Exception { 1221 // Respond with a complete custom audience including the request values as is. 1222 JSONObject response1 = getFullSuccessfulJsonResponse(BUYER, false); 1223 Uri differentDailyUpdateUri = CommonFixture.getUri(BUYER, "/differentUpdate"); 1224 JSONObject response2 = 1225 getFullSuccessfulJsonResponse(BUYER, false) 1226 .put(DAILY_UPDATE_URI_KEY, differentDailyUpdateUri.toString()); 1227 1228 MockWebServer mockWebServer = 1229 mMockWebServerRule.startMockWebServer( 1230 new Dispatcher() { 1231 AtomicInteger mNumCalls = new AtomicInteger(0); 1232 1233 @Override 1234 public MockResponse dispatch(RecordedRequest request) { 1235 if (mNumCalls.get() == 0) { 1236 mNumCalls.addAndGet(1); 1237 return new MockResponse().setBody(response1.toString()); 1238 } else if (mNumCalls.get() == 1) { 1239 mNumCalls.addAndGet(1); 1240 return new MockResponse().setBody(response2.toString()); 1241 } else { 1242 throw new IllegalStateException("Expected only 2 calls!"); 1243 } 1244 } 1245 }); 1246 1247 FetchCustomAudienceTestCallback callback1 = callFetchCustomAudience(mInputBuilder.build()); 1248 assertTrue(callback1.mIsSuccess); 1249 verify(mCustomAudienceDaoMock) 1250 .insertOrOverwriteCustomAudience( 1251 FetchCustomAudienceFixture.getFullSuccessfulDBCustomAudience(), 1252 getValidDailyUpdateUriByBuyer(BUYER), 1253 DevContext.createForDevOptionsDisabled().getDevOptionsEnabled()); 1254 1255 FetchCustomAudienceTestCallback callback2 = callFetchCustomAudience(mInputBuilder.build()); 1256 assertTrue(callback2.mIsSuccess); 1257 verify(mCustomAudienceDaoMock) 1258 .insertOrOverwriteCustomAudience( 1259 FetchCustomAudienceFixture.getFullSuccessfulDBCustomAudience(), 1260 differentDailyUpdateUri, 1261 DevContext.createForDevOptionsDisabled().getDevOptionsEnabled()); 1262 1263 verify(mAdServicesLoggerMock, times(2)) 1264 .logFledgeApiCallStats( 1265 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_SUCCESS), anyInt()); 1266 } 1267 1268 @Test testImpl_requestRejectedByServer()1269 public void testImpl_requestRejectedByServer() throws Exception { 1270 // Respond with a 403 1271 MockWebServer mockWebServer = 1272 mMockWebServerRule.startMockWebServer( 1273 List.of(new MockResponse().setResponseCode(403))); 1274 1275 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 1276 assertEquals(1, mockWebServer.getRequestCount()); 1277 assertFalse(callback.mIsSuccess); 1278 assertEquals(STATUS_INTERNAL_ERROR, callback.mFledgeErrorResponse.getStatusCode()); 1279 verify(mCustomAudienceDaoMock, times(/* wantedNumberOfInvocations= */ 0)) 1280 .insertOrOverwriteCustomAudience(any(), any(), anyBoolean()); 1281 verify(mAdServicesLoggerMock) 1282 .logFledgeApiCallStats( 1283 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_INTERNAL_ERROR), anyInt()); 1284 } 1285 1286 @Test testImpl_AddsToQuarantineTableWhenServerReturns429()1287 public void testImpl_AddsToQuarantineTableWhenServerReturns429() throws Exception { 1288 // Respond with a 429 1289 MockWebServer mockWebServer = 1290 mMockWebServerRule.startMockWebServer( 1291 List.of(new MockResponse().setResponseCode(429))); 1292 1293 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 1294 assertEquals(1, mockWebServer.getRequestCount()); 1295 assertFalse(callback.mIsSuccess); 1296 assertEquals( 1297 STATUS_SERVER_RATE_LIMIT_REACHED, callback.mFledgeErrorResponse.getStatusCode()); 1298 verify(mCustomAudienceDaoMock) 1299 .safelyInsertCustomAudienceQuarantine( 1300 any(DBCustomAudienceQuarantine.class), anyLong()); 1301 verify(mAdServicesLoggerMock) 1302 .logFledgeApiCallStats( 1303 eq(API_NAME), 1304 eq(TEST_PACKAGE_NAME), 1305 eq(STATUS_SERVER_RATE_LIMIT_REACHED), 1306 anyInt()); 1307 } 1308 1309 @Test testImpl_ReturnsServerRateLimitReachedWhenEntryIsInQuarantineTable()1310 public void testImpl_ReturnsServerRateLimitReachedWhenEntryIsInQuarantineTable() 1311 throws Exception { 1312 doReturn(true) 1313 .when(mCustomAudienceDaoMock) 1314 .doesCustomAudienceQuarantineExist(VALID_OWNER, BUYER); 1315 // Return a time in the future so request gets filtered 1316 doReturn(CLOCK.instant().plusMillis(2 * DEFAULT_RETRY_AFTER_VALUE.toMillis())) 1317 .when(mCustomAudienceDaoMock) 1318 .getCustomAudienceQuarantineExpiration(VALID_OWNER, BUYER); 1319 1320 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 1321 assertFalse(callback.mIsSuccess); 1322 assertEquals( 1323 STATUS_SERVER_RATE_LIMIT_REACHED, callback.mFledgeErrorResponse.getStatusCode()); 1324 verify(mCustomAudienceDaoMock).doesCustomAudienceQuarantineExist(VALID_OWNER, BUYER); 1325 verify(mCustomAudienceDaoMock).getCustomAudienceQuarantineExpiration(VALID_OWNER, BUYER); 1326 verify(mAdServicesLoggerMock) 1327 .logFledgeApiCallStats( 1328 eq(API_NAME), 1329 eq(TEST_PACKAGE_NAME), 1330 eq(STATUS_SERVER_RATE_LIMIT_REACHED), 1331 anyInt()); 1332 } 1333 1334 @Test testImpl_SucceedsAndRemovesEntryFromQuarantineTable()1335 public void testImpl_SucceedsAndRemovesEntryFromQuarantineTable() throws Exception { 1336 doReturn(true) 1337 .when(mCustomAudienceDaoMock) 1338 .doesCustomAudienceQuarantineExist(VALID_OWNER, BUYER); 1339 // Return a time in the past so request is not filtered 1340 doReturn(CLOCK.instant().minusMillis(2 * DEFAULT_RETRY_AFTER_VALUE.toMillis())) 1341 .when(mCustomAudienceDaoMock) 1342 .getCustomAudienceQuarantineExpiration(VALID_OWNER, BUYER); 1343 1344 // Respond with a complete custom audience including the request values as is. 1345 MockWebServer mockWebServer = 1346 mMockWebServerRule.startMockWebServer( 1347 List.of( 1348 new MockResponse() 1349 .setBody(getFullSuccessfulJsonResponseString(BUYER)))); 1350 1351 FetchCustomAudienceTestCallback callback = callFetchCustomAudience(mInputBuilder.build()); 1352 assertEquals(1, mockWebServer.getRequestCount()); 1353 assertTrue(callback.mIsSuccess); 1354 1355 verify(mCustomAudienceDaoMock).doesCustomAudienceQuarantineExist(VALID_OWNER, BUYER); 1356 verify(mCustomAudienceDaoMock).getCustomAudienceQuarantineExpiration(VALID_OWNER, BUYER); 1357 verify(mCustomAudienceDaoMock).deleteQuarantineEntry(VALID_OWNER, BUYER); 1358 verify(mCustomAudienceDaoMock) 1359 .insertOrOverwriteCustomAudience( 1360 FetchCustomAudienceFixture.getFullSuccessfulDBCustomAudience(), 1361 getValidDailyUpdateUriByBuyer(BUYER), 1362 DevContext.createForDevOptionsDisabled().getDevOptionsEnabled()); 1363 1364 verify(mAdServicesLoggerMock) 1365 .logFledgeApiCallStats( 1366 eq(API_NAME), eq(TEST_PACKAGE_NAME), eq(STATUS_SUCCESS), anyInt()); 1367 } 1368 enableAuctionServerRequestFlags()1369 private void enableAuctionServerRequestFlags() { 1370 // Enable auction server request flags 1371 mFetchCustomAudienceImpl = 1372 getImplWithFlags( 1373 new FetchCustomAudienceFlags() { 1374 @Override 1375 public boolean getFledgeAuctionServerRequestFlagsEnabled() { 1376 return true; 1377 } 1378 }); 1379 } 1380 callFetchCustomAudience( FetchAndJoinCustomAudienceInput input)1381 private FetchCustomAudienceTestCallback callFetchCustomAudience( 1382 FetchAndJoinCustomAudienceInput input) throws Exception { 1383 CountDownLatch resultLatch = new CountDownLatch(1); 1384 FetchCustomAudienceTestCallback callback = new FetchCustomAudienceTestCallback(resultLatch); 1385 mFetchCustomAudienceImpl.doFetchCustomAudience( 1386 input, callback, DevContext.createForDevOptionsDisabled()); 1387 resultLatch.await(); 1388 return callback; 1389 } 1390 callFetchCustomAudienceWithErrorCallback( FetchAndJoinCustomAudienceInput input, int numCountDown)1391 private FetchCustomAudienceTestCallback callFetchCustomAudienceWithErrorCallback( 1392 FetchAndJoinCustomAudienceInput input, int numCountDown) throws Exception { 1393 CountDownLatch resultLatch = new CountDownLatch(numCountDown); 1394 Answer<Void> countDownAnswer = 1395 unused -> { 1396 resultLatch.countDown(); 1397 return null; 1398 }; 1399 doAnswer(countDownAnswer) 1400 .when(mAdServicesLoggerMock) 1401 .logFledgeApiCallStats(anyInt(), any(), anyInt(), anyInt()); 1402 FetchCustomAudienceTestThrowingCallback callback = 1403 new FetchCustomAudienceTestThrowingCallback(resultLatch); 1404 mFetchCustomAudienceImpl.doFetchCustomAudience( 1405 input, callback, DevContext.createForDevOptionsDisabled()); 1406 resultLatch.await(); 1407 return callback; 1408 } 1409 getImplWithFlags(Flags flags)1410 private FetchCustomAudienceImpl getImplWithFlags(Flags flags) { 1411 return new FetchCustomAudienceImpl( 1412 flags, 1413 CLOCK, 1414 mAdServicesLoggerMock, 1415 DIRECT_EXECUTOR, 1416 mCustomAudienceDaoMock, 1417 mCallingAppUid, 1418 mCustomAudienceServiceFilterMock, 1419 mHttpClientSpy, 1420 mAdFilteringFeatureFactory.getFrequencyCapAdDataValidator(), 1421 mAdRenderIdValidator, 1422 AD_DATA_CONVERSION_STRATEGY); 1423 } 1424 1425 public static class FetchCustomAudienceTestCallback 1426 extends FetchAndJoinCustomAudienceCallback.Stub { 1427 protected final CountDownLatch mCountDownLatch; 1428 boolean mIsSuccess = false; 1429 FledgeErrorResponse mFledgeErrorResponse; 1430 FetchCustomAudienceTestCallback(CountDownLatch countDownLatch)1431 public FetchCustomAudienceTestCallback(CountDownLatch countDownLatch) { 1432 mCountDownLatch = countDownLatch; 1433 } 1434 isSuccess()1435 public boolean isSuccess() { 1436 return mIsSuccess; 1437 } 1438 1439 @Override onSuccess()1440 public void onSuccess() throws RemoteException { 1441 LoggerFactory.getFledgeLogger() 1442 .v("Reporting success to FetchCustomAudienceTestCallback."); 1443 mIsSuccess = true; 1444 mCountDownLatch.countDown(); 1445 } 1446 1447 @Override onFailure(FledgeErrorResponse fledgeErrorResponse)1448 public void onFailure(FledgeErrorResponse fledgeErrorResponse) throws RemoteException { 1449 LoggerFactory.getFledgeLogger() 1450 .v("Reporting failure to FetchCustomAudienceTestCallback."); 1451 mFledgeErrorResponse = fledgeErrorResponse; 1452 mCountDownLatch.countDown(); 1453 } 1454 } 1455 1456 public static class FetchCustomAudienceTestThrowingCallback 1457 extends FetchCustomAudienceTestCallback { FetchCustomAudienceTestThrowingCallback(CountDownLatch countDownLatch)1458 public FetchCustomAudienceTestThrowingCallback(CountDownLatch countDownLatch) { 1459 super(countDownLatch); 1460 } 1461 1462 @Override onFailure(FledgeErrorResponse fledgeErrorResponse)1463 public void onFailure(FledgeErrorResponse fledgeErrorResponse) throws RemoteException { 1464 mFledgeErrorResponse = fledgeErrorResponse; 1465 mCountDownLatch.countDown(); 1466 throw new RemoteException(); 1467 } 1468 1469 @Override onSuccess()1470 public void onSuccess() throws RemoteException { 1471 mIsSuccess = true; 1472 mCountDownLatch.countDown(); 1473 throw new RemoteException(); 1474 } 1475 } 1476 1477 private static class FetchCustomAudienceFlags implements Flags { 1478 @Override getFledgeFetchCustomAudienceEnabled()1479 public boolean getFledgeFetchCustomAudienceEnabled() { 1480 return true; 1481 } 1482 1483 @Override getFledgeFrequencyCapFilteringEnabled()1484 public boolean getFledgeFrequencyCapFilteringEnabled() { 1485 return true; 1486 } 1487 1488 @Override getFledgeAppInstallFilteringEnabled()1489 public boolean getFledgeAppInstallFilteringEnabled() { 1490 return true; 1491 } 1492 } 1493 } 1494