blob: 964e790a49f2bba71fc49de5eebda8453d01058b [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.adservices.service.adselection;
import static android.adservices.adselection.AdSelectionFromOutcomesConfigFixture.SAMPLE_SELLER;
import static android.adservices.common.AdServicesStatusUtils.STATUS_INTERNAL_ERROR;
import static android.adservices.common.AdServicesStatusUtils.STATUS_INVALID_ARGUMENT;
import static com.android.adservices.data.adselection.AdSelectionDatabase.DATABASE_NAME;
import static com.android.adservices.service.adselection.OutcomeSelectionRunner.SELECTED_OUTCOME_MUST_BE_ONE_OF_THE_INPUTS;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.DB_AD_SELECTION_FILE_SIZE;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import android.adservices.adselection.AdSelectionCallback;
import android.adservices.adselection.AdSelectionFromOutcomesConfig;
import android.adservices.adselection.AdSelectionFromOutcomesConfigFixture;
import android.adservices.adselection.AdSelectionFromOutcomesInput;
import android.adservices.adselection.AdSelectionResponse;
import android.adservices.adselection.CustomAudienceSignalsFixture;
import android.adservices.common.AdSelectionSignals;
import android.adservices.common.AdTechIdentifier;
import android.adservices.common.CallerMetadata;
import android.adservices.common.CallingAppUidSupplierProcessImpl;
import android.adservices.common.CommonFixture;
import android.adservices.common.FledgeErrorResponse;
import android.adservices.http.MockWebServerRule;
import android.content.Context;
import android.net.Uri;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import androidx.room.Room;
import androidx.test.core.app.ApplicationProvider;
import com.android.adservices.MockWebServerRuleFactory;
import com.android.adservices.concurrency.AdServicesExecutors;
import com.android.adservices.data.DbTestUtil;
import com.android.adservices.data.adselection.AdSelectionDatabase;
import com.android.adservices.data.adselection.AdSelectionEntryDao;
import com.android.adservices.data.adselection.AppInstallDao;
import com.android.adservices.data.adselection.CustomAudienceSignals;
import com.android.adservices.data.adselection.DBAdSelection;
import com.android.adservices.data.adselection.FrequencyCapDao;
import com.android.adservices.data.adselection.SharedStorageDatabase;
import com.android.adservices.data.customaudience.CustomAudienceDao;
import com.android.adservices.data.customaudience.CustomAudienceDatabase;
import com.android.adservices.data.customaudience.DBCustomAudience;
import com.android.adservices.data.enrollment.EnrollmentDao;
import com.android.adservices.service.Flags;
import com.android.adservices.service.FlagsFactory;
import com.android.adservices.service.common.AdSelectionServiceFilter;
import com.android.adservices.service.common.FledgeAuthorizationFilter;
import com.android.adservices.service.common.Throttler;
import com.android.adservices.service.common.cache.CacheProviderFactory;
import com.android.adservices.service.common.httpclient.AdServicesHttpsClient;
import com.android.adservices.service.consent.ConsentManager;
import com.android.adservices.service.devapi.DevContext;
import com.android.adservices.service.devapi.DevContextFilter;
import com.android.adservices.service.js.JSScriptEngine;
import com.android.adservices.service.stats.AdServicesLogger;
import com.android.adservices.service.stats.AdServicesLoggerImpl;
import com.android.adservices.service.stats.AdServicesStatsLog;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.google.mockwebserver.Dispatcher;
import com.google.mockwebserver.MockResponse;
import com.google.mockwebserver.MockWebServer;
import com.google.mockwebserver.RecordedRequest;
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoSession;
import org.mockito.Spy;
import org.mockito.quality.Strictness;
import java.io.File;
import java.time.Instant;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
public class AdSelectionFromOutcomesE2ETest {
private static final int CALLER_UID = Process.myUid();
private static final String SELECTION_PICK_HIGHEST_LOGIC_JS_PATH = "/selectionPickHighestJS/";
private static final String SELECTION_PICK_NONE_LOGIC_JS_PATH = "/selectionPickNoneJS/";
private static final String SELECTION_WATERFALL_LOGIC_JS_PATH = "/selectionWaterfallJS/";
private static final String SELECTION_FAULTY_LOGIC_JS_PATH = "/selectionFaultyJS/";
private static final String SELECTION_PICK_HIGHEST_LOGIC_JS =
"function selectOutcome(outcomes, selection_signals) {\n"
+ " let max_bid = 0;\n"
+ " let winner_outcome = null;\n"
+ " for (let outcome of outcomes) {\n"
+ " if (outcome.bid > max_bid) {\n"
+ " max_bid = outcome.bid;\n"
+ " winner_outcome = outcome;\n"
+ " }\n"
+ " }\n"
+ " return {'status': 0, 'result': winner_outcome};\n"
+ "}";
private static final String SELECTION_PICK_NONE_LOGIC_JS =
"function selectOutcome(outcomes, selection_signals) {\n"
+ " return {'status': 0, 'result': null};\n"
+ "}";
private static final String SELECTION_WATERFALL_LOGIC_JS =
"function selectOutcome(outcomes, selection_signals) {\n"
+ " if (outcomes.length != 1 || selection_signals.bid_floor =="
+ " undefined) return null;\n"
+ "\n"
+ " const outcome_1p = outcomes[0];\n"
+ " return {'status': 0, 'result': (outcome_1p.bid >"
+ " selection_signals.bid_floor) ? outcome_1p : null};\n"
+ "}";
private static final String SELECTION_FAULTY_LOGIC_JS =
"function selectOutcome(outcomes, selection_signals) {\n"
+ " return {'status': 0, 'result': {\"id\": outcomes[0].id + 1, \"bid\": "
+ "outcomes[0].bid}};\n"
+ "}";
private static final String BID_FLOOR_SELECTION_SIGNAL_TEMPLATE = "{\"bid_floor\":%s}";
private static final AdTechIdentifier SELLER_INCONSISTENT_WITH_SELECTION_URI =
AdTechIdentifier.fromString("inconsistent.developer.android.com");
private static final long BINDER_ELAPSED_TIME_MS = 100L;
private static final String CALLER_PACKAGE_NAME = CommonFixture.TEST_PACKAGE_NAME;
private static final long AD_SELECTION_ID_1 = 12345L;
private static final long AD_SELECTION_ID_2 = 123456L;
private static final long AD_SELECTION_ID_3 = 1234567L;
private final AdServicesLogger mAdServicesLoggerMock =
ExtendedMockito.mock(AdServicesLoggerImpl.class);
private final Flags mFlags = new AdSelectionFromOutcomesE2ETest.TestFlags();
@Rule public MockWebServerRule mMockWebServerRule = MockWebServerRuleFactory.createForHttps();
// Mocking DevContextFilter to test behavior with and without override api authorization
@Mock DevContextFilter mDevContextFilter;
@Mock CallerMetadata mMockCallerMetadata;
@Spy private Context mContext = ApplicationProvider.getApplicationContext();
@Mock private File mMockDBAdSelectionFile;
@Mock private ConsentManager mConsentManagerMock;
FledgeAuthorizationFilter mFledgeAuthorizationFilter =
new FledgeAuthorizationFilter(
mContext.getPackageManager(),
new EnrollmentDao(mContext, DbTestUtil.getDbHelperForTest()),
mAdServicesLoggerMock);
private MockitoSession mStaticMockSession = null;
private ExecutorService mLightweightExecutorService;
private ExecutorService mBackgroundExecutorService;
private ScheduledThreadPoolExecutor mScheduledExecutor;
private CustomAudienceDao mCustomAudienceDao;
private AppInstallDao mAppInstallDao;
private FrequencyCapDao mFrequencyCapDao;
@Spy private AdSelectionEntryDao mAdSelectionEntryDaoSpy;
private AdServicesHttpsClient mAdServicesHttpsClient;
private AdSelectionServiceImpl mAdSelectionService;
private Dispatcher mDispatcher;
private AdFilteringFeatureFactory mAdFilteringFeatureFactory;
@Mock private AdSelectionServiceFilter mAdSelectionServiceFilter;
@Before
public void setUp() throws Exception {
// Every test in this class requires that the JS Sandbox be available. The JS Sandbox
// availability depends on an external component (the system webview) being higher than a
// certain minimum version. Marking that as an assumption that the test is making.
Assume.assumeTrue(JSScriptEngine.AvailabilityChecker.isJSSandboxAvailable());
mAdSelectionEntryDaoSpy =
Room.inMemoryDatabaseBuilder(mContext, AdSelectionDatabase.class)
.build()
.adSelectionEntryDao();
// Test applications don't have the required permissions to read config P/H flags, and
// injecting mocked flags everywhere is annoying and non-trivial for static methods
mStaticMockSession =
ExtendedMockito.mockitoSession()
.spyStatic(FlagsFactory.class)
.initMocks(this)
.strictness(Strictness.LENIENT)
.startMocking();
// Initialize dependencies for the AdSelectionService
mLightweightExecutorService = AdServicesExecutors.getLightWeightExecutor();
mBackgroundExecutorService = AdServicesExecutors.getBackgroundExecutor();
mScheduledExecutor = AdServicesExecutors.getScheduler();
mCustomAudienceDao =
Room.inMemoryDatabaseBuilder(mContext, CustomAudienceDatabase.class)
.addTypeConverter(new DBCustomAudience.Converters(true))
.build()
.customAudienceDao();
SharedStorageDatabase sharedDb =
Room.inMemoryDatabaseBuilder(mContext, SharedStorageDatabase.class).build();
mAppInstallDao = sharedDb.appInstallDao();
mFrequencyCapDao = sharedDb.frequencyCapDao();
mAdFilteringFeatureFactory =
new AdFilteringFeatureFactory(mAppInstallDao, mFrequencyCapDao, mFlags);
mAdServicesHttpsClient =
new AdServicesHttpsClient(
AdServicesExecutors.getBlockingExecutor(),
CacheProviderFactory.createNoOpCache());
when(mDevContextFilter.createDevContext())
.thenReturn(DevContext.createForDevOptionsDisabled());
when(mMockCallerMetadata.getBinderElapsedTimestamp())
.thenReturn(SystemClock.elapsedRealtime() - BINDER_ELAPSED_TIME_MS);
// Create an instance of AdSelection Service with real dependencies
mAdSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDaoSpy,
mAppInstallDao,
mCustomAudienceDao,
mAdServicesHttpsClient,
mDevContextFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mContext,
mAdServicesLoggerMock,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilter,
mAdSelectionServiceFilter,
mAdFilteringFeatureFactory,
mConsentManagerMock);
// Create a dispatcher that helps map a request -> response in mockWebServer
mDispatcher =
new Dispatcher() {
@Override
public MockResponse dispatch(RecordedRequest request) {
switch (request.getPath()) {
case SELECTION_PICK_HIGHEST_LOGIC_JS_PATH:
return new MockResponse().setBody(SELECTION_PICK_HIGHEST_LOGIC_JS);
case SELECTION_PICK_NONE_LOGIC_JS_PATH:
return new MockResponse().setBody(SELECTION_PICK_NONE_LOGIC_JS);
case SELECTION_WATERFALL_LOGIC_JS_PATH:
return new MockResponse().setBody(SELECTION_WATERFALL_LOGIC_JS);
case SELECTION_FAULTY_LOGIC_JS_PATH:
return new MockResponse().setBody(SELECTION_FAULTY_LOGIC_JS);
default:
return new MockResponse().setResponseCode(404);
}
}
};
when(mContext.getDatabasePath(DATABASE_NAME)).thenReturn(mMockDBAdSelectionFile);
when(mMockDBAdSelectionFile.length()).thenReturn(DB_AD_SELECTION_FILE_SIZE);
doNothing()
.when(mAdSelectionServiceFilter)
.filterRequest(
SAMPLE_SELLER,
CALLER_PACKAGE_NAME,
false,
true,
CALLER_UID,
AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS,
Throttler.ApiKey.FLEDGE_API_SELECT_ADS);
}
@After
public void tearDown() {
if (mStaticMockSession != null) {
mStaticMockSession.finishMocking();
}
}
@Test
public void testSelectAdsFromOutcomesPickHighestSuccess() throws Exception {
doReturn(new AdSelectionFromOutcomesE2ETest.TestFlags()).when(FlagsFactory::getFlags);
MockWebServer server = mMockWebServerRule.startMockWebServer(mDispatcher);
final String selectionLogicPath = SELECTION_PICK_HIGHEST_LOGIC_JS_PATH;
Map<Long, Double> adSelectionIdToBidMap =
Map.of(
AD_SELECTION_ID_1, 10.0,
AD_SELECTION_ID_2, 20.0,
AD_SELECTION_ID_3, 30.0);
persistAdSelectionEntryDaoResults(adSelectionIdToBidMap);
AdSelectionFromOutcomesConfig config =
AdSelectionFromOutcomesConfigFixture.anAdSelectionFromOutcomesConfig(
List.of(AD_SELECTION_ID_1, AD_SELECTION_ID_2, AD_SELECTION_ID_3),
AdSelectionSignals.EMPTY,
mMockWebServerRule.uriForPath(selectionLogicPath));
AdSelectionFromOutcomesE2ETest.AdSelectionFromOutcomesTestCallback resultsCallback =
invokeSelectAdsFromOutcomes(mAdSelectionService, config, CALLER_PACKAGE_NAME);
assertThat(resultsCallback.mIsSuccess).isTrue();
assertThat(resultsCallback.mAdSelectionResponse).isNotNull();
assertEquals(resultsCallback.mAdSelectionResponse.getAdSelectionId(), AD_SELECTION_ID_3);
mMockWebServerRule.verifyMockServerRequests(
server, 1, Collections.singletonList(selectionLogicPath), String::equals);
}
@Test
public void testSelectAdsFromOutcomesWaterfallMediationAdBidHigherThanBidFloorSuccess()
throws Exception {
doReturn(new AdSelectionFromOutcomesE2ETest.TestFlags()).when(FlagsFactory::getFlags);
MockWebServer server = mMockWebServerRule.startMockWebServer(mDispatcher);
final String selectionLogicPath = SELECTION_WATERFALL_LOGIC_JS_PATH;
Map<Long, Double> adSelectionIdToBidMap = Map.of(AD_SELECTION_ID_1, 10.0);
persistAdSelectionEntryDaoResults(adSelectionIdToBidMap);
AdSelectionFromOutcomesConfig config =
AdSelectionFromOutcomesConfigFixture.anAdSelectionFromOutcomesConfig(
Collections.singletonList(AD_SELECTION_ID_1),
AdSelectionSignals.fromString(
String.format(BID_FLOOR_SELECTION_SIGNAL_TEMPLATE, 9)),
mMockWebServerRule.uriForPath(selectionLogicPath));
AdSelectionFromOutcomesE2ETest.AdSelectionFromOutcomesTestCallback resultsCallback =
invokeSelectAdsFromOutcomes(mAdSelectionService, config, CALLER_PACKAGE_NAME);
assertThat(resultsCallback.mIsSuccess).isTrue();
assertThat(resultsCallback.mAdSelectionResponse).isNotNull();
assertEquals(resultsCallback.mAdSelectionResponse.getAdSelectionId(), AD_SELECTION_ID_1);
mMockWebServerRule.verifyMockServerRequests(
server, 1, Collections.singletonList(selectionLogicPath), String::equals);
}
@Test
public void testSelectAdsFromOutcomesWaterfallMediationAdBidLowerThanBidFloorSuccess()
throws Exception {
doReturn(new AdSelectionFromOutcomesE2ETest.TestFlags()).when(FlagsFactory::getFlags);
MockWebServer server = mMockWebServerRule.startMockWebServer(mDispatcher);
final String selectionLogicPath = SELECTION_WATERFALL_LOGIC_JS_PATH;
Map<Long, Double> adSelectionIdToBidMap = Map.of(AD_SELECTION_ID_1, 10.0);
persistAdSelectionEntryDaoResults(adSelectionIdToBidMap);
AdSelectionFromOutcomesConfig config =
AdSelectionFromOutcomesConfigFixture.anAdSelectionFromOutcomesConfig(
Collections.singletonList(AD_SELECTION_ID_1),
AdSelectionSignals.fromString(
String.format(BID_FLOOR_SELECTION_SIGNAL_TEMPLATE, 11)),
mMockWebServerRule.uriForPath(selectionLogicPath));
AdSelectionFromOutcomesE2ETest.AdSelectionFromOutcomesTestCallback resultsCallback =
invokeSelectAdsFromOutcomes(mAdSelectionService, config, CALLER_PACKAGE_NAME);
assertThat(resultsCallback.mIsSuccess).isTrue();
assertThat(resultsCallback.mAdSelectionResponse).isNull();
mMockWebServerRule.verifyMockServerRequests(
server, 1, Collections.singletonList(selectionLogicPath), String::equals);
}
@Test
public void testSelectAdsFromOutcomesReturnsNullSuccess() throws Exception {
doReturn(new AdSelectionFromOutcomesE2ETest.TestFlags()).when(FlagsFactory::getFlags);
MockWebServer server = mMockWebServerRule.startMockWebServer(mDispatcher);
final String selectionLogicPath = SELECTION_PICK_NONE_LOGIC_JS_PATH;
Map<Long, Double> adSelectionIdToBidMap =
Map.of(
AD_SELECTION_ID_1, 10.0,
AD_SELECTION_ID_2, 20.0,
AD_SELECTION_ID_3, 30.0);
persistAdSelectionEntryDaoResults(adSelectionIdToBidMap);
AdSelectionFromOutcomesConfig config =
AdSelectionFromOutcomesConfigFixture.anAdSelectionFromOutcomesConfig(
List.of(AD_SELECTION_ID_1, AD_SELECTION_ID_2, AD_SELECTION_ID_3),
AdSelectionSignals.EMPTY,
mMockWebServerRule.uriForPath(selectionLogicPath));
AdSelectionFromOutcomesE2ETest.AdSelectionFromOutcomesTestCallback resultsCallback =
invokeSelectAdsFromOutcomes(mAdSelectionService, config, CALLER_PACKAGE_NAME);
assertThat(resultsCallback.mIsSuccess).isTrue();
assertThat(resultsCallback.mAdSelectionResponse).isNull();
mMockWebServerRule.verifyMockServerRequests(
server, 1, Collections.singletonList(selectionLogicPath), String::equals);
}
@Test
public void testSelectAdsFromOutcomesInvalidAdSelectionConfigFromOutcomes() throws Exception {
doReturn(new AdSelectionFromOutcomesE2ETest.TestFlags()).when(FlagsFactory::getFlags);
MockWebServer server = mMockWebServerRule.startMockWebServer(mDispatcher);
final String selectionLogicPath = "/unreachableLogicJS/";
long adSelectionId1 = 12345L;
long adSelectionId2 = 123456L;
long adSelectionId3 = 1234567L;
Map<Long, Double> adSelectionIdToBidMap =
Map.of(
adSelectionId1, 10.0,
adSelectionId2, 20.0,
adSelectionId3, 30.0);
persistAdSelectionEntryDaoResults(adSelectionIdToBidMap);
AdSelectionFromOutcomesConfig config =
AdSelectionFromOutcomesConfigFixture.anAdSelectionFromOutcomesConfig(
SELLER_INCONSISTENT_WITH_SELECTION_URI,
List.of(adSelectionId1, adSelectionId2, adSelectionId3),
AdSelectionSignals.EMPTY,
mMockWebServerRule.uriForPath(selectionLogicPath));
AdSelectionFromOutcomesE2ETest.AdSelectionFromOutcomesTestCallback resultsCallback =
invokeSelectAdsFromOutcomes(mAdSelectionService, config, CALLER_PACKAGE_NAME);
assertThat(resultsCallback.mIsSuccess).isFalse();
assertThat(resultsCallback.mFledgeErrorResponse).isNotNull();
assertThat(resultsCallback.mFledgeErrorResponse.getStatusCode())
.isEqualTo(STATUS_INVALID_ARGUMENT);
mMockWebServerRule.verifyMockServerRequests(
server, 0, Collections.emptyList(), String::equals);
}
@Test
public void testSelectAdsFromOutcomesJsReturnsFaultyAdSelectionIdFailure() throws Exception {
doReturn(new AdSelectionFromOutcomesE2ETest.TestFlags()).when(FlagsFactory::getFlags);
MockWebServer server = mMockWebServerRule.startMockWebServer(mDispatcher);
final String selectionLogicPath = SELECTION_FAULTY_LOGIC_JS_PATH;
Map<Long, Double> adSelectionIdToBidMap =
Map.of(
AD_SELECTION_ID_1, 10.0,
AD_SELECTION_ID_2, 20.0,
AD_SELECTION_ID_3, 30.0);
persistAdSelectionEntryDaoResults(adSelectionIdToBidMap);
AdSelectionFromOutcomesConfig config =
AdSelectionFromOutcomesConfigFixture.anAdSelectionFromOutcomesConfig(
List.of(AD_SELECTION_ID_1, AD_SELECTION_ID_2, AD_SELECTION_ID_3),
AdSelectionSignals.EMPTY,
mMockWebServerRule.uriForPath(selectionLogicPath));
AdSelectionFromOutcomesE2ETest.AdSelectionFromOutcomesTestCallback resultsCallback =
invokeSelectAdsFromOutcomes(mAdSelectionService, config, CALLER_PACKAGE_NAME);
assertThat(resultsCallback.mIsSuccess).isFalse();
assertThat(resultsCallback.mFledgeErrorResponse).isNotNull();
assertThat(resultsCallback.mFledgeErrorResponse.getStatusCode())
.isEqualTo(STATUS_INTERNAL_ERROR);
assertThat(resultsCallback.mFledgeErrorResponse.getErrorMessage())
.contains(SELECTED_OUTCOME_MUST_BE_ONE_OF_THE_INPUTS);
mMockWebServerRule.verifyMockServerRequests(
server, 1, Collections.singletonList(selectionLogicPath), String::equals);
}
private AdSelectionFromOutcomesE2ETest.AdSelectionFromOutcomesTestCallback
invokeSelectAdsFromOutcomes(
AdSelectionServiceImpl adSelectionService,
AdSelectionFromOutcomesConfig adSelectionFromOutcomesConfig,
String callerPackageName)
throws InterruptedException, RemoteException {
CountDownLatch countdownLatch = new CountDownLatch(1);
AdSelectionFromOutcomesE2ETest.AdSelectionFromOutcomesTestCallback adSelectionTestCallback =
new AdSelectionFromOutcomesE2ETest.AdSelectionFromOutcomesTestCallback(
countdownLatch);
AdSelectionFromOutcomesInput input =
new AdSelectionFromOutcomesInput.Builder()
.setAdSelectionFromOutcomesConfig(adSelectionFromOutcomesConfig)
.setCallerPackageName(callerPackageName)
.build();
adSelectionService.selectAdsFromOutcomes(
input, mMockCallerMetadata, adSelectionTestCallback);
adSelectionTestCallback.mCountDownLatch.await();
return adSelectionTestCallback;
}
private void persistAdSelectionEntryDaoResults(Map<Long, Double> adSelectionIdToBidMap) {
persistAdSelectionEntryDaoResults(adSelectionIdToBidMap, CALLER_PACKAGE_NAME);
}
private void persistAdSelectionEntryDaoResults(
Map<Long, Double> adSelectionIdToBidMap, String callerPackageName) {
final Uri biddingLogicUri1 = Uri.parse("https://www.domain.com/logic/1");
final Uri renderUri = Uri.parse("https://www.domain.com/advert/");
final Instant activationTime = Instant.now();
final String contextualSignals = "contextual_signals";
final CustomAudienceSignals customAudienceSignals =
CustomAudienceSignalsFixture.aCustomAudienceSignals();
for (Map.Entry<Long, Double> entry : adSelectionIdToBidMap.entrySet()) {
final DBAdSelection dbAdSelectionEntry =
new DBAdSelection.Builder()
.setAdSelectionId(entry.getKey())
.setCustomAudienceSignals(customAudienceSignals)
.setContextualSignals(contextualSignals)
.setBiddingLogicUri(biddingLogicUri1)
.setWinningAdRenderUri(renderUri)
.setWinningAdBid(entry.getValue())
.setCreationTimestamp(activationTime)
.setCallerPackageName(callerPackageName)
.build();
mAdSelectionEntryDaoSpy.persistAdSelection(dbAdSelectionEntry);
}
}
static class AdSelectionFromOutcomesTestCallback extends AdSelectionCallback.Stub {
final CountDownLatch mCountDownLatch;
boolean mIsSuccess = false;
AdSelectionResponse mAdSelectionResponse;
FledgeErrorResponse mFledgeErrorResponse;
AdSelectionFromOutcomesTestCallback(CountDownLatch countDownLatch) {
mCountDownLatch = countDownLatch;
mAdSelectionResponse = null;
mFledgeErrorResponse = null;
}
@Override
public void onSuccess(AdSelectionResponse adSelectionResponse) throws RemoteException {
mIsSuccess = true;
mAdSelectionResponse = adSelectionResponse;
mCountDownLatch.countDown();
}
@Override
public void onFailure(FledgeErrorResponse fledgeErrorResponse) throws RemoteException {
mIsSuccess = false;
mFledgeErrorResponse = fledgeErrorResponse;
mCountDownLatch.countDown();
}
}
private static class TestFlags implements Flags {
@Override
public boolean getEnforceIsolateMaxHeapSize() {
return false;
}
@Override
public boolean getEnforceForegroundStatusForFledgeRunAdSelection() {
return true;
}
@Override
public boolean getEnforceForegroundStatusForFledgeReportImpression() {
return true;
}
@Override
public boolean getEnforceForegroundStatusForFledgeOverrides() {
return true;
}
@Override
public boolean getDisableFledgeEnrollmentCheck() {
return true;
}
@Override
public float getSdkRequestPermitsPerSecond() {
// Unlimited rate for unit tests to avoid flake in tests due to rate
// limiting
return -1;
}
@Override
public boolean getFledgeAdSelectionFilteringEnabled() {
return false;
}
}
}