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.service.adselection; 18 19 import static android.adservices.adselection.AdSelectionFromOutcomesConfigFixture.SAMPLE_SELLER; 20 import static android.adservices.common.AdServicesStatusUtils.STATUS_INVALID_ARGUMENT; 21 import static android.adservices.common.AdServicesStatusUtils.STATUS_TIMEOUT; 22 import static android.adservices.common.AdServicesStatusUtils.STATUS_USER_CONSENT_REVOKED; 23 24 import static com.android.adservices.common.CommonFlagsValues.EXTENDED_FLEDGE_AD_SELECTION_FROM_OUTCOMES_OVERALL_TIMEOUT_MS; 25 import static com.android.adservices.common.CommonFlagsValues.EXTENDED_FLEDGE_AD_SELECTION_SELECTING_OUTCOME_TIMEOUT_MS; 26 import static com.android.adservices.service.FlagsConstants.KEY_DISABLE_FLEDGE_ENROLLMENT_CHECK; 27 import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_AD_SELECTION_FROM_OUTCOMES_OVERALL_TIMEOUT_MS; 28 import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_AD_SELECTION_SELECTING_OUTCOME_TIMEOUT_MS; 29 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS_FROM_OUTCOMES; 30 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; 31 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; 32 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow; 33 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; 34 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; 35 36 import static com.google.common.truth.Truth.assertWithMessage; 37 38 import static org.mockito.ArgumentMatchers.any; 39 import static org.mockito.ArgumentMatchers.anyInt; 40 import static org.mockito.ArgumentMatchers.argThat; 41 import static org.mockito.Mockito.never; 42 43 import android.adservices.adselection.AdSelectionCallback; 44 import android.adservices.adselection.AdSelectionFromOutcomesConfig; 45 import android.adservices.adselection.AdSelectionFromOutcomesConfigFixture; 46 import android.adservices.adselection.AdSelectionFromOutcomesInput; 47 import android.adservices.adselection.AdSelectionResponse; 48 import android.adservices.adselection.CustomAudienceSignalsFixture; 49 import android.adservices.common.CommonFixture; 50 import android.adservices.common.FledgeErrorResponse; 51 import android.annotation.NonNull; 52 import android.net.Uri; 53 import android.os.Process; 54 import android.os.RemoteException; 55 56 import androidx.room.Room; 57 import androidx.test.core.app.ApplicationProvider; 58 59 import com.android.adservices.common.AdServicesExtendedMockitoTestCase; 60 import com.android.adservices.concurrency.AdServicesExecutors; 61 import com.android.adservices.data.adselection.AdSelectionDatabase; 62 import com.android.adservices.data.adselection.AdSelectionEntryDao; 63 import com.android.adservices.data.adselection.CustomAudienceSignals; 64 import com.android.adservices.data.adselection.DBAdSelection; 65 import com.android.adservices.data.adselection.datahandlers.AdSelectionResultBidAndUri; 66 import com.android.adservices.service.DebugFlags; 67 import com.android.adservices.service.Flags; 68 import com.android.adservices.service.FlagsFactory; 69 import com.android.adservices.service.common.AdSelectionServiceFilter; 70 import com.android.adservices.service.common.Throttler; 71 import com.android.adservices.service.consent.ConsentManager; 72 import com.android.adservices.service.devapi.DevContext; 73 import com.android.adservices.service.exception.FilterException; 74 import com.android.adservices.service.stats.AdServicesLogger; 75 import com.android.adservices.service.stats.AdServicesLoggerImpl; 76 import com.android.adservices.service.stats.SelectAdsFromOutcomesExecutionLogger; 77 import com.android.adservices.shared.testing.SkipLoggingUsageRule; 78 import com.android.adservices.shared.testing.annotations.SetFlagTrue; 79 import com.android.adservices.shared.testing.annotations.SetLongFlag; 80 import com.android.dx.mockito.inline.extended.ExtendedMockito; 81 import com.android.modules.utils.testing.ExtendedMockitoRule.SpyStatic; 82 83 import com.google.common.util.concurrent.ListenableFuture; 84 import com.google.common.util.concurrent.ListeningExecutorService; 85 86 import org.junit.Before; 87 import org.junit.Test; 88 import org.mockito.ArgumentMatcher; 89 import org.mockito.Mock; 90 91 import java.time.Instant; 92 import java.util.HashSet; 93 import java.util.List; 94 import java.util.concurrent.CountDownLatch; 95 import java.util.stream.Collectors; 96 97 @SpyStatic(FlagsFactory.class) 98 @SpyStatic(DebugFlags.class) 99 @SetLongFlag( 100 name = KEY_FLEDGE_AD_SELECTION_SELECTING_OUTCOME_TIMEOUT_MS, 101 value = EXTENDED_FLEDGE_AD_SELECTION_SELECTING_OUTCOME_TIMEOUT_MS) 102 @SetLongFlag( 103 name = KEY_FLEDGE_AD_SELECTION_FROM_OUTCOMES_OVERALL_TIMEOUT_MS, 104 value = EXTENDED_FLEDGE_AD_SELECTION_FROM_OUTCOMES_OVERALL_TIMEOUT_MS) 105 // TODO (b/384952360): refine CEL related verifications later 106 @SkipLoggingUsageRule(reason = "b/384952360") 107 public final class OutcomeSelectionRunnerTest extends AdServicesExtendedMockitoTestCase { 108 private static final int CALLER_UID = Process.myUid(); 109 private static final String MY_APP_PACKAGE_NAME = CommonFixture.TEST_PACKAGE_NAME; 110 private static final String ANOTHER_CALLER_PACKAGE_NAME = "another.caller.package"; 111 private static final Uri RENDER_URI_1 = Uri.parse("https://www.domain.com/advert1/"); 112 private static final Uri RENDER_URI_2 = Uri.parse("https://www.domain.com/advert2/"); 113 private static final Uri RENDER_URI_3 = Uri.parse("https://www.domain.com/advert3/"); 114 private static final long AD_SELECTION_ID_1 = 1; 115 private static final long AD_SELECTION_ID_2 = 2; 116 private static final long AD_SELECTION_ID_3 = 3; 117 private static final double BID_1 = 10.0; 118 private static final double BID_2 = 20.0; 119 private static final double BID_3 = 30.0; 120 private static final AdSelectionResultBidAndUri AD_SELECTION_WITH_BID_1 = 121 AdSelectionResultBidAndUri.builder() 122 .setAdSelectionId(AD_SELECTION_ID_1) 123 .setWinningAdBid(BID_1) 124 .setWinningAdRenderUri(RENDER_URI_1) 125 .build(); 126 private static final AdSelectionResultBidAndUri AD_SELECTION_WITH_BID_2 = 127 AdSelectionResultBidAndUri.builder() 128 .setAdSelectionId(AD_SELECTION_ID_2) 129 .setWinningAdBid(BID_2) 130 .setWinningAdRenderUri(RENDER_URI_2) 131 .build(); 132 private static final AdSelectionResultBidAndUri AD_SELECTION_WITH_BID_3 = 133 AdSelectionResultBidAndUri.builder() 134 .setAdSelectionId(AD_SELECTION_ID_3) 135 .setWinningAdBid(BID_3) 136 .setWinningAdRenderUri(RENDER_URI_3) 137 .build(); 138 139 private AdSelectionEntryDao mAdSelectionEntryDao; 140 @Mock private AdOutcomeSelector mAdOutcomeSelectorMock; 141 private OutcomeSelectionRunner mOutcomeSelectionRunner; 142 private final AdServicesLogger mAdServicesLoggerMock = 143 ExtendedMockito.mock(AdServicesLoggerImpl.class); 144 private ListeningExecutorService mBlockingExecutorService; 145 146 @Mock private AdSelectionServiceFilter mAdSelectionServiceFilter; 147 @Mock private SelectAdsFromOutcomesExecutionLogger mSelectAdsFromOutcomesExecutionLoggerMock; 148 149 @Before setup()150 public void setup() { 151 mBlockingExecutorService = AdServicesExecutors.getBlockingExecutor(); 152 153 mAdSelectionEntryDao = 154 Room.inMemoryDatabaseBuilder( 155 ApplicationProvider.getApplicationContext(), 156 AdSelectionDatabase.class) 157 .build() 158 .adSelectionEntryDao(); 159 mocker.mockGetDebugFlags(mFakeDebugFlags); 160 mOutcomeSelectionRunner = 161 new OutcomeSelectionRunner( 162 CALLER_UID, 163 mAdOutcomeSelectorMock, 164 mAdSelectionEntryDao, 165 mBlockingExecutorService, 166 AdServicesExecutors.getLightWeightExecutor(), 167 AdServicesExecutors.getScheduler(), 168 mAdServicesLoggerMock, 169 mContext, 170 mFakeFlags, 171 mFakeDebugFlags, 172 mAdSelectionServiceFilter, 173 DevContext.createForDevOptionsDisabled(), 174 false); 175 doNothing() 176 .when(mAdSelectionServiceFilter) 177 .filterRequest( 178 SAMPLE_SELLER, 179 MY_APP_PACKAGE_NAME, 180 true, 181 true, 182 true, 183 CALLER_UID, 184 AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS_FROM_OUTCOMES, 185 Throttler.ApiKey.FLEDGE_API_SELECT_ADS, 186 DevContext.createForDevOptionsDisabled()); 187 } 188 189 @Test testRunOutcomeSelectionInvalidAdSelectionConfigFromOutcomes()190 public void testRunOutcomeSelectionInvalidAdSelectionConfigFromOutcomes() { 191 List<AdSelectionResultBidAndUri> adSelectionIdWithBidAndRenderUris = 192 List.of(AD_SELECTION_WITH_BID_1, AD_SELECTION_WITH_BID_2, AD_SELECTION_WITH_BID_3); 193 persistAdSelectionEntry(adSelectionIdWithBidAndRenderUris.get(0), MY_APP_PACKAGE_NAME); 194 // Not persisting index 1 195 // Persisting index 2 with a different package name 196 persistAdSelectionEntry( 197 adSelectionIdWithBidAndRenderUris.get(2), ANOTHER_CALLER_PACKAGE_NAME); 198 199 List<Long> adOutcomesConfigParam = 200 adSelectionIdWithBidAndRenderUris.stream() 201 .map(AdSelectionResultBidAndUri::getAdSelectionId) 202 .collect(Collectors.toList()); 203 204 AdSelectionFromOutcomesConfig config = 205 AdSelectionFromOutcomesConfigFixture.anAdSelectionFromOutcomesConfig( 206 adOutcomesConfigParam); 207 208 AdSelectionTestCallback resultsCallback = 209 invokeRunAdSelectionFromOutcomes( 210 mOutcomeSelectionRunner, 211 config, 212 MY_APP_PACKAGE_NAME, 213 mSelectAdsFromOutcomesExecutionLoggerMock); 214 215 var unused = 216 verify(mAdOutcomeSelectorMock, never()).runAdOutcomeSelector(any(), any(), any()); 217 expect.that(resultsCallback.mIsSuccess).isFalse(); 218 expect.that(STATUS_INVALID_ARGUMENT) 219 .isEqualTo(resultsCallback.mFledgeErrorResponse.getStatusCode()); 220 verify(mAdServicesLoggerMock) 221 .logFledgeApiCallStats( 222 eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS_FROM_OUTCOMES), 223 eq(MY_APP_PACKAGE_NAME), 224 eq(STATUS_INVALID_ARGUMENT), 225 anyInt()); 226 } 227 228 @Test testRunOutcomeSelectionRevokedUserConsentEmptyResult()229 public void testRunOutcomeSelectionRevokedUserConsentEmptyResult() { 230 doThrow(new FilterException(new ConsentManager.RevokedConsentException())) 231 .when(mAdSelectionServiceFilter) 232 .filterRequest( 233 SAMPLE_SELLER, 234 MY_APP_PACKAGE_NAME, 235 true, 236 true, 237 true, 238 CALLER_UID, 239 AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS_FROM_OUTCOMES, 240 Throttler.ApiKey.FLEDGE_API_SELECT_ADS, 241 DevContext.createForDevOptionsDisabled()); 242 243 List<AdSelectionResultBidAndUri> adSelectionIdWithBidAndRenderUris = 244 List.of(AD_SELECTION_WITH_BID_1, AD_SELECTION_WITH_BID_2, AD_SELECTION_WITH_BID_3); 245 for (AdSelectionResultBidAndUri idWithBid : adSelectionIdWithBidAndRenderUris) { 246 persistAdSelectionEntry(idWithBid, MY_APP_PACKAGE_NAME); 247 } 248 249 List<Long> adOutcomesConfigParam = 250 adSelectionIdWithBidAndRenderUris.stream() 251 .map(AdSelectionResultBidAndUri::getAdSelectionId) 252 .collect(Collectors.toList()); 253 254 AdSelectionFromOutcomesConfig config = 255 AdSelectionFromOutcomesConfigFixture.anAdSelectionFromOutcomesConfig( 256 adOutcomesConfigParam); 257 258 AdSelectionTestCallback resultsCallback = 259 invokeRunAdSelectionFromOutcomes( 260 mOutcomeSelectionRunner, 261 config, 262 MY_APP_PACKAGE_NAME, 263 mSelectAdsFromOutcomesExecutionLoggerMock); 264 265 var unused = 266 verify(mAdOutcomeSelectorMock, never()).runAdOutcomeSelector(any(), any(), any()); 267 expect.that(resultsCallback.mIsSuccess).isTrue(); 268 expect.that(resultsCallback.mAdSelectionResponse).isNull(); 269 270 // Confirm a duplicate log entry does not exist. 271 // AdSelectionServiceFilter ensures the failing assertion is logged internally. 272 verify(mAdServicesLoggerMock, never()) 273 .logFledgeApiCallStats( 274 eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS_FROM_OUTCOMES), 275 eq(MY_APP_PACKAGE_NAME), 276 eq(STATUS_USER_CONSENT_REVOKED), 277 anyInt()); 278 } 279 280 @Test testRunOutcomeSelectionRevokedUserConsentEmptyResult_UXNotificationNotEnforced()281 public void testRunOutcomeSelectionRevokedUserConsentEmptyResult_UXNotificationNotEnforced() { 282 mockGetConsentNotificationDebugMode(true); 283 284 doThrow(new FilterException(new ConsentManager.RevokedConsentException())) 285 .when(mAdSelectionServiceFilter) 286 .filterRequest( 287 SAMPLE_SELLER, 288 MY_APP_PACKAGE_NAME, 289 true, 290 true, 291 false, 292 CALLER_UID, 293 AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS_FROM_OUTCOMES, 294 Throttler.ApiKey.FLEDGE_API_SELECT_ADS, 295 DevContext.createForDevOptionsDisabled()); 296 297 List<AdSelectionResultBidAndUri> adSelectionIdWithBidAndRenderUris = 298 List.of(AD_SELECTION_WITH_BID_1, AD_SELECTION_WITH_BID_2, AD_SELECTION_WITH_BID_3); 299 for (AdSelectionResultBidAndUri idWithBid : adSelectionIdWithBidAndRenderUris) { 300 persistAdSelectionEntry(idWithBid, MY_APP_PACKAGE_NAME); 301 } 302 303 List<Long> adOutcomesConfigParam = 304 adSelectionIdWithBidAndRenderUris.stream() 305 .map(AdSelectionResultBidAndUri::getAdSelectionId) 306 .collect(Collectors.toList()); 307 308 AdSelectionFromOutcomesConfig config = 309 AdSelectionFromOutcomesConfigFixture.anAdSelectionFromOutcomesConfig( 310 adOutcomesConfigParam); 311 312 OutcomeSelectionRunner outcomeSelectionRunner = 313 new OutcomeSelectionRunner( 314 CALLER_UID, 315 mAdOutcomeSelectorMock, 316 mAdSelectionEntryDao, 317 mBlockingExecutorService, 318 AdServicesExecutors.getLightWeightExecutor(), 319 AdServicesExecutors.getScheduler(), 320 mAdServicesLoggerMock, 321 mContext, 322 mFakeFlags, 323 mFakeDebugFlags, 324 mAdSelectionServiceFilter, 325 DevContext.createForDevOptionsDisabled(), 326 false); 327 328 AdSelectionTestCallback resultsCallback = 329 invokeRunAdSelectionFromOutcomes( 330 outcomeSelectionRunner, 331 config, 332 MY_APP_PACKAGE_NAME, 333 mSelectAdsFromOutcomesExecutionLoggerMock); 334 335 var unused = 336 verify(mAdOutcomeSelectorMock, never()).runAdOutcomeSelector(any(), any(), any()); 337 expect.that(resultsCallback.mIsSuccess).isTrue(); 338 expect.that(resultsCallback.mAdSelectionResponse).isNull(); 339 340 // Confirm a duplicate log entry does not exist. 341 // AdSelectionServiceFilter ensures the failing assertion is logged internally. 342 verify(mAdServicesLoggerMock, never()) 343 .logFledgeApiCallStats( 344 eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS_FROM_OUTCOMES), 345 eq(MY_APP_PACKAGE_NAME), 346 eq(STATUS_USER_CONSENT_REVOKED), 347 anyInt()); 348 349 verify(mAdSelectionServiceFilter) 350 .filterRequest( 351 SAMPLE_SELLER, 352 MY_APP_PACKAGE_NAME, 353 true, 354 true, 355 false, 356 CALLER_UID, 357 AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS_FROM_OUTCOMES, 358 Throttler.ApiKey.FLEDGE_API_SELECT_ADS, 359 DevContext.createForDevOptionsDisabled()); 360 } 361 362 @Test 363 @SetLongFlag(name = KEY_FLEDGE_AD_SELECTION_SELECTING_OUTCOME_TIMEOUT_MS, value = 300) 364 @SetLongFlag(name = KEY_FLEDGE_AD_SELECTION_FROM_OUTCOMES_OVERALL_TIMEOUT_MS, value = 100) 365 @SetFlagTrue(KEY_DISABLE_FLEDGE_ENROLLMENT_CHECK) testRunOutcomeSelectionOrchestrationTimeoutFailure()366 public void testRunOutcomeSelectionOrchestrationTimeoutFailure() { 367 List<AdSelectionResultBidAndUri> adSelectionIdWithBidAndRenderUris = 368 List.of(AD_SELECTION_WITH_BID_1, AD_SELECTION_WITH_BID_2, AD_SELECTION_WITH_BID_3); 369 for (AdSelectionResultBidAndUri idWithBid : adSelectionIdWithBidAndRenderUris) { 370 persistAdSelectionEntry(idWithBid, MY_APP_PACKAGE_NAME); 371 } 372 373 List<Long> adOutcomesConfigParam = 374 adSelectionIdWithBidAndRenderUris.stream() 375 .map(AdSelectionResultBidAndUri::getAdSelectionId) 376 .collect(Collectors.toList()); 377 378 AdSelectionFromOutcomesConfig config = 379 AdSelectionFromOutcomesConfigFixture.anAdSelectionFromOutcomesConfig( 380 adOutcomesConfigParam); 381 382 GenericListMatcher matcher = new GenericListMatcher(adSelectionIdWithBidAndRenderUris); 383 doAnswer((ignored) -> getSelectedOutcomeWithDelay(AD_SELECTION_ID_1, mFakeFlags)) 384 .when(mAdOutcomeSelectorMock) 385 .runAdOutcomeSelector( 386 argThat(matcher), 387 eq(config), 388 eq(mSelectAdsFromOutcomesExecutionLoggerMock)); 389 390 OutcomeSelectionRunner outcomeSelectionRunner = 391 new OutcomeSelectionRunner( 392 CALLER_UID, 393 mAdOutcomeSelectorMock, 394 mAdSelectionEntryDao, 395 mBlockingExecutorService, 396 AdServicesExecutors.getLightWeightExecutor(), 397 AdServicesExecutors.getScheduler(), 398 mAdServicesLoggerMock, 399 mContext, 400 mFakeFlags, 401 mFakeDebugFlags, 402 mAdSelectionServiceFilter, 403 DevContext.createForDevOptionsDisabled(), 404 false); 405 406 AdSelectionTestCallback resultsCallback = 407 invokeRunAdSelectionFromOutcomes( 408 outcomeSelectionRunner, 409 config, 410 MY_APP_PACKAGE_NAME, 411 mSelectAdsFromOutcomesExecutionLoggerMock); 412 413 var unused = verify(mAdOutcomeSelectorMock).runAdOutcomeSelector(any(), any(), any()); 414 expect.that(resultsCallback.mIsSuccess).isFalse(); 415 assertWithMessage("mFledgeErrorResponse") 416 .that(resultsCallback.mFledgeErrorResponse) 417 .isNotNull(); 418 expect.that(STATUS_TIMEOUT).isEqualTo(resultsCallback.mFledgeErrorResponse.getStatusCode()); 419 verify(mAdServicesLoggerMock) 420 .logFledgeApiCallStats( 421 eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS_FROM_OUTCOMES), 422 eq(MY_APP_PACKAGE_NAME), 423 eq(STATUS_TIMEOUT), 424 anyInt()); 425 } 426 persistAdSelectionEntry( AdSelectionResultBidAndUri idWithBidAndRenderUri, String callerPackageName)427 private void persistAdSelectionEntry( 428 AdSelectionResultBidAndUri idWithBidAndRenderUri, String callerPackageName) { 429 final Uri biddingLogicUri1 = Uri.parse("https://www.domain.com/logic/1"); 430 final Instant activationTime = Instant.now(); 431 final String contextualSignals = "contextual_signals"; 432 final CustomAudienceSignals customAudienceSignals = 433 CustomAudienceSignalsFixture.aCustomAudienceSignals(); 434 435 final DBAdSelection dbAdSelectionEntry = 436 new DBAdSelection.Builder() 437 .setAdSelectionId(idWithBidAndRenderUri.getAdSelectionId()) 438 .setCustomAudienceSignals(customAudienceSignals) 439 .setBuyerContextualSignals(contextualSignals) 440 .setBiddingLogicUri(biddingLogicUri1) 441 .setWinningAdRenderUri(idWithBidAndRenderUri.getWinningAdRenderUri()) 442 .setWinningAdBid(idWithBidAndRenderUri.getWinningAdBid()) 443 .setCreationTimestamp(activationTime) 444 .setCallerPackageName(callerPackageName) 445 .build(); 446 mAdSelectionEntryDao.persistAdSelection(dbAdSelectionEntry); 447 } 448 invokeRunAdSelectionFromOutcomes( OutcomeSelectionRunner outcomeSelectionRunner, AdSelectionFromOutcomesConfig config, String callerPackageName, SelectAdsFromOutcomesExecutionLogger selectAdsFromOutcomesExecutionLogger)449 private OutcomeSelectionRunnerTest.AdSelectionTestCallback invokeRunAdSelectionFromOutcomes( 450 OutcomeSelectionRunner outcomeSelectionRunner, 451 AdSelectionFromOutcomesConfig config, 452 String callerPackageName, 453 SelectAdsFromOutcomesExecutionLogger selectAdsFromOutcomesExecutionLogger) { 454 455 // Counted down in the callback 456 CountDownLatch countDownLatch = new CountDownLatch(1); 457 OutcomeSelectionRunnerTest.AdSelectionTestCallback adSelectionTestCallback = 458 new OutcomeSelectionRunnerTest.AdSelectionTestCallback(countDownLatch); 459 460 AdSelectionFromOutcomesInput input = 461 new AdSelectionFromOutcomesInput.Builder() 462 .setAdSelectionFromOutcomesConfig(config) 463 .setCallerPackageName(callerPackageName) 464 .build(); 465 466 outcomeSelectionRunner.runOutcomeSelection( 467 input, adSelectionTestCallback, selectAdsFromOutcomesExecutionLogger); 468 try { 469 adSelectionTestCallback.mCountDownLatch.await(); 470 } catch (InterruptedException e) { 471 e.printStackTrace(); 472 } 473 return adSelectionTestCallback; 474 } 475 getSelectedOutcomeWithDelay( Long outcomeId, @NonNull Flags flags)476 private ListenableFuture<Long> getSelectedOutcomeWithDelay( 477 Long outcomeId, @NonNull Flags flags) { 478 return mBlockingExecutorService.submit( 479 () -> { 480 Thread.sleep(2 * flags.getAdSelectionFromOutcomesOverallTimeoutMs()); 481 return outcomeId; 482 }); 483 } 484 485 private static final class AdSelectionTestCallback extends AdSelectionCallback.Stub { 486 487 final CountDownLatch mCountDownLatch; 488 boolean mIsSuccess = false; 489 AdSelectionResponse mAdSelectionResponse; 490 FledgeErrorResponse mFledgeErrorResponse; 491 AdSelectionTestCallback(CountDownLatch countDownLatch)492 AdSelectionTestCallback(CountDownLatch countDownLatch) { 493 mCountDownLatch = countDownLatch; 494 mAdSelectionResponse = null; 495 mFledgeErrorResponse = null; 496 } 497 498 @Override onSuccess(AdSelectionResponse adSelectionResponse)499 public void onSuccess(AdSelectionResponse adSelectionResponse) throws RemoteException { 500 mIsSuccess = true; 501 mAdSelectionResponse = adSelectionResponse; 502 mCountDownLatch.countDown(); 503 } 504 505 @Override onFailure(FledgeErrorResponse fledgeErrorResponse)506 public void onFailure(FledgeErrorResponse fledgeErrorResponse) throws RemoteException { 507 mIsSuccess = false; 508 mFledgeErrorResponse = fledgeErrorResponse; 509 mCountDownLatch.countDown(); 510 } 511 } 512 513 private static final class GenericListMatcher 514 implements ArgumentMatcher<List<AdSelectionResultBidAndUri>> { 515 private final List<AdSelectionResultBidAndUri> mTruth; 516 GenericListMatcher(List<AdSelectionResultBidAndUri> truth)517 GenericListMatcher(List<AdSelectionResultBidAndUri> truth) { 518 this.mTruth = truth; 519 } 520 521 @Override matches(List<AdSelectionResultBidAndUri> argument)522 public boolean matches(List<AdSelectionResultBidAndUri> argument) { 523 return mTruth.size() == argument.size() 524 && new HashSet<>(mTruth).equals(new HashSet<>(argument)); 525 } 526 } 527 } 528