blob: 4aea3c27f0de9daed38b0a15f99b24aa7897910e [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 com.android.adservices.service.common.Throttler.ApiKey.FLEDGE_API_REPORT_IMPRESSIONS;
import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION;
import android.adservices.adselection.AdSelectionConfig;
import android.adservices.adselection.ReportImpressionCallback;
import android.adservices.adselection.ReportImpressionInput;
import android.adservices.common.AdSelectionSignals;
import android.adservices.common.AdServicesStatusUtils;
import android.adservices.common.FledgeErrorResponse;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.net.Uri;
import android.os.LimitExceededException;
import android.os.RemoteException;
import android.util.Pair;
import com.android.adservices.LogUtil;
import com.android.adservices.data.adselection.AdSelectionEntryDao;
import com.android.adservices.data.adselection.CustomAudienceSignals;
import com.android.adservices.data.adselection.DBAdSelectionEntry;
import com.android.adservices.service.Flags;
import com.android.adservices.service.common.AdServicesHttpsClient;
import com.android.adservices.service.common.AdTechUriValidator;
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.common.ValidatorUtil;
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.stats.AdServicesLogger;
import com.android.internal.util.Preconditions;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.util.concurrent.FluentFuture;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import org.json.JSONException;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
/** Encapsulates the Impression Reporting logic */
public class ImpressionReporter {
public static final String UNABLE_TO_FIND_AD_SELECTION_WITH_GIVEN_ID =
"Unable to find ad selection with given ID";
public static final String CALLER_PACKAGE_NAME_MISMATCH =
"Caller package name does not match name used in ad selection";
private static final String REPORTING_URI_FIELD_NAME = "reporting URI";
@VisibleForTesting
static final String REPORT_IMPRESSION_THROTTLED = "Report impression exceeded rate limit";
@NonNull private final Context mContext;
@NonNull private final AdSelectionEntryDao mAdSelectionEntryDao;
@NonNull private final AdServicesHttpsClient mAdServicesHttpsClient;
@NonNull private final ListeningExecutorService mLightweightExecutorService;
@NonNull private final ListeningExecutorService mBackgroundExecutorService;
@NonNull private final ScheduledThreadPoolExecutor mScheduledExecutor;
@NonNull private final ReportImpressionScriptEngine mJsEngine;
@NonNull private final ConsentManager mConsentManager;
@NonNull private final AdSelectionDevOverridesHelper mAdSelectionDevOverridesHelper;
@NonNull private final AdServicesLogger mAdServicesLogger;
@NonNull private final Flags mFlags;
@NonNull private final Supplier<Throttler> mThrottlerSupplier;
@NonNull private final AppImportanceFilter mAppImportanceFilter;
private final int mCallerUid;
@NonNull private final FledgeAuthorizationFilter mFledgeAuthorizationFilter;
@NonNull private final FledgeAllowListsFilter mFledgeAllowListsFilter;
public ImpressionReporter(
@NonNull Context context,
@NonNull ExecutorService lightweightExecutor,
@NonNull ExecutorService backgroundExecutor,
@NonNull ScheduledThreadPoolExecutor scheduledExecutor,
@NonNull AdSelectionEntryDao adSelectionEntryDao,
@NonNull AdServicesHttpsClient adServicesHttpsClient,
@NonNull ConsentManager consentManager,
@NonNull DevContext devContext,
@NonNull AdServicesLogger adServicesLogger,
@NonNull AppImportanceFilter appImportanceFilter,
@NonNull final Flags flags,
@NonNull final Supplier<Throttler> throttlerSupplier,
int callerUid,
@NonNull FledgeAuthorizationFilter fledgeAuthorizationFilter,
@NonNull final FledgeAllowListsFilter fledgeAllowListsFilter) {
Objects.requireNonNull(context);
Objects.requireNonNull(lightweightExecutor);
Objects.requireNonNull(backgroundExecutor);
Objects.requireNonNull(scheduledExecutor);
Objects.requireNonNull(adSelectionEntryDao);
Objects.requireNonNull(adServicesHttpsClient);
Objects.requireNonNull(consentManager);
Objects.requireNonNull(devContext);
Objects.requireNonNull(adServicesLogger);
Objects.requireNonNull(appImportanceFilter);
Objects.requireNonNull(flags);
Objects.requireNonNull(throttlerSupplier);
Objects.requireNonNull(fledgeAuthorizationFilter);
Objects.requireNonNull(fledgeAllowListsFilter);
mContext = context;
mLightweightExecutorService = MoreExecutors.listeningDecorator(lightweightExecutor);
mBackgroundExecutorService = MoreExecutors.listeningDecorator(backgroundExecutor);
mScheduledExecutor = scheduledExecutor;
mAdSelectionEntryDao = adSelectionEntryDao;
mAdServicesHttpsClient = adServicesHttpsClient;
mJsEngine =
new ReportImpressionScriptEngine(
mContext,
() -> flags.getEnforceIsolateMaxHeapSize(),
() -> flags.getIsolateMaxHeapSizeBytes());
mConsentManager = consentManager;
mAdSelectionDevOverridesHelper =
new AdSelectionDevOverridesHelper(devContext, mAdSelectionEntryDao);
mAdServicesLogger = adServicesLogger;
mFlags = flags;
mThrottlerSupplier = throttlerSupplier;
mAppImportanceFilter = appImportanceFilter;
mCallerUid = callerUid;
mFledgeAuthorizationFilter = fledgeAuthorizationFilter;
mFledgeAllowListsFilter = fledgeAllowListsFilter;
}
/** Invokes the onFailure function from the callback and handles the exception. */
private void invokeFailure(
@NonNull ReportImpressionCallback callback, int statusCode, String errorMessage) {
int resultCode = AdServicesStatusUtils.STATUS_UNSET;
try {
callback.onFailure(
new FledgeErrorResponse.Builder()
.setStatusCode(statusCode)
.setErrorMessage(errorMessage)
.build());
resultCode = statusCode;
} catch (RemoteException e) {
LogUtil.e(e, "Unable to send failed result to the callback");
resultCode = AdServicesStatusUtils.STATUS_UNKNOWN_ERROR;
throw e.rethrowFromSystemServer();
} finally {
mAdServicesLogger.logFledgeApiCallStats(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION, resultCode, 0);
}
}
/** Invokes the onSuccess function from the callback and handles the exception. */
private void invokeSuccess(@NonNull ReportImpressionCallback callback, int resultCode) {
try {
callback.onSuccess();
} catch (RemoteException e) {
LogUtil.e(e, "Unable to send successful result to the callback");
resultCode = AdServicesStatusUtils.STATUS_UNKNOWN_ERROR;
throw e.rethrowFromSystemServer();
} finally {
// TODO(b/233681870): Investigate implementation of actual failures in
// logs/metrics
mAdServicesLogger.logFledgeApiCallStats(
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION, resultCode, 0);
}
}
/**
* Run the impression report logic asynchronously. Invoked seller's reportResult() as well as
* the buyer's reportWin() in the case of a remarketing ad.
*
* <p>After invoking the javascript functions, invokes the onSuccess function of the callback
* and reports URIs resulting from the javascript functions.
*
* @param requestParams request parameters containing the {@code adSelectionId}, {@code
* adSelectionConfig}, and {@code callerPackageName}
* @param callback callback function to be called in case of success or failure
*/
public void reportImpression(
@NonNull ReportImpressionInput requestParams,
@NonNull ReportImpressionCallback callback) {
LogUtil.v("Executing reportImpression API");
// Getting PH flags in a non binder thread
FluentFuture<Long> timeoutFuture =
FluentFuture.from(
mLightweightExecutorService.submit(
mFlags::getReportImpressionOverallTimeoutMs));
timeoutFuture.addCallback(
new FutureCallback<Long>() {
@Override
public void onSuccess(Long timeout) {
invokeReporting(requestParams, callback);
}
@Override
public void onFailure(Throwable t) {
LogUtil.e(t, "Report Impression failed!");
notifyFailureToCaller(callback, t);
}
},
mLightweightExecutorService);
}
private void invokeReporting(
@NonNull ReportImpressionInput requestParams,
@NonNull ReportImpressionCallback callback) {
long adSelectionId = requestParams.getAdSelectionId();
AdSelectionConfig adSelectionConfig = requestParams.getAdSelectionConfig();
ListenableFuture<Void> validateRequestFuture =
Futures.submit(
() ->
validateRequest(
adSelectionConfig, requestParams.getCallerPackageName()),
mLightweightExecutorService);
FluentFuture.from(validateRequestFuture)
.transformAsync(
ignoredVoid ->
computeReportingUris(
adSelectionId,
adSelectionConfig,
requestParams.getCallerPackageName()),
mLightweightExecutorService)
.transform(
reportingUrisAndContext ->
notifySuccessToCaller(
callback,
reportingUrisAndContext.first,
reportingUrisAndContext.second),
mLightweightExecutorService)
.withTimeout(
mFlags.getReportImpressionOverallTimeoutMs(),
TimeUnit.MILLISECONDS,
// TODO(b/237103033): Comply with thread usage policy for AdServices;
// use a global scheduled executor
mScheduledExecutor)
.transformAsync(
reportingUrisAndContext ->
doReport(
reportingUrisAndContext.first,
reportingUrisAndContext.second),
mLightweightExecutorService)
.addCallback(
new FutureCallback<List<Void>>() {
@Override
public void onSuccess(List<Void> result) {
LogUtil.d("Report impression succeeded!");
}
@Override
public void onFailure(Throwable t) {
LogUtil.e(t, "Report Impression invocation failed!");
if (t instanceof ConsentManager.RevokedConsentException) {
invokeSuccess(
callback,
AdServicesStatusUtils.STATUS_USER_CONSENT_REVOKED);
} else {
notifyFailureToCaller(callback, t);
}
}
},
mLightweightExecutorService);
}
private Pair<ReportingUris, ReportingContext> notifySuccessToCaller(
@NonNull ReportImpressionCallback callback,
@NonNull ReportingUris reportingUris,
@NonNull ReportingContext ctx) {
invokeSuccess(callback, AdServicesStatusUtils.STATUS_SUCCESS);
return Pair.create(reportingUris, ctx);
}
private void notifyFailureToCaller(
@NonNull ReportImpressionCallback callback, @NonNull Throwable t) {
if (t instanceof IllegalArgumentException) {
invokeFailure(callback, AdServicesStatusUtils.STATUS_INVALID_ARGUMENT, t.getMessage());
} else if (t instanceof WrongCallingApplicationStateException) {
invokeFailure(callback, AdServicesStatusUtils.STATUS_BACKGROUND_CALLER, t.getMessage());
} else if (t instanceof FledgeAuthorizationFilter.AdTechNotAllowedException
|| t instanceof FledgeAllowListsFilter.AppNotAllowedException) {
invokeFailure(
callback, AdServicesStatusUtils.STATUS_CALLER_NOT_ALLOWED, t.getMessage());
} else if (t instanceof FledgeAuthorizationFilter.CallerMismatchException) {
invokeFailure(callback, AdServicesStatusUtils.STATUS_UNAUTHORIZED, t.getMessage());
} else if (t instanceof LimitExceededException) {
invokeFailure(
callback, AdServicesStatusUtils.STATUS_RATE_LIMIT_REACHED, t.getMessage());
} else {
invokeFailure(callback, AdServicesStatusUtils.STATUS_INTERNAL_ERROR, t.getMessage());
}
}
@NonNull
private ListenableFuture<List<Void>> doReport(
ReportingUris reportingUris, ReportingContext ctx) {
LogUtil.v("Reporting URIs");
ListenableFuture<Void> sellerFuture;
// Validate seller uri before reporting
AdTechUriValidator sellerValidator =
new AdTechUriValidator(
ValidatorUtil.AD_TECH_ROLE_SELLER,
ctx.mAdSelectionConfig.getSeller().toString(),
this.getClass().getSimpleName(),
REPORTING_URI_FIELD_NAME);
try {
sellerValidator.validate(reportingUris.sellerReportingUri);
// Perform reporting if no exception was thrown
sellerFuture = mAdServicesHttpsClient.reportUri(reportingUris.sellerReportingUri);
} catch (IllegalArgumentException e) {
LogUtil.v("Seller reporting URI validation failed!");
sellerFuture = Futures.immediateFuture(null);
}
ListenableFuture<Void> buyerFuture;
// Validate buyer uri if it exists
if (!Objects.isNull(reportingUris.buyerReportingUri)) {
CustomAudienceSignals customAudienceSignals =
Objects.requireNonNull(ctx.mDBAdSelectionEntry.getCustomAudienceSignals());
AdTechUriValidator buyerValidator =
new AdTechUriValidator(
ValidatorUtil.AD_TECH_ROLE_BUYER,
customAudienceSignals.getBuyer().toString(),
this.getClass().getSimpleName(),
REPORTING_URI_FIELD_NAME);
try {
buyerValidator.validate(reportingUris.buyerReportingUri);
// Perform reporting if no exception was thrown
buyerFuture = mAdServicesHttpsClient.reportUri(reportingUris.buyerReportingUri);
} catch (IllegalArgumentException e) {
LogUtil.v("Buyer reporting URI validation failed!");
buyerFuture = Futures.immediateFuture(null);
}
} else {
// In case of contextual ad
buyerFuture = Futures.immediateFuture(null);
}
return Futures.allAsList(sellerFuture, buyerFuture);
}
private FluentFuture<Pair<ReportingUris, ReportingContext>> computeReportingUris(
long adSelectionId, AdSelectionConfig adSelectionConfig, String callerPackageName) {
return fetchAdSelectionEntry(adSelectionId, callerPackageName)
.transformAsync(
dbAdSelectionEntry -> {
ReportingContext ctx = new ReportingContext();
ctx.mDBAdSelectionEntry = dbAdSelectionEntry;
ctx.mAdSelectionConfig = adSelectionConfig;
return fetchSellerDecisionLogic(ctx);
},
mLightweightExecutorService)
.transformAsync(
decisionLogicJsAndCtx ->
invokeSellerScript(
decisionLogicJsAndCtx.first, decisionLogicJsAndCtx.second),
mLightweightExecutorService)
.transformAsync(
sellerResultAndCtx ->
invokeBuyerScript(
sellerResultAndCtx.first, sellerResultAndCtx.second),
mLightweightExecutorService);
}
private FluentFuture<DBAdSelectionEntry> fetchAdSelectionEntry(
long adSelectionId, String callerPackageName) {
LogUtil.v(
"Fetching ad selection entry ID %d for caller \"%s\"",
adSelectionId, callerPackageName);
return FluentFuture.from(
mBackgroundExecutorService.submit(
() -> {
Preconditions.checkArgument(
mAdSelectionEntryDao.doesAdSelectionIdExist(adSelectionId),
UNABLE_TO_FIND_AD_SELECTION_WITH_GIVEN_ID);
Preconditions.checkArgument(
mAdSelectionEntryDao
.doesAdSelectionMatchingCallerPackageNameExist(
adSelectionId, callerPackageName),
CALLER_PACKAGE_NAME_MISMATCH);
return mAdSelectionEntryDao.getAdSelectionEntityById(adSelectionId);
}));
}
private FluentFuture<Pair<String, ReportingContext>> fetchSellerDecisionLogic(
ReportingContext ctx) {
LogUtil.v("Fetching Seller decision logic");
FluentFuture<String> jsOverrideFuture =
FluentFuture.from(
mBackgroundExecutorService.submit(
() ->
mAdSelectionDevOverridesHelper.getDecisionLogicOverride(
ctx.mAdSelectionConfig)));
return jsOverrideFuture
.transformAsync(
jsOverride -> {
if (jsOverride == null) {
return mAdServicesHttpsClient.fetchPayload(
ctx.mAdSelectionConfig.getDecisionLogicUri());
} else {
LogUtil.i(
"Developer options enabled and an override JS is provided "
+ "for the current ad selection config. "
+ "Skipping call to server.");
return Futures.immediateFuture(jsOverride);
}
},
mLightweightExecutorService)
.transform(
stringResult -> Pair.create(stringResult, ctx),
mLightweightExecutorService);
}
private FluentFuture<Pair<ReportImpressionScriptEngine.SellerReportingResult, ReportingContext>>
invokeSellerScript(String decisionLogicJs, ReportingContext ctx) {
LogUtil.v("Invoking seller script");
try {
return FluentFuture.from(
mJsEngine.reportResult(
decisionLogicJs,
ctx.mAdSelectionConfig,
ctx.mDBAdSelectionEntry.getWinningAdRenderUri(),
ctx.mDBAdSelectionEntry.getWinningAdBid(),
AdSelectionSignals.fromString(
ctx.mDBAdSelectionEntry.getContextualSignals())))
.transform(
sellerResult -> Pair.create(sellerResult, ctx),
mLightweightExecutorService);
} catch (JSONException e) {
throw new IllegalArgumentException("Invalid JSON data", e);
}
}
private FluentFuture<Pair<ReportingUris, ReportingContext>> invokeBuyerScript(
ReportImpressionScriptEngine.SellerReportingResult sellerReportingResult,
ReportingContext ctx) {
LogUtil.v("Invoking buyer script");
final boolean isContextual =
Objects.isNull(ctx.mDBAdSelectionEntry.getCustomAudienceSignals())
&& Objects.isNull(ctx.mDBAdSelectionEntry.getBuyerDecisionLogicJs());
if (isContextual) {
return FluentFuture.from(
Futures.immediateFuture(
Pair.create(
new ReportingUris(
null, sellerReportingResult.getReportingUri()),
ctx)));
}
final CustomAudienceSignals customAudienceSignals =
Objects.requireNonNull(ctx.mDBAdSelectionEntry.getCustomAudienceSignals());
try {
return FluentFuture.from(
mJsEngine.reportWin(
ctx.mDBAdSelectionEntry.getBuyerDecisionLogicJs(),
ctx.mAdSelectionConfig.getAdSelectionSignals(),
ctx.mAdSelectionConfig
.getPerBuyerSignals()
.get(customAudienceSignals.getBuyer()),
sellerReportingResult.getSignalsForBuyer(),
AdSelectionSignals.fromString(
ctx.mDBAdSelectionEntry.getContextualSignals()),
ctx.mDBAdSelectionEntry.getCustomAudienceSignals()))
.transform(
resultUri ->
Pair.create(
new ReportingUris(
resultUri,
sellerReportingResult.getReportingUri()),
ctx),
mLightweightExecutorService);
} catch (JSONException e) {
throw new IllegalArgumentException("Invalid JSON args", e);
}
}
/**
* Asserts that FLEDGE APIs and the Privacy Sandbox as a whole have user consent.
*
* @return an ignorable {@code null}
* @throws ConsentManager.RevokedConsentException if FLEDGE or the Privacy Sandbox do not have
* user consent
*/
private Void assertCallerHasUserConsent() throws ConsentManager.RevokedConsentException {
if (!mConsentManager.getConsent(mContext.getPackageManager()).isGiven()) {
throw new ConsentManager.RevokedConsentException();
}
return null;
}
/**
* Asserts that the caller has the appropriate foreground status, if enabled.
*
* @return an ignorable {@code null}
* @throws WrongCallingApplicationStateException if the foreground check is enabled and fails
*/
private Void maybeAssertForegroundCaller() throws WrongCallingApplicationStateException {
if (mFlags.getEnforceForegroundStatusForFledgeReportImpression()) {
mAppImportanceFilter.assertCallerIsInForeground(
mCallerUid, AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION, null);
}
return null;
}
/**
* Asserts that the package name provided by the caller is one of the packages of the calling
* uid.
*
* @param callerPackageName caller package name from the request
* @throws FledgeAuthorizationFilter.CallerMismatchException if the provided {@code
* callerPackageName} is not valid
* @return an ignorable {@code null}
*/
private Void assertCallerPackageName(String callerPackageName)
throws FledgeAuthorizationFilter.CallerMismatchException {
mFledgeAuthorizationFilter.assertCallingPackageName(
callerPackageName, mCallerUid, AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION);
return null;
}
/**
* Validates the {@code adSelectionConfig} from the request.
*
* @param adSelectionConfig the adSelectionConfig to be validated
* @throws IllegalArgumentException if the provided {@code adSelectionConfig} is not valid
* @return an ignorable {@code null}
*/
private Void validateAdSelectionConfig(AdSelectionConfig adSelectionConfig)
throws IllegalArgumentException {
AdSelectionConfigValidator adSelectionConfigValidator = new AdSelectionConfigValidator();
adSelectionConfigValidator.validate(adSelectionConfig);
return null;
}
/**
* Check if a certain ad tech is enrolled and authorized to perform the operation for the
* package.
*
* @param callerPackageName the package name to check against
* @param adSelectionConfig contains the ad tech to check against
* @throws FledgeAuthorizationFilter.AdTechNotAllowedException if the ad tech is not authorized
* to perform the operation
*/
private Void assertFledgeEnrollment(
AdSelectionConfig adSelectionConfig, String callerPackageName)
throws FledgeAuthorizationFilter.AdTechNotAllowedException {
if (!mFlags.getDisableFledgeEnrollmentCheck()) {
mFledgeAuthorizationFilter.assertAdTechAllowed(
mContext,
callerPackageName,
adSelectionConfig.getSeller(),
AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION);
}
return null;
}
/**
* Asserts the package is allowed to call PPAPI.
*
* @param callerPackageName the package name to be validated.
* @throws FledgeAllowListsFilter.AppNotAllowedException if the package is not authorized.
*/
private Void assertAppInAllowList(String callerPackageName)
throws FledgeAllowListsFilter.AppNotAllowedException {
mFledgeAllowListsFilter.assertAppCanUsePpapi(
callerPackageName, AD_SERVICES_API_CALLED__API_NAME__REPORT_IMPRESSION);
return null;
}
/**
* Ensures that the caller package is not throttled from calling current the API
*
* @param callerPackageName the package name, which should be verified
* @throws LimitExceededException if the provided {@code callerPackageName} exceeds its rate
* limits
* @return an ignorable {@code null}
*/
private Void assertCallerNotThrottled(final String callerPackageName)
throws LimitExceededException {
LogUtil.v("Checking if API is throttled for package: %s ", callerPackageName);
Throttler throttler = mThrottlerSupplier.get();
boolean isThrottled =
!throttler.tryAcquire(FLEDGE_API_REPORT_IMPRESSIONS, callerPackageName);
if (isThrottled) {
LogUtil.e("Rate Limit Reached for API: %s", FLEDGE_API_REPORT_IMPRESSIONS);
throw new LimitExceededException(REPORT_IMPRESSION_THROTTLED);
}
return null;
}
/**
* Validates the {@code reportImpression} request.
*
* @param adSelectionConfig the adSelectionConfig to be validated
* @param callerPackageName caller package name to be validated
* @throws FledgeAuthorizationFilter.CallerMismatchException if the {@code callerPackageName} is
* not valid
* @throws WrongCallingApplicationStateException if the foreground check is enabled and fails
* @throws FledgeAuthorizationFilter.AdTechNotAllowedException if the ad tech is not authorized
* to perform the operation
* @throws FledgeAllowListsFilter.AppNotAllowedException if the package is not authorized.
* @throws ConsentManager.RevokedConsentException if FLEDGE or the Privacy Sandbox do not have
* user consent
* @throws IllegalArgumentException if the provided {@code adSelectionConfig} is not valid
* @throws LimitExceededException if the provided {@code callerPackageName} exceeds the rate
* limits
* @return an ignorable {@code null}
*/
private Void validateRequest(AdSelectionConfig adSelectionConfig, String callerPackageName) {
LogUtil.v("Validating reportImpression Request");
assertCallerPackageName(callerPackageName);
maybeAssertForegroundCaller();
assertCallerNotThrottled(callerPackageName);
assertFledgeEnrollment(adSelectionConfig, callerPackageName);
assertAppInAllowList(callerPackageName);
assertCallerHasUserConsent();
validateAdSelectionConfig(adSelectionConfig);
return null;
}
private static class ReportingContext {
@NonNull AdSelectionConfig mAdSelectionConfig;
@NonNull DBAdSelectionEntry mDBAdSelectionEntry;
}
private static final class ReportingUris {
@Nullable public final Uri buyerReportingUri;
@NonNull public final Uri sellerReportingUri;
private ReportingUris(@Nullable Uri buyerReportingUri, @NonNull Uri sellerReportingUri) {
Objects.requireNonNull(sellerReportingUri);
this.buyerReportingUri = buyerReportingUri;
this.sellerReportingUri = sellerReportingUri;
}
}
}