blob: 7a30c49fb7db387d18ad1c2b6c49b35be5ece559 [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.measurement;
import static android.adservices.common.AdServicesStatusUtils.STATUS_BACKGROUND_CALLER;
import static android.adservices.common.AdServicesStatusUtils.STATUS_CALLER_NOT_ALLOWED;
import static android.adservices.common.AdServicesStatusUtils.STATUS_KILLSWITCH_ENABLED;
import static android.adservices.common.AdServicesStatusUtils.STATUS_PERMISSION_NOT_REQUESTED;
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.measurement.MeasurementManager.MEASUREMENT_API_STATE_DISABLED;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.adservices.common.AdServicesStatusUtils;
import android.adservices.common.CallerMetadata;
import android.adservices.measurement.DeletionParam;
import android.adservices.measurement.DeletionRequest;
import android.adservices.measurement.IMeasurementApiStatusCallback;
import android.adservices.measurement.IMeasurementCallback;
import android.adservices.measurement.MeasurementErrorResponse;
import android.adservices.measurement.MeasurementManager;
import android.adservices.measurement.RegistrationRequest;
import android.adservices.measurement.StatusParam;
import android.adservices.measurement.WebSourceParams;
import android.adservices.measurement.WebSourceRegistrationRequest;
import android.adservices.measurement.WebSourceRegistrationRequestInternal;
import android.adservices.measurement.WebTriggerParams;
import android.adservices.measurement.WebTriggerRegistrationRequest;
import android.adservices.measurement.WebTriggerRegistrationRequestInternal;
import android.content.Context;
import android.net.Uri;
import android.os.Binder;
import android.os.SystemClock;
import android.test.mock.MockContext;
import androidx.test.filters.SmallTest;
import com.android.adservices.service.Flags;
import com.android.adservices.service.FlagsFactory;
import com.android.adservices.service.common.AppImportanceFilter;
import com.android.adservices.service.common.PermissionHelper;
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.DevContext;
import com.android.adservices.service.devapi.DevContextFilter;
import com.android.adservices.service.stats.AdServicesLogger;
import com.android.adservices.service.stats.ApiCallStats;
import com.android.adservices.service.stats.Clock;
import com.android.compatibility.common.util.TestUtils;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/** Unit tests for {@link MeasurementServiceImpl} */
@SmallTest
public final class MeasurementServiceImplTest {
private static final String ALLOW_ALL_PACKAGES = "*";
private static final Uri APP_DESTINATION = Uri.parse("android-app://test.app-destination");
private static final String APP_PACKAGE_NAME = "app.package.name";
private static final Uri REGISTRATION_URI = WebUtil.validUri("https://registration-uri.test");
private static final Uri LOCALHOST = Uri.parse("https://localhost");
private static final String SDK_PACKAGE_NAME = "sdk.package.name";
private static final int TIMEOUT = 5_000;
private static final Uri WEB_DESTINATION = WebUtil.validUri("https://web-destination-uri.test");
@Mock private AdServicesLogger mMockAdServicesLogger;
@Mock private AppImportanceFilter mMockAppImportanceFilter;
@Mock private ConsentManager mMockConsentManager;
@Mock private Flags mMockFlags;
@Mock private MeasurementImpl mMockMeasurementImpl;
@Mock private Throttler mMockThrottler;
@Mock private MockContext mMockContext;
@Mock private DevContextFilter mDevContextFilter;
private MeasurementServiceImpl mMeasurementServiceImpl;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mMeasurementServiceImpl = createServiceWithMocks();
}
@Test
public void testRegisterSource_success() throws Exception {
runRunMocks(
Api.REGISTER_SOURCE,
new AccessDenier(),
() -> {
CountDownLatch countDownLatch = new CountDownLatch(1);
final List<Integer> list = new ArrayList<>();
createServiceWithMocks()
.register(
createRegistrationSourceRequest(),
createCallerMetadata(),
new IMeasurementCallback.Stub() {
@Override
public void onResult() {
list.add(STATUS_SUCCESS);
countDownLatch.countDown();
}
@Override
public void onFailure(
MeasurementErrorResponse responseParcel) {}
});
assertThat(countDownLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue();
assertThat(list.size()).isEqualTo(1);
assertThat(list.get(0)).isEqualTo(STATUS_SUCCESS);
assertPackageNameLogged();
});
}
private void registerSourceAndAssertFailure(@AdServicesStatusUtils.StatusCode int status)
throws InterruptedException {
registerSourceAndAssertFailure(status, createRegistrationSourceRequest());
}
private void registerSourceAndAssertFailure(@AdServicesStatusUtils.StatusCode int status,
RegistrationRequest registrationSourceRequest) throws InterruptedException {
final CountDownLatch countDownLatch = new CountDownLatch(1);
final List<MeasurementErrorResponse> errorContainer = new ArrayList<>();
createServiceWithMocks()
.register(
registrationSourceRequest,
createCallerMetadata(),
new IMeasurementCallback.Stub() {
@Override
public void onResult() {}
@Override
public void onFailure(MeasurementErrorResponse responseParcel) {
errorContainer.add(responseParcel);
countDownLatch.countDown();
}
});
assertThat(countDownLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue();
verify(mMockMeasurementImpl, never()).register(any(), anyBoolean(), anyLong());
Assert.assertEquals(1, errorContainer.size());
Assert.assertEquals(status, errorContainer.get(0).getStatusCode());
}
@Test
public void testRegisterSource_failureByDevContextAccessResolver() throws Exception {
runRunMocks(
Api.REGISTER_SOURCE,
new AccessDenier().deniedByDevContext(),
() -> registerSourceAndAssertFailure(
STATUS_UNAUTHORIZED, createRegistrationSourceRequest(true)));
}
@Test
public void testRegisterSource_failureByAppPackagePpApiAccessResolver() throws Exception {
runRunMocks(
Api.REGISTER_SOURCE,
new AccessDenier().deniedByAppPackagePpApiApp(),
() -> registerSourceAndAssertFailure(STATUS_CALLER_NOT_ALLOWED));
}
@Test
public void testRegisterSource_failureByAttributionPermissionResolver() throws Exception {
runRunMocks(
Api.REGISTER_SOURCE,
new AccessDenier().deniedByAttributionPermission(),
() -> registerSourceAndAssertFailure(STATUS_PERMISSION_NOT_REQUESTED));
}
@Test
public void testRegisterSource_failureByConsentResolver() throws Exception {
runRunMocks(
Api.REGISTER_SOURCE,
new AccessDenier().deniedByConsent(),
() -> registerSourceAndAssertFailure(STATUS_USER_CONSENT_REVOKED));
}
@Test
public void testRegisterSource_failureByForegroundEnforcementAccessResolver() throws Exception {
runRunMocks(
Api.REGISTER_SOURCE,
new AccessDenier().deniedByForegroundEnforcement(),
() -> registerSourceAndAssertFailure(STATUS_BACKGROUND_CALLER));
}
@Test
public void testRegisterSource_failureByKillSwitchAccessResolver() throws Exception {
runRunMocks(
Api.REGISTER_SOURCE,
new AccessDenier().deniedByKillSwitch(),
() -> registerSourceAndAssertFailure(STATUS_KILLSWITCH_ENABLED));
}
@Test
public void testRegisterSource_failureByThrottler() throws Exception {
runRunMocks(
Api.REGISTER_SOURCE,
new AccessDenier().deniedByThrottler(),
() -> registerSourceAndAssertFailure(STATUS_RATE_LIMIT_REACHED));
}
@Test
public void testRegisterTrigger_success() throws Exception {
runRunMocks(
Api.REGISTER_TRIGGER,
new AccessDenier(),
() -> {
CountDownLatch countDownLatch = new CountDownLatch(1);
final List<Integer> list = new ArrayList<>();
createServiceWithMocks()
.register(
createRegistrationTriggerRequest(),
createCallerMetadata(),
new IMeasurementCallback.Stub() {
@Override
public void onResult() {
list.add(STATUS_SUCCESS);
countDownLatch.countDown();
}
@Override
public void onFailure(
MeasurementErrorResponse responseParcel) {}
});
assertThat(countDownLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue();
assertThat(list.size()).isEqualTo(1);
assertThat(list.get(0)).isEqualTo(STATUS_SUCCESS);
assertPackageNameLogged();
});
}
private void registerTriggerAndAssertFailure(@AdServicesStatusUtils.StatusCode int status)
throws InterruptedException {
registerTriggerAndAssertFailure(status, createRegistrationTriggerRequest());
}
private void registerTriggerAndAssertFailure(@AdServicesStatusUtils.StatusCode int status,
RegistrationRequest registrationTriggerRequest) throws InterruptedException {
final CountDownLatch countDownLatch = new CountDownLatch(1);
final List<MeasurementErrorResponse> errorContainer = new ArrayList<>();
createServiceWithMocks()
.register(
registrationTriggerRequest,
createCallerMetadata(),
new IMeasurementCallback.Stub() {
@Override
public void onResult() {}
@Override
public void onFailure(MeasurementErrorResponse responseParcel) {
errorContainer.add(responseParcel);
countDownLatch.countDown();
}
});
assertThat(countDownLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue();
verify(mMockMeasurementImpl, never()).register(any(), anyBoolean(), anyLong());
Assert.assertEquals(1, errorContainer.size());
Assert.assertEquals(status, errorContainer.get(0).getStatusCode());
}
@Test
public void testRegisterTrigger_failureByDevContextAccessResolver() throws Exception {
runRunMocks(
Api.REGISTER_TRIGGER,
new AccessDenier().deniedByDevContext(),
() -> registerTriggerAndAssertFailure(
STATUS_UNAUTHORIZED, createRegistrationTriggerRequest(true)));
}
@Test
public void testRegisterTrigger_failureByAppPackagePpApiAccessResolver() throws Exception {
runRunMocks(
Api.REGISTER_TRIGGER,
new AccessDenier().deniedByAppPackagePpApiApp(),
() -> registerTriggerAndAssertFailure(STATUS_CALLER_NOT_ALLOWED));
}
@Test
public void testRegisterTrigger_failureByAttributionPermissionResolver() throws Exception {
runRunMocks(
Api.REGISTER_TRIGGER,
new AccessDenier().deniedByAttributionPermission(),
() -> registerTriggerAndAssertFailure(STATUS_PERMISSION_NOT_REQUESTED));
}
@Test
public void testRegisterTrigger_failureByConsentResolver() throws Exception {
runRunMocks(
Api.REGISTER_TRIGGER,
new AccessDenier().deniedByConsent(),
() -> registerTriggerAndAssertFailure(STATUS_USER_CONSENT_REVOKED));
}
@Test
public void testRegisterTrigger_failureByForegroundEnforcementAccessResolver()
throws Exception {
runRunMocks(
Api.REGISTER_TRIGGER,
new AccessDenier().deniedByForegroundEnforcement(),
() -> registerTriggerAndAssertFailure(STATUS_BACKGROUND_CALLER));
}
@Test
public void testRegisterTrigger_failureByKillSwitchAccessResolver() throws Exception {
runRunMocks(
Api.REGISTER_TRIGGER,
new AccessDenier().deniedByKillSwitch(),
() -> registerTriggerAndAssertFailure(STATUS_KILLSWITCH_ENABLED));
}
@Test
public void testRegisterTrigger_failureByThrottler() throws Exception {
runRunMocks(
Api.REGISTER_TRIGGER,
new AccessDenier().deniedByThrottler(),
() -> registerTriggerAndAssertFailure(STATUS_RATE_LIMIT_REACHED));
}
@Test
public void testRegister_invalidRequest_throwException() {
assertThrows(
NullPointerException.class,
() ->
createServiceWithMocks()
.register(
/* request = */ null,
createCallerMetadata(),
new IMeasurementCallback.Default()));
}
@Test
public void testRegister_invalidCallerMetadata_throwException() {
assertThrows(
NullPointerException.class,
() ->
createServiceWithMocks()
.register(
createRegistrationSourceRequest(),
/* callerMetadata = */ null,
new IMeasurementCallback.Default()));
}
@Test
public void testRegister_invalidCallback_throwException() {
assertThrows(
NullPointerException.class,
() ->
createServiceWithMocks()
.register(
createRegistrationSourceRequest(),
createCallerMetadata(),
/* callback = */ null));
}
@Test
public void testDeleteRegistrations_success() throws Exception {
runRunMocks(
Api.DELETE_REGISTRATIONS,
new AccessDenier(),
() -> {
CountDownLatch countDownLatch = new CountDownLatch(1);
final List<Integer> list = new ArrayList<>();
mMeasurementServiceImpl.deleteRegistrations(
createDeletionRequest(),
createCallerMetadata(),
new IMeasurementCallback.Stub() {
@Override
public void onResult() {
list.add(STATUS_SUCCESS);
countDownLatch.countDown();
}
@Override
public void onFailure(MeasurementErrorResponse responseParcel) {}
});
assertThat(countDownLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue();
assertThat(list.get(0)).isEqualTo(STATUS_SUCCESS);
assertThat(list.size()).isEqualTo(1);
assertPackageNameLogged();
});
}
private void deleteRegistrationsAndAssertFailure(@AdServicesStatusUtils.StatusCode int status)
throws InterruptedException {
final List<MeasurementErrorResponse> errorContainer = new ArrayList<>();
final CountDownLatch countDownLatch = new CountDownLatch(1);
createServiceWithMocks()
.deleteRegistrations(
createDeletionRequest(),
createCallerMetadata(),
new IMeasurementCallback.Stub() {
@Override
public void onResult() {}
@Override
public void onFailure(MeasurementErrorResponse errorResponse) {
errorContainer.add(errorResponse);
countDownLatch.countDown();
}
});
assertThat(countDownLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue();
verify(mMockMeasurementImpl, never()).deleteRegistrations(any());
Assert.assertEquals(1, errorContainer.size());
Assert.assertEquals(status, errorContainer.get(0).getStatusCode());
}
@Test
public void testDeleteRegistrations_failureByAppPackagePpApiAccessResolver() throws Exception {
runRunMocks(
Api.DELETE_REGISTRATIONS,
new AccessDenier().deniedByAppPackagePpApiApp(),
() -> deleteRegistrationsAndAssertFailure(STATUS_CALLER_NOT_ALLOWED));
}
@Test
public void testDeleteRegistrations_failureByAppPackageWebContextClientAccessResolver()
throws Exception {
runRunMocks(
Api.DELETE_REGISTRATIONS,
new AccessDenier().deniedByAppPackageWebContextClientApp(),
() -> deleteRegistrationsAndAssertFailure(STATUS_CALLER_NOT_ALLOWED));
}
@Test
public void testDeleteRegistrations_failureByForegroundEnforcementAccessResolver()
throws Exception {
runRunMocks(
Api.DELETE_REGISTRATIONS,
new AccessDenier().deniedByForegroundEnforcement(),
() -> deleteRegistrationsAndAssertFailure(STATUS_BACKGROUND_CALLER));
}
@Test
public void testDeleteRegistrations_failureByKillSwitchAccessResolver() throws Exception {
runRunMocks(
Api.DELETE_REGISTRATIONS,
new AccessDenier().deniedByKillSwitch(),
() -> deleteRegistrationsAndAssertFailure(STATUS_KILLSWITCH_ENABLED));
}
@Test
public void testDeleteRegistrations_failureByThrottler() throws Exception {
runRunMocks(
Api.DELETE_REGISTRATIONS,
new AccessDenier().deniedByThrottler(),
() -> deleteRegistrationsAndAssertFailure(STATUS_RATE_LIMIT_REACHED));
}
@Test
public void testDeleteRegistrations_invalidRequest_throwException() {
assertThrows(
NullPointerException.class,
() ->
createServiceWithMocks()
.deleteRegistrations(
/* request = */ null,
createCallerMetadata(),
new IMeasurementCallback.Default()));
}
@Test
public void testDeleteRegistrations_invalidCallerMetadata_throwException() {
assertThrows(
NullPointerException.class,
() ->
createServiceWithMocks()
.deleteRegistrations(
createDeletionRequest(),
/* callerMetadata = */ null,
new IMeasurementCallback.Default()));
}
@Test
public void testDeleteRegistrations_invalidCallback_throwException() {
assertThrows(
NullPointerException.class,
() ->
createServiceWithMocks()
.deleteRegistrations(
createDeletionRequest(),
createCallerMetadata(),
/* callback = */ null));
}
@Test
public void testGetMeasurementApiStatus_success() throws Exception {
runRunMocks(
Api.STATUS,
new AccessDenier(),
() -> {
CountDownLatch countDownLatch = new CountDownLatch(1);
final AtomicInteger resultWrapper = new AtomicInteger();
createServiceWithMocks()
.getMeasurementApiStatus(
createStatusParam(),
createCallerMetadata(),
new IMeasurementApiStatusCallback.Stub() {
@Override
public void onResult(int result) {
resultWrapper.set(result);
countDownLatch.countDown();
}
});
assertThat(countDownLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue();
assertThat(resultWrapper.get())
.isEqualTo(MeasurementManager.MEASUREMENT_API_STATE_ENABLED);
assertPackageNameLogged();
});
}
private void getMeasurementApiStatusAndAssertFailure() throws InterruptedException {
final CountDownLatch countDownLatchAny = new CountDownLatch(1);
final AtomicInteger resultWrapper = new AtomicInteger();
createServiceWithMocks()
.getMeasurementApiStatus(
createStatusParam(),
createCallerMetadata(),
new IMeasurementApiStatusCallback.Stub() {
@Override
public void onResult(int result) {
resultWrapper.set(result);
countDownLatchAny.countDown();
}
});
assertThat(countDownLatchAny.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue();
assertThat(resultWrapper.get()).isEqualTo(MEASUREMENT_API_STATE_DISABLED);
}
@Test
public void testGetMeasurementApiStatus_failureByAppPackagePpApiAccessResolver()
throws Exception {
runRunMocks(
Api.STATUS,
new AccessDenier().deniedByAppPackagePpApiApp(),
this::getMeasurementApiStatusAndAssertFailure);
}
@Test
public void testGetMeasurementApiStatus_failureByForegroundEnforcementAccessResolver()
throws Exception {
runRunMocks(
Api.STATUS,
new AccessDenier().deniedByForegroundEnforcement(),
this::getMeasurementApiStatusAndAssertFailure);
}
@Test
public void testGetMeasurementApiStatus_failureByKillSwitchAccessResolver() throws Exception {
runRunMocks(
Api.STATUS,
new AccessDenier().deniedByKillSwitch(),
this::getMeasurementApiStatusAndAssertFailure);
}
@Test
public void testGetMeasurementApiStatus_failureByConsentAccessResolver() throws Exception {
runRunMocks(
Api.STATUS,
new AccessDenier().deniedByConsent(),
this::getMeasurementApiStatusAndAssertFailure);
}
@Test
public void testGetMeasurementApiStatus_invalidRequest_throwException() {
assertThrows(
NullPointerException.class,
() ->
mMeasurementServiceImpl.getMeasurementApiStatus(
/* request = */ null,
createCallerMetadata(),
new IMeasurementApiStatusCallback.Default()));
}
@Test
public void testGetMeasurementApiStatus_invalidCallerMetadata_throwException() {
assertThrows(
NullPointerException.class,
() ->
mMeasurementServiceImpl.getMeasurementApiStatus(
createStatusParam(),
/* callerMetadata = */ null,
new IMeasurementApiStatusCallback.Default()));
}
@Test
public void testGetMeasurementApiStatus_invalidCallback_throwException() {
assertThrows(
NullPointerException.class,
() ->
mMeasurementServiceImpl.getMeasurementApiStatus(
createStatusParam(),
createCallerMetadata(),
/* callback = */ null));
}
@Test
public void registerWebSource_success() throws Exception {
runRunMocks(
Api.REGISTER_WEB_SOURCE,
new AccessDenier(),
() -> {
CountDownLatch countDownLatch = new CountDownLatch(1);
final List<Integer> list = new ArrayList<>();
createServiceWithMocks()
.registerWebSource(
createWebSourceRegistrationRequest(),
createCallerMetadata(),
new IMeasurementCallback.Stub() {
@Override
public void onResult() {
list.add(STATUS_SUCCESS);
countDownLatch.countDown();
}
@Override
public void onFailure(
MeasurementErrorResponse
measurementErrorResponse) {}
});
assertThat(countDownLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue();
assertThat(list.get(0)).isEqualTo(STATUS_SUCCESS);
assertThat(list.size()).isEqualTo(1);
assertPackageNameLogged();
});
}
private void registerWebSourceAndAssertFailure(@AdServicesStatusUtils.StatusCode int status)
throws InterruptedException {
registerWebSourceAndAssertFailure(status, createWebSourceRegistrationRequest());
}
private void registerWebSourceAndAssertFailure(@AdServicesStatusUtils.StatusCode int status,
WebSourceRegistrationRequestInternal webSourceRegistrationRequest)
throws InterruptedException {
final CountDownLatch countDownLatch = new CountDownLatch(1);
final List<MeasurementErrorResponse> errorContainer = new ArrayList<>();
createServiceWithMocks()
.registerWebSource(
webSourceRegistrationRequest,
createCallerMetadata(),
new IMeasurementCallback.Stub() {
@Override
public void onResult() {}
@Override
public void onFailure(MeasurementErrorResponse responseParcel) {
errorContainer.add(responseParcel);
countDownLatch.countDown();
}
});
assertThat(countDownLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue();
verify(mMockMeasurementImpl, never()).registerWebSource(any(), anyBoolean(), anyLong());
Assert.assertEquals(1, errorContainer.size());
Assert.assertEquals(status, errorContainer.get(0).getStatusCode());
}
@Test
public void testRegisterWebSource_failureByDevContextAccessResolver() throws Exception {
runRunMocks(
Api.REGISTER_WEB_SOURCE,
new AccessDenier().deniedByDevContext(),
() -> registerWebSourceAndAssertFailure(
STATUS_UNAUTHORIZED, createWebSourceRegistrationRequest(true)));
}
@Test
public void testRegisterWebSource_failureByAppPackagePpApiAccessResolver() throws Exception {
runRunMocks(
Api.REGISTER_WEB_SOURCE,
new AccessDenier().deniedByAppPackagePpApiApp(),
() -> registerWebSourceAndAssertFailure(STATUS_CALLER_NOT_ALLOWED));
}
@Test
public void testRegisterWebSource_failureByAppPackageWebContextClientAccessResolver()
throws Exception {
runRunMocks(
Api.REGISTER_WEB_SOURCE,
new AccessDenier().deniedByAppPackageWebContextClientApp(),
() -> registerWebSourceAndAssertFailure(STATUS_CALLER_NOT_ALLOWED));
}
@Test
public void testRegisterWebSource_failureByAttributionPermissionResolver() throws Exception {
runRunMocks(
Api.REGISTER_WEB_SOURCE,
new AccessDenier().deniedByAttributionPermission(),
() -> registerWebSourceAndAssertFailure(STATUS_PERMISSION_NOT_REQUESTED));
}
@Test
public void testRegisterWebSource_failureByConsentResolver() throws Exception {
runRunMocks(
Api.REGISTER_WEB_SOURCE,
new AccessDenier().deniedByConsent(),
() -> registerWebSourceAndAssertFailure(STATUS_USER_CONSENT_REVOKED));
}
@Test
public void testRegisterWebSource_failureByForegroundEnforcementAccessResolver()
throws Exception {
runRunMocks(
Api.REGISTER_WEB_SOURCE,
new AccessDenier().deniedByForegroundEnforcement(),
() -> registerWebSourceAndAssertFailure(STATUS_BACKGROUND_CALLER));
}
@Test
public void testRegisterWebSource_failureByKillSwitchAccessResolver() throws Exception {
runRunMocks(
Api.REGISTER_WEB_SOURCE,
new AccessDenier().deniedByKillSwitch(),
() -> registerWebSourceAndAssertFailure(STATUS_KILLSWITCH_ENABLED));
}
@Test
public void testRegisterWebSource_failureByThrottler() throws Exception {
runRunMocks(
Api.REGISTER_WEB_SOURCE,
new AccessDenier().deniedByThrottler(),
() -> registerWebSourceAndAssertFailure(STATUS_RATE_LIMIT_REACHED));
}
@Test
public void registerWebSource_invalidRequest_throwException() {
assertThrows(
NullPointerException.class,
() ->
mMeasurementServiceImpl.registerWebSource(
/* request = */ null,
createCallerMetadata(),
new IMeasurementCallback.Default()));
}
@Test
public void registerWebSource_invalidCallerMetadata_throwException() {
assertThrows(
NullPointerException.class,
() ->
mMeasurementServiceImpl.registerWebSource(
createWebSourceRegistrationRequest(),
/* callerMetadata */ null,
new IMeasurementCallback.Default()));
}
@Test
public void registerWebSource_invalidCallback_throwException() {
assertThrows(
NullPointerException.class,
() ->
mMeasurementServiceImpl.registerWebSource(
createWebSourceRegistrationRequest(),
createCallerMetadata(),
/* callback = */ null));
}
@Test
public void registerWebTrigger_success() throws Exception {
runRunMocks(
Api.REGISTER_WEB_TRIGGER,
new AccessDenier(),
() -> {
CountDownLatch countDownLatch = new CountDownLatch(1);
final List<Integer> list = new ArrayList<>();
createServiceWithMocks()
.registerWebTrigger(
createWebTriggerRegistrationRequest(),
createCallerMetadata(),
new IMeasurementCallback.Stub() {
@Override
public void onResult() {
list.add(STATUS_SUCCESS);
countDownLatch.countDown();
}
@Override
public void onFailure(
MeasurementErrorResponse
measurementErrorResponse) {}
});
assertThat(countDownLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue();
assertThat(list.get(0)).isEqualTo(STATUS_SUCCESS);
assertThat(list.size()).isEqualTo(1);
assertPackageNameLogged();
});
}
private void registerWebTriggerAndAssertFailure(@AdServicesStatusUtils.StatusCode int status)
throws InterruptedException {
registerWebTriggerAndAssertFailure(status, createWebTriggerRegistrationRequest());
}
private void registerWebTriggerAndAssertFailure(@AdServicesStatusUtils.StatusCode int status,
WebTriggerRegistrationRequestInternal webTriggerRegistrationRequest)
throws InterruptedException {
final CountDownLatch countDownLatch = new CountDownLatch(1);
final List<MeasurementErrorResponse> errorContainer = new ArrayList<>();
createServiceWithMocks()
.registerWebTrigger(
webTriggerRegistrationRequest,
createCallerMetadata(),
new IMeasurementCallback.Stub() {
@Override
public void onResult() {}
@Override
public void onFailure(MeasurementErrorResponse responseParcel) {
errorContainer.add(responseParcel);
countDownLatch.countDown();
}
});
assertThat(countDownLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue();
verify(mMockMeasurementImpl, never()).registerWebTrigger(any(), anyBoolean(), anyLong());
Assert.assertEquals(1, errorContainer.size());
Assert.assertEquals(status, errorContainer.get(0).getStatusCode());
}
@Test
public void testRegisterWebTrigger_failureByDevContextAccessResolver() throws Exception {
runRunMocks(
Api.REGISTER_WEB_TRIGGER,
new AccessDenier().deniedByDevContext(),
() -> registerWebTriggerAndAssertFailure(
STATUS_UNAUTHORIZED, createWebTriggerRegistrationRequest(true)));
}
@Test
public void testRegisterWebTrigger_failureByAppPackagePpApiAccessResolver() throws Exception {
runRunMocks(
Api.REGISTER_WEB_TRIGGER,
new AccessDenier().deniedByAppPackagePpApiApp(),
() -> registerWebTriggerAndAssertFailure(STATUS_CALLER_NOT_ALLOWED));
}
@Test
public void testRegisterWebTrigger_failureByAttributionPermissionResolver() throws Exception {
runRunMocks(
Api.REGISTER_WEB_TRIGGER,
new AccessDenier().deniedByAttributionPermission(),
() -> registerWebTriggerAndAssertFailure(STATUS_PERMISSION_NOT_REQUESTED));
}
@Test
public void testRegisterWebTrigger_failureByConsentResolver() throws Exception {
runRunMocks(
Api.REGISTER_WEB_TRIGGER,
new AccessDenier().deniedByConsent(),
() -> registerWebTriggerAndAssertFailure(STATUS_USER_CONSENT_REVOKED));
}
@Test
public void testRegisterWebTrigger_failureByForegroundEnforcementAccessResolver()
throws Exception {
runRunMocks(
Api.REGISTER_WEB_TRIGGER,
new AccessDenier().deniedByForegroundEnforcement(),
() -> registerWebTriggerAndAssertFailure(STATUS_BACKGROUND_CALLER));
}
@Test
public void testRegisterWebTrigger_failureByKillSwitchAccessResolver() throws Exception {
runRunMocks(
Api.REGISTER_WEB_TRIGGER,
new AccessDenier().deniedByKillSwitch(),
() -> registerWebTriggerAndAssertFailure(STATUS_KILLSWITCH_ENABLED));
}
@Test
public void testRegisterWebTrigger_failureByThrottler() throws Exception {
runRunMocks(
Api.REGISTER_WEB_TRIGGER,
new AccessDenier().deniedByThrottler(),
() -> registerWebTriggerAndAssertFailure(STATUS_RATE_LIMIT_REACHED));
}
@Test
public void registerWebTrigger_invalidRequest_throwException() {
assertThrows(
NullPointerException.class,
() ->
mMeasurementServiceImpl.registerWebTrigger(
/* request = */ null,
createCallerMetadata(),
new IMeasurementCallback.Default()));
}
@Test
public void registerWebTrigger_invalidCallerMetadata_throwException() {
assertThrows(
NullPointerException.class,
() ->
mMeasurementServiceImpl.registerWebTrigger(
createWebTriggerRegistrationRequest(),
/* callerMetadata */ null,
new IMeasurementCallback.Default()));
}
@Test
public void registerWebTrigger_invalidCallback_throwException() {
assertThrows(
NullPointerException.class,
() ->
mMeasurementServiceImpl.registerWebTrigger(
createWebTriggerRegistrationRequest(),
createCallerMetadata(),
/* callback = */ null));
}
private RegistrationRequest createRegistrationSourceRequest() {
return createRegistrationSourceRequest(false);
}
private RegistrationRequest createRegistrationSourceRequest(boolean isLocalhost) {
return new RegistrationRequest.Builder(
RegistrationRequest.REGISTER_SOURCE,
isLocalhost ? LOCALHOST : REGISTRATION_URI,
APP_PACKAGE_NAME,
SDK_PACKAGE_NAME)
.build();
}
private RegistrationRequest createRegistrationTriggerRequest() {
return createRegistrationTriggerRequest(false);
}
private RegistrationRequest createRegistrationTriggerRequest(boolean isLocalhost) {
return new RegistrationRequest.Builder(
RegistrationRequest.REGISTER_TRIGGER,
isLocalhost ? LOCALHOST : REGISTRATION_URI,
APP_PACKAGE_NAME,
SDK_PACKAGE_NAME)
.build();
}
private WebSourceRegistrationRequestInternal createWebSourceRegistrationRequest() {
return createWebSourceRegistrationRequest(false);
}
private WebSourceRegistrationRequestInternal createWebSourceRegistrationRequest(
boolean isLocalhost) {
WebSourceRegistrationRequest sourceRegistrationRequest =
new WebSourceRegistrationRequest.Builder(
Collections.singletonList(
new WebSourceParams.Builder(
isLocalhost ? LOCALHOST : REGISTRATION_URI)
.setDebugKeyAllowed(true)
.build()),
Uri.parse("android-app//com.example"))
.setWebDestination(WEB_DESTINATION)
.setAppDestination(APP_DESTINATION)
.build();
return new WebSourceRegistrationRequestInternal.Builder(
sourceRegistrationRequest, APP_PACKAGE_NAME, SDK_PACKAGE_NAME, 10000L)
.build();
}
private WebTriggerRegistrationRequestInternal createWebTriggerRegistrationRequest() {
return createWebTriggerRegistrationRequest(false);
}
private WebTriggerRegistrationRequestInternal createWebTriggerRegistrationRequest(
boolean isLocalhost) {
WebTriggerRegistrationRequest webTriggerRegistrationRequest =
new WebTriggerRegistrationRequest.Builder(
Collections.singletonList(
new WebTriggerParams.Builder(
isLocalhost ? LOCALHOST : REGISTRATION_URI)
.setDebugKeyAllowed(true)
.build()),
Uri.parse("android-app://com.example"))
.build();
return new WebTriggerRegistrationRequestInternal.Builder(
webTriggerRegistrationRequest, APP_PACKAGE_NAME, SDK_PACKAGE_NAME)
.build();
}
private DeletionParam createDeletionRequest() {
return new DeletionParam.Builder(
Collections.emptyList(),
Collections.emptyList(),
Instant.MIN,
Instant.MAX,
APP_PACKAGE_NAME,
SDK_PACKAGE_NAME)
.setMatchBehavior(DeletionRequest.MATCH_BEHAVIOR_DELETE)
.setDeletionMode(DeletionRequest.DELETION_MODE_ALL)
.build();
}
private CallerMetadata createCallerMetadata() {
return new CallerMetadata.Builder()
.setBinderElapsedTimestamp(SystemClock.elapsedRealtime())
.build();
}
private StatusParam createStatusParam() {
return new StatusParam.Builder(APP_PACKAGE_NAME, SDK_PACKAGE_NAME).build();
}
private void assertPackageNameLogged() throws InterruptedException {
// Sleep just a tiny fraction
// Logging happens in a separate thread and happens after the result code is returned
TimeUnit.MILLISECONDS.sleep(20);
ArgumentCaptor<ApiCallStats> captorStatus = ArgumentCaptor.forClass(ApiCallStats.class);
verify(mMockAdServicesLogger).logApiCallStats(captorStatus.capture());
assertEquals(APP_PACKAGE_NAME, captorStatus.getValue().getAppPackageName());
assertEquals(SDK_PACKAGE_NAME, captorStatus.getValue().getSdkPackageName());
}
private MeasurementServiceImpl createServiceWithMocks() {
return new MeasurementServiceImpl(
mMockMeasurementImpl,
mMockContext,
Clock.SYSTEM_CLOCK,
mMockConsentManager,
mMockThrottler,
mMockFlags,
mMockAdServicesLogger,
mMockAppImportanceFilter,
mDevContextFilter);
}
private enum Api {
DELETE_REGISTRATIONS,
REGISTER_SOURCE,
REGISTER_TRIGGER,
REGISTER_WEB_SOURCE,
REGISTER_WEB_TRIGGER,
STATUS
}
private void runRunMocks(
final Api api,
final AccessDenier accessDenier,
final TestUtils.RunnableWithThrow execute)
throws Exception {
final MockitoSession mockitoSession =
ExtendedMockito.mockitoSession()
.mockStatic(Binder.class)
.mockStatic(FlagsFactory.class)
.mockStatic(PermissionHelper.class)
.strictness(Strictness.LENIENT)
.startMocking();
try {
// Flags
ExtendedMockito.doReturn(mMockFlags).when(FlagsFactory::getFlags);
// Binder
ExtendedMockito.doReturn(1).when(Binder::getCallingUidOrThrow);
switch (api) {
case DELETE_REGISTRATIONS:
mockDeleteRegistrationsApi(accessDenier);
break;
case REGISTER_SOURCE:
mockRegisterSourceApi(accessDenier);
break;
case REGISTER_TRIGGER:
mockRegisterTriggerApi(accessDenier);
break;
case REGISTER_WEB_SOURCE:
mockRegisterWebSourceApi(accessDenier);
break;
case REGISTER_WEB_TRIGGER:
mockRegisterWebTriggerApi(accessDenier);
break;
case STATUS:
mockStatusApi(accessDenier);
break;
default:
break;
}
execute.run();
} finally {
mockitoSession.finishMocking();
}
}
/**
* Mock related objects for Delete Registrations API only. Same mock objects could be shared
* among other APIs, but implementation could change. Therefore, each mock{API} should describe
* the mock objects the API uses for readability and separation of concerns.
*
* @param accessDenier describes if API is allowed or denied by any barrier
*/
private void mockDeleteRegistrationsApi(AccessDenier accessDenier) {
// Throttler
updateThrottlerDenied(accessDenier.mByThrottler);
// Access Resolvers
// Kill Switch Resolver
final boolean killSwitchEnabled = accessDenier.mByKillSwitch;
when(mMockFlags.getMeasurementApiDeleteRegistrationsKillSwitch())
.thenReturn(killSwitchEnabled);
// Foreground Resolver
if (accessDenier.mByForegroundEnforcement) {
when(mMockFlags.getEnforceForegroundStatusForMeasurementDeleteRegistrations())
.thenReturn(true);
doThrowExceptionCallerNotInForeground();
} else {
when(mMockFlags.getEnforceForegroundStatusForMeasurementDeleteRegistrations())
.thenReturn(false);
}
// App Package Resolver Pp Api
updateAppPackagePpApiResolverDenied(accessDenier.mByAppPackagePpApiApp);
// App Package Resolver Web Context Client App
updateAppPackageResolverWebAppDenied(accessDenier.mByAppPackageWebContextClientApp);
// Results
when(mMockMeasurementImpl.deleteRegistrations(any(DeletionParam.class)))
.thenReturn(STATUS_SUCCESS);
}
/**
* Mock related objects for Register Source API only. Same mock objects could be shared among
* other APIs, but implementation could change. Therefore, each mock{API} should describe the
* mock objects the API uses for readability and separation of concerns.
*
* @param accessDenier describes if API is allowed or denied by any barrier
*/
private void mockRegisterSourceApi(AccessDenier accessDenier) {
// Throttler
updateThrottlerDenied(accessDenier.mByThrottler);
// Access Resolvers
// Kill Switch Resolver
final boolean killSwitchEnabled = accessDenier.mByKillSwitch;
when(mMockFlags.getMeasurementApiRegisterSourceKillSwitch()).thenReturn(killSwitchEnabled);
// Foreground Resolver
if (accessDenier.mByForegroundEnforcement) {
when(mMockFlags.getEnforceForegroundStatusForMeasurementRegisterSource())
.thenReturn(true);
doThrowExceptionCallerNotInForeground();
} else {
when(mMockFlags.getEnforceForegroundStatusForMeasurementRegisterSource())
.thenReturn(false);
}
// DevContext
updateDevContextDenied(accessDenier.mByDevContext);
// App Package Resolver Pp Api
updateAppPackagePpApiResolverDenied(accessDenier.mByAppPackagePpApiApp);
// Consent Resolver
updateConsentDenied(accessDenier.mByConsent);
// PermissionHelper
updateAttributionPermissionDenied(accessDenier.mByAttributionPermission);
// Results
when(mMockMeasurementImpl.register(any(RegistrationRequest.class), anyBoolean(), anyLong()))
.thenReturn(STATUS_SUCCESS);
}
/**
* Mock related objects for Register Trigger API only. Same mock objects could be shared among
* other APIs, but implementation could change. Therefore, each mock{API} should describe the
* mock objects the API uses for readability and separation of concerns.
*
* @param accessDenier describes if API is allowed or denied by any barrier
*/
private void mockRegisterTriggerApi(AccessDenier accessDenier) {
// Throttler
updateThrottlerDenied(accessDenier.mByThrottler);
// Access Resolvers
// Kill Switch Resolver
final boolean killSwitchEnabled = accessDenier.mByKillSwitch;
when(mMockFlags.getMeasurementApiRegisterTriggerKillSwitch()).thenReturn(killSwitchEnabled);
// Foreground Resolver
if (accessDenier.mByForegroundEnforcement) {
when(mMockFlags.getEnforceForegroundStatusForMeasurementRegisterTrigger())
.thenReturn(true);
doThrowExceptionCallerNotInForeground();
} else {
when(mMockFlags.getEnforceForegroundStatusForMeasurementRegisterTrigger())
.thenReturn(false);
}
// DevContext
updateDevContextDenied(accessDenier.mByDevContext);
// App Package Resolver Pp Api
updateAppPackagePpApiResolverDenied(accessDenier.mByAppPackagePpApiApp);
// Consent Resolver
updateConsentDenied(accessDenier.mByConsent);
// PermissionHelper
updateAttributionPermissionDenied(accessDenier.mByAttributionPermission);
// Results
when(mMockMeasurementImpl.register(any(RegistrationRequest.class), anyBoolean(), anyLong()))
.thenReturn(STATUS_SUCCESS);
}
/**
* Mock related objects for Register Web Source API only. Same mock objects could be shared
* among other APIs, but implementation could change. Therefore, each mock{API} should describe
* the mock objects the API uses for readability and separation of concerns.
*
* @param accessDenier describes if API is allowed or denied by any barrier
*/
private void mockRegisterWebSourceApi(AccessDenier accessDenier) {
// Throttler
updateThrottlerDenied(accessDenier.mByThrottler);
// Access Resolvers
// Kill Switch Resolver
final boolean killSwitchEnabled = accessDenier.mByKillSwitch;
when(mMockFlags.getMeasurementApiRegisterWebSourceKillSwitch())
.thenReturn(killSwitchEnabled);
// Foreground Resolver
if (accessDenier.mByForegroundEnforcement) {
when(mMockFlags.getEnforceForegroundStatusForMeasurementRegisterWebSource())
.thenReturn(true);
doThrowExceptionCallerNotInForeground();
} else {
when(mMockFlags.getEnforceForegroundStatusForMeasurementRegisterWebSource())
.thenReturn(false);
}
// DevContext
updateDevContextDenied(accessDenier.mByDevContext);
// App Package Resolver Pp Api
updateAppPackagePpApiResolverDenied(accessDenier.mByAppPackagePpApiApp);
// App Package Resolver Web Context Client App
updateAppPackageResolverWebAppDenied(accessDenier.mByAppPackageWebContextClientApp);
// Consent Resolver
updateConsentDenied(accessDenier.mByConsent);
// PermissionHelper
updateAttributionPermissionDenied(accessDenier.mByAttributionPermission);
// Results
when(mMockMeasurementImpl.registerWebSource(
any(WebSourceRegistrationRequestInternal.class), anyBoolean(), anyLong()))
.thenReturn(STATUS_SUCCESS);
}
/**
* Mock related objects for Register Web Trigger API only. Same mock objects could be shared
* among other APIs, but implementation could change. Therefore, each mock{API} should describe
* the mock objects the API uses for readability and separation of concerns.
*
* @param accessDenier describes if API is allowed or denied by any barrier
*/
private void mockRegisterWebTriggerApi(AccessDenier accessDenier) {
// Throttler
updateThrottlerDenied(accessDenier.mByThrottler);
// Access Resolvers
// Kill Switch Resolver
final boolean killSwitchEnabled = accessDenier.mByKillSwitch;
when(mMockFlags.getMeasurementApiRegisterWebTriggerKillSwitch())
.thenReturn(killSwitchEnabled);
// Foreground Resolver
if (accessDenier.mByForegroundEnforcement) {
when(mMockFlags.getEnforceForegroundStatusForMeasurementRegisterWebTrigger())
.thenReturn(true);
doThrowExceptionCallerNotInForeground();
} else {
when(mMockFlags.getEnforceForegroundStatusForMeasurementRegisterWebTrigger())
.thenReturn(false);
}
// DevContext
updateDevContextDenied(accessDenier.mByDevContext);
// App Package Resolver Pp Api
updateAppPackagePpApiResolverDenied(accessDenier.mByAppPackagePpApiApp);
// Consent Resolver
updateConsentDenied(accessDenier.mByConsent);
// PermissionHelper
updateAttributionPermissionDenied(accessDenier.mByAttributionPermission);
// Results
when(mMockMeasurementImpl.registerWebTrigger(
any(WebTriggerRegistrationRequestInternal.class), anyBoolean(), anyLong()))
.thenReturn(STATUS_SUCCESS);
}
/**
* Mock related objects for Status API only. Same mock objects could be shared among other APIs,
* but implementation could change. Therefore, each mock{API} should describe the mock objects
* the API uses for readability and separation of concerns.
*
* @param accessDenier describes if API is allowed or denied by any barrier
*/
private void mockStatusApi(AccessDenier accessDenier) {
// Access Resolvers
// Kill Switch Resolver
final boolean killSwitchEnabled = accessDenier.mByKillSwitch;
when(mMockFlags.getMeasurementApiStatusKillSwitch()).thenReturn(killSwitchEnabled);
// Foreground Resolver
if (accessDenier.mByForegroundEnforcement) {
when(mMockFlags.getEnforceForegroundStatusForMeasurementStatus()).thenReturn(true);
doThrowExceptionCallerNotInForeground();
} else {
when(mMockFlags.getEnforceForegroundStatusForMeasurementStatus()).thenReturn(false);
}
// App Package Resolver Pp Api
updateAppPackagePpApiResolverDenied(accessDenier.mByAppPackagePpApiApp);
// Consent Resolver
updateConsentDenied(accessDenier.mByConsent);
}
private void doThrowExceptionCallerNotInForeground() {
doThrow(new AppImportanceFilter.WrongCallingApplicationStateException())
.when(mMockAppImportanceFilter)
.assertCallerIsInForeground(anyInt(), anyInt(), any());
}
private void updateDevContextDenied(boolean denied) {
if (denied) {
when(mDevContextFilter.createDevContextFromCallingUid(anyInt()))
.thenReturn(DevContext.createForDevOptionsDisabled());
}
}
private void updateAppPackagePpApiResolverDenied(boolean denied) {
String allowList = denied ? "" : ALLOW_ALL_PACKAGES;
when(mMockFlags.getPpapiAppAllowList()).thenReturn(allowList);
}
private void updateAppPackageResolverWebAppDenied(boolean denied) {
String allowList = denied ? "" : ALLOW_ALL_PACKAGES;
when(mMockFlags.getWebContextClientAppAllowList()).thenReturn(allowList);
}
private void updateAttributionPermissionDenied(boolean denied) {
final boolean allowed = !denied;
ExtendedMockito.doReturn(allowed)
.when(
() ->
PermissionHelper.hasAttributionPermission(
any(Context.class), anyString()));
}
private void updateConsentDenied(boolean denied) {
final AdServicesApiConsent apiConsent =
denied ? AdServicesApiConsent.REVOKED : AdServicesApiConsent.GIVEN;
when(mMockConsentManager.getConsent()).thenReturn(apiConsent);
}
private void updateThrottlerDenied(boolean denied) {
final boolean canAcquire = !denied;
when(mMockThrottler.tryAcquire(any(), any())).thenReturn(canAcquire);
}
private static class AccessDenier {
private boolean mByAppPackagePpApiApp;
private boolean mByAppPackageWebContextClientApp;
private boolean mByAttributionPermission;
private boolean mByConsent;
private boolean mByForegroundEnforcement;
private boolean mByKillSwitch;
private boolean mByThrottler;
private boolean mByDevContext;
private AccessDenier deniedByDevContext() {
mByDevContext = true;
return this;
}
private AccessDenier deniedByAppPackagePpApiApp() {
mByAppPackagePpApiApp = true;
return this;
}
private AccessDenier deniedByAppPackageWebContextClientApp() {
mByAppPackageWebContextClientApp = true;
return this;
}
private AccessDenier deniedByAttributionPermission() {
mByAttributionPermission = true;
return this;
}
private AccessDenier deniedByConsent() {
mByConsent = true;
return this;
}
private AccessDenier deniedByForegroundEnforcement() {
mByForegroundEnforcement = true;
return this;
}
private AccessDenier deniedByKillSwitch() {
mByKillSwitch = true;
return this;
}
private AccessDenier deniedByThrottler() {
mByThrottler = true;
return this;
}
}
}