blob: 1707f886bdc227a0318ca28f4ec313b3bd5318ed [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.common.AdServicesStatusUtils.STATUS_CALLER_NOT_ALLOWED;
import static android.adservices.common.AdServicesStatusUtils.STATUS_INTERNAL_ERROR;
import static android.adservices.common.AdServicesStatusUtils.STATUS_INVALID_ARGUMENT;
import static android.adservices.common.AdServicesStatusUtils.STATUS_RATE_LIMIT_REACHED;
import static android.adservices.common.AdServicesStatusUtils.STATUS_SUCCESS;
import static android.adservices.common.AdServicesStatusUtils.STATUS_UNAUTHORIZED;
import static android.adservices.common.AdServicesStatusUtils.STATUS_USER_CONSENT_REVOKED;
import static android.adservices.common.CommonFixture.TEST_PACKAGE_NAME;
import static com.android.adservices.service.adselection.ImpressionReporter.REPORT_IMPRESSION_THROTTLED;
import static com.android.adservices.service.adselection.ImpressionReporter.UNABLE_TO_FIND_AD_SELECTION_WITH_GIVEN_ID;
import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__OVERRIDE_AD_SELECTION_CONFIG_REMOTE_INFO;
import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__REMOVE_AD_SELECTION_CONFIG_REMOTE_INFO_OVERRIDE;
import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION;
import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__RESET_ALL_AD_SELECTION_CONFIG_REMOTE_OVERRIDES;
import static com.android.adservices.stats.FledgeApiCallStatsMatcher.aCallStatForFledgeApiWithStatus;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
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.doThrow;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
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 static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import android.adservices.adselection.AdSelectionConfig;
import android.adservices.adselection.AdSelectionConfigFixture;
import android.adservices.adselection.AdSelectionOverrideCallback;
import android.adservices.adselection.CustomAudienceSignalsFixture;
import android.adservices.adselection.ReportImpressionCallback;
import android.adservices.adselection.ReportImpressionInput;
import android.adservices.common.AdSelectionSignals;
import android.adservices.common.AdServicesStatusUtils;
import android.adservices.common.AdTechIdentifier;
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 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.adselection.AdSelectionDatabase;
import com.android.adservices.data.adselection.AdSelectionEntryDao;
import com.android.adservices.data.adselection.CustomAudienceSignals;
import com.android.adservices.data.adselection.DBAdSelection;
import com.android.adservices.data.adselection.DBAdSelectionOverride;
import com.android.adservices.data.adselection.DBBuyerDecisionLogic;
import com.android.adservices.data.customaudience.CustomAudienceDao;
import com.android.adservices.data.customaudience.CustomAudienceDatabase;
import com.android.adservices.service.Flags;
import com.android.adservices.service.common.AdServicesHttpsClient;
import com.android.adservices.service.common.AppImportanceFilter;
import com.android.adservices.service.common.AppImportanceFilter.WrongCallingApplicationStateException;
import com.android.adservices.service.common.FledgeAllowListsFilter;
import com.android.adservices.service.common.FledgeAuthorizationFilter;
import com.android.adservices.service.common.Throttler;
import com.android.adservices.service.consent.AdServicesApiConsent;
import com.android.adservices.service.consent.ConsentManager;
import com.android.adservices.service.devapi.AdSelectionDevOverridesHelper;
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.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
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.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoSession;
import org.mockito.Spy;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
public class AdSelectionServiceImplTest {
private static final Context CONTEXT = ApplicationProvider.getApplicationContext();
private static final Clock CLOCK = Clock.fixed(Instant.now(), ZoneOffset.UTC);
private static final Uri RENDER_URI = Uri.parse("https://test.com/advert/");
private static final Instant ACTIVATION_TIME = CLOCK.instant().truncatedTo(ChronoUnit.MILLIS);
private static final Uri BUYER_BIDDING_LOGIC_URI = Uri.parse("https://test.com");
private static final long AD_SELECTION_ID = 1;
private static final long INCORRECT_AD_SELECTION_ID = 2;
private static final double BID = 5.0;
private static final AdTechIdentifier SELLER_VALID = AdTechIdentifier.fromString("test.com");
private static final Uri DECISION_LOGIC_URI_INCONSISTENT =
Uri.parse("https://testinconsistent.com/test/decisions_logic_urls");
private static final String DUMMY_DECISION_LOGIC_JS =
"function test() { return \"hello world\"; }";
private static final AdSelectionSignals DUMMY_TRUSTED_SCORING_SIGNALS =
AdSelectionSignals.fromString(
"{\n"
+ "\t\"render_url_1\": \"signals_for_1\",\n"
+ "\t\"render_url_2\": \"signals_for_2\"\n"
+ "}");
// Auto-generated variable names are too long for lint check
private static final int SHORT_API_NAME_OVERRIDE =
AD_SERVICES_API_CALLED__API_NAME__OVERRIDE_AD_SELECTION_CONFIG_REMOTE_INFO;
private static final int SHORT_API_NAME_REMOVE_OVERRIDE =
AD_SERVICES_API_CALLED__API_NAME__REMOVE_AD_SELECTION_CONFIG_REMOTE_INFO_OVERRIDE;
private static final int SHORT_API_NAME_RESET_ALL_OVERRIDES =
AD_SERVICES_API_CALLED__API_NAME__RESET_ALL_AD_SELECTION_CONFIG_REMOTE_OVERRIDES;
private final ExecutorService mLightweightExecutorService =
AdServicesExecutors.getLightWeightExecutor();
private final ExecutorService mBackgroundExecutorService =
AdServicesExecutors.getBackgroundExecutor();
private final String mSellerReportingPath = "/reporting/seller";
private final String mBuyerReportingPath = "/reporting/buyer";
private final String mFetchJavaScriptPath = "/fetchJavascript/";
private final String mFetchTrustedScoringSignalsPath = "/fetchTrustedSignals/";
private final AdTechIdentifier mContextualSignals =
AdTechIdentifier.fromString("{\"contextual_signals\":1}");
private static final String TIMEOUT_MESSAGE = "Timed out:";
private final int mBytesPerPeriod = 1;
private final AdServicesHttpsClient mClient =
new AdServicesHttpsClient(AdServicesExecutors.getBlockingExecutor());
private Flags mFlags = new FlagsWithEnrollmentCheckEnabledSwitch(false);
private MockitoSession mStaticMockSession = null;
@Spy private final AdServicesLogger mAdServicesLoggerSpy = AdServicesLoggerImpl.getInstance();
@Spy
FledgeAuthorizationFilter mFledgeAuthorizationFilterSpy =
FledgeAuthorizationFilter.create(CONTEXT, mAdServicesLoggerSpy);
@Spy
FledgeAllowListsFilter mFledgeAllowListsFilterSpy =
new FledgeAllowListsFilter(mFlags, mAdServicesLoggerSpy);
@Rule public MockWebServerRule mMockWebServerRule = MockWebServerRuleFactory.createForHttps();
// This object access some system APIs
@Mock public DevContextFilter mDevContextFilter;
@Mock public AppImportanceFilter mAppImportanceFilter;
@Mock private ConsentManager mConsentManagerMock;
private CustomAudienceDao mCustomAudienceDao;
private AdSelectionEntryDao mAdSelectionEntryDao;
private AdSelectionConfig.Builder mAdSelectionConfigBuilder;
@Before
public void setUp() {
mStaticMockSession =
ExtendedMockito.mockitoSession()
.spyStatic(JSScriptEngine.class)
// mAdServicesLoggerSpy is not referenced in many tests
.strictness(Strictness.LENIENT)
.initMocks(this)
.startMocking();
mCustomAudienceDao =
Room.inMemoryDatabaseBuilder(CONTEXT, CustomAudienceDatabase.class)
.build()
.customAudienceDao();
mAdSelectionEntryDao =
Room.inMemoryDatabaseBuilder(
ApplicationProvider.getApplicationContext(),
AdSelectionDatabase.class)
.build()
.adSelectionEntryDao();
mAdSelectionConfigBuilder =
AdSelectionConfigFixture.anAdSelectionConfigBuilder()
.setSeller(
AdTechIdentifier.fromString(
mMockWebServerRule
.uriForPath(mFetchJavaScriptPath)
.getHost()))
.setDecisionLogicUri(mMockWebServerRule.uriForPath(mFetchJavaScriptPath))
.setTrustedScoringSignalsUri(
mMockWebServerRule.uriForPath(mFetchTrustedScoringSignalsPath));
}
@After
public void tearDown() {
if (mStaticMockSession != null) {
mStaticMockSession.finishMocking();
}
}
@Test
public void testReportImpressionSuccess() throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
Uri sellerReportingUri = mMockWebServerRule.uriForPath(mSellerReportingPath);
Uri buyerReportingUri = mMockWebServerRule.uriForPath(mBuyerReportingPath);
String sellerDecisionLogicJs =
"function reportResult(ad_selection_config, render_url, bid, contextual_signals) {"
+ " \n"
+ " return {'status': 0, 'results': {'signals_for_buyer':"
+ " '{\"signals_for_buyer\":1}', 'reporting_url': '"
+ sellerReportingUri
+ "' } };\n"
+ "}";
String buyerDecisionLogicJs =
"function reportWin(ad_selection_signals, per_buyer_signals, signals_for_buyer,"
+ " contextual_signals, custom_audience_signals) { \n"
+ " return {'status': 0, 'results': {'reporting_url': '"
+ buyerReportingUri
+ "' } };\n"
+ "}";
MockWebServer server =
mMockWebServerRule.startMockWebServer(
List.of(
new MockResponse().setBody(sellerDecisionLogicJs),
new MockResponse(),
new MockResponse()));
DBBuyerDecisionLogic dbBuyerDecisionLogic =
new DBBuyerDecisionLogic.Builder()
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setBuyerDecisionLogicJs(buyerDecisionLogicJs)
.build();
CustomAudienceSignals customAudienceSignals =
CustomAudienceSignalsFixture.aCustomAudienceSignals();
DBAdSelection dbAdSelection =
new DBAdSelection.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setCustomAudienceSignals(customAudienceSignals)
.setContextualSignals(mContextualSignals.toString())
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setWinningAdRenderUri(RENDER_URI)
.setWinningAdBid(BID)
.setCreationTimestamp(ACTIVATION_TIME)
.setCallerPackageName(CommonFixture.TEST_PACKAGE_NAME)
.build();
mAdSelectionEntryDao.persistAdSelection(dbAdSelection);
mAdSelectionEntryDao.persistBuyerDecisionLogic(dbBuyerDecisionLogic);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
when(mDevContextFilter.createDevContext())
.thenReturn(DevContext.createForDevOptionsDisabled());
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
ReportImpressionInput input =
new ReportImpressionInput.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setAdSelectionConfig(adSelectionConfig)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
ReportImpressionTestCallback callback = callReportImpression(adSelectionService, input);
assertTrue(callback.mIsSuccess);
RecordedRequest fetchRequest = server.takeRequest();
assertEquals(mFetchJavaScriptPath, fetchRequest.getPath());
List<String> notifications =
ImmutableList.of(server.takeRequest().getPath(), server.takeRequest().getPath());
assertThat(notifications).containsExactly(mSellerReportingPath, mBuyerReportingPath);
verify(mAdServicesLoggerSpy)
.logFledgeApiCallStats(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION, STATUS_SUCCESS);
verify(mAdServicesLoggerSpy)
.logApiCallStats(
aCallStatForFledgeApiWithStatus(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION,
STATUS_SUCCESS));
}
@Test
public void testReportImpressionWithRevokedUserConsentSuccess() throws Exception {
doReturn(AdServicesApiConsent.REVOKED).when(mConsentManagerMock).getConsent(any());
Uri buyerReportingUri = mMockWebServerRule.uriForPath(mBuyerReportingPath);
String buyerDecisionLogicJs =
"function reportWin(ad_selection_signals, per_buyer_signals, signals_for_buyer,"
+ " contextual_signals, custom_audience_signals) { \n"
+ " return {'status': 0, 'results': {'reporting_url': '"
+ buyerReportingUri
+ "' } };\n"
+ "}";
MockWebServer server =
mMockWebServerRule.startMockWebServer(
new Dispatcher() {
@Override
public MockResponse dispatch(RecordedRequest request) {
throw new IllegalStateException(
"No calls should be made without user consent");
}
});
DBBuyerDecisionLogic dbBuyerDecisionLogic =
new DBBuyerDecisionLogic.Builder()
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setBuyerDecisionLogicJs(buyerDecisionLogicJs)
.build();
CustomAudienceSignals customAudienceSignals =
CustomAudienceSignalsFixture.aCustomAudienceSignals();
DBAdSelection dbAdSelection =
new DBAdSelection.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setCustomAudienceSignals(customAudienceSignals)
.setContextualSignals(mContextualSignals.toString())
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setWinningAdRenderUri(RENDER_URI)
.setWinningAdBid(BID)
.setCreationTimestamp(ACTIVATION_TIME)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
mAdSelectionEntryDao.persistAdSelection(dbAdSelection);
mAdSelectionEntryDao.persistBuyerDecisionLogic(dbBuyerDecisionLogic);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
when(mDevContextFilter.createDevContext())
.thenReturn(DevContext.createForDevOptionsDisabled());
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
ReportImpressionInput input =
new ReportImpressionInput.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setAdSelectionConfig(adSelectionConfig)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
ReportImpressionTestCallback callback = callReportImpression(adSelectionService, input);
assertTrue(callback.mIsSuccess);
assertEquals(0, server.getRequestCount());
verify(mAdServicesLoggerSpy)
.logFledgeApiCallStats(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION,
STATUS_USER_CONSENT_REVOKED);
verify(mAdServicesLoggerSpy)
.logApiCallStats(
aCallStatForFledgeApiWithStatus(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION,
STATUS_USER_CONSENT_REVOKED));
}
@Test
public void testReportImpressionFailsWhenReportResultTakesTooLong() throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
Uri sellerReportingUri = mMockWebServerRule.uriForPath(mSellerReportingPath);
Uri buyerReportingUri = mMockWebServerRule.uriForPath(mBuyerReportingPath);
String jsWaitMoreThanAllowed =
insertJsWait(2 * mFlags.getReportImpressionOverallTimeoutMs());
String sellerDecisionLogicJs =
"function reportResult(ad_selection_config, render_url, bid, contextual_signals) {"
+ " \n"
+ jsWaitMoreThanAllowed
+ " return {'status': 0, 'results': {'signals_for_buyer':"
+ " '{\"signals_for_buyer\":1}', 'reporting_url': '"
+ sellerReportingUri
+ "' } };\n"
+ "}";
String buyerDecisionLogicJs =
"function reportWin(ad_selection_signals, per_buyer_signals, signals_for_buyer,"
+ " contextual_signals, custom_audience_signals) { \n"
+ " return {'status': 0, 'results': {'reporting_url': '"
+ buyerReportingUri
+ "' } };\n"
+ "}";
mMockWebServerRule.startMockWebServer(
List.of(
new MockResponse().setBody(sellerDecisionLogicJs),
new MockResponse(),
new MockResponse()));
DBBuyerDecisionLogic dbBuyerDecisionLogic =
new DBBuyerDecisionLogic.Builder()
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setBuyerDecisionLogicJs(buyerDecisionLogicJs)
.build();
CustomAudienceSignals customAudienceSignals =
CustomAudienceSignalsFixture.aCustomAudienceSignals();
DBAdSelection dbAdSelection =
new DBAdSelection.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setCustomAudienceSignals(customAudienceSignals)
.setContextualSignals(mContextualSignals.toString())
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setWinningAdRenderUri(RENDER_URI)
.setWinningAdBid(BID)
.setCreationTimestamp(ACTIVATION_TIME)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
mAdSelectionEntryDao.persistAdSelection(dbAdSelection);
mAdSelectionEntryDao.persistBuyerDecisionLogic(dbBuyerDecisionLogic);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
when(mDevContextFilter.createDevContext())
.thenReturn(DevContext.createForDevOptionsDisabled());
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
ReportImpressionInput input =
new ReportImpressionInput.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setAdSelectionConfig(adSelectionConfig)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
ReportImpressionTestCallback callback = callReportImpression(adSelectionService, input);
assertFalse(callback.mIsSuccess);
assertEquals(callback.mFledgeErrorResponse.getStatusCode(), STATUS_INTERNAL_ERROR);
assertTrue(callback.mFledgeErrorResponse.getErrorMessage().contains(TIMEOUT_MESSAGE));
verify(mAdServicesLoggerSpy)
.logFledgeApiCallStats(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION, STATUS_INTERNAL_ERROR);
verify(mAdServicesLoggerSpy)
.logApiCallStats(
aCallStatForFledgeApiWithStatus(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION,
STATUS_INTERNAL_ERROR));
}
@Test
public void testReportImpressionFailsWhenReportWinTakesTooLong() throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
Uri sellerReportingUri = mMockWebServerRule.uriForPath(mSellerReportingPath);
Uri buyerReportingUri = mMockWebServerRule.uriForPath(mBuyerReportingPath);
String jsWaitMoreThanAllowed =
insertJsWait(2 * mFlags.getReportImpressionOverallTimeoutMs());
String sellerDecisionLogicJs =
"function reportResult(ad_selection_config, render_url, bid, contextual_signals) {"
+ " \n"
+ " return {'status': 0, 'results': {'signals_for_buyer':"
+ " '{\"signals_for_buyer\":1}', 'reporting_url': '"
+ sellerReportingUri
+ "' } };\n"
+ "}";
String buyerDecisionLogicJs =
"function reportWin(ad_selection_signals, per_buyer_signals, signals_for_buyer,"
+ " contextual_signals, custom_audience_signals) { \n"
+ jsWaitMoreThanAllowed
+ " return {'status': 0, 'results': {'reporting_url': '"
+ buyerReportingUri
+ "' } };\n"
+ "}";
mMockWebServerRule.startMockWebServer(
List.of(
new MockResponse().setBody(sellerDecisionLogicJs),
new MockResponse(),
new MockResponse()));
DBBuyerDecisionLogic dbBuyerDecisionLogic =
new DBBuyerDecisionLogic.Builder()
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setBuyerDecisionLogicJs(buyerDecisionLogicJs)
.build();
CustomAudienceSignals customAudienceSignals =
CustomAudienceSignalsFixture.aCustomAudienceSignals();
DBAdSelection dbAdSelection =
new DBAdSelection.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setCustomAudienceSignals(customAudienceSignals)
.setContextualSignals(mContextualSignals.toString())
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setWinningAdRenderUri(RENDER_URI)
.setWinningAdBid(BID)
.setCreationTimestamp(ACTIVATION_TIME)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
mAdSelectionEntryDao.persistAdSelection(dbAdSelection);
mAdSelectionEntryDao.persistBuyerDecisionLogic(dbBuyerDecisionLogic);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
when(mDevContextFilter.createDevContext())
.thenReturn(DevContext.createForDevOptionsDisabled());
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
ReportImpressionInput input =
new ReportImpressionInput.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setAdSelectionConfig(adSelectionConfig)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
ReportImpressionTestCallback callback = callReportImpression(adSelectionService, input);
assertFalse(callback.mIsSuccess);
assertEquals(callback.mFledgeErrorResponse.getStatusCode(), STATUS_INTERNAL_ERROR);
assertTrue(callback.mFledgeErrorResponse.getErrorMessage().contains(TIMEOUT_MESSAGE));
verify(mAdServicesLoggerSpy)
.logFledgeApiCallStats(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION, STATUS_INTERNAL_ERROR);
verify(mAdServicesLoggerSpy)
.logApiCallStats(
aCallStatForFledgeApiWithStatus(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION,
STATUS_INTERNAL_ERROR));
}
@Test
public void testReportImpressionFailsWhenOverallJSTimesOut() throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
Uri sellerReportingUri = mMockWebServerRule.uriForPath(mSellerReportingPath);
Uri buyerReportingUri = mMockWebServerRule.uriForPath(mBuyerReportingPath);
String jsWaitMoreThanAllowed =
insertJsWait((long) (.25 * mFlags.getReportImpressionOverallTimeoutMs()));
String sellerDecisionLogicJs =
"function reportResult(ad_selection_config, render_url, bid, contextual_signals) {"
+ " \n"
+ jsWaitMoreThanAllowed
+ " return {'status': 0, 'results': {'signals_for_buyer':"
+ " '{\"signals_for_buyer\":1}', 'reporting_url': '"
+ sellerReportingUri
+ "' } };\n"
+ "}";
String buyerDecisionLogicJs =
"function reportWin(ad_selection_signals, per_buyer_signals, signals_for_buyer,"
+ " contextual_signals, custom_audience_signals) { \n"
+ jsWaitMoreThanAllowed
+ " return {'status': 0, 'results': {'reporting_url': '"
+ buyerReportingUri
+ "' } };\n"
+ "}";
mMockWebServerRule.startMockWebServer(
List.of(
new MockResponse()
.setBody(sellerDecisionLogicJs)
.throttleBody(
mBytesPerPeriod,
(long) (.5 * mFlags.getReportImpressionOverallTimeoutMs()),
TimeUnit.MILLISECONDS),
new MockResponse(),
new MockResponse()));
DBBuyerDecisionLogic dbBuyerDecisionLogic =
new DBBuyerDecisionLogic.Builder()
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setBuyerDecisionLogicJs(buyerDecisionLogicJs)
.build();
CustomAudienceSignals customAudienceSignals =
CustomAudienceSignalsFixture.aCustomAudienceSignals();
DBAdSelection dbAdSelection =
new DBAdSelection.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setCustomAudienceSignals(customAudienceSignals)
.setContextualSignals(mContextualSignals.toString())
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setWinningAdRenderUri(RENDER_URI)
.setWinningAdBid(BID)
.setCreationTimestamp(ACTIVATION_TIME)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
mAdSelectionEntryDao.persistAdSelection(dbAdSelection);
mAdSelectionEntryDao.persistBuyerDecisionLogic(dbBuyerDecisionLogic);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
when(mDevContextFilter.createDevContext())
.thenReturn(DevContext.createForDevOptionsDisabled());
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
ReportImpressionInput input =
new ReportImpressionInput.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setAdSelectionConfig(adSelectionConfig)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
ReportImpressionTestCallback callback = callReportImpression(adSelectionService, input);
assertFalse(callback.mIsSuccess);
assertEquals(callback.mFledgeErrorResponse.getStatusCode(), STATUS_INTERNAL_ERROR);
assertTrue(callback.mFledgeErrorResponse.getErrorMessage().contains(TIMEOUT_MESSAGE));
verify(mAdServicesLoggerSpy)
.logFledgeApiCallStats(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION, STATUS_INTERNAL_ERROR);
verify(mAdServicesLoggerSpy)
.logApiCallStats(
aCallStatForFledgeApiWithStatus(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION,
STATUS_INTERNAL_ERROR));
}
@Test
public void testReportImpressionFailsWhenJSFetchTakesTooLong() throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
Uri sellerReportingUri = mMockWebServerRule.uriForPath(mSellerReportingPath);
Uri buyerReportingUri = mMockWebServerRule.uriForPath(mBuyerReportingPath);
String sellerDecisionLogicJs =
"function reportResult(ad_selection_config, render_url, bid, contextual_signals) {"
+ " \n"
+ " return {'status': 0, 'results': {'signals_for_buyer':"
+ " '{\"signals_for_buyer\":1}', 'reporting_url': '"
+ sellerReportingUri
+ "' } };\n"
+ "}";
String buyerDecisionLogicJs =
"function reportWin(ad_selection_signals, per_buyer_signals, signals_for_buyer,"
+ " contextual_signals, custom_audience_signals) { \n"
+ " return {'status': 0, 'results': {'reporting_url': '"
+ buyerReportingUri
+ "' } };\n"
+ "}";
mMockWebServerRule.startMockWebServer(
List.of(
new MockResponse()
.setBody(sellerDecisionLogicJs)
.throttleBody(
mBytesPerPeriod,
mFlags.getReportImpressionOverallTimeoutMs(),
TimeUnit.MILLISECONDS),
new MockResponse(),
new MockResponse()));
DBBuyerDecisionLogic dbBuyerDecisionLogic =
new DBBuyerDecisionLogic.Builder()
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setBuyerDecisionLogicJs(buyerDecisionLogicJs)
.build();
CustomAudienceSignals customAudienceSignals =
CustomAudienceSignalsFixture.aCustomAudienceSignals();
DBAdSelection dbAdSelection =
new DBAdSelection.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setCustomAudienceSignals(customAudienceSignals)
.setContextualSignals(mContextualSignals.toString())
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setWinningAdRenderUri(RENDER_URI)
.setWinningAdBid(BID)
.setCreationTimestamp(ACTIVATION_TIME)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
mAdSelectionEntryDao.persistAdSelection(dbAdSelection);
mAdSelectionEntryDao.persistBuyerDecisionLogic(dbBuyerDecisionLogic);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
when(mDevContextFilter.createDevContext())
.thenReturn(DevContext.createForDevOptionsDisabled());
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
ReportImpressionInput input =
new ReportImpressionInput.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setAdSelectionConfig(adSelectionConfig)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
ReportImpressionTestCallback callback = callReportImpression(adSelectionService, input);
assertFalse(callback.mIsSuccess);
assertEquals(callback.mFledgeErrorResponse.getStatusCode(), STATUS_INTERNAL_ERROR);
assertTrue(callback.mFledgeErrorResponse.getErrorMessage().contains(TIMEOUT_MESSAGE));
verify(mAdServicesLoggerSpy)
.logFledgeApiCallStats(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION, STATUS_INTERNAL_ERROR);
verify(mAdServicesLoggerSpy)
.logApiCallStats(
aCallStatForFledgeApiWithStatus(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION,
STATUS_INTERNAL_ERROR));
}
@Test
public void testReportImpressionFailsWithInvalidAdSelectionId() throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
Uri sellerReportingUri = mMockWebServerRule.uriForPath(mSellerReportingPath);
Uri buyerReportingUri = mMockWebServerRule.uriForPath(mBuyerReportingPath);
String sellerDecisionLogicJs =
"function reportResult(ad_selection_config, render_url, bid, contextual_signals) {"
+ " \n"
+ " return {'status': 0, 'results': {'signals_for_buyer':"
+ " '{\"signals_for_buyer\":1}', 'reporting_url': '"
+ sellerReportingUri
+ "' } };\n"
+ "}";
String buyerDecisionLogicJs =
"function reportWin(ad_selection_signals, per_buyer_signals, signals_for_buyer,"
+ " contextual_signals, custom_audience_signals) { \n"
+ " return {'status': 0, 'results': {'reporting_url': '"
+ buyerReportingUri
+ "' } };\n"
+ "}";
mMockWebServerRule.startMockWebServer(
List.of(
new MockResponse().setBody(sellerDecisionLogicJs),
new MockResponse(),
new MockResponse()));
DBBuyerDecisionLogic dbBuyerDecisionLogic =
new DBBuyerDecisionLogic.Builder()
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setBuyerDecisionLogicJs(buyerDecisionLogicJs)
.build();
CustomAudienceSignals customAudienceSignals =
CustomAudienceSignalsFixture.aCustomAudienceSignals();
DBAdSelection dbAdSelection =
new DBAdSelection.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setCustomAudienceSignals(customAudienceSignals)
.setContextualSignals(mContextualSignals.toString())
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setWinningAdRenderUri(RENDER_URI)
.setWinningAdBid(BID)
.setCreationTimestamp(ACTIVATION_TIME)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
mAdSelectionEntryDao.persistAdSelection(dbAdSelection);
mAdSelectionEntryDao.persistBuyerDecisionLogic(dbBuyerDecisionLogic);
when(mDevContextFilter.createDevContext())
.thenReturn(DevContext.createForDevOptionsDisabled());
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
ReportImpressionInput request =
new ReportImpressionInput.Builder()
.setAdSelectionId(INCORRECT_AD_SELECTION_ID)
.setAdSelectionConfig(adSelectionConfig)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
ReportImpressionTestCallback callback = callReportImpression(adSelectionService, request);
assertFalse(callback.mIsSuccess);
assertEquals(callback.mFledgeErrorResponse.getStatusCode(), STATUS_INVALID_ARGUMENT);
verify(mAdServicesLoggerSpy)
.logFledgeApiCallStats(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION,
STATUS_INVALID_ARGUMENT);
verify(mAdServicesLoggerSpy)
.logApiCallStats(
aCallStatForFledgeApiWithStatus(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION,
STATUS_INVALID_ARGUMENT));
}
@Test
public void testReportImpressionBadSellerJavascriptFailsWithInternalError() throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
Uri sellerReportingUri = mMockWebServerRule.uriForPath(mSellerReportingPath);
Uri buyerReportingUri = mMockWebServerRule.uriForPath(mBuyerReportingPath);
String invalidSellerDecisionLogicJsMissingCurlyBracket =
"function reportResult(ad_selection_config, render_url, bid, contextual_signals) {"
+ " \n"
+ " return {'status': 0, 'results': 'signals_for_buyer':"
+ " '{\"signals_for_buyer\":1}', 'reporting_url': '"
+ sellerReportingUri
+ "' } };\n"
+ "}";
String buyerDecisionLogicJs =
"function reportWin(ad_selection_signals, per_buyer_signals, signals_for_buyer,"
+ " contextual_signals, custom_audience_signals) { \n"
+ " return {'status': 0, 'results': {'reporting_url': '"
+ buyerReportingUri
+ "' } };\n"
+ "}";
mMockWebServerRule.startMockWebServer(
List.of(
new MockResponse().setBody(invalidSellerDecisionLogicJsMissingCurlyBracket),
new MockResponse(),
new MockResponse()));
DBBuyerDecisionLogic dbBuyerDecisionLogic =
new DBBuyerDecisionLogic.Builder()
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setBuyerDecisionLogicJs(buyerDecisionLogicJs)
.build();
CustomAudienceSignals customAudienceSignals =
CustomAudienceSignalsFixture.aCustomAudienceSignals();
DBAdSelection dbAdSelection =
new DBAdSelection.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setCustomAudienceSignals(customAudienceSignals)
.setContextualSignals(mContextualSignals.toString())
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setWinningAdRenderUri(RENDER_URI)
.setWinningAdBid(BID)
.setCreationTimestamp(ACTIVATION_TIME)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
mAdSelectionEntryDao.persistAdSelection(dbAdSelection);
mAdSelectionEntryDao.persistBuyerDecisionLogic(dbBuyerDecisionLogic);
when(mDevContextFilter.createDevContext())
.thenReturn(DevContext.createForDevOptionsDisabled());
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
ReportImpressionInput request =
new ReportImpressionInput.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setAdSelectionConfig(adSelectionConfig)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
ReportImpressionTestCallback callback = callReportImpression(adSelectionService, request);
assertFalse(callback.mIsSuccess);
assertEquals(callback.mFledgeErrorResponse.getStatusCode(), STATUS_INTERNAL_ERROR);
verify(mAdServicesLoggerSpy)
.logFledgeApiCallStats(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION, STATUS_INTERNAL_ERROR);
verify(mAdServicesLoggerSpy)
.logApiCallStats(
aCallStatForFledgeApiWithStatus(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION,
STATUS_INTERNAL_ERROR));
}
@Test
public void testReportImpressionBadBuyerJavascriptFailsWithInternalError() throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
Uri sellerReportingUri = mMockWebServerRule.uriForPath(mSellerReportingPath);
Uri buyerReportingUri = mMockWebServerRule.uriForPath(mBuyerReportingPath);
String sellerDecisionLogicJs =
"function reportResult(ad_selection_config, render_url, bid, contextual_signals) {"
+ " \n"
+ " return {'status': 0, 'results': {'signals_for_buyer':"
+ " '{\"signals_for_buyer\":1}', 'reporting_url': '"
+ sellerReportingUri
+ "' } };\n"
+ "}";
String inValidBuyerDecisionLogicJsMissingCurlyBracket =
"function reportWin(ad_selection_signals, per_buyer_signals, signals_for_buyer,"
+ " contextual_signals, custom_audience_signals) { \n"
+ " return {'status': 0, 'results': 'reporting_url': '"
+ buyerReportingUri
+ "' } };\n"
+ "}";
mMockWebServerRule.startMockWebServer(
List.of(
new MockResponse().setBody(sellerDecisionLogicJs),
new MockResponse(),
new MockResponse()));
DBBuyerDecisionLogic dbBuyerDecisionLogic =
new DBBuyerDecisionLogic.Builder()
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setBuyerDecisionLogicJs(inValidBuyerDecisionLogicJsMissingCurlyBracket)
.build();
CustomAudienceSignals customAudienceSignals =
CustomAudienceSignalsFixture.aCustomAudienceSignals();
DBAdSelection dbAdSelection =
new DBAdSelection.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setCustomAudienceSignals(customAudienceSignals)
.setContextualSignals(mContextualSignals.toString())
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setWinningAdRenderUri(RENDER_URI)
.setWinningAdBid(BID)
.setCreationTimestamp(ACTIVATION_TIME)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
mAdSelectionEntryDao.persistAdSelection(dbAdSelection);
mAdSelectionEntryDao.persistBuyerDecisionLogic(dbBuyerDecisionLogic);
when(mDevContextFilter.createDevContext())
.thenReturn(DevContext.createForDevOptionsDisabled());
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
ReportImpressionInput request =
new ReportImpressionInput.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setAdSelectionConfig(adSelectionConfig)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
ReportImpressionTestCallback callback = callReportImpression(adSelectionService, request);
assertFalse(callback.mIsSuccess);
assertEquals(callback.mFledgeErrorResponse.getStatusCode(), STATUS_INTERNAL_ERROR);
verify(mAdServicesLoggerSpy)
.logFledgeApiCallStats(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION, STATUS_INTERNAL_ERROR);
verify(mAdServicesLoggerSpy)
.logApiCallStats(
aCallStatForFledgeApiWithStatus(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION,
STATUS_INTERNAL_ERROR));
}
@Test
public void testReportImpressionUseDevOverrideForSellerJS() throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
Uri sellerReportingUri = mMockWebServerRule.uriForPath(mSellerReportingPath);
Uri buyerReportingUri = mMockWebServerRule.uriForPath(mBuyerReportingPath);
String sellerDecisionLogicJs =
"function reportResult(ad_selection_config, render_url, bid, contextual_signals) {"
+ " \n"
+ " return {'status': 0, 'results': {'signals_for_buyer':"
+ " '{\"signals_for_buyer\":1}', 'reporting_url': '"
+ sellerReportingUri
+ "' } };\n"
+ "}";
String buyerDecisionLogicJs =
"function reportWin(ad_selection_signals, per_buyer_signals, signals_for_buyer,"
+ " contextual_signals, custom_audience_signals) { \n"
+ " return {'status': 0, 'results': {'reporting_url': '"
+ buyerReportingUri
+ "' } };\n"
+ "}";
MockWebServer server =
mMockWebServerRule.startMockWebServer(
List.of(
// There is no need to fetch JS
new MockResponse(), new MockResponse()));
DBBuyerDecisionLogic dbBuyerDecisionLogic =
new DBBuyerDecisionLogic.Builder()
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setBuyerDecisionLogicJs(buyerDecisionLogicJs)
.build();
CustomAudienceSignals customAudienceSignals =
CustomAudienceSignalsFixture.aCustomAudienceSignals();
DBAdSelection dbAdSelection =
new DBAdSelection.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setCustomAudienceSignals(customAudienceSignals)
.setContextualSignals(mContextualSignals.toString())
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setWinningAdRenderUri(RENDER_URI)
.setWinningAdBid(BID)
.setCreationTimestamp(ACTIVATION_TIME)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
mAdSelectionEntryDao.persistAdSelection(dbAdSelection);
mAdSelectionEntryDao.persistBuyerDecisionLogic(dbBuyerDecisionLogic);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
when(mDevContextFilter.createDevContext())
.thenReturn(DevContext.createForDevOptionsDisabled());
// Set dev override for this AdSelection
DBAdSelectionOverride adSelectionOverride =
DBAdSelectionOverride.builder()
.setAdSelectionConfigId(
AdSelectionDevOverridesHelper.calculateAdSelectionConfigId(
adSelectionConfig))
.setAppPackageName(TEST_PACKAGE_NAME)
.setDecisionLogicJS(sellerDecisionLogicJs)
.setTrustedScoringSignals(DUMMY_TRUSTED_SCORING_SIGNALS.toString())
.build();
mAdSelectionEntryDao.persistAdSelectionOverride(adSelectionOverride);
when(mDevContextFilter.createDevContext())
.thenReturn(
DevContext.builder()
.setDevOptionsEnabled(true)
.setCallingAppPackageName(TEST_PACKAGE_NAME)
.build());
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
ReportImpressionInput input =
new ReportImpressionInput.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setAdSelectionConfig(adSelectionConfig)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
ReportImpressionTestCallback callback = callReportImpression(adSelectionService, input);
assertTrue(callback.mIsSuccess);
List<String> notifications =
ImmutableList.of(server.takeRequest().getPath(), server.takeRequest().getPath());
assertThat(notifications).containsExactly(mSellerReportingPath, mBuyerReportingPath);
verify(mAdServicesLoggerSpy)
.logFledgeApiCallStats(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION, STATUS_SUCCESS);
verify(mAdServicesLoggerSpy)
.logApiCallStats(
aCallStatForFledgeApiWithStatus(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION,
STATUS_SUCCESS));
}
@Test
public void testOverrideAdSelectionConfigRemoteInfoSuccess() throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
when(mDevContextFilter.createDevContext())
.thenReturn(
DevContext.builder()
.setDevOptionsEnabled(true)
.setCallingAppPackageName(TEST_PACKAGE_NAME)
.build());
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
AdSelectionOverrideTestCallback callback =
callAddOverride(
adSelectionService,
adSelectionConfig,
DUMMY_DECISION_LOGIC_JS,
DUMMY_TRUSTED_SCORING_SIGNALS);
assertTrue(callback.mIsSuccess);
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
AdSelectionDevOverridesHelper.calculateAdSelectionConfigId(
adSelectionConfig),
TEST_PACKAGE_NAME));
verify(mAdServicesLoggerSpy).logFledgeApiCallStats(SHORT_API_NAME_OVERRIDE, STATUS_SUCCESS);
verify(mAdServicesLoggerSpy)
.logApiCallStats(
aCallStatForFledgeApiWithStatus(SHORT_API_NAME_OVERRIDE, STATUS_SUCCESS));
}
@Test
public void testOverrideAdSelectionConfigRemoteInfoWithRevokedUserConsentSuccess()
throws Exception {
doReturn(AdServicesApiConsent.REVOKED).when(mConsentManagerMock).getConsent(any());
when(mDevContextFilter.createDevContext())
.thenReturn(
DevContext.builder()
.setDevOptionsEnabled(true)
.setCallingAppPackageName(TEST_PACKAGE_NAME)
.build());
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
AdSelectionOverrideTestCallback callback =
callAddOverride(
adSelectionService,
adSelectionConfig,
DUMMY_DECISION_LOGIC_JS,
DUMMY_TRUSTED_SCORING_SIGNALS);
assertTrue(callback.mIsSuccess);
assertFalse(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
AdSelectionDevOverridesHelper.calculateAdSelectionConfigId(
adSelectionConfig),
TEST_PACKAGE_NAME));
verify(mAdServicesLoggerSpy)
.logFledgeApiCallStats(SHORT_API_NAME_OVERRIDE, STATUS_USER_CONSENT_REVOKED);
verify(mAdServicesLoggerSpy)
.logApiCallStats(
aCallStatForFledgeApiWithStatus(
SHORT_API_NAME_OVERRIDE, STATUS_USER_CONSENT_REVOKED));
}
@Test
public void testOverrideAdSelectionConfigRemoteInfoFailsWithDevOptionsDisabled() {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
when(mDevContextFilter.createDevContext())
.thenReturn(DevContext.createForDevOptionsDisabled());
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
assertThrows(
SecurityException.class,
() ->
callAddOverride(
adSelectionService,
adSelectionConfig,
DUMMY_DECISION_LOGIC_JS,
DUMMY_TRUSTED_SCORING_SIGNALS));
assertFalse(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
AdSelectionDevOverridesHelper.calculateAdSelectionConfigId(
adSelectionConfig),
TEST_PACKAGE_NAME));
verify(mAdServicesLoggerSpy)
.logFledgeApiCallStats(SHORT_API_NAME_OVERRIDE, STATUS_INTERNAL_ERROR);
verify(mAdServicesLoggerSpy)
.logApiCallStats(
aCallStatForFledgeApiWithStatus(
SHORT_API_NAME_OVERRIDE, STATUS_INTERNAL_ERROR));
}
@Test
public void testRemoveAdSelectionConfigRemoteInfoOverrideSuccess() throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
when(mDevContextFilter.createDevContext())
.thenReturn(
DevContext.builder()
.setDevOptionsEnabled(true)
.setCallingAppPackageName(TEST_PACKAGE_NAME)
.build());
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
String adSelectionConfigId =
AdSelectionDevOverridesHelper.calculateAdSelectionConfigId(adSelectionConfig);
DBAdSelectionOverride dbAdSelectionOverride =
DBAdSelectionOverride.builder()
.setAdSelectionConfigId(adSelectionConfigId)
.setAppPackageName(TEST_PACKAGE_NAME)
.setDecisionLogicJS(DUMMY_DECISION_LOGIC_JS)
.setTrustedScoringSignals(DUMMY_TRUSTED_SCORING_SIGNALS.toString())
.build();
mAdSelectionEntryDao.persistAdSelectionOverride(dbAdSelectionOverride);
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId, TEST_PACKAGE_NAME));
AdSelectionOverrideTestCallback callback =
callRemoveOverride(adSelectionService, adSelectionConfig);
assertTrue(callback.mIsSuccess);
assertFalse(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId, TEST_PACKAGE_NAME));
verify(mAdServicesLoggerSpy)
.logFledgeApiCallStats(SHORT_API_NAME_REMOVE_OVERRIDE, STATUS_SUCCESS);
verify(mAdServicesLoggerSpy)
.logApiCallStats(
aCallStatForFledgeApiWithStatus(
SHORT_API_NAME_REMOVE_OVERRIDE, STATUS_SUCCESS));
}
@Test
public void testRemoveAdSelectionConfigRemoteInfoOverrideWithRevokedUserConsentSuccess()
throws Exception {
doReturn(AdServicesApiConsent.REVOKED).when(mConsentManagerMock).getConsent(any());
when(mDevContextFilter.createDevContext())
.thenReturn(
DevContext.builder()
.setDevOptionsEnabled(true)
.setCallingAppPackageName(TEST_PACKAGE_NAME)
.build());
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
String adSelectionConfigId =
AdSelectionDevOverridesHelper.calculateAdSelectionConfigId(adSelectionConfig);
DBAdSelectionOverride dbAdSelectionOverride =
DBAdSelectionOverride.builder()
.setAdSelectionConfigId(adSelectionConfigId)
.setAppPackageName(TEST_PACKAGE_NAME)
.setDecisionLogicJS(DUMMY_DECISION_LOGIC_JS)
.setTrustedScoringSignals(DUMMY_TRUSTED_SCORING_SIGNALS.toString())
.build();
mAdSelectionEntryDao.persistAdSelectionOverride(dbAdSelectionOverride);
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId, TEST_PACKAGE_NAME));
AdSelectionOverrideTestCallback callback =
callRemoveOverride(adSelectionService, adSelectionConfig);
assertTrue(callback.mIsSuccess);
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId, TEST_PACKAGE_NAME));
verify(mAdServicesLoggerSpy)
.logFledgeApiCallStats(SHORT_API_NAME_REMOVE_OVERRIDE, STATUS_USER_CONSENT_REVOKED);
verify(mAdServicesLoggerSpy)
.logApiCallStats(
aCallStatForFledgeApiWithStatus(
SHORT_API_NAME_REMOVE_OVERRIDE, STATUS_USER_CONSENT_REVOKED));
}
@Test
public void testRemoveAdSelectionConfigRemoteInfoOverrideFailsWithDevOptionsDisabled() {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
when(mDevContextFilter.createDevContext())
.thenReturn(DevContext.createForDevOptionsDisabled());
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
String adSelectionConfigId =
AdSelectionDevOverridesHelper.calculateAdSelectionConfigId(adSelectionConfig);
DBAdSelectionOverride dbAdSelectionOverride =
DBAdSelectionOverride.builder()
.setAdSelectionConfigId(adSelectionConfigId)
.setAppPackageName(TEST_PACKAGE_NAME)
.setDecisionLogicJS(DUMMY_DECISION_LOGIC_JS)
.setTrustedScoringSignals(DUMMY_TRUSTED_SCORING_SIGNALS.toString())
.build();
mAdSelectionEntryDao.persistAdSelectionOverride(dbAdSelectionOverride);
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId, TEST_PACKAGE_NAME));
assertThrows(
SecurityException.class,
() -> callRemoveOverride(adSelectionService, adSelectionConfig));
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId, TEST_PACKAGE_NAME));
verify(mAdServicesLoggerSpy)
.logFledgeApiCallStats(SHORT_API_NAME_REMOVE_OVERRIDE, STATUS_INTERNAL_ERROR);
verify(mAdServicesLoggerSpy)
.logApiCallStats(
aCallStatForFledgeApiWithStatus(
SHORT_API_NAME_REMOVE_OVERRIDE, STATUS_INTERNAL_ERROR));
}
@Test
public void testRemoveAdSelectionConfigRemoteInfoOverrideDoesNotDeleteWithIncorrectPackageName()
throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
String incorrectPackageName = "com.google.ppapi.test.incorrect";
when(mDevContextFilter.createDevContext())
.thenReturn(
DevContext.builder()
.setDevOptionsEnabled(true)
.setCallingAppPackageName(incorrectPackageName)
.build());
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
String adSelectionConfigId =
AdSelectionDevOverridesHelper.calculateAdSelectionConfigId(adSelectionConfig);
DBAdSelectionOverride dbAdSelectionOverride =
DBAdSelectionOverride.builder()
.setAdSelectionConfigId(adSelectionConfigId)
.setAppPackageName(TEST_PACKAGE_NAME)
.setDecisionLogicJS(DUMMY_DECISION_LOGIC_JS)
.setTrustedScoringSignals(DUMMY_TRUSTED_SCORING_SIGNALS.toString())
.build();
mAdSelectionEntryDao.persistAdSelectionOverride(dbAdSelectionOverride);
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId, TEST_PACKAGE_NAME));
AdSelectionOverrideTestCallback callback =
callRemoveOverride(adSelectionService, adSelectionConfig);
assertTrue(callback.mIsSuccess);
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId, TEST_PACKAGE_NAME));
verify(mAdServicesLoggerSpy)
.logFledgeApiCallStats(SHORT_API_NAME_REMOVE_OVERRIDE, STATUS_SUCCESS);
verify(mAdServicesLoggerSpy)
.logApiCallStats(
aCallStatForFledgeApiWithStatus(
SHORT_API_NAME_REMOVE_OVERRIDE, STATUS_SUCCESS));
}
@Test
public void testResetAllAdSelectionConfigRemoteOverridesDoesNotDeleteWithIncorrectPackageName()
throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
String incorrectPackageName = "com.google.ppapi.test.incorrect";
when(mDevContextFilter.createDevContext())
.thenReturn(
DevContext.builder()
.setDevOptionsEnabled(true)
.setCallingAppPackageName(incorrectPackageName)
.build());
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
AdSelectionConfig adSelectionConfig1 = mAdSelectionConfigBuilder.build();
AdSelectionConfig adSelectionConfig2 =
mAdSelectionConfigBuilder
.setSeller(AdTechIdentifier.fromString("adidas.com"))
.setDecisionLogicUri(Uri.parse("https://adidas.com/decisoin_logic_url"))
.build();
AdSelectionConfig adSelectionConfig3 =
mAdSelectionConfigBuilder
.setSeller(AdTechIdentifier.fromString("nike.com"))
.setDecisionLogicUri(Uri.parse("https://nike.com/decisoin_logic_url"))
.build();
String adSelectionConfigId1 =
AdSelectionDevOverridesHelper.calculateAdSelectionConfigId(adSelectionConfig1);
String adSelectionConfigId2 =
AdSelectionDevOverridesHelper.calculateAdSelectionConfigId(adSelectionConfig2);
String adSelectionConfigId3 =
AdSelectionDevOverridesHelper.calculateAdSelectionConfigId(adSelectionConfig3);
DBAdSelectionOverride dbAdSelectionOverride1 =
DBAdSelectionOverride.builder()
.setAdSelectionConfigId(adSelectionConfigId1)
.setAppPackageName(TEST_PACKAGE_NAME)
.setDecisionLogicJS(DUMMY_DECISION_LOGIC_JS)
.setTrustedScoringSignals(DUMMY_TRUSTED_SCORING_SIGNALS.toString())
.build();
DBAdSelectionOverride dbAdSelectionOverride2 =
DBAdSelectionOverride.builder()
.setAdSelectionConfigId(adSelectionConfigId2)
.setAppPackageName(TEST_PACKAGE_NAME)
.setDecisionLogicJS(DUMMY_DECISION_LOGIC_JS)
.setTrustedScoringSignals(DUMMY_TRUSTED_SCORING_SIGNALS.toString())
.build();
DBAdSelectionOverride dbAdSelectionOverride3 =
DBAdSelectionOverride.builder()
.setAdSelectionConfigId(adSelectionConfigId3)
.setAppPackageName(TEST_PACKAGE_NAME)
.setDecisionLogicJS(DUMMY_DECISION_LOGIC_JS)
.setTrustedScoringSignals(DUMMY_TRUSTED_SCORING_SIGNALS.toString())
.build();
mAdSelectionEntryDao.persistAdSelectionOverride(dbAdSelectionOverride1);
mAdSelectionEntryDao.persistAdSelectionOverride(dbAdSelectionOverride2);
mAdSelectionEntryDao.persistAdSelectionOverride(dbAdSelectionOverride3);
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId1, TEST_PACKAGE_NAME));
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId2, TEST_PACKAGE_NAME));
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId3, TEST_PACKAGE_NAME));
AdSelectionOverrideTestCallback callback = callResetAllOverrides(adSelectionService);
assertTrue(callback.mIsSuccess);
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId1, TEST_PACKAGE_NAME));
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId2, TEST_PACKAGE_NAME));
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId3, TEST_PACKAGE_NAME));
verify(mAdServicesLoggerSpy)
.logFledgeApiCallStats(SHORT_API_NAME_RESET_ALL_OVERRIDES, STATUS_SUCCESS);
verify(mAdServicesLoggerSpy)
.logApiCallStats(
aCallStatForFledgeApiWithStatus(
SHORT_API_NAME_RESET_ALL_OVERRIDES, STATUS_SUCCESS));
}
@Test
public void testResetAllAdSelectionConfigRemoteOverridesSuccess() throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
when(mDevContextFilter.createDevContext())
.thenReturn(
DevContext.builder()
.setDevOptionsEnabled(true)
.setCallingAppPackageName(TEST_PACKAGE_NAME)
.build());
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
AdSelectionConfig adSelectionConfig1 = mAdSelectionConfigBuilder.build();
AdSelectionConfig adSelectionConfig2 =
mAdSelectionConfigBuilder
.setSeller(AdTechIdentifier.fromString("adidas.com"))
.setDecisionLogicUri(Uri.parse("https://adidas.com/decisoin_logic_url"))
.build();
AdSelectionConfig adSelectionConfig3 =
mAdSelectionConfigBuilder
.setSeller(AdTechIdentifier.fromString("nike.com"))
.setDecisionLogicUri(Uri.parse("https://nike.com/decisoin_logic_url"))
.build();
String adSelectionConfigId1 =
AdSelectionDevOverridesHelper.calculateAdSelectionConfigId(adSelectionConfig1);
String adSelectionConfigId2 =
AdSelectionDevOverridesHelper.calculateAdSelectionConfigId(adSelectionConfig2);
String adSelectionConfigId3 =
AdSelectionDevOverridesHelper.calculateAdSelectionConfigId(adSelectionConfig3);
DBAdSelectionOverride dbAdSelectionOverride1 =
DBAdSelectionOverride.builder()
.setAdSelectionConfigId(adSelectionConfigId1)
.setAppPackageName(TEST_PACKAGE_NAME)
.setDecisionLogicJS(DUMMY_DECISION_LOGIC_JS)
.setTrustedScoringSignals(DUMMY_TRUSTED_SCORING_SIGNALS.toString())
.build();
DBAdSelectionOverride dbAdSelectionOverride2 =
DBAdSelectionOverride.builder()
.setAdSelectionConfigId(adSelectionConfigId2)
.setAppPackageName(TEST_PACKAGE_NAME)
.setDecisionLogicJS(DUMMY_DECISION_LOGIC_JS)
.setTrustedScoringSignals(DUMMY_TRUSTED_SCORING_SIGNALS.toString())
.build();
DBAdSelectionOverride dbAdSelectionOverride3 =
DBAdSelectionOverride.builder()
.setAdSelectionConfigId(adSelectionConfigId3)
.setAppPackageName(TEST_PACKAGE_NAME)
.setDecisionLogicJS(DUMMY_DECISION_LOGIC_JS)
.setTrustedScoringSignals(DUMMY_TRUSTED_SCORING_SIGNALS.toString())
.build();
mAdSelectionEntryDao.persistAdSelectionOverride(dbAdSelectionOverride1);
mAdSelectionEntryDao.persistAdSelectionOverride(dbAdSelectionOverride2);
mAdSelectionEntryDao.persistAdSelectionOverride(dbAdSelectionOverride3);
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId1, TEST_PACKAGE_NAME));
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId2, TEST_PACKAGE_NAME));
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId3, TEST_PACKAGE_NAME));
AdSelectionOverrideTestCallback callback = callResetAllOverrides(adSelectionService);
assertTrue(callback.mIsSuccess);
assertFalse(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId1, TEST_PACKAGE_NAME));
assertFalse(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId2, TEST_PACKAGE_NAME));
assertFalse(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId3, TEST_PACKAGE_NAME));
verify(mAdServicesLoggerSpy)
.logFledgeApiCallStats(SHORT_API_NAME_RESET_ALL_OVERRIDES, STATUS_SUCCESS);
verify(mAdServicesLoggerSpy)
.logApiCallStats(
aCallStatForFledgeApiWithStatus(
SHORT_API_NAME_RESET_ALL_OVERRIDES, STATUS_SUCCESS));
}
@Test
public void testResetAllAdSelectionConfigRemoteOverridesWithRevokedUserConsentSuccess()
throws Exception {
doReturn(AdServicesApiConsent.REVOKED).when(mConsentManagerMock).getConsent(any());
when(mDevContextFilter.createDevContext())
.thenReturn(
DevContext.builder()
.setDevOptionsEnabled(true)
.setCallingAppPackageName(TEST_PACKAGE_NAME)
.build());
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
AdSelectionConfig adSelectionConfig1 = mAdSelectionConfigBuilder.build();
AdSelectionConfig adSelectionConfig2 =
mAdSelectionConfigBuilder
.setSeller(AdTechIdentifier.fromString("adidas.com"))
.setDecisionLogicUri(Uri.parse("https://adidas.com/decisoin_logic_url"))
.build();
AdSelectionConfig adSelectionConfig3 =
mAdSelectionConfigBuilder
.setSeller(AdTechIdentifier.fromString("nike.com"))
.setDecisionLogicUri(Uri.parse("https://nike.com/decisoin_logic_url"))
.build();
String adSelectionConfigId1 =
AdSelectionDevOverridesHelper.calculateAdSelectionConfigId(adSelectionConfig1);
String adSelectionConfigId2 =
AdSelectionDevOverridesHelper.calculateAdSelectionConfigId(adSelectionConfig2);
String adSelectionConfigId3 =
AdSelectionDevOverridesHelper.calculateAdSelectionConfigId(adSelectionConfig3);
DBAdSelectionOverride dbAdSelectionOverride1 =
DBAdSelectionOverride.builder()
.setAdSelectionConfigId(adSelectionConfigId1)
.setAppPackageName(TEST_PACKAGE_NAME)
.setDecisionLogicJS(DUMMY_DECISION_LOGIC_JS)
.setTrustedScoringSignals(DUMMY_TRUSTED_SCORING_SIGNALS.toString())
.build();
DBAdSelectionOverride dbAdSelectionOverride2 =
DBAdSelectionOverride.builder()
.setAdSelectionConfigId(adSelectionConfigId2)
.setAppPackageName(TEST_PACKAGE_NAME)
.setDecisionLogicJS(DUMMY_DECISION_LOGIC_JS)
.setTrustedScoringSignals(DUMMY_TRUSTED_SCORING_SIGNALS.toString())
.build();
DBAdSelectionOverride dbAdSelectionOverride3 =
DBAdSelectionOverride.builder()
.setAdSelectionConfigId(adSelectionConfigId3)
.setAppPackageName(TEST_PACKAGE_NAME)
.setDecisionLogicJS(DUMMY_DECISION_LOGIC_JS)
.setTrustedScoringSignals(DUMMY_TRUSTED_SCORING_SIGNALS.toString())
.build();
mAdSelectionEntryDao.persistAdSelectionOverride(dbAdSelectionOverride1);
mAdSelectionEntryDao.persistAdSelectionOverride(dbAdSelectionOverride2);
mAdSelectionEntryDao.persistAdSelectionOverride(dbAdSelectionOverride3);
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId1, TEST_PACKAGE_NAME));
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId2, TEST_PACKAGE_NAME));
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId3, TEST_PACKAGE_NAME));
AdSelectionOverrideTestCallback callback = callResetAllOverrides(adSelectionService);
assertTrue(callback.mIsSuccess);
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId1, TEST_PACKAGE_NAME));
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId2, TEST_PACKAGE_NAME));
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId3, TEST_PACKAGE_NAME));
verify(mAdServicesLoggerSpy)
.logFledgeApiCallStats(
SHORT_API_NAME_RESET_ALL_OVERRIDES, STATUS_USER_CONSENT_REVOKED);
verify(mAdServicesLoggerSpy)
.logApiCallStats(
aCallStatForFledgeApiWithStatus(
SHORT_API_NAME_RESET_ALL_OVERRIDES, STATUS_USER_CONSENT_REVOKED));
}
@Test
public void testResetAllAdSelectionConfigRemoteOverridesFailsWithDevOptionsDisabled() {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
when(mDevContextFilter.createDevContext())
.thenReturn(DevContext.createForDevOptionsDisabled());
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
AdSelectionConfig adSelectionConfig1 = mAdSelectionConfigBuilder.build();
AdSelectionConfig adSelectionConfig2 =
mAdSelectionConfigBuilder
.setSeller(AdTechIdentifier.fromString("adidas.com"))
.setDecisionLogicUri(Uri.parse("https://adidas.com/decisoin_logic_url"))
.build();
AdSelectionConfig adSelectionConfig3 =
mAdSelectionConfigBuilder
.setSeller(AdTechIdentifier.fromString("nike.com"))
.setDecisionLogicUri(Uri.parse("https://nike.com/decisoin_logic_url"))
.build();
String adSelectionConfigId1 =
AdSelectionDevOverridesHelper.calculateAdSelectionConfigId(adSelectionConfig1);
String adSelectionConfigId2 =
AdSelectionDevOverridesHelper.calculateAdSelectionConfigId(adSelectionConfig2);
String adSelectionConfigId3 =
AdSelectionDevOverridesHelper.calculateAdSelectionConfigId(adSelectionConfig3);
DBAdSelectionOverride dbAdSelectionOverride1 =
DBAdSelectionOverride.builder()
.setAdSelectionConfigId(adSelectionConfigId1)
.setAppPackageName(TEST_PACKAGE_NAME)
.setDecisionLogicJS(DUMMY_DECISION_LOGIC_JS)
.setTrustedScoringSignals(DUMMY_TRUSTED_SCORING_SIGNALS.toString())
.build();
DBAdSelectionOverride dbAdSelectionOverride2 =
DBAdSelectionOverride.builder()
.setAdSelectionConfigId(adSelectionConfigId2)
.setAppPackageName(TEST_PACKAGE_NAME)
.setDecisionLogicJS(DUMMY_DECISION_LOGIC_JS)
.setTrustedScoringSignals(DUMMY_TRUSTED_SCORING_SIGNALS.toString())
.build();
DBAdSelectionOverride dbAdSelectionOverride3 =
DBAdSelectionOverride.builder()
.setAdSelectionConfigId(adSelectionConfigId3)
.setAppPackageName(TEST_PACKAGE_NAME)
.setDecisionLogicJS(DUMMY_DECISION_LOGIC_JS)
.setTrustedScoringSignals(DUMMY_TRUSTED_SCORING_SIGNALS.toString())
.build();
mAdSelectionEntryDao.persistAdSelectionOverride(dbAdSelectionOverride1);
mAdSelectionEntryDao.persistAdSelectionOverride(dbAdSelectionOverride2);
mAdSelectionEntryDao.persistAdSelectionOverride(dbAdSelectionOverride3);
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId1, TEST_PACKAGE_NAME));
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId2, TEST_PACKAGE_NAME));
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId3, TEST_PACKAGE_NAME));
assertThrows(SecurityException.class, () -> callResetAllOverrides(adSelectionService));
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId1, TEST_PACKAGE_NAME));
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId2, TEST_PACKAGE_NAME));
assertTrue(
mAdSelectionEntryDao.doesAdSelectionOverrideExistForPackageName(
adSelectionConfigId3, TEST_PACKAGE_NAME));
verify(mAdServicesLoggerSpy)
.logFledgeApiCallStats(SHORT_API_NAME_RESET_ALL_OVERRIDES, STATUS_INTERNAL_ERROR);
verify(mAdServicesLoggerSpy)
.logApiCallStats(
aCallStatForFledgeApiWithStatus(
SHORT_API_NAME_RESET_ALL_OVERRIDES, STATUS_INTERNAL_ERROR));
}
@Test
public void testCloseJSScriptEngineConnectionAtShutDown() {
JSScriptEngine jsScriptEngineMock = mock(JSScriptEngine.class);
when(JSScriptEngine.getInstance(any())).thenReturn(jsScriptEngineMock);
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
adSelectionService.destroy();
verify(jsScriptEngineMock).shutdown();
}
@Test
public void testReportImpressionForegroundCheckEnabledFails_throwsException() throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
when(mDevContextFilter.createDevContext())
.thenReturn(DevContext.createForDevOptionsDisabled());
doThrow(new WrongCallingApplicationStateException())
.when(mAppImportanceFilter)
.assertCallerIsInForeground(
Process.myUid(),
AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION,
null);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
ReportImpressionInput request =
new ReportImpressionInput.Builder()
.setAdSelectionId(INCORRECT_AD_SELECTION_ID)
.setAdSelectionConfig(adSelectionConfig)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
ReportImpressionTestCallback callback = callReportImpression(adSelectionService, request);
// The call fails because there is no ad selection with the given ID
assertFalse(callback.mIsSuccess);
assertEquals(
AdServicesStatusUtils.STATUS_BACKGROUND_CALLER,
callback.mFledgeErrorResponse.getStatusCode());
assertEquals(
AdServicesStatusUtils.ILLEGAL_STATE_BACKGROUND_CALLER_ERROR_MESSAGE,
callback.mFledgeErrorResponse.getErrorMessage());
}
@Test
public void testReportImpressionForegroundCheckDisabled_acceptBackgroundApp() throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
when(mDevContextFilter.createDevContext())
.thenReturn(DevContext.createForDevOptionsDisabled());
doThrow(new WrongCallingApplicationStateException())
.when(mAppImportanceFilter)
.assertCallerIsInForeground(
Process.myUid(),
AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION,
null);
AdSelectionConfig adSelectionConfig =
mAdSelectionConfigBuilder
.setCustomAudienceBuyers(ImmutableList.of())
.setPerBuyerSignals(ImmutableMap.of())
.build();
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
FlagsWithOverriddenFledgeChecks.createFlagsWithFledgeChecksDisabled(),
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
ReportImpressionInput request =
new ReportImpressionInput.Builder()
.setAdSelectionId(INCORRECT_AD_SELECTION_ID)
.setAdSelectionConfig(adSelectionConfig)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
ReportImpressionTestCallback callback = callReportImpression(adSelectionService, request);
// The call fails because there is no ad selection with the given ID
assertFalse(callback.mIsSuccess);
assertEquals(
UNABLE_TO_FIND_AD_SELECTION_WITH_GIVEN_ID,
callback.mFledgeErrorResponse.getErrorMessage());
}
@Test
public void testOverrideAdSelectionForegroundCheckEnabledFails_throwsException()
throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
when(mDevContextFilter.createDevContext())
.thenReturn(
DevContext.builder()
.setDevOptionsEnabled(true)
.setCallingAppPackageName(TEST_PACKAGE_NAME)
.build());
doThrow(new WrongCallingApplicationStateException())
.when(mAppImportanceFilter)
.assertCallerIsInForeground(
Process.myUid(),
AD_SERVICES_API_CALLED__API_NAME__OVERRIDE_AD_SELECTION_CONFIG_REMOTE_INFO,
null);
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
AdSelectionOverrideTestCallback callback =
callAddOverride(
adSelectionService,
adSelectionConfig,
DUMMY_DECISION_LOGIC_JS,
DUMMY_TRUSTED_SCORING_SIGNALS);
// The call fails because there is no ad selection with the given ID
assertFalse(callback.mIsSuccess);
assertEquals(
AdServicesStatusUtils.STATUS_BACKGROUND_CALLER,
callback.mFledgeErrorResponse.getStatusCode());
assertEquals(
AdServicesStatusUtils.ILLEGAL_STATE_BACKGROUND_CALLER_ERROR_MESSAGE,
callback.mFledgeErrorResponse.getErrorMessage());
}
@Test
public void testOverrideAdSelectionForegroundCheckDisabled_acceptBackgroundApp()
throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
when(mDevContextFilter.createDevContext())
.thenReturn(
DevContext.builder()
.setDevOptionsEnabled(true)
.setCallingAppPackageName(TEST_PACKAGE_NAME)
.build());
doThrow(new WrongCallingApplicationStateException())
.when(mAppImportanceFilter)
.assertCallerIsInForeground(
Process.myUid(),
AD_SERVICES_API_CALLED__API_NAME__OVERRIDE_AD_SELECTION_CONFIG_REMOTE_INFO,
null);
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
FlagsWithOverriddenFledgeChecks.createFlagsWithFledgeChecksDisabled(),
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
AdSelectionOverrideTestCallback callback =
callAddOverride(
adSelectionService,
adSelectionConfig,
DUMMY_DECISION_LOGIC_JS,
DUMMY_TRUSTED_SCORING_SIGNALS);
assertTrue(callback.mIsSuccess);
}
@Test
public void testRemoveOverrideForegroundCheckEnabledFails_throwsException() throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
when(mDevContextFilter.createDevContext())
.thenReturn(
DevContext.builder()
.setDevOptionsEnabled(true)
.setCallingAppPackageName(TEST_PACKAGE_NAME)
.build());
int apiName =
AD_SERVICES_API_CALLED__API_NAME__REMOVE_AD_SELECTION_CONFIG_REMOTE_INFO_OVERRIDE;
doThrow(new WrongCallingApplicationStateException())
.when(mAppImportanceFilter)
.assertCallerIsInForeground(Process.myUid(), apiName, null);
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
AdSelectionOverrideTestCallback callback =
callRemoveOverride(adSelectionService, adSelectionConfig);
// The call fails because there is no ad selection with the given ID
assertFalse(callback.mIsSuccess);
assertEquals(
AdServicesStatusUtils.STATUS_BACKGROUND_CALLER,
callback.mFledgeErrorResponse.getStatusCode());
assertEquals(
AdServicesStatusUtils.ILLEGAL_STATE_BACKGROUND_CALLER_ERROR_MESSAGE,
callback.mFledgeErrorResponse.getErrorMessage());
}
@Test
public void testRemoveOverrideForegroundCheckDisabled_acceptBackgroundApp() throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
when(mDevContextFilter.createDevContext())
.thenReturn(
DevContext.builder()
.setDevOptionsEnabled(true)
.setCallingAppPackageName(TEST_PACKAGE_NAME)
.build());
int apiName =
AD_SERVICES_API_CALLED__API_NAME__REMOVE_AD_SELECTION_CONFIG_REMOTE_INFO_OVERRIDE;
doThrow(new WrongCallingApplicationStateException())
.when(mAppImportanceFilter)
.assertCallerIsInForeground(Process.myUid(), apiName, null);
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
FlagsWithOverriddenFledgeChecks.createFlagsWithFledgeChecksDisabled(),
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
AdSelectionOverrideTestCallback callback =
callRemoveOverride(adSelectionService, adSelectionConfig);
assertTrue(callback.mIsSuccess);
}
@Test
public void testResetAllOverridesForegroundCheckEnabledFails_throwsException()
throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
when(mDevContextFilter.createDevContext())
.thenReturn(
DevContext.builder()
.setDevOptionsEnabled(true)
.setCallingAppPackageName(TEST_PACKAGE_NAME)
.build());
int apiName =
AD_SERVICES_API_CALLED__API_NAME__RESET_ALL_AD_SELECTION_CONFIG_REMOTE_OVERRIDES;
doThrow(new WrongCallingApplicationStateException())
.when(mAppImportanceFilter)
.assertCallerIsInForeground(Process.myUid(), apiName, null);
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
AdSelectionOverrideTestCallback callback = callResetAllOverrides(adSelectionService);
// The call fails because there is no ad selection with the given ID
assertFalse(callback.mIsSuccess);
assertEquals(
AdServicesStatusUtils.STATUS_BACKGROUND_CALLER,
callback.mFledgeErrorResponse.getStatusCode());
assertEquals(
AdServicesStatusUtils.ILLEGAL_STATE_BACKGROUND_CALLER_ERROR_MESSAGE,
callback.mFledgeErrorResponse.getErrorMessage());
}
@Test
public void testResetAllOverridesForegroundCheckDisabled_acceptBackgroundApp()
throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
when(mDevContextFilter.createDevContext())
.thenReturn(
DevContext.builder()
.setDevOptionsEnabled(true)
.setCallingAppPackageName(TEST_PACKAGE_NAME)
.build());
int apiName =
AD_SERVICES_API_CALLED__API_NAME__RESET_ALL_AD_SELECTION_CONFIG_REMOTE_OVERRIDES;
doThrow(new WrongCallingApplicationStateException())
.when(mAppImportanceFilter)
.assertCallerIsInForeground(Process.myUid(), apiName, null);
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
FlagsWithOverriddenFledgeChecks.createFlagsWithFledgeChecksDisabled(),
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
AdSelectionOverrideTestCallback callback = callResetAllOverrides(adSelectionService);
assertTrue(callback.mIsSuccess);
}
@Test
public void testReportImpressionFailsWithInvalidPackageName() throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
String otherPackageName = CommonFixture.TEST_PACKAGE_NAME + "incorrectPackage";
Uri sellerReportingUri = mMockWebServerRule.uriForPath(mSellerReportingPath);
Uri buyerReportingUri = mMockWebServerRule.uriForPath(mBuyerReportingPath);
String sellerDecisionLogicJs =
"function reportResult(ad_selection_config, render_url, bid, contextual_signals) {"
+ " \n"
+ " return {'status': 0, 'results': {'signals_for_buyer':"
+ " '{\"signals_for_buyer\":1}', 'reporting_url': '"
+ sellerReportingUri
+ "' } };\n"
+ "}";
String buyerDecisionLogicJs =
"function reportWin(ad_selection_signals, per_buyer_signals, signals_for_buyer,"
+ " contextual_signals, custom_audience_signals) { \n"
+ " return {'status': 0, 'results': {'reporting_url': '"
+ buyerReportingUri
+ "' } };\n"
+ "}";
mMockWebServerRule.startMockWebServer(
List.of(
new MockResponse().setBody(sellerDecisionLogicJs),
new MockResponse(),
new MockResponse()));
DBBuyerDecisionLogic dbBuyerDecisionLogic =
new DBBuyerDecisionLogic.Builder()
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setBuyerDecisionLogicJs(buyerDecisionLogicJs)
.build();
CustomAudienceSignals customAudienceSignals =
CustomAudienceSignalsFixture.aCustomAudienceSignals();
DBAdSelection dbAdSelection =
new DBAdSelection.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setCustomAudienceSignals(customAudienceSignals)
.setContextualSignals(mContextualSignals.toString())
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setWinningAdRenderUri(RENDER_URI)
.setWinningAdBid(BID)
.setCreationTimestamp(ACTIVATION_TIME)
.setCallerPackageName(CommonFixture.TEST_PACKAGE_NAME)
.build();
mAdSelectionEntryDao.persistAdSelection(dbAdSelection);
mAdSelectionEntryDao.persistBuyerDecisionLogic(dbBuyerDecisionLogic);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
when(mDevContextFilter.createDevContext())
.thenReturn(DevContext.createForDevOptionsDisabled());
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
ReportImpressionInput input =
new ReportImpressionInput.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setAdSelectionConfig(adSelectionConfig)
.setCallerPackageName(otherPackageName)
.build();
ReportImpressionTestCallback callback = callReportImpression(adSelectionService, input);
assertFalse(callback.mIsSuccess);
assertEquals(callback.mFledgeErrorResponse.getStatusCode(), STATUS_UNAUTHORIZED);
assertEquals(
callback.mFledgeErrorResponse.getErrorMessage(),
AdServicesStatusUtils
.SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_ON_BEHALF_ERROR_MESSAGE);
}
@Test
public void testReportImpressionFailsWhenAppCannotUsePPApi() throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
doThrow(new FledgeAuthorizationFilter.AdTechNotAllowedException())
.when(mFledgeAllowListsFilterSpy)
.assertAppCanUsePpapi(
TEST_PACKAGE_NAME, AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION);
Uri sellerReportingUri = mMockWebServerRule.uriForPath(mSellerReportingPath);
Uri buyerReportingUri = mMockWebServerRule.uriForPath(mBuyerReportingPath);
String sellerDecisionLogicJs =
"function reportResult(ad_selection_config, render_url, bid, contextual_signals) {"
+ " \n"
+ " return {'status': 0, 'results': {'signals_for_buyer':"
+ " '{\"signals_for_buyer\":1}', 'reporting_url': '"
+ sellerReportingUri
+ "' } };\n"
+ "}";
String buyerDecisionLogicJs =
"function reportWin(ad_selection_signals, per_buyer_signals, signals_for_buyer,"
+ " contextual_signals, custom_audience_signals) { \n"
+ " return {'status': 0, 'results': {'reporting_url': '"
+ buyerReportingUri
+ "' } };\n"
+ "}";
MockWebServer server =
mMockWebServerRule.startMockWebServer(
List.of(
new MockResponse().setBody(sellerDecisionLogicJs),
new MockResponse(),
new MockResponse()));
DBBuyerDecisionLogic dbBuyerDecisionLogic =
new DBBuyerDecisionLogic.Builder()
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setBuyerDecisionLogicJs(buyerDecisionLogicJs)
.build();
CustomAudienceSignals customAudienceSignals =
CustomAudienceSignalsFixture.aCustomAudienceSignals();
DBAdSelection dbAdSelection =
new DBAdSelection.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setCustomAudienceSignals(customAudienceSignals)
.setContextualSignals(mContextualSignals.toString())
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setWinningAdRenderUri(RENDER_URI)
.setWinningAdBid(BID)
.setCreationTimestamp(ACTIVATION_TIME)
.setCallerPackageName(CommonFixture.TEST_PACKAGE_NAME)
.build();
mAdSelectionEntryDao.persistAdSelection(dbAdSelection);
mAdSelectionEntryDao.persistBuyerDecisionLogic(dbBuyerDecisionLogic);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
when(mDevContextFilter.createDevContext())
.thenReturn(DevContext.createForDevOptionsDisabled());
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
ReportImpressionInput input =
new ReportImpressionInput.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setAdSelectionConfig(adSelectionConfig)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
ReportImpressionTestCallback callback = callReportImpression(adSelectionService, input);
assertFalse(callback.mIsSuccess);
assertEquals(STATUS_CALLER_NOT_ALLOWED, callback.mFledgeErrorResponse.getStatusCode());
assertEquals(
AdServicesStatusUtils.SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_ERROR_MESSAGE,
callback.mFledgeErrorResponse.getErrorMessage());
// TODO(b/242139312): Remove atLeastOnce once this the double logging is addressed
Mockito.verify(mAdServicesLoggerSpy, Mockito.atLeastOnce())
.logFledgeApiCallStats(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION,
STATUS_CALLER_NOT_ALLOWED);
verify(mAdServicesLoggerSpy, Mockito.atLeastOnce())
.logApiCallStats(
aCallStatForFledgeApiWithStatus(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION,
STATUS_CALLER_NOT_ALLOWED));
}
@Test
public void testReportImpressionFailsWhenAdTechFailsEnrollmentCheck() throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
// Reset flags to perform enrollment check
mFlags = new FlagsWithEnrollmentCheckEnabledSwitch(true);
Uri sellerReportingUri = mMockWebServerRule.uriForPath(mSellerReportingPath);
Uri buyerReportingUri = mMockWebServerRule.uriForPath(mBuyerReportingPath);
String sellerDecisionLogicJs =
"function reportResult(ad_selection_config, render_url, bid, contextual_signals) {"
+ " \n"
+ " return {'status': 0, 'results': {'signals_for_buyer':"
+ " '{\"signals_for_buyer\":1}', 'reporting_url': '"
+ sellerReportingUri
+ "' } };\n"
+ "}";
String buyerDecisionLogicJs =
"function reportWin(ad_selection_signals, per_buyer_signals, signals_for_buyer,"
+ " contextual_signals, custom_audience_signals) { \n"
+ " return {'status': 0, 'results': {'reporting_url': '"
+ buyerReportingUri
+ "' } };\n"
+ "}";
MockWebServer server =
mMockWebServerRule.startMockWebServer(
List.of(
new MockResponse().setBody(sellerDecisionLogicJs),
new MockResponse(),
new MockResponse()));
DBBuyerDecisionLogic dbBuyerDecisionLogic =
new DBBuyerDecisionLogic.Builder()
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setBuyerDecisionLogicJs(buyerDecisionLogicJs)
.build();
CustomAudienceSignals customAudienceSignals =
CustomAudienceSignalsFixture.aCustomAudienceSignals();
DBAdSelection dbAdSelection =
new DBAdSelection.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setCustomAudienceSignals(customAudienceSignals)
.setContextualSignals(mContextualSignals.toString())
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setWinningAdRenderUri(RENDER_URI)
.setWinningAdBid(BID)
.setCreationTimestamp(ACTIVATION_TIME)
.setCallerPackageName(CommonFixture.TEST_PACKAGE_NAME)
.build();
mAdSelectionEntryDao.persistAdSelection(dbAdSelection);
mAdSelectionEntryDao.persistBuyerDecisionLogic(dbBuyerDecisionLogic);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
when(mDevContextFilter.createDevContext())
.thenReturn(DevContext.createForDevOptionsDisabled());
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
ReportImpressionInput input =
new ReportImpressionInput.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setAdSelectionConfig(adSelectionConfig)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
ReportImpressionTestCallback callback = callReportImpression(adSelectionService, input);
assertFalse(callback.mIsSuccess);
assertEquals(STATUS_CALLER_NOT_ALLOWED, callback.mFledgeErrorResponse.getStatusCode());
assertEquals(
AdServicesStatusUtils.SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_ERROR_MESSAGE,
callback.mFledgeErrorResponse.getErrorMessage());
// TODO(b/242139312): Remove atLeastOnce once this the double logging is addressed
Mockito.verify(mAdServicesLoggerSpy, Mockito.atLeastOnce())
.logFledgeApiCallStats(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION,
STATUS_CALLER_NOT_ALLOWED);
verify(mAdServicesLoggerSpy, Mockito.atLeastOnce())
.logApiCallStats(
aCallStatForFledgeApiWithStatus(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION,
STATUS_CALLER_NOT_ALLOWED));
}
@Test
public void testReportImpressionSucceedsWhenAdTechPassesEnrollmentCheck() throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
// Reset flags to perform enrollment check
mFlags = new FlagsWithEnrollmentCheckEnabledSwitch(true);
Uri sellerReportingUri = mMockWebServerRule.uriForPath(mSellerReportingPath);
Uri buyerReportingUri = mMockWebServerRule.uriForPath(mBuyerReportingPath);
String sellerDecisionLogicJs =
"function reportResult(ad_selection_config, render_url, bid, contextual_signals) {"
+ " \n"
+ " return {'status': 0, 'results': {'signals_for_buyer':"
+ " '{\"signals_for_buyer\":1}', 'reporting_url': '"
+ sellerReportingUri
+ "' } };\n"
+ "}";
String buyerDecisionLogicJs =
"function reportWin(ad_selection_signals, per_buyer_signals, signals_for_buyer,"
+ " contextual_signals, custom_audience_signals) { \n"
+ " return {'status': 0, 'results': {'reporting_url': '"
+ buyerReportingUri
+ "' } };\n"
+ "}";
MockWebServer server =
mMockWebServerRule.startMockWebServer(
List.of(
new MockResponse().setBody(sellerDecisionLogicJs),
new MockResponse(),
new MockResponse()));
DBBuyerDecisionLogic dbBuyerDecisionLogic =
new DBBuyerDecisionLogic.Builder()
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setBuyerDecisionLogicJs(buyerDecisionLogicJs)
.build();
CustomAudienceSignals customAudienceSignals =
CustomAudienceSignalsFixture.aCustomAudienceSignals();
DBAdSelection dbAdSelection =
new DBAdSelection.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setCustomAudienceSignals(customAudienceSignals)
.setContextualSignals(mContextualSignals.toString())
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setWinningAdRenderUri(RENDER_URI)
.setWinningAdBid(BID)
.setCreationTimestamp(ACTIVATION_TIME)
.setCallerPackageName(CommonFixture.TEST_PACKAGE_NAME)
.build();
mAdSelectionEntryDao.persistAdSelection(dbAdSelection);
mAdSelectionEntryDao.persistBuyerDecisionLogic(dbBuyerDecisionLogic);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
doNothing()
.when(mFledgeAuthorizationFilterSpy)
.assertAdTechAllowed(
CONTEXT,
TEST_PACKAGE_NAME,
adSelectionConfig.getSeller(),
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION);
when(mDevContextFilter.createDevContext())
.thenReturn(DevContext.createForDevOptionsDisabled());
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
ReportImpressionInput input =
new ReportImpressionInput.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setAdSelectionConfig(adSelectionConfig)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
ReportImpressionTestCallback callback = callReportImpression(adSelectionService, input);
assertTrue(callback.mIsSuccess);
RecordedRequest fetchRequest = server.takeRequest();
assertEquals(mFetchJavaScriptPath, fetchRequest.getPath());
List<String> notifications =
ImmutableList.of(server.takeRequest().getPath(), server.takeRequest().getPath());
assertThat(notifications).containsExactly(mSellerReportingPath, mBuyerReportingPath);
verify(mAdServicesLoggerSpy)
.logFledgeApiCallStats(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION, STATUS_SUCCESS);
verify(mAdServicesLoggerSpy)
.logApiCallStats(
aCallStatForFledgeApiWithStatus(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION,
STATUS_SUCCESS));
}
private AdSelectionOverrideTestCallback callAddOverride(
AdSelectionServiceImpl adSelectionService,
AdSelectionConfig adSelectionConfig,
String decisionLogicJS,
AdSelectionSignals trustedScoringSignals)
throws Exception {
// Counted down in 1) callback and 2) logApiCall
CountDownLatch resultLatch = new CountDownLatch(2);
AdSelectionOverrideTestCallback callback = new AdSelectionOverrideTestCallback(resultLatch);
// Wait for the logging call, which happens after the callback
Answer<Void> countDownAnswer =
unused -> {
resultLatch.countDown();
return null;
};
doAnswer(countDownAnswer).when(mAdServicesLoggerSpy).logApiCallStats(any());
adSelectionService.overrideAdSelectionConfigRemoteInfo(
adSelectionConfig, decisionLogicJS, trustedScoringSignals, callback);
resultLatch.await();
return callback;
}
private AdSelectionOverrideTestCallback callRemoveOverride(
AdSelectionServiceImpl adSelectionService, AdSelectionConfig adSelectionConfig)
throws Exception {
// Counted down in 1) callback and 2) logApiCall
CountDownLatch resultLatch = new CountDownLatch(2);
AdSelectionOverrideTestCallback callback = new AdSelectionOverrideTestCallback(resultLatch);
// Wait for the logging call, which happens after the callback
Answer<Void> countDownAnswer =
unused -> {
resultLatch.countDown();
return null;
};
doAnswer(countDownAnswer).when(mAdServicesLoggerSpy).logApiCallStats(any());
adSelectionService.removeAdSelectionConfigRemoteInfoOverride(adSelectionConfig, callback);
resultLatch.await();
return callback;
}
@Test
public void testAdSelectionConfigInvalidSellerAndSellerUris() throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
Uri sellerReportingUri = mMockWebServerRule.uriForPath(mSellerReportingPath);
Uri buyerReportingUri = mMockWebServerRule.uriForPath(mBuyerReportingPath);
String sellerDecisionLogicJs =
"function reportResult(ad_selection_config, render_url, bid, contextual_signals) {"
+ " \n"
+ " return {'status': 0, 'results': {'signals_for_buyer':"
+ " '{\"signals_for_buyer\":1}', 'reporting_url': '"
+ sellerReportingUri
+ "' } };\n"
+ "}";
String buyerDecisionLogicJs =
"function reportWin(ad_selection_signals, per_buyer_signals, signals_for_buyer,"
+ " contextual_signals, custom_audience_signals) { \n"
+ " return {'status': 0, 'results': {'reporting_url': '"
+ buyerReportingUri
+ "' } };\n"
+ "}";
DBBuyerDecisionLogic dbBuyerDecisionLogic =
new DBBuyerDecisionLogic.Builder()
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setBuyerDecisionLogicJs(buyerDecisionLogicJs)
.build();
CustomAudienceSignals customAudienceSignals =
CustomAudienceSignalsFixture.aCustomAudienceSignals();
DBAdSelection dbAdSelection =
new DBAdSelection.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setCustomAudienceSignals(customAudienceSignals)
.setContextualSignals(mContextualSignals.toString())
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setWinningAdRenderUri(RENDER_URI)
.setWinningAdBid(BID)
.setCreationTimestamp(ACTIVATION_TIME)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
mAdSelectionEntryDao.persistAdSelection(dbAdSelection);
mAdSelectionEntryDao.persistBuyerDecisionLogic(dbBuyerDecisionLogic);
AdSelectionConfig invalidAdSelectionConfig =
AdSelectionConfigFixture.anAdSelectionConfigBuilder()
.setSeller(SELLER_VALID)
.setDecisionLogicUri(DECISION_LOGIC_URI_INCONSISTENT)
.build();
when(mDevContextFilter.createDevContext())
.thenReturn(DevContext.createForDevOptionsDisabled());
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
mFlags,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
ReportImpressionInput request =
new ReportImpressionInput.Builder()
.setAdSelectionId(INCORRECT_AD_SELECTION_ID)
.setAdSelectionConfig(invalidAdSelectionConfig)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
ReportImpressionTestCallback callback = callReportImpression(adSelectionService, request);
assertFalse(callback.mIsSuccess);
FledgeErrorResponse response = callback.mFledgeErrorResponse;
assertEquals(
"Error response code mismatch", STATUS_INVALID_ARGUMENT, response.getStatusCode());
verify(mAdServicesLoggerSpy)
.logFledgeApiCallStats(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION,
STATUS_INVALID_ARGUMENT);
verify(mAdServicesLoggerSpy)
.logApiCallStats(
aCallStatForFledgeApiWithStatus(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION,
STATUS_INVALID_ARGUMENT));
}
@Test
public void testReportImpressionSuccessThrottledSubsequentCallFailure() throws Exception {
doReturn(AdServicesApiConsent.GIVEN).when(mConsentManagerMock).getConsent(any());
Uri sellerReportingUri = mMockWebServerRule.uriForPath(mSellerReportingPath);
Uri buyerReportingUri = mMockWebServerRule.uriForPath(mBuyerReportingPath);
String sellerDecisionLogicJs =
"function reportResult(ad_selection_config, render_url, bid, contextual_signals) {"
+ " \n"
+ " return {'status': 0, 'results': {'signals_for_buyer':"
+ " '{\"signals_for_buyer\":1}', 'reporting_url': '"
+ sellerReportingUri
+ "' } };\n"
+ "}";
String buyerDecisionLogicJs =
"function reportWin(ad_selection_signals, per_buyer_signals, signals_for_buyer,"
+ " contextual_signals, custom_audience_signals) { \n"
+ " return {'status': 0, 'results': {'reporting_url': '"
+ buyerReportingUri
+ "' } };\n"
+ "}";
MockWebServer server =
mMockWebServerRule.startMockWebServer(
List.of(
new MockResponse().setBody(sellerDecisionLogicJs),
new MockResponse(),
new MockResponse()));
DBBuyerDecisionLogic dbBuyerDecisionLogic =
new DBBuyerDecisionLogic.Builder()
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setBuyerDecisionLogicJs(buyerDecisionLogicJs)
.build();
CustomAudienceSignals customAudienceSignals =
CustomAudienceSignalsFixture.aCustomAudienceSignals();
DBAdSelection dbAdSelection =
new DBAdSelection.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setCustomAudienceSignals(customAudienceSignals)
.setContextualSignals(mContextualSignals.toString())
.setBiddingLogicUri(BUYER_BIDDING_LOGIC_URI)
.setWinningAdRenderUri(RENDER_URI)
.setWinningAdBid(BID)
.setCreationTimestamp(ACTIVATION_TIME)
.setCallerPackageName(CommonFixture.TEST_PACKAGE_NAME)
.build();
mAdSelectionEntryDao.persistAdSelection(dbAdSelection);
mAdSelectionEntryDao.persistBuyerDecisionLogic(dbBuyerDecisionLogic);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
when(mDevContextFilter.createDevContext())
.thenReturn(DevContext.createForDevOptionsDisabled());
Flags flagsWithThrottling =
new Flags() {
@Override
public boolean getEnforceForegroundStatusForFledgeRunAdSelection() {
return true;
}
@Override
public boolean getEnforceForegroundStatusForFledgeReportImpression() {
return true;
}
@Override
public boolean getEnforceForegroundStatusForFledgeOverrides() {
return true;
}
@Override
public long getReportImpressionOverallTimeoutMs() {
return 500;
}
@Override
public boolean getEnforceIsolateMaxHeapSize() {
return false;
}
@Override
public boolean getDisableFledgeEnrollmentCheck() {
return true;
}
@Override
public float getSdkRequestPermitsPerSecond() {
// Getting default value of flags with throttling enabled
return 1;
}
};
Throttler.destroyExistingThrottler();
AdSelectionServiceImpl adSelectionService =
new AdSelectionServiceImpl(
mAdSelectionEntryDao,
mCustomAudienceDao,
mClient,
mDevContextFilter,
mAppImportanceFilter,
mLightweightExecutorService,
mBackgroundExecutorService,
CONTEXT,
mConsentManagerMock,
mAdServicesLoggerSpy,
flagsWithThrottling,
CallingAppUidSupplierProcessImpl.create(),
mFledgeAuthorizationFilterSpy,
mFledgeAllowListsFilterSpy);
ReportImpressionInput input =
new ReportImpressionInput.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setAdSelectionConfig(adSelectionConfig)
.setCallerPackageName(TEST_PACKAGE_NAME)
.build();
// First call should succeed
ReportImpressionTestCallback callbackFirstCall =
callReportImpression(adSelectionService, input);
// Immediately made subsequent call should fail
ReportImpressionTestCallback callbackSubsequentCall =
callReportImpression(adSelectionService, input);
assertTrue(callbackFirstCall.mIsSuccess);
RecordedRequest fetchRequest = server.takeRequest();
assertEquals(mFetchJavaScriptPath, fetchRequest.getPath());
verify(mAdServicesLoggerSpy)
.logFledgeApiCallStats(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION, STATUS_SUCCESS);
verify(mAdServicesLoggerSpy)
.logApiCallStats(
aCallStatForFledgeApiWithStatus(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION,
STATUS_SUCCESS));
assertFalse(callbackSubsequentCall.mIsSuccess);
assertEquals(
STATUS_RATE_LIMIT_REACHED,
callbackSubsequentCall.mFledgeErrorResponse.getStatusCode());
assertEquals(
callbackSubsequentCall.mFledgeErrorResponse.getErrorMessage(),
REPORT_IMPRESSION_THROTTLED);
resetThrottlerToNoRateLimits();
}
/**
* Given Throttler is singleton, & shared across tests, this method should be invoked after
* tests that impose restrictive rate limits.
*/
private void resetThrottlerToNoRateLimits() {
Throttler.destroyExistingThrottler();
final double noRateLimit = -1;
Throttler.getInstance(noRateLimit);
}
private AdSelectionOverrideTestCallback callResetAllOverrides(
AdSelectionServiceImpl adSelectionService) throws Exception {
// Counted down in 1) callback and 2) logApiCall
CountDownLatch resultLatch = new CountDownLatch(2);
AdSelectionOverrideTestCallback callback = new AdSelectionOverrideTestCallback(resultLatch);
// Wait for the logging call, which happens after the callback
Answer<Void> countDownAnswer =
unused -> {
resultLatch.countDown();
return null;
};
doAnswer(countDownAnswer).when(mAdServicesLoggerSpy).logApiCallStats(any());
adSelectionService.resetAllAdSelectionConfigRemoteOverrides(callback);
resultLatch.await();
return callback;
}
private ReportImpressionTestCallback callReportImpression(
AdSelectionServiceImpl adSelectionService, ReportImpressionInput requestParams)
throws Exception {
// Counted down in 1) callback and 2) logApiCall
CountDownLatch resultLatch = new CountDownLatch(2);
ReportImpressionTestCallback callback = new ReportImpressionTestCallback(resultLatch);
// Wait for the logging call, which happens after the callback
Answer<Void> countDownAnswer =
unused -> {
resultLatch.countDown();
return null;
};
doAnswer(countDownAnswer).when(mAdServicesLoggerSpy).logApiCallStats(any());
adSelectionService.reportImpression(requestParams, callback);
resultLatch.await();
return callback;
}
private String insertJsWait(long waitTime) {
return " const wait = (ms) => {\n"
+ " var start = new Date().getTime();\n"
+ " var end = start;\n"
+ " while(end < start + ms) {\n"
+ " end = new Date().getTime();\n"
+ " }\n"
+ " }\n"
+ String.format(Locale.ENGLISH, " wait(\"%d\");\n", waitTime);
}
public static class ReportImpressionTestCallback extends ReportImpressionCallback.Stub {
private final CountDownLatch mCountDownLatch;
boolean mIsSuccess = false;
FledgeErrorResponse mFledgeErrorResponse;
public ReportImpressionTestCallback(CountDownLatch countDownLatch) {
mCountDownLatch = countDownLatch;
}
@Override
public void onSuccess() throws RemoteException {
mIsSuccess = true;
mCountDownLatch.countDown();
}
@Override
public void onFailure(FledgeErrorResponse fledgeErrorResponse) throws RemoteException {
mFledgeErrorResponse = fledgeErrorResponse;
mCountDownLatch.countDown();
}
}
public static class AdSelectionOverrideTestCallback extends AdSelectionOverrideCallback.Stub {
private final CountDownLatch mCountDownLatch;
boolean mIsSuccess = false;
FledgeErrorResponse mFledgeErrorResponse;
public AdSelectionOverrideTestCallback(CountDownLatch countDownLatch) {
mCountDownLatch = countDownLatch;
}
@Override
public void onSuccess() throws RemoteException {
mIsSuccess = true;
mCountDownLatch.countDown();
}
@Override
public void onFailure(FledgeErrorResponse fledgeErrorResponse) throws RemoteException {
mFledgeErrorResponse = fledgeErrorResponse;
mCountDownLatch.countDown();
}
}
private static class FlagsWithEnrollmentCheckEnabledSwitch implements Flags {
private final boolean mEnrollmentCheckEnabled;
FlagsWithEnrollmentCheckEnabledSwitch(boolean enrollmentCheckEnabled) {
mEnrollmentCheckEnabled = enrollmentCheckEnabled;
}
@Override
public boolean getDisableFledgeEnrollmentCheck() {
return !mEnrollmentCheckEnabled;
}
@Override
public boolean getEnforceForegroundStatusForFledgeRunAdSelection() {
return true;
}
@Override
public boolean getEnforceForegroundStatusForFledgeReportImpression() {
return true;
}
@Override
public boolean getEnforceForegroundStatusForFledgeOverrides() {
return true;
}
@Override
public long getReportImpressionOverallTimeoutMs() {
return 500;
}
@Override
public boolean getEnforceIsolateMaxHeapSize() {
return false;
}
@Override
public float getSdkRequestPermitsPerSecond() {
// Unlimited rate for unit tests to avoid flake in tests due to rate limiting
return -1;
}
}
}