diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-01-10 19:01:23 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-01-10 19:01:23 +0000 |
commit | 41e7bbcf3d375486769059090e15260dc92f16c7 (patch) | |
tree | 5c368873297b82c25a8ba866cbeda97cce94d9fc | |
parent | 4f73884441d6a5c6ba4af7e5026807822aa034a0 (diff) | |
parent | 7f6cf1d740d6f047507d8bd2bc07ab12496e8024 (diff) | |
download | AdServices-aml_tz5_341510010.tar.gz |
Snap for 11296156 from 7f6cf1d740d6f047507d8bd2bc07ab12496e8024 to mainline-tzdata5-releaseaml_tz5_341510050aml_tz5_341510010aml_tz5_341510010
Change-Id: I84e6c0df7ccbfafbe8dd398518474c2b7a676013
454 files changed, 21735 insertions, 7414 deletions
diff --git a/adservices/TEST_MAPPING b/adservices/TEST_MAPPING index cd6c7f89c..795b16f7e 100644 --- a/adservices/TEST_MAPPING +++ b/adservices/TEST_MAPPING @@ -172,6 +172,24 @@ ] }, { + // Install com.google.android.adservices.apex and run AdServicesServiceCoreCommonUnitTests. + "name": "AdServicesServiceCoreCommonUnitTests[com.google.android.adservices.apex]", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] + }, + { + // Install com.google.android.adservices.apex and run AdExtServicesServiceCoreCommonUnitTests. + "name": "AdExtServicesServiceCoreCommonUnitTests[com.google.android.extservices.apex]", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] + }, + { // Install com.google.android.adservices.apex and run AdServicesServiceCoreAppSearchUnitTests. "name": "AdServicesServiceCoreAppSearchUnitTests[com.google.android.adservices.apex]", "options": [ @@ -635,6 +653,14 @@ ] }, { + "name": "AdServicesServiceCoreCommonUnitTests", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] + }, + { "name": "AdServicesServiceCoreAppSearchUnitTests", "options": [ { @@ -651,9 +677,7 @@ ] }, { - // Install com.google.android.adservices.apex and run - // AdServicesServiceCoreMeasurementUnitTests. - "name": "AdServicesServiceCoreMeasurementUnitTests[com.google.android.adservices.apex]", + "name": "AdServicesServiceCoreMeasurementUnitTests", "options": [ { "exclude-annotation": "androidx.test.filters.FlakyTest" @@ -661,9 +685,7 @@ ] }, { - // Install com.google.android.extservices.apex and run - // AdExtServicesServiceCoreMeasurementUnitTests. - "name": "AdExtServicesServiceCoreMeasurementUnitTests[com.google.android.extservices.apex]", + "name": "AdExtServicesServiceCoreMeasurementUnitTests", "options": [ { "exclude-annotation": "androidx.test.filters.FlakyTest" diff --git a/adservices/apk/AdExtServicesManifest.xml b/adservices/apk/AdExtServicesManifest.xml index 717ef8db0..c58937d6e 100644 --- a/adservices/apk/AdExtServicesManifest.xml +++ b/adservices/apk/AdExtServicesManifest.xml @@ -109,6 +109,10 @@ <uses-permission android:name="android.permission.ACCESS_PRIVILEGED_APP_SET_ID_COMPAT"/> <uses-permission android:name="android.permission.ACCESS_PRIVILEGED_ADSERVICES_COBALT_UPLOAD_COMPAT" /> + <!-- Permission to allow self calling Common Service. --> + <uses-permission android:name="android.permission.ACCESS_ADSERVICES_STATE_COMPAT"/> + <uses-permission android:name="android.permission.MODIFY_ADSERVICES_STATE_COMPAT"/> + <!-- Allows JobScheduler to persist jobs across reboot. --> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> diff --git a/adservices/apk/AndroidManifest.xml b/adservices/apk/AndroidManifest.xml index ad3a4cbfd..cb998df71 100644 --- a/adservices/apk/AndroidManifest.xml +++ b/adservices/apk/AndroidManifest.xml @@ -275,6 +275,15 @@ </intent-filter> </service> + <service android:name="com.android.adservices.shell.AdServicesShellCommandService" + android:exported="true" + android:visibleToInstantApps="false" + > + <intent-filter android:priority="1"> + <action android:name="android.adservices.SHELL_COMMAND_SERVICE"/> + </intent-filter> + </service> + <!-- Daily maintenance Job. --> <service android:name="com.android.adservices.service.MaintenanceJobService" android:permission="android.permission.BIND_JOB_SERVICE"> @@ -436,3 +445,4 @@ /> </application> </manifest> + diff --git a/adservices/apk/java/com/android/adservices/common/AdServicesCommonService.java b/adservices/apk/java/com/android/adservices/common/AdServicesCommonService.java index 9c99aa982..f8fb54d8e 100644 --- a/adservices/apk/java/com/android/adservices/common/AdServicesCommonService.java +++ b/adservices/apk/java/com/android/adservices/common/AdServicesCommonService.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.IBinder; +import android.os.Trace; import androidx.annotation.RequiresApi; @@ -51,6 +52,8 @@ public class AdServicesCommonService extends Service { @Override public void onCreate() { super.onCreate(); + + Trace.beginSection("AdServicesCommonService#Initialization"); if (mAdServicesCommonService == null) { mAdServicesCommonService = new AdServicesCommonServiceImpl( @@ -80,6 +83,7 @@ public class AdServicesCommonService extends Service { "getting exception when register consumer in AdServicesSyncUtil of " + e.getMessage()); } + Trace.endSection(); } @Override @@ -90,7 +94,6 @@ public class AdServicesCommonService extends Service { // TODO(b/308009734): STOPSHIP - remove this method once the proper service is available @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - enforceCallingPermission(android.Manifest.permission.DUMP, /* message = */ "dump()"); if (args != null && args.length > 0 && args[0].equals("cmd")) { boolean enabled = FlagsFactory.getFlags().getAdServicesShellCommandEnabled(); if (!enabled) { diff --git a/adservices/apk/java/com/android/adservices/shell/AdServicesShellCommandService.java b/adservices/apk/java/com/android/adservices/shell/AdServicesShellCommandService.java new file mode 100644 index 000000000..8d07378ca --- /dev/null +++ b/adservices/apk/java/com/android/adservices/shell/AdServicesShellCommandService.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2023 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.shell; + +import android.annotation.Nullable; +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +import com.android.adservices.LogUtil; +import com.android.adservices.service.FlagsFactory; +import com.android.adservices.service.shell.ShellCommandServiceImpl; + +import java.util.Objects; + +/** Implements a service which runs the shell command in the adservices process. */ +public final class AdServicesShellCommandService extends Service { + + /** The binder service. This field must only be accessed on the main thread. */ + private ShellCommandServiceImpl mShellCommandService; + + @Override + public void onCreate() { + if (!FlagsFactory.getFlags().getAdServicesShellCommandEnabled()) { + LogUtil.e("Shell command service is not enabled."); + return; + } + + mShellCommandService = new ShellCommandServiceImpl(); + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + if (!FlagsFactory.getFlags().getAdServicesShellCommandEnabled()) { + LogUtil.e("Shell command service is not enabled."); + return null; + } + return Objects.requireNonNull(mShellCommandService); + } + + // TODO(b/308009734): Implement dump method. +} diff --git a/adservices/apk/java/com/android/adservices/ui/UxUtil.java b/adservices/apk/java/com/android/adservices/ui/UxUtil.java index 035c3d284..9efc670f3 100644 --- a/adservices/apk/java/com/android/adservices/ui/UxUtil.java +++ b/adservices/apk/java/com/android/adservices/ui/UxUtil.java @@ -36,7 +36,11 @@ public class UxUtil { /** Returns whether the device is an EEA device. */ public static boolean isEeaDevice(FragmentActivity fragmentActivity, Context context) { return FlagsFactory.getFlags().getConsentNotificationActivityDebugMode() - ? fragmentActivity.getIntent().getBooleanExtra("isEUDevice", /* default= */ true) + ? fragmentActivity + .getIntent() + .getBooleanExtra( + "isEUDevice", + /* default= */ UxStatesManager.getInstance(context).isEeaDevice()) : !ConsentManager.getInstance(context).isAdIdEnabled() || UxStatesManager.getInstance(context).isEeaDevice(); } diff --git a/adservices/apk/java/com/android/adservices/ui/notifications/ConsentNotificationActivity.java b/adservices/apk/java/com/android/adservices/ui/notifications/ConsentNotificationActivity.java index 9492a5188..3455cc7fd 100644 --- a/adservices/apk/java/com/android/adservices/ui/notifications/ConsentNotificationActivity.java +++ b/adservices/apk/java/com/android/adservices/ui/notifications/ConsentNotificationActivity.java @@ -107,11 +107,7 @@ public class ConsentNotificationActivity extends FragmentActivity implements UxS @Override public void initGA() { - if (FlagsFactory.getFlags().getEuNotifFlowChangeEnabled()) { - setContentView(R.layout.consent_notification_ga_v2_activity); - } else { - setContentView(R.layout.consent_notification_ga_activity); - } + setContentView(R.layout.consent_notification_ga_v2_activity); } @Override @@ -131,11 +127,7 @@ public class ConsentNotificationActivity extends FragmentActivity implements UxS private void initFragment() { if (FlagsFactory.getFlags().getGaUxFeatureEnabled()) { - if (FlagsFactory.getFlags().getEuNotifFlowChangeEnabled()) { - setContentView(R.layout.consent_notification_ga_v2_activity); - } else { - setContentView(R.layout.consent_notification_ga_activity); - } + setContentView(R.layout.consent_notification_ga_v2_activity); } else { setContentView(R.layout.consent_notification_activity); } diff --git a/adservices/apk/java/com/android/adservices/ui/notifications/ConsentNotificationTrigger.java b/adservices/apk/java/com/android/adservices/ui/notifications/ConsentNotificationTrigger.java index 98230b848..a11a5a645 100644 --- a/adservices/apk/java/com/android/adservices/ui/notifications/ConsentNotificationTrigger.java +++ b/adservices/apk/java/com/android/adservices/ui/notifications/ConsentNotificationTrigger.java @@ -16,7 +16,6 @@ package com.android.adservices.ui.notifications; -import static com.android.adservices.service.FlagsConstants.KEY_EU_NOTIF_FLOW_CHANGE_ENABLED; import static com.android.adservices.service.FlagsConstants.KEY_GA_UX_FEATURE_ENABLED; import static com.android.adservices.service.FlagsConstants.KEY_NOTIFICATION_DISMISSED_ON_CLICK; import static com.android.adservices.service.FlagsConstants.KEY_RECORD_MANUAL_INTERACTION_ENABLED; @@ -37,7 +36,6 @@ import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; import com.android.adservices.api.R; -import com.android.adservices.service.FlagsFactory; import com.android.adservices.service.consent.AdServicesApiType; import com.android.adservices.service.consent.ConsentManager; import com.android.adservices.service.stats.UiStatsLogger; @@ -127,12 +125,7 @@ public class ConsentNotificationTrigger { if (isUxStatesReady(context)) { switch (UxUtil.getUx(context)) { case GA_UX: - if (UxStatesManager.getInstance(context) - .getFlag(KEY_EU_NOTIF_FLOW_CHANGE_ENABLED)) { - notification = getGaV2ConsentNotification(context, isEuDevice); - } else { - notification = getGaConsentNotification(context, isEuDevice); - } + notification = getGaV2ConsentNotification(context, isEuDevice); break; // Both U18_UX and RVC_UX are showing U18 Notification case U18_UX: @@ -143,15 +136,11 @@ public class ConsentNotificationTrigger { notification = getConsentNotification(context, isEuDevice); break; default: - notification = getGaConsentNotification(context, isEuDevice); + notification = getGaV2ConsentNotification(context, isEuDevice); } } else { if (gaUxFeatureEnabled) { - if (FlagsFactory.getFlags().getEuNotifFlowChangeEnabled()) { - notification = getGaV2ConsentNotification(context, isEuDevice); - } else { - notification = getGaConsentNotification(context, isEuDevice); - } + notification = getGaV2ConsentNotification(context, isEuDevice); } else { notification = getConsentNotification(context, isEuDevice); } @@ -252,54 +241,7 @@ public class ConsentNotificationTrigger { .setContentIntent(pendingIntent); return notification.build(); } - - /** - * Returns a {@link NotificationCompat.Builder} which can be used to display consent - * notification to the user when GaUxFeature flag is enabled. - * - * @param context {@link Context} which is used to prepare a {@link NotificationCompat}. - */ - private static Notification getGaConsentNotification( - @NonNull Context context, boolean isEuDevice) { - Intent intent = new Intent(context, ConsentNotificationActivity.class); - - PendingIntent pendingIntent = - PendingIntent.getActivity(context, 1, intent, PendingIntent.FLAG_IMMUTABLE); - NotificationCompat.BigTextStyle textStyle = - new NotificationCompat.BigTextStyle() - .bigText( - isEuDevice - ? context.getString( - R.string.notificationUI_notification_ga_content_eu) - : context.getString( - R.string.notificationUI_notification_ga_content)); - NotificationCompat.Builder notification = - new NotificationCompat.Builder(context, CHANNEL_ID) - .setSmallIcon(R.drawable.ic_info_icon) - .setContentTitle( - context.getString( - isEuDevice - ? R.string.notificationUI_notification_ga_title_eu - : R.string.notificationUI_notification_ga_title)) - .setContentText( - context.getString( - isEuDevice - ? R.string.notificationUI_notification_ga_content_eu - : R.string.notificationUI_notification_ga_content)) - .setStyle(textStyle) - .setPriority(NOTIFICATION_PRIORITY) - .setAutoCancel(true) - .setContentIntent(pendingIntent); - - if (isEuDevice - && !UxStatesManager.getInstance(context) - .getFlag(KEY_NOTIFICATION_DISMISSED_ON_CLICK)) { - notification.setAutoCancel(false); - } - - return notification.build(); - } - + /** * Returns a {@link NotificationCompat.Builder} which can be used to display consent * notification to the user. diff --git a/adservices/apk/java/com/android/adservices/ui/settings/activities/AdServicesSettingsMainActivity.java b/adservices/apk/java/com/android/adservices/ui/settings/activities/AdServicesSettingsMainActivity.java index ee9067980..619978aad 100644 --- a/adservices/apk/java/com/android/adservices/ui/settings/activities/AdServicesSettingsMainActivity.java +++ b/adservices/apk/java/com/android/adservices/ui/settings/activities/AdServicesSettingsMainActivity.java @@ -19,6 +19,7 @@ import static com.android.adservices.ui.UxUtil.isUxStatesReady; import android.os.Build; import android.os.Bundle; +import android.os.Trace; import androidx.annotation.RequiresApi; import androidx.lifecycle.ViewModelProvider; @@ -58,6 +59,7 @@ public class AdServicesSettingsMainActivity extends AdServicesBaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { + Trace.beginSection("AdServicesSettingsMainActivity#OnCreate"); // Only for main view, we want to use the most up to date OTA strings on the device to // create the ResourcesLoader. if (FlagsFactory.getFlags().getUiOtaStringsFeatureEnabled()) { @@ -70,6 +72,7 @@ public class AdServicesSettingsMainActivity extends AdServicesBaseActivity { if (!isUxStatesReady(this)) { initMainFragment(); } + Trace.endSection(); } private void initMainFragment() { diff --git a/adservices/apk/res/values-da/strings.xml b/adservices/apk/res/values-da/strings.xml index ee2f09319..5f0eea93e 100644 --- a/adservices/apk/res/values-da/strings.xml +++ b/adservices/apk/res/values-da/strings.xml @@ -557,7 +557,7 @@ <string name="topic10320" msgid="5279440074507438174">"Onlinefællesskaber"</string> <string name="topic10321" msgid="7018890132742820265">"Clipart og animerede GIF-billeder"</string> <string name="topic10322" msgid="1585737438943011532">"Dating og personlige annoncer"</string> - <string name="topic10323" msgid="962863497957179646">"Feedtilføjelser og sociale bogmærker"</string> + <string name="topic10323" msgid="962863497957179646">"Feedaggregering og sociale bogmærker"</string> <string name="topic10324" msgid="1425257460381023853">"Fildeling og hosting"</string> <string name="topic10325" msgid="8698912164970408378">"Udbydere af fora og chatprogrammer"</string> <string name="topic10326" msgid="2425798096705921796">"Microblogs"</string> diff --git a/adservices/apk/tests/notification/OWNERS b/adservices/apk/tests/notification/OWNERS index 9530244f7..b0f9f618b 100644 --- a/adservices/apk/tests/notification/OWNERS +++ b/adservices/apk/tests/notification/OWNERS @@ -6,3 +6,4 @@ tccyp@google.com yangwangyw@google.com albertkong@google.com bozhaobz@google.com +ardroid@google.com diff --git a/adservices/apk/tests/notification/src/com/android/adservices/ui/notifications/ConsentNotificationTriggerTest.java b/adservices/apk/tests/notification/src/com/android/adservices/ui/notifications/ConsentNotificationTriggerTest.java index 42eff2d38..20dccae4a 100644 --- a/adservices/apk/tests/notification/src/com/android/adservices/ui/notifications/ConsentNotificationTriggerTest.java +++ b/adservices/apk/tests/notification/src/com/android/adservices/ui/notifications/ConsentNotificationTriggerTest.java @@ -16,20 +16,19 @@ package com.android.adservices.ui.notifications; +import static com.android.adservices.service.FlagsConstants.KEY_GA_UX_FEATURE_ENABLED; +import static com.android.adservices.service.FlagsConstants.KEY_NOTIFICATION_DISMISSED_ON_CLICK; +import static com.android.adservices.service.FlagsConstants.KEY_RVC_UX_ENABLED; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_SETTINGS_USAGE_REPORTED; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_SETTINGS_USAGE_REPORTED__DEFAULT_AD_ID_STATE__AD_ID_DISABLED; -import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_SETTINGS_USAGE_REPORTED__DEFAULT_CONSENT__PP_API_DEFAULT_OPT_OUT; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_SETTINGS_USAGE_REPORTED__DEFAULT_CONSENT__MEASUREMENT_DEFAULT_OPT_OUT; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_SETTINGS_USAGE_REPORTED__DEFAULT_CONSENT__PP_API_DEFAULT_OPT_OUT; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_SETTINGS_USAGE_REPORTED__ENROLLMENT_CHANNEL__FIRST_CONSENT_NOTIFICATION_CHANNEL; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_SETTINGS_USAGE_REPORTED__ENROLLMENT_CHANNEL__RVC_POST_OTA_NOTIFICATION_CHANNEL; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_SETTINGS_USAGE_REPORTED__REGION__EU; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_SETTINGS_USAGE_REPORTED__REGION__ROW; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_SETTINGS_USAGE_REPORTED__UX__GA_UX; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_SETTINGS_USAGE_REPORTED__UX__RVC_UX; -import static com.android.adservices.service.FlagsConstants.KEY_EU_NOTIF_FLOW_CHANGE_ENABLED; -import static com.android.adservices.service.FlagsConstants.KEY_GA_UX_FEATURE_ENABLED; -import static com.android.adservices.service.FlagsConstants.KEY_NOTIFICATION_DISMISSED_ON_CLICK; -import static com.android.adservices.service.FlagsConstants.KEY_RVC_UX_ENABLED; import static com.android.adservices.service.ui.ux.collection.PrivacySandboxUxCollection.BETA_UX; import static com.android.adservices.service.ui.ux.collection.PrivacySandboxUxCollection.GA_UX; import static com.android.adservices.service.ui.ux.collection.PrivacySandboxUxCollection.RVC_UX; @@ -129,7 +128,6 @@ public final class ConsentNotificationTriggerTest extends AdServicesExtendedMock doReturn(false).when(mMockUxStatesManager).getFlag(any(String.class)); doReturn(GA_UX).when(mMockUxStatesManager).getUx(); doReturn(true).when(mMockUxStatesManager).getFlag(KEY_NOTIFICATION_DISMISSED_ON_CLICK); - doReturn(false).when(mMockUxStatesManager).getFlag(KEY_EU_NOTIF_FLOW_CHANGE_ENABLED); cancelAllPreviousNotifications(); } @@ -141,59 +139,6 @@ public final class ConsentNotificationTriggerTest extends AdServicesExtendedMock } @Test - public void testEuNotification() throws InterruptedException, UiObjectNotFoundException { - doReturn(true).when(mMockFlags).isEeaDevice(); - doReturn(false).when(mMockUxStatesManager).getFlag(KEY_GA_UX_FEATURE_ENABLED); - doReturn(BETA_UX).when(mMockUxStatesManager).getUx(); - - final String expectedTitle = - mContext.getString(R.string.notificationUI_notification_title_eu); - final String expectedContent = - mContext.getString(R.string.notificationUI_notification_content_eu); - - ConsentNotificationTrigger.showConsentNotification(mContext, true); - Thread.sleep(1000); // wait 1s to make sure that Notification is displayed. - - verify(mAdServicesLogger, times(2)).logUIStats(any()); - - verify(mConsentManager, times(2)).getDefaultConsent(); - verify(mConsentManager, times(2)).getDefaultAdIdState(); - verify(mConsentManager).disable(mContext); - verify(mConsentManager).recordNotificationDisplayed(true); - - assertThat(mNotificationManager.getActiveNotifications()).hasLength(1); - final Notification notification = - mNotificationManager.getActiveNotifications()[0].getNotification(); - assertThat(notification.getChannelId()).isEqualTo(NOTIFICATION_CHANNEL_ID); - assertThat(notification.extras.getCharSequence(Notification.EXTRA_TITLE).toString()) - .isEqualTo(expectedTitle); - assertThat(notification.extras.getCharSequence(Notification.EXTRA_TEXT).toString()) - .isEqualTo(expectedContent); - assertThat(Notification.FLAG_ONGOING_EVENT & notification.flags).isEqualTo(0); - assertThat(Notification.FLAG_NO_CLEAR & notification.flags).isEqualTo(0); - assertThat(Notification.FLAG_AUTO_CANCEL & notification.flags) - .isEqualTo(Notification.FLAG_AUTO_CANCEL); - - sDevice.openNotification(); - sDevice.wait(Until.hasObject(By.pkg("com.android.systemui")), LAUNCH_TIMEOUT); - UiObject scroller = - sDevice.findObject( - new UiSelector() - .packageName("com.android.systemui") - .resourceId("com.android.systemui:id/notification_stack_scroller")); - - UiSelector notificationCardSelector = - new UiSelector().text(getString(R.string.notificationUI_notification_title_eu)); - UiObject notificationCard = scroller.getChild(notificationCardSelector); - assertThat(notificationCard.exists()).isTrue(); - - notificationCard.click(); - Thread.sleep(LAUNCH_TIMEOUT); - UiObject title = getPageElement(sDevice, R.string.notificationUI_header_title_eu); - assertThat(title.exists()).isTrue(); - } - - @Test public void testEuNotification_gaUxFlagEnabled() throws InterruptedException, UiObjectNotFoundException { doReturn(true).when(mMockFlags).isEeaDevice(); @@ -201,9 +146,9 @@ public final class ConsentNotificationTriggerTest extends AdServicesExtendedMock doReturn(GA_UX).when(mMockUxStatesManager).getUx(); final String expectedTitle = - mContext.getString(R.string.notificationUI_notification_ga_title_eu); + mContext.getString(R.string.notificationUI_notification_ga_title_eu_v2); final String expectedContent = - mContext.getString(R.string.notificationUI_notification_ga_content_eu); + mContext.getString(R.string.notificationUI_notification_ga_content_eu_v2); ConsentNotificationTrigger.showConsentNotification(mContext, true); Thread.sleep(1000); // wait 1s to make sure that Notification is displayed. @@ -237,7 +182,11 @@ public final class ConsentNotificationTriggerTest extends AdServicesExtendedMock .isEqualTo(Notification.FLAG_AUTO_CANCEL); sDevice.openNotification(); - sDevice.wait(Until.hasObject(By.pkg("com.android.systemui")), LAUNCH_TIMEOUT); + sDevice.wait( + Until.hasObject( + By.pkg("com.android.systemui") + .res("com.android.systemui:id/notification_stack_scroller")), + LAUNCH_TIMEOUT); UiObject scroller = sDevice.findObject( @@ -248,68 +197,16 @@ public final class ConsentNotificationTriggerTest extends AdServicesExtendedMock UiObject notificationCard = scroller.getChild( new UiSelector() - .text(getString(R.string.notificationUI_notification_ga_title_eu))); + .text( + getString( + R.string + .notificationUI_notification_ga_title_eu_v2))); assertThat(notificationCard.exists()).isTrue(); notificationCard.click(); Thread.sleep(LAUNCH_TIMEOUT); assertThat(mNotificationManager.getActiveNotifications()).hasLength(0); } - - @Test - public void testNonEuNotifications() throws InterruptedException, UiObjectNotFoundException { - doReturn(false).when(mMockFlags).isEeaDevice(); - doReturn(false).when(mMockUxStatesManager).getFlag(KEY_GA_UX_FEATURE_ENABLED); - doReturn(BETA_UX).when(mMockUxStatesManager).getUx(); - - final String expectedTitle = mContext.getString(R.string.notificationUI_notification_title); - final String expectedContent = - mContext.getString(R.string.notificationUI_notification_content); - - ConsentNotificationTrigger.showConsentNotification(mContext, false); - Thread.sleep(1000); // wait 1s to make sure that Notification is displayed. - - verify(mAdServicesLogger, times(2)).logUIStats(any()); - - verify(mConsentManager, times(2)).getDefaultConsent(); - verify(mConsentManager, times(2)).getDefaultAdIdState(); - verify(mConsentManager).enable(mContext); - verify(mConsentManager).recordNotificationDisplayed(true); - - assertThat(mNotificationManager.getActiveNotifications()).hasLength(1); - final Notification notification = - mNotificationManager.getActiveNotifications()[0].getNotification(); - assertThat(notification.getChannelId()).isEqualTo(NOTIFICATION_CHANNEL_ID); - assertThat(notification.extras.getCharSequence(Notification.EXTRA_TITLE).toString()) - .isEqualTo(expectedTitle); - assertThat(notification.extras.getCharSequence(Notification.EXTRA_TEXT).toString()) - .isEqualTo(expectedContent); - assertThat(Notification.FLAG_ONGOING_EVENT & notification.flags).isEqualTo(0); - assertThat(Notification.FLAG_NO_CLEAR & notification.flags).isEqualTo(0); - assertThat(Notification.FLAG_AUTO_CANCEL & notification.flags) - .isEqualTo(Notification.FLAG_AUTO_CANCEL); - - sDevice.openNotification(); - sDevice.wait(Until.hasObject(By.pkg("com.android.systemui")), LAUNCH_TIMEOUT); - - UiObject scroller = - sDevice.findObject( - new UiSelector() - .packageName("com.android.systemui") - .resourceId("com.android.systemui:id/notification_stack_scroller")); - assertThat(scroller.exists()).isTrue(); - UiObject notificationCard = - scroller.getChild( - new UiSelector() - .text(getString(R.string.notificationUI_notification_title))); - assertThat(notificationCard.exists()).isTrue(); - - notificationCard.click(); - Thread.sleep(LAUNCH_TIMEOUT); - UiObject title = getPageElement(sDevice, R.string.notificationUI_header_title); - assertThat(title.exists()).isTrue(); - } - @Test public void testNonEuNotifications_gaUxEnabled() throws InterruptedException { doReturn(false).when(mMockFlags).isEeaDevice(); @@ -317,9 +214,9 @@ public final class ConsentNotificationTriggerTest extends AdServicesExtendedMock doReturn(GA_UX).when(mMockUxStatesManager).getUx(); final String expectedTitle = - mContext.getString(R.string.notificationUI_notification_ga_title); + mContext.getString(R.string.notificationUI_notification_ga_title_v2); final String expectedContent = - mContext.getString(R.string.notificationUI_notification_ga_content); + mContext.getString(R.string.notificationUI_notification_ga_content_v2); ConsentNotificationTrigger.showConsentNotification(mContext, false); Thread.sleep(1000); // wait 1s to make sure that Notification is displayed. @@ -362,9 +259,9 @@ public final class ConsentNotificationTriggerTest extends AdServicesExtendedMock doReturn(false).when(mMockUxStatesManager).getFlag(KEY_NOTIFICATION_DISMISSED_ON_CLICK); final String expectedTitle = - mContext.getString(R.string.notificationUI_notification_ga_title_eu); + mContext.getString(R.string.notificationUI_notification_ga_title_eu_v2); final String expectedContent = - mContext.getString(R.string.notificationUI_notification_ga_content_eu); + mContext.getString(R.string.notificationUI_notification_ga_content_eu_v2); ConsentNotificationTrigger.showConsentNotification(mContext, true); Thread.sleep(1000); // wait 1s to make sure that Notification is displayed. @@ -394,11 +291,16 @@ public final class ConsentNotificationTriggerTest extends AdServicesExtendedMock .isEqualTo(Notification.FLAG_ONGOING_EVENT); assertThat(Notification.FLAG_NO_CLEAR & notification.flags) .isEqualTo(Notification.FLAG_NO_CLEAR); - assertThat(Notification.FLAG_AUTO_CANCEL & notification.flags).isEqualTo(0); + assertThat(Notification.FLAG_AUTO_CANCEL & notification.flags) + .isEqualTo(Notification.FLAG_AUTO_CANCEL); assertThat(notification.actions).isNull(); sDevice.openNotification(); - sDevice.wait(Until.hasObject(By.pkg("com.android.systemui")), LAUNCH_TIMEOUT); + sDevice.wait( + Until.hasObject( + By.pkg("com.android.systemui") + .res("com.android.systemui:id/notification_stack_scroller")), + LAUNCH_TIMEOUT); UiObject scroller = sDevice.findObject( @@ -412,7 +314,7 @@ public final class ConsentNotificationTriggerTest extends AdServicesExtendedMock UiSelector notificationCardSelector = new UiSelector() .textContains( - getString(R.string.notificationUI_notification_ga_title_eu) + getString(R.string.notificationUI_notification_ga_title_eu_v2) .substring(0, 15)); if (scroller.exists()) { notificationCard = scroller.getChild(notificationCardSelector); @@ -424,7 +326,7 @@ public final class ConsentNotificationTriggerTest extends AdServicesExtendedMock notificationCard.click(); Thread.sleep(LAUNCH_TIMEOUT); - assertThat(mNotificationManager.getActiveNotifications()).hasLength(1); + assertThat(mNotificationManager.getActiveNotifications()).hasLength(0); } @Test @@ -438,13 +340,12 @@ public final class ConsentNotificationTriggerTest extends AdServicesExtendedMock doReturn(true).when(mMockFlags).getGaUxFeatureEnabled(); doReturn(GA_UX).when(mMockUxStatesManager).getUx(); doReturn(true).when(mMockUxStatesManager).getFlag(KEY_GA_UX_FEATURE_ENABLED); - doReturn(false).when(mMockUxStatesManager).getFlag(KEY_EU_NOTIF_FLOW_CHANGE_ENABLED); doReturn(false).when(mMockUxStatesManager).getFlag(KEY_NOTIFICATION_DISMISSED_ON_CLICK); final String expectedTitle = - mContext.getString(R.string.notificationUI_notification_ga_title_eu); + mContext.getString(R.string.notificationUI_notification_ga_title_eu_v2); final String expectedContent = - mContext.getString(R.string.notificationUI_notification_ga_content_eu); + mContext.getString(R.string.notificationUI_notification_ga_content_eu_v2); ConsentNotificationTrigger.showConsentNotification(mContext, true); Thread.sleep(1000); // wait 1s to make sure that Notification is displayed. @@ -473,12 +374,17 @@ public final class ConsentNotificationTriggerTest extends AdServicesExtendedMock .isEqualTo(Notification.FLAG_ONGOING_EVENT); assertThat(Notification.FLAG_NO_CLEAR & notification.flags) .isEqualTo(Notification.FLAG_NO_CLEAR); - assertThat(Notification.FLAG_AUTO_CANCEL & notification.flags).isEqualTo(0); + assertThat(Notification.FLAG_AUTO_CANCEL & notification.flags) + .isEqualTo(Notification.FLAG_AUTO_CANCEL); assertThat(notification.actions).isNull(); // verify that notification was displayed sDevice.openNotification(); - sDevice.wait(Until.hasObject(By.pkg("com.android.systemui")), LAUNCH_TIMEOUT); + sDevice.wait( + Until.hasObject( + By.pkg("com.android.systemui") + .res("com.android.systemui:id/notification_stack_scroller")), + LAUNCH_TIMEOUT); UiObject scroller = sDevice.findObject( new UiSelector() @@ -491,7 +397,7 @@ public final class ConsentNotificationTriggerTest extends AdServicesExtendedMock UiSelector notificationCardSelector = new UiSelector() .textContains( - getString(R.string.notificationUI_notification_ga_title_eu) + getString(R.string.notificationUI_notification_ga_title_eu_v2) .substring(0, 15)); if (scroller.exists()) { notificationCard = scroller.getChild(notificationCardSelector); @@ -504,13 +410,15 @@ public final class ConsentNotificationTriggerTest extends AdServicesExtendedMock // click the notification and verify that notification still exists (wasn't dismissed) notificationCard.click(); Thread.sleep(LAUNCH_TIMEOUT); - assertThat(mNotificationManager.getActiveNotifications()).hasLength(1); + assertThat(mNotificationManager.getActiveNotifications()).hasLength(0); // go to confirmation page and verify that notification was dismissed UiObject leftControlButton = - getPageElement(sDevice, R.string.notificationUI_left_control_button_text_eu); + getPageElement( + sDevice, R.string.notificationUI_confirmation_left_control_button_text); UiObject rightControlButton = - getPageElement(sDevice, R.string.notificationUI_right_control_button_ga_text_eu); + getPageElement( + sDevice, R.string.notificationUI_confirmation_right_control_button_text); UiObject moreButton = getPageElement(sDevice, R.string.notificationUI_more_button_text); verifyControlsAndMoreButtonAreDisplayed(leftControlButton, rightControlButton, moreButton); Thread.sleep(LAUNCH_TIMEOUT); @@ -615,7 +523,11 @@ public final class ConsentNotificationTriggerTest extends AdServicesExtendedMock .isEqualTo(Notification.FLAG_AUTO_CANCEL); sDevice.openNotification(); - sDevice.wait(Until.hasObject(By.pkg("com.android.systemui")), LAUNCH_TIMEOUT); + sDevice.wait( + Until.hasObject( + By.pkg("com.android.systemui") + .res("com.android.systemui:id/notification_stack_scroller")), + LAUNCH_TIMEOUT); UiObject scroller = sDevice.findObject( @@ -662,13 +574,13 @@ public final class ConsentNotificationTriggerTest extends AdServicesExtendedMock final String expectedTitle = mContext.getString( isEeaDevice - ? R.string.notificationUI_notification_ga_title_eu - : R.string.notificationUI_notification_ga_title); + ? R.string.notificationUI_notification_ga_title_eu_v2 + : R.string.notificationUI_notification_ga_title_v2); final String expectedContent = mContext.getString( isEeaDevice - ? R.string.notificationUI_notification_ga_content_eu - : R.string.notificationUI_notification_ga_content); + ? R.string.notificationUI_notification_ga_content_eu_v2 + : R.string.notificationUI_notification_ga_content_v2); ConsentNotificationTrigger.showConsentNotification(mContext, isEeaDevice); Thread.sleep(1000); // wait 1s to make sure that Notification is displayed. @@ -736,7 +648,11 @@ public final class ConsentNotificationTriggerTest extends AdServicesExtendedMock .isEqualTo(Notification.FLAG_AUTO_CANCEL); sDevice.openNotification(); - sDevice.wait(Until.hasObject(By.pkg("com.android.systemui")), LAUNCH_TIMEOUT); + sDevice.wait( + Until.hasObject( + By.pkg("com.android.systemui") + .res("com.android.systemui:id/notification_stack_scroller")), + LAUNCH_TIMEOUT); UiObject scroller = sDevice.findObject( @@ -751,9 +667,9 @@ public final class ConsentNotificationTriggerTest extends AdServicesExtendedMock getString( isEeaDevice ? R.string - .notificationUI_notification_ga_title_eu + .notificationUI_notification_ga_title_eu_v2 : R.string - .notificationUI_notification_ga_title))); + .notificationUI_notification_ga_title_v2))); assertThat(notificationCard.exists()).isTrue(); notificationCard.click(); diff --git a/adservices/apk/tests/notification/src/com/android/adservices/ui/notifications/NotificationActivityGAV2UiAutomatorTest.java b/adservices/apk/tests/notification/src/com/android/adservices/ui/notifications/NotificationActivityGAV2UiAutomatorTest.java index 06bee04fa..c58676854 100644 --- a/adservices/apk/tests/notification/src/com/android/adservices/ui/notifications/NotificationActivityGAV2UiAutomatorTest.java +++ b/adservices/apk/tests/notification/src/com/android/adservices/ui/notifications/NotificationActivityGAV2UiAutomatorTest.java @@ -15,7 +15,6 @@ */ package com.android.adservices.ui.notifications; -import static com.android.adservices.service.FlagsConstants.KEY_EU_NOTIF_FLOW_CHANGE_ENABLED; import static com.android.adservices.service.FlagsConstants.KEY_GA_UX_FEATURE_ENABLED; import static com.google.common.truth.Truth.assertThat; @@ -73,8 +72,7 @@ public class NotificationActivityGAV2UiAutomatorTest { public final AdServicesFlagsSetterRule flags = AdServicesFlagsSetterRule.forGlobalKillSwitchDisabledTests() .setCompatModeFlags() - .setFlag(KEY_GA_UX_FEATURE_ENABLED, true) - .setFlag(KEY_EU_NOTIF_FLOW_CHANGE_ENABLED, true); + .setFlag(KEY_GA_UX_FEATURE_ENABLED, true); @BeforeClass public static void classSetup() throws InterruptedException { diff --git a/adservices/apk/tests/notification/src/com/android/adservices/ui/notifications/NotificationActivityGAV2UxSelectorUiAutomatorTest.java b/adservices/apk/tests/notification/src/com/android/adservices/ui/notifications/NotificationActivityGAV2UxSelectorUiAutomatorTest.java index ffbb73a66..0fd5f7fea 100644 --- a/adservices/apk/tests/notification/src/com/android/adservices/ui/notifications/NotificationActivityGAV2UxSelectorUiAutomatorTest.java +++ b/adservices/apk/tests/notification/src/com/android/adservices/ui/notifications/NotificationActivityGAV2UxSelectorUiAutomatorTest.java @@ -16,7 +16,6 @@ package com.android.adservices.ui.notifications; import static com.android.adservices.service.FlagsConstants.KEY_ENABLE_AD_SERVICES_SYSTEM_API; -import static com.android.adservices.service.FlagsConstants.KEY_EU_NOTIF_FLOW_CHANGE_ENABLED; import static com.android.adservices.service.FlagsConstants.KEY_GA_UX_FEATURE_ENABLED; import static com.android.adservices.service.FlagsConstants.KEY_U18_UX_ENABLED; @@ -75,8 +74,7 @@ public class NotificationActivityGAV2UxSelectorUiAutomatorTest { .setCompatModeFlags() .setFlag(KEY_ENABLE_AD_SERVICES_SYSTEM_API, true) .setFlag(KEY_GA_UX_FEATURE_ENABLED, true) - .setFlag(KEY_U18_UX_ENABLED, true) - .setFlag(KEY_EU_NOTIF_FLOW_CHANGE_ENABLED, true); + .setFlag(KEY_U18_UX_ENABLED, true); @BeforeClass public static void classSetup() throws InterruptedException { diff --git a/adservices/apk/tests/notification/src/com/android/adservices/ui/notifications/NotificationActivityUiAutomatorTest.java b/adservices/apk/tests/notification/src/com/android/adservices/ui/notifications/NotificationActivityUiAutomatorTest.java index 261f44282..ce4cc0077 100644 --- a/adservices/apk/tests/notification/src/com/android/adservices/ui/notifications/NotificationActivityUiAutomatorTest.java +++ b/adservices/apk/tests/notification/src/com/android/adservices/ui/notifications/NotificationActivityUiAutomatorTest.java @@ -84,7 +84,6 @@ public final class NotificationActivityUiAutomatorTest extends AdServicesExtende public void setup() throws UiObjectNotFoundException, IOException { mContext = spy(appContext.get()); - doReturn(false).when(mMockFlags).getEuNotifFlowChangeEnabled(); doReturn(true).when(mMockFlags).getUIDialogsFeatureEnabled(); doReturn(true).when(mMockFlags).isUiFeatureTypeLoggingEnabled(); doReturn(true).when(mMockFlags).getRecordManualInteractionEnabled(); @@ -113,68 +112,6 @@ public final class NotificationActivityUiAutomatorTest extends AdServicesExtende AdservicesTestHelper.killAdservicesProcess(mContext); } - - @Test - @FlakyTest(bugId = 302607350) - public void moreButtonTest() throws Exception { - startActivity(true); - UiObject leftControlButton = - getElement(R.string.notificationUI_left_control_button_text_eu); - UiObject rightControlButton = - getElement(R.string.notificationUI_right_control_button_text_eu); - UiObject moreButton = getElement(R.string.notificationUI_more_button_text); - while (moreButton.exists()) { - moreButton.click(); - Thread.sleep(2000); - } - assertThat(leftControlButton.exists()).isTrue(); - assertThat(rightControlButton.exists()).isTrue(); - assertThat(moreButton.exists()).isFalse(); - } - - @Test - public void acceptedConfirmationScreenTest() throws Exception { - doReturn(false).when(mMockFlags).getGaUxFeatureEnabled(); - - startActivity(true); - UiObject leftControlButton = - getElement(R.string.notificationUI_left_control_button_text_eu); - UiObject rightControlButton = - getElement(R.string.notificationUI_right_control_button_text_eu); - UiObject moreButton = getElement(R.string.notificationUI_more_button_text); - while (moreButton.exists()) { - moreButton.click(); - Thread.sleep(2000); - } - assertThat(leftControlButton.exists()).isTrue(); - assertThat(rightControlButton.exists()).isTrue(); - assertThat(moreButton.exists()).isFalse(); - - rightControlButton.click(); - UiObject acceptedTitle = getElement(R.string.notificationUI_confirmation_accept_title); - assertThat(acceptedTitle.exists()).isTrue(); - } - - @Test - @FlakyTest(bugId = 302607350) - public void notificationEuGaTest() throws Exception { - doReturn(true).when(mMockFlags).getGaUxFeatureEnabled(); - doReturn("GA_UX").when(mMockFlags).getDebugUx(); - - startActivity(true); - - UiObject notificationEuGaTitle = getElement(R.string.notificationUI_header_ga_title_eu); - assertThat(notificationEuGaTitle.exists()).isTrue(); - - UiObject leftControlButton = - getElement(R.string.notificationUI_left_control_button_text_eu); - UiObject rightControlButton = - getElement(R.string.notificationUI_right_control_button_ga_text_eu); - UiObject moreButton = getElement(R.string.notificationUI_more_button_text); - - verifyControlsAndMoreButtonAreDisplayed(leftControlButton, rightControlButton, moreButton); - } - @Test @FlakyTest(bugId = 302607350) public void notificationRowGaTest() throws Exception { @@ -192,59 +129,29 @@ public final class NotificationActivityUiAutomatorTest extends AdServicesExtende } @Test - public void acceptedConfirmationScreenGaTest() throws Exception { - doReturn(true).when(mMockFlags).getGaUxFeatureEnabled(); - doReturn("GA_UX").when(mMockFlags).getDebugUx(); - - startActivity(true); - - UiObject leftControlButton = - getElement(R.string.notificationUI_left_control_button_text_eu); - UiObject rightControlButton = - getElement(R.string.notificationUI_right_control_button_ga_text_eu); - UiObject moreButton = getElement(R.string.notificationUI_more_button_text); - - verifyControlsAndMoreButtonAreDisplayed(leftControlButton, rightControlButton, moreButton); - - rightControlButton.click(); - - UiObject acceptedTitle = getElement(R.string.notificationUI_fledge_measurement_title); - assertThat(acceptedTitle.exists()).isTrue(); - UiObject leftControlButtonOnSecondPage = - getElement(R.string.notificationUI_confirmation_left_control_button_text); - UiObject rightControlButtonOnSecondPage = - getElement(R.string.notificationUI_confirmation_right_control_button_text); - UiObject moreButtonOnSecondPage = getElement(R.string.notificationUI_more_button_text); - verifyControlsAndMoreButtonAreDisplayed( - leftControlButtonOnSecondPage, - rightControlButtonOnSecondPage, - moreButtonOnSecondPage); - } - - @Test @FlakyTest(bugId = 302607350) - public void declinedConfirmationScreenGaTest() throws Exception { + public void notificationEuGaTest() throws Exception { doReturn(true).when(mMockFlags).getGaUxFeatureEnabled(); doReturn("GA_UX").when(mMockFlags).getDebugUx(); startActivity(true); UiObject leftControlButton = - getElement(R.string.notificationUI_left_control_button_text_eu); + getElement(R.string.notificationUI_confirmation_left_control_button_text); UiObject rightControlButton = - getElement(R.string.notificationUI_right_control_button_ga_text_eu); + getElement(R.string.notificationUI_confirmation_right_control_button_text); UiObject moreButton = getElement(R.string.notificationUI_more_button_text); verifyControlsAndMoreButtonAreDisplayed(leftControlButton, rightControlButton, moreButton); - leftControlButton.click(); + rightControlButton.click(); - UiObject acceptedTitle = getElement(R.string.notificationUI_fledge_measurement_title); + UiObject acceptedTitle = getElement(R.string.notificationUI_header_ga_title_eu_v2); assertThat(acceptedTitle.exists()).isTrue(); UiObject leftControlButtonOnSecondPage = - getElement(R.string.notificationUI_confirmation_left_control_button_text); + getElement(R.string.notificationUI_left_control_button_text_eu_v2); UiObject rightControlButtonOnSecondPage = - getElement(R.string.notificationUI_confirmation_right_control_button_text); + getElement(R.string.notificationUI_right_control_button_ga_text_eu_v2); UiObject moreButtonOnSecondPage = getElement(R.string.notificationUI_more_button_text); verifyControlsAndMoreButtonAreDisplayed( leftControlButtonOnSecondPage, diff --git a/adservices/apk/tests/settings/OWNERS b/adservices/apk/tests/settings/OWNERS index 9530244f7..b0f9f618b 100644 --- a/adservices/apk/tests/settings/OWNERS +++ b/adservices/apk/tests/settings/OWNERS @@ -6,3 +6,4 @@ tccyp@google.com yangwangyw@google.com albertkong@google.com bozhaobz@google.com +ardroid@google.com diff --git a/adservices/apk/tests/settings/src/com/android/adservices/ui/settings/DialogFragmentTest.java b/adservices/apk/tests/settings/src/com/android/adservices/ui/settings/DialogFragmentTest.java index f8ec1d3f6..0df5e26eb 100644 --- a/adservices/apk/tests/settings/src/com/android/adservices/ui/settings/DialogFragmentTest.java +++ b/adservices/apk/tests/settings/src/com/android/adservices/ui/settings/DialogFragmentTest.java @@ -22,21 +22,26 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; import android.content.Context; import android.content.Intent; +import android.os.RemoteException; +import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.FlakyTest; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.uiautomator.By; import androidx.test.uiautomator.UiDevice; -import androidx.test.uiautomator.UiObject; +import androidx.test.uiautomator.UiObject2; import androidx.test.uiautomator.Until; +import com.android.adservices.LogUtil; import com.android.adservices.api.R; import com.android.adservices.common.AdServicesExtendedMockitoTestCase; +import com.android.adservices.common.AdservicesTestHelper; import com.android.adservices.common.RequiresSdkLevelAtLeastT; import com.android.adservices.data.topics.Topic; import com.android.adservices.service.Flags; @@ -70,10 +75,13 @@ public final class DialogFragmentTest extends AdServicesExtendedMockitoTestCase private static final String PRIVACY_SANDBOX_TEST_PACKAGE = "android.test.adservices.ui.MAIN"; private static final int LAUNCH_TIMEOUT = 5000; + private static final Context sContext = ApplicationProvider.getApplicationContext(); private static UiDevice sDevice; - @Mock private ConsentManager mConsentManager; - @Mock private Flags mMockFlags; + @Mock + private ConsentManager mConsentManager; + @Mock + private Flags mMockFlags; @Before public void setup() throws Exception { @@ -82,6 +90,7 @@ public final class DialogFragmentTest extends AdServicesExtendedMockitoTestCase // UiDialogFragmentEnable flag should be on for this test doReturn(true).when(mMockFlags).getUiDialogFragmentEnabled(); doReturn(true).when(mMockFlags).getUIDialogsFeatureEnabled(); + doReturn(true).when(mMockFlags).getRecordManualInteractionEnabled(); List<Topic> tempList = new ArrayList<>(); tempList.add(Topic.create(10001, 1, 1)); tempList.add(Topic.create(10002, 1, 1)); @@ -135,15 +144,20 @@ public final class DialogFragmentTest extends AdServicesExtendedMockitoTestCase doNothing().when(mConsentManager).disable(any(Context.class)); doReturn(GA_UX).when(mConsentManager).getUx(); - startActivityFromHomeAndCheckMainSwitch(); + try { + startActivityFromHomeAndCheckMainSwitch(); + } catch (RemoteException e) { + LogUtil.e("RemoteException from setOrientation."); + } } - private void startActivityFromHomeAndCheckMainSwitch() { + private void startActivityFromHomeAndCheckMainSwitch() throws RemoteException { // Initialize UiDevice instance sDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); // Start from the home screen sDevice.pressHome(); + sDevice.setOrientationNatural(); // Wait for launcher final String launcherPackage = sDevice.getLauncherPackageName(); @@ -164,12 +178,13 @@ public final class DialogFragmentTest extends AdServicesExtendedMockitoTestCase @After public void teardown() { ApkTestUtil.takeScreenshot(sDevice, getClass().getSimpleName() + "_" + getTestName() + "_"); + AdservicesTestHelper.killAdservicesProcess(sContext); } @Test - public void optOutDialogTest() throws Exception { - UiObject consentSwitch = ApkTestUtil.getConsentSwitch(sDevice); - assertThat(consentSwitch.exists()).isTrue(); + public void optOutDialogTest() { + UiObject2 consentSwitch = ApkTestUtil.getConsentSwitch2(sDevice); + assertThat(consentSwitch).isNotNull(); // guarantee in on state if (!consentSwitch.isChecked()) { @@ -178,24 +193,28 @@ public final class DialogFragmentTest extends AdServicesExtendedMockitoTestCase // click switch consentSwitch.click(); - UiObject dialogTitle = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_opt_out_title); + UiObject2 dialogTitle = + ApkTestUtil.getElement(sContext, sDevice, R.string.settingsUI_dialog_opt_out_title); - UiObject negativeText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_negative_text); - assertThat(dialogTitle.exists()).isTrue(); - assertThat(negativeText.exists()).isTrue(); + UiObject2 negativeText = + ApkTestUtil.getElement(sContext, sDevice, R.string.settingsUI_dialog_negative_text); + assertThat(dialogTitle).isNotNull(); + assertThat(negativeText).isNotNull(); // cancel negativeText.click(); + // Retrieve a new instance to avoid android.support.test.uiautomator.StaleObjectException. + consentSwitch = ApkTestUtil.getConsentSwitch2(sDevice); // click switch consentSwitch.click(); - dialogTitle = ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_opt_out_title); - UiObject positiveText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_opt_out_positive_text); - assertThat(dialogTitle.exists()).isTrue(); - assertThat(negativeText.exists()).isTrue(); + dialogTitle = ApkTestUtil.getElement(sContext, sDevice, + R.string.settingsUI_dialog_opt_out_title); + UiObject2 positiveText = + ApkTestUtil.getElement(sContext, sDevice, + R.string.settingsUI_dialog_opt_out_positive_text); + assertThat(dialogTitle).isNotNull(); + assertThat(negativeText).isNotNull(); // confirm positiveText.click(); } @@ -203,35 +222,37 @@ public final class DialogFragmentTest extends AdServicesExtendedMockitoTestCase @Test public void blockTopicDialogTest() throws Exception { // open topics view - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_topics_title); - UiObject blockTopicText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_block_topic_title, 0); - assertThat(blockTopicText.exists()).isTrue(); + ApkTestUtil.scrollToAndClick(sContext, sDevice, R.string.settingsUI_topics_title); + UiObject2 blockTopicText = + ApkTestUtil.getElement(sContext, sDevice, R.string.settingsUI_block_topic_title, 0); + assertThat(blockTopicText).isNotNull(); // click block blockTopicText.click(); - UiObject dialogTitle = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_block_topic_message); - UiObject positiveText = - ApkTestUtil.getElement( - sDevice, R.string.settingsUI_dialog_block_topic_positive_text); - assertThat(dialogTitle.exists()).isTrue(); - assertThat(positiveText.exists()).isTrue(); + UiObject2 dialogTitle = + ApkTestUtil.getElement(sContext, sDevice, + R.string.settingsUI_dialog_block_topic_message); + UiObject2 positiveText = + ApkTestUtil.getElement(sContext, sDevice, + R.string.settingsUI_dialog_block_topic_positive_text); + assertThat(dialogTitle).isNotNull(); + assertThat(positiveText).isNotNull(); // confirm positiveText.click(); - verify(mConsentManager).revokeConsentForTopic(any(Topic.class)); - blockTopicText = ApkTestUtil.getElement(sDevice, R.string.settingsUI_block_topic_title, 0); - assertThat(blockTopicText.exists()).isTrue(); + verify(mConsentManager, timeout(1000)).revokeConsentForTopic(any(Topic.class)); + blockTopicText = ApkTestUtil.getElement(sContext, sDevice, + R.string.settingsUI_block_topic_title, 0); + assertThat(blockTopicText).isNotNull(); // click block again blockTopicText.click(); - dialogTitle = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_block_topic_message); - UiObject negativeText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_negative_text); - assertThat(dialogTitle.exists()).isTrue(); - assertThat(negativeText.exists()).isTrue(); + dialogTitle = ApkTestUtil.getElement(sContext, sDevice, + R.string.settingsUI_dialog_block_topic_message); + UiObject2 negativeText = + ApkTestUtil.getElement(sContext, sDevice, R.string.settingsUI_dialog_negative_text); + assertThat(dialogTitle).isNotNull(); + assertThat(negativeText).isNotNull(); // cancel and verify it has still only been called once negativeText.click(); @@ -241,59 +262,62 @@ public final class DialogFragmentTest extends AdServicesExtendedMockitoTestCase @Test public void unblockTopicDialogTest() throws Exception { // open topics view - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_topics_title); + ApkTestUtil.scrollToAndClick(sContext, sDevice, R.string.settingsUI_topics_title); // open blocked topics view - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_blocked_topics_title); - UiObject unblockTopicText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_unblock_topic_title, 0); - assertThat(unblockTopicText.exists()).isTrue(); + ApkTestUtil.scrollToAndClick(sContext, sDevice, R.string.settingsUI_blocked_topics_title); + UiObject2 unblockTopicText = + ApkTestUtil.getElement(sContext, sDevice, R.string.settingsUI_unblock_topic_title, + 0); + assertThat(unblockTopicText).isNotNull(); // click unblock unblockTopicText.click(); - UiObject dialogTitle = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_unblock_topic_message); - UiObject positiveText = - ApkTestUtil.getElement( + UiObject2 dialogTitle = + ApkTestUtil.getElement(sContext, sDevice, + R.string.settingsUI_dialog_unblock_topic_message); + UiObject2 positiveText = + ApkTestUtil.getElement(sContext, sDevice, R.string.settingsUI_dialog_unblock_topic_positive_text); - assertThat(dialogTitle.exists()).isTrue(); - assertThat(positiveText.exists()).isTrue(); + assertThat(dialogTitle).isNotNull(); + assertThat(positiveText).isNotNull(); // confirm positiveText.click(); verify(mConsentManager).restoreConsentForTopic(any(Topic.class)); - unblockTopicText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_unblock_topic_title, 0); - assertThat(unblockTopicText.exists()).isTrue(); + unblockTopicText = ApkTestUtil.getElement(sContext, sDevice, + R.string.settingsUI_unblock_topic_title, 0); + assertThat(unblockTopicText).isNotNull(); } @Test public void resetTopicDialogTest() throws Exception { // open topics view - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_topics_title); + ApkTestUtil.scrollToAndClick(sContext, sDevice, R.string.settingsUI_topics_title); // click reset - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_reset_topics_title); - UiObject dialogTitle = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_reset_topic_message); - UiObject positiveText = - ApkTestUtil.getElement( - sDevice, R.string.settingsUI_dialog_reset_topic_positive_text); - assertThat(dialogTitle.exists()).isTrue(); - assertThat(positiveText.exists()).isTrue(); + ApkTestUtil.scrollToAndClick(sContext, sDevice, R.string.settingsUI_reset_topics_title); + UiObject2 dialogTitle = + ApkTestUtil.getElement(sContext, sDevice, + R.string.settingsUI_dialog_reset_topic_message); + UiObject2 positiveText = + ApkTestUtil.getElement(sContext, sDevice, + R.string.settingsUI_dialog_reset_topic_positive_text); + assertThat(dialogTitle).isNotNull(); + assertThat(positiveText).isNotNull(); // confirm positiveText.click(); - verify(mConsentManager).resetTopics(); + verify(mConsentManager, timeout(1000)).resetTopics(); // click reset again - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_reset_topics_title); - dialogTitle = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_reset_topic_message); - UiObject negativeText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_negative_text); - assertThat(dialogTitle.exists()).isTrue(); - assertThat(negativeText.exists()).isTrue(); + ApkTestUtil.scrollToAndClick(sContext, sDevice, R.string.settingsUI_reset_topics_title); + dialogTitle = ApkTestUtil.getElement(sContext, sDevice, + R.string.settingsUI_dialog_reset_topic_message); + UiObject2 negativeText = + ApkTestUtil.getElement(sContext, sDevice, R.string.settingsUI_dialog_negative_text); + assertThat(dialogTitle).isNotNull(); + assertThat(negativeText).isNotNull(); // cancel and verify it has still only been called once negativeText.click(); @@ -305,39 +329,44 @@ public final class DialogFragmentTest extends AdServicesExtendedMockitoTestCase public void blockAppDialogTest() throws Exception { // perform a gentle swipe so scroll won't miss the text close to the // bottom of the current screen. - UiObject appsTitle = ApkTestUtil.getElement(sDevice, R.string.settingsUI_apps_title); - if(!appsTitle.exists()){ + UiObject2 appsTitle = ApkTestUtil.getElement(sContext, sDevice, + R.string.settingsUI_apps_title); + if (appsTitle == null) { ApkTestUtil.gentleSwipe(sDevice); } // open apps view - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_apps_title); - UiObject blockAppText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_block_app_title, 0); - assertThat(blockAppText.exists()).isTrue(); + ApkTestUtil.scrollToAndClick(sContext, sDevice, R.string.settingsUI_apps_title); + UiObject2 blockAppText = + ApkTestUtil.getElement(sContext, sDevice, R.string.settingsUI_block_app_title, 0); + assertThat(blockAppText).isNotNull(); // click block blockAppText.click(); - UiObject dialogTitle = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_block_app_message); - UiObject positiveText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_block_app_positive_text); - assertThat(dialogTitle.exists()).isTrue(); - assertThat(positiveText.exists()).isTrue(); + UiObject2 dialogTitle = + ApkTestUtil.getElement(sContext, sDevice, + R.string.settingsUI_dialog_block_app_message); + UiObject2 positiveText = + ApkTestUtil.getElement(sContext, sDevice, + R.string.settingsUI_dialog_block_app_positive_text); + assertThat(dialogTitle).isNotNull(); + assertThat(positiveText).isNotNull(); // confirm positiveText.click(); - verify(mConsentManager).revokeConsentForApp(any(App.class)); - blockAppText = ApkTestUtil.getElement(sDevice, R.string.settingsUI_block_app_title, 0); - assertThat(blockAppText.exists()).isTrue(); + verify(mConsentManager, timeout(1000)).revokeConsentForApp(any(App.class)); + blockAppText = ApkTestUtil.getElement(sContext, sDevice, + R.string.settingsUI_block_app_title, 0); + assertThat(blockAppText).isNotNull(); // click block again blockAppText.click(); - dialogTitle = ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_block_app_message); - UiObject negativeText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_negative_text); - assertThat(dialogTitle.exists()).isTrue(); - assertThat(negativeText.exists()).isTrue(); + dialogTitle = ApkTestUtil.getElement(sContext, sDevice, + R.string.settingsUI_dialog_block_app_message); + UiObject2 negativeText = + ApkTestUtil.getElement(sContext, sDevice, R.string.settingsUI_dialog_negative_text); + assertThat(dialogTitle).isNotNull(); + assertThat(negativeText).isNotNull(); // cancel and verify it has still only been called once negativeText.click(); @@ -346,71 +375,78 @@ public final class DialogFragmentTest extends AdServicesExtendedMockitoTestCase @Test public void unblockAppDialogTest() throws Exception { - UiObject appsTitle = ApkTestUtil.getElement(sDevice, R.string.settingsUI_apps_title); - if(!appsTitle.exists()){ + UiObject2 appsTitle = ApkTestUtil.getElement(sContext, sDevice, + R.string.settingsUI_apps_title); + if (appsTitle == null) { ApkTestUtil.gentleSwipe(sDevice); } // open apps view - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_apps_title); + ApkTestUtil.scrollToAndClick(sContext, sDevice, R.string.settingsUI_apps_title); // perform a gentle swipe so scroll won't miss the text close to the // bottom of the current screen. ApkTestUtil.gentleSwipe(sDevice); // open blocked apps view - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_blocked_apps_title); - UiObject unblockAppText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_unblock_app_title, 0); - assertThat(unblockAppText.exists()).isTrue(); + ApkTestUtil.scrollToAndClick(sContext, sDevice, R.string.settingsUI_blocked_apps_title); + UiObject2 unblockAppText = + ApkTestUtil.getElement(sContext, sDevice, R.string.settingsUI_unblock_app_title, 0); + assertThat(unblockAppText).isNotNull(); // click unblock unblockAppText.click(); - UiObject dialogTitle = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_unblock_app_message); - UiObject positiveText = - ApkTestUtil.getElement( - sDevice, R.string.settingsUI_dialog_unblock_app_positive_text); - assertThat(dialogTitle.exists()).isTrue(); - assertThat(positiveText.exists()).isTrue(); + UiObject2 dialogTitle = + ApkTestUtil.getElement(sContext, sDevice, + R.string.settingsUI_dialog_unblock_app_message); + UiObject2 positiveText = + ApkTestUtil.getElement(sContext, sDevice, + R.string.settingsUI_dialog_unblock_app_positive_text); + assertThat(dialogTitle).isNotNull(); + assertThat(positiveText).isNotNull(); // confirm positiveText.click(); verify(mConsentManager).restoreConsentForApp(any(App.class)); - unblockAppText = ApkTestUtil.getElement(sDevice, R.string.settingsUI_unblock_app_title, 0); - assertThat(unblockAppText.exists()).isTrue(); + unblockAppText = ApkTestUtil.getElement(sContext, sDevice, + R.string.settingsUI_unblock_app_title, 0); + assertThat(unblockAppText).isNotNull(); } @Test public void resetAppDialogTest() throws Exception { - UiObject appsTitle = ApkTestUtil.getElement(sDevice, R.string.settingsUI_apps_title); - if(!appsTitle.exists()){ + UiObject2 appsTitle = ApkTestUtil.getElement(sContext, sDevice, + R.string.settingsUI_apps_title); + if (appsTitle != null) { ApkTestUtil.gentleSwipe(sDevice); } // open apps view - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_apps_title); + ApkTestUtil.scrollToAndClick(sContext, sDevice, R.string.settingsUI_apps_title); // click reset - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_reset_apps_title); - UiObject dialogTitle = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_reset_app_message); - UiObject positiveText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_reset_app_positive_text); - assertThat(dialogTitle.exists()).isTrue(); - assertThat(positiveText.exists()).isTrue(); + ApkTestUtil.scrollToAndClick(sContext, sDevice, R.string.settingsUI_reset_apps_title); + UiObject2 dialogTitle = + ApkTestUtil.getElement(sContext, sDevice, + R.string.settingsUI_dialog_reset_app_message); + UiObject2 positiveText = + ApkTestUtil.getElement(sContext, sDevice, + R.string.settingsUI_dialog_reset_app_positive_text); + assertThat(dialogTitle).isNotNull(); + assertThat(positiveText).isNotNull(); // confirm positiveText.click(); - verify(mConsentManager).resetApps(); + verify(mConsentManager, timeout(1000)).resetApps(); // click reset again - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_reset_apps_title); - dialogTitle = ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_reset_app_message); - UiObject negativeText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_negative_text); - assertThat(dialogTitle.exists()).isTrue(); - assertThat(negativeText.exists()).isTrue(); + ApkTestUtil.scrollToAndClick(sContext, sDevice, R.string.settingsUI_reset_apps_title); + dialogTitle = ApkTestUtil.getElement(sContext, sDevice, + R.string.settingsUI_dialog_reset_app_message); + UiObject2 negativeText = + ApkTestUtil.getElement(sContext, sDevice, R.string.settingsUI_dialog_negative_text); + assertThat(dialogTitle).isNotNull(); + assertThat(negativeText).isNotNull(); // cancel and verify it has still only been called once negativeText.click(); diff --git a/adservices/apk/tests/settingsgaota/OWNERS b/adservices/apk/tests/settingsgaota/OWNERS index 9530244f7..b0f9f618b 100644 --- a/adservices/apk/tests/settingsgaota/OWNERS +++ b/adservices/apk/tests/settingsgaota/OWNERS @@ -6,3 +6,4 @@ tccyp@google.com yangwangyw@google.com albertkong@google.com bozhaobz@google.com +ardroid@google.com diff --git a/adservices/apk/tests/settingsgaota/src/com/android/adservices/ui/settingsga/SettingsActivityUiAutomatorTest.java b/adservices/apk/tests/settingsgaota/src/com/android/adservices/ui/settingsga/SettingsActivityUiAutomatorTest.java deleted file mode 100644 index 46dd56ef3..000000000 --- a/adservices/apk/tests/settingsgaota/src/com/android/adservices/ui/settingsga/SettingsActivityUiAutomatorTest.java +++ /dev/null @@ -1,569 +0,0 @@ -/* - * 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.ui.settingsga; - -import static com.android.adservices.service.ui.ux.collection.PrivacySandboxUxCollection.BETA_UX; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import android.content.Context; -import android.content.Intent; -import android.os.Build; - -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.FlakyTest; -import androidx.test.platform.app.InstrumentationRegistry; -import androidx.test.uiautomator.By; -import androidx.test.uiautomator.UiDevice; -import androidx.test.uiautomator.UiObject; -import androidx.test.uiautomator.UiObjectNotFoundException; -import androidx.test.uiautomator.Until; - -import com.android.adservices.api.R; -import com.android.adservices.common.AdServicesExtendedMockitoTestCase; -import com.android.adservices.common.AdservicesTestHelper; -import com.android.adservices.data.topics.Topic; -import com.android.adservices.service.Flags; -import com.android.adservices.service.FlagsFactory; -import com.android.adservices.service.common.BackgroundJobsManager; -import com.android.adservices.service.consent.AdServicesApiConsent; -import com.android.adservices.service.consent.AdServicesApiType; -import com.android.adservices.service.consent.App; -import com.android.adservices.service.consent.ConsentManager; -import com.android.adservices.service.ui.data.UxStatesManager; -import com.android.adservices.ui.util.ApkTestUtil; -import com.android.dx.mockito.inline.extended.ExtendedMockito; -import com.android.modules.utils.build.SdkLevel; -import com.android.modules.utils.testing.ExtendedMockitoRule.SpyStatic; - -import com.google.common.collect.ImmutableList; - -import org.junit.After; -import org.junit.Assume; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -@SpyStatic(FlagsFactory.class) -@SpyStatic(BackgroundJobsManager.class) -@SpyStatic(ConsentManager.class) -@SpyStatic(UxStatesManager.class) -@RunWith(AndroidJUnit4.class) -public final class SettingsActivityUiAutomatorTest extends AdServicesExtendedMockitoTestCase { - - private static final String PRIVACY_SANDBOX_TEST_PACKAGE = "android.test.adservices.ui.MAIN"; - private static final int LAUNCH_TIMEOUT = 5_000; - - private static UiDevice sDevice; - - private String mTestName; - private ConsentManager mConsentManager; - @Mock private Flags mMockFlags; - @Mock private UxStatesManager mUxStatesManager; - - @Before - public void setup() throws UiObjectNotFoundException, IOException { - Assume.assumeTrue(SdkLevel.isAtLeastS()); - - // Mock static method FlagsFactory.getFlags() to return Mock Flags. - ExtendedMockito.doReturn(mMockFlags).when(FlagsFactory::getFlags); - doReturn(false).when(mMockFlags).getUiDialogFragmentEnabled(); - doReturn(true).when(mMockFlags).getUIDialogsFeatureEnabled(); - // prepare objects used by static mocking - mConsentManager = mock(ConsentManager.class); - List<Topic> tempList = new ArrayList<>(); - tempList.add(Topic.create(10001, 1, 1)); - tempList.add(Topic.create(10002, 1, 1)); - tempList.add(Topic.create(10003, 1, 1)); - ImmutableList<Topic> topicsList = ImmutableList.copyOf(tempList); - doReturn(topicsList).when(mConsentManager).getKnownTopicsWithConsent(); - - tempList = new ArrayList<>(); - tempList.add(Topic.create(10004, 1, 1)); - tempList.add(Topic.create(10005, 1, 1)); - ImmutableList<Topic> blockedTopicsList = ImmutableList.copyOf(tempList); - doReturn(blockedTopicsList).when(mConsentManager).getTopicsWithRevokedConsent(); - - List<App> appTempList = new ArrayList<>(); - appTempList.add(App.create("app1")); - appTempList.add(App.create("app2")); - ImmutableList<App> appsList = ImmutableList.copyOf(appTempList); - doReturn(appsList).when(mConsentManager).getKnownAppsWithConsent(); - - appTempList = new ArrayList<>(); - appTempList.add(App.create("app3")); - ImmutableList<App> blockedAppsList = ImmutableList.copyOf(appTempList); - doReturn(blockedAppsList).when(mConsentManager).getAppsWithRevokedConsent(); - - doNothing().when(mConsentManager).resetTopicsAndBlockedTopics(); - doNothing().when(mConsentManager).resetTopics(); - doNothing().when(mConsentManager).revokeConsentForTopic(any(Topic.class)); - doNothing().when(mConsentManager).restoreConsentForTopic(any(Topic.class)); - try { - doNothing().when(mConsentManager).resetAppsAndBlockedApps(); - doNothing().when(mConsentManager).resetApps(); - doNothing().when(mConsentManager).revokeConsentForApp(any(App.class)); - doNothing().when(mConsentManager).restoreConsentForApp(any(App.class)); - } catch (IOException e) { - e.printStackTrace(); - } - doNothing().when(mConsentManager).resetMeasurement(); - - ExtendedMockito.doNothing() - .when(() -> BackgroundJobsManager.scheduleAllBackgroundJobs(any(Context.class))); - ExtendedMockito.doReturn(mConsentManager) - .when(() -> ConsentManager.getInstance(any(Context.class))); - doReturn(AdServicesApiConsent.GIVEN).when(mConsentManager).getConsent(); - doReturn(AdServicesApiConsent.GIVEN) - .when(mConsentManager) - .getConsent(AdServicesApiType.TOPICS); - doReturn(AdServicesApiConsent.GIVEN) - .when(mConsentManager) - .getConsent(AdServicesApiType.FLEDGE); - doReturn(AdServicesApiConsent.GIVEN) - .when(mConsentManager) - .getConsent(AdServicesApiType.MEASUREMENTS); - - doNothing().when(mConsentManager).enable(any(Context.class)); - doNothing().when(mConsentManager).disable(any(Context.class)); - - // Mock BETA_UX for testing. - ExtendedMockito.doReturn(mUxStatesManager) - .when(() -> UxStatesManager.getInstance(any(Context.class))); - doReturn(false).when(mMockFlags).getConsentNotificationActivityDebugMode(); - doReturn(BETA_UX).when(mUxStatesManager).getUx(); - doReturn(BETA_UX).when(mConsentManager).getUx(); - - startActivityFromHomeAndCheckMainSwitch(); - } - - private void startActivityFromHomeAndCheckMainSwitch() throws UiObjectNotFoundException { - // Initialize UiDevice instance - sDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); - - // Start from the home screen - sDevice.pressHome(); - - // Wait for launcher - final String launcherPackage = sDevice.getLauncherPackageName(); - assertThat(launcherPackage).isNotNull(); - sDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), LAUNCH_TIMEOUT); - - // launch app - Context context = ApplicationProvider.getApplicationContext(); - Intent intent = new Intent(PRIVACY_SANDBOX_TEST_PACKAGE); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - - // Wait for the app to appear - sDevice.wait( - Until.hasObject(By.pkg(PRIVACY_SANDBOX_TEST_PACKAGE).depth(0)), LAUNCH_TIMEOUT); - } - - @After - public void teardown() { - ApkTestUtil.takeScreenshot(sDevice, getClass().getSimpleName() + "_" + mTestName + "_"); - - AdservicesTestHelper.killAdservicesProcess(ApplicationProvider.getApplicationContext()); - } - - @Test - public void optOutDialogTest() throws UiObjectNotFoundException { - mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); - - UiObject consentSwitch = ApkTestUtil.getConsentSwitch(sDevice); - assertThat(consentSwitch.exists()).isTrue(); - - // click switch - consentSwitch.click(); - UiObject dialogTitle = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_opt_out_title); - UiObject positiveText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_opt_out_positive_text); - assertThat(dialogTitle.exists()).isTrue(); - assertThat(positiveText.exists()).isTrue(); - - // confirm - positiveText.click(); - - // click switch - consentSwitch.click(); - dialogTitle = ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_opt_out_title); - UiObject negativeText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_negative_text); - assertThat(dialogTitle.exists()).isTrue(); - assertThat(negativeText.exists()).isTrue(); - - // cancel - negativeText.click(); - } - - @Test - public void blockTopicDialogTest() throws UiObjectNotFoundException { - mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); - // open topics view - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_topics_title); - UiObject blockTopicText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_block_topic_title, 0); - assertThat(blockTopicText.exists()).isTrue(); - - // click block - blockTopicText.click(); - UiObject dialogTitle = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_block_topic_message); - UiObject positiveText = - ApkTestUtil.getElement( - sDevice, R.string.settingsUI_dialog_block_topic_positive_text); - assertThat(dialogTitle.exists()).isTrue(); - assertThat(positiveText.exists()).isTrue(); - - // confirm - positiveText.click(); - verify(mConsentManager).revokeConsentForTopic(any(Topic.class)); - blockTopicText = ApkTestUtil.getElement(sDevice, R.string.settingsUI_block_topic_title, 0); - assertThat(blockTopicText.exists()).isTrue(); - - // click block again - blockTopicText.click(); - dialogTitle = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_block_topic_message); - UiObject negativeText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_negative_text); - assertThat(dialogTitle.exists()).isTrue(); - assertThat(negativeText.exists()).isTrue(); - - // cancel and verify it has still only been called once - negativeText.click(); - verify(mConsentManager).revokeConsentForTopic(any(Topic.class)); - } - - @Test - public void unblockTopicDialogTest() throws UiObjectNotFoundException { - mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); - // open topics view - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_topics_title); - - // open blocked topics view - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_blocked_topics_title); - UiObject unblockTopicText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_unblock_topic_title, 0); - assertThat(unblockTopicText.exists()).isTrue(); - - // click unblock - unblockTopicText.click(); - UiObject dialogTitle = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_unblock_topic_message); - UiObject positiveText = - ApkTestUtil.getElement( - sDevice, R.string.settingsUI_dialog_unblock_topic_positive_text); - assertThat(dialogTitle.exists()).isTrue(); - assertThat(positiveText.exists()).isTrue(); - - // confirm - positiveText.click(); - verify(mConsentManager).restoreConsentForTopic(any(Topic.class)); - unblockTopicText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_unblock_topic_title, 0); - assertThat(unblockTopicText.exists()).isTrue(); - } - - @Test - public void resetMeasurementDialogTest() throws UiObjectNotFoundException { - mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); - doReturn(true).when(mMockFlags).getGaUxFeatureEnabled(); - - startActivityFromHomeAndCheckMainSwitch(); - // open measurement view - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_measurement_view_title); - - // click reset - clickResetBtn(); - - // click reset again - clickResetBtn(); - - verify(mConsentManager, times(2)).resetMeasurement(); - } - - @Test - public void resetTopicDialogTest() throws UiObjectNotFoundException { - mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); - // open topics view - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_topics_title); - - // click reset - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_reset_topics_title); - UiObject dialogTitle = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_reset_topic_message); - UiObject positiveText = - ApkTestUtil.getElement( - sDevice, R.string.settingsUI_dialog_reset_topic_positive_text); - assertThat(dialogTitle.exists()).isTrue(); - assertThat(positiveText.exists()).isTrue(); - - // confirm - positiveText.click(); - verify(mConsentManager).resetTopics(); - - // click reset again - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_reset_topics_title); - dialogTitle = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_reset_topic_message); - UiObject negativeText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_negative_text); - assertThat(dialogTitle.exists()).isTrue(); - assertThat(negativeText.exists()).isTrue(); - - // cancel and verify it has still only been called once - negativeText.click(); - verify(mConsentManager).resetTopics(); - } - - @Test - public void blockAppDialogTest() throws UiObjectNotFoundException, IOException { - mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); - - UiObject appsTitle = ApkTestUtil.getElement(sDevice, R.string.settingsUI_apps_title); - if (!appsTitle.exists()) { - ApkTestUtil.gentleSwipe(sDevice); - } - - // open apps view - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_apps_title); - UiObject blockAppText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_block_app_title, 0); - assertThat(blockAppText.exists()).isTrue(); - - // click block - blockAppText.click(); - UiObject dialogTitle = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_block_app_message); - UiObject positiveText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_block_app_positive_text); - assertThat(dialogTitle.exists()).isTrue(); - assertThat(positiveText.exists()).isTrue(); - - // confirm - positiveText.click(); - verify(mConsentManager).revokeConsentForApp(any(App.class)); - blockAppText = ApkTestUtil.getElement(sDevice, R.string.settingsUI_block_app_title, 0); - assertThat(blockAppText.exists()).isTrue(); - - // click block again - blockAppText.click(); - dialogTitle = ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_block_app_message); - UiObject negativeText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_negative_text); - assertThat(dialogTitle.exists()).isTrue(); - assertThat(negativeText.exists()).isTrue(); - - // cancel and verify it has still only been called once - negativeText.click(); - verify(mConsentManager).revokeConsentForApp(any(App.class)); - } - - @Test - public void unblockAppDialogTest() throws UiObjectNotFoundException, IOException { - mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); - - UiObject appsTitle = ApkTestUtil.getElement(sDevice, R.string.settingsUI_apps_title); - if (!appsTitle.exists()) { - ApkTestUtil.gentleSwipe(sDevice); - } - - // open apps view - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_apps_title); - - // perform a gentle swipe so scroll won't miss the text close to the - // bottom of the current screen. - ApkTestUtil.gentleSwipe(sDevice); - - // open blocked apps view - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_blocked_apps_title); - UiObject unblockAppText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_unblock_app_title, 0); - assertThat(unblockAppText.exists()).isTrue(); - - // click unblock - unblockAppText.click(); - UiObject dialogTitle = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_unblock_app_message); - UiObject positiveText = - ApkTestUtil.getElement( - sDevice, R.string.settingsUI_dialog_unblock_app_positive_text); - assertThat(dialogTitle.exists()).isTrue(); - assertThat(positiveText.exists()).isTrue(); - - // confirm - positiveText.click(); - verify(mConsentManager).restoreConsentForApp(any(App.class)); - unblockAppText = ApkTestUtil.getElement(sDevice, R.string.settingsUI_unblock_app_title, 0); - assertThat(unblockAppText.exists()).isTrue(); - } - - @Test - public void resetAppDialogTest() throws UiObjectNotFoundException, IOException { - mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); - - // perform a gentle swipe so scroll won't miss the text close to the - // bottom of the current screen. - UiObject appsTitle = ApkTestUtil.getElement(sDevice, R.string.settingsUI_apps_title); - if (!appsTitle.exists()) { - ApkTestUtil.gentleSwipe(sDevice); - } - - // open apps view - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_apps_title); - - // click reset - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_reset_apps_title); - UiObject dialogTitle = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_reset_app_message); - UiObject positiveText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_reset_app_positive_text); - assertThat(dialogTitle.exists()).isTrue(); - assertThat(positiveText.exists()).isTrue(); - - // confirm - positiveText.click(); - verify(mConsentManager).resetApps(); - - // click reset again - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_reset_apps_title); - dialogTitle = ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_reset_app_message); - UiObject negativeText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_negative_text); - assertThat(dialogTitle.exists()).isTrue(); - assertThat(negativeText.exists()).isTrue(); - - // cancel and verify it has still only been called once - negativeText.click(); - verify(mConsentManager).resetApps(); - } - - @Test - public void disableMeasurementTest() throws UiObjectNotFoundException { - mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); - - doReturn(false).when(mMockFlags).getGaUxFeatureEnabled(); - // start the activity again to reflect the GaUxFeature flag change - startActivityFromHomeAndCheckMainSwitch(); - // the entry point of ads measurement should be hidden - UiObject adsMeasurementTitle = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_measurement_view_title); - assertThat(adsMeasurementTitle.exists()).isFalse(); - } - - @Test - @FlakyTest(bugId = 295896410, detail = "UX test time out in presubmit") - public void disableDialogFeatureTest() throws UiObjectNotFoundException { - mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); - - doReturn(false).when(mMockFlags).getUIDialogsFeatureEnabled(); - - UiObject consentSwitch = ApkTestUtil.getConsentSwitch(sDevice); - assertThat(consentSwitch.exists()).isTrue(); - - // click switch - consentSwitch.click(); - UiObject dialogTitle = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_opt_out_title); - assertThat(dialogTitle.exists()).isFalse(); - - // open topics view - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_topics_title); - UiObject blockTopicText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_block_topic_title, 0); - assertThat(blockTopicText.exists()).isTrue(); - - // block topic - blockTopicText.click(); - dialogTitle = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_block_topic_message); - assertThat(dialogTitle.exists()).isFalse(); - verify(mConsentManager).revokeConsentForTopic(any(Topic.class)); - - // reset topic - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_reset_topics_title); - dialogTitle = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_reset_topic_message); - assertThat(dialogTitle.exists()).isFalse(); - verify(mConsentManager).resetTopics(); - - // open unblock topic view - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_blocked_topics_title); - UiObject unblockTopicText = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_unblock_topic_title, 0); - assertThat(unblockTopicText.exists()).isTrue(); - - // click unblock - unblockTopicText.click(); - dialogTitle = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_unblock_topic_message); - assertThat(dialogTitle.exists()).isFalse(); - verify(mConsentManager).restoreConsentForTopic(any(Topic.class)); - } - - /** - * Test for the Button to show blocked topics when the list of Topics is Empty The Button should - * be disabled if blocked topics is empty - */ - @Test - public void blockedTopicsWhenEmptyStateButtonTest() throws UiObjectNotFoundException { - // Topics UI is not available on R - Assume.assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S); - mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); - - // Return an empty topics list - doReturn(ImmutableList.of()).when(mConsentManager).getKnownTopicsWithConsent(); - // Return a non-empty blocked topics list - List<Topic> tempList = new ArrayList<>(); - tempList.add(Topic.create(10004, 1, 1)); - tempList.add(Topic.create(10005, 1, 1)); - ImmutableList<Topic> blockedTopicsList = ImmutableList.copyOf(tempList); - doReturn(blockedTopicsList).when(mConsentManager).getTopicsWithRevokedConsent(); - // navigate to topics page - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_topics_title); - UiObject blockedTopicsWhenEmptyStateButton = - ApkTestUtil.scrollTo(sDevice, R.string.settingsUI_blocked_topics_title); - assertThat(blockedTopicsWhenEmptyStateButton.isEnabled()).isTrue(); - } - - public void clickResetBtn() throws UiObjectNotFoundException { - // R Msmt UI is not scrollable - if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) { - ApkTestUtil.click( - sDevice, - com.android.adservices.api.R.string.settingsUI_measurement_view_reset_title); - } else { - ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_measurement_view_reset_title); - } - } -} diff --git a/adservices/apk/tests/settingsgaota/src/com/android/adservices/ui/settingsga/SettingsGaUiAutomatorTest.java b/adservices/apk/tests/settingsgaota/src/com/android/adservices/ui/settingsga/SettingsGaUiAutomatorTest.java index 3b3b5f406..5848168b1 100644 --- a/adservices/apk/tests/settingsgaota/src/com/android/adservices/ui/settingsga/SettingsGaUiAutomatorTest.java +++ b/adservices/apk/tests/settingsgaota/src/com/android/adservices/ui/settingsga/SettingsGaUiAutomatorTest.java @@ -69,14 +69,14 @@ public class SettingsGaUiAutomatorTest { .setFlag(KEY_ENABLE_AD_SERVICES_SYSTEM_API, false); @Before - public void setup() { + public void setup() throws RemoteException { Assume.assumeTrue(SdkLevel.isAtLeastS()); // Initialize UiDevice instance sDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); // Start from the home screen sDevice.pressHome(); - + sDevice.setOrientationNatural(); // Wait for launcher final String launcherPackage = sDevice.getLauncherPackageName(); assertThat(launcherPackage).isNotNull(); @@ -86,65 +86,10 @@ public class SettingsGaUiAutomatorTest { @After public void teardown() { ApkTestUtil.takeScreenshot(sDevice, getClass().getSimpleName() + "_" + mTestName + "_"); - AdservicesTestHelper.killAdservicesProcess(sContext); } @Test - public void mainPageGaUxFlagEnableToDisableFlipTest() throws UiObjectNotFoundException { - mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); - ShellUtils.runShellCommand("device_config put adservices ga_ux_enabled true"); - - ApkTestUtil.launchSettingView(sContext, sDevice, LAUNCH_TIMEOUT); - // beta switch shouldn't exist - UiObject mainSwitch = - sDevice.findObject(new UiSelector().className("android.widget.Switch")); - mainSwitch.waitForExists(PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT); - assertThat(mainSwitch.exists()).isFalse(); - - // make sure all the GA elements are there - scrollTo(R.string.settingsUI_topics_ga_title); - UiObject topicsButton = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_topics_ga_title); - topicsButton.waitForExists(PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT); - assertThat(topicsButton.exists()).isTrue(); - - scrollTo(R.string.settingsUI_apps_ga_title); - UiObject appButton = ApkTestUtil.getElement(sDevice, R.string.settingsUI_apps_ga_title); - appButton.waitForExists(PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT); - assertThat(appButton.exists()).isTrue(); - - scrollTo(R.string.settingsUI_measurement_view_title); - UiObject measurementButton = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_measurement_view_title); - appButton.waitForExists(PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT); - assertThat(measurementButton.exists()).isTrue(); - - sDevice.pressHome(); - ShellUtils.runShellCommand("device_config put adservices ga_ux_enabled false"); - - ApkTestUtil.launchSettingView(sContext, sDevice, LAUNCH_TIMEOUT); - // beta switch should exist - mainSwitch = sDevice.findObject(new UiSelector().className("android.widget.Switch")); - mainSwitch.waitForExists(PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT); - assertThat(mainSwitch.exists()).isTrue(); - - // make sure all the GA elements are gone - topicsButton = ApkTestUtil.getElement(sDevice, R.string.settingsUI_topics_ga_title); - topicsButton.waitForExists(PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT); - assertThat(topicsButton.exists()).isFalse(); - - appButton = ApkTestUtil.getElement(sDevice, R.string.settingsUI_apps_ga_title); - appButton.waitForExists(PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT); - assertThat(appButton.exists()).isFalse(); - - measurementButton = - ApkTestUtil.getElement(sDevice, R.string.settingsUI_measurement_view_title); - measurementButton.waitForExists(PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT); - assertThat(measurementButton.exists()).isFalse(); - } - - @Test public void mainPageGaUxFlagDisableToEnableFlipTest() throws UiObjectNotFoundException { mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); ShellUtils.runShellCommand("device_config put adservices ga_ux_enabled false"); @@ -291,9 +236,6 @@ public class SettingsGaUiAutomatorTest { // 3) check if Topics API is enabled ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_topics_ga_title); sDevice.waitForIdle(); - // rotate device to test rotating as well - sDevice.setOrientationLeft(); - sDevice.setOrientationNatural(); topicsToggle = sDevice.findObject(new UiSelector().className("android.widget.Switch")); topicsToggle.waitForExists(PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT); assertThat(topicsToggle.isChecked()).isTrue(); @@ -335,9 +277,7 @@ public class SettingsGaUiAutomatorTest { // 3) check if Fledge API is enabled ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_apps_ga_title); sDevice.waitForIdle(); - // rotate device to test rotating as well - sDevice.setOrientationLeft(); - sDevice.setOrientationNatural(); + fledgeToggle = sDevice.findObject(new UiSelector().className("android.widget.Switch")); fledgeToggle.waitForExists(PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT); assertThat(fledgeToggle.isChecked()).isTrue(); @@ -379,9 +319,7 @@ public class SettingsGaUiAutomatorTest { // 3) check if Measurement API is enabled ApkTestUtil.scrollToAndClick(sDevice, R.string.settingsUI_measurement_view_title); sDevice.waitForIdle(); - // rotate device to test rotating as well - sDevice.setOrientationLeft(); - sDevice.setOrientationNatural(); + measurementToggle = sDevice.findObject(new UiSelector().className("android.widget.Switch")); measurementToggle.waitForExists(PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT); assertThat(measurementToggle.isChecked()).isTrue(); @@ -453,10 +391,6 @@ public class SettingsGaUiAutomatorTest { ApkTestUtil.getElement(sDevice, R.string.settingsUI_dialog_opt_out_title); assertThat(dialogTitle.exists()).isTrue(); - - sDevice.setOrientationRight(); - assertThat(dialogTitle.exists()).isTrue(); - sDevice.setOrientationNatural(); } @Test diff --git a/adservices/apk/tests/settingsgauxselector/OWNERS b/adservices/apk/tests/settingsgauxselector/OWNERS index 4559e4fb5..b0f9f618b 100644 --- a/adservices/apk/tests/settingsgauxselector/OWNERS +++ b/adservices/apk/tests/settingsgauxselector/OWNERS @@ -6,4 +6,4 @@ tccyp@google.com yangwangyw@google.com albertkong@google.com bozhaobz@google.com - +ardroid@google.com diff --git a/adservices/apk/tests/settingsgauxselector/src/com/android/adservices/ui/settingsga/SettingsRvcUxSelectorUiAutomatorTest.java b/adservices/apk/tests/settingsgauxselector/src/com/android/adservices/ui/settingsga/SettingsRvcUxSelectorUiAutomatorTest.java index 352a1dcee..e8cbe676b 100644 --- a/adservices/apk/tests/settingsgauxselector/src/com/android/adservices/ui/settingsga/SettingsRvcUxSelectorUiAutomatorTest.java +++ b/adservices/apk/tests/settingsgauxselector/src/com/android/adservices/ui/settingsga/SettingsRvcUxSelectorUiAutomatorTest.java @@ -19,7 +19,7 @@ import static com.android.adservices.service.FlagsConstants.KEY_CONSENT_NOTIFICA import static com.android.adservices.service.FlagsConstants.KEY_DEBUG_UX; import static com.android.adservices.service.FlagsConstants.KEY_ENABLE_AD_SERVICES_SYSTEM_API; import static com.android.adservices.service.FlagsConstants.KEY_GA_UX_FEATURE_ENABLED; -import static com.android.adservices.service.FlagsConstants.KEY_RVC_NOTIFICATION_ENABLED; +import static com.android.adservices.service.FlagsConstants.KEY_RVC_POST_OTA_NOTIFICATION_ENABLED; import static com.android.adservices.service.FlagsConstants.KEY_RVC_UX_ENABLED; import static com.google.common.truth.Truth.assertThat; @@ -71,7 +71,7 @@ public class SettingsRvcUxSelectorUiAutomatorTest { .setFlag(KEY_ENABLE_AD_SERVICES_SYSTEM_API, true) .setFlag(KEY_CONSENT_NOTIFICATION_ACTIVITY_DEBUG_MODE, true) .setFlag(KEY_RVC_UX_ENABLED, true) - .setFlag(KEY_RVC_NOTIFICATION_ENABLED, true) + .setFlag(KEY_RVC_POST_OTA_NOTIFICATION_ENABLED, true) .setFlag(KEY_GA_UX_FEATURE_ENABLED, true) .setFlag(KEY_DEBUG_UX, "RVC_UX") .setCompatModeFlags(); diff --git a/adservices/apk/tests/util/java/com/android/adservices/ui/util/ApkTestUtil.java b/adservices/apk/tests/util/java/com/android/adservices/ui/util/ApkTestUtil.java index e90cb8895..bc18c4e0f 100644 --- a/adservices/apk/tests/util/java/com/android/adservices/ui/util/ApkTestUtil.java +++ b/adservices/apk/tests/util/java/com/android/adservices/ui/util/ApkTestUtil.java @@ -16,17 +16,22 @@ package com.android.adservices.ui.util; +import static com.google.common.truth.Truth.assertThat; + import android.app.Instrumentation; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.graphics.Point; import android.net.Uri; import androidx.test.core.app.ApplicationProvider; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.uiautomator.By; +import androidx.test.uiautomator.Direction; import androidx.test.uiautomator.UiDevice; import androidx.test.uiautomator.UiObject; +import androidx.test.uiautomator.UiObject2; import androidx.test.uiautomator.UiObjectNotFoundException; import androidx.test.uiautomator.UiScrollable; import androidx.test.uiautomator.UiSelector; @@ -40,12 +45,16 @@ import java.io.File; import java.text.SimpleDateFormat; import java.time.Instant; import java.util.Date; +import java.util.List; import java.util.Locale; +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; /** Util class for APK tests. */ public class ApkTestUtil { private static final String PRIVACY_SANDBOX_UI = "android.adservices.ui.SETTINGS"; + private static final String ANDROID_WIDGET_SCROLLVIEW = "android.widget.ScrollView"; private static final int WINDOW_LAUNCH_TIMEOUT = 1000; private static final int SCROLL_TIMEOUT = 500; public static final int PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT_MS = 1000; @@ -80,6 +89,23 @@ public class ApkTestUtil { return consentSwitch; } + public static UiObject2 getConsentSwitch2(UiDevice device) { + UiObject2 consentSwitch = + device.wait( + Until.findObject(By.clazz("android.widget.Switch")), + PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT_MS); + // Swipe the screen by the width of the toggle so it's not blocked by the nav bar on AOSP + // devices. + device.swipe( + consentSwitch.getVisibleBounds().centerX(), + 500, + consentSwitch.getVisibleBounds().centerX(), + 0, + 100); + + return consentSwitch; + } + /** Returns the UiObject corresponding to a resource ID. */ public static UiObject getElement(UiDevice device, int resId) { UiObject obj = device.findObject(new UiSelector().text(getString(resId))); @@ -95,6 +121,11 @@ public class ApkTestUtil { return ApplicationProvider.getApplicationContext().getResources().getString(resourceId); } + /** Returns the string corresponding to a resource ID. */ + public static String getString(Context context, int resourceId) { + return context.getResources().getString(resourceId); + } + /** Click the top left of the UiObject corresponding to a resource ID after scrolling. */ public static void scrollToAndClick(UiDevice device, int resId) throws UiObjectNotFoundException { @@ -103,18 +134,29 @@ public class ApkTestUtil { obj.clickTopLeft(); } + public static void scrollToAndClick(Context context, UiDevice device, int resId) + throws InterruptedException { + UiObject2 obj = scrollTo(context, device, resId); + clickTopLeft(obj); + } + public static void click(UiDevice device, int resId) throws UiObjectNotFoundException { UiObject obj = device.findObject(new UiSelector().text(getString(resId))); // objects may be partially hidden by the status bar and nav bars. obj.clickTopLeft(); } - public static void gentleSwipe(UiDevice device) throws UiObjectNotFoundException { - UiScrollable scrollView = - new UiScrollable( - new UiSelector().scrollable(true).className("android.widget.ScrollView")); + public static void clickTopLeft(UiObject2 obj) { + assertThat(obj).isNotNull(); + obj.click(new Point(obj.getVisibleBounds().top, obj.getVisibleBounds().left)); + } - scrollView.scrollForward(100); + public static void gentleSwipe(UiDevice device) { + device.waitForWindowUpdate(null, WINDOW_LAUNCH_TIMEOUT); + UiObject2 scrollView = device.wait( + Until.findObject(By.scrollable(true).clazz(ANDROID_WIDGET_SCROLLVIEW)), + PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT_MS); + scrollView.scroll(Direction.DOWN, /* percent */ 0.25F); } /** Returns the UiObject corresponding to a resource ID after scrolling. */ @@ -128,11 +170,26 @@ public class ApkTestUtil { try { Thread.sleep(SCROLL_TIMEOUT); } catch (InterruptedException e) { - LogUtil.e("InterruptedException:", e.getMessage()); + LogUtil.e("InterruptedException: %s", e.getMessage()); } return obj; } + public static UiObject2 scrollTo(Context context, UiDevice device, int resId) { + device.waitForWindowUpdate(null, WINDOW_LAUNCH_TIMEOUT); + UiObject2 scrollView = device.wait( + Until.findObject(By.scrollable(true).clazz(ANDROID_WIDGET_SCROLLVIEW)), + PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT_MS); + String targetStr = getString(context, resId); + scrollView.scrollUntil( + Direction.DOWN, + Until.findObject(By.text(Pattern.compile(targetStr, Pattern.CASE_INSENSITIVE)))); + scrollView.scrollUntil( + Direction.UP, + Until.findObject(By.text(Pattern.compile(targetStr, Pattern.CASE_INSENSITIVE)))); + return getElement(context, device, resId); + } + /** Returns the string corresponding to a resource ID and index. */ public static UiObject getElement(UiDevice device, int resId, int index) { UiObject obj = device.findObject(new UiSelector().text(getString(resId)).instance(index)); @@ -145,6 +202,34 @@ public class ApkTestUtil { } /** Returns the UiObject corresponding to a resource ID. */ + public static UiObject2 getElement(Context context, UiDevice device, int resId) { + String targetStr = getString(context, resId); + UiObject2 obj = + device.wait( + Until.findObject(By.text(targetStr)), + PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT_MS); + if (obj == null) { + obj = device.findObject(By.text(targetStr.toUpperCase(Locale.getDefault()))); + } + return obj; + } + + /** Returns the string corresponding to a resource ID and index. */ + public static UiObject2 getElement(Context context, UiDevice device, int resId, int index) { + String targetStr = getString(context, resId); + List<UiObject2> objs = + device.wait( + Until.findObjects(By.text(targetStr)), + PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT_MS); + if (objs == null) { + return device.wait( + Until.findObjects(By.text(targetStr.toUpperCase(Locale.getDefault()))), + PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT_MS).get(index); + } + return objs.get(index); + } + + /** Returns the UiObject corresponding to a resource ID. */ public static UiObject getPageElement(UiDevice device, int resId) { return device.findObject(new UiSelector().text(getString(resId))); } diff --git a/adservices/apk/unittest/src/com/android/adservices/topics/TopicsServiceTest.java b/adservices/apk/unittest/src/com/android/adservices/topics/TopicsServiceTest.java index e1f4abb44..e3c0b1c50 100644 --- a/adservices/apk/unittest/src/com/android/adservices/topics/TopicsServiceTest.java +++ b/adservices/apk/unittest/src/com/android/adservices/topics/TopicsServiceTest.java @@ -30,6 +30,7 @@ import android.os.IBinder; import androidx.test.core.app.ApplicationProvider; +import com.android.adservices.common.AdServicesExtendedMockitoTestCase; import com.android.adservices.data.enrollment.EnrollmentDao; import com.android.adservices.download.MddJobService; import com.android.adservices.service.Flags; @@ -45,56 +46,40 @@ import com.android.adservices.service.stats.AdServicesLoggerImpl; import com.android.adservices.service.topics.EpochJobService; import com.android.adservices.service.topics.TopicsWorker; import com.android.dx.mockito.inline.extended.ExtendedMockito; +import com.android.modules.utils.testing.ExtendedMockitoRule.SpyStatic; -import org.junit.Before; import org.junit.Test; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.MockitoSession; import java.util.function.Supplier; /** Unit test for {@link com.android.adservices.topics.TopicsService}. */ -public class TopicsServiceTest { - @SuppressWarnings("unused") - private static final String TAG = "TopicsServiceTest"; - - @Mock TopicsWorker mMockTopicsWorker; - @Mock ConsentManager mMockConsentManager; - @Mock EnrollmentDao mMockEnrollmentDao; - @Mock AppImportanceFilter mMockAppImportanceFilter; - @Mock Flags mMockFlags; - @Mock AdServicesApiConsent mMockAdServicesApiConsent; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - } +@SpyStatic(FlagsFactory.class) +@SpyStatic(TopicsWorker.class) +@SpyStatic(ConsentManager.class) +@SpyStatic(AdServicesLoggerImpl.class) +@SpyStatic(MaintenanceJobService.class) +@SpyStatic(EncryptionKeyJobService.class) +@SpyStatic(EpochJobService.class) +@SpyStatic(MddJobService.class) +@SpyStatic(EnrollmentDao.class) +@SpyStatic(AppImportanceFilter.class) +@SpyStatic(PackageChangedReceiver.class) +public final class TopicsServiceTest extends AdServicesExtendedMockitoTestCase { + + @Mock private TopicsWorker mMockTopicsWorker; + @Mock private ConsentManager mMockConsentManager; + @Mock private EnrollmentDao mMockEnrollmentDao; + @Mock private AppImportanceFilter mMockAppImportanceFilter; + @Mock private Flags mMockFlags; + @Mock private AdServicesApiConsent mMockAdServicesApiConsent; @Test public void testBindableTopicsService_killswitchOff() { - // Start a mockitoSession to mock static method - MockitoSession session = - ExtendedMockito.mockitoSession() - .spyStatic(FlagsFactory.class) - .spyStatic(TopicsWorker.class) - .spyStatic(ConsentManager.class) - .spyStatic(AdServicesLoggerImpl.class) - .spyStatic(MaintenanceJobService.class) - .spyStatic(EncryptionKeyJobService.class) - .spyStatic(EpochJobService.class) - .spyStatic(MddJobService.class) - .spyStatic(EnrollmentDao.class) - .spyStatic(AppImportanceFilter.class) - .spyStatic(PackageChangedReceiver.class) - .startMocking(); - - try { // Killswitch is off. doReturn(false).when(mMockFlags).getTopicsKillSwitch(); - // Mock static method FlagsFactory.getFlags() to return Mock Flags. - ExtendedMockito.doReturn(mMockFlags).when(FlagsFactory::getFlags); + extendedMockito.mockGetFlags(mMockFlags); ExtendedMockito.doReturn(mMockTopicsWorker) .when(() -> TopicsWorker.getInstance(any(Context.class))); @@ -135,31 +120,19 @@ public class TopicsServiceTest { spyTopicsService.onCreate(); IBinder binder = spyTopicsService.onBind(getIntentForTopicsService()); assertNotNull(binder); - } finally { - session.finishMocking(); - } } @Test public void testBindableTopicsService_killswitchOn() { - // Start a mockitoSession to mock static method - MockitoSession session = - ExtendedMockito.mockitoSession().spyStatic(FlagsFactory.class).startMocking(); - - try { // Killswitch is on. doReturn(true).when(mMockFlags).getTopicsKillSwitch(); - // Mock static method FlagsFactory.getFlags() to return Mock Flags. - ExtendedMockito.doReturn(mMockFlags).when(FlagsFactory::getFlags); + extendedMockito.mockGetFlags(mMockFlags); TopicsService topicsService = new TopicsService(); topicsService.onCreate(); IBinder binder = topicsService.onBind(getIntentForTopicsService()); assertNull(binder); - } finally { - session.finishMocking(); - } } /** @@ -168,28 +141,10 @@ public class TopicsServiceTest { */ @Test public void testBindableTopicsService_killswitchOffGaUxFeatureFlagOn() { - // Start a mockitoSession to mock static method - MockitoSession session = - ExtendedMockito.mockitoSession() - .spyStatic(FlagsFactory.class) - .spyStatic(TopicsWorker.class) - .spyStatic(ConsentManager.class) - .spyStatic(AdServicesLoggerImpl.class) - .spyStatic(MaintenanceJobService.class) - .spyStatic(EncryptionKeyJobService.class) - .spyStatic(EpochJobService.class) - .spyStatic(MddJobService.class) - .spyStatic(EnrollmentDao.class) - .spyStatic(AppImportanceFilter.class) - .spyStatic(PackageChangedReceiver.class) - .startMocking(); - - try { // Killswitch is off. doReturn(false).when(mMockFlags).getTopicsKillSwitch(); - // Mock static method FlagsFactory.getFlags() to return Mock Flags. - ExtendedMockito.doReturn(mMockFlags).when(FlagsFactory::getFlags); + extendedMockito.mockGetFlags(mMockFlags); ExtendedMockito.doReturn(mMockTopicsWorker) .when(() -> TopicsWorker.getInstance(any(Context.class))); @@ -231,9 +186,6 @@ public class TopicsServiceTest { IBinder binder = spyTopicsService.onBind(getIntentForTopicsService()); assertNotNull(binder); verifyMethodExecutionOnUserConsentGiven(); - } finally { - session.finishMocking(); - } } private Intent getIntentForTopicsService() { diff --git a/adservices/clients/java/android/adservices/clients/measurement/MeasurementClient.java b/adservices/clients/java/android/adservices/clients/measurement/MeasurementClient.java index 639ffd38d..a70270de6 100644 --- a/adservices/clients/java/android/adservices/clients/measurement/MeasurementClient.java +++ b/adservices/clients/java/android/adservices/clients/measurement/MeasurementClient.java @@ -16,7 +16,6 @@ package android.adservices.clients.measurement; -import android.adservices.common.AdServicesOutcomeReceiver; import android.adservices.measurement.DeletionRequest; import android.adservices.measurement.MeasurementManager; import android.adservices.measurement.WebSourceRegistrationRequest; @@ -58,20 +57,82 @@ public class MeasurementClient { @NonNull public ListenableFuture<Void> registerSource( @NonNull Uri attributionSource, @Nullable InputEvent inputEvent) { + Objects.requireNonNull(attributionSource); + + return Build.VERSION.SDK_INT > Build.VERSION_CODES.R + ? registerSourceForSPlus(attributionSource, inputEvent) + : registerSourceForR(attributionSource, inputEvent); + } + + /** + * Invokes the {@code registerTrigger} method of {@link MeasurementManager}, and returns a Void + * future. + */ + @NonNull + public ListenableFuture<Void> registerTrigger(@NonNull Uri trigger) { + Objects.requireNonNull(trigger); + + return Build.VERSION.SDK_INT > Build.VERSION_CODES.R + ? registerTriggerForSPlus(trigger) + : registerTriggerForR(trigger); + } + + /** + * Invokes the {@code registerWebSource} method of {@link MeasurementManager}, and returns a + * Void future. + */ + @NonNull + public ListenableFuture<Void> registerWebSource(@NonNull WebSourceRegistrationRequest request) { + Objects.requireNonNull(request); + + return Build.VERSION.SDK_INT > Build.VERSION_CODES.R + ? registerWebSourceForSPlus(request) + : registerWebSourceForR(request); + } + + /** + * Invokes the {@code registerWebTrigger} method of {@link MeasurementManager}, and returns a + * Void future. + */ + @NonNull + public ListenableFuture<Void> registerWebTrigger( + @NonNull WebTriggerRegistrationRequest request) { + Objects.requireNonNull(request); + + return Build.VERSION.SDK_INT > Build.VERSION_CODES.R + ? registerWebTriggerForSPlus(request) + : registerWebTriggerForR(request); + } + + /** + * Invokes the {@code registerWebTrigger} method of {@link MeasurementManager} with a null + * callback, and returns a Void future if successful, or an {@link Exception} if unsuccessful. + */ + @NonNull + public ListenableFuture<Void> deleteRegistrations(@NonNull DeletionRequest request) { + Objects.requireNonNull(request); + + return Build.VERSION.SDK_INT > Build.VERSION_CODES.R + ? deleteRegistrationsForSPlus(request) + : deleteRegistrationsForR(request); + } + + private ListenableFuture<Void> registerSourceForSPlus( + Uri attributionSource, InputEvent inputEvent) { return CallbackToFutureAdapter.getFuture( completer -> { mMeasurementManager.registerSource( attributionSource, inputEvent, mExecutor, - new AdServicesOutcomeReceiver<>() { + new android.os.OutcomeReceiver<>() { @Override - public void onResult(@NonNull Object ignoredResult) { + public void onResult(Object ignoredResult) { completer.set(null); } @Override - public void onError(@NonNull Exception error) { + public void onError(Exception error) { completer.setException(error); } }); @@ -81,18 +142,61 @@ public class MeasurementClient { }); } - /** - * Invokes the {@code registerTrigger} method of {@link MeasurementManager}, and returns a Void - * future. - */ - @NonNull - public ListenableFuture<Void> registerTrigger(@NonNull Uri trigger) { + private ListenableFuture<Void> registerSourceForR( + Uri attributionSource, InputEvent inputEvent) { + return CallbackToFutureAdapter.getFuture( + completer -> { + mMeasurementManager.registerSource( + attributionSource, + inputEvent, + mExecutor, + new android.adservices.common.AdServicesOutcomeReceiver<>() { + @Override + public void onResult(Object ignoredResult) { + completer.set(null); + } + + @Override + public void onError(Exception error) { + completer.setException(error); + } + }); + // This value is used only for debug purposes: it will be used in toString() + // of returned future or error cases. + return "registerSource"; + }); + } + + private ListenableFuture<Void> registerTriggerForSPlus(Uri trigger) { + return CallbackToFutureAdapter.getFuture( + completer -> { + mMeasurementManager.registerTrigger( + trigger, + mExecutor, + new android.os.OutcomeReceiver<>() { + @Override + public void onResult(Object ignoredResult) { + completer.set(null); + } + + @Override + public void onError(Exception error) { + completer.setException(error); + } + }); + // This value is used only for debug purposes: it will be used in toString() + // of returned future or error cases. + return "registerTrigger"; + }); + } + + private ListenableFuture<Void> registerTriggerForR(Uri trigger) { return CallbackToFutureAdapter.getFuture( completer -> { mMeasurementManager.registerTrigger( trigger, mExecutor, - new AdServicesOutcomeReceiver<>() { + new android.adservices.common.AdServicesOutcomeReceiver<>() { @Override public void onResult(@NonNull Object ignoredResult) { completer.set(null); @@ -108,25 +212,21 @@ public class MeasurementClient { return "registerTrigger"; }); } - /** - * Invokes the {@code registerWebSource} method of {@link MeasurementManager}, and returns a - * Void future. - */ - @NonNull - public ListenableFuture<Void> registerWebSource(@NonNull WebSourceRegistrationRequest request) { + + private ListenableFuture<Void> registerWebSourceForSPlus(WebSourceRegistrationRequest request) { return CallbackToFutureAdapter.getFuture( completer -> { mMeasurementManager.registerWebSource( request, mExecutor, - new AdServicesOutcomeReceiver<>() { + new android.os.OutcomeReceiver<>() { @Override - public void onResult(@NonNull Object ignoredResult) { + public void onResult(Object ignoredResult) { completer.set(null); } @Override - public void onError(@NonNull Exception error) { + public void onError(Exception error) { completer.setException(error); } }); @@ -136,26 +236,44 @@ public class MeasurementClient { }); } - /** - * Invokes the {@code registerWebTrigger} method of {@link MeasurementManager}, and returns a - * Void future. - */ - @NonNull - public ListenableFuture<Void> registerWebTrigger( - @NonNull WebTriggerRegistrationRequest request) { + private ListenableFuture<Void> registerWebSourceForR(WebSourceRegistrationRequest request) { + return CallbackToFutureAdapter.getFuture( + completer -> { + mMeasurementManager.registerWebSource( + request, + mExecutor, + new android.adservices.common.AdServicesOutcomeReceiver<>() { + @Override + public void onResult(Object ignoredResult) { + completer.set(null); + } + + @Override + public void onError(Exception error) { + completer.setException(error); + } + }); + // This value is used only for debug purposes: it will be used in toString() + // of returned future or error cases. + return "registerWebSource"; + }); + } + + private ListenableFuture<Void> registerWebTriggerForSPlus( + WebTriggerRegistrationRequest request) { return CallbackToFutureAdapter.getFuture( completer -> { mMeasurementManager.registerWebTrigger( request, mExecutor, - new AdServicesOutcomeReceiver<>() { + new android.os.OutcomeReceiver<>() { @Override - public void onResult(@NonNull Object ignoredResult) { + public void onResult(Object ignoredResult) { completer.set(null); } @Override - public void onError(@NonNull Exception error) { + public void onError(Exception error) { completer.setException(error); } }); @@ -165,25 +283,66 @@ public class MeasurementClient { }); } - /** - * Invokes the {@code registerWebTrigger} method of {@link MeasurementManager} with a null - * callback, and returns a Void future if successful, or an {@link Exception} if unsuccessful. - */ - @NonNull - public ListenableFuture<Void> deleteRegistrations(@NonNull DeletionRequest request) { + private ListenableFuture<Void> registerWebTriggerForR(WebTriggerRegistrationRequest request) { + return CallbackToFutureAdapter.getFuture( + completer -> { + mMeasurementManager.registerWebTrigger( + request, + mExecutor, + new android.adservices.common.AdServicesOutcomeReceiver<>() { + @Override + public void onResult(Object ignoredResult) { + completer.set(null); + } + + @Override + public void onError(Exception error) { + completer.setException(error); + } + }); + // This value is used only for debug purposes: it will be used in toString() + // of returned future or error cases. + return "registerWebTrigger"; + }); + } + + private ListenableFuture<Void> deleteRegistrationsForSPlus(DeletionRequest request) { return CallbackToFutureAdapter.getFuture( completer -> { mMeasurementManager.deleteRegistrations( request, mExecutor, - new AdServicesOutcomeReceiver<>() { + new android.os.OutcomeReceiver<>() { @Override - public void onResult(@NonNull Object ignoredResult) { + public void onResult(Object ignoredResult) { completer.set(null); } @Override - public void onError(@NonNull Exception error) { + public void onError(Exception error) { + completer.setException(error); + } + }); + // This value is used only for debug purposes: it will be used in toString() + // of returned future or error cases. + return "deleteRegistrations"; + }); + } + + private ListenableFuture<Void> deleteRegistrationsForR(DeletionRequest request) { + return CallbackToFutureAdapter.getFuture( + completer -> { + mMeasurementManager.deleteRegistrations( + request, + mExecutor, + new android.adservices.common.AdServicesOutcomeReceiver<>() { + @Override + public void onResult(Object ignoredResult) { + completer.set(null); + } + + @Override + public void onError(Exception error) { completer.setException(error); } }); diff --git a/adservices/framework/Android.bp b/adservices/framework/Android.bp index 4b1b7859a..e854efb64 100644 --- a/adservices/framework/Android.bp +++ b/adservices/framework/Android.bp @@ -37,6 +37,7 @@ java_sdk_library { "modules-utils-preconditions", "framework-sdksandbox.impl", "androidx.annotation_annotation", + "error_prone_annotations", ], sdk_version: "module_current", min_sdk_version: "30", @@ -59,6 +60,7 @@ java_library { "modules-utils-preconditions", "framework-sdksandbox.impl", "androidx.annotation_annotation", + "error_prone_annotations", ], min_sdk_version: "30", defaults: ["framework-module-defaults"], diff --git a/adservices/framework/java/android/adservices/adid/AdIdCompatibleManager.java b/adservices/framework/java/android/adservices/adid/AdIdCompatibleManager.java index 4a11d7600..d6aed3e05 100644 --- a/adservices/framework/java/android/adservices/adid/AdIdCompatibleManager.java +++ b/adservices/framework/java/android/adservices/adid/AdIdCompatibleManager.java @@ -16,6 +16,7 @@ package android.adservices.adid; import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID; +import static android.adservices.common.AdServicesStatusUtils.ILLEGAL_STATE_EXCEPTION_ERROR_MESSAGE; import android.adservices.common.AdServicesOutcomeReceiver; import android.adservices.common.AdServicesStatusUtils; @@ -87,6 +88,11 @@ public class AdIdCompatibleManager { IAdIdService service = null; try { service = mServiceBinder.getService(); + + // Throw ISE and set it to the callback when service is not available + if (service == null) { + throw new IllegalStateException(ILLEGAL_STATE_EXCEPTION_ERROR_MESSAGE); + } } catch (RuntimeException e) { LogUtil.e(e, "Failed binding to AdId service"); executor.execute(() -> callback.onError(e)); diff --git a/adservices/framework/java/android/adservices/adselection/SignedContextualAds.java b/adservices/framework/java/android/adservices/adselection/SignedContextualAds.java index c52ebd4bb..7a86553f9 100644 --- a/adservices/framework/java/android/adservices/adselection/SignedContextualAds.java +++ b/adservices/framework/java/android/adservices/adselection/SignedContextualAds.java @@ -28,7 +28,7 @@ import java.util.List; import java.util.Objects; /** - * Contains a buyer supplied {@link ContextualAds} bundle and its signature. + * Contains a buyer supplied {@link AdWithBid} bundle and its signature. * * <p>Instances of this class are created by SDKs to be injected as part of {@link * AdSelectionConfig} and passed to {@link AdSelectionManager#selectAds} diff --git a/adservices/framework/java/android/adservices/appsetid/AppSetIdManager.java b/adservices/framework/java/android/adservices/appsetid/AppSetIdManager.java index b305dbb18..c85184dbb 100644 --- a/adservices/framework/java/android/adservices/appsetid/AppSetIdManager.java +++ b/adservices/framework/java/android/adservices/appsetid/AppSetIdManager.java @@ -15,6 +15,8 @@ */ package android.adservices.appsetid; +import static android.adservices.common.AdServicesStatusUtils.ILLEGAL_STATE_EXCEPTION_ERROR_MESSAGE; + import android.adservices.common.AdServicesStatusUtils; import android.adservices.common.CallerMetadata; import android.adservices.common.SandboxedSdkContextUtils; @@ -106,6 +108,11 @@ public class AppSetIdManager { IAppSetIdService service = null; try { service = mServiceBinder.getService(); + + // Throw ISE and set it to the callback when service is not available + if (service == null) { + throw new IllegalStateException(ILLEGAL_STATE_EXCEPTION_ERROR_MESSAGE); + } } catch (RuntimeException e) { LogUtil.e(e, "Failed binding to AppSetId service"); executor.execute(() -> callback.onError(e)); diff --git a/adservices/framework/java/android/adservices/common/AdServicesPermissions.java b/adservices/framework/java/android/adservices/common/AdServicesPermissions.java index 2a94c711b..140359b1d 100644 --- a/adservices/framework/java/android/adservices/common/AdServicesPermissions.java +++ b/adservices/framework/java/android/adservices/common/AdServicesPermissions.java @@ -36,14 +36,6 @@ public class AdServicesPermissions { public static final String ACCESS_ADSERVICES_CUSTOM_AUDIENCE = "android.permission.ACCESS_ADSERVICES_CUSTOM_AUDIENCE"; - /** - * This permission needs to be declared by the caller of Protected Signals APIs. - * - * @hide - */ - public static final String ACCESS_ADSERVICES_PROTECTED_SIGNALS = - "android.permission.ACCESS_ADSERVICES_PROTECTED_SIGNALS"; - /** This permission needs to be declared by the caller of Advertising ID APIs. */ public static final String ACCESS_ADSERVICES_AD_ID = "android.permission.ACCESS_ADSERVICES_AD_ID"; diff --git a/adservices/framework/java/android/adservices/shell/IShellCommand.aidl b/adservices/framework/java/android/adservices/shell/IShellCommand.aidl new file mode 100644 index 000000000..2b6bd81cf --- /dev/null +++ b/adservices/framework/java/android/adservices/shell/IShellCommand.aidl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 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 android.adservices.shell; + +import android.adservices.shell.IShellCommandCallback; +import android.adservices.shell.ShellCommandParam; +import android.os.ParcelFileDescriptor; + +/** + * Shell Service. + * + * {@hide} + */ +oneway interface IShellCommand { + /** + * Runs the shell command and returns the result through callback. + */ + void runShellCommand(in ShellCommandParam request, in IShellCommandCallback callback); +}
\ No newline at end of file diff --git a/adservices/framework/java/android/adservices/shell/IShellCommandCallback.aidl b/adservices/framework/java/android/adservices/shell/IShellCommandCallback.aidl new file mode 100644 index 000000000..7a86fbb0c --- /dev/null +++ b/adservices/framework/java/android/adservices/shell/IShellCommandCallback.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2023 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 android.adservices.shell; + +import android.adservices.shell.ShellCommandResult; + +/** + * Callback from the runShellCommand request. + * {@hide} + */ +oneway interface IShellCommandCallback { + void onResult(in ShellCommandResult result); +}
\ No newline at end of file diff --git a/adservices/framework/java/android/adservices/shell/ShellCommandParam.aidl b/adservices/framework/java/android/adservices/shell/ShellCommandParam.aidl new file mode 100644 index 000000000..440947220 --- /dev/null +++ b/adservices/framework/java/android/adservices/shell/ShellCommandParam.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2023 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 android.adservices.shell; + +/** {@hide} */ +parcelable ShellCommandParam;
\ No newline at end of file diff --git a/adservices/framework/java/android/adservices/shell/ShellCommandParam.java b/adservices/framework/java/android/adservices/shell/ShellCommandParam.java new file mode 100644 index 000000000..18b628d8e --- /dev/null +++ b/adservices/framework/java/android/adservices/shell/ShellCommandParam.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2023 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 android.adservices.shell; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Represents request which contains command and args as an input to runShellCommand API. + * + * @hide + */ +public class ShellCommandParam implements Parcelable { + + /* Array containing command name with all the args */ + private final String[] mCommandArgs; + + public ShellCommandParam(String... commandArgs) { + mCommandArgs = Objects.requireNonNull(commandArgs); + } + + private ShellCommandParam(Parcel in) { + this(in.createStringArray()); + } + + public static final Creator<ShellCommandParam> CREATOR = + new Parcelable.Creator<>() { + @Override + public ShellCommandParam createFromParcel(Parcel in) { + return new ShellCommandParam(in); + } + + @Override + public ShellCommandParam[] newArray(int size) { + return new ShellCommandParam[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeStringArray(mCommandArgs); + } + + /** Get the command name with all the args as a list. */ + public String[] getCommandArgs() { + return mCommandArgs; + } +} diff --git a/adservices/framework/java/android/adservices/shell/ShellCommandResult.aidl b/adservices/framework/java/android/adservices/shell/ShellCommandResult.aidl new file mode 100644 index 000000000..821fcbc38 --- /dev/null +++ b/adservices/framework/java/android/adservices/shell/ShellCommandResult.aidl @@ -0,0 +1,20 @@ + +/* + * Copyright (C) 2023 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 android.adservices.shell; + +/** {@hide} */ +parcelable ShellCommandResult;
\ No newline at end of file diff --git a/adservices/framework/java/android/adservices/shell/ShellCommandResult.java b/adservices/framework/java/android/adservices/shell/ShellCommandResult.java new file mode 100644 index 000000000..442c67e58 --- /dev/null +++ b/adservices/framework/java/android/adservices/shell/ShellCommandResult.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2023 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 android.adservices.shell; + +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Represents the response from the runShellCommand API. + * + * @hide + */ +public class ShellCommandResult implements Parcelable { + + private static final int RESULT_OK = 0; + + private final int mResultCode; + @Nullable private final String mOut; + @Nullable private final String mErr; + + private ShellCommandResult(int resultCode, @Nullable String out, @Nullable String err) { + mResultCode = resultCode; + mOut = out; + mErr = err; + } + + private ShellCommandResult(Parcel in) { + this(in.readInt(), in.readString(), in.readString()); + } + + private ShellCommandResult(Builder builder) { + this(builder.mResultCode, builder.mOut, builder.mErr); + } + + public static final Creator<ShellCommandResult> CREATOR = + new Parcelable.Creator<>() { + @Override + public ShellCommandResult createFromParcel(Parcel in) { + Objects.requireNonNull(in); + return new ShellCommandResult(in); + } + + @Override + public ShellCommandResult[] newArray(int size) { + return new ShellCommandResult[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + Objects.requireNonNull(dest); + dest.writeInt(mResultCode); + dest.writeString(mOut); + dest.writeString(mErr); + } + + /** Returns the command status. */ + public int getResultCode() { + return mResultCode; + } + + /** Returns {@code true} if {@link #getResultCode} is greater than equal to 0. */ + public boolean isSuccess() { + return getResultCode() >= 0; + } + + /** Returns the output of the shell command result. */ + @Nullable + public String getOut() { + return mOut; + } + + /** Returns the error message associated with this response. */ + @Nullable + public String getErr() { + return mErr; + } + + /** + * Builder for {@link ShellCommandResult}. + * + * @hide + */ + public static final class Builder { + private int mResultCode = RESULT_OK; + @Nullable private String mOut; + @Nullable private String mErr; + + public Builder() {} + + /** Sets the Status Code. */ + public Builder setResultCode(int resultCode) { + mResultCode = resultCode; + return this; + } + + /** Sets the shell command output in case of success. */ + public Builder setOut(@Nullable String out) { + mOut = out; + return this; + } + + /** Sets the error message in case of command failure. */ + public Builder setErr(@Nullable String err) { + mErr = err; + return this; + } + + /** Builds a {@link ShellCommandResult} object. */ + public ShellCommandResult build() { + return new ShellCommandResult(this); + } + } +} diff --git a/adservices/framework/java/android/adservices/signals/ProtectedSignalsManager.java b/adservices/framework/java/android/adservices/signals/ProtectedSignalsManager.java index c0b54d956..ffbb084e4 100644 --- a/adservices/framework/java/android/adservices/signals/ProtectedSignalsManager.java +++ b/adservices/framework/java/android/adservices/signals/ProtectedSignalsManager.java @@ -16,7 +16,7 @@ package android.adservices.signals; -import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_PROTECTED_SIGNALS; +import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE; import android.adservices.common.AdServicesStatusUtils; import android.adservices.common.FledgeErrorResponse; @@ -181,7 +181,7 @@ public class ProtectedSignalsManager { * <p>This call fails with an {@link IllegalStateException} if an internal service error is * encountered. */ - @RequiresPermission(ACCESS_ADSERVICES_PROTECTED_SIGNALS) + @RequiresPermission(ACCESS_ADSERVICES_CUSTOM_AUDIENCE) public void updateSignals( @NonNull UpdateSignalsRequest updateSignalsRequest, @NonNull @CallbackExecutor Executor executor, diff --git a/adservices/framework/java/com/android/adservices/AdServicesCommon.java b/adservices/framework/java/com/android/adservices/AdServicesCommon.java index 5fae08509..18b324691 100644 --- a/adservices/framework/java/com/android/adservices/AdServicesCommon.java +++ b/adservices/framework/java/com/android/adservices/AdServicesCommon.java @@ -26,6 +26,7 @@ import java.util.List; // TODO(b/295321663): need to split constants into AdServicesCommonConstants so they can be used by // host-side test artifacts. + /** * Common constants for AdServices * @@ -82,6 +83,10 @@ public class AdServicesCommon { public static final String ACTION_AD_SERVICES_COBALT_UPLOAD_SERVICE = AdServicesCobaltUploadService.SERVICE_INTERFACE; + /** Intent action to discover the Shell Command service in the APK. */ + public static final String ACTION_SHELL_COMMAND_SERVICE = + "android.adservices.SHELL_COMMAND_SERVICE"; + // Used to differentiate between AdServices APK package name and AdExtServices APK package name. // The AdExtServices APK package name suffix is android.ext.services. public static final String ADSERVICES_APK_PACKAGE_NAME_SUFFIX = "android.adservices.api"; diff --git a/adservices/framework/java/com/android/adservices/AndroidServiceBinder.java b/adservices/framework/java/com/android/adservices/AndroidServiceBinder.java index 280be9a2c..b8ce7dd2a 100644 --- a/adservices/framework/java/com/android/adservices/AndroidServiceBinder.java +++ b/adservices/framework/java/com/android/adservices/AndroidServiceBinder.java @@ -27,6 +27,7 @@ import static com.android.adservices.AdServicesCommon.ACTION_APPSETID_PROVIDER_S import static com.android.adservices.AdServicesCommon.ACTION_APPSETID_SERVICE; import static com.android.adservices.AdServicesCommon.ACTION_CUSTOM_AUDIENCE_SERVICE; import static com.android.adservices.AdServicesCommon.ACTION_MEASUREMENT_SERVICE; +import static com.android.adservices.AdServicesCommon.ACTION_SHELL_COMMAND_SERVICE; import static com.android.adservices.AdServicesCommon.ACTION_TOPICS_SERVICE; import static com.android.adservices.AdServicesCommon.SYSTEM_PROPERTY_FOR_DEBUGGING_FEATURE_RAM_LOW; @@ -221,7 +222,8 @@ class AndroidServiceBinder<T> extends ServiceBinder<T> { && !mServiceIntentAction.equals(ACTION_APPSETID_PROVIDER_SERVICE) && !mServiceIntentAction.equals(ACTION_AD_SERVICES_COBALT_UPLOAD_SERVICE) && !mServiceIntentAction.equals(ACTION_AD_SERVICES_COMMON_SERVICE) - && !mServiceIntentAction.equals(ACTION_AD_EXT_DATA_STORAGE_SERVICE)) { + && !mServiceIntentAction.equals(ACTION_AD_EXT_DATA_STORAGE_SERVICE) + && !mServiceIntentAction.equals(ACTION_SHELL_COMMAND_SERVICE)) { LogUtil.e("Bad service intent action: " + mServiceIntentAction); return null; } diff --git a/adservices/framework/java/com/android/adservices/LogUtil.java b/adservices/framework/java/com/android/adservices/LogUtil.java index 6c6121be7..968b3e811 100644 --- a/adservices/framework/java/com/android/adservices/LogUtil.java +++ b/adservices/framework/java/com/android/adservices/LogUtil.java @@ -18,6 +18,9 @@ package com.android.adservices; import android.util.Log; +import com.google.errorprone.annotations.FormatMethod; +import com.google.errorprone.annotations.FormatString; + import java.util.Locale; /** @@ -38,7 +41,8 @@ public class LogUtil { } /** Log the message as VERBOSE. Return The number of bytes written. */ - public static int v(String format, Object... params) { + @FormatMethod + public static int v(@FormatString String format, Object... params) { if (Log.isLoggable(TAG, Log.VERBOSE)) { String msg = format(format, params); return Log.v(TAG, msg); @@ -55,7 +59,8 @@ public class LogUtil { } /** Log the message as DEBUG. Return The number of bytes written. */ - public static int d(String format, Object... params) { + @FormatMethod + public static int d(@FormatString String format, Object... params) { if (Log.isLoggable(TAG, Log.DEBUG)) { String msg = format(format, params); return Log.d(TAG, msg); @@ -64,7 +69,8 @@ public class LogUtil { } /** Log the message as DEBUG. Return The number of bytes written. */ - public static int d(Throwable tr, String format, Object... params) { + @FormatMethod + public static int d(Throwable tr, @FormatString String format, Object... params) { if (Log.isLoggable(TAG, Log.DEBUG)) { String msg = format(format, params); return Log.d(TAG, msg, tr); @@ -81,7 +87,8 @@ public class LogUtil { } /** Log the message as INFO. Return The number of bytes written */ - public static int i(String format, Object... params) { + @FormatMethod + public static int i(@FormatString String format, Object... params) { if (Log.isLoggable(TAG, Log.INFO)) { String msg = format(format, params); return Log.i(TAG, msg); @@ -98,7 +105,8 @@ public class LogUtil { } /** Log the message as WARNING. Return The number of bytes written */ - public static int w(String format, Object... params) { + @FormatMethod + public static int w(@FormatString String format, Object... params) { if (Log.isLoggable(TAG, Log.WARN)) { String msg = format(format, params); return Log.w(TAG, msg); @@ -115,7 +123,8 @@ public class LogUtil { } /** Log the message as ERROR. Return The number of bytes written */ - public static int e(String format, Object... params) { + @FormatMethod + public static int e(@FormatString String format, Object... params) { if (Log.isLoggable(TAG, Log.ERROR)) { String msg = format(format, params); return Log.e(TAG, msg); @@ -138,12 +147,14 @@ public class LogUtil { } /** Log the message as ERROR. Return The number of bytes written */ - public static int e(Throwable tr, String format, Object... params) { + @FormatMethod + public static int e(Throwable tr, @FormatString String format, Object... params) { return Log.isLoggable(TAG, Log.ERROR) ? e(tr, format(format, params)) : 0; } /** Log the message as WARNING. Return The number of bytes written */ - public static int w(Throwable tr, String format, Object... params) { + @FormatMethod + public static int w(Throwable tr, @FormatString String format, Object... params) { if (Log.isLoggable(TAG, Log.WARN)) { if (Log.isLoggable(TAG, Log.DEBUG)) { String msg = format(format, params); @@ -165,7 +176,9 @@ public class LogUtil { */ @Deprecated public static int d(String msg, Throwable tr) { - return d(tr, msg); + @SuppressWarnings("FormatStringAnnotation") + int result = d(tr, msg); + return result; } /** @@ -176,7 +189,9 @@ public class LogUtil { */ @Deprecated public static int w(String msg, Throwable tr) { - return w(tr, msg); + @SuppressWarnings("FormatStringAnnotation") + int result = w(tr, msg); + return result; } /** diff --git a/adservices/linter/java/android/adservices/lint/AdServicesLintCheckerIssueRegistry.kt b/adservices/linter/java/android/adservices/lint/AdServicesLintCheckerIssueRegistry.kt index dea6c6ffb..7fe96278c 100644 --- a/adservices/linter/java/android/adservices/lint/AdServicesLintCheckerIssueRegistry.kt +++ b/adservices/linter/java/android/adservices/lint/AdServicesLintCheckerIssueRegistry.kt @@ -30,6 +30,7 @@ class AdServicesLintCheckerIssueRegistry : IssueRegistry() { BackCompatJobServiceDetector.ISSUE, BackCompatNewFileDetector.ISSUE, RoomDatabaseMigrationDetector.ISSUE, + PreconditionsCheckStateDetector.ISSUE, ) override val api: Int diff --git a/adservices/linter/java/android/adservices/lint/PreconditionsCheckStateDetector.kt b/adservices/linter/java/android/adservices/lint/PreconditionsCheckStateDetector.kt new file mode 100644 index 000000000..96c995fd3 --- /dev/null +++ b/adservices/linter/java/android/adservices/lint/PreconditionsCheckStateDetector.kt @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2023 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 android.adservices.lint + +import com.android.tools.lint.detector.api.Category +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Implementation +import com.android.tools.lint.detector.api.Issue +import com.android.tools.lint.detector.api.JavaContext +import com.android.tools.lint.detector.api.Scope +import com.android.tools.lint.detector.api.Severity +import com.android.tools.lint.detector.api.SourceCodeScanner +import com.intellij.psi.PsiMethod +import org.jetbrains.uast.UCallExpression + +class PreconditionsCheckStateDetector : Detector(), SourceCodeScanner { + override fun getApplicableMethodNames(): List<String>? { + return listOf("checkState") + } + + override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) { + if (method.name == "checkState" && method.containingClass?.qualifiedName == + "com.android.internal.util.Preconditions" && method.parameterList.parametersCount >= 3) { + context.report(issue = ISSUE, location = context.getNameLocation(node), + message = "DO NOT USE com.android.internal.util.Preconditions.CheckState(boolean, String, Object...)" + + " because it is not available in R-. Use Preconditions.CheckState(boolean, String, Object...) " + + "from adservices-shared-util instead.") + } + } + + companion object { + val ISSUE = Issue.create( + id = "AvoidPreconditions.CheckState", + briefDescription = "DO NOT USE Preconditions.CheckState", + explanation = """ + DO NOT USE com.android.internal.util.Preconditions.CheckState(boolean, String, Object...) + because it is not available in R-. Use Preconditions.CheckState(boolean, String, Object...) from adservices-shared-util instead. + """, + category = Category.COMPLIANCE, + severity = Severity.ERROR, + implementation = Implementation(PreconditionsCheckStateDetector::class.java, Scope.JAVA_FILE_SCOPE)) + } +}
\ No newline at end of file diff --git a/adservices/linter/tests/java/android/adservices/lint/test/PreconditionsCheckStateDetectorTest.kt b/adservices/linter/tests/java/android/adservices/lint/test/PreconditionsCheckStateDetectorTest.kt new file mode 100644 index 000000000..a579d4949 --- /dev/null +++ b/adservices/linter/tests/java/android/adservices/lint/test/PreconditionsCheckStateDetectorTest.kt @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2023 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 android.adservices.lint.test + +import android.adservices.lint.BackCompatNewFileDetector +import android.adservices.lint.PreconditionsCheckStateDetector +import com.android.tools.lint.checks.infrastructure.LintDetectorTest +import com.android.tools.lint.checks.infrastructure.TestFile +import com.android.tools.lint.checks.infrastructure.TestLintTask +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Issue +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +class PreconditionsCheckStateDetectorTest : LintDetectorTest() { + override fun getDetector(): Detector = PreconditionsCheckStateDetector() + + override fun getIssues(): List<Issue> = listOf(PreconditionsCheckStateDetector.ISSUE) + + override fun lint(): TestLintTask = super.lint().allowMissingSdk(true) + + override fun allowCompilationErrors(): Boolean { + // Some of these unit tests are still relying on source code that references + // unresolved symbols etc. + return true + } + + @Test + fun applicableMethodCalls_throws() { + lint().files(java(""" +package com.android.adservices; + +import com.android.internal.util.Preconditions; + +public final class FakeClass { + public FakeClass() { + Preconditions.checkState( + true, + fakeStringFormat, + fakeObject); + } +} + """).indented(), + *stubs) + .issues(PreconditionsCheckStateDetector.ISSUE) + .run() + .expect( + """ + src/com/android/adservices/FakeClass.java:7: Error: DO NOT USE com.android.internal.util.Preconditions.CheckState(boolean, String, Object...) because it is not available in R-. Use Preconditions.CheckState(boolean, String, Object...) from adservices-shared-util instead. [AvoidPreconditions.CheckState] + Preconditions.checkState( + ~~~~~~~~~~ +1 errors, 0 warnings + """.trimIndent()) + } + + @Test + fun applicableMethodCalls_noPreconditionsCheckState_pass() { + lint().files(java(""" +package com.android.adservices; + +import com.android.internal.util.Preconditions; + +public final class FakeClass { + public FakeClass() { + Preconditions.checkArgument( // Use other method from Preconditions class will not trigger lint errors. + true, + fakeString, + fakeObject); + } +} + """).indented(), + *stubs) + .issues(BackCompatNewFileDetector.ISSUE) + .run() + .expectClean() + } + + @Test + fun applicableMethodCalls_usingSharedUtilClass_pass() { + lint().files(java(""" +package com.android.adservices; + +import com.android.adservices.shared.util.Preconditions; + +public final class FakeClass { + public FakeClass() { + Preconditions.checkState( // Use checkState from Helper class will not trigger lint errors. + true, + fakeStringFormat, + fakeObject); + } +} + """).indented(), + *stubs) + .issues(BackCompatNewFileDetector.ISSUE) + .run() + .expectClean() + } + + private val preconditionsClassStub: TestFile = + java(""" + package com.android.internal.util; + + public class Preconditions { + public static void checkState(boolean b, String errorMessageTemplate, Object o1) { + } + } + """).indented() + + private val helperClassWithCheckStateStub: TestFile = + java(""" + package com.android.adservices.shared.util; + + public class Preconditions { + public static void checkState(boolean b, String errorMessageTemplate, Object o1) { + } + } + """).indented() + + private val stubs = + arrayOf( + preconditionsClassStub, + helperClassWithCheckStateStub + ) +}
\ No newline at end of file diff --git a/adservices/samples/topics/sampleapp1/Android.bp b/adservices/samples/topics/sampleapp1/Android.bp index a320f3617..f254f53da 100644 --- a/adservices/samples/topics/sampleapp1/Android.bp +++ b/adservices/samples/topics/sampleapp1/Android.bp @@ -28,11 +28,15 @@ android_app { ], static_libs: [ "adservices-clients", + "adservices-service-core", "androidx.appcompat_appcompat", "androidx-constraintlayout_constraintlayout", "com.google.android.material_material", "androidx.concurrent_concurrent-futures", ], + jni_libs: [ + "libhpke_jni", + ], resource_dirs: ["res"], platform_apis: true, min_sdk_version: "30", diff --git a/adservices/samples/topics/sampleapp1/java/com/example/adservices/samples/topics/sampleapp1/MainActivity.java b/adservices/samples/topics/sampleapp1/java/com/example/adservices/samples/topics/sampleapp1/MainActivity.java index 68b1c77e9..6cbe1935c 100644 --- a/adservices/samples/topics/sampleapp1/java/com/example/adservices/samples/topics/sampleapp1/MainActivity.java +++ b/adservices/samples/topics/sampleapp1/java/com/example/adservices/samples/topics/sampleapp1/MainActivity.java @@ -18,6 +18,7 @@ package com.example.adservices.samples.topics.sampleapp1; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import android.adservices.clients.topics.AdvertisingTopicsClient; +import android.adservices.topics.EncryptedTopic; import android.adservices.topics.GetTopicsResponse; import android.adservices.topics.Topic; import android.content.Context; @@ -31,6 +32,9 @@ import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; +import com.android.adservices.HpkeJni; + +import com.google.common.primitives.Bytes; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -39,6 +43,7 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Base64; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -56,6 +61,12 @@ public class MainActivity extends AppCompatActivity { private static final String TAG = "SampleApp1"; private static final String RB_SETTING_APP_INTENT = "android.adservices.ui.SETTINGS"; private static final List<String> SDK_NAMES = new ArrayList<>(Arrays.asList("SdkName1")); + // Test constants for testing encryption + private static final String TEST_PRIVATE_KEY_BASE64 = + "f86EzLmGaVmc+PwjJk5ADPE4ijQvliWf0CQyY/Zyy7I="; + private static final byte[] DECODED_PRIVATE_KEY = + Base64.getDecoder().decode(TEST_PRIVATE_KEY_BASE64); + private static final byte[] EMPTY_CONTEXT_INFO = new byte[] {}; private Button mTopicsClientButton; private Button mTopicsPreviewButton; private TextView mResultTextView; @@ -108,6 +119,21 @@ public class MainActivity extends AppCompatActivity { return sb.toString(); } + private String getDecryptedTopics(List<EncryptedTopic> arr) { + StringBuilder sb = new StringBuilder(); + int index = 1; + for (EncryptedTopic encryptedTopic : arr) { + byte[] cipherText = + Bytes.concat( + encryptedTopic.getEncapsulatedKey(), + encryptedTopic.getEncryptedTopic()); + byte[] decryptedText = + HpkeJni.decrypt(DECODED_PRIVATE_KEY, cipherText, EMPTY_CONTEXT_INFO); + sb.append(index++).append(". ").append(new String(decryptedText)).append(NEWLINE); + } + return sb.toString(); + } + @SuppressWarnings("NewApi") private void getTopics(String sdkName, boolean shouldRecordObservation) { // On R, Privacy Sandbox is initially disabled. @@ -139,6 +165,9 @@ public class MainActivity extends AppCompatActivity { public void onSuccess(GetTopicsResponse result) { Log.d(TAG, "GetTopics for sdk " + sdkName + " succeeded!"); String topics = getTopics(result.getTopics()); + String encryptedTopicsDecrypted = + getDecryptedTopics(result.getEncryptedTopics()); + mHandler.post( new Runnable() { @Override @@ -149,9 +178,22 @@ public class MainActivity extends AppCompatActivity { + NEWLINE + topics + NEWLINE); + mResultTextView.append( + sdkName + + "'s encrypted topics, decrypted: " + + NEWLINE + + encryptedTopicsDecrypted + + NEWLINE); } }); Log.d(TAG, sdkName + "'s topics: " + NEWLINE + topics + NEWLINE); + Log.d( + TAG, + sdkName + + "'s encrypted topics, decrypted: " + + NEWLINE + + encryptedTopicsDecrypted + + NEWLINE); } @Override diff --git a/adservices/samples/topics/sampleapp2/Android.bp b/adservices/samples/topics/sampleapp2/Android.bp index d128f18ed..9494490b9 100644 --- a/adservices/samples/topics/sampleapp2/Android.bp +++ b/adservices/samples/topics/sampleapp2/Android.bp @@ -28,11 +28,15 @@ android_app { ], static_libs: [ "adservices-clients", + "adservices-service-core", "androidx.appcompat_appcompat", "androidx-constraintlayout_constraintlayout", "com.google.android.material_material", "androidx.concurrent_concurrent-futures", ], + jni_libs: [ + "libhpke_jni", + ], resource_dirs: ["res"], platform_apis: true, min_sdk_version: "30", diff --git a/adservices/samples/topics/sampleapp2/java/com/example/adservices/samples/topics/sampleapp2/MainActivity.java b/adservices/samples/topics/sampleapp2/java/com/example/adservices/samples/topics/sampleapp2/MainActivity.java index 4abb6d7fd..e946e2958 100644 --- a/adservices/samples/topics/sampleapp2/java/com/example/adservices/samples/topics/sampleapp2/MainActivity.java +++ b/adservices/samples/topics/sampleapp2/java/com/example/adservices/samples/topics/sampleapp2/MainActivity.java @@ -18,6 +18,7 @@ package com.example.adservices.samples.topics.sampleapp2; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import android.adservices.clients.topics.AdvertisingTopicsClient; +import android.adservices.topics.EncryptedTopic; import android.adservices.topics.GetTopicsResponse; import android.adservices.topics.Topic; import android.os.Bundle; @@ -28,15 +29,18 @@ import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; +import com.android.adservices.HpkeJni; + +import com.google.common.primitives.Bytes; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; - import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Base64; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -54,6 +58,12 @@ public class MainActivity extends AppCompatActivity { private static final String TAG = "SampleApp"; private static final List<String> SDK_NAMES = new ArrayList<>(Arrays.asList("SdkName1", "SdkName2")); + // Test constants for testing encryption + private static final String TEST_PRIVATE_KEY_BASE64 = + "f86EzLmGaVmc+PwjJk5ADPE4ijQvliWf0CQyY/Zyy7I="; + private static final byte[] DECODED_PRIVATE_KEY = + Base64.getDecoder().decode(TEST_PRIVATE_KEY_BASE64); + private static final byte[] EMPTY_CONTEXT_INFO = new byte[] {}; private Button mTopicsClientButton; private TextView mResultTextView; private AdvertisingTopicsClient mAdvertisingTopicsClient; @@ -91,25 +101,42 @@ public class MainActivity extends AppCompatActivity { public void onSuccess(GetTopicsResponse result) { Log.d(TAG, "GetTopics for sdk " + sdkName + " succeeded!"); String topics = getTopics(result.getTopics()); + String encryptedTopicsDecrypted = + getDecryptedTopics(result.getEncryptedTopics()); + + mHandler.post( + new Runnable() { + @Override + public void run() { + mResultTextView.append( + sdkName + + "'s topics: " + + NEWLINE + + topics + + NEWLINE); + mResultTextView.append( + sdkName + + "'s encrypted topics," + + " decrypted: " + + NEWLINE + + encryptedTopicsDecrypted + + NEWLINE); + } + }); - mHandler.post(new Runnable() { - @Override - public void run() { - mResultTextView.append( + Log.d( + TAG, sdkName + "'s topics: " + NEWLINE + topics + NEWLINE); - } - }); - Log.d( TAG, sdkName - + "'s topics: " + + "'s encrypted topics, decrypted: " + NEWLINE - + topics + + encryptedTopicsDecrypted + NEWLINE); } @@ -126,17 +153,18 @@ public class MainActivity extends AppCompatActivity { + ": " + t.getMessage()); - mHandler.post(new Runnable() { - @Override - public void run() { - mResultTextView.append( - "Failed to getTopics for sdk " - + sdkName - + ": " - + t.toString() - + NEWLINE); - } - }); + mHandler.post( + new Runnable() { + @Override + public void run() { + mResultTextView.append( + "Failed to getTopics for sdk " + + sdkName + + ": " + + t.toString() + + NEWLINE); + } + }); } }, directExecutor()); @@ -152,4 +180,19 @@ public class MainActivity extends AppCompatActivity { } return sb.toString(); } + + private String getDecryptedTopics(List<EncryptedTopic> arr) { + StringBuilder sb = new StringBuilder(); + int index = 1; + for (EncryptedTopic encryptedTopic : arr) { + byte[] cipherText = + Bytes.concat( + encryptedTopic.getEncapsulatedKey(), + encryptedTopic.getEncryptedTopic()); + byte[] decryptedText = + HpkeJni.decrypt(DECODED_PRIVATE_KEY, cipherText, EMPTY_CONTEXT_INFO); + sb.append(index++).append(". ").append(new String(decryptedText)).append(NEWLINE); + } + return sb.toString(); + } } diff --git a/adservices/samples/topics/sampleapp3/Android.bp b/adservices/samples/topics/sampleapp3/Android.bp index 9e9d2ae64..c1cae4581 100644 --- a/adservices/samples/topics/sampleapp3/Android.bp +++ b/adservices/samples/topics/sampleapp3/Android.bp @@ -28,11 +28,15 @@ android_app { ], static_libs: [ "adservices-clients", + "adservices-service-core", "androidx.appcompat_appcompat", "androidx-constraintlayout_constraintlayout", "com.google.android.material_material", "androidx.concurrent_concurrent-futures", ], + jni_libs: [ + "libhpke_jni", + ], resource_dirs: ["res"], platform_apis: true, min_sdk_version: "30", diff --git a/adservices/samples/topics/sampleapp3/java/com/example/adservices/samples/topics/sampleapp3/MainActivity.java b/adservices/samples/topics/sampleapp3/java/com/example/adservices/samples/topics/sampleapp3/MainActivity.java index 2433d130f..d2c889f24 100644 --- a/adservices/samples/topics/sampleapp3/java/com/example/adservices/samples/topics/sampleapp3/MainActivity.java +++ b/adservices/samples/topics/sampleapp3/java/com/example/adservices/samples/topics/sampleapp3/MainActivity.java @@ -18,6 +18,7 @@ package com.example.adservices.samples.topics.sampleapp3; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import android.adservices.clients.topics.AdvertisingTopicsClient; +import android.adservices.topics.EncryptedTopic; import android.adservices.topics.GetTopicsResponse; import android.adservices.topics.Topic; import android.os.Bundle; @@ -28,6 +29,9 @@ import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; +import com.android.adservices.HpkeJni; + +import com.google.common.primitives.Bytes; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -36,6 +40,7 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Base64; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -53,6 +58,12 @@ public class MainActivity extends AppCompatActivity { private static final String TAG = "SampleApp"; private static final List<String> SDK_NAMES = new ArrayList<>(Arrays.asList("SdkName2", "SdkName3")); + // Test constants for testing encryption + private static final String TEST_PRIVATE_KEY_BASE64 = + "f86EzLmGaVmc+PwjJk5ADPE4ijQvliWf0CQyY/Zyy7I="; + private static final byte[] DECODED_PRIVATE_KEY = + Base64.getDecoder().decode(TEST_PRIVATE_KEY_BASE64); + private static final byte[] EMPTY_CONTEXT_INFO = new byte[] {}; private Button mTopicsClientButton; private TextView mResultTextView; private AdvertisingTopicsClient mAdvertisingTopicsClient; @@ -90,24 +101,42 @@ public class MainActivity extends AppCompatActivity { public void onSuccess(GetTopicsResponse result) { Log.d(TAG, "GetTopics for sdk " + sdkName + " succeeded!"); String topics = getTopics(result.getTopics()); - mHandler.post(new Runnable() { - @Override - public void run() { - mResultTextView.append( + String encryptedTopicsDecrypted = + getDecryptedTopics(result.getEncryptedTopics()); + + mHandler.post( + new Runnable() { + @Override + public void run() { + mResultTextView.append( + sdkName + + "'s topics: " + + NEWLINE + + topics + + NEWLINE); + mResultTextView.append( + sdkName + + "'s encrypted topics," + + " decrypted: " + + NEWLINE + + encryptedTopicsDecrypted + + NEWLINE); + } + }); + + Log.d( + TAG, sdkName + "'s topics: " + NEWLINE + topics + NEWLINE); - } - }); - Log.d( TAG, sdkName - + "'s topics: " + + "'s encrypted topics, decrypted: " + NEWLINE - + topics + + encryptedTopicsDecrypted + NEWLINE); } @@ -124,17 +153,18 @@ public class MainActivity extends AppCompatActivity { + ": " + t.getMessage()); - mHandler.post(new Runnable() { - @Override - public void run() { - mResultTextView.append( - "Failed to getTopics for sdk " - + sdkName - + ": " - + t.toString() - + NEWLINE); - } - }); + mHandler.post( + new Runnable() { + @Override + public void run() { + mResultTextView.append( + "Failed to getTopics for sdk " + + sdkName + + ": " + + t.toString() + + NEWLINE); + } + }); } }, directExecutor()); @@ -150,4 +180,19 @@ public class MainActivity extends AppCompatActivity { } return sb.toString(); } + + private String getDecryptedTopics(List<EncryptedTopic> arr) { + StringBuilder sb = new StringBuilder(); + int index = 1; + for (EncryptedTopic encryptedTopic : arr) { + byte[] cipherText = + Bytes.concat( + encryptedTopic.getEncapsulatedKey(), + encryptedTopic.getEncryptedTopic()); + byte[] decryptedText = + HpkeJni.decrypt(DECODED_PRIVATE_KEY, cipherText, EMPTY_CONTEXT_INFO); + sb.append(index++).append(". ").append(new String(decryptedText)).append(NEWLINE); + } + return sb.toString(); + } } diff --git a/adservices/samples/topics/sampleapp4/Android.bp b/adservices/samples/topics/sampleapp4/Android.bp index ea06a545e..b848db7d8 100644 --- a/adservices/samples/topics/sampleapp4/Android.bp +++ b/adservices/samples/topics/sampleapp4/Android.bp @@ -28,11 +28,15 @@ android_app { ], static_libs: [ "adservices-clients", + "adservices-service-core", "androidx.appcompat_appcompat", "androidx-constraintlayout_constraintlayout", "com.google.android.material_material", "androidx.concurrent_concurrent-futures", ], + jni_libs: [ + "libhpke_jni", + ], resource_dirs: ["res"], platform_apis: true, min_sdk_version: "30", diff --git a/adservices/samples/topics/sampleapp4/java/com/example/adservices/samples/topics/sampleapp4/MainActivity.java b/adservices/samples/topics/sampleapp4/java/com/example/adservices/samples/topics/sampleapp4/MainActivity.java index 03531b4d4..46ae04f79 100644 --- a/adservices/samples/topics/sampleapp4/java/com/example/adservices/samples/topics/sampleapp4/MainActivity.java +++ b/adservices/samples/topics/sampleapp4/java/com/example/adservices/samples/topics/sampleapp4/MainActivity.java @@ -18,6 +18,7 @@ package com.example.adservices.samples.topics.sampleapp4; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import android.adservices.clients.topics.AdvertisingTopicsClient; +import android.adservices.topics.EncryptedTopic; import android.adservices.topics.GetTopicsResponse; import android.adservices.topics.Topic; import android.os.Bundle; @@ -28,6 +29,9 @@ import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; +import com.android.adservices.HpkeJni; + +import com.google.common.primitives.Bytes; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -36,6 +40,7 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Base64; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -53,6 +58,12 @@ public class MainActivity extends AppCompatActivity { private static final String TAG = "SampleApp"; private static final List<String> SDK_NAMES = new ArrayList<>(Arrays.asList("SdkName3", "SdkName4")); + // Test constants for testing encryption + private static final String TEST_PRIVATE_KEY_BASE64 = + "f86EzLmGaVmc+PwjJk5ADPE4ijQvliWf0CQyY/Zyy7I="; + private static final byte[] DECODED_PRIVATE_KEY = + Base64.getDecoder().decode(TEST_PRIVATE_KEY_BASE64); + private static final byte[] EMPTY_CONTEXT_INFO = new byte[] {}; private Button mTopicsClientButton; private TextView mResultTextView; private AdvertisingTopicsClient mAdvertisingTopicsClient; @@ -90,25 +101,42 @@ public class MainActivity extends AppCompatActivity { public void onSuccess(GetTopicsResponse result) { Log.d(TAG, "GetTopics for sdk " + sdkName + " succeeded!"); String topics = getTopics(result.getTopics()); + String encryptedTopicsDecrypted = + getDecryptedTopics(result.getEncryptedTopics()); + + mHandler.post( + new Runnable() { + @Override + public void run() { + mResultTextView.append( + sdkName + + "'s topics: " + + NEWLINE + + topics + + NEWLINE); + mResultTextView.append( + sdkName + + "'s encrypted topics," + + " decrypted: " + + NEWLINE + + encryptedTopicsDecrypted + + NEWLINE); + } + }); - mHandler.post(new Runnable() { - @Override - public void run() { - mResultTextView.append( + Log.d( + TAG, sdkName + "'s topics: " + NEWLINE + topics + NEWLINE); - } - }); - Log.d( TAG, sdkName - + "'s topics: " + + "'s encrypted topics, decrypted: " + NEWLINE - + topics + + encryptedTopicsDecrypted + NEWLINE); } @@ -125,17 +153,18 @@ public class MainActivity extends AppCompatActivity { + ": " + t.getMessage()); - mHandler.post(new Runnable() { - @Override - public void run() { - mResultTextView.append( - "Failed to getTopics for sdk " - + sdkName - + ": " - + t.toString() - + NEWLINE); - } - }); + mHandler.post( + new Runnable() { + @Override + public void run() { + mResultTextView.append( + "Failed to getTopics for sdk " + + sdkName + + ": " + + t.toString() + + NEWLINE); + } + }); } }, directExecutor()); @@ -151,4 +180,19 @@ public class MainActivity extends AppCompatActivity { } return sb.toString(); } + + private String getDecryptedTopics(List<EncryptedTopic> arr) { + StringBuilder sb = new StringBuilder(); + int index = 1; + for (EncryptedTopic encryptedTopic : arr) { + byte[] cipherText = + Bytes.concat( + encryptedTopic.getEncapsulatedKey(), + encryptedTopic.getEncryptedTopic()); + byte[] decryptedText = + HpkeJni.decrypt(DECODED_PRIVATE_KEY, cipherText, EMPTY_CONTEXT_INFO); + sb.append(index++).append(". ").append(new String(decryptedText)).append(NEWLINE); + } + return sb.toString(); + } } diff --git a/adservices/samples/topics/sampleapp5/Android.bp b/adservices/samples/topics/sampleapp5/Android.bp index eefc38270..2e794df73 100644 --- a/adservices/samples/topics/sampleapp5/Android.bp +++ b/adservices/samples/topics/sampleapp5/Android.bp @@ -28,11 +28,15 @@ android_app { ], static_libs: [ "adservices-clients", + "adservices-service-core", "androidx.appcompat_appcompat", "androidx-constraintlayout_constraintlayout", "com.google.android.material_material", "androidx.concurrent_concurrent-futures", ], + jni_libs: [ + "libhpke_jni", + ], resource_dirs: ["res"], platform_apis: true, min_sdk_version: "30", diff --git a/adservices/samples/topics/sampleapp5/java/com/example/adservices/samples/topics/sampleapp5/MainActivity.java b/adservices/samples/topics/sampleapp5/java/com/example/adservices/samples/topics/sampleapp5/MainActivity.java index 6d93fb918..0c155d2d7 100644 --- a/adservices/samples/topics/sampleapp5/java/com/example/adservices/samples/topics/sampleapp5/MainActivity.java +++ b/adservices/samples/topics/sampleapp5/java/com/example/adservices/samples/topics/sampleapp5/MainActivity.java @@ -18,6 +18,7 @@ package com.example.adservices.samples.topics.sampleapp5; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import android.adservices.clients.topics.AdvertisingTopicsClient; +import android.adservices.topics.EncryptedTopic; import android.adservices.topics.GetTopicsResponse; import android.adservices.topics.Topic; import android.os.Bundle; @@ -28,6 +29,9 @@ import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; +import com.android.adservices.HpkeJni; + +import com.google.common.primitives.Bytes; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -36,6 +40,7 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Base64; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -53,6 +58,11 @@ public class MainActivity extends AppCompatActivity { private static final String TAG = "SampleApp"; private static final List<String> SDK_NAMES = new ArrayList<>(Arrays.asList("SdkName3", "SdkName4", "SdkName5")); + // Test constants for testing encryption + private static final String PRIVATE_KEY_BASE64 = "f86EzLmGaVmc+PwjJk5ADPE4ijQvliWf0CQyY/Zyy7I="; + private static final byte[] DECODED_PRIVATE_KEY = + Base64.getDecoder().decode(PRIVATE_KEY_BASE64); + private static final byte[] EMPTY_CONTEXT_INFO = new byte[] {}; private Button mTopicsClientButton; private TextView mResultTextView; private AdvertisingTopicsClient mAdvertisingTopicsClient; @@ -90,24 +100,41 @@ public class MainActivity extends AppCompatActivity { public void onSuccess(GetTopicsResponse result) { Log.d(TAG, "GetTopics for sdk " + sdkName + " succeeded!"); String topics = getTopics(result.getTopics()); - mHandler.post(new Runnable() { - @Override - public void run() { - mResultTextView.append( + String encryptedTopicsDecrypted = + getDecryptedTopics(result.getEncryptedTopics()); + mHandler.post( + new Runnable() { + @Override + public void run() { + mResultTextView.append( + sdkName + + "'s topics: " + + NEWLINE + + topics + + NEWLINE); + mResultTextView.append( + sdkName + + "'s encrypted topics," + + " decrypted: " + + NEWLINE + + encryptedTopicsDecrypted + + NEWLINE); + } + }); + + Log.d( + TAG, sdkName + "'s topics: " + NEWLINE + topics + NEWLINE); - } - }); - Log.d( TAG, sdkName - + "'s topics: " + + "'s encrypted topics, decrypted: " + NEWLINE - + topics + + encryptedTopicsDecrypted + NEWLINE); } @@ -124,17 +151,18 @@ public class MainActivity extends AppCompatActivity { + ": " + t.getMessage()); - mHandler.post(new Runnable() { - @Override - public void run() { - mResultTextView.append( - "Failed to getTopics for sdk " - + sdkName - + ": " - + t.toString() - + NEWLINE); - } - }); + mHandler.post( + new Runnable() { + @Override + public void run() { + mResultTextView.append( + "Failed to getTopics for sdk " + + sdkName + + ": " + + t.toString() + + NEWLINE); + } + }); } }, directExecutor()); @@ -150,4 +178,19 @@ public class MainActivity extends AppCompatActivity { } return sb.toString(); } + + private String getDecryptedTopics(List<EncryptedTopic> arr) { + StringBuilder sb = new StringBuilder(); + int index = 1; + for (EncryptedTopic encryptedTopic : arr) { + byte[] cipherText = + Bytes.concat( + encryptedTopic.getEncapsulatedKey(), + encryptedTopic.getEncryptedTopic()); + byte[] decryptedText = + HpkeJni.decrypt(DECODED_PRIVATE_KEY, cipherText, EMPTY_CONTEXT_INFO); + sb.append(index++).append(". ").append(new String(decryptedText)).append(NEWLINE); + } + return sb.toString(); + } } diff --git a/adservices/samples/topics/sampleapp6/Android.bp b/adservices/samples/topics/sampleapp6/Android.bp index c3a5ec7e6..298656d2f 100644 --- a/adservices/samples/topics/sampleapp6/Android.bp +++ b/adservices/samples/topics/sampleapp6/Android.bp @@ -28,11 +28,15 @@ android_app { ], static_libs: [ "adservices-clients", + "adservices-service-core", "androidx.appcompat_appcompat", "androidx-constraintlayout_constraintlayout", "com.google.android.material_material", "androidx.concurrent_concurrent-futures", ], + jni_libs: [ + "libhpke_jni", + ], resource_dirs: ["res"], platform_apis: true, min_sdk_version: "30", diff --git a/adservices/samples/topics/sampleapp6/java/com/example/adservices/samples/topics/sampleapp6/MainActivity.java b/adservices/samples/topics/sampleapp6/java/com/example/adservices/samples/topics/sampleapp6/MainActivity.java index 69e2017d6..7492debcf 100644 --- a/adservices/samples/topics/sampleapp6/java/com/example/adservices/samples/topics/sampleapp6/MainActivity.java +++ b/adservices/samples/topics/sampleapp6/java/com/example/adservices/samples/topics/sampleapp6/MainActivity.java @@ -18,6 +18,7 @@ package com.example.adservices.samples.topics.sampleapp6; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import android.adservices.clients.topics.AdvertisingTopicsClient; +import android.adservices.topics.EncryptedTopic; import android.adservices.topics.GetTopicsResponse; import android.adservices.topics.Topic; import android.os.Bundle; @@ -28,6 +29,9 @@ import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; +import com.android.adservices.HpkeJni; + +import com.google.common.primitives.Bytes; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -36,6 +40,7 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Base64; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -54,6 +59,12 @@ public class MainActivity extends AppCompatActivity { private static final List<String> SDK_NAMES = new ArrayList<>( Arrays.asList("SdkName1", "SdkName2", "SdkName3", "SdkName4", "SdkName5")); + // Test constants for testing encryption + private static final String TEST_PRIVATE_KEY_BASE64 = + "f86EzLmGaVmc+PwjJk5ADPE4ijQvliWf0CQyY/Zyy7I="; + private static final byte[] DECODED_PRIVATE_KEY = + Base64.getDecoder().decode(TEST_PRIVATE_KEY_BASE64); + private static final byte[] EMPTY_CONTEXT_INFO = new byte[] {}; private Button mTopicsClientButton; private TextView mResultTextView; private AdvertisingTopicsClient mAdvertisingTopicsClient; @@ -91,23 +102,40 @@ public class MainActivity extends AppCompatActivity { public void onSuccess(GetTopicsResponse result) { Log.d(TAG, "GetTopics for sdk " + sdkName + " succeeded!"); String topics = getTopics(result.getTopics()); - mHandler.post(new Runnable() { - @Override - public void run() { - mResultTextView.append( + String encryptedTopicsDecrypted = + getDecryptedTopics(result.getEncryptedTopics()); + mHandler.post( + new Runnable() { + @Override + public void run() { + mResultTextView.append( + sdkName + + "'s topics: " + + NEWLINE + + topics + + NEWLINE); + mResultTextView.append( + sdkName + + "'s encrypted topics," + + " decrypted: " + + NEWLINE + + encryptedTopicsDecrypted + + NEWLINE); + } + }); + Log.d( + TAG, sdkName + "'s topics: " + NEWLINE + topics + NEWLINE); - } - }); Log.d( TAG, sdkName - + "'s topics: " + + "'s encrypted topics, decrypted: " + NEWLINE - + topics + + encryptedTopicsDecrypted + NEWLINE); } @@ -122,17 +150,18 @@ public class MainActivity extends AppCompatActivity { + sdkName + ": " + sw.toString()); - mHandler.post(new Runnable() { - @Override - public void run() { - mResultTextView.append( - "Failed to getTopics for sdk " - + sdkName - + ": " - + t.toString() - + NEWLINE); - } - }); + mHandler.post( + new Runnable() { + @Override + public void run() { + mResultTextView.append( + "Failed to getTopics for sdk " + + sdkName + + ": " + + t.toString() + + NEWLINE); + } + }); } }, directExecutor()); @@ -148,4 +177,19 @@ public class MainActivity extends AppCompatActivity { } return sb.toString(); } + + private String getDecryptedTopics(List<EncryptedTopic> arr) { + StringBuilder sb = new StringBuilder(); + int index = 1; + for (EncryptedTopic encryptedTopic : arr) { + byte[] cipherText = + Bytes.concat( + encryptedTopic.getEncapsulatedKey(), + encryptedTopic.getEncryptedTopic()); + byte[] decryptedText = + HpkeJni.decrypt(DECODED_PRIVATE_KEY, cipherText, EMPTY_CONTEXT_INFO); + sb.append(index++).append(". ").append(new String(decryptedText)).append(NEWLINE); + } + return sb.toString(); + } } diff --git a/adservices/samples/topics/sampleappwithnoperm/Android.bp b/adservices/samples/topics/sampleappwithnoperm/Android.bp index 2f3e59b1d..fbfedd590 100644 --- a/adservices/samples/topics/sampleappwithnoperm/Android.bp +++ b/adservices/samples/topics/sampleappwithnoperm/Android.bp @@ -28,11 +28,15 @@ android_app { ], static_libs: [ "adservices-clients", + "adservices-service-core", "androidx.appcompat_appcompat", "androidx-constraintlayout_constraintlayout", "com.google.android.material_material", "androidx.concurrent_concurrent-futures", ], + jni_libs: [ + "libhpke_jni", + ], resource_dirs: ["res"], platform_apis: true, min_sdk_version: "30", diff --git a/adservices/samples/topics/sampleappwithnoperm/java/com/example/adservices/samples/topics/sampleappwithnoperm/MainActivity.java b/adservices/samples/topics/sampleappwithnoperm/java/com/example/adservices/samples/topics/sampleappwithnoperm/MainActivity.java index 229a4bf7d..2343bc71a 100644 --- a/adservices/samples/topics/sampleappwithnoperm/java/com/example/adservices/samples/topics/sampleappwithnoperm/MainActivity.java +++ b/adservices/samples/topics/sampleappwithnoperm/java/com/example/adservices/samples/topics/sampleappwithnoperm/MainActivity.java @@ -18,6 +18,7 @@ package com.example.adservices.samples.topics.sampleappwithnoperm; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import android.adservices.clients.topics.AdvertisingTopicsClient; +import android.adservices.topics.EncryptedTopic; import android.adservices.topics.GetTopicsResponse; import android.adservices.topics.Topic; import android.os.Bundle; @@ -28,14 +29,18 @@ import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; +import com.android.adservices.HpkeJni; + +import com.google.common.primitives.Bytes; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import java.io.StringWriter; import java.io.PrintWriter; +import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Base64; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -53,6 +58,12 @@ public class MainActivity extends AppCompatActivity { private static final String TAG = "SampleApp"; private static final List<String> SDK_NAMES = new ArrayList<>(Arrays.asList("SdkName1", "SdkName2")); + // Test constants for testing encryption + private static final String TEST_PRIVATE_KEY_BASE64 = + "f86EzLmGaVmc+PwjJk5ADPE4ijQvliWf0CQyY/Zyy7I="; + private static final byte[] DECODED_PRIVATE_KEY = + Base64.getDecoder().decode(TEST_PRIVATE_KEY_BASE64); + private static final byte[] EMPTY_CONTEXT_INFO = new byte[] {}; private Button mTopicsClientButton; private TextView mResultTextView; private AdvertisingTopicsClient mAdvertisingTopicsClient; @@ -89,25 +100,42 @@ public class MainActivity extends AppCompatActivity { public void onSuccess(GetTopicsResponse result) { Log.d(TAG, "GetTopics for sdk " + sdkName + " succeeded!"); String topics = getTopics(result.getTopics()); + String encryptedTopicsDecrypted = + getDecryptedTopics(result.getEncryptedTopics()); + + mHandler.post( + new Runnable() { + @Override + public void run() { + mResultTextView.append( + sdkName + + "'s topics: " + + NEWLINE + + topics + + NEWLINE); + mResultTextView.append( + sdkName + + "'s encrypted topics," + + " decrypted: " + + NEWLINE + + encryptedTopicsDecrypted + + NEWLINE); + } + }); - mHandler.post(new Runnable() { - @Override - public void run() { - mResultTextView.append( + Log.d( + TAG, sdkName + "'s topics: " + NEWLINE + topics + NEWLINE); - } - }); - Log.d( TAG, sdkName - + "'s topics: " + + "'s encrypted topics, decrypted: " + NEWLINE - + topics + + encryptedTopicsDecrypted + NEWLINE); } @@ -124,17 +152,18 @@ public class MainActivity extends AppCompatActivity { + ": " + t.getMessage()); - mHandler.post(new Runnable() { - @Override - public void run() { - mResultTextView.append( - "Failed to getTopics for sdk " - + sdkName - + ": " - + t.toString() - + NEWLINE); - } - }); + mHandler.post( + new Runnable() { + @Override + public void run() { + mResultTextView.append( + "Failed to getTopics for sdk " + + sdkName + + ": " + + t.toString() + + NEWLINE); + } + }); } }, directExecutor()); @@ -150,4 +179,19 @@ public class MainActivity extends AppCompatActivity { } return sb.toString(); } + + private String getDecryptedTopics(List<EncryptedTopic> arr) { + StringBuilder sb = new StringBuilder(); + int index = 1; + for (EncryptedTopic encryptedTopic : arr) { + byte[] cipherText = + Bytes.concat( + encryptedTopic.getEncapsulatedKey(), + encryptedTopic.getEncryptedTopic()); + byte[] decryptedText = + HpkeJni.decrypt(DECODED_PRIVATE_KEY, cipherText, EMPTY_CONTEXT_INFO); + sb.append(index++).append(". ").append(new String(decryptedText)).append(NEWLINE); + } + return sb.toString(); + } } diff --git a/adservices/samples/topics/sampleappwithoptoutall/Android.bp b/adservices/samples/topics/sampleappwithoptoutall/Android.bp index fa7910917..02970f432 100644 --- a/adservices/samples/topics/sampleappwithoptoutall/Android.bp +++ b/adservices/samples/topics/sampleappwithoptoutall/Android.bp @@ -28,11 +28,15 @@ android_app { ], static_libs: [ "adservices-clients", + "adservices-service-core", "androidx.appcompat_appcompat", "androidx-constraintlayout_constraintlayout", "com.google.android.material_material", "androidx.concurrent_concurrent-futures", ], + jni_libs: [ + "libhpke_jni", + ], resource_dirs: ["res"], platform_apis: true, min_sdk_version: "30", diff --git a/adservices/samples/topics/sampleappwithoptoutall/java/com/example/adservices/samples/topics/sampleappwithoptoutall/MainActivity.java b/adservices/samples/topics/sampleappwithoptoutall/java/com/example/adservices/samples/topics/sampleappwithoptoutall/MainActivity.java index e65d42ea4..e1208e724 100644 --- a/adservices/samples/topics/sampleappwithoptoutall/java/com/example/adservices/samples/topics/sampleappwithoptoutall/MainActivity.java +++ b/adservices/samples/topics/sampleappwithoptoutall/java/com/example/adservices/samples/topics/sampleappwithoptoutall/MainActivity.java @@ -18,6 +18,7 @@ package com.example.adservices.samples.topics.sampleappwithoptoutall; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import android.adservices.clients.topics.AdvertisingTopicsClient; +import android.adservices.topics.EncryptedTopic; import android.adservices.topics.GetTopicsResponse; import android.adservices.topics.Topic; import android.os.Bundle; @@ -28,14 +29,18 @@ import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; +import com.android.adservices.HpkeJni; + +import com.google.common.primitives.Bytes; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import java.io.StringWriter; import java.io.PrintWriter; +import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Base64; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -53,6 +58,12 @@ public class MainActivity extends AppCompatActivity { private static final String TAG = "SampleApp"; private static final List<String> SDK_NAMES = new ArrayList<>(Arrays.asList("SdkName1", "SdkName2")); + // Test constants for testing encryption + private static final String TEST_PRIVATE_KEY_BASE64 = + "f86EzLmGaVmc+PwjJk5ADPE4ijQvliWf0CQyY/Zyy7I="; + private static final byte[] DECODED_PRIVATE_KEY = + Base64.getDecoder().decode(TEST_PRIVATE_KEY_BASE64); + private static final byte[] EMPTY_CONTEXT_INFO = new byte[] {}; private Button mTopicsClientButton; private TextView mResultTextView; private AdvertisingTopicsClient mAdvertisingTopicsClient; @@ -89,25 +100,42 @@ public class MainActivity extends AppCompatActivity { public void onSuccess(GetTopicsResponse result) { Log.d(TAG, "GetTopics for sdk " + sdkName + " succeeded!"); String topics = getTopics(result.getTopics()); + String encryptedTopicsDecrypted = + getDecryptedTopics(result.getEncryptedTopics()); + + mHandler.post( + new Runnable() { + @Override + public void run() { + mResultTextView.append( + sdkName + + "'s topics: " + + NEWLINE + + topics + + NEWLINE); + mResultTextView.append( + sdkName + + "'s encrypted topics," + + " decrypted: " + + NEWLINE + + encryptedTopicsDecrypted + + NEWLINE); + } + }); - mHandler.post(new Runnable() { - @Override - public void run() { - mResultTextView.append( + Log.d( + TAG, sdkName + "'s topics: " + NEWLINE + topics + NEWLINE); - } - }); - Log.d( TAG, sdkName - + "'s topics: " + + "'s encrypted topics, decrypted: " + NEWLINE - + topics + + encryptedTopicsDecrypted + NEWLINE); } @@ -124,17 +152,18 @@ public class MainActivity extends AppCompatActivity { + ": " + t.getMessage()); - mHandler.post(new Runnable() { - @Override - public void run() { - mResultTextView.append( - "Failed to getTopics for sdk " - + sdkName - + ": " - + t.toString() - + NEWLINE); - } - }); + mHandler.post( + new Runnable() { + @Override + public void run() { + mResultTextView.append( + "Failed to getTopics for sdk " + + sdkName + + ": " + + t.toString() + + NEWLINE); + } + }); } }, directExecutor()); @@ -150,4 +179,19 @@ public class MainActivity extends AppCompatActivity { } return sb.toString(); } + + private String getDecryptedTopics(List<EncryptedTopic> arr) { + StringBuilder sb = new StringBuilder(); + int index = 1; + for (EncryptedTopic encryptedTopic : arr) { + byte[] cipherText = + Bytes.concat( + encryptedTopic.getEncapsulatedKey(), + encryptedTopic.getEncryptedTopic()); + byte[] decryptedText = + HpkeJni.decrypt(DECODED_PRIVATE_KEY, cipherText, EMPTY_CONTEXT_INFO); + sb.append(index++).append(". ").append(new String(decryptedText)).append(NEWLINE); + } + return sb.toString(); + } } diff --git a/adservices/service-core/Android.bp b/adservices/service-core/Android.bp index be523ebc5..e74241e52 100644 --- a/adservices/service-core/Android.bp +++ b/adservices/service-core/Android.bp @@ -201,7 +201,10 @@ cc_library_shared { shared_libs: ["liblog",], static_libs: ["libact", "libcrypto_static"], version_script: "jni/version-script.lds", - apex_available: ["com.android.adservices", "com.android.extservices"], + apex_available: [ + "//apex_available:platform", + "com.android.adservices", + "com.android.extservices"], visibility: [ "//packages/modules/AdServices:__subpackages__", "//packages/modules/ExtServices:__subpackages__", diff --git a/adservices/service-core/java/com/android/adservices/data/adselection/EncryptionKeyDao.java b/adservices/service-core/java/com/android/adservices/data/adselection/EncryptionKeyDao.java index 6b32c9f41..bd0ac8b4b 100644 --- a/adservices/service-core/java/com/android/adservices/data/adselection/EncryptionKeyDao.java +++ b/adservices/service-core/java/com/android/adservices/data/adselection/EncryptionKeyDao.java @@ -24,7 +24,7 @@ import androidx.room.Query; import java.time.Instant; import java.util.List; -/** Dao to manage access to entities in the EncryptionKey table. */ +/** Dao to manage access to entities in the Auction Server Encryption Key table. */ @Dao public abstract class EncryptionKeyDao { /** diff --git a/adservices/service-core/java/com/android/adservices/data/measurement/MeasurementDao.java b/adservices/service-core/java/com/android/adservices/data/measurement/MeasurementDao.java index 4722f4f1d..78f7c18b5 100644 --- a/adservices/service-core/java/com/android/adservices/data/measurement/MeasurementDao.java +++ b/adservices/service-core/java/com/android/adservices/data/measurement/MeasurementDao.java @@ -2761,6 +2761,9 @@ class MeasurementDao implements IMeasurementDao { values.put( MeasurementTables.AsyncRegistrationContract.REQUEST_POST_BODY, asyncRegistration.getPostBody()); + values.put( + MeasurementTables.AsyncRegistrationContract.REDIRECT_BEHAVIOR, + asyncRegistration.getRedirectBehavior().name()); long rowId = mSQLTransaction .getDatabase() diff --git a/adservices/service-core/java/com/android/adservices/data/measurement/MeasurementDbHelper.java b/adservices/service-core/java/com/android/adservices/data/measurement/MeasurementDbHelper.java index 30697186a..398f4f661 100644 --- a/adservices/service-core/java/com/android/adservices/data/measurement/MeasurementDbHelper.java +++ b/adservices/service-core/java/com/android/adservices/data/measurement/MeasurementDbHelper.java @@ -50,6 +50,7 @@ import com.android.adservices.data.measurement.migration.MeasurementDbMigratorV2 import com.android.adservices.data.measurement.migration.MeasurementDbMigratorV28; import com.android.adservices.data.measurement.migration.MeasurementDbMigratorV29; import com.android.adservices.data.measurement.migration.MeasurementDbMigratorV30; +import com.android.adservices.data.measurement.migration.MeasurementDbMigratorV31; import com.android.adservices.data.measurement.migration.MeasurementDbMigratorV7; import com.android.adservices.data.measurement.migration.MeasurementDbMigratorV8; import com.android.adservices.data.measurement.migration.MeasurementDbMigratorV9; @@ -69,7 +70,7 @@ import java.util.stream.Stream; public class MeasurementDbHelper extends SQLiteOpenHelper { private static final String DATABASE_NAME = FileCompatUtils.getAdservicesFilename("adservices_msmt.db"); - public static final int CURRENT_DATABASE_VERSION = 30; + public static final int CURRENT_DATABASE_VERSION = 31; public static final int OLD_DATABASE_FINAL_VERSION = 6; private static MeasurementDbHelper sSingleton = null; @@ -171,7 +172,8 @@ public class MeasurementDbHelper extends SQLiteOpenHelper { new MeasurementDbMigratorV27(), new MeasurementDbMigratorV28(), new MeasurementDbMigratorV29(), - new MeasurementDbMigratorV30()); + new MeasurementDbMigratorV30(), + new MeasurementDbMigratorV31()); } private boolean hasAllV6MeasurementTables(SQLiteDatabase db) { diff --git a/adservices/service-core/java/com/android/adservices/data/measurement/MeasurementTables.java b/adservices/service-core/java/com/android/adservices/data/measurement/MeasurementTables.java index 8e249191e..2ffd42877 100644 --- a/adservices/service-core/java/com/android/adservices/data/measurement/MeasurementTables.java +++ b/adservices/service-core/java/com/android/adservices/data/measurement/MeasurementTables.java @@ -93,6 +93,7 @@ public final class MeasurementTables { String REGISTRATION_ID = "registration_id"; String PLATFORM_AD_ID = "platform_ad_id"; String REQUEST_POST_BODY = "request_post_body"; + String REDIRECT_BEHAVIOR = "redirect_behavior"; } /** Contract for Source. */ @@ -327,39 +328,41 @@ public final class MeasurementTables { public static final String CREATE_TABLE_ASYNC_REGISTRATION_LATEST = "CREATE TABLE " - + MeasurementTables.AsyncRegistrationContract.TABLE + + AsyncRegistrationContract.TABLE + " (" - + MeasurementTables.AsyncRegistrationContract.ID + + AsyncRegistrationContract.ID + " TEXT PRIMARY KEY NOT NULL, " - + MeasurementTables.AsyncRegistrationContract.REGISTRATION_URI + + AsyncRegistrationContract.REGISTRATION_URI + " TEXT, " - + MeasurementTables.AsyncRegistrationContract.WEB_DESTINATION + + AsyncRegistrationContract.WEB_DESTINATION + " TEXT, " - + MeasurementTables.AsyncRegistrationContract.OS_DESTINATION + + AsyncRegistrationContract.OS_DESTINATION + " TEXT, " - + MeasurementTables.AsyncRegistrationContract.VERIFIED_DESTINATION + + AsyncRegistrationContract.VERIFIED_DESTINATION + " TEXT, " - + MeasurementTables.AsyncRegistrationContract.TOP_ORIGIN + + AsyncRegistrationContract.TOP_ORIGIN + " TEXT, " - + MeasurementTables.AsyncRegistrationContract.SOURCE_TYPE + + AsyncRegistrationContract.SOURCE_TYPE + " INTEGER, " - + MeasurementTables.AsyncRegistrationContract.REGISTRANT + + AsyncRegistrationContract.REGISTRANT + " TEXT, " - + MeasurementTables.AsyncRegistrationContract.REQUEST_TIME + + AsyncRegistrationContract.REQUEST_TIME + " INTEGER, " - + MeasurementTables.AsyncRegistrationContract.RETRY_COUNT + + AsyncRegistrationContract.RETRY_COUNT + " INTEGER, " - + MeasurementTables.AsyncRegistrationContract.TYPE + + AsyncRegistrationContract.TYPE + " INTEGER, " - + MeasurementTables.AsyncRegistrationContract.DEBUG_KEY_ALLOWED + + AsyncRegistrationContract.DEBUG_KEY_ALLOWED + " INTEGER, " - + MeasurementTables.AsyncRegistrationContract.AD_ID_PERMISSION + + AsyncRegistrationContract.AD_ID_PERMISSION + " INTEGER, " - + MeasurementTables.AsyncRegistrationContract.REGISTRATION_ID + + AsyncRegistrationContract.REGISTRATION_ID + " TEXT NOT NULL," - + MeasurementTables.AsyncRegistrationContract.PLATFORM_AD_ID + + AsyncRegistrationContract.PLATFORM_AD_ID + " TEXT, " + AsyncRegistrationContract.REQUEST_POST_BODY + + " TEXT, " + + AsyncRegistrationContract.REDIRECT_BEHAVIOR + " TEXT " + ")"; diff --git a/adservices/service-core/java/com/android/adservices/data/measurement/SqliteObjectMapper.java b/adservices/service-core/java/com/android/adservices/data/measurement/SqliteObjectMapper.java index 671b5db98..cb5aee7e5 100644 --- a/adservices/service-core/java/com/android/adservices/data/measurement/SqliteObjectMapper.java +++ b/adservices/service-core/java/com/android/adservices/data/measurement/SqliteObjectMapper.java @@ -27,6 +27,7 @@ import com.android.adservices.service.measurement.Source; import com.android.adservices.service.measurement.Trigger; import com.android.adservices.service.measurement.aggregation.AggregateEncryptionKey; import com.android.adservices.service.measurement.aggregation.AggregateReport; +import com.android.adservices.service.measurement.registration.AsyncRedirect; import com.android.adservices.service.measurement.registration.AsyncRegistration; import com.android.adservices.service.measurement.reporting.DebugReport; import com.android.adservices.service.measurement.util.UnsignedLong; @@ -471,6 +472,14 @@ public class SqliteObjectMapper { cursor, MeasurementTables.AsyncRegistrationContract.REQUEST_POST_BODY, builder::setPostBody); + setTextColumn( + cursor, + MeasurementTables.AsyncRegistrationContract.REDIRECT_BEHAVIOR, + (enumValue) -> + builder.setRedirectBehavior( + enumValue == null + ? null + : AsyncRedirect.RedirectBehavior.valueOf(enumValue))); return builder.build(); } diff --git a/adservices/service-core/java/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV31.java b/adservices/service-core/java/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV31.java new file mode 100644 index 000000000..72f150e0d --- /dev/null +++ b/adservices/service-core/java/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV31.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2023 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.data.measurement.migration; + +import android.annotation.NonNull; +import android.content.ContentValues; +import android.database.sqlite.SQLiteDatabase; + +import com.android.adservices.LoggerFactory; +import com.android.adservices.data.measurement.MeasurementTables; +import com.android.adservices.service.measurement.registration.AsyncRedirect; + +import java.util.Locale; + +/** + * Migrates Measurement DB to version 31. This upgrade adds one column in the async registration + * table to persist the configuration for redirecting Location type redirects to .well-known path. + */ +public class MeasurementDbMigratorV31 extends AbstractMeasurementDbMigrator { + public MeasurementDbMigratorV31() { + super(31); + } + + @Override + protected void performMigration(@NonNull SQLiteDatabase db) { + addRedirectBehaviorColumn(db); + updateRedirectBehaviorForAllAsyncRegistrations(db); + } + + private void addRedirectBehaviorColumn(SQLiteDatabase db) { + MigrationHelpers.addTextColumnIfAbsent( + db, + MeasurementTables.AsyncRegistrationContract.TABLE, + MeasurementTables.AsyncRegistrationContract.REDIRECT_BEHAVIOR); + } + + private void updateRedirectBehaviorForAllAsyncRegistrations(@NonNull SQLiteDatabase db) { + ContentValues values = new ContentValues(); + values.put( + MeasurementTables.AsyncRegistrationContract.REDIRECT_BEHAVIOR, + AsyncRedirect.RedirectBehavior.AS_IS.name()); + long rows = + db.update( + MeasurementTables.AsyncRegistrationContract.TABLE, + values, + null, + new String[0]); + String log = + String.format( + Locale.ENGLISH, + "Updated %s for %d %s records", + MeasurementTables.AsyncRegistrationContract.REDIRECT_BEHAVIOR, + rows, + MeasurementTables.AsyncRegistrationContract.TABLE); + LoggerFactory.getMeasurementLogger().d(log); + } +} diff --git a/adservices/service-core/java/com/android/adservices/data/signals/DBSignalsUpdateMetadata.java b/adservices/service-core/java/com/android/adservices/data/signals/DBSignalsUpdateMetadata.java new file mode 100644 index 000000000..67204d719 --- /dev/null +++ b/adservices/service-core/java/com/android/adservices/data/signals/DBSignalsUpdateMetadata.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2023 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.data.signals; + +import android.adservices.common.AdTechIdentifier; + +import androidx.annotation.NonNull; +import androidx.room.ColumnInfo; +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +import com.google.auto.value.AutoValue; + +import java.time.Instant; + +/** Represents an entry for signals update metadata. */ +@AutoValue +@AutoValue.CopyAnnotations +@Entity(tableName = DBSignalsUpdateMetadata.TABLE_NAME, inheritSuperIndices = true) +public abstract class DBSignalsUpdateMetadata { + + public static final String TABLE_NAME = "signals_update_metadata"; + + /** The ad-tech buyer */ + @AutoValue.CopyAnnotations + @ColumnInfo(name = "buyer") + @PrimaryKey + @NonNull + public abstract AdTechIdentifier getBuyer(); + + /** The last time update happened to a buyer's signals */ + @AutoValue.CopyAnnotations + @ColumnInfo(name = "last_signals_updated_time") + public abstract Instant getLastSignalsUpdatedTime(); + + /** + * @return an instance of {@link DBSignalsUpdateMetadata} + */ + public static DBSignalsUpdateMetadata create( + @NonNull AdTechIdentifier buyer, @NonNull Instant lastSignalsUpdatedTime) { + return builder().setBuyer(buyer).setLastSignalsUpdatedTime(lastSignalsUpdatedTime).build(); + } + + /** + * @return a builder for creating a {@link DBSignalsUpdateMetadata} + */ + public static DBSignalsUpdateMetadata.Builder builder() { + return new AutoValue_DBSignalsUpdateMetadata.Builder(); + } + + /** Provides a builder to create an instance of {@link DBSignalsUpdateMetadata} */ + @AutoValue.Builder + public abstract static class Builder { + + /** For more details see {@link #getBuyer()} */ + public abstract DBSignalsUpdateMetadata.Builder setBuyer(@NonNull AdTechIdentifier value); + + /** For more details see {@link #getLastSignalsUpdatedTime()} ()} */ + public abstract DBSignalsUpdateMetadata.Builder setLastSignalsUpdatedTime( + @NonNull Instant value); + + /** + * @return an instance of {@link DBSignalsUpdateMetadata} + */ + public abstract DBSignalsUpdateMetadata build(); + } +} diff --git a/adservices/service-core/java/com/android/adservices/data/signals/EncoderLogicHandler.java b/adservices/service-core/java/com/android/adservices/data/signals/EncoderLogicHandler.java index 0c21c6f57..10f2770a4 100644 --- a/adservices/service-core/java/com/android/adservices/data/signals/EncoderLogicHandler.java +++ b/adservices/service-core/java/com/android/adservices/data/signals/EncoderLogicHandler.java @@ -61,6 +61,7 @@ public class EncoderLogicHandler { @NonNull private final EncoderPersistenceDao mEncoderPersistenceDao; @NonNull private final EncoderEndpointsDao mEncoderEndpointsDao; @NonNull private final EncoderLogicMetadataDao mEncoderLogicMetadataDao; + @NonNull private final ProtectedSignalsDao mProtectedSignalsDao; @NonNull private final AdServicesHttpsClient mAdServicesHttpsClient; @NonNull private final ListeningExecutorService mBackgroundExecutorService; @@ -77,16 +78,19 @@ public class EncoderLogicHandler { @NonNull EncoderPersistenceDao encoderPersistenceDao, @NonNull EncoderEndpointsDao encoderEndpointsDao, @NonNull EncoderLogicMetadataDao encoderLogicMetadataDao, + @NonNull ProtectedSignalsDao protectedSignalsDao, @NonNull AdServicesHttpsClient httpsClient, @NonNull ListeningExecutorService backgroundExecutorService) { Objects.requireNonNull(encoderPersistenceDao); Objects.requireNonNull(encoderEndpointsDao); Objects.requireNonNull(encoderLogicMetadataDao); + Objects.requireNonNull(protectedSignalsDao); Objects.requireNonNull(httpsClient); Objects.requireNonNull(backgroundExecutorService); mEncoderPersistenceDao = encoderPersistenceDao; mEncoderEndpointsDao = encoderEndpointsDao; mEncoderLogicMetadataDao = encoderLogicMetadataDao; + mProtectedSignalsDao = protectedSignalsDao; mAdServicesHttpsClient = httpsClient; mBackgroundExecutorService = backgroundExecutorService; } @@ -96,6 +100,7 @@ public class EncoderLogicHandler { EncoderPersistenceDao.getInstance(context), ProtectedSignalsDatabase.getInstance(context).getEncoderEndpointsDao(), ProtectedSignalsDatabase.getInstance(context).getEncoderLogicMetadataDao(), + ProtectedSignalsDatabase.getInstance(context).protectedSignalsDao(), new AdServicesHttpsClient( AdServicesExecutors.getBackgroundExecutor(), CacheProviderFactory.createNoOpCache()), @@ -265,6 +270,7 @@ public class EncoderLogicHandler { mEncoderLogicMetadataDao.deleteEncoder(buyer); mEncoderPersistenceDao.deleteEncoder(buyer); mEncoderEndpointsDao.deleteEncoderEndpoint(buyer); + mProtectedSignalsDao.deleteSignalsUpdateMetadata(buyer); buyerLock.unlock(); } } diff --git a/adservices/service-core/java/com/android/adservices/data/signals/ProtectedSignalsDao.java b/adservices/service-core/java/com/android/adservices/data/signals/ProtectedSignalsDao.java index f30770821..d0a4a8169 100644 --- a/adservices/service-core/java/com/android/adservices/data/signals/ProtectedSignalsDao.java +++ b/adservices/service-core/java/com/android/adservices/data/signals/ProtectedSignalsDao.java @@ -23,12 +23,14 @@ import androidx.annotation.NonNull; import androidx.room.Dao; import androidx.room.Delete; import androidx.room.Insert; +import androidx.room.OnConflictStrategy; import androidx.room.Query; import androidx.room.Transaction; import com.android.adservices.data.common.CleanupUtils; import com.android.adservices.data.enrollment.EnrollmentDao; import com.android.adservices.service.Flags; +import com.android.internal.annotations.VisibleForTesting; import java.time.Instant; import java.util.List; @@ -76,10 +78,17 @@ public abstract class ProtectedSignalsDao { */ @Transaction public void insertAndDelete( + @NonNull AdTechIdentifier buyer, + @NonNull Instant now, @NonNull List<DBProtectedSignal> signalsToInsert, @NonNull List<DBProtectedSignal> signalsToDelete) { insertSignals(signalsToInsert); deleteSignals(signalsToDelete); + persistSignalsUpdateMetadata( + DBSignalsUpdateMetadata.builder() + .setBuyer(buyer) + .setLastSignalsUpdatedTime(now) + .build()); } /** @@ -88,7 +97,31 @@ public abstract class ProtectedSignalsDao { * @return the number of deleted signals */ @Query("DELETE FROM protected_signals WHERE creationTime < :expiryTime") - public abstract int deleteSignalsBeforeTime(@NonNull Instant expiryTime); + protected abstract int deleteSignalsBeforeTime(@NonNull Instant expiryTime); + + /** Returns buyers with expired signals. */ + @Query("SELECT DISTINCT buyer FROM protected_signals WHERE creationTime < :expiryTime") + protected abstract List<AdTechIdentifier> getBuyersWithExpiredSignals( + @NonNull Instant expiryTime); + + /** + * Deletes expired signals and updates buyer metadata. + * + * @return the number of deleted signals + */ + @Transaction + public int deleteExpiredSignalsAndUpdateSignalsUpdateMetadata( + @NonNull Instant expiryTime, @NonNull Instant now) { + List<AdTechIdentifier> buyers = getBuyersWithExpiredSignals(expiryTime); + for (AdTechIdentifier buyer : buyers) { + persistSignalsUpdateMetadata( + DBSignalsUpdateMetadata.builder() + .setBuyer(buyer) + .setLastSignalsUpdatedTime(now) + .build()); + } + return deleteSignalsBeforeTime(expiryTime); + } /** * Deletes all signals belonging to disallowed buyer ad techs in a single transaction, where the @@ -111,6 +144,9 @@ public abstract class ProtectedSignalsDao { int numDeletedEvents = 0; if (!buyersToRemove.isEmpty()) { numDeletedEvents = deleteByBuyers(buyersToRemove); + for (AdTechIdentifier buyer : buyersToRemove) { + deleteSignalsUpdateMetadata(buyer); + } } return numDeletedEvents; @@ -141,10 +177,11 @@ public abstract class ProtectedSignalsDao { * @return the number of deleted signals */ @Transaction - public int deleteAllDisallowedPackageSignals( - @NonNull PackageManager packageManager, @NonNull Flags flags) { + public int deleteAllDisallowedPackageSignalsAndUpdateSignalUpdateMetadata( + @NonNull PackageManager packageManager, @NonNull Flags flags, @NonNull Instant now) { Objects.requireNonNull(packageManager); Objects.requireNonNull(flags); + Objects.requireNonNull(now); List<String> sourceAppsToRemove = getAllPackages(); if (sourceAppsToRemove.isEmpty()) { @@ -155,6 +192,14 @@ public abstract class ProtectedSignalsDao { int numDeletedEvents = 0; if (!sourceAppsToRemove.isEmpty()) { + List<AdTechIdentifier> buyers = getBuyersForPackages(sourceAppsToRemove); + for (AdTechIdentifier buyer : buyers) { + persistSignalsUpdateMetadata( + DBSignalsUpdateMetadata.builder() + .setBuyer(buyer) + .setLastSignalsUpdatedTime(now) + .build()); + } numDeletedEvents = deleteSignalsByPackage(sourceAppsToRemove); // TODO(b/300661099): Collect and send telemetry on signal deletion } @@ -165,7 +210,8 @@ public abstract class ProtectedSignalsDao { * Returns the list of all unique packages in the signals table. * * <p>This method is not meant to be called externally, but is a helper for {@link - * #deleteAllDisallowedPackageSignals} + * #deleteAllDisallowedPackageSignalsAndUpdateSignalUpdateMetadata(PackageManager, Flags, + * Instant)} */ @Query("SELECT DISTINCT packageName FROM protected_signals") protected abstract List<String> getAllPackages(); @@ -174,10 +220,33 @@ public abstract class ProtectedSignalsDao { * Deletes all signals generated from the given packages. * * <p>This method is not meant to be called externally, but is a helper for {@link - * #deleteAllDisallowedPackageSignals} + * #deleteAllDisallowedPackageSignalsAndUpdateSignalUpdateMetadata(PackageManager, Flags, + * Instant)} * * @return the number of deleted histogram events */ @Query("DELETE FROM protected_signals WHERE packageName in (:packages)") protected abstract int deleteSignalsByPackage(@NonNull List<String> packages); + + /** Returns all buyers for the given packages. */ + @Query("SELECT DISTINCT buyer FROM protected_signals WHERE packageName in (:packages)") + protected abstract List<AdTechIdentifier> getBuyersForPackages(@NonNull List<String> packages); + + /** Create or update a buyer metadata entry. */ + @Insert(entity = DBSignalsUpdateMetadata.class, onConflict = OnConflictStrategy.REPLACE) + @VisibleForTesting + protected abstract long persistSignalsUpdateMetadata( + DBSignalsUpdateMetadata dbSignalsUpdateMetadata); + + /** Returns a metadata entry according to the buyer. */ + @Query("SELECT * FROM signals_update_metadata WHERE buyer=:buyer") + public abstract DBSignalsUpdateMetadata getSignalsUpdateMetadata(AdTechIdentifier buyer); + + /** Delete the metadata for the buyer. */ + @Query("DELETE FROM signals_update_metadata WHERE buyer=:buyer") + public abstract void deleteSignalsUpdateMetadata(AdTechIdentifier buyer); + + /** Delete all metadata in the storage. */ + @Query("DELETE FROM signals_update_metadata") + public abstract void deleteAllSignalsUpdateMetadata(); } diff --git a/adservices/service-core/java/com/android/adservices/data/signals/ProtectedSignalsDatabase.java b/adservices/service-core/java/com/android/adservices/data/signals/ProtectedSignalsDatabase.java index ee6b3bc26..d068e2a48 100644 --- a/adservices/service-core/java/com/android/adservices/data/signals/ProtectedSignalsDatabase.java +++ b/adservices/service-core/java/com/android/adservices/data/signals/ProtectedSignalsDatabase.java @@ -35,18 +35,20 @@ import java.util.Objects; DBProtectedSignal.class, DBEncoderEndpoint.class, DBEncoderLogicMetadata.class, - DBEncodedPayload.class + DBEncodedPayload.class, + DBSignalsUpdateMetadata.class, }, autoMigrations = { @AutoMigration(from = 1, to = 2), @AutoMigration(from = 2, to = 3), + @AutoMigration(from = 3, to = 4) }, version = ProtectedSignalsDatabase.DATABASE_VERSION) @TypeConverters({FledgeRoomConverters.class}) public abstract class ProtectedSignalsDatabase extends RoomDatabase { private static final Object SINGLETON_LOCK = new Object(); - public static final int DATABASE_VERSION = 3; + public static final int DATABASE_VERSION = 4; public static final String DATABASE_NAME = FileCompatUtils.getAdservicesFilename("protectedsignals.db"); diff --git a/adservices/service-core/java/com/android/adservices/service/Flags.java b/adservices/service-core/java/com/android/adservices/service/Flags.java index 200ccec1f..774504370 100644 --- a/adservices/service-core/java/com/android/adservices/service/Flags.java +++ b/adservices/service-core/java/com/android/adservices/service/Flags.java @@ -117,6 +117,18 @@ public interface Flags extends CommonFlags { } /** + * Flag to override base64 public key used for encryption testing. + * + * <p>Note: Default value for this flag should not be changed from empty. + */ + String TOPICS_TEST_ENCRYPTION_PUBLIC_KEY = ""; + + /** Returns test public key used for encrypting topics for testing. */ + default String getTopicsTestEncryptionPublicKey() { + return TOPICS_TEST_ENCRYPTION_PUBLIC_KEY; + } + + /** * Returns the number of epochs to look back when deciding if a caller has observed a topic * before. */ @@ -697,7 +709,7 @@ public interface Flags extends CommonFlags { return MEASUREMENT_FLEX_API_MAX_INFORMATION_GAIN_EVENT; } - float MEASUREMENT_FLEX_API_MAX_INFORMATION_GAIN_NAVIGATION = 11.46173F; + float MEASUREMENT_FLEX_API_MAX_INFORMATION_GAIN_NAVIGATION = 11.5F; /** Returns max information gain in Flexible Event API for Navigation sources */ default float getMeasurementFlexApiMaxInformationGainNavigation() { @@ -3419,14 +3431,6 @@ public interface Flags extends CommonFlags { return DEFAULT_ADSERVICES_VERSION_MAPPINGS; } - /** Default Determines whether EU notification flow change is enabled. */ - boolean DEFAULT_EU_NOTIF_FLOW_CHANGE_ENABLED = true; - - /** Determines whether EU notification flow change is enabled. */ - default boolean getEuNotifFlowChangeEnabled() { - return DEFAULT_EU_NOTIF_FLOW_CHANGE_ENABLED; - } - /** Default value for Measurement flexible event reporting API */ boolean MEASUREMENT_FLEXIBLE_EVENT_REPORTING_API_ENABLED = false; @@ -3518,14 +3522,6 @@ public interface Flags extends CommonFlags { return MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS; } - /** Disable early reporting windows configurability by default. */ - boolean MEASUREMENT_ENABLE_CONFIGURABLE_EVENT_REPORTING_WINDOWS = false; - - /** Returns true if event reporting windows configurability is enabled, false otherwise. */ - default boolean getMeasurementEnableConfigurableEventReportingWindows() { - return MEASUREMENT_ENABLE_CONFIGURABLE_EVENT_REPORTING_WINDOWS; - } - /** * Default early reporting windows for VTC type source. Derived from {@link * com.android.adservices.service.measurement.PrivacyParams#EVENT_EARLY_REPORTING_WINDOW_MILLISECONDS}. @@ -3585,18 +3581,8 @@ public interface Flags extends CommonFlags { return MEASUREMENT_AGGREGATE_REPORT_DELAY_CONFIG; } - /** Disable conversions configurability by default. */ - boolean DEFAULT_MEASUREMENT_ENABLE_VTC_CONFIGURABLE_MAX_EVENT_REPORTS = false; - - /** - * Returns true, if event reports max conversions configurability is enabled, false otherwise. - */ - default boolean getMeasurementEnableVtcConfigurableMaxEventReports() { - return DEFAULT_MEASUREMENT_ENABLE_VTC_CONFIGURABLE_MAX_EVENT_REPORTS; - } - - /** Disable conversions configurability by default. */ - int DEFAULT_MEASUREMENT_VTC_CONFIGURABLE_MAX_EVENT_REPORTS_COUNT = 2; + /** Default max allowed number of event reports. */ + int DEFAULT_MEASUREMENT_VTC_CONFIGURABLE_MAX_EVENT_REPORTS_COUNT = 1; /** Returns the default max allowed number of event reports. */ default int getMeasurementVtcConfigurableMaxEventReportsCount() { @@ -3979,11 +3965,11 @@ public interface Flags extends CommonFlags { } /** Default RVC NOTIFICATION feature flag.. */ - boolean DEFAULT_RVC_NOTIFICATION_ENABLED = false; + boolean DEFAULT_RVC_POST_OTA_NOTIFICATION_ENABLED = false; /** RVC Notification feature flag.. */ - default boolean getEnableRvcNotification() { - return DEFAULT_RVC_NOTIFICATION_ENABLED; + default boolean getEnableRvcPostOtaNotification() { + return DEFAULT_RVC_POST_OTA_NOTIFICATION_ENABLED; } /** Default enableAdServices system API feature flag.. */ @@ -4100,76 +4086,6 @@ public interface Flags extends CommonFlags { return MEASUREMENT_MIN_REPORTING_ORIGIN_UPDATE_WINDOW; } - float MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY = 0.0000208f; - - /** - * {@link Source} Noise probability for 'Event' when both destinations (app and web) are - * available on the source and supports install attribution. - */ - default float getMeasurementInstallAttrDualDestinationEventNoiseProbability() { - return MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY; - } - - float MEASUREMENT_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY = 0.0170218f; - - /** - * {@link Source} Noise probability for 'Navigation' when both destinations (app and web) are - * available on the source. - */ - default float getMeasurementDualDestinationNavigationNoiseProbability() { - return MEASUREMENT_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY; - } - - float MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY = - MEASUREMENT_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY; - - /** - * {@link Source} Noise probability for 'Navigation' when both destinations (app and web) are - * available on the source and supports install attribution. - */ - default float getMeasurementInstallAttrDualDestinationNavigationNoiseProbability() { - return MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY; - } - - float MEASUREMENT_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY = 0.0000042f; - - /** - * {@link Source} Noise probability for 'Event' when both destinations (app and web) are - * available on the source. - */ - default float getMeasurementDualDestinationEventNoiseProbability() { - return MEASUREMENT_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY; - } - - float MEASUREMENT_INSTALL_ATTR_EVENT_NOISE_PROBABILITY = 0.0000125f; - - /** {@link Source} Noise probability for 'Event' which supports install attribution. */ - default float getMeasurementInstallAttrEventNoiseProbability() { - return MEASUREMENT_INSTALL_ATTR_EVENT_NOISE_PROBABILITY; - } - - float MEASUREMENT_EVENT_NOISE_PROBABILITY = 0.0000025f; - - /** {@link Source} Noise probability for 'Event'. */ - default float getMeasurementEventNoiseProbability() { - return MEASUREMENT_EVENT_NOISE_PROBABILITY; - } - - float MEASUREMENT_NAVIGATION_NOISE_PROBABILITY = 0.0024263f; - - /** {@link Source} Noise probability for 'Navigation'. */ - default float getMeasurementNavigationNoiseProbability() { - return MEASUREMENT_NAVIGATION_NOISE_PROBABILITY; - } - - float MEASUREMENT_INSTALL_ATTR_NAVIGATION_NOISE_PROBABILITY = - MEASUREMENT_NAVIGATION_NOISE_PROBABILITY; - - /** {@link Source} Noise probability for 'Navigation' which supports install attribution. */ - default float getMeasurementInstallAttrNavigationNoiseProbability() { - return MEASUREMENT_INSTALL_ATTR_NAVIGATION_NOISE_PROBABILITY; - } - boolean MEASUREMENT_ENABLE_PREINSTALL_CHECK = false; /** Returns true when pre-install check is enabled. */ @@ -4281,6 +4197,16 @@ public interface Flags extends CommonFlags { } /** + * Flag to control whether redirect registration urls should be modified to prefix the path + * string with .well-known + */ + boolean MEASUREMENT_ENABLE_REDIRECT_TO_WELL_KNOWN_PATH = false; + + default boolean getMeasurementEnableRedirectToWellKnownPath() { + return MEASUREMENT_ENABLE_REDIRECT_TO_WELL_KNOWN_PATH; + } + + /** * Default whether to limit logging for enrollment metrics to avoid performance issues. This * includes not logging data that requires database queries and downloading MDD files. */ @@ -4499,4 +4425,28 @@ public interface Flags extends CommonFlags { default int getBackgroundJobSamplingLoggingRate() { return DEFAULT_BACKGROUND_JOB_SAMPLING_LOGGING_RATE; } + + /** Default value of the timeout for AppSearch write operations */ + int DEFAULT_APPSEARCH_WRITE_TIMEOUT_MS = 3000; + + /** + * Gets the value of the timeout for AppSearch write operations, in milliseconds. + * + * @return the timeout, in milliseconds, for AppSearch write operations + */ + default int getAppSearchWriteTimeout() { + return DEFAULT_APPSEARCH_WRITE_TIMEOUT_MS; + } + + /** Default value of the timeout for AppSearch read operations */ + int DEFAULT_APPSEARCH_READ_TIMEOUT_MS = 500; + + /** + * Gets the value of the timeout for AppSearch read operations, in milliseconds. + * + * @return the timeout, in milliseconds, for AppSearch read operations + */ + default int getAppSearchReadTimeout() { + return DEFAULT_APPSEARCH_READ_TIMEOUT_MS; + } } diff --git a/adservices/service-core/java/com/android/adservices/service/FlagsConstants.java b/adservices/service-core/java/com/android/adservices/service/FlagsConstants.java index 56ca5efe2..cb408cefc 100644 --- a/adservices/service-core/java/com/android/adservices/service/FlagsConstants.java +++ b/adservices/service-core/java/com/android/adservices/service/FlagsConstants.java @@ -94,6 +94,8 @@ public final class FlagsConstants { public static final String KEY_TOPICS_ENCRYPTION_ENABLED = "topics_encryption_enabled"; public static final String KEY_TOPICS_DISABLE_PLAINTEXT_RESPONSE = "topics_disable_plaintext_response"; + public static final String KEY_TOPICS_TEST_ENCRYPTION_PUBLIC_KEY = + "topics_test_encryption_public_key"; // Topics classifier keys public static final String KEY_CLASSIFIER_TYPE = "classifier_type"; @@ -284,9 +286,6 @@ public final class FlagsConstants { public static final String KEY_MEASUREMENT_ENABLE_COARSE_EVENT_REPORT_DESTINATIONS = "measurement_enable_coarse_event_report_destinations"; - public static final String KEY_MEASUREMENT_ENABLE_VTC_CONFIGURABLE_MAX_EVENT_REPORTS = - "measurement_enable_vtc_configurable_max_event_reports_count"; - public static final String KEY_MEASUREMENT_VTC_CONFIGURABLE_MAX_EVENT_REPORTS_COUNT = "measurement_vtc_configurable_max_event_reports_count"; @@ -811,6 +810,8 @@ public final class FlagsConstants { public static final String KEY_PPAPI_APP_SIGNATURE_ALLOW_LIST = "ppapi_app_signature_allow_list"; + public static final String KEY_APPSEARCH_WRITE_TIMEOUT_MS = "appsearch_write_timeout_ms"; + public static final String KEY_APPSEARCH_READ_TIMEOUT_MS = "appsearch_read_timeout_ms"; public static final String KEY_APPSEARCH_WRITER_ALLOW_LIST_OVERRIDE = "appsearch_writer_allow_list_override"; @@ -1069,9 +1070,6 @@ public final class FlagsConstants { public static final String KEY_MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS = "measurement_min_event_report_delay_millis"; - public static final String KEY_MEASUREMENT_ENABLE_CONFIGURABLE_EVENT_REPORTING_WINDOWS = - "measurement_enable_configurable_event_reporting_windows"; - public static final String KEY_MEASUREMENT_EVENT_REPORTS_VTC_EARLY_REPORTING_WINDOWS = "measurement_event_reports_vtc_early_reporting_windows"; @@ -1134,31 +1132,8 @@ public final class FlagsConstants { public static final String KEY_MEASUREMENT_ENABLE_API_STATUS_ALLOW_LIST_CHECK = "measurement_enable_api_status_allow_list_check"; - public static final String - KEY_MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY = - "measurement_install_attr_dual_destination_event_noise_probability"; - - public static final String KEY_MEASUREMENT_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY = - "measurement_dual_destination_navigation_noise_probability"; - - public static final String - KEY_MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY = - "measurement_install_attr_dual_destination_navigation_noise_probability"; - - public static final String KEY_MEASUREMENT_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY = - "measurement_dual_destination_event_noise_probability"; - - public static final String KEY_MEASUREMENT_INSTALL_ATTR_EVENT_NOISE_PROBABILITY = - "measurement_install_attr_event_noise_probability"; - - public static final String KEY_MEASUREMENT_INSTALL_ATTR_NAVIGATION_NOISE_PROBABILITY = - "measurement_install_attr_navigation_noise_probability"; - - public static final String KEY_MEASUREMENT_EVENT_NOISE_PROBABILITY = - "measurement_event_noise_probability"; - - public static final String KEY_MEASUREMENT_NAVIGATION_NOISE_PROBABILITY = - "measurement_navigation_noise_probability"; + public static final String KEY_MEASUREMENT_ENABLE_REDIRECT_TO_WELL_KNOWN_PATH = + "measurement_enable_redirect_to_well_known_path"; // Database Schema Version Flags public static final String KEY_ENABLE_DATABASE_SCHEMA_VERSION_8 = @@ -1166,8 +1141,6 @@ public final class FlagsConstants { public static final String KEY_ENABLE_DATABASE_SCHEMA_VERSION_9 = "enable_database_schema_version_9"; - public static final String KEY_EU_NOTIF_FLOW_CHANGE_ENABLED = "eu_notif_flow_change_enabled"; - public static final String KEY_NOTIFICATION_DISMISSED_ON_CLICK = "notification_dmsmissed_on_click"; @@ -1175,7 +1148,8 @@ public final class FlagsConstants { public static final String KEY_RVC_UX_ENABLED = "rvc_ux_enabled"; - public static final String KEY_RVC_NOTIFICATION_ENABLED = "rvc_notification_enabled"; + public static final String KEY_RVC_POST_OTA_NOTIFICATION_ENABLED = + "rvc_post_ota_notification_enabled"; public static final String KEY_ENABLE_AD_SERVICES_SYSTEM_API = "enable_ad_services_system_api"; diff --git a/adservices/service-core/java/com/android/adservices/service/PhFlags.java b/adservices/service-core/java/com/android/adservices/service/PhFlags.java index 28b572092..70948e5dd 100644 --- a/adservices/service-core/java/com/android/adservices/service/PhFlags.java +++ b/adservices/service-core/java/com/android/adservices/service/PhFlags.java @@ -16,6 +16,8 @@ package com.android.adservices.service; +import static com.android.adservices.service.FlagsConstants.KEY_APPSEARCH_READ_TIMEOUT_MS; +import static com.android.adservices.service.FlagsConstants.KEY_APPSEARCH_WRITE_TIMEOUT_MS; import static com.android.adservices.service.FlagsConstants.KEY_ENCRYPTION_KEY_JOB_PERIOD_MS; import static com.android.adservices.service.FlagsConstants.KEY_ENCRYPTION_KEY_JOB_REQUIRED_NETWORK_TYPE; import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_MEASUREMENT_REPORT_AND_REGISTER_EVENT_API_ENABLED; @@ -245,6 +247,15 @@ public final class PhFlags extends CommonPhFlags implements Flags { } @Override + public String getTopicsTestEncryptionPublicKey() { + // The priority of applying the flag values: PH (DeviceConfig) and then hard-coded value. + return DeviceConfig.getString( + FlagsConstants.NAMESPACE_ADSERVICES, + /* flagName */ FlagsConstants.KEY_TOPICS_TEST_ENCRYPTION_PUBLIC_KEY, + /* defaultValue */ TOPICS_TEST_ENCRYPTION_PUBLIC_KEY); + } + + @Override public int getClassifierType() { // The priority of applying the flag values: SystemProperties, PH (DeviceConfig), then // hard-coded value. @@ -3472,16 +3483,6 @@ public final class PhFlags extends CommonPhFlags implements Flags { } @Override - public boolean getMeasurementEnableConfigurableEventReportingWindows() { - // The priority of applying the flag values: PH (DeviceConfig) and then hard-coded value. - return DeviceConfig.getBoolean( - FlagsConstants.NAMESPACE_ADSERVICES, - /* flagName */ FlagsConstants - .KEY_MEASUREMENT_ENABLE_CONFIGURABLE_EVENT_REPORTING_WINDOWS, - /* defaultValue */ MEASUREMENT_ENABLE_CONFIGURABLE_EVENT_REPORTING_WINDOWS); - } - - @Override public String getMeasurementEventReportsVtcEarlyReportingWindows() { // The priority of applying the flag values: PH (DeviceConfig) and then hard-coded value. return DeviceConfig.getString( @@ -3567,6 +3568,14 @@ public final class PhFlags extends CommonPhFlags implements Flags { } @Override + public boolean getMeasurementEnableRedirectToWellKnownPath() { + return DeviceConfig.getBoolean( + FlagsConstants.NAMESPACE_ADSERVICES, + /* flagName */ FlagsConstants.KEY_MEASUREMENT_ENABLE_REDIRECT_TO_WELL_KNOWN_PATH, + /* defaultValue */ MEASUREMENT_ENABLE_REDIRECT_TO_WELL_KNOWN_PATH); + } + + @Override public boolean getFledgeMeasurementReportAndRegisterEventApiEnabled() { return DeviceConfig.getBoolean( FlagsConstants.NAMESPACE_ADSERVICES, @@ -4281,49 +4290,6 @@ public final class PhFlags extends CommonPhFlags implements Flags { + getMeasurementMinReportingOriginUpdateWindow()); writer.println( "\t" - + FlagsConstants - .KEY_MEASUREMENT_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY - + " = " - + getMeasurementDualDestinationNavigationNoiseProbability()); - writer.println( - "\t" - + FlagsConstants - .KEY_MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY - + " = " - + getMeasurementInstallAttrDualDestinationEventNoiseProbability()); - writer.println( - "\t" - + FlagsConstants - .KEY_MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY - + " = " - + getMeasurementInstallAttrDualDestinationNavigationNoiseProbability()); - writer.println( - "\t" - + FlagsConstants.KEY_MEASUREMENT_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY - + " = " - + getMeasurementDualDestinationEventNoiseProbability()); - writer.println( - "\t" - + FlagsConstants.KEY_MEASUREMENT_INSTALL_ATTR_EVENT_NOISE_PROBABILITY - + " = " - + getMeasurementInstallAttrEventNoiseProbability()); - writer.println( - "\t" - + FlagsConstants.KEY_MEASUREMENT_INSTALL_ATTR_NAVIGATION_NOISE_PROBABILITY - + " = " - + getMeasurementInstallAttrNavigationNoiseProbability()); - writer.println( - "\t" - + FlagsConstants.KEY_MEASUREMENT_EVENT_NOISE_PROBABILITY - + " = " - + getMeasurementEventNoiseProbability()); - writer.println( - "\t" - + FlagsConstants.KEY_MEASUREMENT_NAVIGATION_NOISE_PROBABILITY - + " = " - + getMeasurementNavigationNoiseProbability()); - writer.println( - "\t" + FlagsConstants.KEY_MEASUREMENT_REGISTRATION_JOB_QUEUE_KILL_SWITCH + " = " + getAsyncRegistrationJobQueueKillSwitch()); @@ -4369,11 +4335,6 @@ public final class PhFlags extends CommonPhFlags implements Flags { + getMeasurementMinEventReportDelayMillis()); writer.println( "\t" - + FlagsConstants.KEY_MEASUREMENT_ENABLE_CONFIGURABLE_EVENT_REPORTING_WINDOWS - + " = " - + getMeasurementEnableConfigurableEventReportingWindows()); - writer.println( - "\t" + FlagsConstants.KEY_MEASUREMENT_EVENT_REPORTS_VTC_EARLY_REPORTING_WINDOWS + " = " + getMeasurementEventReportsVtcEarlyReportingWindows()); @@ -5154,11 +5115,6 @@ public final class PhFlags extends CommonPhFlags implements Flags { writer.println("==== AdServices PH Flags Dump UI Related Flags ===="); writer.println( "\t" - + FlagsConstants.KEY_EU_NOTIF_FLOW_CHANGE_ENABLED - + " = " - + getEuNotifFlowChangeEnabled()); - writer.println( - "\t" + FlagsConstants.KEY_UI_FEATURE_TYPE_LOGGING_ENABLED + " = " + isUiFeatureTypeLoggingEnabled()); @@ -5500,6 +5456,8 @@ public final class PhFlags extends CommonPhFlags implements Flags { + KEY_MEASUREMENT_ENABLE_SESSION_STABLE_KILL_SWITCHES + " = " + getMeasurementEnableSessionStableKillSwitches()); + writer.println("\t" + KEY_APPSEARCH_WRITE_TIMEOUT_MS + " = " + getAppSearchWriteTimeout()); + writer.println("\t" + KEY_APPSEARCH_READ_TIMEOUT_MS + " = " + getAppSearchReadTimeout()); } @VisibleForTesting @@ -5667,14 +5625,6 @@ public final class PhFlags extends CommonPhFlags implements Flags { } @Override - public boolean getEuNotifFlowChangeEnabled() { - return DeviceConfig.getBoolean( - FlagsConstants.NAMESPACE_ADSERVICES, - /* flagName */ FlagsConstants.KEY_EU_NOTIF_FLOW_CHANGE_ENABLED, - /* defaultValue */ DEFAULT_EU_NOTIF_FLOW_CHANGE_ENABLED); - } - - @Override public boolean getNotificationDismissedOnClick() { return DeviceConfig.getBoolean( FlagsConstants.NAMESPACE_ADSERVICES, @@ -5701,12 +5651,12 @@ public final class PhFlags extends CommonPhFlags implements Flags { } @Override - public boolean getEnableRvcNotification() { + public boolean getEnableRvcPostOtaNotification() { return getEnableAdServicesSystemApi() && DeviceConfig.getBoolean( FlagsConstants.NAMESPACE_ADSERVICES, - /* flagName */ FlagsConstants.KEY_RVC_NOTIFICATION_ENABLED, - /* defaultValue */ DEFAULT_RVC_NOTIFICATION_ENABLED); + /* flagName */ FlagsConstants.KEY_RVC_POST_OTA_NOTIFICATION_ENABLED, + /* defaultValue */ DEFAULT_RVC_POST_OTA_NOTIFICATION_ENABLED); } @Override @@ -5729,7 +5679,9 @@ public final class PhFlags extends CommonPhFlags implements Flags { getRecordManualInteractionEnabled()); uxMap.put(FlagsConstants.KEY_GA_UX_FEATURE_ENABLED, getGaUxFeatureEnabled()); uxMap.put(FlagsConstants.KEY_RVC_UX_ENABLED, getEnableRvcUx()); - uxMap.put(FlagsConstants.KEY_RVC_NOTIFICATION_ENABLED, getEnableRvcNotification()); + uxMap.put( + FlagsConstants.KEY_RVC_POST_OTA_NOTIFICATION_ENABLED, + getEnableRvcPostOtaNotification()); uxMap.put( FlagsConstants.KEY_UI_OTA_STRINGS_FEATURE_ENABLED, getUiOtaStringsFeatureEnabled()); uxMap.put( @@ -5742,7 +5694,6 @@ public final class PhFlags extends CommonPhFlags implements Flags { uxMap.put( FlagsConstants.KEY_CONSENT_NOTIFICATION_ACTIVITY_DEBUG_MODE, getConsentNotificationActivityDebugMode()); - uxMap.put(FlagsConstants.KEY_EU_NOTIF_FLOW_CHANGE_ENABLED, getEuNotifFlowChangeEnabled()); uxMap.put(FlagsConstants.KEY_U18_UX_ENABLED, getU18UxEnabled()); uxMap.put( FlagsConstants.KEY_NOTIFICATION_DISMISSED_ON_CLICK, @@ -5863,83 +5814,6 @@ public final class PhFlags extends CommonPhFlags implements Flags { } @Override - public float getMeasurementInstallAttrDualDestinationEventNoiseProbability() { - // The priority of applying the flag values: PH (DeviceConfig) and then hard-coded value. - return DeviceConfig.getFloat( - FlagsConstants.NAMESPACE_ADSERVICES, - /* flagName */ FlagsConstants - .KEY_MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY, - /* defaultValue */ MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY); - } - - @Override - public float getMeasurementDualDestinationNavigationNoiseProbability() { - // The priority of applying the flag values: PH (DeviceConfig) and then hard-coded value. - return DeviceConfig.getFloat( - FlagsConstants.NAMESPACE_ADSERVICES, - /* flagName */ FlagsConstants - .KEY_MEASUREMENT_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY, - /* defaultValue */ MEASUREMENT_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY); - } - - @Override - public float getMeasurementInstallAttrDualDestinationNavigationNoiseProbability() { - // The priority of applying the flag values: PH (DeviceConfig) and then hard-coded value. - return DeviceConfig.getFloat( - FlagsConstants.NAMESPACE_ADSERVICES, - /* flagName */ FlagsConstants - .KEY_MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY, - /* defaultValue */ MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY); - } - - @Override - public float getMeasurementDualDestinationEventNoiseProbability() { - // The priority of applying the flag values: PH (DeviceConfig) and then hard-coded value. - return DeviceConfig.getFloat( - FlagsConstants.NAMESPACE_ADSERVICES, - /* flagName */ FlagsConstants - .KEY_MEASUREMENT_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY, - /* defaultValue */ MEASUREMENT_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY); - } - - @Override - public float getMeasurementInstallAttrEventNoiseProbability() { - // The priority of applying the flag values: PH (DeviceConfig) and then hard-coded value. - return DeviceConfig.getFloat( - FlagsConstants.NAMESPACE_ADSERVICES, - /* flagName */ FlagsConstants.KEY_MEASUREMENT_INSTALL_ATTR_EVENT_NOISE_PROBABILITY, - /* defaultValue */ MEASUREMENT_INSTALL_ATTR_EVENT_NOISE_PROBABILITY); - } - - @Override - public float getMeasurementInstallAttrNavigationNoiseProbability() { - // The priority of applying the flag values: PH (DeviceConfig) and then hard-coded value. - return DeviceConfig.getFloat( - FlagsConstants.NAMESPACE_ADSERVICES, - /* flagName */ FlagsConstants - .KEY_MEASUREMENT_INSTALL_ATTR_NAVIGATION_NOISE_PROBABILITY, - /* defaultValue */ MEASUREMENT_INSTALL_ATTR_NAVIGATION_NOISE_PROBABILITY); - } - - @Override - public float getMeasurementEventNoiseProbability() { - // The priority of applying the flag values: PH (DeviceConfig) and then hard-coded value. - return DeviceConfig.getFloat( - FlagsConstants.NAMESPACE_ADSERVICES, - /* flagName */ FlagsConstants.KEY_MEASUREMENT_EVENT_NOISE_PROBABILITY, - /* defaultValue */ MEASUREMENT_EVENT_NOISE_PROBABILITY); - } - - @Override - public float getMeasurementNavigationNoiseProbability() { - // The priority of applying the flag values: PH (DeviceConfig) and then hard-coded value. - return DeviceConfig.getFloat( - FlagsConstants.NAMESPACE_ADSERVICES, - /* flagName */ FlagsConstants.KEY_MEASUREMENT_NAVIGATION_NOISE_PROBABILITY, - /* defaultValue */ MEASUREMENT_NAVIGATION_NOISE_PROBABILITY); - } - - @Override public boolean getMeasurementEnablePreinstallCheck() { // The priority of applying the flag values: PH (DeviceConfig) and then hard-coded value. return DeviceConfig.getBoolean( @@ -5949,16 +5823,6 @@ public final class PhFlags extends CommonPhFlags implements Flags { } @Override - public boolean getMeasurementEnableVtcConfigurableMaxEventReports() { - return DeviceConfig.getBoolean( - FlagsConstants.NAMESPACE_ADSERVICES, - /* flagName */ FlagsConstants - .KEY_MEASUREMENT_ENABLE_VTC_CONFIGURABLE_MAX_EVENT_REPORTS, - /* defaultValue */ - DEFAULT_MEASUREMENT_ENABLE_VTC_CONFIGURABLE_MAX_EVENT_REPORTS); - } - - @Override public int getMeasurementVtcConfigurableMaxEventReportsCount() { return DeviceConfig.getInt( FlagsConstants.NAMESPACE_ADSERVICES, @@ -6526,4 +6390,20 @@ public final class PhFlags extends CommonPhFlags implements Flags { return loggingRatio; } + + @Override + public int getAppSearchWriteTimeout() { + return DeviceConfig.getInt( + FlagsConstants.NAMESPACE_ADSERVICES, + /* name= */ FlagsConstants.KEY_APPSEARCH_WRITE_TIMEOUT_MS, + /* defaultValue= */ DEFAULT_APPSEARCH_WRITE_TIMEOUT_MS); + } + + @Override + public int getAppSearchReadTimeout() { + return DeviceConfig.getInt( + FlagsConstants.NAMESPACE_ADSERVICES, + /* name= */ FlagsConstants.KEY_APPSEARCH_READ_TIMEOUT_MS, + /* defaultValue= */ DEFAULT_APPSEARCH_READ_TIMEOUT_MS); + } } diff --git a/adservices/service-core/java/com/android/adservices/service/adselection/AdSelectionRunner.java b/adservices/service-core/java/com/android/adservices/service/adselection/AdSelectionRunner.java index c337bf17b..22eea4b05 100644 --- a/adservices/service-core/java/com/android/adservices/service/adselection/AdSelectionRunner.java +++ b/adservices/service-core/java/com/android/adservices/service/adselection/AdSelectionRunner.java @@ -49,7 +49,10 @@ import com.android.adservices.data.adselection.datahandlers.AdSelectionResultBid import com.android.adservices.data.adselection.datahandlers.WinningCustomAudience; import com.android.adservices.data.customaudience.CustomAudienceDao; import com.android.adservices.data.customaudience.DBCustomAudience; +import com.android.adservices.data.encryptionkey.EncryptionKeyDao; +import com.android.adservices.data.enrollment.EnrollmentDao; import com.android.adservices.service.Flags; +import com.android.adservices.service.adselection.signature.ProtectedAudienceSignatureManager; import com.android.adservices.service.common.AdSelectionServiceFilter; import com.android.adservices.service.common.FrequencyCapAdDataValidator; import com.android.adservices.service.common.Throttler; @@ -76,6 +79,7 @@ import com.google.common.util.concurrent.UncheckedTimeoutException; import java.time.Clock; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -123,8 +127,11 @@ public abstract class AdSelectionRunner { static final String ON_DEVICE_AUCTION_KILL_SWITCH_ENABLED = "On device auction kill switch enabled"; + @NonNull protected final Context mContext; @NonNull protected final CustomAudienceDao mCustomAudienceDao; @NonNull protected final AdSelectionEntryDao mAdSelectionEntryDao; + @NonNull protected final EncryptionKeyDao mEncryptionKeyDao; + @NonNull protected final EnrollmentDao mEnrollmentDao; @NonNull protected final ListeningExecutorService mLightweightExecutorService; @NonNull protected final ListeningExecutorService mBackgroundExecutorService; @NonNull protected final ScheduledThreadPoolExecutor mScheduledExecutor; @@ -157,6 +164,8 @@ public abstract class AdSelectionRunner { @NonNull final Context context, @NonNull final CustomAudienceDao customAudienceDao, @NonNull final AdSelectionEntryDao adSelectionEntryDao, + @NonNull final EncryptionKeyDao encryptionKeyDao, + @NonNull final EnrollmentDao enrollmentDao, @NonNull final ExecutorService lightweightExecutorService, @NonNull final ExecutorService backgroundExecutorService, @NonNull final ScheduledThreadPoolExecutor scheduledExecutor, @@ -173,6 +182,8 @@ public abstract class AdSelectionRunner { Objects.requireNonNull(context); Objects.requireNonNull(customAudienceDao); Objects.requireNonNull(adSelectionEntryDao); + Objects.requireNonNull(encryptionKeyDao); + Objects.requireNonNull(enrollmentDao); Objects.requireNonNull(lightweightExecutorService); Objects.requireNonNull(backgroundExecutorService); Objects.requireNonNull(adServicesLogger); @@ -184,8 +195,11 @@ public abstract class AdSelectionRunner { Objects.requireNonNull(debugReporting); Objects.requireNonNull(adSelectionExecutionLogger); + mContext = context; mCustomAudienceDao = customAudienceDao; mAdSelectionEntryDao = adSelectionEntryDao; + mEncryptionKeyDao = encryptionKeyDao; + mEnrollmentDao = enrollmentDao; mLightweightExecutorService = MoreExecutors.listeningDecorator(lightweightExecutorService); mBackgroundExecutorService = MoreExecutors.listeningDecorator(backgroundExecutorService); mScheduledExecutor = scheduledExecutor; @@ -209,6 +223,8 @@ public abstract class AdSelectionRunner { @NonNull final Context context, @NonNull final CustomAudienceDao customAudienceDao, @NonNull final AdSelectionEntryDao adSelectionEntryDao, + @NonNull final EncryptionKeyDao encryptionKeyDao, + @NonNull final EnrollmentDao enrollmentDao, @NonNull final ExecutorService lightweightExecutorService, @NonNull final ExecutorService backgroundExecutorService, @NonNull final ScheduledThreadPoolExecutor scheduledExecutor, @@ -227,6 +243,8 @@ public abstract class AdSelectionRunner { Objects.requireNonNull(context); Objects.requireNonNull(customAudienceDao); Objects.requireNonNull(adSelectionEntryDao); + Objects.requireNonNull(encryptionKeyDao); + Objects.requireNonNull(enrollmentDao); Objects.requireNonNull(lightweightExecutorService); Objects.requireNonNull(backgroundExecutorService); Objects.requireNonNull(scheduledExecutor); @@ -240,8 +258,11 @@ public abstract class AdSelectionRunner { Objects.requireNonNull(adCounterHistogramUpdater); Objects.requireNonNull(debugReporting); + mContext = context; mCustomAudienceDao = customAudienceDao; mAdSelectionEntryDao = adSelectionEntryDao; + mEncryptionKeyDao = encryptionKeyDao; + mEnrollmentDao = enrollmentDao; mLightweightExecutorService = MoreExecutors.listeningDecorator(lightweightExecutorService); mBackgroundExecutorService = MoreExecutors.listeningDecorator(backgroundExecutorService); mScheduledExecutor = scheduledExecutor; @@ -277,7 +298,6 @@ public abstract class AdSelectionRunner { final int traceCookie = Tracing.beginAsyncSection(Tracing.RUN_AD_SELECTION); Objects.requireNonNull(inputParams); Objects.requireNonNull(callback); - AdSelectionConfig adSelectionConfig = inputParams.getAdSelectionConfig(); try { ListenableFuture<Void> filterAndValidateRequestFuture = @@ -501,7 +521,7 @@ public abstract class AdSelectionRunner { @NonNull final String callerPackageName) { sLogger.v("Beginning Ad Selection Orchestration"); - AdSelectionConfig adSelectionConfigInput = adSelectionConfig; + AdSelectionConfig adSelectionConfigInput; if (!mFlags.getFledgeAdSelectionContextualAdsEnabled()) { // Empty all contextual ads if the feature is disabled sLogger.v("Contextual flow is disabled"); @@ -594,9 +614,7 @@ public abstract class AdSelectionRunner { private int countBuyersRequested(@NonNull AdSelectionConfig adSelectionConfig) { Objects.requireNonNull(adSelectionConfig); - return adSelectionConfig.getCustomAudienceBuyers().stream() - .collect(Collectors.toSet()) - .size(); + return new HashSet<>(adSelectionConfig.getCustomAudienceBuyers()).size(); } private int countBuyersFromCustomAudiences( @@ -728,10 +746,27 @@ public abstract class AdSelectionRunner { AdSelectionConfig adSelectionConfig) { Map<AdTechIdentifier, SignedContextualAds> filteredContextualAdsMap = new HashMap<>(); sLogger.v("Filtering contextual ads in Ad Selection Config"); + boolean isEnrollmentCheckEnabled = !mFlags.getDisableFledgeEnrollmentCheck(); + ProtectedAudienceSignatureManager signatureManager = + new ProtectedAudienceSignatureManager( + mEnrollmentDao, mEncryptionKeyDao, isEnrollmentCheckEnabled); for (Map.Entry<AdTechIdentifier, SignedContextualAds> entry : adSelectionConfig.getBuyerSignedContextualAds().entrySet()) { + if (!signatureManager.isVerified(entry.getKey(), entry.getValue())) { + sLogger.v( + "Contextual ads for buyer: '%s' have an invalid signature and will be" + + " removed from the auction", + entry.getKey()); + continue; + } filteredContextualAdsMap.put( entry.getKey(), mAdFilterer.filterContextualAds(entry.getValue())); + sLogger.v( + "Buyer '%s' has a valid signature. It's contextual ads filtered from " + + "%s ad(s) to %s ad(s)", + entry.getKey(), + entry.getValue().getAdsWithBid().size(), + filteredContextualAdsMap.get(entry.getKey()).getAdsWithBid().size()); } return adSelectionConfig .cloneToBuilder() diff --git a/adservices/service-core/java/com/android/adservices/service/adselection/AdSelectionServiceImpl.java b/adservices/service-core/java/com/android/adservices/service/adselection/AdSelectionServiceImpl.java index 85bad0af0..9bff5dea6 100644 --- a/adservices/service-core/java/com/android/adservices/service/adselection/AdSelectionServiceImpl.java +++ b/adservices/service-core/java/com/android/adservices/service/adselection/AdSelectionServiceImpl.java @@ -71,12 +71,12 @@ import com.android.adservices.data.adselection.AdSelectionDebugReportingDatabase import com.android.adservices.data.adselection.AdSelectionEntryDao; import com.android.adservices.data.adselection.AdSelectionServerDatabase; import com.android.adservices.data.adselection.AppInstallDao; -import com.android.adservices.data.adselection.EncryptionContextDao; -import com.android.adservices.data.adselection.EncryptionKeyDao; import com.android.adservices.data.adselection.FrequencyCapDao; import com.android.adservices.data.adselection.SharedStorageDatabase; import com.android.adservices.data.customaudience.CustomAudienceDao; import com.android.adservices.data.customaudience.CustomAudienceDatabase; +import com.android.adservices.data.encryptionkey.EncryptionKeyDao; +import com.android.adservices.data.enrollment.EnrollmentDao; import com.android.adservices.data.signals.EncodedPayloadDao; import com.android.adservices.data.signals.ProtectedSignalsDatabase; import com.android.adservices.service.Flags; @@ -137,8 +137,8 @@ public class AdSelectionServiceImpl extends AdSelectionService.Stub { @NonNull private final CustomAudienceDao mCustomAudienceDao; @NonNull private final EncodedPayloadDao mEncodedPayloadDao; @NonNull private final FrequencyCapDao mFrequencyCapDao; - @NonNull private final EncryptionContextDao mEncryptionContextDao; @NonNull private final EncryptionKeyDao mEncryptionKeyDao; + @NonNull private final EnrollmentDao mEnrollmentDao; @NonNull private final AdServicesHttpsClient mAdServicesHttpsClient; @NonNull private final ExecutorService mLightweightExecutor; @NonNull private final ExecutorService mBackgroundExecutor; @@ -168,8 +168,8 @@ public class AdSelectionServiceImpl extends AdSelectionService.Stub { @NonNull CustomAudienceDao customAudienceDao, @NonNull EncodedPayloadDao encodedPayloadDao, @NonNull FrequencyCapDao frequencyCapDao, - @NonNull EncryptionContextDao encryptionContextDao, @NonNull EncryptionKeyDao encryptionKeyDao, + @NonNull EnrollmentDao enrollmentDao, @NonNull AdServicesHttpsClient adServicesHttpsClient, @NonNull DevContextFilter devContextFilter, @NonNull ExecutorService lightweightExecutorService, @@ -193,8 +193,8 @@ public class AdSelectionServiceImpl extends AdSelectionService.Stub { Objects.requireNonNull(customAudienceDao); Objects.requireNonNull(encodedPayloadDao); Objects.requireNonNull(frequencyCapDao); - Objects.requireNonNull(encryptionContextDao); Objects.requireNonNull(encryptionKeyDao); + Objects.requireNonNull(enrollmentDao); Objects.requireNonNull(adServicesHttpsClient); Objects.requireNonNull(devContextFilter); Objects.requireNonNull(lightweightExecutorService); @@ -213,8 +213,8 @@ public class AdSelectionServiceImpl extends AdSelectionService.Stub { mCustomAudienceDao = customAudienceDao; mEncodedPayloadDao = encodedPayloadDao; mFrequencyCapDao = frequencyCapDao; - mEncryptionContextDao = encryptionContextDao; mEncryptionKeyDao = encryptionKeyDao; + mEnrollmentDao = enrollmentDao; mAdServicesHttpsClient = adServicesHttpsClient; mDevContextFilter = devContextFilter; mLightweightExecutor = lightweightExecutorService; @@ -249,8 +249,8 @@ public class AdSelectionServiceImpl extends AdSelectionService.Stub { CustomAudienceDatabase.getInstance(context).customAudienceDao(), ProtectedSignalsDatabase.getInstance(context).getEncodedPayloadDao(), SharedStorageDatabase.getInstance(context).frequencyCapDao(), - AdSelectionServerDatabase.getInstance(context).encryptionContextDao(), - AdSelectionServerDatabase.getInstance(context).encryptionKeyDao(), + EncryptionKeyDao.getInstance(context), + EnrollmentDao.getInstance(context), new AdServicesHttpsClient( AdServicesExecutors.getBlockingExecutor(), CacheProviderFactory.create(context, FlagsFactory.getFlags())), @@ -332,7 +332,7 @@ public class AdSelectionServiceImpl extends AdSelectionService.Stub { } // Caller permissions must be checked in the binder thread, before anything else - mFledgeAuthorizationFilter.assertAppDeclaredCustomAudiencePermission( + mFledgeAuthorizationFilter.assertAppDeclaredPermission( mContext, inputParams.getCallerPackageName(), apiName); int callingUid = getCallingUid(apiName); @@ -370,7 +370,7 @@ public class AdSelectionServiceImpl extends AdSelectionService.Stub { } // Caller permissions must be checked in the binder thread, before anything else - mFledgeAuthorizationFilter.assertAppDeclaredCustomAudiencePermission( + mFledgeAuthorizationFilter.assertAppDeclaredPermission( mContext, inputParams.getCallerPackageName(), apiName); int callingUid = getCallingUid(apiName); @@ -473,7 +473,7 @@ public class AdSelectionServiceImpl extends AdSelectionService.Stub { } // Caller permissions must be checked in the binder thread, before anything else - mFledgeAuthorizationFilter.assertAppDeclaredCustomAudiencePermission( + mFledgeAuthorizationFilter.assertAppDeclaredPermission( mContext, inputParams.getCallerPackageName(), apiName); int callingUid = getCallingUid(apiName); @@ -674,6 +674,8 @@ public class AdSelectionServiceImpl extends AdSelectionService.Stub { mContext, mCustomAudienceDao, mAdSelectionEntryDao, + mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mLightweightExecutor, mBackgroundExecutor, @@ -709,6 +711,8 @@ public class AdSelectionServiceImpl extends AdSelectionService.Stub { mContext, mCustomAudienceDao, mAdSelectionEntryDao, + mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mLightweightExecutor, mBackgroundExecutor, @@ -758,7 +762,7 @@ public class AdSelectionServiceImpl extends AdSelectionService.Stub { } // Caller permissions must be checked in the binder thread, before anything else - mFledgeAuthorizationFilter.assertAppDeclaredCustomAudiencePermission( + mFledgeAuthorizationFilter.assertAppDeclaredPermission( mContext, inputParams.getCallerPackageName(), apiName); int callingUid = getCallingUid(apiName); @@ -801,7 +805,7 @@ public class AdSelectionServiceImpl extends AdSelectionService.Stub { } // Caller permissions must be checked in the binder thread, before anything else - mFledgeAuthorizationFilter.assertAppDeclaredCustomAudiencePermission( + mFledgeAuthorizationFilter.assertAppDeclaredPermission( mContext, requestParams.getCallerPackageName(), apiName); DevContext devContext = mDevContextFilter.createDevContext(); @@ -867,7 +871,7 @@ public class AdSelectionServiceImpl extends AdSelectionService.Stub { } // Caller permissions must be checked in the binder thread, before anything else - mFledgeAuthorizationFilter.assertAppDeclaredCustomAudiencePermission( + mFledgeAuthorizationFilter.assertAppDeclaredPermission( mContext, inputParams.getCallerPackageName(), apiName); int callerUid = getCallingUid(apiName); @@ -924,7 +928,7 @@ public class AdSelectionServiceImpl extends AdSelectionService.Stub { } // Caller permissions must be checked in the binder thread, before anything else - mFledgeAuthorizationFilter.assertAppDeclaredCustomAudiencePermission( + mFledgeAuthorizationFilter.assertAppDeclaredPermission( mContext, request.getCallerPackageName(), apiName); AppInstallAdvertisersSetter setter = @@ -956,7 +960,7 @@ public class AdSelectionServiceImpl extends AdSelectionService.Stub { } // Caller permissions must be checked in the binder thread, before anything else - mFledgeAuthorizationFilter.assertAppDeclaredCustomAudiencePermission( + mFledgeAuthorizationFilter.assertAppDeclaredPermission( mContext, inputParams.getCallerPackageName(), apiName); final int callingUid = getCallingUid(apiName); @@ -1029,7 +1033,7 @@ public class AdSelectionServiceImpl extends AdSelectionService.Stub { } // Caller permissions must be checked with a non-null callingAppPackageName - mFledgeAuthorizationFilter.assertAppDeclaredCustomAudiencePermission( + mFledgeAuthorizationFilter.assertAppDeclaredPermission( mContext, devContext.getCallingAppPackageName(), apiName); int callingUid = getCallingUid(apiName); @@ -1096,7 +1100,7 @@ public class AdSelectionServiceImpl extends AdSelectionService.Stub { } // Caller permissions must be checked with a non-null callingAppPackageName - mFledgeAuthorizationFilter.assertAppDeclaredCustomAudiencePermission( + mFledgeAuthorizationFilter.assertAppDeclaredPermission( mContext, devContext.getCallingAppPackageName(), apiName); int callingUid = getCallingUid(apiName); @@ -1146,7 +1150,7 @@ public class AdSelectionServiceImpl extends AdSelectionService.Stub { } // Caller permissions must be checked with a non-null callingAppPackageName - mFledgeAuthorizationFilter.assertAppDeclaredCustomAudiencePermission( + mFledgeAuthorizationFilter.assertAppDeclaredPermission( mContext, devContext.getCallingAppPackageName(), apiName); int callingUid = getCallingUid(apiName); @@ -1201,7 +1205,7 @@ public class AdSelectionServiceImpl extends AdSelectionService.Stub { } // Caller permissions must be checked with a non-null callingAppPackageName - mFledgeAuthorizationFilter.assertAppDeclaredCustomAudiencePermission( + mFledgeAuthorizationFilter.assertAppDeclaredPermission( mContext, devContext.getCallingAppPackageName(), apiName); int callingUid = getCallingUid(apiName); @@ -1252,7 +1256,7 @@ public class AdSelectionServiceImpl extends AdSelectionService.Stub { } // Caller permissions must be checked with a non-null callingAppPackageName - mFledgeAuthorizationFilter.assertAppDeclaredCustomAudiencePermission( + mFledgeAuthorizationFilter.assertAppDeclaredPermission( mContext, devContext.getCallingAppPackageName(), apiName); int callingUid = getCallingUid(apiName); @@ -1301,7 +1305,7 @@ public class AdSelectionServiceImpl extends AdSelectionService.Stub { } // Caller permissions must be checked with a non-null callingAppPackageName - mFledgeAuthorizationFilter.assertAppDeclaredCustomAudiencePermission( + mFledgeAuthorizationFilter.assertAppDeclaredPermission( mContext, devContext.getCallingAppPackageName(), apiName); int callingUid = getCallingUid(apiName); @@ -1351,7 +1355,7 @@ public class AdSelectionServiceImpl extends AdSelectionService.Stub { } // Caller permissions must be checked with a non-null callingAppPackageName - mFledgeAuthorizationFilter.assertAppDeclaredCustomAudiencePermission( + mFledgeAuthorizationFilter.assertAppDeclaredPermission( mContext, devContext.getCallingAppPackageName(), apiName); // TODO(b/265204820): Implement service @@ -1389,7 +1393,7 @@ public class AdSelectionServiceImpl extends AdSelectionService.Stub { } // Caller permissions must be checked with a non-null callingAppPackageName - mFledgeAuthorizationFilter.assertAppDeclaredCustomAudiencePermission( + mFledgeAuthorizationFilter.assertAppDeclaredPermission( mContext, devContext.getCallingAppPackageName(), apiName); // TODO(b/265204820): Implement service @@ -1424,7 +1428,7 @@ public class AdSelectionServiceImpl extends AdSelectionService.Stub { } // Caller permissions must be checked with a non-null callingAppPackageName - mFledgeAuthorizationFilter.assertAppDeclaredCustomAudiencePermission( + mFledgeAuthorizationFilter.assertAppDeclaredPermission( mContext, devContext.getCallingAppPackageName(), apiName); // TODO(b/265204820): Implement service diff --git a/adservices/service-core/java/com/android/adservices/service/adselection/OnDeviceAdSelectionRunner.java b/adservices/service-core/java/com/android/adservices/service/adselection/OnDeviceAdSelectionRunner.java index 4c5b280dd..e2a93966f 100644 --- a/adservices/service-core/java/com/android/adservices/service/adselection/OnDeviceAdSelectionRunner.java +++ b/adservices/service-core/java/com/android/adservices/service/adselection/OnDeviceAdSelectionRunner.java @@ -35,6 +35,8 @@ import com.android.adservices.data.adselection.AdSelectionEntryDao; import com.android.adservices.data.adselection.DBAdSelection; import com.android.adservices.data.customaudience.CustomAudienceDao; import com.android.adservices.data.customaudience.DBCustomAudience; +import com.android.adservices.data.encryptionkey.EncryptionKeyDao; +import com.android.adservices.data.enrollment.EnrollmentDao; import com.android.adservices.service.Flags; import com.android.adservices.service.common.AdSelectionServiceFilter; import com.android.adservices.service.common.BinderFlagReader; @@ -79,6 +81,8 @@ public class OnDeviceAdSelectionRunner extends AdSelectionRunner { @NonNull final Context context, @NonNull final CustomAudienceDao customAudienceDao, @NonNull final AdSelectionEntryDao adSelectionEntryDao, + @NonNull final EncryptionKeyDao encryptionKeyDao, + @NonNull final EnrollmentDao enrollmentDao, @NonNull final AdServicesHttpsClient adServicesHttpsClient, @NonNull final ExecutorService lightweightExecutorService, @NonNull final ExecutorService backgroundExecutorService, @@ -99,6 +103,8 @@ public class OnDeviceAdSelectionRunner extends AdSelectionRunner { context, customAudienceDao, adSelectionEntryDao, + encryptionKeyDao, + enrollmentDao, lightweightExecutorService, backgroundExecutorService, scheduledExecutor, @@ -172,6 +178,8 @@ public class OnDeviceAdSelectionRunner extends AdSelectionRunner { @NonNull final Context context, @NonNull final CustomAudienceDao customAudienceDao, @NonNull final AdSelectionEntryDao adSelectionEntryDao, + @NonNull final EncryptionKeyDao encryptionKeyDao, + @NonNull final EnrollmentDao enrollmentDao, @NonNull final AdServicesHttpsClient adServicesHttpsClient, @NonNull final ExecutorService lightweightExecutorService, @NonNull final ExecutorService backgroundExecutorService, @@ -195,6 +203,8 @@ public class OnDeviceAdSelectionRunner extends AdSelectionRunner { context, customAudienceDao, adSelectionEntryDao, + encryptionKeyDao, + enrollmentDao, lightweightExecutorService, backgroundExecutorService, scheduledExecutor, @@ -360,7 +370,10 @@ public class OnDeviceAdSelectionRunner extends AdSelectionRunner { .filter(Objects::nonNull) .collect(Collectors.toList())); } - + sLogger.v( + "Invoking score generator with %s bids and %s contextual ads.", + validBiddingOutcomes.size(), + adSelectionConfig.getBuyerSignedContextualAds().size()); return mAdsScoreGenerator .runAdScoring(validBiddingOutcomes, adSelectionConfig) .transform( diff --git a/adservices/service-core/java/com/android/adservices/service/adselection/TrustedServerAdSelectionRunner.java b/adservices/service-core/java/com/android/adservices/service/adselection/TrustedServerAdSelectionRunner.java index 190ebef74..b372378b9 100644 --- a/adservices/service-core/java/com/android/adservices/service/adselection/TrustedServerAdSelectionRunner.java +++ b/adservices/service-core/java/com/android/adservices/service/adselection/TrustedServerAdSelectionRunner.java @@ -34,6 +34,8 @@ import com.android.adservices.data.adselection.CustomAudienceSignals; import com.android.adservices.data.adselection.DBAdSelection; import com.android.adservices.data.customaudience.CustomAudienceDao; import com.android.adservices.data.customaudience.DBCustomAudience; +import com.android.adservices.data.encryptionkey.EncryptionKeyDao; +import com.android.adservices.data.enrollment.EnrollmentDao; import com.android.adservices.service.Flags; import com.android.adservices.service.common.AdRenderIdValidator; import com.android.adservices.service.common.AdSelectionServiceFilter; @@ -97,6 +99,8 @@ public class TrustedServerAdSelectionRunner extends AdSelectionRunner { @NonNull final Context context, @NonNull final CustomAudienceDao customAudienceDao, @NonNull final AdSelectionEntryDao adSelectionEntryDao, + @NonNull final EncryptionKeyDao encryptionKeyDao, + @NonNull final EnrollmentDao enrollmentDao, @NonNull final AdServicesHttpsClient adServicesHttpsClient, @NonNull final ExecutorService lightweightExecutorService, @NonNull final ExecutorService backgroundExecutorService, @@ -116,6 +120,8 @@ public class TrustedServerAdSelectionRunner extends AdSelectionRunner { context, customAudienceDao, adSelectionEntryDao, + encryptionKeyDao, + enrollmentDao, lightweightExecutorService, backgroundExecutorService, scheduledExecutor, @@ -146,6 +152,8 @@ public class TrustedServerAdSelectionRunner extends AdSelectionRunner { @NonNull final Context context, @NonNull final CustomAudienceDao customAudienceDao, @NonNull final AdSelectionEntryDao adSelectionEntryDao, + @NonNull final EncryptionKeyDao encryptionKeyDao, + @NonNull final EnrollmentDao enrollmentDao, @NonNull final ExecutorService lightweightExecutorService, @NonNull final ExecutorService backgroundExecutorService, @NonNull final ScheduledThreadPoolExecutor scheduledExecutor, @@ -165,6 +173,8 @@ public class TrustedServerAdSelectionRunner extends AdSelectionRunner { context, customAudienceDao, adSelectionEntryDao, + encryptionKeyDao, + enrollmentDao, lightweightExecutorService, backgroundExecutorService, scheduledExecutor, diff --git a/adservices/service-core/java/com/android/adservices/service/adselection/signature/ProtectedAudienceSignatureManager.java b/adservices/service-core/java/com/android/adservices/service/adselection/signature/ProtectedAudienceSignatureManager.java index 63a3d405a..ec5511f61 100644 --- a/adservices/service-core/java/com/android/adservices/service/adselection/signature/ProtectedAudienceSignatureManager.java +++ b/adservices/service-core/java/com/android/adservices/service/adselection/signature/ProtectedAudienceSignatureManager.java @@ -16,9 +16,9 @@ package com.android.adservices.service.adselection.signature; +import android.adservices.adselection.SignedContextualAds; import android.adservices.common.AdTechIdentifier; import android.annotation.NonNull; -import android.content.Context; import com.android.adservices.LoggerFactory; import com.android.adservices.data.encryptionkey.EncryptionKeyDao; @@ -28,6 +28,7 @@ import com.android.adservices.service.enrollment.EnrollmentData; import com.google.common.annotations.VisibleForTesting; +import java.util.Base64; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -41,32 +42,96 @@ import java.util.stream.Collectors; */ public class ProtectedAudienceSignatureManager { private static final LoggerFactory.Logger sLogger = LoggerFactory.getFledgeLogger(); - private final Context mContext; - private final EnrollmentDao mEnrollmentDao; - private final EncryptionKeyDao mEncryptionKeyDao; + /** + * This P-256 ECDSA key is used to verify signatures if {@link + * com.android.adservices.service.FlagsConstants #KEY_DISABLE_FLEDGE_ENROLLMENT_CHECK} is set to + * true. + * + * <p>This enables CTS and integration testing. + * + * <p>To test with this key, {@link SignedContextualAds} should be signed with {@link + * ProtectedAudienceSignatureManager#PRIVATE_TEST_KEY_STRING}. + */ + public static final String PUBLIC_TEST_KEY_STRING = + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+Eyo0TOllW8as2pTTzxawQ57pXJiH16VERgHqcV1/YpADt3iq6" + + "9vbhwW8Ksi3M0GrxacOuge/AwiM7Uh6+V3PA=="; - public ProtectedAudienceSignatureManager(@NonNull Context context) { - Objects.requireNonNull(context); + /** + * Private key pair of the {@link ProtectedAudienceSignatureManager#PUBLIC_TEST_KEY_STRING} + * + * <p>See {@link ProtectedAudienceSignatureManager#PUBLIC_TEST_KEY_STRING} + */ + public static final String PRIVATE_TEST_KEY_STRING = + "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgECetqRr9eE9DKKjILR+hP66Y1niEw/bqPD/MNx" + + "PTMvmhRANCAAT4TKjRM6WVbxqzalNPPFrBDnulcmIfXpURGAepxXX9ikAO3eKrr29uHBbwqyLczQ" + + "avFpw66B78DCIztSHr5Xc8"; - mContext = context; + @NonNull private final EnrollmentDao mEnrollmentDao; + @NonNull private final EncryptionKeyDao mEncryptionKeyDao; + private final boolean mIsEnrollmentCheckEnabled; - mEnrollmentDao = EnrollmentDao.getInstance(mContext); - mEncryptionKeyDao = EncryptionKeyDao.getInstance(mContext); + private final SignatureVerifier mSignatureVerifier; + + public ProtectedAudienceSignatureManager( + @NonNull EnrollmentDao enrollmentDao, + @NonNull EncryptionKeyDao encryptionKeyDao, + boolean isEnrollmentCheckEnabled) { + Objects.requireNonNull(enrollmentDao); + Objects.requireNonNull(encryptionKeyDao); + + mEnrollmentDao = enrollmentDao; + mEncryptionKeyDao = encryptionKeyDao; + mIsEnrollmentCheckEnabled = isEnrollmentCheckEnabled; + + mSignatureVerifier = new ECDSASignatureVerifier(); } @VisibleForTesting ProtectedAudienceSignatureManager( - @NonNull Context context, @NonNull EnrollmentDao enrollmentDao, - @NonNull EncryptionKeyDao encryptionKeyDao) { - mContext = context; + @NonNull EncryptionKeyDao encryptionKeyDao, + @NonNull SignatureVerifier signatureVerifier) { mEnrollmentDao = enrollmentDao; mEncryptionKeyDao = encryptionKeyDao; + mSignatureVerifier = signatureVerifier; + + mIsEnrollmentCheckEnabled = true; + } + + /** + * Returns whether is the given {@link SignedContextualAds} object is valid or not + * + * @param buyer Ad tech's identifier to resolve their public key + * @param signedContextualAds contextual ads object to verify + * @return true if the object is valid else false + */ + public boolean isVerified( + @NonNull AdTechIdentifier buyer, @NonNull SignedContextualAds signedContextualAds) { + Objects.requireNonNull(buyer); + Objects.requireNonNull(signedContextualAds); + + List<byte[]> publicKeys = fetchPublicKeyForAdTech(buyer); + boolean isVerified = false; + SignedContextualAdsHashUtil contextualAdsHashUtil; + for (byte[] publicKey : publicKeys) { + contextualAdsHashUtil = new SignedContextualAdsHashUtil(); + byte[] serialized = contextualAdsHashUtil.serialize(signedContextualAds); + isVerified = + mSignatureVerifier.verify( + publicKey, serialized, signedContextualAds.getSignature()); + } + return isVerified; } @VisibleForTesting - List<String> fetchPublicKeyForAdTech(AdTechIdentifier adTech) { + List<byte[]> fetchPublicKeyForAdTech(AdTechIdentifier adTech) { + Base64.Decoder decoder = Base64.getDecoder(); + if (!mIsEnrollmentCheckEnabled) { + sLogger.v("Enrollment check is disabled, returning the default key"); + return Collections.singletonList(decoder.decode(PUBLIC_TEST_KEY_STRING)); + } + sLogger.v("Fetching EnrollmentData for %s", adTech); EnrollmentData enrollmentData = mEnrollmentDao.getEnrollmentDataForFledgeByAdTechIdentifier(adTech); @@ -81,10 +146,10 @@ public class ProtectedAudienceSignatureManager { mEncryptionKeyDao.getEncryptionKeyFromEnrollmentIdAndKeyType( enrollmentData.getEnrollmentId(), EncryptionKey.KeyType.SIGNING); - sLogger.v("Received %s signing keys", encryptionKeys.size()); + sLogger.v("Received %s signing key(s)", encryptionKeys.size()); return encryptionKeys.stream() .sorted(Comparator.comparingLong(EncryptionKey::getExpiration)) - .map(EncryptionKey::getBody) + .map(key -> decoder.decode(key.getBody())) .collect(Collectors.toList()); } } diff --git a/adservices/service-core/java/com/android/adservices/service/adselection/signature/ThreadUnsafeByteArrayOutputStream.java b/adservices/service-core/java/com/android/adservices/service/adselection/signature/ThreadUnsafeByteArrayOutputStream.java index 4857768a6..a9918549b 100644 --- a/adservices/service-core/java/com/android/adservices/service/adselection/signature/ThreadUnsafeByteArrayOutputStream.java +++ b/adservices/service-core/java/com/android/adservices/service/adselection/signature/ThreadUnsafeByteArrayOutputStream.java @@ -23,13 +23,13 @@ import java.util.Arrays; /** ByteArrayStream implementation that reduces amount of copy operation */ public class ThreadUnsafeByteArrayOutputStream { private static final int ONE_KILOBYTE = 1024; - private static final int INITIAL_CAPACITY = 100 * ONE_KILOBYTE; + private static final int DEFAULT_INITIAL_CAPACITY = 100 * ONE_KILOBYTE; private static final int SOFT_MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8; private byte[] mBuffer; private int mCount; public ThreadUnsafeByteArrayOutputStream() { - this.mBuffer = new byte[INITIAL_CAPACITY]; + this.mBuffer = new byte[DEFAULT_INITIAL_CAPACITY]; this.mCount = 0; } diff --git a/adservices/service-core/java/com/android/adservices/service/appsearch/AppSearchConsentStorageManager.java b/adservices/service-core/java/com/android/adservices/service/appsearch/AppSearchConsentStorageManager.java index cbd867c0f..04c6f6030 100644 --- a/adservices/service-core/java/com/android/adservices/service/appsearch/AppSearchConsentStorageManager.java +++ b/adservices/service-core/java/com/android/adservices/service/appsearch/AppSearchConsentStorageManager.java @@ -412,8 +412,7 @@ public class AppSearchConsentStorageManager implements IConsentStorage { /** Saves the default consent by apiType. */ @Override - public void recordDefaultConsent(AdServicesApiType apiType, boolean defaultConsent) - throws IOException { + public void recordDefaultConsent(AdServicesApiType apiType, boolean defaultConsent) { mAppSearchConsentWorker.setConsent(apiType.toDefaultConsentDatastoreKey(), defaultConsent); } @@ -436,8 +435,7 @@ public class AppSearchConsentStorageManager implements IConsentStorage { } /** Saves information to the storage that user interacted with consent manually. */ - public void recordUserManualInteractionWithConsent( - @ConsentManager.UserManualInteraction int interaction) { + public void recordUserManualInteractionWithConsent(int interaction) { mAppSearchConsentWorker.recordUserManualInteractionWithConsent(interaction); } diff --git a/adservices/service-core/java/com/android/adservices/service/appsearch/AppSearchConsentWorker.java b/adservices/service-core/java/com/android/adservices/service/appsearch/AppSearchConsentWorker.java index 42c5c1ed8..bda9d1744 100644 --- a/adservices/service-core/java/com/android/adservices/service/appsearch/AppSearchConsentWorker.java +++ b/adservices/service-core/java/com/android/adservices/service/appsearch/AppSearchConsentWorker.java @@ -65,15 +65,11 @@ import java.util.stream.Collectors; * source of truth for S-. When a device upgrades from S- to T+, the consent is initialized from * AppSearch. */ -// TODO(b/269798827): Enable for R. @RequiresApi(Build.VERSION_CODES.S) class AppSearchConsentWorker { // At the worker level, we ensure that writes do not conflict with any other writes/reads. private static final ReadWriteLock READ_WRITE_LOCK = new ReentrantReadWriteLock(); - // Timeout for AppSearch write query in milliseconds. - private static final int TIMEOUT_MS = 2000; - private static final String CONSENT_DATABASE_NAME = "adservices_consent"; private static final String APP_CONSENT_DATABASE_NAME = "adservices_app_consent"; private static final String NOTIFICATION_DATABASE_NAME = "adservices_notification"; @@ -82,68 +78,69 @@ class AppSearchConsentWorker { private static final String UX_STATES_DATABASE_NAME = "adservices-ux-states"; // Required for allowing AdServices apk access to read consent written by ExtServices module. - private String mAdservicesPackageName; - private Context mContext; + private final String mAdservicesPackageName; - private ListenableFuture<AppSearchSession> mConsentSearchSession; - private ListenableFuture<AppSearchSession> mAppConsentSearchSession; - private ListenableFuture<AppSearchSession> mNotificationSearchSession; - private ListenableFuture<AppSearchSession> mInteractionsSearchSession; - private ListenableFuture<AppSearchSession> mTopicsSearchSession; - private ListenableFuture<AppSearchSession> mUxStatesSearchSession; + // Timeout for AppSearch write query in milliseconds. + private final int mTimeoutMs; + + private final ListenableFuture<AppSearchSession> mConsentSearchSession; + private final ListenableFuture<AppSearchSession> mAppConsentSearchSession; + private final ListenableFuture<AppSearchSession> mNotificationSearchSession; + private final ListenableFuture<AppSearchSession> mInteractionsSearchSession; + private final ListenableFuture<AppSearchSession> mTopicsSearchSession; + private final ListenableFuture<AppSearchSession> mUxStatesSearchSession; // When reading across APKs, a GlobalSearchSession is needed, hence we use it when reading. - private ListenableFuture<GlobalSearchSession> mGlobalSearchSession; - private Executor mExecutor = AdServicesExecutors.getBackgroundExecutor(); + private final ListenableFuture<GlobalSearchSession> mGlobalSearchSession; + private final Executor mExecutor = AdServicesExecutors.getBackgroundExecutor(); - private List<PackageIdentifier> mPackageIdentifiers = new ArrayList<>(); + private final List<PackageIdentifier> mPackageIdentifiers = new ArrayList<>(); // There is a single user ID for a given process, so this class would not be instantiated // across two user IDs. - private String mUid = getUserIdentifierFromBinderCallingUid(); + private final String mUid = getUserIdentifierFromBinderCallingUid(); private static final String SPLITTER = ","; private AppSearchConsentWorker(@NonNull Context context) { Objects.requireNonNull(context); - mContext = context; // We write with multiple schemas, so we need to initialize sessions per db. mConsentSearchSession = PlatformStorage.createSearchSessionAsync( - new PlatformStorage.SearchContext.Builder(mContext, CONSENT_DATABASE_NAME) + new PlatformStorage.SearchContext.Builder(context, CONSENT_DATABASE_NAME) .build()); mAppConsentSearchSession = PlatformStorage.createSearchSessionAsync( new PlatformStorage.SearchContext.Builder( - mContext, APP_CONSENT_DATABASE_NAME) + context, APP_CONSENT_DATABASE_NAME) .build()); mNotificationSearchSession = PlatformStorage.createSearchSessionAsync( new PlatformStorage.SearchContext.Builder( - mContext, NOTIFICATION_DATABASE_NAME) + context, NOTIFICATION_DATABASE_NAME) .build()); mInteractionsSearchSession = PlatformStorage.createSearchSessionAsync( new PlatformStorage.SearchContext.Builder( - mContext, INTERACTIONS_DATABASE_NAME) + context, INTERACTIONS_DATABASE_NAME) .build()); mTopicsSearchSession = PlatformStorage.createSearchSessionAsync( - new PlatformStorage.SearchContext.Builder(mContext, TOPICS_DATABASE_NAME) + new PlatformStorage.SearchContext.Builder(context, TOPICS_DATABASE_NAME) .build()); mUxStatesSearchSession = PlatformStorage.createSearchSessionAsync( - new PlatformStorage.SearchContext.Builder(mContext, UX_STATES_DATABASE_NAME) + new PlatformStorage.SearchContext.Builder(context, UX_STATES_DATABASE_NAME) .build()); // We use global session for reads since we may perform read on T+ AdServices package to // restore consent data post OTA. mGlobalSearchSession = PlatformStorage.createGlobalSearchSessionAsync( - new PlatformStorage.GlobalSearchContext.Builder(mContext).build()); + new PlatformStorage.GlobalSearchContext.Builder(context).build()); // The package identifier of the AdServices package on T+ should always have access to read // data written by AdExtServices package on S-. - mAdservicesPackageName = getAdServicesPackageName(mContext); + mAdservicesPackageName = getAdServicesPackageName(context); String shaCertsFlagValue = FlagsFactory.getFlags().getAdservicesApkShaCertificate(); for (String shaCert : shaCertsFlagValue.split(SPLITTER)) { @@ -151,6 +148,8 @@ class AppSearchConsentWorker { new PackageIdentifier( mAdservicesPackageName, new Signature(shaCert).toByteArray())); } + + mTimeoutMs = FlagsFactory.getFlags().getAppSearchWriteTimeout(); } /** Get an instance of AppSearchConsentWorker. */ @@ -175,8 +174,8 @@ class AppSearchConsentWorker { /** * Sets the consent for this user ID for this API type in AppSearch. If we do not get - * confirmation that the write was successful, then we throw an exception so that user does not - * incorrectly think that the consent is updated. + * confirmation that the write operation was successful, then we throw an exception so that user + * does not incorrectly think that the consent is updated. */ void setConsent(@NonNull String apiType, @NonNull Boolean consented) { Objects.requireNonNull(apiType); @@ -193,11 +192,11 @@ class AppSearchConsentWorker { apiType, consented.toString()); dao.writeData(mConsentSearchSession, mPackageIdentifiers, mExecutor) - .get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + .get(mTimeoutMs, TimeUnit.MILLISECONDS); LogUtil.d("Wrote consent data to AppSearch: " + dao); } catch (InterruptedException | TimeoutException | ExecutionException e) { - LogUtil.e("Failed to write consent to AppSearch ", e); - throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + LogUtil.e(e, "Failed to write consent to AppSearch"); + throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE, e); } finally { READ_WRITE_LOCK.writeLock().unlock(); } @@ -235,10 +234,10 @@ class AppSearchConsentWorker { mExecutor, AppSearchAppConsentDao.getRowId(mUid, consentType), AppSearchAppConsentDao.NAMESPACE) - .get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + .get(mTimeoutMs, TimeUnit.MILLISECONDS); } catch (InterruptedException | TimeoutException | ExecutionException e) { - LogUtil.e("Failed to delete consent to AppSearch ", e); - throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + LogUtil.e(e, "Failed to delete consent to AppSearch"); + throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE, e); } finally { READ_WRITE_LOCK.writeLock().unlock(); } @@ -280,11 +279,11 @@ class AppSearchConsentWorker { dao.setApps(apps); } dao.writeData(mAppConsentSearchSession, mPackageIdentifiers, mExecutor) - .get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + .get(mTimeoutMs, TimeUnit.MILLISECONDS); LogUtil.d("Wrote app consent data to AppSearch (add): " + dao); return true; } catch (InterruptedException | TimeoutException | ExecutionException e) { - LogUtil.e("Failed to write consent to AppSearch ", e); + LogUtil.e(e, "Failed to write consent to AppSearch"); return false; } finally { READ_WRITE_LOCK.writeLock().unlock(); @@ -293,8 +292,8 @@ class AppSearchConsentWorker { /** * Removes an app from the list of apps with this consentType for this user. If we do not get - * confirmation that the write was successful, then we throw an exception so that user does not - * incorrectly think that the consent is updated. + * confirmation that the write operation was successful, then we throw an exception so that user + * does not incorrectly think that the consent is updated. */ void removeAppWithConsent(@NonNull String consentType, @NonNull String app) { Objects.requireNonNull(consentType); @@ -320,11 +319,11 @@ class AppSearchConsentWorker { .filter(filterApp -> !filterApp.equals(app)) .collect(Collectors.toList())); dao.writeData(mAppConsentSearchSession, mPackageIdentifiers, mExecutor) - .get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + .get(mTimeoutMs, TimeUnit.MILLISECONDS); LogUtil.d("Wrote app consent data to AppSearch (remove): " + dao); } catch (InterruptedException | TimeoutException | ExecutionException e) { - LogUtil.e("Failed to write consent to AppSearch ", e); - throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + LogUtil.e(e, "Failed to write consent to AppSearch"); + throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE, e); } finally { READ_WRITE_LOCK.readLock().unlock(); } @@ -367,11 +366,11 @@ class AppSearchConsentWorker { /* wasNotificationDisplayed= */ wasNotificationDisplayed, /* wasGaUxNotificationDisplayed= */ wasGaUxNotificationDisplayed()); dao.writeData(mNotificationSearchSession, mPackageIdentifiers, mExecutor) - .get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + .get(mTimeoutMs, TimeUnit.MILLISECONDS); LogUtil.d("Wrote notification data to AppSearch: " + dao); } catch (InterruptedException | TimeoutException | ExecutionException e) { - LogUtil.e("Failed to write notification data to AppSearch ", e); - throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + LogUtil.e(e, "Failed to write notification data to AppSearch"); + throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE, e); } finally { READ_WRITE_LOCK.writeLock().unlock(); } @@ -389,11 +388,11 @@ class AppSearchConsentWorker { /* wasNotificationDisplayed= */ wasNotificationDisplayed(), /* wasGaUxNotificationDisplayed= */ wasNotificationDisplayed); dao.writeData(mNotificationSearchSession, mPackageIdentifiers, mExecutor) - .get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + .get(mTimeoutMs, TimeUnit.MILLISECONDS); LogUtil.d("Wrote notification data to AppSearch: " + dao); } catch (InterruptedException | TimeoutException | ExecutionException e) { - LogUtil.e("Failed to write notification data to AppSearch ", e); - throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + LogUtil.e(e, "Failed to write notification data to AppSearch"); + throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE, e); } finally { READ_WRITE_LOCK.writeLock().unlock(); } @@ -426,11 +425,11 @@ class AppSearchConsentWorker { apiType, currentFeatureType.ordinal()); dao.writeData(mInteractionsSearchSession, mPackageIdentifiers, mExecutor) - .get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + .get(mTimeoutMs, TimeUnit.MILLISECONDS); LogUtil.d("Wrote feature type data to AppSearch: " + dao); } catch (InterruptedException | TimeoutException | ExecutionException e) { - LogUtil.e("Failed to write interactions data to AppSearch ", e); - throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + LogUtil.e(e, "Failed to write interactions data to AppSearch"); + throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE, e); } finally { READ_WRITE_LOCK.writeLock().unlock(); } @@ -466,11 +465,11 @@ class AppSearchConsentWorker { apiType, interaction); dao.writeData(mInteractionsSearchSession, mPackageIdentifiers, mExecutor) - .get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + .get(mTimeoutMs, TimeUnit.MILLISECONDS); LogUtil.d("Wrote interactions data to AppSearch: " + dao); } catch (InterruptedException | TimeoutException | ExecutionException e) { - LogUtil.e("Failed to write interactions data to AppSearch ", e); - throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + LogUtil.e(e, "Failed to write interactions data to AppSearch"); + throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE, e); } finally { READ_WRITE_LOCK.writeLock().unlock(); } @@ -512,11 +511,11 @@ class AppSearchConsentWorker { dao.addBlockedTopic(topic); } dao.writeData(mTopicsSearchSession, mPackageIdentifiers, mExecutor) - .get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + .get(mTimeoutMs, TimeUnit.MILLISECONDS); LogUtil.d("Wrote topics consent data to AppSearch (block): " + dao); } catch (InterruptedException | TimeoutException | ExecutionException e) { - LogUtil.e("Failed to write consent to AppSearch ", e); - throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + LogUtil.e(e, "Failed to write consent to AppSearch"); + throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE, e); } finally { READ_WRITE_LOCK.writeLock().unlock(); } @@ -539,11 +538,11 @@ class AppSearchConsentWorker { } dao.removeBlockedTopic(topic); dao.writeData(mTopicsSearchSession, mPackageIdentifiers, mExecutor) - .get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + .get(mTimeoutMs, TimeUnit.MILLISECONDS); LogUtil.d("Wrote topics consent data to AppSearch (unblock): " + dao); } catch (InterruptedException | TimeoutException | ExecutionException e) { - LogUtil.e("Failed to write consent to AppSearch ", e); - throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + LogUtil.e(e, "Failed to write consent to AppSearch"); + throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE, e); } finally { READ_WRITE_LOCK.writeLock().unlock(); } @@ -554,7 +553,7 @@ class AppSearchConsentWorker { READ_WRITE_LOCK.writeLock().lock(); try { // We don't do {read, modify, write} here since the DAO has no other information besides - // blocked topics so we can rewrite it. + // blocked topics, so we can rewrite it. AppSearchTopicsConsentDao dao = new AppSearchTopicsConsentDao( mUid, @@ -564,11 +563,11 @@ class AppSearchConsentWorker { List.of(), List.of()); dao.writeData(mTopicsSearchSession, mPackageIdentifiers, mExecutor) - .get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + .get(mTimeoutMs, TimeUnit.MILLISECONDS); LogUtil.d("Wrote topics consent data to AppSearch (clear): " + dao); } catch (InterruptedException | TimeoutException | ExecutionException e) { - LogUtil.e("Failed to write consent to AppSearch ", e); - throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + LogUtil.e(e, "Failed to write consent to AppSearch"); + throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE, e); } finally { READ_WRITE_LOCK.writeLock().unlock(); } @@ -606,7 +605,7 @@ class AppSearchConsentWorker { AdServicesCommon.ADEXTSERVICES_PACKAGE_NAME_SUFFIX, AdServicesCommon.ADSERVICES_APK_PACKAGE_NAME_SUFFIX); } - // If we don't know the AdServices package name, we can't do a write. + // If we don't know the AdServices package name, we can't write. throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); } @@ -637,11 +636,11 @@ class AppSearchConsentWorker { } dao.setAdIdEnabled(isAdIdEnabled); dao.writeData(mUxStatesSearchSession, mPackageIdentifiers, mExecutor) - .get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + .get(mTimeoutMs, TimeUnit.MILLISECONDS); LogUtil.d("Wrote the isAdIdEnabled bit to AppSearch: " + dao); } catch (InterruptedException | TimeoutException | ExecutionException e) { - LogUtil.e("Failed to write the isAdIdEnabled to AppSearch ", e); - throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + LogUtil.e(e, "Failed to write the isAdIdEnabled to AppSearch"); + throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE, e); } finally { READ_WRITE_LOCK.writeLock().unlock(); } @@ -674,11 +673,11 @@ class AppSearchConsentWorker { } dao.setU18Account(isU18Account); dao.writeData(mUxStatesSearchSession, mPackageIdentifiers, mExecutor) - .get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + .get(mTimeoutMs, TimeUnit.MILLISECONDS); LogUtil.d("Wrote the isU18Account bit to AppSearch: " + dao); } catch (InterruptedException | TimeoutException | ExecutionException e) { - LogUtil.e("Failed to write the isU18Account to AppSearch ", e); - throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + LogUtil.e(e, "Failed to write the isU18Account to AppSearch"); + throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE, e); } finally { READ_WRITE_LOCK.writeLock().unlock(); } @@ -711,11 +710,11 @@ class AppSearchConsentWorker { } dao.setEntryPointEnabled(isEntryPointEnabled); dao.writeData(mUxStatesSearchSession, mPackageIdentifiers, mExecutor) - .get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + .get(mTimeoutMs, TimeUnit.MILLISECONDS); LogUtil.d("Wrote the isEntryPointEnabled bit to AppSearch: " + dao); } catch (InterruptedException | TimeoutException | ExecutionException e) { - LogUtil.e("Failed to write the isEntryPointEnabled to AppSearch ", e); - throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + LogUtil.e(e, "Failed to write the isEntryPointEnabled to AppSearch"); + throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE, e); } finally { READ_WRITE_LOCK.writeLock().unlock(); } @@ -748,11 +747,11 @@ class AppSearchConsentWorker { } dao.setAdultAccount(isAdultAccount); dao.writeData(mUxStatesSearchSession, mPackageIdentifiers, mExecutor) - .get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + .get(mTimeoutMs, TimeUnit.MILLISECONDS); LogUtil.d("Wrote the isAdultAccount bit to AppSearch: " + dao); } catch (InterruptedException | TimeoutException | ExecutionException e) { - LogUtil.e("Failed to write the isAdultAccount to AppSearch ", e); - throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + LogUtil.e(e, "Failed to write the isAdultAccount to AppSearch"); + throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE, e); } finally { READ_WRITE_LOCK.writeLock().unlock(); } @@ -785,11 +784,11 @@ class AppSearchConsentWorker { } dao.setU18NotificationDisplayed(wasU18NotificationDisplayed); dao.writeData(mUxStatesSearchSession, mPackageIdentifiers, mExecutor) - .get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + .get(mTimeoutMs, TimeUnit.MILLISECONDS); LogUtil.d("Wrote the wasU18NotificationDisplayed bit to AppSearch: " + dao); } catch (InterruptedException | TimeoutException | ExecutionException e) { - LogUtil.e("Failed to write the wasU18NotificationDisplayed to AppSearch ", e); - throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + LogUtil.e(e, "Failed to write the wasU18NotificationDisplayed to AppSearch"); + throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE, e); } finally { READ_WRITE_LOCK.writeLock().unlock(); } @@ -822,11 +821,11 @@ class AppSearchConsentWorker { } dao.setUx(ux.toString()); dao.writeData(mUxStatesSearchSession, mPackageIdentifiers, mExecutor) - .get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + .get(mTimeoutMs, TimeUnit.MILLISECONDS); LogUtil.d("Wrote PrivacySandboxUx to AppSearch: " + dao); } catch (InterruptedException | TimeoutException | ExecutionException e) { - LogUtil.e("Failed to write PrivacySandboxUx to AppSearch ", e); - throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + LogUtil.e(e, "Failed to write PrivacySandboxUx to AppSearch"); + throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE, e); } finally { READ_WRITE_LOCK.writeLock().unlock(); } @@ -865,11 +864,11 @@ class AppSearchConsentWorker { } dao.setEnrollmentChannel(enrollmentChannel.toString()); dao.writeData(mUxStatesSearchSession, mPackageIdentifiers, mExecutor) - .get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + .get(mTimeoutMs, TimeUnit.MILLISECONDS); LogUtil.d("Wrote PrivacySandboxUx to AppSearch: " + dao); } catch (InterruptedException | TimeoutException | ExecutionException e) { - LogUtil.e("Failed to write PrivacySandboxUx to AppSearch ", e); - throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + LogUtil.e(e, "Failed to write PrivacySandboxUx to AppSearch"); + throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE, e); } finally { READ_WRITE_LOCK.writeLock().unlock(); } diff --git a/adservices/service-core/java/com/android/adservices/service/appsearch/AppSearchDao.java b/adservices/service-core/java/com/android/adservices/service/appsearch/AppSearchDao.java index 4fdf36f67..cd52dbd72 100644 --- a/adservices/service-core/java/com/android/adservices/service/appsearch/AppSearchDao.java +++ b/adservices/service-core/java/com/android/adservices/service/appsearch/AppSearchDao.java @@ -16,6 +16,8 @@ package com.android.adservices.service.appsearch; +import static com.android.adservices.service.consent.ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE; + import android.annotation.NonNull; import android.os.Build; @@ -31,6 +33,7 @@ import androidx.appsearch.app.RemoveByDocumentIdRequest; import androidx.appsearch.app.SearchResults; import androidx.appsearch.app.SearchSpec; import androidx.appsearch.app.SetSchemaRequest; +import androidx.appsearch.app.SetSchemaResponse.MigrationFailure; import androidx.appsearch.exceptions.AppSearchException; import com.android.adservices.AdServicesCommon; @@ -38,7 +41,6 @@ import com.android.adservices.LogUtil; import com.android.adservices.service.Flags; import com.android.adservices.service.FlagsFactory; import com.android.adservices.service.common.AllowLists; -import com.android.adservices.service.consent.ConsentConstants; import com.android.internal.annotations.VisibleForTesting; import com.google.common.util.concurrent.FluentFuture; @@ -58,12 +60,8 @@ import java.util.function.BiFunction; * Base class for all data access objects for AppSearch. This class handles the common logic for * reading from and writing to AppSearch. */ -// TODO(b/269798827): Enable for R. @RequiresApi(Build.VERSION_CODES.S) class AppSearchDao { - // Timeout for AppSearch search query in milliseconds. - private static final int TIMEOUT_MS = 500; - /** * Iterate over the search results returned for the search query by AppSearch. * @@ -88,7 +86,7 @@ class AppSearchDao { // Converts GenericDocument object to the type of object passed in cls. documentResult = genericDocument.toDocumentClass(cls); } catch (AppSearchException e) { - LogUtil.e("Failed to convert GenericDocument to " + cls.getName(), e); + LogUtil.e(e, "Failed to convert GenericDocument to %s", cls.getName()); } } @@ -203,9 +201,11 @@ class AppSearchDao { results -> iterateSearchResults(cls, results, executor), executor) .transform(result -> ((T) result), executor); - return future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + + int timeout = FlagsFactory.getFlags().getAppSearchReadTimeout(); + return future.get(timeout, TimeUnit.MILLISECONDS); } catch (ExecutionException | InterruptedException | TimeoutException e) { - LogUtil.e("getConsent() Appsearch lookup failed with: ", e); + LogUtil.e(e, "getConsent() AppSearch lookup failed"); } return null; } @@ -216,7 +216,7 @@ class AppSearchDao { * we specify the packageIdentifier as that of the T+ AdServices APK, which after OTA, needs * access to the data written before OTA. What is written is the subclass type of DAO. * - * @return the result of the write. + * @return the result of the write operation. */ FluentFuture<AppSearchBatchResult<String, Void>> writeData( @NonNull ListenableFuture<AppSearchSession> appSearchSession, @@ -245,14 +245,17 @@ class AppSearchDao { // If we get failures in schemaResponse then we cannot try // to write. if (!setSchemaResponse.getMigrationFailures().isEmpty()) { + MigrationFailure failure = + setSchemaResponse.getMigrationFailures().get(0); LogUtil.e( "SetSchemaResponse migration failure: " - + setSchemaResponse - .getMigrationFailures() - .get(0)); - throw new RuntimeException( - ConsentConstants - .ERROR_MESSAGE_APPSEARCH_FAILURE); + + failure); + String message = + String.format( + "%s Migration failure: %s", + ERROR_MESSAGE_APPSEARCH_FAILURE, + failure.getAppSearchResult()); + throw new RuntimeException(message); } // The database knows about this schemaType and write can // occur. @@ -264,17 +267,17 @@ class AppSearchDao { executor); return putFuture; } catch (AppSearchException e) { - LogUtil.e("Cannot instantiate AppSearch database: " + e.getMessage()); + LogUtil.e(e, "Cannot instantiate AppSearch database"); + return FluentFuture.from( + Futures.immediateFailedFuture( + new RuntimeException(ERROR_MESSAGE_APPSEARCH_FAILURE, e))); } - return FluentFuture.from( - Futures.immediateFailedFuture( - new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE))); } /** * Delete a row from the database. * - * @return the result of the delete. + * @return the result of the delete operation. */ protected static <T> FluentFuture<AppSearchBatchResult<String, Void>> deleteData( @NonNull Class<T> cls, @@ -308,8 +311,7 @@ class AppSearchDao { .getMigrationFailures() .get(0)); throw new RuntimeException( - ConsentConstants - .ERROR_MESSAGE_APPSEARCH_FAILURE); + ERROR_MESSAGE_APPSEARCH_FAILURE); } // The database knows about this schemaType and write can // occur. @@ -325,6 +327,6 @@ class AppSearchDao { } return FluentFuture.from( Futures.immediateFailedFuture( - new RuntimeException(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE))); + new RuntimeException(ERROR_MESSAGE_APPSEARCH_FAILURE))); } } diff --git a/adservices/service-core/java/com/android/adservices/service/appsearch/AppSearchMeasurementRollbackWorker.java b/adservices/service-core/java/com/android/adservices/service/appsearch/AppSearchMeasurementRollbackWorker.java index f81566cc2..b282cfefa 100644 --- a/adservices/service-core/java/com/android/adservices/service/appsearch/AppSearchMeasurementRollbackWorker.java +++ b/adservices/service-core/java/com/android/adservices/service/appsearch/AppSearchMeasurementRollbackWorker.java @@ -28,6 +28,7 @@ import androidx.appsearch.platformstorage.PlatformStorage; import com.android.adservices.LogUtil; import com.android.adservices.concurrency.AdServicesExecutors; +import com.android.adservices.service.FlagsFactory; import com.android.adservices.service.common.compat.FileCompatUtils; import com.android.adservices.service.consent.ConsentConstants; import com.android.adservices.service.measurement.rollback.MeasurementRollbackWorker; @@ -59,7 +60,7 @@ public final class AppSearchMeasurementRollbackWorker implements MeasurementRoll private static final ReadWriteLock READ_WRITE_LOCK = new ReentrantReadWriteLock(); // Timeout for AppSearch write query in milliseconds. - private static final int TIMEOUT_MS = 2000; + private final int mTimeoutMs; private final String mUserId; private final String mAdServicesPackageName; @@ -75,6 +76,8 @@ public final class AppSearchMeasurementRollbackWorker implements MeasurementRoll mSearchSession = PlatformStorage.createSearchSessionAsync( new PlatformStorage.SearchContext.Builder(context, DATABASE_NAME).build()); + + mTimeoutMs = FlagsFactory.getFlags().getAppSearchWriteTimeout(); } /** Return an instance of {@link AppSearchMeasurementRollbackWorker} */ @@ -98,7 +101,7 @@ public final class AppSearchMeasurementRollbackWorker implements MeasurementRoll // don't need to share it with the T package. Thus, we can send an empty list for the // packageIdentifiers parameter. dao.writeData(mSearchSession, List.of(), mExecutor) - .get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + .get(mTimeoutMs, TimeUnit.MILLISECONDS); LogUtil.d("Wrote measurement rollback data to AppSearch: %s", dao); } catch (InterruptedException | TimeoutException | ExecutionException e) { LogUtil.e(e, "Failed to write measurement rollback to AppSearch"); @@ -130,7 +133,7 @@ public final class AppSearchMeasurementRollbackWorker implements MeasurementRoll mExecutor, storageIdentifier, AppSearchMeasurementRollbackDao.NAMESPACE) - .get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + .get(mTimeoutMs, TimeUnit.MILLISECONDS); LogUtil.d("Deleted MeasurementRollback data from AppSearch for: %s", storageIdentifier); } catch (InterruptedException | TimeoutException | ExecutionException e) { LogUtil.e(e, "Failed to delete MeasurementRollback data in AppSearch"); diff --git a/adservices/service-core/java/com/android/adservices/service/common/AdServicesCommonServiceImpl.java b/adservices/service-core/java/com/android/adservices/service/common/AdServicesCommonServiceImpl.java index bf002effd..ecb1e1115 100644 --- a/adservices/service-core/java/com/android/adservices/service/common/AdServicesCommonServiceImpl.java +++ b/adservices/service-core/java/com/android/adservices/service/common/AdServicesCommonServiceImpl.java @@ -58,6 +58,7 @@ import android.content.SharedPreferences; import android.os.Binder; import android.os.Build; import android.os.RemoteException; +import android.os.Trace; import androidx.annotation.RequiresApi; @@ -285,7 +286,9 @@ public class AdServicesCommonServiceImpl extends IAdServicesCommonService.Stub { @NonNull IEnableAdServicesCallback callback) { LogUtil.d(ENABLE_AD_SERVICES_API_CALLED_MESSAGE); + Trace.beginSection("AdServicesCommonService#EnableAdServices_PermissionCheck"); boolean authorizedCaller = PermissionHelper.hasModifyAdServicesStatePermission(mContext); + Trace.endSection(); sBackgroundExecutor.execute( () -> { @@ -308,7 +311,10 @@ public class AdServicesCommonServiceImpl extends IAdServicesCommonService.Stub { return; } + Trace.beginSection("AdServicesCommonService#EnableAdServices_UxEngineFlow"); mUxEngine.start(adServicesStates); + Trace.endSection(); + LogUtil.d("enableAdServices(): UxEngine started."); callback.onResult( diff --git a/adservices/service-core/java/com/android/adservices/service/common/AppManifestConfig.java b/adservices/service-core/java/com/android/adservices/service/common/AppManifestConfig.java index 5db3b171e..672e443db 100644 --- a/adservices/service-core/java/com/android/adservices/service/common/AppManifestConfig.java +++ b/adservices/service-core/java/com/android/adservices/service/common/AppManifestConfig.java @@ -16,6 +16,11 @@ package com.android.adservices.service.common; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_ALLOWED_APP_ALLOWS_ALL; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_ALLOWED_APP_ALLOWS_SPECIFIC_ID; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_ALLOWED_BY_DEFAULT_APP_HAS_CONFIG_WITHOUT_API_SECTION; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_DISALLOWED_APP_HAS_CONFIG_WITHOUT_API_SECTION; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_DISALLOWED_BY_APP; import static com.android.adservices.service.common.AppManifestConfigParser.TAG_ADID; import static com.android.adservices.service.common.AppManifestConfigParser.TAG_APPSETID; import static com.android.adservices.service.common.AppManifestConfigParser.TAG_ATTRIBUTION; @@ -26,6 +31,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import com.android.adservices.LogUtil; +import com.android.adservices.service.common.AppManifestConfigCall.Result; import java.util.function.Supplier; @@ -96,9 +102,9 @@ public final class AppManifestConfig { * Returns if the ad partner is permitted to access Attribution API for config represented by * this object. * - * <p>If the tag is not found in the app manifest config, returns {@code false}. + * <p>See constants in {@link AppManifestConfigCall} for the returned value. */ - public boolean isAllowedAttributionAccess(@NonNull String enrollmentId) { + public @Result int isAllowedAttributionAccess(@NonNull String enrollmentId) { return isAllowedAccess(TAG_ATTRIBUTION, mAttributionConfig, enrollmentId); } @@ -119,9 +125,9 @@ public final class AppManifestConfig { * Returns {@code true} if an ad tech with the given enrollment ID is permitted to access Custom * Audience API for config represented by this object. * - * <p>If the tag is not found in the app manifest config, returns {@code false}. + * <p>See constants in {@link AppManifestConfigCall} for the returned value. */ - public boolean isAllowedCustomAudiencesAccess(@NonNull String enrollmentId) { + public @Result int isAllowedCustomAudiencesAccess(@NonNull String enrollmentId) { return isAllowedAccess(TAG_CUSTOM_AUDIENCES, mCustomAudiencesConfig, enrollmentId); } @@ -140,9 +146,9 @@ public final class AppManifestConfig { * Returns if the ad partner is permitted to access Topics API for config represented by this * object. * - * <p>If the tag is not found in the app manifest config, returns {@code false}. + * <p>See constants in {@link AppManifestConfigCall} for the returned value. */ - public boolean isAllowedTopicsAccess(@NonNull String enrollmentId) { + public @Result int isAllowedTopicsAccess(@NonNull String enrollmentId) { return isAllowedAccess(TAG_TOPICS, mTopicsConfig, enrollmentId); } @@ -159,9 +165,9 @@ public final class AppManifestConfig { /** * Returns if sdk is permitted to access AdId API for config represented by this object. * - * <p>If the tag is not found in the app manifest config, returns {@code false}. + * <p>See constants in {@link AppManifestConfigCall} for the returned value. */ - public boolean isAllowedAdIdAccess(@NonNull String sdk) { + public @Result int isAllowedAdIdAccess(@NonNull String sdk) { return isAllowedAccess(TAG_ADID, mAdIdConfig, sdk); } @@ -181,9 +187,9 @@ public final class AppManifestConfig { /** * Returns if sdk is permitted to access AppSetId API for config represented by this object. * - * <p>If the tag is not found in the app manifest config, returns {@code false}. + * <p>See constants in {@link AppManifestConfigCall} for the returned value. */ - public boolean isAllowedAppSetIdAccess(@NonNull String sdk) { + public @Result int isAllowedAppSetIdAccess(@NonNull String sdk) { return isAllowedAccess(TAG_APPSETID, mAppSetIdConfig, sdk); } @@ -200,15 +206,21 @@ public final class AppManifestConfig { return null; } - private boolean isAllowedAccess( + private @Result int isAllowedAccess( String tag, @Nullable AppManifestApiConfig config, String partnerId) { if (config == null) { LogUtil.v( "app manifest config tag '%s' not found, returning %b", tag, mEnabledByDefault); - return mEnabledByDefault; + return mEnabledByDefault + ? RESULT_ALLOWED_BY_DEFAULT_APP_HAS_CONFIG_WITHOUT_API_SECTION + : RESULT_DISALLOWED_APP_HAS_CONFIG_WITHOUT_API_SECTION; } - - return config.getAllowAllToAccess() - || config.getAllowAdPartnersToAccess().contains(partnerId); + if (config.getAllowAllToAccess()) { + return RESULT_ALLOWED_APP_ALLOWS_ALL; + } + if (config.getAllowAdPartnersToAccess().contains(partnerId)) { + return RESULT_ALLOWED_APP_ALLOWS_SPECIFIC_ID; + } + return RESULT_DISALLOWED_BY_APP; } } diff --git a/adservices/service-core/java/com/android/adservices/service/common/AppManifestConfigCall.java b/adservices/service-core/java/com/android/adservices/service/common/AppManifestConfigCall.java index 449a159c3..79c93bc55 100644 --- a/adservices/service-core/java/com/android/adservices/service/common/AppManifestConfigCall.java +++ b/adservices/service-core/java/com/android/adservices/service/common/AppManifestConfigCall.java @@ -15,33 +15,162 @@ */ package com.android.adservices.service.common; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.annotation.IntDef; + +import com.android.internal.annotations.VisibleForTesting; + +import java.lang.annotation.Retention; import java.util.Objects; // TODO(b/310270746): make it package-protected when TopicsServiceImplTest is refactored /** Represents a call to a public {@link AppManifestConfigHelper} method. */ public final class AppManifestConfigCall { + + // TODO(b/306417555): use values from statsd atom for constants below + static final int API_UNSPECIFIED = 0; + static final int API_TOPICS = 1; + static final int API_CUSTOM_AUDIENCES = 2; + static final int API_ATTRIBUTION = 3; + + @IntDef({API_TOPICS, API_CUSTOM_AUDIENCES, API_ATTRIBUTION}) + @Retention(SOURCE) + public @interface ApiType {} + + // TODO(b/306417555): use values from statsd atom for constants below + static final int RESULT_UNSPECIFIED = 0; + static final int RESULT_ALLOWED_BY_DEFAULT_APP_DOES_NOT_HAVE_CONFIG = 1; + static final int RESULT_ALLOWED_BY_DEFAULT_APP_HAS_CONFIG_WITHOUT_API_SECTION = 2; + static final int RESULT_ALLOWED_APP_ALLOWS_ALL = 3; + static final int RESULT_ALLOWED_APP_ALLOWS_SPECIFIC_ID = 4; + static final int RESULT_DISALLOWED_APP_DOES_NOT_EXIST = 5; + static final int RESULT_DISALLOWED_APP_CONFIG_PARSING_ERROR = 6; + static final int RESULT_DISALLOWED_APP_DOES_NOT_HAVE_CONFIG = 7; + static final int RESULT_DISALLOWED_APP_HAS_CONFIG_WITHOUT_API_SECTION = 8; + static final int RESULT_DISALLOWED_BY_APP = 9; + static final int RESULT_DISALLOWED_GENERIC_ERROR = 10; + + @IntDef({ + RESULT_ALLOWED_BY_DEFAULT_APP_DOES_NOT_HAVE_CONFIG, + RESULT_ALLOWED_BY_DEFAULT_APP_HAS_CONFIG_WITHOUT_API_SECTION, + RESULT_ALLOWED_APP_ALLOWS_ALL, + RESULT_ALLOWED_APP_ALLOWS_SPECIFIC_ID, + RESULT_DISALLOWED_APP_DOES_NOT_EXIST, + RESULT_DISALLOWED_APP_CONFIG_PARSING_ERROR, + RESULT_DISALLOWED_APP_DOES_NOT_HAVE_CONFIG, + RESULT_DISALLOWED_APP_HAS_CONFIG_WITHOUT_API_SECTION, + RESULT_DISALLOWED_BY_APP, + RESULT_DISALLOWED_GENERIC_ERROR + }) + @Retention(SOURCE) + public @interface Result {} + + @VisibleForTesting static final String INVALID_API_TEMPLATE = "Invalid API: %d"; + public final String packageName; - public boolean appExists; - public boolean appHasConfig; - public boolean enabledByDefault; - public boolean result; + public final @ApiType int api; + public @Result int result; - public AppManifestConfigCall(String packageName) { + public AppManifestConfigCall(String packageName, @ApiType int api) { + switch (api) { + case API_TOPICS: + case API_CUSTOM_AUDIENCES: + case API_ATTRIBUTION: + this.api = api; + break; + default: + throw new IllegalArgumentException(String.format(INVALID_API_TEMPLATE, api)); + } this.packageName = Objects.requireNonNull(packageName, "packageName cannot be null"); } @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + AppManifestConfigCall other = (AppManifestConfigCall) obj; + return api == other.api + && Objects.equals(packageName, other.packageName) + && result == other.result; + } + + @Override + public int hashCode() { + return Objects.hash(api, packageName, result); + } + + @Override public String toString() { return "AppManifestConfigCall[pkg=" + packageName - + ", appExists=" - + appExists - + ", appHasConfig=" - + appHasConfig - + ", enabledByDefault=" - + enabledByDefault + + ", api=" + + apiToString(api) + ", result=" - + result + + resultToString(result) + "]"; } + + static String resultToString(@Result int result) { + switch (result) { + case RESULT_UNSPECIFIED: + return "UNSPECIFIED"; + case RESULT_ALLOWED_BY_DEFAULT_APP_DOES_NOT_HAVE_CONFIG: + return "ALLOWED_BY_DEFAULT_APP_DOES_NOT_HAVE_CONFIG"; + case RESULT_ALLOWED_BY_DEFAULT_APP_HAS_CONFIG_WITHOUT_API_SECTION: + return "ALLOWED_BY_DEFAULT_APP_HAS_CONFIG_WITHOUT_API_SECTION"; + case RESULT_ALLOWED_APP_ALLOWS_ALL: + return "ALLOWED_APP_ALLOWS_ALL"; + case RESULT_ALLOWED_APP_ALLOWS_SPECIFIC_ID: + return "ALLOWED_APP_ALLOWS_SPECIFIC_ID"; + case RESULT_DISALLOWED_APP_DOES_NOT_EXIST: + return "DISALLOWED_APP_DOES_NOT_EXIST"; + case RESULT_DISALLOWED_APP_CONFIG_PARSING_ERROR: + return "DISALLOWED_APP_CONFIG_PARSING_ERROR"; + case RESULT_DISALLOWED_APP_DOES_NOT_HAVE_CONFIG: + return "DISALLOWED_APP_DOES_NOT_HAVE_CONFIG"; + case RESULT_DISALLOWED_APP_HAS_CONFIG_WITHOUT_API_SECTION: + return "DISALLOWED_APP_HAS_CONFIG_WITHOUT_API_SECTION"; + case RESULT_DISALLOWED_BY_APP: + return "DISALLOWED_BY_APP"; + case RESULT_DISALLOWED_GENERIC_ERROR: + return "DISALLOWED_GENERIC_ERROR"; + default: + return "INVALID-" + result; + } + } + + static String apiToString(@ApiType int result) { + switch (result) { + case API_UNSPECIFIED: + return "UNSPECIFIED"; + case API_TOPICS: + return "TOPICS"; + case API_CUSTOM_AUDIENCES: + return "CUSTOM_AUDIENCES"; + case API_ATTRIBUTION: + return "ATTRIBUTION"; + default: + return "INVALID-" + result; + } + } + + static boolean isAllowed(@Result int result) { + switch (result) { + case RESULT_ALLOWED_BY_DEFAULT_APP_DOES_NOT_HAVE_CONFIG: + case RESULT_ALLOWED_BY_DEFAULT_APP_HAS_CONFIG_WITHOUT_API_SECTION: + case RESULT_ALLOWED_APP_ALLOWS_ALL: + case RESULT_ALLOWED_APP_ALLOWS_SPECIFIC_ID: + return true; + default: + return false; + } + } } diff --git a/adservices/service-core/java/com/android/adservices/service/common/AppManifestConfigHelper.java b/adservices/service-core/java/com/android/adservices/service/common/AppManifestConfigHelper.java index 0fe09ff8d..b549717ee 100644 --- a/adservices/service-core/java/com/android/adservices/service/common/AppManifestConfigHelper.java +++ b/adservices/service-core/java/com/android/adservices/service/common/AppManifestConfigHelper.java @@ -16,6 +16,18 @@ package com.android.adservices.service.common; +import static com.android.adservices.service.common.AppManifestConfigCall.API_ATTRIBUTION; +import static com.android.adservices.service.common.AppManifestConfigCall.API_CUSTOM_AUDIENCES; +import static com.android.adservices.service.common.AppManifestConfigCall.API_TOPICS; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_ALLOWED_APP_ALLOWS_SPECIFIC_ID; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_ALLOWED_BY_DEFAULT_APP_DOES_NOT_HAVE_CONFIG; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_DISALLOWED_APP_CONFIG_PARSING_ERROR; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_DISALLOWED_APP_DOES_NOT_EXIST; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_DISALLOWED_APP_DOES_NOT_HAVE_CONFIG; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_DISALLOWED_BY_APP; +import static com.android.adservices.service.common.AppManifestConfigCall.isAllowed; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__APP_MANIFEST_CONFIG_PARSING_ERROR; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__COMMON; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__APP_MANIFEST_CONFIG_PARSING_ERROR; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__COMMON; @@ -62,6 +74,7 @@ public class AppManifestConfigHelper { @NonNull String enrollmentId) { return isAllowedApiAccess( "isAllowedAttributionAccess()", + API_ATTRIBUTION, appPackageName, enrollmentId, config -> config.isAllowedAttributionAccess(enrollmentId)); @@ -80,6 +93,7 @@ public class AppManifestConfigHelper { @NonNull String enrollmentId) { return isAllowedApiAccess( "isAllowedCustomAudiencesAccess()", + API_CUSTOM_AUDIENCES, appPackageName, enrollmentId, config -> config.isAllowedCustomAudiencesAccess(enrollmentId)); @@ -101,14 +115,17 @@ public class AppManifestConfigHelper { @NonNull String enrollmentId) { return isAllowedApiAccess( "isAllowedTopicsAccess()", + API_TOPICS, appPackageName, enrollmentId, config -> { // If the request comes directly from the app, check that the app has declared // that it includes this Sdk library. if (!useSandboxCheck) { - return config.getIncludesSdkLibraryConfig().contains(enrollmentId) - && config.isAllowedTopicsAccess(enrollmentId); + return (config.getIncludesSdkLibraryConfig().contains(enrollmentId) + && isAllowed(config.isAllowedTopicsAccess(enrollmentId))) + ? RESULT_ALLOWED_APP_ALLOWS_SPECIFIC_ID + : RESULT_DISALLOWED_BY_APP; } // If the request comes from the SdkRuntime, then the app had to have declared @@ -118,29 +135,22 @@ public class AppManifestConfigHelper { } @Nullable - private static XmlResourceParser getXmlParser(AppManifestConfigCall call) + private static XmlResourceParser getXmlParser(String appPackageName) throws NameNotFoundException, XmlParseException, XmlPullParserException, IOException { Context context = ApplicationContextSingleton.get(); - String appPackageName = call.packageName; - LogUtil.v("getXmlParser(%s): context=%s", call.packageName, context); + LogUtil.v("getXmlParser(%s): context=%s", appPackageName, context); + // NOTE: resources is only used pre-S, but it must be called regardless to make sure the app // exists Resources resources = context.getPackageManager().getResourcesForApplication(appPackageName); - call.appExists = true; - Integer resId = SdkLevel.isAtLeastS() ? getAdServicesConfigResourceIdOnExistingPackageOnSPlus( context, appPackageName) : getAdServicesConfigResourceIdOnRMinus(context, resources, appPackageName); - XmlResourceParser xmlResourceParser = null; - if (resId != null) { - xmlResourceParser = resources.getXml(resId); - call.appHasConfig = true; - } - return xmlResourceParser; + return resId != null ? resources.getXml(resId) : null; } @Nullable @@ -171,28 +181,37 @@ public class AppManifestConfigHelper { private static boolean isAllowedApiAccess( String method, + int api, String appPackageName, String enrollmentId, ApiAccessChecker checker) { Objects.requireNonNull(appPackageName); Objects.requireNonNull(enrollmentId); - AppManifestConfigCall call = new AppManifestConfigCall(appPackageName); - call.enabledByDefault = FlagsFactory.getFlags().getAppConfigReturnsEnabledByDefault(); + + AppManifestConfigCall call = new AppManifestConfigCall(appPackageName, api); + boolean enabledByDefault = FlagsFactory.getFlags().getAppConfigReturnsEnabledByDefault(); + try { - XmlResourceParser in = getXmlParser(call); + XmlResourceParser in = getXmlParser(appPackageName); if (in == null) { + call.result = + enabledByDefault + ? RESULT_ALLOWED_BY_DEFAULT_APP_DOES_NOT_HAVE_CONFIG + : RESULT_DISALLOWED_APP_DOES_NOT_HAVE_CONFIG; LogUtil.v( "%s: returning %b for app (%s) that doesn't have the AdServices XML config", - method, call.enabledByDefault, appPackageName); - return call.enabledByDefault; + method, enabledByDefault, appPackageName); + return enabledByDefault; } AppManifestConfig appManifestConfig = - AppManifestConfigParser.getConfig(in, call.enabledByDefault); + AppManifestConfigParser.getConfig(in, enabledByDefault); call.result = checker.isAllowedAccess(appManifestConfig); } catch (NameNotFoundException e) { + call.result = RESULT_DISALLOWED_APP_DOES_NOT_EXIST; LogUtil.v( "Name not found while looking for manifest for app %s: %s", appPackageName, e); } catch (Exception e) { + call.result = RESULT_DISALLOWED_APP_CONFIG_PARSING_ERROR; LogUtil.e(e, "App manifest parse failed."); ErrorLogUtil.e( e, @@ -201,10 +220,10 @@ public class AppManifestConfigHelper { } finally { AppManifestConfigMetricsLogger.logUsage(call); } - return call.result; + return isAllowed(call.result); } private interface ApiAccessChecker { - boolean isAllowedAccess(AppManifestConfig config); + int isAllowedAccess(AppManifestConfig config); } } diff --git a/adservices/service-core/java/com/android/adservices/service/common/AppManifestConfigMetricsLogger.java b/adservices/service-core/java/com/android/adservices/service/common/AppManifestConfigMetricsLogger.java index d573e05ef..17524ee94 100644 --- a/adservices/service-core/java/com/android/adservices/service/common/AppManifestConfigMetricsLogger.java +++ b/adservices/service-core/java/com/android/adservices/service/common/AppManifestConfigMetricsLogger.java @@ -15,9 +15,13 @@ */ package com.android.adservices.service.common; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_UNSPECIFIED; +import static com.android.adservices.service.common.AppManifestConfigCall.apiToString; +import static com.android.adservices.service.common.AppManifestConfigCall.resultToString; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__COMMON; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__APP_MANIFEST_CONFIG_LOGGING_ERROR; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__SHARED_PREF_EXCEPTION; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__SHARED_PREF_UPDATE_FAILURE; -import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__COMMON; import android.content.Context; import android.content.SharedPreferences; @@ -27,12 +31,15 @@ import com.android.adservices.LogUtil; import com.android.adservices.concurrency.AdServicesExecutors; import com.android.adservices.errorlogging.ErrorLogUtil; import com.android.adservices.service.FlagsFactory; +import com.android.adservices.service.common.AppManifestConfigCall.ApiType; +import com.android.adservices.service.common.AppManifestConfigCall.Result; import com.android.adservices.service.common.compat.FileCompatUtils; import com.android.adservices.shared.common.ApplicationContextSingleton; import com.android.internal.annotations.VisibleForTesting; import java.io.File; import java.io.PrintWriter; +import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; @@ -45,40 +52,39 @@ public final class AppManifestConfigMetricsLogger { static final String PREFS_NAME = FileCompatUtils.getAdservicesFilename("AppManifestConfigMetricsLogger"); - private static final int NOT_SET = -1; - private static final int FLAG_APP_EXISTS = 0x1; - private static final int FLAG_APP_HAS_CONFIG = 0x2; - private static final int FLAG_ENABLED_BY_DEFAULT = 0x4; + @VisibleForTesting static final String PREFS_KEY_TEMPLATE = "%s-%d"; // TODO(b/310270746): make it package-protected when TopicsServiceImplTest is refactored /** Represents a call to a public {@link AppManifestConfigHelper} method. */ /** Logs the app usage. */ public static void logUsage(AppManifestConfigCall call) { Objects.requireNonNull(call, "call cannot be null"); + + // Cannot be RESULT_UNSPECIFIED because that's used to check if the shared preferences value + // doesn't exist yet + if (call.result == RESULT_UNSPECIFIED) { + LogUtil.e("invalid call result: %s", call); + ErrorLogUtil.e( + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__APP_MANIFEST_CONFIG_LOGGING_ERROR, + AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__COMMON); + return; + } AdServicesExecutors.getBackgroundExecutor().execute(() -> handleLogUsage(call)); } private static void handleLogUsage(AppManifestConfigCall call) { Context context = ApplicationContextSingleton.get(); try { - int newValue = - (call.appExists ? FLAG_APP_EXISTS : 0) - | (call.appHasConfig ? FLAG_APP_HAS_CONFIG : 0) - | (call.enabledByDefault ? FLAG_ENABLED_BY_DEFAULT : 0); + @Result int newValue = call.result; LogUtil.d( - "AppManifestConfigMetricsLogger.logUsage(): app=[name=%s, exists=%b," - + " hasConfig=%b], enabledByDefault=%b, newValue=%d", - call.packageName, - call.appExists, - call.appHasConfig, - call.enabledByDefault, - newValue); + "AppManifestConfigMetricsLogger.logUsage(): call=%s, newValue=%d", + call, newValue); SharedPreferences prefs = getPrefs(context); - String key = call.packageName; + String key = String.format(Locale.US, PREFS_KEY_TEMPLATE, call.packageName, call.api); - int currentValue = prefs.getInt(key, NOT_SET); - if (currentValue == NOT_SET) { + @Result int currentValue = prefs.getInt(key, RESULT_UNSPECIFIED); + if (currentValue == RESULT_UNSPECIFIED) { LogUtil.v("Logging for the first time (value=%d)", newValue); } else if (currentValue != newValue) { LogUtil.v("Logging as value change (was %d)", currentValue); @@ -95,28 +101,14 @@ public final class AppManifestConfigMetricsLogger { LogUtil.v("Changes committed"); } else { LogUtil.e( - "logUsage(ctx, file=%s, app=%s, appExist=%b, appHasConfig=%b," - + " enabledByDefault=%b, newValue=%d): failed to commit", - PREFS_NAME, - call.packageName, - call.appExists, - call.appHasConfig, - call.enabledByDefault, - newValue); + "logUsage(ctx, file=%s, call=%s, newValue=%d): failed to commit", + PREFS_NAME, call, newValue); ErrorLogUtil.e( AD_SERVICES_ERROR_REPORTED__ERROR_CODE__SHARED_PREF_UPDATE_FAILURE, AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__COMMON); } } catch (Exception e) { - LogUtil.e( - e, - "logUsage(ctx, file=%s, app=%s, appExist=%b, appHasConfig=%b," - + " enabledByDefault=%b) failed", - PREFS_NAME, - call.packageName, - call.appExists, - call.appHasConfig, - call.enabledByDefault); + LogUtil.e(e, "logUsage(ctx, file=%s, call=%s) failed", PREFS_NAME, call); ErrorLogUtil.e( e, AD_SERVICES_ERROR_REPORTED__ERROR_CODE__SHARED_PREF_EXCEPTION, @@ -126,13 +118,13 @@ public final class AppManifestConfigMetricsLogger { /** Dumps the internal state. */ public static void dump(Context context, PrintWriter pw) { + String prefix = " "; pw.println("AppManifestConfigMetricsLogger"); - String prefix = " "; - @SuppressWarnings("NewAdServicesFile") // PREFS_NAME already called FileCompatUtils // NOTE: shared_prefs is hard-coded on ContextImpl, but unfortunately Context doesn't offer // any API we could use here to get that path (getSharedPreferencesPath() is @removed and // the available APIs return a SharedPreferences, not a File). + @SuppressWarnings("NewAdServicesFile") // PREFS_NAME already called FileCompatUtils String path = new File(context.getDataDir() + "/shared_prefs", PREFS_NAME).getAbsolutePath(); pw.printf("%sPreferences file: %s.xml\n", prefix, path); @@ -147,19 +139,25 @@ public final class AppManifestConfigMetricsLogger { String prefix2 = prefix + " "; for (Entry<String, ?> pref : appPrefs.entrySet()) { - String app = pref.getKey(); + String key = pref.getKey(); + String appAndApi = key; + try { + String[] keyParts = key.split("-"); + String app = keyParts[0]; + @ApiType int api = Integer.parseInt(keyParts[1]); + appAndApi = app + "-" + apiToString(api); + } catch (Exception e) { + LogUtil.e(e, "failed to parse key %s", key); + } Object value = pref.getValue(); if (value instanceof Integer) { - int flags = (Integer) value; - boolean appExists = (flags & FLAG_APP_EXISTS) != 0; - boolean appHasConfig = (flags & FLAG_APP_HAS_CONFIG) != 0; - boolean enabledByDefault = (flags & FLAG_ENABLED_BY_DEFAULT) != 0; - pw.printf( - "%s%s: rawValue=%d, appExists=%b, appHasConfig=%b, enabledByDefault=%b\n", - prefix2, app, flags, appExists, appHasConfig, enabledByDefault); + @Result int result = (Integer) value; + pw.printf("%s%s: %s\n", prefix2, appAndApi, resultToString(result)); } else { // Shouldn't happen - pw.printf(" %s: unexpected value %s (class %s):\n", app, value, value.getClass()); + pw.printf( + " %s: unexpected value %s (class %s):\n", + appAndApi, value, value.getClass()); } } } diff --git a/adservices/service-core/java/com/android/adservices/service/common/FledgeAuthorizationFilter.java b/adservices/service-core/java/com/android/adservices/service/common/FledgeAuthorizationFilter.java index c99b0b9a3..7809ecd40 100644 --- a/adservices/service-core/java/com/android/adservices/service/common/FledgeAuthorizationFilter.java +++ b/adservices/service-core/java/com/android/adservices/service/common/FledgeAuthorizationFilter.java @@ -123,47 +123,22 @@ public class FledgeAuthorizationFilter { * @param apiNameLoggingId the id of the api being called * @throws SecurityException if the package did not declare custom audience permission */ - public void assertAppDeclaredCustomAudiencePermission( + public void assertAppDeclaredPermission( @NonNull Context context, @NonNull String appPackageName, int apiNameLoggingId) throws SecurityException { Objects.requireNonNull(context); Objects.requireNonNull(appPackageName); if (!PermissionHelper.hasCustomAudiencesPermission(context, appPackageName)) { - logAndThrowPermissionFailure(apiNameLoggingId); - } - } - - /** - * Check if the app had declared the protected signals permission. - * - * @param context api service context - * @param apiNameLoggingId the id of the api being called - * @throws SecurityException if the package did not declare custom audience permission - */ - public void assertAppDeclaredProtectedSignalsPermission( - @NonNull Context context, @NonNull String appPackageName, int apiNameLoggingId) - throws SecurityException { - Objects.requireNonNull(context); - Objects.requireNonNull(appPackageName); - - if (!PermissionHelper.hasProtectedSignalsPermission(context, appPackageName)) { - /* - * Using the same message for both since getAdSelectionData can be called with either - * permission and we don't want the error message to depend on which is checked first. - */ - logAndThrowPermissionFailure(apiNameLoggingId); + sLogger.v("Permission not declared by caller in API %d", apiNameLoggingId); + mAdServicesLogger.logFledgeApiCallStats( + apiNameLoggingId, STATUS_PERMISSION_NOT_REQUESTED, 0); + throw new SecurityException( + AdServicesStatusUtils + .SECURITY_EXCEPTION_PERMISSION_NOT_REQUESTED_ERROR_MESSAGE); } } - private void logAndThrowPermissionFailure(int apiNameLoggingId) { - sLogger.v("Permission not declared by caller in API %d", apiNameLoggingId); - mAdServicesLogger.logFledgeApiCallStats( - apiNameLoggingId, STATUS_PERMISSION_NOT_REQUESTED, 0); - throw new SecurityException( - AdServicesStatusUtils.SECURITY_EXCEPTION_PERMISSION_NOT_REQUESTED_ERROR_MESSAGE); - } - /** * Check if a certain ad tech is enrolled and authorized to perform the operation for the * package. diff --git a/adservices/service-core/java/com/android/adservices/service/common/PackageChangedReceiver.java b/adservices/service-core/java/com/android/adservices/service/common/PackageChangedReceiver.java index e2afca136..35d6ff658 100644 --- a/adservices/service-core/java/com/android/adservices/service/common/PackageChangedReceiver.java +++ b/adservices/service-core/java/com/android/adservices/service/common/PackageChangedReceiver.java @@ -295,6 +295,10 @@ public class PackageChangedReceiver extends BroadcastReceiver { @VisibleForTesting void consentOnPackageFullyRemoved( @NonNull Context context, @NonNull Uri packageUri, int packageUid) { + if (!SdkLevel.isAtLeastS()) { + LogUtil.d("consentOnPackageFullyRemoved is not needed on Android R, returning..."); + return; + } Objects.requireNonNull(context); Objects.requireNonNull(packageUri); diff --git a/adservices/service-core/java/com/android/adservices/service/common/PermissionHelper.java b/adservices/service-core/java/com/android/adservices/service/common/PermissionHelper.java index 67142b6d3..d02225e4e 100644 --- a/adservices/service-core/java/com/android/adservices/service/common/PermissionHelper.java +++ b/adservices/service-core/java/com/android/adservices/service/common/PermissionHelper.java @@ -101,16 +101,6 @@ public final class PermissionHelper { } /** - * @return {@code true} if the caller has the permission to invoke Protected Signals APIs. - */ - public static boolean hasProtectedSignalsPermission( - @NonNull Context context, @NonNull String appPackageName) { - // TODO(b/236268316): Add check for SDK permission. - return hasPermission( - context, appPackageName, AdServicesPermissions.ACCESS_ADSERVICES_PROTECTED_SIGNALS); - } - - /** * @return {@code true} if the caller has the permission to invoke AdService's state * modification API. */ diff --git a/adservices/service-core/java/com/android/adservices/service/common/httpclient/AdServicesHttpsClient.java b/adservices/service-core/java/com/android/adservices/service/common/httpclient/AdServicesHttpsClient.java index f809950c9..7ab2ff9fa 100644 --- a/adservices/service-core/java/com/android/adservices/service/common/httpclient/AdServicesHttpsClient.java +++ b/adservices/service-core/java/com/android/adservices/service/common/httpclient/AdServicesHttpsClient.java @@ -175,7 +175,7 @@ public class AdServicesHttpsClient { // Setting true explicitly to follow redirects Uri uri = Uri.parse(url.toString()); if (WebAddresses.isLocalhost(uri) && devContext.getDevOptionsEnabled()) { - LogUtil.v("Using unsafe HTTPS for url ", url.toString()); + LogUtil.v("Using unsafe HTTPS for url %s", url.toString()); urlConnection.setSSLSocketFactory(getUnsafeSslSocketFactory()); } else if (WebAddresses.isLocalhost(uri)) { LogUtil.v( diff --git a/adservices/service-core/java/com/android/adservices/service/consent/AdServicesStorageManager.java b/adservices/service-core/java/com/android/adservices/service/consent/AdServicesStorageManager.java index c32e72702..f3aa86683 100644 --- a/adservices/service-core/java/com/android/adservices/service/consent/AdServicesStorageManager.java +++ b/adservices/service-core/java/com/android/adservices/service/consent/AdServicesStorageManager.java @@ -125,13 +125,20 @@ public final class AdServicesStorageManager implements IConsentStorage { @Override public AdServicesApiConsent getConsent(AdServicesApiType apiType) { int consentApiType = apiType.toConsentApiType(); - return AdServicesApiConsent.getConsent( - mAdServicesManager.getConsent(consentApiType).isIsGiven()); + ConsentParcel consentParcel = mAdServicesManager.getConsent(consentApiType); + if (consentParcel == null) { + return AdServicesApiConsent.REVOKED; + } + return AdServicesApiConsent.getConsent(consentParcel.isIsGiven()); } /** Returns the current privacy sandbox feature. */ @Override - public PrivacySandboxFeatureType getCurrentPrivacySandboxFeature() { + public PrivacySandboxFeatureType getCurrentPrivacySandboxFeature() throws IOException { + if (mAdServicesManager == null + || mAdServicesManager.getCurrentPrivacySandboxFeature() == null) { + return PrivacySandboxFeatureType.PRIVACY_SANDBOX_UNSUPPORTED; + } return PrivacySandboxFeatureType.valueOf( mAdServicesManager.getCurrentPrivacySandboxFeature()); } @@ -434,7 +441,8 @@ public final class AdServicesStorageManager implements IConsentStorage { return mAdServicesManager.wasU18NotificationDisplayed(); } - private PrivacySandboxUxCollection convertUxString(String uxString) { + private PrivacySandboxUxCollection convertUxString(@NonNull String uxString) { + Objects.requireNonNull(uxString); return Stream.of(PrivacySandboxUxCollection.values()) .filter(ux -> uxString.equals(ux.toString())) .findFirst() diff --git a/adservices/service-core/java/com/android/adservices/service/consent/AppConsentForRStorageManager.java b/adservices/service-core/java/com/android/adservices/service/consent/AppConsentForRStorageManager.java new file mode 100644 index 000000000..259b57cbd --- /dev/null +++ b/adservices/service-core/java/com/android/adservices/service/consent/AppConsentForRStorageManager.java @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2023 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.consent; + +import android.os.Build; + +import androidx.annotation.RequiresApi; + +import com.android.adservices.data.common.BooleanFileDatastore; +import com.android.adservices.data.consent.AppConsentDao; +import com.android.adservices.service.extdata.AdServicesExtDataStorageServiceManager; +import com.android.adservices.service.ui.data.UxStatesDao; + +import com.google.common.collect.ImmutableList; + +import java.io.IOException; + +/** + * AppConsentStorageManager to handle user's consent related Apis in Android R. + * + * <p>It shares similarities with AppConsentStorageManager's logic, but adds additional storage + * functionality specific to AdServicesExtDataStorageServiceManager. + * + * <p>Used in PPAPI_AND_ADEXT_SERVICE + */ +@RequiresApi(Build.VERSION_CODES.S) +public class AppConsentForRStorageManager extends AppConsentStorageManager { + + private final AdServicesExtDataStorageServiceManager mAdExtDataManager; + /** + * Constructor of AppConsentForRStorageManager + * + * @param datastore stores consent + * @param appConsentDao mostly used by FLEDGE + * @param uxStatesDao stores ux related data + */ + public AppConsentForRStorageManager( + BooleanFileDatastore datastore, + AppConsentDao appConsentDao, + UxStatesDao uxStatesDao, + AdServicesExtDataStorageServiceManager adExtDataManager) { + super(datastore, appConsentDao, uxStatesDao); + this.mAdExtDataManager = adExtDataManager; + } + + /** Clear ConsentForUninstalledApp, not support for Measurement. */ + @Override + public void clearAllAppConsentData() { + // PPAPI_AND_ADEXT_SERVICE is only set on R which supports only + // Measurement. + throw new IllegalStateException( + getAdExtExceptionMessage(/* illegalAction= */ "reset consent for apps")); + } + + /** Clear ConsentForUninstalledApp, not support for Measurement. */ + @Override + public void clearConsentForUninstalledApp(String packageName) { + throw new IllegalStateException( + getAdExtExceptionMessage(/* illegalAction= */ "clear consent for uninstalled app")); + } + + /** Clear ConsentForUninstalledApp, not support for Measurement. */ + @Override + public void clearConsentForUninstalledApp(String packageName, int packageUid) { + throw new IllegalStateException( + getAdExtExceptionMessage(/* illegalAction= */ "clear consent for uninstalled app")); + } + + /** Clear KnownAppsWithConsent flag, not support for Measurement. */ + @Override + public void clearKnownAppsWithConsent() { + // PPAPI_AND_ADEXT_SERVICE is only set on R which supports only + // Measurement. + throw new IllegalStateException( + getAdExtExceptionMessage(/* illegalAction= */ "reset apps")); + } + + /** Gets getAppsWithRevokedConsent flag, not support for Measurement. */ + @Override + public ImmutableList<String> getAppsWithRevokedConsent() { + // PPAPI_AND_ADEXT_SERVICE is only set on R which supports only + // Measurement. + throw new IllegalStateException( + getAdExtExceptionMessage(/* illegalAction= */ "fetch apps with revoked consent")); + } + + /** Gets Consent by api flag. */ + @Override + public AdServicesApiConsent getConsent(AdServicesApiType apiType) { + if (apiType == AdServicesApiType.MEASUREMENTS) { + return AdServicesApiConsent.getConsent(mAdExtDataManager.getMsmtConsent()); + } + return AdServicesApiConsent.REVOKED; + } + + /** Gets getKnownAppsWithConsent flag, not support for Measurement. */ + @Override + public ImmutableList<String> getKnownAppsWithConsent() throws IOException { + // PPAPI_AND_ADEXT_SERVICE is only set on R which supports only + // Measurement. + throw new IllegalStateException( + getAdExtExceptionMessage(/* illegalAction= */ "fetch apps with consent")); + } + + /** Gets UserManualInteraction flag. */ + @Override + public int getUserManualInteractionWithConsent() { + return mAdExtDataManager.getManualInteractionWithConsentStatus(); + } + + /** Gets isAdultAccount flag. */ + @Override + public boolean isAdultAccount() { + return mAdExtDataManager.getIsAdultAccount(); + } + + /** Gets isConsentRevokedForApp flag, not support for Measurement. */ + @Override + public boolean isConsentRevokedForApp(String packageName) { + // PPAPI_AND_ADEXT_SERVICE is only set on R which supports only + // Measurement. + throw new IllegalStateException( + getAdExtExceptionMessage( + /* illegalAction= */ "check if consent has been revoked for" + " app")); + } + + /** Gets isU18 account flag. */ + @Override + public boolean isU18Account() { + return mAdExtDataManager.getIsU18Account(); + } + + /** Records GA notification displayed. */ + @Override + public void recordGaUxNotificationDisplayed(boolean wasGaUxDisplayed) { + // PPAPI_AND_ADEXT_SERVICE is only set on R which should never show + // GA UX. + throw new IllegalStateException( + getAdExtExceptionMessage( + /* illegalAction= */ "store if GA notification was displayed")); + } + + /** Records notification displayed. */ + @Override + public void recordNotificationDisplayed(boolean wasNotificationDisplayed) { + // PPAPI_AND_ADEXT_SERVICE is only set on R which should never show + // Beta UX. + throw new IllegalStateException( + getAdExtExceptionMessage(/* illegalAction= */ "store if beta notif was displayed")); + } + + /** Records user manual interaction bit. */ + @Override + public void recordUserManualInteractionWithConsent(int interaction) { + mAdExtDataManager.setManualInteractionWithConsentStatus(interaction); + } + + /** Sets consent by api type. */ + @Override + public void setAdultAccount(boolean isAdultAccount) { + mAdExtDataManager.setIsAdultAccount(isAdultAccount); + } + + /** Sets consent by api type. */ + @Override + public void setConsent(AdServicesApiType apiType, boolean isGiven) throws IOException { + if (apiType == AdServicesApiType.ALL_API) { + super.setConsent(apiType, isGiven); + return; + } + // PPAPI_AND_ADEXT_SERVICE is only set on R which supports only + // Measurement. There should never be a call to set consent for other PPAPIs. + if (apiType != AdServicesApiType.MEASUREMENTS) { + throw new IllegalStateException( + getAdExtExceptionMessage( + /* illegalAction= */ "set consent for a non-msmt API")); + } + mAdExtDataManager.setMsmtConsent(isGiven); + } + + /** + * setConsentForApp. + * + * <p>PPAPI_AND_ADEXT_SERVICE is only set on R which only supports Measurement + */ + @Override + public void setConsentForApp(String packageName, boolean isConsentRevoked) { + throw new IllegalStateException( + getAdExtExceptionMessage(/* illegalAction= */ "revoke consent for app")); + } + + /** + * SetConsentForAppIfNew. + * + * <p>PPAPI_AND_ADEXT_SERVICE is only set on R which only supports Measurement + */ + @Override + public boolean setConsentForAppIfNew(String packageName, boolean isConsentRevoked) { + throw new IllegalStateException( + getAdExtExceptionMessage( + /* illegalAction= */ "check if consent has been revoked for" + " app")); + } + + @Override + public void recordDefaultConsent(AdServicesApiType apiType, boolean defaultConsent) + throws IOException { + if (apiType == AdServicesApiType.MEASUREMENTS) { + super.recordDefaultConsent(apiType, defaultConsent); + } else { + throw new IllegalStateException( + getAdExtExceptionMessage( + /* illegalAction= */ "record default consent for " + + apiType.toString())); + } + } + + /** Stores isU18Account bit in AdExtData. */ + @Override + public void setU18Account(boolean isU18Account) { + mAdExtDataManager.setIsU18Account(isU18Account); + } + + /** Stores U18 notification bit in AdExtData. */ + @Override + public void setU18NotificationDisplayed(boolean wasU18NotificationDisplayed) { + mAdExtDataManager.setNotificationDisplayed(wasU18NotificationDisplayed); + } + + /** GA UX is never shown on R, so this info is not stored. */ + @Override + public boolean wasGaUxNotificationDisplayed() { + return false; + } + + /** Beta UX is never shown on R, so this info is not stored. */ + @Override + public boolean wasNotificationDisplayed() { + return false; + } + + /** Android R only U18 notification is allowed to be displayed. */ + @Override + public boolean wasU18NotificationDisplayed() { + return mAdExtDataManager.getNotificationDisplayed(); + } + + private static String getAdExtExceptionMessage(String illegalAction) { + return String.format( + "Attempting to %s using PPAPI_AND_ADEXT_SERVICE consent source of truth!", + illegalAction); + } +} diff --git a/adservices/service-core/java/com/android/adservices/service/consent/AppConsentStorageManager.java b/adservices/service-core/java/com/android/adservices/service/consent/AppConsentStorageManager.java index a7dc3050a..af1b2ddd0 100644 --- a/adservices/service-core/java/com/android/adservices/service/consent/AppConsentStorageManager.java +++ b/adservices/service-core/java/com/android/adservices/service/consent/AppConsentStorageManager.java @@ -31,6 +31,7 @@ import com.android.adservices.service.ui.ux.collection.PrivacySandboxUxCollectio import com.google.common.collect.ImmutableList; import java.io.IOException; +import java.util.Objects; import java.util.stream.Collectors; /** @@ -131,7 +132,8 @@ public class AppConsentStorageManager implements IConsentStorage { */ @Override public AdServicesApiConsent getConsent(AdServicesApiType apiType) { - return AdServicesApiConsent.getConsent(mDatastore.get(apiType.toPpApiDatastoreKey())); + return AdServicesApiConsent.getConsent( + Objects.requireNonNullElse(mDatastore.get(apiType.toPpApiDatastoreKey()), false)); } /** @@ -153,6 +155,15 @@ public class AppConsentStorageManager implements IConsentStorage { return PrivacySandboxFeatureType.PRIVACY_SANDBOX_UNSUPPORTED; } + /** Set the current privacy sandbox feature. */ + @Override + public void setCurrentPrivacySandboxFeature(PrivacySandboxFeatureType featureType) + throws IOException { + for (PrivacySandboxFeatureType currentFeatureType : PrivacySandboxFeatureType.values()) { + mDatastore.put(currentFeatureType.name(), currentFeatureType == featureType); + } + } + /** * Retrieves the default AdId state. * @@ -160,7 +171,8 @@ public class AppConsentStorageManager implements IConsentStorage { */ @Override public boolean getDefaultAdIdState() { - return mDatastore.get(ConsentConstants.DEFAULT_AD_ID_STATE); + return Objects.requireNonNullElse( + mDatastore.get(ConsentConstants.DEFAULT_AD_ID_STATE), false); } /** @@ -170,7 +182,9 @@ public class AppConsentStorageManager implements IConsentStorage { */ @Override public AdServicesApiConsent getDefaultConsent(AdServicesApiType apiType) { - return AdServicesApiConsent.getConsent(mDatastore.get(apiType.toPpApiDatastoreKey())); + return AdServicesApiConsent.getConsent( + Objects.requireNonNullElse( + mDatastore.get(apiType.toDefaultConsentDatastoreKey()), false)); } /** Returns current enrollment channel. */ @@ -215,16 +229,34 @@ public class AppConsentStorageManager implements IConsentStorage { return mUxStatesDao.getUx(); } + /** Set the current UX to storage. */ + @Override + public void setUx(PrivacySandboxUxCollection ux) { + mUxStatesDao.setUx(ux); + } + /** Returns whether the isAdIdEnabled bit is true. */ @Override public boolean isAdIdEnabled() { - return mDatastore.get(ConsentConstants.IS_AD_ID_ENABLED); + return Objects.requireNonNullElse(mDatastore.get(ConsentConstants.IS_AD_ID_ENABLED), false); + } + + /** Set the AdIdEnabled bit to storage. */ + @Override + public void setAdIdEnabled(boolean isAdIdEnabled) throws IOException { + mDatastore.put(ConsentConstants.IS_AD_ID_ENABLED, isAdIdEnabled); } /** Returns whether the isAdultAccount bit is true. */ @Override public boolean isAdultAccount() { - return mDatastore.get(ConsentConstants.IS_ADULT_ACCOUNT); + return Objects.requireNonNullElse(mDatastore.get(ConsentConstants.IS_ADULT_ACCOUNT), false); + } + + /** Set the AdultAccount bit to storage. */ + @Override + public void setAdultAccount(boolean isAdultAccount) throws IOException { + mDatastore.put(ConsentConstants.IS_ADULT_ACCOUNT, isAdultAccount); } /** @@ -241,7 +273,8 @@ public class AppConsentStorageManager implements IConsentStorage { @Override public boolean isConsentRevokedForApp(String packageName) throws IllegalArgumentException { try { - return mAppConsentDao.isConsentRevokedForApp(packageName); + return Objects.requireNonNullElse( + mAppConsentDao.isConsentRevokedForApp(packageName), false); } catch (IOException exception) { LogUtil.e(exception, "FLEDGE consent check failed due to IOException"); } @@ -251,13 +284,26 @@ public class AppConsentStorageManager implements IConsentStorage { /** Returns whether the isEntryPointEnabled bit is true. */ @Override public boolean isEntryPointEnabled() { - return mDatastore.get(ConsentConstants.IS_ENTRY_POINT_ENABLED); + return Objects.requireNonNullElse( + mDatastore.get(ConsentConstants.IS_ENTRY_POINT_ENABLED), false); + } + + /** Set the EntryPointEnabled bit to storage . */ + @Override + public void setEntryPointEnabled(boolean isEntryPointEnabled) throws IOException { + mDatastore.put(ConsentConstants.IS_ENTRY_POINT_ENABLED, isEntryPointEnabled); } /** Returns whether the isU18Account bit is true. */ @Override public boolean isU18Account() { - return mDatastore.get(ConsentConstants.IS_U18_ACCOUNT); + return Objects.requireNonNullElse(mDatastore.get(ConsentConstants.IS_U18_ACCOUNT), false); + } + + /** Set the U18Account bit to storage. */ + @Override + public void setU18Account(boolean isU18Account) throws IOException { + mDatastore.put(ConsentConstants.IS_U18_ACCOUNT, isU18Account); } /** Saves the default AdId state bit to data stores based on source of truth. */ @@ -270,7 +316,7 @@ public class AppConsentStorageManager implements IConsentStorage { @Override public void recordDefaultConsent(AdServicesApiType apiType, boolean defaultConsent) throws IOException { - mDatastore.put(apiType.toPpApiDatastoreKey(), defaultConsent); + mDatastore.put(apiType.toDefaultConsentDatastoreKey(), defaultConsent); } /** @@ -311,22 +357,9 @@ public class AppConsentStorageManager implements IConsentStorage { } } - /** Set the AdIdEnabled bit to storage. */ - @Override - public void setAdIdEnabled(boolean isAdIdEnabled) throws IOException { - // test - mDatastore.put(ConsentConstants.IS_AD_ID_ENABLED, isAdIdEnabled); - } - - /** Set the AdultAccount bit to storage. */ - @Override - public void setAdultAccount(boolean isAdultAccount) throws IOException { - mDatastore.put(ConsentConstants.IS_ADULT_ACCOUNT, isAdultAccount); - } - /** * Sets the consent for this user ID for this API type in AppSearch. If we do not get - * confirmation that the write was successful, then we throw an exception so that user does not + * confirmation that to write was successful, then we throw an exception so that user does not * incorrectly think that the consent is updated. * * @throws IOException if the operation fails @@ -361,7 +394,7 @@ public class AppConsentStorageManager implements IConsentStorage { @Override public boolean setConsentForAppIfNew(String packageName, boolean isConsentRevoked) throws IllegalArgumentException { - // test + // TODO(b/317595641) clean up setConsentForAppIfNew logic try { return mAppConsentDao.setConsentForAppIfNew(packageName, isConsentRevoked); } catch (IOException exception) { @@ -370,15 +403,6 @@ public class AppConsentStorageManager implements IConsentStorage { } } - /** Set the current privacy sandbox feature. */ - @Override - public void setCurrentPrivacySandboxFeature(PrivacySandboxFeatureType featureType) - throws IOException { - for (PrivacySandboxFeatureType currentFeatureType : PrivacySandboxFeatureType.values()) { - mDatastore.put(currentFeatureType.name(), currentFeatureType == featureType); - } - } - /** Set the current enrollment channel to storage. */ @Override public void setEnrollmentChannel( @@ -386,18 +410,6 @@ public class AppConsentStorageManager implements IConsentStorage { mUxStatesDao.setEnrollmentChannel(ux, channel); } - /** Set the EntryPointEnabled bit to storage . */ - @Override - public void setEntryPointEnabled(boolean isEntryPointEnabled) throws IOException { - mDatastore.put(ConsentConstants.IS_ENTRY_POINT_ENABLED, isEntryPointEnabled); - } - - /** Set the U18Account bit to storage. */ - @Override - public void setU18Account(boolean isU18Account) throws IOException { - mDatastore.put(ConsentConstants.IS_U18_ACCOUNT, isU18Account); - } - /** Set the U18NotificationDisplayed bit to storage. */ @Override public void setU18NotificationDisplayed(boolean wasU18NotificationDisplayed) @@ -406,12 +418,6 @@ public class AppConsentStorageManager implements IConsentStorage { ConsentConstants.WAS_U18_NOTIFICATION_DISPLAYED, wasU18NotificationDisplayed); } - /** Set the current UX to storage. */ - @Override - public void setUx(PrivacySandboxUxCollection ux) { - mUxStatesDao.setUx(ux); - } - /** * Retrieves if GA UX notification has been displayed. * @@ -419,7 +425,8 @@ public class AppConsentStorageManager implements IConsentStorage { */ @Override public boolean wasGaUxNotificationDisplayed() { - return mDatastore.get(ConsentConstants.GA_UX_NOTIFICATION_DISPLAYED_ONCE); + return Objects.requireNonNullElse( + mDatastore.get(ConsentConstants.GA_UX_NOTIFICATION_DISPLAYED_ONCE), false); } /** @@ -429,12 +436,16 @@ public class AppConsentStorageManager implements IConsentStorage { */ @Override public boolean wasNotificationDisplayed() { - return mDatastore.get(ConsentConstants.NOTIFICATION_DISPLAYED_ONCE); + return Objects.requireNonNullElse( + mDatastore.get(ConsentConstants.NOTIFICATION_DISPLAYED_ONCE), false); } /** Returns whether the wasU18NotificationDisplayed bit is true. */ @Override public boolean wasU18NotificationDisplayed() { - return mDatastore.get(ConsentConstants.WAS_U18_NOTIFICATION_DISPLAYED); + return Objects.requireNonNullElse( + mDatastore.get(ConsentConstants.WAS_U18_NOTIFICATION_DISPLAYED), false); } + + } diff --git a/adservices/service-core/java/com/android/adservices/service/consent/ConsentCompositeStorage.java b/adservices/service-core/java/com/android/adservices/service/consent/ConsentCompositeStorage.java index 730b0788b..75229cb1c 100644 --- a/adservices/service-core/java/com/android/adservices/service/consent/ConsentCompositeStorage.java +++ b/adservices/service-core/java/com/android/adservices/service/consent/ConsentCompositeStorage.java @@ -22,13 +22,18 @@ import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICE import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ERROR_WHILE_GET_CONSENT; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__PRIVACY_SANDBOX_SAVE_FAILURE; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX; +import static com.android.adservices.service.ui.ux.collection.PrivacySandboxUxCollection.U18_UX; import android.annotation.NonNull; import android.annotation.SuppressLint; +import android.os.Build; + +import androidx.annotation.RequiresApi; import com.android.adservices.LogUtil; import com.android.adservices.errorlogging.ErrorLogUtil; import com.android.adservices.service.common.feature.PrivacySandboxFeatureType; +import com.android.adservices.service.exception.ConsentStorageDeferException; import com.android.adservices.service.ui.enrollment.collection.PrivacySandboxEnrollmentChannelCollection; import com.android.adservices.service.ui.ux.collection.PrivacySandboxUxCollection; import com.android.internal.annotations.VisibleForTesting; @@ -36,14 +41,22 @@ import com.android.internal.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import java.io.IOException; -import java.util.List; /** * CompositeStorage to handle read/write user's to multiple source of truth * * <p>Every source of truth should have its own dedicated storage class that implements the * IConsentStorage interface, and pass in the instances to ConsentCompositeStorage. + * + * <p>By default, when caller set value to the storage, CompositeStorage will iterate through every + * instance in mConsentStorageList and call the corresponding method. For getter, CompositeStorage + * will only return the first one. + * + * <p>If the method not available in some implementation, the implementation class should throw + * {code ConsentStorageDeferException}, CompositeStorage will try to get the result for next + * instance. */ +@RequiresApi(Build.VERSION_CODES.S) public class ConsentCompositeStorage implements IConsentStorage { private static final int UNKNOWN = 0; private final ImmutableList<IConsentStorage> mConsentStorageList; @@ -54,7 +67,6 @@ public class ConsentCompositeStorage implements IConsentStorage { * @param consentStorageList storage implementation instance list. */ public ConsentCompositeStorage(ImmutableList<IConsentStorage> consentStorageList) { - assert (consentStorageList.size() > 0); if (consentStorageList == null || consentStorageList.isEmpty()) { throw new IllegalArgumentException("consent storage list can not be empty!"); } @@ -67,9 +79,21 @@ public class ConsentCompositeStorage implements IConsentStorage { * <p>This should be called when the Privacy Sandbox has been disabled. */ @Override - public void clearAllAppConsentData() throws IOException { + public void clearAllAppConsentData() { for (IConsentStorage storage : getConsentStorageList()) { - storage.clearAllAppConsentData(); + try { + storage.clearAllAppConsentData(); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); + } catch (IOException e) { + ErrorLogUtil.e( + e, + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ERROR_WHILE_GET_CONSENT, + AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); + throw new RuntimeException(e); + } } } @@ -80,12 +104,19 @@ public class ConsentCompositeStorage implements IConsentStorage { */ @Override public void clearConsentForUninstalledApp(@NonNull String packageName) { - try { - for (IConsentStorage storage : getConsentStorageList()) { + for (IConsentStorage storage : getConsentStorageList()) { + try { storage.clearConsentForUninstalledApp(packageName); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); + } catch (IOException e) { + ErrorLogUtil.e( + e, + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ERROR_WHILE_GET_CONSENT, + AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); } - } catch (IOException e) { - throw new RuntimeException(e); } } @@ -97,12 +128,18 @@ public class ConsentCompositeStorage implements IConsentStorage { */ @Override public void clearConsentForUninstalledApp(String packageName, int packageUid) { - for (IConsentStorage storage : getConsentStorageList()) { try { storage.clearConsentForUninstalledApp(packageName, packageUid); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); } catch (IOException e) { - LogUtil.e(getClass().getSimpleName() + " failed. " + e.getMessage()); + ErrorLogUtil.e( + e, + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ERROR_WHILE_GET_CONSENT, + AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); } } } @@ -117,8 +154,15 @@ public class ConsentCompositeStorage implements IConsentStorage { for (IConsentStorage storage : getConsentStorageList()) { try { storage.clearKnownAppsWithConsent(); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); } catch (IOException e) { - throw new RuntimeException(e); + ErrorLogUtil.e( + e, + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ERROR_WHILE_GET_CONSENT, + AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); } } } @@ -126,15 +170,24 @@ public class ConsentCompositeStorage implements IConsentStorage { /** * @return an {@link ImmutableList} of all known apps in the database that have had user consent * revoked - * @throws IOException if the operation fails */ @Override public ImmutableList<String> getAppsWithRevokedConsent() { - try { - return getPrimaryStorage().getAppsWithRevokedConsent(); - } catch (IOException e) { - throw new RuntimeException(e); + for (IConsentStorage storage : getConsentStorageList()) { + try { + return storage.getAppsWithRevokedConsent(); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); + } catch (IOException e) { + ErrorLogUtil.e( + e, + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ERROR_WHILE_GET_CONSENT, + AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); + } } + return ImmutableList.of(); } /** @@ -147,15 +200,22 @@ public class ConsentCompositeStorage implements IConsentStorage { */ @Override public AdServicesApiConsent getConsent(AdServicesApiType apiType) { - try { - return getPrimaryStorage().getConsent(apiType); - } catch (IOException e) { - ErrorLogUtil.e( - e, - AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ERROR_WHILE_GET_CONSENT, - AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); - return AdServicesApiConsent.REVOKED; + for (IConsentStorage storage : getConsentStorageList()) { + try { + return storage.getConsent(apiType); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); + } catch (IOException | RuntimeException e) { + ErrorLogUtil.e( + e, + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ERROR_WHILE_GET_CONSENT, + AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); + return AdServicesApiConsent.REVOKED; + } } + return AdServicesApiConsent.REVOKED; } /** @@ -178,15 +238,42 @@ public class ConsentCompositeStorage implements IConsentStorage { */ @Override public PrivacySandboxFeatureType getCurrentPrivacySandboxFeature() { - try { - return getPrimaryStorage().getCurrentPrivacySandboxFeature(); - } catch (IOException e) { - ErrorLogUtil.e( - e, - AD_SERVICES_ERROR_REPORTED__ERROR_CODE__PRIVACY_SANDBOX_SAVE_FAILURE, - AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); - LogUtil.e(getClass().getSimpleName() + " failed. " + e.getMessage()); - return PrivacySandboxFeatureType.PRIVACY_SANDBOX_UNSUPPORTED; + for (IConsentStorage storage : getConsentStorageList()) { + try { + return storage.getCurrentPrivacySandboxFeature(); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); + } catch (IOException | RuntimeException e) { + ErrorLogUtil.e( + e, + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__PRIVACY_SANDBOX_SAVE_FAILURE, + AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); + return PrivacySandboxFeatureType.PRIVACY_SANDBOX_UNSUPPORTED; + } + } + return PrivacySandboxFeatureType.PRIVACY_SANDBOX_UNSUPPORTED; + } + + /** Sets the current privacy sandbox feature. */ + @Override + public void setCurrentPrivacySandboxFeature(PrivacySandboxFeatureType featureType) { + for (IConsentStorage storage : getConsentStorageList()) { + try { + storage.setCurrentPrivacySandboxFeature(featureType); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); + } catch (IOException e) { + ErrorLogUtil.e( + e, + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__PRIVACY_SANDBOX_SAVE_FAILURE, + AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); + throw new RuntimeException( + getClass().getSimpleName() + " failed. " + e.getMessage()); + } } } @@ -197,7 +284,19 @@ public class ConsentCompositeStorage implements IConsentStorage { */ @Override public boolean getDefaultAdIdState() { - return getPrimaryStorage().getDefaultAdIdState(); + for (IConsentStorage storage : getConsentStorageList()) { + try { + return storage.getDefaultAdIdState(); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); + } catch (IOException e) { + logDatastoreWhileRecordingDefaultConsent(e); + return false; + } + } + return false; } /** @@ -207,22 +306,37 @@ public class ConsentCompositeStorage implements IConsentStorage { */ @Override public AdServicesApiConsent getDefaultConsent(AdServicesApiType apiType) { - try { - return getPrimaryStorage().getDefaultConsent(apiType); - } catch (IOException e) { - ErrorLogUtil.e( - e, - AD_SERVICES_ERROR_REPORTED__ERROR_CODE__DATASTORE_EXCEPTION_WHILE_RECORDING_DEFAULT_CONSENT, - AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); - return AdServicesApiConsent.REVOKED; + for (IConsentStorage storage : getConsentStorageList()) { + try { + return storage.getDefaultConsent(apiType); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); + } catch (IOException e) { + logDatastoreWhileRecordingDefaultConsent(e); + return AdServicesApiConsent.REVOKED; + } } + return AdServicesApiConsent.REVOKED; } /** Returns current enrollment channel. */ @Override public PrivacySandboxEnrollmentChannelCollection getEnrollmentChannel( PrivacySandboxUxCollection ux) { - return getPrimaryStorage().getEnrollmentChannel(ux); + for (IConsentStorage storage : getConsentStorageList()) { + try { + return storage.getEnrollmentChannel(ux); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); + } catch (IOException e) { + logDatastoreWhileRecordingDefaultConsent(e); + } + } + return null; } /** @@ -233,26 +347,20 @@ public class ConsentCompositeStorage implements IConsentStorage { */ @Override public ImmutableList<String> getKnownAppsWithConsent() { - try { - return getPrimaryStorage().getKnownAppsWithConsent(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - /** - * Gets first storage instance, for read operations, should always return from the first source - * of truth. - * - * @return first instance of IConsentStorage. - */ - @VisibleForTesting - public IConsentStorage getPrimaryStorage() { - List<IConsentStorage> storageList = getConsentStorageList(); - if (storageList.isEmpty()) { - throw new IllegalStateException("Consent Storage List is empty."); + for (IConsentStorage storage : getConsentStorageList()) { + try { + return storage.getKnownAppsWithConsent(); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); + } catch (IOException e) { + logDatastoreWhileRecordingDefaultConsent(e); + } catch (IllegalStateException e) { + LogUtil.i("IllegalStateException" + e); + } } - return storageList.get(0); + return ImmutableList.of(); } /** @@ -262,33 +370,121 @@ public class ConsentCompositeStorage implements IConsentStorage { */ @Override public int getUserManualInteractionWithConsent() { - try { - return getPrimaryStorage().getUserManualInteractionWithConsent(); - } catch (IOException e) { - ErrorLogUtil.e( - e, - AD_SERVICES_ERROR_REPORTED__ERROR_CODE__DATASTORE_EXCEPTION_WHILE_RECORDING_MANUAL_CONSENT_INTERACTION, - AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); - return UNKNOWN; + for (IConsentStorage storage : getConsentStorageList()) { + try { + return storage.getUserManualInteractionWithConsent(); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); + } catch (IOException e) { + logDatastoreManualInteractionException(e); + return UNKNOWN; + } } + return 0; } /** Returns current UX. */ @Override public PrivacySandboxUxCollection getUx() { - return getPrimaryStorage().getUx(); + for (IConsentStorage storage : getConsentStorageList()) { + try { + return storage.getUx(); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); + } catch (IOException e) { + logDatastoreManualInteractionException(e); + throw new RuntimeException(e); + } + } + return null; + } + + /** Sets the current UX to storage. */ + @Override + public void setUx(PrivacySandboxUxCollection ux) { + for (IConsentStorage storage : getConsentStorageList()) { + try { + storage.setUx(ux); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); + } catch (IOException e) { + logDataStoreWhileRecordingException(e); + } + } } /** Returns whether the isAdIdEnabled bit is true. */ @Override public boolean isAdIdEnabled() { - return getPrimaryStorage().isAdIdEnabled(); + for (IConsentStorage storage : getConsentStorageList()) { + try { + return storage.isAdIdEnabled(); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); + } catch (IOException e) { + logDatastoreManualInteractionException(e); + throw new RuntimeException(e); + } + } + return false; + } + + /** Set the AdIdEnabled bit to storage. */ + @Override + public void setAdIdEnabled(boolean isAdIdEnabled) { + for (IConsentStorage storage : getConsentStorageList()) { + try { + storage.setAdIdEnabled(isAdIdEnabled); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); + } catch (IOException e) { + logDataStoreWhileRecordingException(e); + } + } } /** Returns whether the isAdultAccount bit is true. */ @Override public boolean isAdultAccount() { - return getPrimaryStorage().isAdultAccount(); + for (IConsentStorage storage : getConsentStorageList()) { + try { + return storage.isAdultAccount(); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); + } catch (IOException e) { + logDatastoreManualInteractionException(e); + throw new RuntimeException(e); + } + } + return false; + } + + /** Set the AdultAccount bit to storage. */ + @Override + public void setAdultAccount(boolean isAdultAccount) { + for (IConsentStorage storage : getConsentStorageList()) { + try { + storage.setAdultAccount(isAdultAccount); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); + } catch (IOException e) { + logDataStoreWhileRecordingException(e); + } + } } /** @@ -300,23 +496,90 @@ public class ConsentCompositeStorage implements IConsentStorage { * * @throws IllegalArgumentException if the package name is invalid or not found as an installed * application - * @throws IOException if the operation fails */ @Override public boolean isConsentRevokedForApp(String packageName) throws IllegalArgumentException { - return getPrimaryStorage().isConsentRevokedForApp(packageName); + for (IConsentStorage storage : getConsentStorageList()) { + try { + return storage.isConsentRevokedForApp(packageName); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); + } catch (IOException e) { + logDatastoreManualInteractionException(e); + throw new RuntimeException(e); + } + } + return false; } /** Returns whether the isEntryPointEnabled bit is true. */ @Override public boolean isEntryPointEnabled() { - return getPrimaryStorage().isEntryPointEnabled(); + for (IConsentStorage storage : getConsentStorageList()) { + try { + return storage.isEntryPointEnabled(); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); + } catch (IOException e) { + logDatastoreManualInteractionException(e); + throw new RuntimeException(e); + } + } + return false; + } + + /** Sets the EntryPointEnabled bit to storage . */ + @Override + public void setEntryPointEnabled(boolean isEntryPointEnabled) { + for (IConsentStorage storage : getConsentStorageList()) { + try { + storage.setEntryPointEnabled(isEntryPointEnabled); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); + } catch (IOException e) { + logDataStoreWhileRecordingException(e); + } + } } /** Returns whether the isU18Account bit is true. */ @Override public boolean isU18Account() { - return getPrimaryStorage().isU18Account(); + for (IConsentStorage storage : getConsentStorageList()) { + try { + return storage.isU18Account(); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); + } catch (IOException e) { + logDatastoreManualInteractionException(e); + throw new RuntimeException(e); + } + } + return false; + } + + /** Sets the U18Account bit to storage. */ + @Override + public void setU18Account(boolean isU18Account) { + for (IConsentStorage storage : getConsentStorageList()) { + try { + storage.setU18Account(isU18Account); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); + } catch (IOException e) { + logDataStoreWhileRecordingException(e); + } + } } /** Saves the default AdId state bit to data stores based on source of truth. */ @@ -325,7 +588,12 @@ public class ConsentCompositeStorage implements IConsentStorage { for (IConsentStorage storage : getConsentStorageList()) { try { storage.recordDefaultAdIdState(defaultAdIdState); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); } catch (IOException e) { + logDatastoreManualInteractionException(e); throw new RuntimeException(e); } } @@ -337,7 +605,12 @@ public class ConsentCompositeStorage implements IConsentStorage { for (IConsentStorage storage : getConsentStorageList()) { try { storage.recordDefaultConsent(apiType, defaultConsent); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); } catch (IOException e) { + logDatastoreManualInteractionException(e); throw new RuntimeException(e); } } @@ -352,7 +625,12 @@ public class ConsentCompositeStorage implements IConsentStorage { for (IConsentStorage storage : getConsentStorageList()) { try { storage.recordGaUxNotificationDisplayed(wasGaUxDisplayed); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); } catch (IOException e) { + logDatastoreManualInteractionException(e); throw new RuntimeException(e); } } @@ -366,10 +644,13 @@ public class ConsentCompositeStorage implements IConsentStorage { public void recordNotificationDisplayed(boolean wasNotificationDisplayed) { for (IConsentStorage storage : getConsentStorageList()) { try { - storage.recordNotificationDisplayed(wasNotificationDisplayed); - + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); } catch (IOException e) { + logDatastoreManualInteractionException(e); throw new RuntimeException(e); } } @@ -381,53 +662,66 @@ public class ConsentCompositeStorage implements IConsentStorage { for (IConsentStorage storage : getConsentStorageList()) { try { storage.recordUserManualInteractionWithConsent(interaction); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); } catch (IOException e) { - ErrorLogUtil.e( - e, - AD_SERVICES_ERROR_REPORTED__ERROR_CODE__DATASTORE_EXCEPTION_WHILE_RECORDING_MANUAL_CONSENT_INTERACTION, - AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); + logDatastoreManualInteractionException(e); throw new RuntimeException( getClass().getSimpleName() + " failed. " + e.getMessage()); } } } - /** Set the AdIdEnabled bit to storage. */ + /** + * Sets the consent for this user ID for this API type in AppSearch. If we do not get + * confirmation that the write was successful, then we throw an exception so that user does not + * incorrectly think that the consent is updated. + */ @Override - public void setAdIdEnabled(boolean isAdIdEnabled) { - for (IConsentStorage storage : getConsentStorageList()) { - try { - storage.setAdIdEnabled(isAdIdEnabled); - } catch (IOException e) { - throw new RuntimeException(e); - } + public void setConsent(AdServicesApiType apiType, boolean isGiven) { + setConsentToApiType(apiType, isGiven); + if (apiType == AdServicesApiType.ALL_API) { + return; } + setAggregatedConsent(); } - /** Set the AdultAccount bit to storage. */ - @Override - public void setAdultAccount(boolean isAdultAccount) { + private void setConsentToApiType(AdServicesApiType apiType, boolean isGiven) { for (IConsentStorage storage : getConsentStorageList()) { try { - storage.setAdultAccount(isAdultAccount); + storage.setConsent(apiType, isGiven); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); } catch (IOException e) { - throw new RuntimeException(e); + logDataStoreWhileRecordingException(e); } } } - /** - * Sets the consent for this user ID for this API type in AppSearch. If we do not get - * confirmation that the write was successful, then we throw an exception so that user does not - * incorrectly think that the consent is updated. - * - * @throws IOException if the operation fails - */ - @Override - public void setConsent(AdServicesApiType apiType, boolean isGiven) throws IOException { - for (IConsentStorage storage : getConsentStorageList()) { - storage.setConsent(apiType, isGiven); + // Set the aggregated consent so that after the rollback of the module + // and the flag which controls the consent flow everything works as expected. + // The problematic edge case which is covered: + // T1: AdServices is installed in pre-GA UX version and the consent is given + // T2: AdServices got upgraded to GA UX binary and GA UX feature flag is enabled + // T3: Consent for the Topics API got revoked + // T4: AdServices got rolledback and the feature flags which controls consent flow + // (SYSTEM_SERVER_ONLY and DUAL_WRITE) also got rolledback + // T5: Restored consent should be revoked + @VisibleForTesting + void setAggregatedConsent() { + if (getUx() == U18_UX) { + // The edge case does not apply to U18 UX. + return; } + setConsentToApiType( + AdServicesApiType.ALL_API, + getConsent(AdServicesApiType.TOPICS).isGiven() + && getConsent(AdServicesApiType.MEASUREMENTS).isGiven() + && getConsent(AdServicesApiType.FLEDGE).isGiven()); } /** @@ -441,7 +735,12 @@ public class ConsentCompositeStorage implements IConsentStorage { for (IConsentStorage storage : getConsentStorageList()) { try { storage.setConsentForApp(packageName, isConsentRevoked); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); } catch (IOException e) { + logDataStoreWhileRecordingException(e); throw new RuntimeException(e); } } @@ -459,34 +758,25 @@ public class ConsentCompositeStorage implements IConsentStorage { */ @Override public boolean setConsentForAppIfNew(String packageName, boolean isConsentRevoked) { - boolean ret = false; - for (IConsentStorage storage : getConsentStorageList()) { - try { - // Same as original logic, only return the last one. - ret = storage.setConsentForAppIfNew(packageName, isConsentRevoked); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - // LogUtil.e(ConsentConstants.ERROR_MESSAGE_INVALID_CONSENT_SOURCE_OF_TRUTH); - return ret; - } - - /** Sets the current privacy sandbox feature. */ - @Override - public void setCurrentPrivacySandboxFeature(PrivacySandboxFeatureType featureType) { - for (IConsentStorage storage : getConsentStorageList()) { + for (IConsentStorage storage : getConsentStorageList().reverse()) { try { - storage.setCurrentPrivacySandboxFeature(featureType); + // Same as original logic, call the PPAPI first + // TODO(b/317595641): clean up the logic + boolean ret = storage.setConsentForAppIfNew(packageName, isConsentRevoked); + if (ret) { + return true; + } + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); } catch (IOException e) { - ErrorLogUtil.e( - e, - AD_SERVICES_ERROR_REPORTED__ERROR_CODE__PRIVACY_SANDBOX_SAVE_FAILURE, - AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); - throw new RuntimeException( - getClass().getSimpleName() + " failed. " + e.getMessage()); + LogUtil.e(ConsentConstants.ERROR_MESSAGE_INVALID_CONSENT_SOURCE_OF_TRUTH); + logDataStoreWhileRecordingException(e); + return false; } } + return false; } /** Sets the current enrollment channel to storage. */ @@ -494,50 +784,35 @@ public class ConsentCompositeStorage implements IConsentStorage { public void setEnrollmentChannel( PrivacySandboxUxCollection ux, PrivacySandboxEnrollmentChannelCollection channel) { for (IConsentStorage storage : getConsentStorageList()) { - storage.setEnrollmentChannel(ux, channel); - } - } - - /** Sets the EntryPointEnabled bit to storage . */ - @Override - public void setEntryPointEnabled(boolean isEntryPointEnabled) { - for (IConsentStorage storage : getConsentStorageList()) { try { - storage.setEntryPointEnabled(isEntryPointEnabled); + storage.setEnrollmentChannel(ux, channel); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); } catch (IOException e) { - throw new RuntimeException(e); + logDataStoreWhileRecordingException(e); } } } - /** Sets the U18Account bit to storage. */ - @Override - public void setU18Account(boolean isU18Account) throws IOException { - for (IConsentStorage storage : getConsentStorageList()) { - storage.setU18Account(isU18Account); - } - } - /** Sets the U18NotificationDisplayed bit to storage. */ @Override public void setU18NotificationDisplayed(boolean wasU18NotificationDisplayed) { for (IConsentStorage storage : getConsentStorageList()) { try { storage.setU18NotificationDisplayed(wasU18NotificationDisplayed); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); } catch (IOException e) { + logDataStoreWhileRecordingException(e); throw new RuntimeException(e); } } } - /** Sets the current UX to storage. */ - @Override - public void setUx(PrivacySandboxUxCollection ux) { - for (IConsentStorage storage : getConsentStorageList()) { - storage.setUx(ux); - } - } - /** * Retrieves if GA UX notification has been displayed. * @@ -545,7 +820,19 @@ public class ConsentCompositeStorage implements IConsentStorage { */ @Override public boolean wasGaUxNotificationDisplayed() { - return getPrimaryStorage().wasGaUxNotificationDisplayed(); + for (IConsentStorage storage : getConsentStorageList()) { + try { + return storage.wasGaUxNotificationDisplayed(); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); + } catch (IOException e) { + logDataStoreWhileRecordingException(e); + return false; + } + } + return false; } /** @@ -556,20 +843,57 @@ public class ConsentCompositeStorage implements IConsentStorage { @SuppressLint("NameOfTheRuleToSuppress") @Override public boolean wasNotificationDisplayed() { - try { - return getPrimaryStorage().wasNotificationDisplayed(); - } catch (IOException e) { - ErrorLogUtil.e( - e, - AD_SERVICES_ERROR_REPORTED__ERROR_CODE__DATASTORE_EXCEPTION_WHILE_RECORDING_NOTIFICATION, - AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); - return false; + for (IConsentStorage storage : getConsentStorageList()) { + try { + return storage.wasNotificationDisplayed(); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); + } catch (IOException e) { + logDataStoreWhileRecordingException(e); + return false; + } } + return false; } /** Returns whether the wasU18NotificationDisplayed bit is true. */ @Override public boolean wasU18NotificationDisplayed() { - return getPrimaryStorage().wasU18NotificationDisplayed(); + for (IConsentStorage storage : getConsentStorageList()) { + try { + return storage.wasU18NotificationDisplayed(); + } catch (ConsentStorageDeferException e) { + LogUtil.i( + "Skip current storage manager %s. Defer to next one", + storage.getClass().getSimpleName()); + } catch (IOException e) { + logDataStoreWhileRecordingException(e); + return false; + } + } + return false; + } + + private static void logDataStoreWhileRecordingException(IOException e) { + ErrorLogUtil.e( + e, + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__DATASTORE_EXCEPTION_WHILE_RECORDING_NOTIFICATION, + AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); + } + + private static void logDatastoreManualInteractionException(IOException e) { + ErrorLogUtil.e( + e, + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__DATASTORE_EXCEPTION_WHILE_RECORDING_MANUAL_CONSENT_INTERACTION, + AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); + } + + private static void logDatastoreWhileRecordingDefaultConsent(IOException e) { + ErrorLogUtil.e( + e, + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__DATASTORE_EXCEPTION_WHILE_RECORDING_DEFAULT_CONSENT, + AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); } } diff --git a/adservices/service-core/java/com/android/adservices/service/consent/ConsentConstants.java b/adservices/service-core/java/com/android/adservices/service/consent/ConsentConstants.java index 8407ed08e..582f2a141 100644 --- a/adservices/service-core/java/com/android/adservices/service/consent/ConsentConstants.java +++ b/adservices/service-core/java/com/android/adservices/service/consent/ConsentConstants.java @@ -17,7 +17,6 @@ package com.android.adservices.service.consent; import com.android.adservices.service.common.compat.FileCompatUtils; -import com.android.internal.annotations.VisibleForTesting; /** ConsentManager related Constants. */ public class ConsentConstants { @@ -37,8 +36,7 @@ public class ConsentConstants { public static final String DEFAULT_AD_ID_STATE = "DEFAULT_AD_ID_STATE"; - @VisibleForTesting - static final String MANUAL_INTERACTION_WITH_CONSENT_RECORDED = + public static final String MANUAL_INTERACTION_WITH_CONSENT_RECORDED = "MANUAL_INTERACTION_WITH_CONSENT_RECORDED"; public static final String CONSENT_KEY = "CONSENT"; diff --git a/adservices/service-core/java/com/android/adservices/service/consent/ConsentManager.java b/adservices/service-core/java/com/android/adservices/service/consent/ConsentManager.java index 900ffd58f..171970b3b 100644 --- a/adservices/service-core/java/com/android/adservices/service/consent/ConsentManager.java +++ b/adservices/service-core/java/com/android/adservices/service/consent/ConsentManager.java @@ -39,6 +39,7 @@ import android.app.job.JobScheduler; import android.content.Context; import android.content.SharedPreferences; import android.os.Build; +import android.os.Trace; import androidx.annotation.RequiresApi; @@ -206,6 +207,7 @@ public class ConsentManager { public static ConsentManager getInstance(@NonNull Context context) { Objects.requireNonNull(context); + Trace.beginSection("ConsentManager#Initialization"); if (sConsentManager == null) { synchronized (LOCK) { if (sConsentManager == null) { @@ -253,7 +255,8 @@ public class ConsentManager { context, datastore, appSearchConsentManager, - adServicesExtDataManager); + adServicesExtDataManager, + statsdAdServicesLogger); } } @@ -288,6 +291,7 @@ public class ConsentManager { } } } + Trace.endSection(); return sConsentManager; } @@ -2154,6 +2158,7 @@ public class ConsentManager { for back compat. */ ThrowableSetter ppapiAndAdExtDataServiceSetter, ErrorLogger errorLogger) { + Trace.beginSection("ConsentManager#WriteOperation"); mReadWriteLock.writeLock().lock(); try { switch (mConsentSourceOfTruth) { @@ -2185,11 +2190,12 @@ public class ConsentManager { if (errorLogger != null) { errorLogger.apply(e); } - throw new RuntimeException(getClass().getSimpleName() + " failed. " + e.getMessage()); + throw new RuntimeException( + getClass().getSimpleName() + " failed. " + e.getMessage(), e); } finally { mReadWriteLock.writeLock().unlock(); + Trace.endSection(); } - } @FunctionalInterface @@ -2218,6 +2224,7 @@ public class ConsentManager { for back compat. */ ThrowableGetter<T> ppapiAndAdExtDataServiceGetter, ErrorLogger errorLogger) { + Trace.beginSection("ConsentManager#ReadOperation"); mReadWriteLock.readLock().lock(); try { switch (mConsentSourceOfTruth) { @@ -2248,6 +2255,7 @@ public class ConsentManager { LogUtil.e(getClass().getSimpleName() + " failed. " + e.getMessage()); } finally { mReadWriteLock.readLock().unlock(); + Trace.endSection(); } return defaultReturn; @@ -2260,8 +2268,20 @@ public class ConsentManager { : AD_SERVICES_SETTINGS_USAGE_REPORTED__REGION__ROW; } - /* Returns an object of ConsentMigrationStats */ - private static ConsentMigrationStats getConsentManagerStatsForLogging( + /*** + * Returns an object of ConsentMigrationStats for logging + * + * @param appConsents AppConsents consents per API (fledge, msmt, topics, default) + * @param migrationStatus Status of migration ( FAILURE, SUCCESS_WITH_SHARED_PREF_UPDATED, + * SUCCESS_WITH_SHARED_PREF_NOT_UPDATED) + * @param migrationType Type of migration ( PPAPI_TO_SYSTEM_SERVICE, + * APPSEARCH_TO_SYSTEM_SERVICE, + * ADEXT_SERVICE_TO_SYSTEM_SERVICE, + * ADEXT_SERVICE_TO_APPSEARCH) + * @param context Context of the application + * @return consentMigrationStats returns ConsentMigrationStats for logging + */ + public static ConsentMigrationStats getConsentManagerStatsForLogging( AppConsents appConsents, ConsentMigrationStats.MigrationStatus migrationStatus, ConsentMigrationStats.MigrationType migrationType, @@ -2269,7 +2289,6 @@ public class ConsentManager { ConsentMigrationStats consentMigrationStats = ConsentMigrationStats.builder() .setMigrationType(migrationType) - .setMigrationStatus(migrationStatus) // When appConsents is null we log it as a failure .setMigrationStatus( appConsents != null diff --git a/adservices/service-core/java/com/android/adservices/service/consent/ConsentManagerV2.java b/adservices/service-core/java/com/android/adservices/service/consent/ConsentManagerV2.java new file mode 100644 index 000000000..b321c6754 --- /dev/null +++ b/adservices/service-core/java/com/android/adservices/service/consent/ConsentManagerV2.java @@ -0,0 +1,1445 @@ +/* + * 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.consent; + +import static com.android.adservices.AdServicesCommon.ADEXTSERVICES_PACKAGE_NAME_SUFFIX; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__APP_SEARCH_DATA_MIGRATION_FAILURE; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__SHARED_PREF_RESET_FAILURE; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__SHARED_PREF_UPDATE_FAILURE; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_MEASUREMENT_WIPEOUT; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_SETTINGS_USAGE_REPORTED__REGION__EU; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_SETTINGS_USAGE_REPORTED__REGION__ROW; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.app.adservices.AdServicesManager; +import android.app.job.JobScheduler; +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Build; + +import androidx.annotation.RequiresApi; + +import com.android.adservices.LogUtil; +import com.android.adservices.concurrency.AdServicesExecutors; +import com.android.adservices.data.adselection.AppInstallDao; +import com.android.adservices.data.adselection.FrequencyCapDao; +import com.android.adservices.data.adselection.SharedStorageDatabase; +import com.android.adservices.data.common.BooleanFileDatastore; +import com.android.adservices.data.consent.AppConsentDao; +import com.android.adservices.data.customaudience.CustomAudienceDao; +import com.android.adservices.data.customaudience.CustomAudienceDatabase; +import com.android.adservices.data.enrollment.EnrollmentDao; +import com.android.adservices.data.topics.Topic; +import com.android.adservices.data.topics.TopicsTables; +import com.android.adservices.errorlogging.ErrorLogUtil; +import com.android.adservices.service.Flags; +import com.android.adservices.service.FlagsFactory; +import com.android.adservices.service.appsearch.AppSearchConsentStorageManager; +import com.android.adservices.service.common.BackgroundJobsManager; +import com.android.adservices.service.common.UserProfileIdManager; +import com.android.adservices.service.common.compat.FileCompatUtils; +import com.android.adservices.service.common.feature.PrivacySandboxFeatureType; +import com.android.adservices.service.extdata.AdServicesExtDataStorageServiceManager; +import com.android.adservices.service.measurement.MeasurementImpl; +import com.android.adservices.service.measurement.WipeoutStatus; +import com.android.adservices.service.stats.AdServicesLoggerImpl; +import com.android.adservices.service.stats.ConsentMigrationStats; +import com.android.adservices.service.stats.MeasurementWipeoutStats; +import com.android.adservices.service.stats.StatsdAdServicesLogger; +import com.android.adservices.service.stats.UiStatsLogger; +import com.android.adservices.service.topics.TopicsWorker; +import com.android.adservices.service.ui.data.UxStatesDao; +import com.android.adservices.service.ui.enrollment.collection.PrivacySandboxEnrollmentChannelCollection; +import com.android.adservices.service.ui.ux.collection.PrivacySandboxUxCollection; +import com.android.internal.annotations.VisibleForTesting; +import com.android.modules.utils.build.SdkLevel; + +import com.google.common.collect.ImmutableList; + +import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * Manager all critical user data such as per API consent. + * + * <p>For Beta the consent is given for all {@link AdServicesApiType} or for none. + * + * <p>Currently there are three types of source of truth to store consent data, + * + * <ul> + * <li>SYSTEM_SERVER_ONLY: Write and read consent from system server only. + * <li>PPAPI_ONLY: Write and read consent from PPAPI only. + * <li>PPAPI_AND_SYSTEM_SERVER: Write consent to both PPAPI and system server. Read consent from + * system server only. + * <li>APPSEARCH_ONLY: Write and read consent from appSearch only for back compat. + * <li>PPAPI_AND_ADEXT_SERVICE: Write and read consent from PPAPI and AdExt service.. + * </ul> + */ +// TODO(b/279042385): move UI logs to UI. +@RequiresApi(Build.VERSION_CODES.S) +public class ConsentManagerV2 { + private static volatile ConsentManagerV2 sConsentManager; + + @IntDef(value = {NO_MANUAL_INTERACTIONS_RECORDED, UNKNOWN, MANUAL_INTERACTIONS_RECORDED}) + @Retention(RetentionPolicy.SOURCE) + public @interface UserManualInteraction {} + + public static final int NO_MANUAL_INTERACTIONS_RECORDED = -1; + public static final int UNKNOWN = 0; + public static final int MANUAL_INTERACTIONS_RECORDED = 1; + + private final Flags mFlags; + private final TopicsWorker mTopicsWorker; + private final BooleanFileDatastore mDatastore; + private final EnrollmentDao mEnrollmentDao; + private final MeasurementImpl mMeasurementImpl; + private final CustomAudienceDao mCustomAudienceDao; + private final AppInstallDao mAppInstallDao; + private final FrequencyCapDao mFrequencyCapDao; + private final AdServicesStorageManager mAdServicesStorageManager; + private final AppSearchConsentStorageManager mAppSearchConsentStorageManager; + private final UserProfileIdManager mUserProfileIdManager; + + private final AppConsentForRStorageManager mAppConsentForRStorageManager; + + private static final Object LOCK = new Object(); + + private ConsentCompositeStorage mConsentCompositeStorage; + + private AppConsentStorageManager mAppConsentStorageManager; + + ConsentManagerV2( + @NonNull TopicsWorker topicsWorker, + @NonNull AppConsentDao appConsentDao, + @NonNull EnrollmentDao enrollmentDao, + @NonNull MeasurementImpl measurementImpl, + @NonNull CustomAudienceDao customAudienceDao, + @NonNull AppConsentStorageManager appConsentStorageManager, + @NonNull AppInstallDao appInstallDao, + @NonNull FrequencyCapDao frequencyCapDao, + @NonNull AdServicesStorageManager adServicesStorageManager, + @NonNull BooleanFileDatastore booleanFileDatastore, + @NonNull AppSearchConsentStorageManager appSearchConsentStorageManager, + @NonNull UserProfileIdManager userProfileIdManager, + @NonNull AppConsentForRStorageManager appConsentForRStorageManager, + @NonNull Flags flags, + @Flags.ConsentSourceOfTruth int consentSourceOfTruth, + boolean enableAppsearchConsentData, + boolean enableAdExtServiceConsentData) { + Objects.requireNonNull(topicsWorker); + Objects.requireNonNull(appConsentDao); + Objects.requireNonNull(measurementImpl); + Objects.requireNonNull(customAudienceDao); + Objects.requireNonNull(appInstallDao); + Objects.requireNonNull(frequencyCapDao); + Objects.requireNonNull(booleanFileDatastore); + Objects.requireNonNull(userProfileIdManager); + + if (consentSourceOfTruth != Flags.PPAPI_ONLY + && consentSourceOfTruth != Flags.APPSEARCH_ONLY) { + Objects.requireNonNull(adServicesStorageManager); + } + + if (enableAppsearchConsentData) { + Objects.requireNonNull(appSearchConsentStorageManager); + } + + if (enableAdExtServiceConsentData) { + Objects.requireNonNull(appConsentForRStorageManager); + } + + mAdServicesStorageManager = adServicesStorageManager; + mTopicsWorker = topicsWorker; + mDatastore = booleanFileDatastore; + mEnrollmentDao = enrollmentDao; + mMeasurementImpl = measurementImpl; + mCustomAudienceDao = customAudienceDao; + mAppInstallDao = appInstallDao; + mFrequencyCapDao = frequencyCapDao; + + mAppSearchConsentStorageManager = appSearchConsentStorageManager; + mUserProfileIdManager = userProfileIdManager; + + mFlags = flags; + mAppConsentStorageManager = appConsentStorageManager; + mAppConsentForRStorageManager = appConsentForRStorageManager; + + mConsentCompositeStorage = + new ConsentCompositeStorage(getStorageListBySourceOfTruth(consentSourceOfTruth)); + } + + private ImmutableList<IConsentStorage> getStorageListBySourceOfTruth( + @Flags.ConsentSourceOfTruth int consentSourceOfTruth) { + switch (consentSourceOfTruth) { + case Flags.PPAPI_ONLY: + return ImmutableList.of(mAppConsentStorageManager); + case Flags.SYSTEM_SERVER_ONLY: + return ImmutableList.of(mAdServicesStorageManager); + case Flags.PPAPI_AND_SYSTEM_SERVER: + // System storage has higher priority + return ImmutableList.of(mAdServicesStorageManager, mAppConsentStorageManager); + case Flags.APPSEARCH_ONLY: + return ImmutableList.of(mAppSearchConsentStorageManager); + case Flags.PPAPI_AND_ADEXT_SERVICE: + return ImmutableList.of(mAppConsentForRStorageManager); + default: + LogUtil.e(ConsentConstants.ERROR_MESSAGE_INVALID_CONSENT_SOURCE_OF_TRUTH); + return ImmutableList.of(); + } + } + + /** + * Gets an instance of {@link ConsentManagerV2} to be used. + * + * <p>If no instance has been initialized yet, a new one will be created. Otherwise, the + * existing instance will be returned. + */ + @NonNull + public static ConsentManagerV2 getInstance(@NonNull Context context) { + Objects.requireNonNull(context); + + if (sConsentManager == null) { + synchronized (LOCK) { + if (sConsentManager == null) { + // Execute one-time consent migration if needed. + int consentSourceOfTruth = FlagsFactory.getFlags().getConsentSourceOfTruth(); + BooleanFileDatastore datastore = createAndInitializeDataStore(context); + AdServicesStorageManager adServicesManager = + AdServicesStorageManager.getInstance( + AdServicesManager.getInstance(context)); + AppConsentDao appConsentDao = AppConsentDao.getInstance(context); + + // It is possible that the old value of the flag lingers after OTA until the + // first PH sync. In that case, we should not use the stale value, but use the + // default instead. The next PH sync will restore the T+ value. + if (SdkLevel.isAtLeastT() && consentSourceOfTruth == Flags.APPSEARCH_ONLY) { + consentSourceOfTruth = Flags.DEFAULT_CONSENT_SOURCE_OF_TRUTH; + } + AppSearchConsentStorageManager appSearchConsentStorageManager = null; + StatsdAdServicesLogger statsdAdServicesLogger = + StatsdAdServicesLogger.getInstance(); + // Flag enable_appsearch_consent_data is true on S- and T+ only when we want to + // use AppSearch to write to or read from. + boolean enableAppsearchConsentData = + FlagsFactory.getFlags().getEnableAppsearchConsentData(); + if (enableAppsearchConsentData) { + appSearchConsentStorageManager = + AppSearchConsentStorageManager.getInstance(); + handleConsentMigrationFromAppSearchIfNeeded( + context, + datastore, + appConsentDao, + appSearchConsentStorageManager, + adServicesManager, + statsdAdServicesLogger); + } + UxStatesDao uxStatesDao = UxStatesDao.getInstance(context); + AppConsentForRStorageManager mAppConsentForRStorageManager = null; + // Flag enable_adext_service_consent_data is true on R and S+ only when + // we want to use AdServicesExtDataStorageService to write to or read from. + boolean enableAdExtServiceConsentData = + FlagsFactory.getFlags().getEnableAdExtServiceConsentData(); + if (enableAdExtServiceConsentData) { + AdServicesExtDataStorageServiceManager adServicesExtDataManager = + AdServicesExtDataStorageServiceManager.getInstance(context); + if (FlagsFactory.getFlags().getEnableAdExtServiceToAppSearchMigration()) { + ConsentMigrationUtils.handleConsentMigrationToAppSearchIfNeededV2( + context, + datastore, + appSearchConsentStorageManager, + adServicesExtDataManager); + } + mAppConsentForRStorageManager = + new AppConsentForRStorageManager( + datastore, + appConsentDao, + uxStatesDao, + adServicesExtDataManager); + } + + // Attempt to migrate consent data from PPAPI to System server if needed. + handleConsentMigrationIfNeeded( + context, + datastore, + adServicesManager, + statsdAdServicesLogger, + consentSourceOfTruth); + + AppConsentStorageManager appConsentStorageManager = + new AppConsentStorageManager(datastore, appConsentDao, uxStatesDao); + sConsentManager = + new ConsentManagerV2( + TopicsWorker.getInstance(context), + appConsentDao, + EnrollmentDao.getInstance(context), + MeasurementImpl.getInstance(context), + CustomAudienceDatabase.getInstance(context).customAudienceDao(), + appConsentStorageManager, + SharedStorageDatabase.getInstance(context).appInstallDao(), + SharedStorageDatabase.getInstance(context).frequencyCapDao(), + adServicesManager, + datastore, + appSearchConsentStorageManager, + UserProfileIdManager.getInstance(context), + // TODO(b/260601944): Remove Flag Instance. + mAppConsentForRStorageManager, + FlagsFactory.getFlags(), + consentSourceOfTruth, + enableAppsearchConsentData, + enableAdExtServiceConsentData); + } + } + } + return sConsentManager; + } + + /** + * Enables all PP API services. It gives consent to Topics, Fledge and Measurements services. + * + * <p>To write consent to PPAPI if consent source of truth is PPAPI_ONLY or dual sources. To + * write to system server consent if source of truth is system server or dual sources. + */ + public void enable(@NonNull Context context) { + Objects.requireNonNull(context); + + // Check current value, if it is already enabled, skip this enable process. so that the Api + // won't be reset. Only add this logic to "enable" not "disable", since if it already + // disabled, there is no harm to reset the api again. + if (mFlags.getConsentManagerLazyEnableMode() && getConsentFromSourceOfTruth()) { + LogUtil.d("CONSENT_KEY already enable. Skipping enable process."); + return; + } + UiStatsLogger.logOptInSelected(); + + BackgroundJobsManager.scheduleAllBackgroundJobs(context); + try { + // reset all state data which should be removed + resetTopicsAndBlockedTopics(); + clearAllAppConsentData(); + resetMeasurement(); + resetUserProfileId(); + mUserProfileIdManager.getOrCreateId(); + } catch (IOException e) { + throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_WHILE_SET_CONTENT, e); + } + setConsentToSourceOfTruth(/* isGiven */ true); + } + + /** + * Disables all PP API services. It revokes consent to Topics, Fledge and Measurements services. + * + * <p>To write consent to PPAPI if consent source of truth is PPAPI_ONLY or dual sources. To + * write to system server consent if source of truth is system server or dual sources. + */ + public void disable(@NonNull Context context) { + Objects.requireNonNull(context); + UiStatsLogger.logOptOutSelected(); + // Disable all the APIs + try { + // reset all data + resetTopicsAndBlockedTopics(); + clearAllAppConsentData(); + resetMeasurement(); + resetEnrollment(); + resetUserProfileId(); + + BackgroundJobsManager.unscheduleAllBackgroundJobs( + context.getSystemService(JobScheduler.class)); + } catch (IOException e) { + throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_WHILE_SET_CONTENT, e); + } + setConsentToSourceOfTruth(/* isGiven */ false); + } + + /** + * Enables the {@code apiType} PP API service. It gives consent to an API which is provided in + * the parameter. + * + * <p>To write consent to PPAPI if consent source of truth is PPAPI_ONLY or dual sources. To + * write to system server consent if source of truth is system server or dual sources. + * + * @param context Context of the application. + * @param apiType Type of the API (Topics, Fledge, Measurement) which should be enabled. + */ + public void enable(@NonNull Context context, AdServicesApiType apiType) { + Objects.requireNonNull(context); + // Check current value, if it is already enabled, skip this enable process. so that the Api + // won't be reset. + if (mFlags.getConsentManagerLazyEnableMode() + && getPerApiConsentFromSourceOfTruth(apiType)) { + LogUtil.d( + "ApiType: is %s already enable. Skipping enable process.", + apiType.toPpApiDatastoreKey()); + return; + } + + UiStatsLogger.logOptInSelected(apiType); + + BackgroundJobsManager.scheduleJobsPerApi(context, apiType); + + try { + // reset all state data which should be removed + resetByApi(apiType); + + if (AdServicesApiType.FLEDGE == apiType) { + mUserProfileIdManager.getOrCreateId(); + } + } catch (IOException e) { + throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_WHILE_SET_CONTENT, e); + } + + setPerApiConsentToSourceOfTruth(/* isGiven */ true, apiType); + } + + /** + * Disables {@code apiType} PP API service. It revokes consent to an API which is provided in + * the parameter. + * + * <p>To write consent to PPAPI if consent source of truth is PPAPI_ONLY or dual sources. To + * write to system server consent if source of truth is system server or dual sources. + */ + public void disable(@NonNull Context context, AdServicesApiType apiType) { + Objects.requireNonNull(context); + + UiStatsLogger.logOptOutSelected(apiType); + + try { + resetByApi(apiType); + BackgroundJobsManager.unscheduleJobsPerApi( + context.getSystemService(JobScheduler.class), apiType); + } catch (IOException e) { + throw new RuntimeException(ConsentConstants.ERROR_MESSAGE_WHILE_SET_CONTENT, e); + } + + setPerApiConsentToSourceOfTruth(/* isGiven */ false, apiType); + + if (areAllApisDisabled()) { + BackgroundJobsManager.unscheduleAllBackgroundJobs( + context.getSystemService(JobScheduler.class)); + } + } + + private boolean areAllApisDisabled() { + if (getConsent(AdServicesApiType.TOPICS).isGiven() + || getConsent(AdServicesApiType.MEASUREMENTS).isGiven() + || getConsent(AdServicesApiType.FLEDGE).isGiven()) { + return false; + } + return true; + } + + /** + * Retrieves the consent for all PP API services. + * + * <p>To read from PPAPI consent if source of truth is PPAPI. To read from system server consent + * if source of truth is system server or dual sources. + * + * @return AdServicesApiConsent the consent + */ + public AdServicesApiConsent getConsent() { + if (mFlags.getConsentManagerDebugMode()) { + return AdServicesApiConsent.GIVEN; + } + return mConsentCompositeStorage.getConsent(AdServicesApiType.ALL_API); + } + + /** + * Retrieves the consent per API. + * + * @param apiType apiType for which the consent should be provided + * @return {@link AdServicesApiConsent} providing information whether the consent was given or + * revoked. + */ + public AdServicesApiConsent getConsent(AdServicesApiType apiType) { + if (mFlags.getConsentManagerDebugMode()) { + return AdServicesApiConsent.GIVEN; + } + return mConsentCompositeStorage.getConsent(apiType); + } + + /** + * Returns whether the user is adult user who OTA from R. + * + * @return true if user is adult user who OTA from R, otherwise false. + */ + public boolean isOtaAdultUserFromRvc() { + if (mFlags.getConsentManagerOTADebugMode()) { + return true; + } + // TODO(313672368) clean up getRvcPostOtaNotifAgeCheck flag after u18 is qualified on R/S + return mAppConsentForRStorageManager != null + && mAppConsentForRStorageManager.wasU18NotificationDisplayed() + && (mFlags.getRvcPostOtaNotifAgeCheck() + ? !mAppConsentForRStorageManager.isU18Account() + && mAppConsentForRStorageManager.isAdultAccount() + : true); + } + + /** + * Proxy call to {@link TopicsWorker} to get {@link ImmutableList} of {@link Topic}s which could + * be returned to the {@link TopicsWorker} clients. + * + * @return {@link ImmutableList} of {@link Topic}s. + */ + @NonNull + public ImmutableList<Topic> getKnownTopicsWithConsent() { + return mTopicsWorker.getKnownTopicsWithConsent(); + } + + /** + * Proxy call to {@link TopicsWorker} to get {@link ImmutableList} of {@link Topic}s which were + * blocked by the user. + * + * @return {@link ImmutableList} of blocked {@link Topic}s. + */ + @NonNull + public ImmutableList<Topic> getTopicsWithRevokedConsent() { + return mTopicsWorker.getTopicsWithRevokedConsent(); + } + + /** + * Proxy call to {@link TopicsWorker} to revoke consent for provided {@link Topic} (block + * topic). + * + * @param topic {@link Topic} to block. + */ + @NonNull + public void revokeConsentForTopic(@NonNull Topic topic) { + mTopicsWorker.revokeConsentForTopic(topic); + } + + /** + * Proxy call to {@link TopicsWorker} to restore consent for provided {@link Topic} (unblock the + * topic). + * + * @param topic {@link Topic} to restore consent for. + */ + @NonNull + public void restoreConsentForTopic(@NonNull Topic topic) { + mTopicsWorker.restoreConsentForTopic(topic); + } + + /** Wipes out all the data gathered by Topics API but blocked topics. */ + public void resetTopics() { + ArrayList<String> tablesToBlock = new ArrayList<>(); + tablesToBlock.add(TopicsTables.BlockedTopicsContract.TABLE); + mTopicsWorker.clearAllTopicsData(tablesToBlock); + } + + /** Wipes out all the data gathered by Topics API. */ + public void resetTopicsAndBlockedTopics() { + mTopicsWorker.clearAllTopicsData(new ArrayList<>()); + } + + /** + * @return an {@link ImmutableList} of all known apps in the database that have not had user + * consent revoked + */ + public ImmutableList<App> getKnownAppsWithConsent() { + return ImmutableList.copyOf( + mConsentCompositeStorage.getKnownAppsWithConsent().stream() + .map(App::create) + .collect(Collectors.toList())); + } + + /** + * @return an {@link ImmutableList} of all known apps in the database that have had user consent + * revoked + */ + public ImmutableList<App> getAppsWithRevokedConsent() { + return ImmutableList.copyOf( + mConsentCompositeStorage.getAppsWithRevokedConsent().stream() + .map(App::create) + .collect(Collectors.toList())); + } + + /** + * Proxy call to {@link AppConsentDao} to revoke consent for provided {@link App}. + * + * <p>Also clears all app data related to the provided {@link App}. + * + * @param app {@link App} to block. + * @throws IOException if the operation fails + */ + public void revokeConsentForApp(@NonNull App app) throws IOException { + mConsentCompositeStorage.setConsentForApp(app.getPackageName(), true); + + asyncExecute( + () -> mCustomAudienceDao.deleteCustomAudienceDataByOwner(app.getPackageName())); + if (mFlags.getFledgeAdSelectionFilteringEnabled()) { + asyncExecute(() -> mAppInstallDao.deleteByPackageName(app.getPackageName())); + asyncExecute( + () -> mFrequencyCapDao.deleteHistogramDataBySourceApp(app.getPackageName())); + } + } + + /** + * Proxy call to {@link AppConsentDao} to restore consent for provided {@link App}. + * + * @param app {@link App} to restore consent for. + * @throws IOException if the operation fails + */ + public void restoreConsentForApp(@NonNull App app) throws IOException { + mConsentCompositeStorage.setConsentForApp(app.getPackageName(), false); + } + + /** + * Deletes all app consent data and all app data gathered or generated by the Privacy Sandbox. + * + * <p>This should be called when the Privacy Sandbox has been disabled. + * + * @throws IOException if the operation fails + */ + public void clearAllAppConsentData() throws IOException { + mConsentCompositeStorage.clearAllAppConsentData(); + + asyncExecute(mCustomAudienceDao::deleteAllCustomAudienceData); + if (mFlags.getFledgeAdSelectionFilteringEnabled()) { + asyncExecute(mAppInstallDao::deleteAllAppInstallData); + asyncExecute(mFrequencyCapDao::deleteAllHistogramData); + } + } + + /** + * Deletes the list of known allowed apps as well as all app data from the Privacy Sandbox. + * + * <p>The list of blocked apps is not reset. + * + * @throws IOException if the operation fails + */ + public void clearKnownAppsWithConsent() throws IOException { + mConsentCompositeStorage.clearKnownAppsWithConsent(); + asyncExecute(mCustomAudienceDao::deleteAllCustomAudienceData); + if (mFlags.getFledgeAdSelectionFilteringEnabled()) { + asyncExecute(mAppInstallDao::deleteAllAppInstallData); + asyncExecute(mFrequencyCapDao::deleteAllHistogramData); + } + } + + /** + * Checks whether a single given installed application (identified by its package name) has had + * user consent to use the FLEDGE APIs revoked. + * + * <p>This method also checks whether a user has opted out of the FLEDGE Privacy Sandbox + * initiative. + * + * @param packageName String package name that uniquely identifies an installed application to + * check + * @return {@code true} if either the FLEDGE Privacy Sandbox initiative has been opted out or if + * the user has revoked consent for the given application to use the FLEDGE APIs + * @throws IllegalArgumentException if the package name is invalid or not found as an installed + * application + */ + public boolean isFledgeConsentRevokedForApp(@NonNull String packageName) + throws IllegalArgumentException { + AdServicesApiConsent consent = getConsent(AdServicesApiType.FLEDGE); + + if (!consent.isGiven()) { + return true; + } + + return mConsentCompositeStorage.isConsentRevokedForApp(packageName); + } + + /** + * Persists the use of a FLEDGE API by a single given installed application (identified by its + * package name) if the app has not already had its consent revoked. + * + * <p>This method also checks whether a user has opted out of the FLEDGE Privacy Sandbox + * initiative. + * + * <p>This is only meant to be called by the FLEDGE APIs. + * + * @param packageName String package name that uniquely identifies an installed application that + * has used a FLEDGE API + * @return {@code true} if user consent has been revoked for the application or API, {@code + * false} otherwise + * @throws IllegalArgumentException if the package name is invalid or not found as an installed + * application + */ + public boolean isFledgeConsentRevokedForAppAfterSettingFledgeUse(@NonNull String packageName) + throws IllegalArgumentException { + AdServicesApiConsent consent = getConsent(AdServicesApiType.FLEDGE); + + if (!consent.isGiven()) { + return true; + } + + return mConsentCompositeStorage.setConsentForAppIfNew(packageName, false); + } + + /** + * Clear consent data after an app was uninstalled. + * + * @param packageName the package name that had been uninstalled. + */ + public void clearConsentForUninstalledApp(String packageName, int packageUid) { + mConsentCompositeStorage.clearConsentForUninstalledApp(packageName, packageUid); + } + + /** + * Clear consent data after an app was uninstalled, but the package Uid is unavailable. This + * could happen because the INTERACT_ACROSS_USERS_FULL permission is not available on Android + * versions prior to T. + * + * <p><strong>This method should only be used for R/S back-compat scenarios.</strong> + * + * @param packageName the package name that had been uninstalled. + */ + public void clearConsentForUninstalledApp(@NonNull String packageName) { + mConsentCompositeStorage.clearConsentForUninstalledApp(packageName); + } + + /** Wipes out all the data gathered by Measurement API. */ + public void resetMeasurement() { + mMeasurementImpl.deleteAllMeasurementData(List.of()); + // Log wipeout event triggered by consent flip to delete data of package + WipeoutStatus wipeoutStatus = new WipeoutStatus(); + wipeoutStatus.setWipeoutType(WipeoutStatus.WipeoutType.CONSENT_FLIP); + logWipeoutStats(wipeoutStatus); + } + + /** Wipes out all the Enrollment data */ + @VisibleForTesting + void resetEnrollment() { + mEnrollmentDao.deleteAll(); + } + + /** + * Saves information to the storage that notification was displayed for the first time to the + * user. + */ + public void recordNotificationDisplayed(boolean wasNotificationDisplayed) { + mConsentCompositeStorage.recordNotificationDisplayed(wasNotificationDisplayed); + } + + /** + * Retrieves if notification has been displayed. + * + * @return true if Consent Notification was displayed, otherwise false. + */ + public Boolean wasNotificationDisplayed() { + return mConsentCompositeStorage.wasNotificationDisplayed(); + } + + /** + * Saves information to the storage that GA UX notification was displayed for the first time to + * the user. + */ + public void recordGaUxNotificationDisplayed(boolean wasGaUxDisplayed) { + mConsentCompositeStorage.recordGaUxNotificationDisplayed(wasGaUxDisplayed); + } + + /** + * Retrieves if GA UX notification has been displayed. + * + * @return true if GA UX Consent Notification was displayed, otherwise false. + */ + public Boolean wasGaUxNotificationDisplayed() { + return mConsentCompositeStorage.wasGaUxNotificationDisplayed(); + } + + /** + * Retrieves the PP API default consent. + * + * @return true if the topics default consent is true, false otherwise. + */ + public Boolean getDefaultConsent() { + return mConsentCompositeStorage.getDefaultConsent(AdServicesApiType.ALL_API).isGiven(); + } + + /** + * Retrieves the topics default consent. + * + * @return true if the topics default consent is true, false otherwise. + */ + public Boolean getTopicsDefaultConsent() { + return mConsentCompositeStorage.getDefaultConsent(AdServicesApiType.TOPICS).isGiven(); + } + + /** + * Retrieves the FLEDGE default consent. + * + * @return true if the FLEDGE default consent is true, false otherwise. + */ + public Boolean getFledgeDefaultConsent() { + return mConsentCompositeStorage.getDefaultConsent(AdServicesApiType.FLEDGE).isGiven(); + } + + /** + * Retrieves the measurement default consent. + * + * @return true if the measurement default consent is true, false otherwise. + */ + public Boolean getMeasurementDefaultConsent() { + return mConsentCompositeStorage.getDefaultConsent(AdServicesApiType.MEASUREMENTS).isGiven(); + } + + /** + * Retrieves the default AdId state. + * + * @return true if the AdId is enabled by default, false otherwise. + */ + public Boolean getDefaultAdIdState() { + return mConsentCompositeStorage.getDefaultAdIdState(); + } + + /** Saves the default consent bit to data stores based on source of truth. */ + public void recordDefaultConsent(boolean defaultConsent) { + mConsentCompositeStorage.recordDefaultConsent(AdServicesApiType.ALL_API, defaultConsent); + } + + /** Saves the topics default consent bit to data stores based on source of truth. */ + public void recordTopicsDefaultConsent(boolean defaultConsent) { + mConsentCompositeStorage.recordDefaultConsent(AdServicesApiType.TOPICS, defaultConsent); + } + + /** Saves the FLEDGE default consent bit to data stores based on source of truth. */ + public void recordFledgeDefaultConsent(boolean defaultConsent) { + mConsentCompositeStorage.recordDefaultConsent(AdServicesApiType.FLEDGE, defaultConsent); + } + + /** Saves the measurement default consent bit to data stores based on source of truth. */ + public void recordMeasurementDefaultConsent(boolean defaultConsent) { + mConsentCompositeStorage.recordDefaultConsent( + AdServicesApiType.MEASUREMENTS, defaultConsent); + } + + /** Saves the default AdId state bit to data stores based on source of truth. */ + public void recordDefaultAdIdState(boolean defaultAdIdState) { + mConsentCompositeStorage.recordDefaultAdIdState(defaultAdIdState); + } + + /** Set the current privacy sandbox feature. */ + public void setCurrentPrivacySandboxFeature(PrivacySandboxFeatureType currentFeatureType) { + mConsentCompositeStorage.setCurrentPrivacySandboxFeature(currentFeatureType); + } + + /** Saves information to the storage that user interacted with consent manually. */ + public void recordUserManualInteractionWithConsent(@UserManualInteraction int interaction) { + mConsentCompositeStorage.recordUserManualInteractionWithConsent(interaction); + } + + /** + * Get the current privacy sandbox feature. + * + * <p>To write to PPAPI if consent source of truth is PPAPI_ONLY or dual sources. To write to + * system server if consent source of truth is SYSTEM_SERVER_ONLY or dual sources. + */ + public PrivacySandboxFeatureType getCurrentPrivacySandboxFeature() { + return mConsentCompositeStorage.getCurrentPrivacySandboxFeature(); + } + + /** + * Returns information whether user interacted with consent manually. + * + * @return true if the user interacted with the consent manually, otherwise false. + */ + public @UserManualInteraction int getUserManualInteractionWithConsent() { + return mConsentCompositeStorage.getUserManualInteractionWithConsent(); + } + + @VisibleForTesting + static BooleanFileDatastore createAndInitializeDataStore(@NonNull Context context) { + BooleanFileDatastore booleanFileDatastore = + new BooleanFileDatastore( + context, + ConsentConstants.STORAGE_XML_IDENTIFIER, + ConsentConstants.STORAGE_VERSION); + + try { + booleanFileDatastore.initialize(); + // TODO(b/259607624): implement a method in the datastore which would support + // this exact scenario - if the value is null, return default value provided + // in the parameter (similar to SP apply etc.) + if (booleanFileDatastore.get(ConsentConstants.NOTIFICATION_DISPLAYED_ONCE) == null) { + booleanFileDatastore.put(ConsentConstants.NOTIFICATION_DISPLAYED_ONCE, false); + } + if (booleanFileDatastore.get(ConsentConstants.GA_UX_NOTIFICATION_DISPLAYED_ONCE) + == null) { + booleanFileDatastore.put(ConsentConstants.GA_UX_NOTIFICATION_DISPLAYED_ONCE, false); + } + } catch (IOException | IllegalArgumentException | NullPointerException e) { + throw new RuntimeException("Failed to initialize the File Datastore!", e); + } + + return booleanFileDatastore; + } + + // Handle different migration requests based on current consent source of Truth + // PPAPI_ONLY: reset the shared preference to reset status of migrating consent from PPAPI to + // system server. + // PPAPI_AND_SYSTEM_SERVER: migrate consent from PPAPI to system server. + // SYSTEM_SERVER_ONLY: migrate consent from PPAPI to system server and clear PPAPI consent + @VisibleForTesting + static void handleConsentMigrationIfNeeded( + @NonNull Context context, + @NonNull BooleanFileDatastore datastore, + AdServicesStorageManager adServicesManager, + @NonNull StatsdAdServicesLogger statsdAdServicesLogger, + @Flags.ConsentSourceOfTruth int consentSourceOfTruth) { + Objects.requireNonNull(context); + // On R/S, handleConsentMigrationIfNeeded should never be executed. + // It is a T+ feature. On T+, this function should only execute if it's within the + // AdServices + // APK and not ExtServices. So check if it's within ExtServices, and bail out if that's the + // case on any platform. + String packageName = context.getPackageName(); + if (packageName != null && packageName.endsWith(ADEXTSERVICES_PACKAGE_NAME_SUFFIX)) { + LogUtil.d("Aborting attempt to migrate consent in ExtServices"); + return; + } + Objects.requireNonNull(datastore); + if (consentSourceOfTruth == Flags.PPAPI_AND_SYSTEM_SERVER + || consentSourceOfTruth == Flags.SYSTEM_SERVER_ONLY) { + Objects.requireNonNull(adServicesManager); + } + + switch (consentSourceOfTruth) { + case Flags.PPAPI_ONLY: + // Technically we only need to reset the SHARED_PREFS_KEY_HAS_MIGRATED bit once. + // What we need is clearIfSet operation which is not available in SP. So here we + // always reset the bit since otherwise we need to read the SP to read the value and + // the clear the value. + // The only flow we would do are: + // Case 1: DUAL-> PPAPI if there is a bug in System Server + // Case 2: DUAL -> SYSTEM_SERVER_ONLY: if everything goes smoothly. + resetSharedPreference(context, ConsentConstants.SHARED_PREFS_KEY_HAS_MIGRATED); + break; + case Flags.PPAPI_AND_SYSTEM_SERVER: + migratePpApiConsentToSystemService( + context, datastore, adServicesManager, statsdAdServicesLogger); + break; + case Flags.SYSTEM_SERVER_ONLY: + migratePpApiConsentToSystemService( + context, datastore, adServicesManager, statsdAdServicesLogger); + clearPpApiConsent(context, datastore); + break; + case Flags.APPSEARCH_ONLY: + // If this is an S- device, the consent source of truth is always APPSEARCH_ONLY. + break; + default: + break; + } + } + + // Reset data for the specific AdServicesApiType + @VisibleForTesting + void resetByApi(AdServicesApiType apiType) throws IOException { + switch (apiType) { + case TOPICS: + resetTopicsAndBlockedTopics(); + break; + case FLEDGE: + clearAllAppConsentData(); + resetUserProfileId(); + break; + case MEASUREMENTS: + resetMeasurement(); + break; + default: + break; + } + } + + private void resetUserProfileId() { + mUserProfileIdManager.deleteId(); + } + + // Perform a one-time migration to migrate existing PPAPI Consent + @VisibleForTesting + // Suppress lint warning for context.getUser in R since this code is unused in R + @SuppressWarnings("NewApi") + static void migratePpApiConsentToSystemService( + @NonNull Context context, + @NonNull BooleanFileDatastore datastore, + @NonNull AdServicesStorageManager adServicesManager, + @NonNull StatsdAdServicesLogger statsdAdServicesLogger) { + Objects.requireNonNull(context); + Objects.requireNonNull(datastore); + Objects.requireNonNull(adServicesManager); + + AppConsents appConsents = null; + try { + // Exit if migration has happened. + SharedPreferences sharedPreferences = + FileCompatUtils.getSharedPreferencesHelper( + context, ConsentConstants.SHARED_PREFS_CONSENT, Context.MODE_PRIVATE); + // If we migrated data to system server either from PPAPI or from AppSearch, do not + // attempt another migration of data to system server. + boolean shouldSkipMigration = + sharedPreferences.getBoolean( + ConsentConstants.SHARED_PREFS_KEY_APPSEARCH_HAS_MIGRATED, + /* default= */ false) + || sharedPreferences.getBoolean( + ConsentConstants.SHARED_PREFS_KEY_HAS_MIGRATED, + /* default= */ false); + if (shouldSkipMigration) { + LogUtil.v( + "Consent migration has happened to user %d, skip...", + context.getUser().getIdentifier()); + return; + } + LogUtil.d("Started migrating Consent from PPAPI to System Service"); + + Boolean consentKey = Boolean.TRUE.equals(datastore.get(ConsentConstants.CONSENT_KEY)); + + // Migrate Consent and Notification Displayed to System Service. + // Set consent enabled only when value is TRUE. FALSE and null are regarded as disabled. + adServicesManager.setConsent(AdServicesApiType.ALL_API, consentKey); + // Set notification displayed only when value is TRUE. FALSE and null are regarded as + // not displayed. + if (Boolean.TRUE.equals(datastore.get(ConsentConstants.NOTIFICATION_DISPLAYED_ONCE))) { + adServicesManager.recordNotificationDisplayed(true); + } + + Boolean manualInteractionRecorded = + datastore.get(ConsentConstants.MANUAL_INTERACTION_WITH_CONSENT_RECORDED); + if (manualInteractionRecorded != null) { + adServicesManager.recordUserManualInteractionWithConsent( + manualInteractionRecorded ? 1 : -1); + } + + // Save migration has happened into shared preferences. + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putBoolean(ConsentConstants.SHARED_PREFS_KEY_HAS_MIGRATED, true); + appConsents = + AppConsents.builder() + .setDefaultConsent(consentKey) + .setMsmtConsent(consentKey) + .setFledgeConsent(consentKey) + .setTopicsConsent(consentKey) + .build(); + + if (editor.commit()) { + LogUtil.d("Finished migrating Consent from PPAPI to System Service"); + statsdAdServicesLogger.logConsentMigrationStats( + getConsentManagerStatsForLogging( + appConsents, + ConsentMigrationStats.MigrationStatus + .SUCCESS_WITH_SHARED_PREF_UPDATED, + ConsentMigrationStats.MigrationType.PPAPI_TO_SYSTEM_SERVICE, + context)); + } else { + LogUtil.e( + "Finished migrating Consent from PPAPI to System Service but shared" + + " preference is not updated."); + ErrorLogUtil.e( + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__SHARED_PREF_UPDATE_FAILURE, + AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); + statsdAdServicesLogger.logConsentMigrationStats( + getConsentManagerStatsForLogging( + appConsents, + ConsentMigrationStats.MigrationStatus + .SUCCESS_WITH_SHARED_PREF_NOT_UPDATED, + ConsentMigrationStats.MigrationType.PPAPI_TO_SYSTEM_SERVICE, + context)); + } + } catch (Exception e) { + LogUtil.e("PPAPI consent data migration failed: ", e); + statsdAdServicesLogger.logConsentMigrationStats( + getConsentManagerStatsForLogging( + appConsents, + ConsentMigrationStats.MigrationStatus.FAILURE, + ConsentMigrationStats.MigrationType.PPAPI_TO_SYSTEM_SERVICE, + context)); + } + } + + // Clear PPAPI Consent if fully migrated to use system server consent. This is because system + // consent cannot be migrated back to PPAPI. This data clearing should only happen once. + @VisibleForTesting + static void clearPpApiConsent( + @NonNull Context context, @NonNull BooleanFileDatastore datastore) { + // Exit if PPAPI consent has cleared. + SharedPreferences sharedPreferences = + FileCompatUtils.getSharedPreferencesHelper( + context, ConsentConstants.SHARED_PREFS_CONSENT, Context.MODE_PRIVATE); + if (sharedPreferences.getBoolean( + ConsentConstants.SHARED_PREFS_KEY_PPAPI_HAS_CLEARED, /* defValue */ false)) { + return; + } + + LogUtil.d("Started clearing Consent in PPAPI."); + + try { + datastore.clear(); + } catch (IOException e) { + throw new RuntimeException("Failed to clear PPAPI Consent", e); + } + + // Save that PPAPI consent has cleared into shared preferences. + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putBoolean(ConsentConstants.SHARED_PREFS_KEY_PPAPI_HAS_CLEARED, true); + + if (editor.commit()) { + LogUtil.d("Finished clearing Consent in PPAPI."); + } else { + LogUtil.e("Finished clearing Consent in PPAPI but shared preference is not updated."); + ErrorLogUtil.e( + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__SHARED_PREF_UPDATE_FAILURE, + AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); + } + } + + // Set the shared preference to false for given key. + @VisibleForTesting + static void resetSharedPreference( + @NonNull Context context, @NonNull String sharedPreferenceKey) { + Objects.requireNonNull(context); + Objects.requireNonNull(sharedPreferenceKey); + + SharedPreferences sharedPreferences = + FileCompatUtils.getSharedPreferencesHelper( + context, ConsentConstants.SHARED_PREFS_CONSENT, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putBoolean(sharedPreferenceKey, false); + + if (editor.commit()) { + LogUtil.d("Finished resetting shared preference for " + sharedPreferenceKey); + } else { + LogUtil.e("Failed to reset shared preference for " + sharedPreferenceKey); + ErrorLogUtil.e( + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__SHARED_PREF_RESET_FAILURE, + AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); + } + } + + // To write to PPAPI if consent source of truth is PPAPI_ONLY or dual sources. + // To write to system server if consent source of truth is SYSTEM_SERVER_ONLY or dual sources. + @VisibleForTesting + void setConsentToSourceOfTruth(boolean isGiven) { + mConsentCompositeStorage.setConsent(AdServicesApiType.ALL_API, isGiven); + } + + @VisibleForTesting + boolean getConsentFromSourceOfTruth() { + return mConsentCompositeStorage.getConsent(AdServicesApiType.ALL_API).isGiven(); + } + + @VisibleForTesting + boolean getPerApiConsentFromSourceOfTruth(AdServicesApiType apiType) { + return mConsentCompositeStorage.getConsent(apiType).isGiven(); + } + + @VisibleForTesting + void setPerApiConsentToSourceOfTruth(boolean isGiven, AdServicesApiType apiType) { + mConsentCompositeStorage.setConsent(apiType, isGiven); + } + + private static void storeUserManualInteractionToPpApi( + @ConsentManagerV2.UserManualInteraction int interaction, BooleanFileDatastore datastore) + throws IOException { + switch (interaction) { + case NO_MANUAL_INTERACTIONS_RECORDED: + datastore.put(ConsentConstants.MANUAL_INTERACTION_WITH_CONSENT_RECORDED, false); + break; + case UNKNOWN: + datastore.remove(ConsentConstants.MANUAL_INTERACTION_WITH_CONSENT_RECORDED); + break; + case MANUAL_INTERACTIONS_RECORDED: + datastore.put(ConsentConstants.MANUAL_INTERACTION_WITH_CONSENT_RECORDED, true); + break; + default: + throw new IllegalArgumentException( + String.format("InteractionId < %d > can not be handled.", interaction)); + } + } + + /** + * This method handles migration of consent data from AppSearch to AdServices. Consent data is + * written to AppSearch on S- and ported to AdServices after OTA to T. If any new data is + * written for consent, we need to make sure it is migrated correctly post-OTA in this method. + */ + @VisibleForTesting + static void handleConsentMigrationFromAppSearchIfNeeded( + @NonNull Context context, + @NonNull BooleanFileDatastore datastore, + @NonNull AppConsentDao appConsentDao, + @NonNull AppSearchConsentStorageManager appSearchConsentStorageManager, + @NonNull AdServicesStorageManager adServicesStorageManager, + @NonNull StatsdAdServicesLogger statsdAdServicesLogger) { + Objects.requireNonNull(context); + Objects.requireNonNull(appSearchConsentStorageManager); + LogUtil.d("Check migrating Consent from AppSearch to PPAPI and System Service"); + + // On R/S, this function should never be executed because AppSearch to PPAPI and + // System Server migration is a T+ feature. On T+, this function should only execute + // if it's within the AdServices APK and not ExtServices. So check if it's within + // ExtServices, and bail out if that's the case on any platform. + String packageName = context.getPackageName(); + if (packageName != null && packageName.endsWith(ADEXTSERVICES_PACKAGE_NAME_SUFFIX)) { + LogUtil.d( + "Aborting attempt to migrate AppSearch to PPAPI and System Service in" + + " ExtServices"); + return; + } + + AppConsents appConsents = null; + try { + // This should be called only once after OTA (if flag is enabled). If we did not record + // showing the notification on T+ yet and we have shown the notification on S- (as + // recorded + // in AppSearch), initialize T+ consent data so that we don't show notification twice + // (after + // OTA upgrade). + SharedPreferences sharedPreferences = + FileCompatUtils.getSharedPreferencesHelper( + context, ConsentConstants.SHARED_PREFS_CONSENT, Context.MODE_PRIVATE); + // If we did not migrate notification data, we should not attempt to migrate anything. + if (!appSearchConsentStorageManager.migrateConsentDataIfNeeded( + sharedPreferences, datastore, adServicesStorageManager, appConsentDao)) { + LogUtil.d("Skipping consent migration from AppSearch"); + return; + } + // Migrate Consent for all APIs and per API to PP API and System Service. + appConsents = + migrateAppSearchConsents( + appSearchConsentStorageManager, adServicesStorageManager, datastore); + // Record interactions data only if we recorded an interaction in AppSearch. + int manualInteractionRecorded = + appSearchConsentStorageManager.getUserManualInteractionWithConsent(); + if (manualInteractionRecorded == MANUAL_INTERACTIONS_RECORDED) { + // Initialize PP API datastore. + storeUserManualInteractionToPpApi(MANUAL_INTERACTIONS_RECORDED, datastore); + // Initialize system service. + adServicesStorageManager.recordUserManualInteractionWithConsent( + manualInteractionRecorded); + } + + // Record that we migrated consent data from AppSearch. We write the notification data + // to system server and perform migration only if system server did not record any + // notification having been displayed. + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putBoolean(ConsentConstants.SHARED_PREFS_KEY_APPSEARCH_HAS_MIGRATED, true); + if (editor.commit()) { + LogUtil.d("Finished migrating Consent from AppSearch to PPAPI + System Service"); + statsdAdServicesLogger.logConsentMigrationStats( + getConsentManagerStatsForLogging( + appConsents, + ConsentMigrationStats.MigrationStatus + .SUCCESS_WITH_SHARED_PREF_UPDATED, + ConsentMigrationStats.MigrationType.APPSEARCH_TO_SYSTEM_SERVICE, + context)); + } else { + LogUtil.e( + "Finished migrating Consent from AppSearch to PPAPI + System Service " + + "but shared preference is not updated."); + statsdAdServicesLogger.logConsentMigrationStats( + getConsentManagerStatsForLogging( + appConsents, + ConsentMigrationStats.MigrationStatus + .SUCCESS_WITH_SHARED_PREF_NOT_UPDATED, + ConsentMigrationStats.MigrationType.APPSEARCH_TO_SYSTEM_SERVICE, + context)); + } + } catch (IOException e) { + LogUtil.e("AppSearch consent data migration failed: ", e); + ErrorLogUtil.e( + e, + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__APP_SEARCH_DATA_MIGRATION_FAILURE, + AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX); + statsdAdServicesLogger.logConsentMigrationStats( + getConsentManagerStatsForLogging( + appConsents, + ConsentMigrationStats.MigrationStatus.FAILURE, + ConsentMigrationStats.MigrationType.APPSEARCH_TO_SYSTEM_SERVICE, + context)); + } + } + + /** + * This method returns and migrates the consent states (opt in/out) for all PPAPIs, each API and + * their default consent values. + */ + @VisibleForTesting + static AppConsents migrateAppSearchConsents( + AppSearchConsentStorageManager appSearchConsentManager, + AdServicesStorageManager adServicesManager, + BooleanFileDatastore datastore) + throws IOException { + Map<String, Boolean> consentMap = new HashMap<>(); + for (AdServicesApiType apiType : AdServicesApiType.values()) { + if (apiType == AdServicesApiType.UNKNOWN) { + continue; + } + boolean consented = appSearchConsentManager.getConsent(apiType).isGiven(); + datastore.put(apiType.toPpApiDatastoreKey(), consented); + adServicesManager.setConsent(apiType, consented); + boolean defaultConsent = appSearchConsentManager.getDefaultConsent(apiType).isGiven(); + datastore.put(apiType.toDefaultConsentDatastoreKey(), defaultConsent); + adServicesManager.recordDefaultConsent(apiType, defaultConsent); + consentMap.put(apiType.toPpApiDatastoreKey(), consented); + consentMap.put(apiType.toDefaultConsentDatastoreKey(), defaultConsent); + } + return AppConsents.builder() + .setMsmtConsent( + consentMap.get(AdServicesApiType.MEASUREMENTS.toPpApiDatastoreKey())) + .setTopicsConsent(consentMap.get(AdServicesApiType.TOPICS.toPpApiDatastoreKey())) + .setFledgeConsent(consentMap.get(AdServicesApiType.FLEDGE.toPpApiDatastoreKey())) + .setDefaultConsent( + consentMap.get(AdServicesApiType.ALL_API.toDefaultConsentDatastoreKey())) + .build(); + } + + /** + * Represents revoked consent as internally determined by the PP APIs. + * + * <p>This is an internal-only exception and is not meant to be returned to external callers. + */ + public static class RevokedConsentException extends IllegalStateException { + public static final String REVOKED_CONSENT_ERROR_MESSAGE = + "Error caused by revoked user consent"; + + /** Creates an instance of a {@link RevokedConsentException}. */ + public RevokedConsentException() { + super(REVOKED_CONSENT_ERROR_MESSAGE); + } + } + + private void asyncExecute(Runnable runnable) { + AdServicesExecutors.getBackgroundExecutor().execute(runnable); + } + + private void logWipeoutStats(WipeoutStatus wipeoutStatus) { + AdServicesLoggerImpl.getInstance() + .logMeasurementWipeoutStats( + new MeasurementWipeoutStats.Builder() + .setCode(AD_SERVICES_MEASUREMENT_WIPEOUT) + .setWipeoutType(wipeoutStatus.getWipeoutType().getValue()) + .build()); + } + + /** Returns whether the isAdIdEnabled bit is true based on consent_source_of_truth. */ + public Boolean isAdIdEnabled() { + return mConsentCompositeStorage.isAdIdEnabled(); + } + + /** Set the AdIdEnabled bit to storage based on consent_source_of_truth. */ + public void setAdIdEnabled(boolean isAdIdEnabled) { + mConsentCompositeStorage.setAdIdEnabled(isAdIdEnabled); + } + + /** Returns whether the isU18Account bit is true based on consent_source_of_truth. */ + public Boolean isU18Account() { + return mConsentCompositeStorage.isU18Account(); + } + + /** Set the U18Account bit to storage based on consent_source_of_truth. */ + public void setU18Account(boolean isU18Account) { + mConsentCompositeStorage.setU18Account(isU18Account); + } + + /** Returns whether the isEntryPointEnabled bit is true based on consent_source_of_truth. */ + public Boolean isEntryPointEnabled() { + return mConsentCompositeStorage.isEntryPointEnabled(); + } + + /** Set the EntryPointEnabled bit to storage based on consent_source_of_truth. */ + public void setEntryPointEnabled(boolean isEntryPointEnabled) { + mConsentCompositeStorage.setEntryPointEnabled(isEntryPointEnabled); + } + + /** Returns whether the isAdultAccount bit is true based on consent_source_of_truth. */ + public Boolean isAdultAccount() { + return mConsentCompositeStorage.isAdultAccount(); + } + + /** Set the AdultAccount bit to storage based on consent_source_of_truth. */ + public void setAdultAccount(boolean isAdultAccount) { + mConsentCompositeStorage.setAdultAccount(isAdultAccount); + } + + /** + * Returns whether the wasU18NotificationDisplayed bit is true based on consent_source_of_truth. + */ + public Boolean wasU18NotificationDisplayed() { + return mConsentCompositeStorage.wasU18NotificationDisplayed(); + } + + /** Set the U18NotificationDisplayed bit to storage based on consent_source_of_truth. */ + public void setU18NotificationDisplayed(boolean wasU18NotificationDisplayed) { + mConsentCompositeStorage.setU18NotificationDisplayed(wasU18NotificationDisplayed); + } + + /** Returns current UX based on consent_source_of_truth. */ + public PrivacySandboxUxCollection getUx() { + return mConsentCompositeStorage.getUx(); + } + + /** Set the current UX to storage based on consent_source_of_truth. */ + public void setUx(PrivacySandboxUxCollection ux) { + mConsentCompositeStorage.setUx(ux); + } + + /** Returns current enrollment channel based on consent_source_of_truth. */ + public PrivacySandboxEnrollmentChannelCollection getEnrollmentChannel( + PrivacySandboxUxCollection ux) { + return mConsentCompositeStorage.getEnrollmentChannel(ux); + } + + /** Set the current enrollment channel to storage based on consent_source_of_truth. */ + public void setEnrollmentChannel( + PrivacySandboxUxCollection ux, PrivacySandboxEnrollmentChannelCollection channel) { + mConsentCompositeStorage.setEnrollmentChannel(ux, channel); + } + + @VisibleForTesting + void setConsentToPpApi(boolean isGiven) throws IOException { + mDatastore.put(ConsentConstants.CONSENT_KEY, isGiven); + } + + /* Returns the region od the device */ + private static int getConsentRegion(@NonNull Context context) { + return DeviceRegionProvider.isEuDevice(context) + ? AD_SERVICES_SETTINGS_USAGE_REPORTED__REGION__EU + : AD_SERVICES_SETTINGS_USAGE_REPORTED__REGION__ROW; + } + + /* Returns an object of ConsentMigrationStats */ + private static ConsentMigrationStats getConsentManagerStatsForLogging( + AppConsents appConsents, + ConsentMigrationStats.MigrationStatus migrationStatus, + ConsentMigrationStats.MigrationType migrationType, + Context context) { + ConsentMigrationStats consentMigrationStats = + ConsentMigrationStats.builder() + .setMigrationType(migrationType) + .setMigrationStatus(migrationStatus) + // When appConsents is null we log it as a failure + .setMigrationStatus( + appConsents != null + ? migrationStatus + : ConsentMigrationStats.MigrationStatus.FAILURE) + .setMsmtConsent(appConsents == null || appConsents.getMsmtConsent()) + .setTopicsConsent(appConsents == null || appConsents.getTopicsConsent()) + .setFledgeConsent(appConsents == null || appConsents.getFledgeConsent()) + .setDefaultConsent(appConsents == null || appConsents.getDefaultConsent()) + .setRegion(getConsentRegion(context)) + .build(); + return consentMigrationStats; + } +} diff --git a/adservices/service-core/java/com/android/adservices/service/consent/ConsentMigrationUtils.java b/adservices/service-core/java/com/android/adservices/service/consent/ConsentMigrationUtils.java index d5ccb28f3..9bd7afd61 100644 --- a/adservices/service-core/java/com/android/adservices/service/consent/ConsentMigrationUtils.java +++ b/adservices/service-core/java/com/android/adservices/service/consent/ConsentMigrationUtils.java @@ -20,6 +20,8 @@ import static android.adservices.extdata.AdServicesExtDataParams.BOOLEAN_TRUE; import static android.adservices.extdata.AdServicesExtDataParams.BOOLEAN_UNKNOWN; import static android.adservices.extdata.AdServicesExtDataParams.STATE_MANUAL_INTERACTIONS_RECORDED; +import static com.android.adservices.service.consent.ConsentManager.getConsentManagerStatsForLogging; + import android.adservices.extdata.AdServicesExtDataParams; import android.annotation.NonNull; import android.annotation.TargetApi; @@ -32,8 +34,11 @@ import androidx.annotation.Nullable; import com.android.adservices.LogUtil; import com.android.adservices.data.common.BooleanFileDatastore; import com.android.adservices.service.appsearch.AppSearchConsentManager; +import com.android.adservices.service.appsearch.AppSearchConsentStorageManager; import com.android.adservices.service.common.compat.FileCompatUtils; import com.android.adservices.service.extdata.AdServicesExtDataStorageServiceManager; +import com.android.adservices.service.stats.ConsentMigrationStats; +import com.android.adservices.service.stats.StatsdAdServicesLogger; import com.android.modules.utils.build.SdkLevel; import java.util.Objects; @@ -50,10 +55,77 @@ public final class ConsentMigrationUtils { * as it's the new consent source of truth. If any new data is written for consent, we need to * make sure it is migrated correctly post-OTA in this method. */ + @TargetApi(Build.VERSION_CODES.S) public static void handleConsentMigrationToAppSearchIfNeeded( @NonNull Context context, @NonNull BooleanFileDatastore datastore, @Nullable AppSearchConsentManager appSearchConsentManager, + @Nullable AdServicesExtDataStorageServiceManager adExtDataManager, + @Nullable StatsdAdServicesLogger statsdAdServicesLogger) { + Objects.requireNonNull(context); + Objects.requireNonNull(datastore); + Objects.requireNonNull(statsdAdServicesLogger); + LogUtil.d("Check if consent migration to AppSearch is needed."); + AppConsents appConsents = null; + try { + SharedPreferences sharedPreferences = + FileCompatUtils.getSharedPreferencesHelper( + context, ConsentConstants.SHARED_PREFS_CONSENT, Context.MODE_PRIVATE); + + if (!isMigrationToAppSearchNeeded( + context, sharedPreferences, appSearchConsentManager, adExtDataManager)) { + LogUtil.d("Skipping consent migration to AppSearch"); + return; + } + + // Reduce number of read calls by fetching all the AdExt data at once. + AdServicesExtDataParams dataFromR = adExtDataManager.getAdServicesExtData(); + if (dataFromR.getIsNotificationDisplayed() != BOOLEAN_TRUE) { + LogUtil.d("Skipping consent migration to AppSearch; notification not shown on R"); + return; + } + + appConsents = migrateDataToAppSearch(appSearchConsentManager, dataFromR, datastore); + + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putBoolean(ConsentConstants.SHARED_PREFS_KEY_HAS_MIGRATED_TO_APP_SEARCH, true); + if (editor.commit()) { + LogUtil.d("Finished migrating consent to AppSearch."); + logMigrationToAppSearch( + statsdAdServicesLogger, + appConsents, + ConsentMigrationStats.MigrationStatus.SUCCESS_WITH_SHARED_PREF_UPDATED, + context); + } else { + LogUtil.e("Finished migrating consent to AppSearch. Shared prefs not updated."); + logMigrationToAppSearch( + statsdAdServicesLogger, + appConsents, + ConsentMigrationStats.MigrationStatus.SUCCESS_WITH_SHARED_PREF_NOT_UPDATED, + context); + } + // No longer need access to Android R data. Safe to clear here. + adExtDataManager.clearDataOnOtaAsync(); + } catch (Exception e) { + LogUtil.e("Consent migration to AppSearch failed: ", e); + logMigrationToAppSearch( + statsdAdServicesLogger, + appConsents, + ConsentMigrationStats.MigrationStatus.FAILURE, + context); + } + } + + /** + * This method handles migration of consent data to AppSearch post-OTA R -> S. Consent data is + * written to AdServicesExtDataStorageService on R and ported over to AppSearch after OTA to S + * as it's the new consent source of truth. If any new data is written for consent, we need to + * make sure it is migrated correctly post-OTA in this method. + */ + public static void handleConsentMigrationToAppSearchIfNeededV2( + @NonNull Context context, + @NonNull BooleanFileDatastore datastore, + @Nullable AppSearchConsentStorageManager appSearchConsentManager, @Nullable AdServicesExtDataStorageServiceManager adExtDataManager) { Objects.requireNonNull(context); Objects.requireNonNull(datastore); @@ -65,7 +137,7 @@ public final class ConsentMigrationUtils { FileCompatUtils.getSharedPreferencesHelper( context, ConsentConstants.SHARED_PREFS_CONSENT, Context.MODE_PRIVATE); - if (!isMigrationToAppSearchNeeded( + if (!isMigrationToAppSearchNeededV2( context, sharedPreferences, appSearchConsentManager, adExtDataManager)) { LogUtil.d("Skipping consent migration to AppSearch"); return; @@ -78,7 +150,7 @@ public final class ConsentMigrationUtils { return; } - migrateDataToAppSearch(appSearchConsentManager, dataFromR, datastore); + migrateDataToAppSearchV2(appSearchConsentManager, dataFromR, datastore); SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putBoolean(ConsentConstants.SHARED_PREFS_KEY_HAS_MIGRATED_TO_APP_SEARCH, true); @@ -143,8 +215,56 @@ public final class ConsentMigrationUtils { return !isNotificationDisplayedOnS; } + private static boolean isMigrationToAppSearchNeededV2( + Context context, + SharedPreferences sharedPreferences, + AppSearchConsentStorageManager appSearchConsentManager, + AdServicesExtDataStorageServiceManager adExtDataManager) { + if (SdkLevel.isAtLeastT() || !SdkLevel.isAtLeastS()) { + LogUtil.d("Not S device. Consent migration to AppSearch not needed"); + return false; + } + + // Cannot be null on S since the consent source of truth has to be APPSEARCH_ONLY. + Objects.requireNonNull(appSearchConsentManager); + + // There could be a case where we may need to ramp down enable_adext_service_consent_data + // flag on S, in which case we should gracefully handle consent migration by skipping. + if (adExtDataManager == null) { + LogUtil.d("AdExtDataManager is null. Consent migration to AppSearch not needed"); + return false; + } + + boolean isMigrationToAppSearchDone = + sharedPreferences.getBoolean( + ConsentConstants.SHARED_PREFS_KEY_HAS_MIGRATED_TO_APP_SEARCH, + /* defValue= */ false); + if (isMigrationToAppSearchDone) { + LogUtil.d( + "Consent migration to AppSearch is already done for user %d.", + context.getUser().getIdentifier()); + return false; + } + + // Just in case, check all notification types to ensure notification is not shown. We do not + // want to override consent if notification is already shown. + boolean isNotificationDisplayedOnS = + appSearchConsentManager.wasU18NotificationDisplayed() + || appSearchConsentManager.wasNotificationDisplayed() + || appSearchConsentManager.wasGaUxNotificationDisplayed(); + LogUtil.d( + "Notification shown status on S for migrating consent to AppSearch: " + + isNotificationDisplayedOnS); + + // If notification is not shown, we will need to perform another check to ensure + // notification was shown on R before performing migration. This check will be performed + // later in order to reduce number of calls to AdExtDataService in the consent migration + // process. + return !isNotificationDisplayedOnS; + } + @TargetApi(Build.VERSION_CODES.S) - private static void migrateDataToAppSearch( + private static AppConsents migrateDataToAppSearch( AppSearchConsentManager appSearchConsentManager, AdServicesExtDataParams dataFromR, BooleanFileDatastore datastore) { @@ -178,5 +298,72 @@ public final class ConsentMigrationUtils { if (dataFromR.getIsAdultAccount() != BOOLEAN_UNKNOWN) { appSearchConsentManager.setAdultAccount(dataFromR.getIsAdultAccount() == BOOLEAN_TRUE); } + + // Logging false for fledge and topics consent by default because only measurement is + // supported on R. + AppConsents appConsents = + AppConsents.builder() + .setDefaultConsent( + measurementDefaultConsent != null + ? measurementDefaultConsent + : false) + .setMsmtConsent(isMeasurementConsented) + .setFledgeConsent(false) + .setTopicsConsent(false) + .build(); + return appConsents; + } + + @TargetApi(Build.VERSION_CODES.S) + private static void logMigrationToAppSearch( + StatsdAdServicesLogger statsdAdServicesLogger, + AppConsents appConsents, + ConsentMigrationStats.MigrationStatus migrationStatus, + Context context) { + statsdAdServicesLogger.logConsentMigrationStats( + getConsentManagerStatsForLogging( + appConsents, + migrationStatus, + ConsentMigrationStats.MigrationType.ADEXT_SERVICE_TO_APPSEARCH, + context)); + } + + @TargetApi(Build.VERSION_CODES.S) + private static void migrateDataToAppSearchV2( + AppSearchConsentStorageManager appSearchConsentStorageManager, + AdServicesExtDataParams dataFromR, + BooleanFileDatastore datastore) { + // Default measurement consent is stored using PPAPI_ONLY source on R. + Boolean measurementDefaultConsent = + datastore.get(ConsentConstants.MEASUREMENT_DEFAULT_CONSENT); + if (measurementDefaultConsent != null) { + appSearchConsentStorageManager.recordDefaultConsent( + AdServicesApiType.MEASUREMENTS, measurementDefaultConsent); + } + + boolean isMeasurementConsented = dataFromR.getIsMeasurementConsented() == BOOLEAN_TRUE; + appSearchConsentStorageManager.setConsent( + AdServicesApiType.MEASUREMENTS, isMeasurementConsented); + + appSearchConsentStorageManager.setU18NotificationDisplayed( + dataFromR.getIsNotificationDisplayed() == BOOLEAN_TRUE); + + // Record interaction data only if we recorded an interaction in + // AdServicesExtDataStorageService. + int manualInteractionRecorded = dataFromR.getManualInteractionWithConsentStatus(); + if (manualInteractionRecorded == STATE_MANUAL_INTERACTIONS_RECORDED) { + appSearchConsentStorageManager.recordUserManualInteractionWithConsent( + manualInteractionRecorded); + } + + if (dataFromR.getIsU18Account() != BOOLEAN_UNKNOWN) { + appSearchConsentStorageManager.setU18Account( + dataFromR.getIsU18Account() == BOOLEAN_TRUE); + } + + if (dataFromR.getIsAdultAccount() != BOOLEAN_UNKNOWN) { + appSearchConsentStorageManager.setAdultAccount( + dataFromR.getIsAdultAccount() == BOOLEAN_TRUE); + } } } diff --git a/adservices/service-core/java/com/android/adservices/service/consent/IConsentStorage.java b/adservices/service-core/java/com/android/adservices/service/consent/IConsentStorage.java index 0c21645d7..ebd47b8da 100644 --- a/adservices/service-core/java/com/android/adservices/service/consent/IConsentStorage.java +++ b/adservices/service-core/java/com/android/adservices/service/consent/IConsentStorage.java @@ -117,7 +117,7 @@ public interface IConsentStorage { * * @return true if the AdId is enabled by default, false otherwise. */ - boolean getDefaultAdIdState(); + boolean getDefaultAdIdState() throws IOException; /** * Retrieves the default consent. @@ -130,7 +130,7 @@ public interface IConsentStorage { /** Returns current enrollment channel. */ @NonNull PrivacySandboxEnrollmentChannelCollection getEnrollmentChannel( - @NonNull PrivacySandboxUxCollection ux); + @NonNull PrivacySandboxUxCollection ux) throws IOException; /** * @return an {@link ImmutableList} of all known apps in the database that have not had user @@ -148,13 +148,13 @@ public interface IConsentStorage { /** Returns current UX. */ @NonNull - PrivacySandboxUxCollection getUx(); + PrivacySandboxUxCollection getUx() throws IOException; /** Returns whether the isAdIdEnabled bit is true. */ - boolean isAdIdEnabled(); + boolean isAdIdEnabled() throws IOException; /** Returns whether the isAdultAccount bit is true. */ - boolean isAdultAccount(); + boolean isAdultAccount() throws IOException; /** * Returns whether a given application (identified by package name) has had user consent @@ -167,13 +167,14 @@ public interface IConsentStorage { * application * @throws IOException if the operation fails */ - boolean isConsentRevokedForApp(@NonNull String packageName) throws IllegalArgumentException; + boolean isConsentRevokedForApp(@NonNull String packageName) + throws IllegalArgumentException, IOException; /** Returns whether the isEntryPointEnabled bit is true. */ - boolean isEntryPointEnabled(); + boolean isEntryPointEnabled() throws IOException; /** Returns whether the isU18Account bit is true. */ - boolean isU18Account(); + boolean isU18Account() throws IOException; /** Saves the default AdId state bit to data stores based on source of truth. */ void recordDefaultAdIdState(boolean defaultAdIdState) throws IOException; @@ -242,7 +243,8 @@ public interface IConsentStorage { /** Sets the current enrollment channel to storage. */ void setEnrollmentChannel( @NonNull PrivacySandboxUxCollection ux, - @NonNull PrivacySandboxEnrollmentChannelCollection channel); + @NonNull PrivacySandboxEnrollmentChannelCollection channel) + throws IOException; /** Sets the EntryPointEnabled bit to storage . */ void setEntryPointEnabled(boolean isEntryPointEnabled) throws IOException; @@ -254,14 +256,14 @@ public interface IConsentStorage { void setU18NotificationDisplayed(boolean wasU18NotificationDisplayed) throws IOException; /** Sets the current UX to storage. */ - void setUx(PrivacySandboxUxCollection ux); + void setUx(PrivacySandboxUxCollection ux) throws IOException; /** * Retrieves if GA UX notification has been displayed. * * @return true if GA UX Consent Notification was displayed, otherwise false. */ - boolean wasGaUxNotificationDisplayed(); + boolean wasGaUxNotificationDisplayed() throws IOException; /** * Retrieves if notification has been displayed. @@ -271,5 +273,5 @@ public interface IConsentStorage { boolean wasNotificationDisplayed() throws IOException; /** Returns whether the wasU18NotificationDisplayed bit is true. */ - boolean wasU18NotificationDisplayed(); + boolean wasU18NotificationDisplayed() throws IOException; } diff --git a/adservices/service-core/java/com/android/adservices/service/customaudience/CustomAudienceServiceImpl.java b/adservices/service-core/java/com/android/adservices/service/customaudience/CustomAudienceServiceImpl.java index 8373bb5eb..fcab6c9a8 100644 --- a/adservices/service-core/java/com/android/adservices/service/customaudience/CustomAudienceServiceImpl.java +++ b/adservices/service-core/java/com/android/adservices/service/customaudience/CustomAudienceServiceImpl.java @@ -203,8 +203,7 @@ public class CustomAudienceServiceImpl extends ICustomAudienceService.Stub { } // Caller permissions must be checked in the binder thread, before anything else - mFledgeAuthorizationFilter.assertAppDeclaredCustomAudiencePermission( - mContext, ownerPackageName, apiName); + mFledgeAuthorizationFilter.assertAppDeclaredPermission(mContext, ownerPackageName, apiName); final int callerUid = getCallingUid(apiName); final DevContext devContext = mDevContextFilter.createDevContext(); @@ -301,7 +300,7 @@ public class CustomAudienceServiceImpl extends ICustomAudienceService.Stub { } // Caller permissions must be checked in the binder thread, before anything else - mFledgeAuthorizationFilter.assertAppDeclaredCustomAudiencePermission( + mFledgeAuthorizationFilter.assertAppDeclaredPermission( mContext, input.getCallerPackageName(), apiName); final int callerUid = getCallingUid(apiName); @@ -386,8 +385,7 @@ public class CustomAudienceServiceImpl extends ICustomAudienceService.Stub { } // Caller permissions must be checked in the binder thread, before anything else - mFledgeAuthorizationFilter.assertAppDeclaredCustomAudiencePermission( - mContext, ownerPackageName, apiName); + mFledgeAuthorizationFilter.assertAppDeclaredPermission(mContext, ownerPackageName, apiName); final int callerUid = getCallingUid(apiName); final DevContext devContext = mDevContextFilter.createDevContext(); @@ -506,7 +504,7 @@ public class CustomAudienceServiceImpl extends ICustomAudienceService.Stub { } // Caller permissions must be checked with a non-null callingAppPackageName - mFledgeAuthorizationFilter.assertAppDeclaredCustomAudiencePermission( + mFledgeAuthorizationFilter.assertAppDeclaredPermission( mContext, devContext.getCallingAppPackageName(), apiName); CustomAudienceDao customAudienceDao = mCustomAudienceImpl.getCustomAudienceDao(); @@ -567,7 +565,7 @@ public class CustomAudienceServiceImpl extends ICustomAudienceService.Stub { } // Caller permissions must be checked with a non-null callingAppPackageName - mFledgeAuthorizationFilter.assertAppDeclaredCustomAudiencePermission( + mFledgeAuthorizationFilter.assertAppDeclaredPermission( mContext, devContext.getCallingAppPackageName(), apiName); CustomAudienceDao customAudienceDao = mCustomAudienceImpl.getCustomAudienceDao(); @@ -616,7 +614,7 @@ public class CustomAudienceServiceImpl extends ICustomAudienceService.Stub { } // Caller permissions must be checked with a non-null callingAppPackageName - mFledgeAuthorizationFilter.assertAppDeclaredCustomAudiencePermission( + mFledgeAuthorizationFilter.assertAppDeclaredPermission( mContext, devContext.getCallingAppPackageName(), apiName); CustomAudienceDao customAudienceDao = mCustomAudienceImpl.getCustomAudienceDao(); diff --git a/adservices/service-core/java/com/android/adservices/service/devapi/DevContextFilter.java b/adservices/service-core/java/com/android/adservices/service/devapi/DevContextFilter.java index 9e4b05ddc..5a78bdaea 100644 --- a/adservices/service-core/java/com/android/adservices/service/devapi/DevContextFilter.java +++ b/adservices/service-core/java/com/android/adservices/service/devapi/DevContextFilter.java @@ -147,7 +147,7 @@ public class DevContextFilter { } catch (PackageManager.NameNotFoundException e) { LogUtil.w( - "Unable to retrieve application info for app with ID %d and resolved package " + "Unable to retrieve application info for app with ID %s and resolved package " + "name '%s', considering not debuggable for safety.", callingAppPackage, callingAppPackage); return false; diff --git a/adservices/service-core/java/com/android/adservices/service/encryptionkey/EncryptionKey.java b/adservices/service-core/java/com/android/adservices/service/encryptionkey/EncryptionKey.java index 193897963..07dc2aa9c 100644 --- a/adservices/service-core/java/com/android/adservices/service/encryptionkey/EncryptionKey.java +++ b/adservices/service-core/java/com/android/adservices/service/encryptionkey/EncryptionKey.java @@ -135,6 +135,21 @@ public class EncryptionKey { return mLastFetchTime; } + /** Returns the builder for the instance */ + public EncryptionKey.Builder cloneToBuilder() { + return new EncryptionKey.Builder() + .setId(this.mId) + .setKeyType(this.mKeyType) + .setEnrollmentId(this.mEnrollmentId) + .setReportingOrigin(this.mReportingOrigin) + .setEncryptionKeyUrl(this.mEncryptionKeyUrl) + .setProtocolType(this.mProtocolType) + .setKeyCommitmentId(this.mKeyCommitmentId) + .setBody(this.mBody) + .setExpiration(this.mExpiration) + .setLastFetchTime(this.mLastFetchTime); + } + /** Builder for {@link EncryptionKey}. */ public static final class Builder { private final EncryptionKey mBuilding; diff --git a/adservices/service-core/java/com/android/adservices/service/enrollment/EnrollmentData.java b/adservices/service-core/java/com/android/adservices/service/enrollment/EnrollmentData.java index d166f3b6a..6cb1f8c0a 100644 --- a/adservices/service-core/java/com/android/adservices/service/enrollment/EnrollmentData.java +++ b/adservices/service-core/java/com/android/adservices/service/enrollment/EnrollmentData.java @@ -140,6 +140,21 @@ public class EnrollmentData { return Arrays.asList(input.trim().split(SEPARATOR)); } + /** Returns the builder for the instance */ + @NonNull + public EnrollmentData.Builder cloneToBuilder() { + return new EnrollmentData.Builder() + .setEnrollmentId(this.mEnrollmentId) + .setCompanyId(this.mCompanyId) + .setSdkNames(this.mSdkNames) + .setAttributionSourceRegistrationUrl(this.mAttributionSourceRegistrationUrl) + .setAttributionTriggerRegistrationUrl(this.mAttributionTriggerRegistrationUrl) + .setAttributionReportingUrl(this.mAttributionReportingUrl) + .setRemarketingResponseBasedRegistrationUrl( + this.mRemarketingResponseBasedRegistrationUrl) + .setEncryptionKeyUrl(this.mEncryptionKeyUrl); + } + /** Builder for {@link EnrollmentData}. */ public static final class Builder { private final EnrollmentData mBuilding; diff --git a/adservices/service-core/java/com/android/adservices/service/exception/ConsentStorageDeferException.java b/adservices/service-core/java/com/android/adservices/service/exception/ConsentStorageDeferException.java new file mode 100644 index 000000000..5edf8e68f --- /dev/null +++ b/adservices/service-core/java/com/android/adservices/service/exception/ConsentStorageDeferException.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2023 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.exception; + +import java.io.IOException; + +/** + * Exception class to indicate operation is not supported by current storage manager. This exception + * will always be caught in {@link ConsentCompositeStorage}, and ConsentCompositeStorage will call + * the next IConsentStorage instance to get/set the right value. + */ +public final class ConsentStorageDeferException extends IOException {} diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/PrivacyParams.java b/adservices/service-core/java/com/android/adservices/service/measurement/PrivacyParams.java index 00a945fa9..b62df0267 100644 --- a/adservices/service-core/java/com/android/adservices/service/measurement/PrivacyParams.java +++ b/adservices/service-core/java/com/android/adservices/service/measurement/PrivacyParams.java @@ -37,11 +37,6 @@ public final class PrivacyParams { public static final int EVENT_SOURCE_MAX_REPORTS = 1; /** - * Max reports for Install Attributed 'Navigation' {@link Source}. - */ - public static final int INSTALL_ATTR_NAVIGATION_SOURCE_MAX_REPORTS = 3; - - /** * Max reports for Install Attributed 'Event' {@link Source}. */ public static final int INSTALL_ATTR_EVENT_SOURCE_MAX_REPORTS = 2; diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/Source.java b/adservices/service-core/java/com/android/adservices/service/measurement/Source.java index 2fe5e16b0..35da5c705 100644 --- a/adservices/service-core/java/com/android/adservices/service/measurement/Source.java +++ b/adservices/service-core/java/com/android/adservices/service/measurement/Source.java @@ -121,6 +121,7 @@ public class Source { if (mParsedEventReportWindows != null) { return mParsedEventReportWindows; } + if (mEventReportWindows == null) { return null; } @@ -140,7 +141,8 @@ public class Source { } /** - * Returns parsed or default value of event report windows. + * Returns parsed or default value of event report windows (can be used during {@code Source} + * construction since the method does not require a {@code Source} object). * * @param eventReportWindows string to be parsed * @param sourceType Source's Type @@ -149,13 +151,13 @@ public class Source { * @return parsed or default value */ @Nullable - public static List<Pair<Long, Long>> getOrDefaultEventReportWindows( + public static List<Pair<Long, Long>> getOrDefaultEventReportWindowsForFlex( @Nullable JSONObject eventReportWindows, @NonNull SourceType sourceType, long expiryDelta, @NonNull Flags flags) { if (eventReportWindows == null) { - return getDefaultEventReportWindows(expiryDelta, sourceType, flags); + return getDefaultEventReportWindowsForFlex(expiryDelta, sourceType, flags); } return parseEventReportWindows(eventReportWindows); } @@ -180,17 +182,17 @@ public class Source { @NonNull JSONObject jsonObject) { List<Pair<Long, Long>> result = new ArrayList<>(); try { - long startDuration = 0; + long startTime = 0L; if (!jsonObject.isNull("start_time")) { - startDuration = jsonObject.getLong("start_time"); + startTime = jsonObject.getLong("start_time"); } JSONArray endTimesJSON = jsonObject.getJSONArray("end_times"); for (int i = 0; i < endTimesJSON.length(); i++) { - long endDuration = endTimesJSON.getLong(i); - Pair<Long, Long> window = new Pair<>(startDuration, endDuration); + long endTime = endTimesJSON.getLong(i); + Pair<Long, Long> window = Pair.create(startTime, endTime); result.add(window); - startDuration = endDuration; + startTime = endTime; } } catch (JSONException e) { LoggerFactory.getMeasurementLogger() @@ -200,24 +202,25 @@ public class Source { return result; } - private static List<Pair<Long, Long>> getDefaultEventReportWindows( + private static List<Pair<Long, Long>> getDefaultEventReportWindowsForFlex( long expiryDelta, SourceType sourceType, Flags flags) { List<Pair<Long, Long>> result = new ArrayList<>(); - List<Long> defaultEarlyWindows = - EventReportWindowCalcDelegate.getDefaultEarlyReportingWindows(sourceType, false); - List<Long> earlyWindows = + // Obtain default early report windows without regard to install-related behaviour. + List<Long> defaultEarlyWindowEnds = + EventReportWindowCalcDelegate.getDefaultEarlyReportingWindowEnds(sourceType, false); + List<Long> earlyWindowEnds = new EventReportWindowCalcDelegate(flags) - .getConfiguredOrDefaultEarlyReportingWindows( - sourceType, defaultEarlyWindows, false); + .getConfiguredOrDefaultEarlyReportingWindowEnds( + sourceType, defaultEarlyWindowEnds); long windowStart = 0; - for (long earlyWindow : earlyWindows) { - if (earlyWindow >= expiryDelta) { - continue; + for (long earlyWindowEnd : earlyWindowEnds) { + if (earlyWindowEnd >= expiryDelta) { + break; } - result.add(new Pair<>(windowStart, earlyWindow)); - windowStart = earlyWindow; + result.add(Pair.create(windowStart, earlyWindowEnd)); + windowStart = earlyWindowEnd; } - result.add(new Pair<>(windowStart, expiryDelta)); + result.add(Pair.create(windowStart, expiryDelta)); return result; } @@ -234,8 +237,7 @@ public class Source { } private double getInformationGainThreshold(Flags flags) { - int destinationMultiplier = getDestinationTypeMultiplier(flags); - if (destinationMultiplier == 2) { + if (getDestinationTypeMultiplier(flags) == 2) { return mSourceType == SourceType.EVENT ? flags.getMeasurementFlexApiMaxInformationGainDualDestinationEvent() : flags.getMeasurementFlexApiMaxInformationGainDualDestinationNavigation(); @@ -262,32 +264,31 @@ public class Source { setFlipProbability(mTriggerSpecs.getFlipProbability(this, flags)); return; } - boolean installCase = SourceNoiseHandler.isInstallDetectionEnabled(this); EventReportWindowCalcDelegate eventReportWindowCalcDelegate = new EventReportWindowCalcDelegate(flags); int reportingWindowCountForNoising = - eventReportWindowCalcDelegate.getReportingWindowCountForNoising(this, installCase); - int maxReportCount = - eventReportWindowCalcDelegate.getMaxReportCount(this, installCase); - int destinationMultiplier = getDestinationTypeMultiplier(flags); + eventReportWindowCalcDelegate.getReportingWindowCountForNoising(this); long numberOfStates = Combinatorics.getNumberOfStarsAndBarsSequences( - /*numStars=*/ maxReportCount, + /*numStars=*/ eventReportWindowCalcDelegate.getMaxReportCount(this), /*numBars=*/ getTriggerDataCardinality() * reportingWindowCountForNoising - * destinationMultiplier); + * getDestinationTypeMultiplier(flags)); setNumStates(numberOfStates); setFlipProbability(Combinatorics.getFlipProbability(numberOfStates)); } + /** Should source report coarse destinations */ + public boolean shouldReportCoarseDestinations(Flags flags) { + return flags.getMeasurementEnableCoarseEventReportDestinations() + && hasCoarseEventReportDestinations(); + } + /** * Returns the number of destination types to use in privacy computations. */ public int getDestinationTypeMultiplier(Flags flags) { - boolean shouldReportCoarseDestinations = - flags.getMeasurementEnableCoarseEventReportDestinations() - && hasCoarseEventReportDestinations(); - return !shouldReportCoarseDestinations && hasAppDestinations() + return !shouldReportCoarseDestinations(flags) && hasAppDestinations() && hasWebDestinations() ? SourceNoiseHandler.DUAL_DESTINATION_IMPRESSION_NOISE_MULTIPLIER : SourceNoiseHandler.SINGLE_DESTINATION_IMPRESSION_NOISE_MULTIPLIER; @@ -650,9 +651,9 @@ public class Source { /** * Time when {@link Source} event report window will expire. (Appends the Event Time to window) */ - public Long getProcessedEventReportWindow() { + public long getEffectiveEventReportWindow() { if (mEventReportWindow == null) { - return null; + return getExpiryTime(); } // TODO(b/290098169): Cleanup after a few releases // Handling cases where ReportWindow is already stored as mEventTime + mEventReportWindow @@ -744,6 +745,11 @@ public class Source { return mInstallCooldownWindow; } + /** Check if install detection is enabled for the source. */ + public boolean isInstallDetectionEnabled() { + return getInstallCooldownWindow() > 0 && hasAppDestinations(); + } + /** * Is an App-install attributed to the {@link Source}. */ @@ -971,10 +977,9 @@ public class Source { @Nullable Integer maxEventLevelReports, @NonNull Flags flags) { if (maxEventLevelReports == null) { - maxEventLevelReports = - sourceType == Source.SourceType.NAVIGATION - ? PrivacyParams.NAVIGATION_SOURCE_MAX_REPORTS - : flags.getMeasurementVtcConfigurableMaxEventReportsCount(); + return sourceType == Source.SourceType.NAVIGATION + ? PrivacyParams.NAVIGATION_SOURCE_MAX_REPORTS + : flags.getMeasurementVtcConfigurableMaxEventReportsCount(); } return maxEventLevelReports; } diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/attribution/AttributionJobHandler.java b/adservices/service-core/java/com/android/adservices/service/measurement/attribution/AttributionJobHandler.java index 20dbe424e..3d2eec8c2 100644 --- a/adservices/service-core/java/com/android/adservices/service/measurement/attribution/AttributionJobHandler.java +++ b/adservices/service-core/java/com/android/adservices/service/measurement/attribution/AttributionJobHandler.java @@ -57,6 +57,7 @@ import com.android.adservices.service.measurement.reporting.DebugKeyAccessor; import com.android.adservices.service.measurement.reporting.DebugReportApi; import com.android.adservices.service.measurement.reporting.DebugReportApi.Type; import com.android.adservices.service.measurement.reporting.EventReportWindowCalcDelegate; +import com.android.adservices.service.measurement.reporting.EventReportWindowCalcDelegate.MomentPlacement; import com.android.adservices.service.measurement.util.BaseUriExtractor; import com.android.adservices.service.measurement.util.Filter; import com.android.adservices.service.measurement.util.UnsignedLong; @@ -813,12 +814,8 @@ class AttributionJobHandler { return TriggeringStatus.DROPPED; } - if (mEventReportWindowCalcDelegate.getReportingTime( - source, trigger.getTriggerTime(), trigger.getDestinationType()) == -1 - && (source.getTriggerSpecsString() == null - || source.getTriggerSpecsString().isEmpty())) { - mDebugReportApi.scheduleTriggerDebugReport( - source, trigger, null, measurementDao, Type.TRIGGER_EVENT_REPORT_WINDOW_PASSED); + if (source.getTriggerSpecs() == null + && !isTriggerFallsWithinWindow(source, trigger, measurementDao)) { return TriggeringStatus.DROPPED; } @@ -1332,16 +1329,10 @@ class AttributionJobHandler { private boolean isWithinReportLimit( Source source, int existingReportCount, @EventSurfaceType int destinationType) { - return mEventReportWindowCalcDelegate.getMaxReportCount( - source, hasAppInstallAttributionOccurred(source, destinationType)) + return mEventReportWindowCalcDelegate.getMaxReportCount(source, destinationType) > existingReportCount; } - private static boolean hasAppInstallAttributionOccurred( - Source source, @EventSurfaceType int destinationType) { - return destinationType == EventSurfaceType.APP && source.isInstallAttributed(); - } - private static boolean isWithinInstallCooldownWindow(Source source, Trigger trigger) { return trigger.getTriggerTime() < (source.getEventTime() + source.getInstallCooldownWindow()); @@ -1754,4 +1745,27 @@ class AttributionJobHandler { attributionStatus.setAggregateDebugReportCount( attributionStatus.getAggregateDebugReportCount() + count); } + + private boolean isTriggerFallsWithinWindow( + Source source, Trigger trigger, IMeasurementDao measurementDao) + throws DatastoreException { + MomentPlacement momentPlacement = + mEventReportWindowCalcDelegate.fallsWithinWindow( + source, trigger.getTriggerTime(), trigger.getDestinationType()); + if (momentPlacement == MomentPlacement.BEFORE) { + mDebugReportApi.scheduleTriggerDebugReport( + source, + trigger, + null, + measurementDao, + Type.TRIGGER_EVENT_REPORT_WINDOW_NOT_STARTED); + return false; + } + if (momentPlacement == MomentPlacement.AFTER) { + mDebugReportApi.scheduleTriggerDebugReport( + source, trigger, null, measurementDao, Type.TRIGGER_EVENT_REPORT_WINDOW_PASSED); + return false; + } + return true; + } } diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/noising/ImpressionNoiseUtil.java b/adservices/service-core/java/com/android/adservices/service/measurement/noising/ImpressionNoiseUtil.java index 0a456919f..c83f608b6 100644 --- a/adservices/service-core/java/com/android/adservices/service/measurement/noising/ImpressionNoiseUtil.java +++ b/adservices/service-core/java/com/android/adservices/service/measurement/noising/ImpressionNoiseUtil.java @@ -29,72 +29,6 @@ import java.util.concurrent.ThreadLocalRandom; */ public final class ImpressionNoiseUtil { - /** - * This is used in the scenario where both app and web destinations are available with the - * {@link com.android.adservices.service.measurement.Source} and the source is of {@link - * com.android.adservices.service.measurement.Source.SourceType#EVENT} type and post-install - * detection is enabled (cooldown window being available). It's a special case because in this - * condition an extra early window is added only if install detection is enabled (install - * cool-down window is available) and only app conversions are relevant in that window. - * - * <p>Reading guide - The outermost array signifies different states of reporting - one of them - * will be picked at a time. The middle array holds reports in 1st and 2nd window, so there will - * be either 0 elements (no conversions in either window), 1 element (a conversion report in one - * of the windows) and 2 elements (conversions in both windows). The innermost array represents - * a single report. 3 elements in the innermost array are trigger metadata (0 - trigger1 or 1 - - * trigger2), window index (0 - window1, 1 - window2) and destination type (0 - app or 1 - web). - * - * <p>E.g. The element at index 5 is {{0, 1, 0}, {0, 1, 0}}, it means 2 conversions with trigger - * metadata as 0 in window2 (1) of app destination type(0). - */ - public static final int[][][] DUAL_DESTINATION_POST_INSTALL_FAKE_REPORT_CONFIG = - new int[][][] { - // window1 - no conversion, window 2 - no conversion - {}, - // window1 - no conversion, window 2 - 1 conversion with metadata 0 - {{0, 1, 0}}, - {{0, 1, 1}}, - // window1 - no conversion, window 2 - 1 conversion with metadata 1 - {{1, 1, 0}}, - {{1, 1, 1}}, - // window1 - no conversion, window 2 - 2 conversions with metadata 0 and 0 - {{0, 1, 0}, {0, 1, 0}}, - {{0, 1, 0}, {0, 1, 1}}, - // window1 - no conversion, window 2 - 2 conversions with metadata 0 and 1 - {{0, 1, 0}, {1, 1, 0}}, - {{0, 1, 0}, {1, 1, 1}}, - {{0, 1, 1}, {1, 1, 0}}, - // window1 - no conversion, window 2 - 2 conversions with metadata 1 and 1 - {{1, 1, 0}, {1, 1, 0}}, - {{1, 1, 0}, {1, 1, 1}}, - // window1 - 1 app conversion with metadata 0, window 2 - no conversion - {{0, 0, 0}}, - // window1 - 1 app conversion with metadata 1, window 2 - no conversion - {{1, 0, 0}}, - // window1 - 2 conversions with metadata 0 and 0, window 2 - no conversion - {{0, 0, 0}, {0, 0, 0}}, - // window1 - 2 app conversions with metadata 0 and 1, window 2 - no conversion - {{0, 0, 0}, {1, 0, 0}}, - // window1 - 2 app conversions with metadata 1 and 1, window 2 - no conversion - {{1, 0, 0}, {1, 0, 0}}, - // window1 - 1 app conversion with metadata 0, window 2 - 1 conversion with - // metadata 0 - {{0, 0, 0}, {0, 1, 0}}, - {{0, 0, 0}, {0, 1, 1}}, - // window1 - 1 app conversion with metadata 0, window 2 - 1 conversion with - // metadata 1 - {{0, 0, 0}, {1, 1, 0}}, - {{0, 0, 0}, {1, 1, 1}}, - // window1 - 1 app conversion with metadata 1, window 2 - 1 conversions with - // metadata 0 - {{1, 0, 0}, {0, 1, 0}}, - {{1, 0, 0}, {0, 1, 1}}, - // window1 - 1 app conversion with metadata 1, window 2 - 1 conversions with - // metadata 1 - {{1, 0, 0}, {1, 1, 0}}, - {{1, 0, 0}, {1, 1, 1}} - }; - private ImpressionNoiseUtil() {} /** diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/noising/SourceNoiseHandler.java b/adservices/service-core/java/com/android/adservices/service/measurement/noising/SourceNoiseHandler.java index 2c28fa51e..8897662a9 100644 --- a/adservices/service-core/java/com/android/adservices/service/measurement/noising/SourceNoiseHandler.java +++ b/adservices/service-core/java/com/android/adservices/service/measurement/noising/SourceNoiseHandler.java @@ -30,11 +30,9 @@ import com.google.common.collect.ImmutableList; import java.math.BigDecimal; import java.math.RoundingMode; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.Random; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; @@ -85,33 +83,24 @@ public class SourceNoiseHandler { List<Source.FakeReport> fakeReports; TriggerSpecs triggerSpecs = source.getTriggerSpecs(); if (triggerSpecs == null) { - if (isVtcDualDestinationModeWithPostInstallEnabled(source)) { - // Source is 'EVENT' type, both app and web destination are set and install - // exclusivity - // window is provided. Pick one of the static reporting states randomly. - fakeReports = generateVtcDualDestinationPostInstallFakeReports(source); - } else { - // There will at least be one (app or web) destination available - ImpressionNoiseParams noiseParams = getImpressionNoiseParams(source); - fakeReports = - ImpressionNoiseUtil.selectRandomStateAndGenerateReportConfigs( - noiseParams, rand) - .stream() - .map( - reportConfig -> - new Source.FakeReport( - new UnsignedLong( - Long.valueOf(reportConfig[0])), - mEventReportWindowCalcDelegate - .getReportingTimeForNoising( - source, - reportConfig[1], - isInstallDetectionEnabled( - source)), - resolveFakeReportDestinations( - source, reportConfig[2]))) - .collect(Collectors.toList()); - } + // There will at least be one (app or web) destination available + ImpressionNoiseParams noiseParams = getImpressionNoiseParams(source); + fakeReports = + ImpressionNoiseUtil.selectRandomStateAndGenerateReportConfigs( + noiseParams, rand) + .stream() + .map( + reportConfig -> + new Source.FakeReport( + new UnsignedLong( + Long.valueOf(reportConfig[0])), + mEventReportWindowCalcDelegate + .getReportingTimeForNoising( + source, + reportConfig[1]), + resolveFakeReportDestinations( + source, reportConfig[2]))) + .collect(Collectors.toList()); } else { int destinationTypeMultiplier = source.getDestinationTypeMultiplier(mFlags); List<int[]> fakeReportConfigs = @@ -144,51 +133,11 @@ public class SourceNoiseHandler { /** @return Probability of selecting random state for attribution */ public double getRandomAttributionProbability(@NonNull Source source) { - if (source.getTriggerSpecs() != null - || mFlags.getMeasurementEnableConfigurableEventReportingWindows() - || mFlags.getMeasurementEnableVtcConfigurableMaxEventReports() - || (mFlags.getMeasurementFlexLiteApiEnabled() - && (source.getMaxEventLevelReports() != null - || source.hasManualEventReportWindows()))) { - return convertToDoubleAndLimitDecimal(source.getFlipProbability(mFlags)); - } - // TODO(b/290117352): Remove Hardcoded noise values - - // Both destinations are set and install attribution is supported - if (!shouldReportCoarseDestinations(source) - && source.hasWebDestinations() - && isInstallDetectionEnabled(source)) { - return source.getSourceType() == Source.SourceType.EVENT - ? convertToDoubleAndLimitDecimal( - mFlags.getMeasurementInstallAttrDualDestinationEventNoiseProbability()) - : convertToDoubleAndLimitDecimal( - mFlags.getMeasurementInstallAttrDualDestinationNavigationNoiseProbability()); - } - - // Both destinations are set but install attribution isn't supported - if (!shouldReportCoarseDestinations(source) - && source.hasAppDestinations() - && source.hasWebDestinations()) { - return source.getSourceType() == Source.SourceType.EVENT - ? convertToDoubleAndLimitDecimal( - mFlags.getMeasurementDualDestinationEventNoiseProbability()) - : convertToDoubleAndLimitDecimal( - mFlags.getMeasurementDualDestinationNavigationNoiseProbability()); - } - - // App destination is set and install attribution is supported - if (isInstallDetectionEnabled(source)) { - return source.getSourceType() == Source.SourceType.EVENT - ? convertToDoubleAndLimitDecimal( - mFlags.getMeasurementInstallAttrEventNoiseProbability()) - : convertToDoubleAndLimitDecimal( - mFlags.getMeasurementInstallAttrNavigationNoiseProbability()); - } - - // One of the destinations is available without install attribution support - return source.getSourceType() == Source.SourceType.EVENT - ? convertToDoubleAndLimitDecimal(mFlags.getMeasurementEventNoiseProbability()) - : convertToDoubleAndLimitDecimal(mFlags.getMeasurementNavigationNoiseProbability()); + // Methods on Source and EventReportWindowCalcDelegate that calculate flip probability for + // the source rely on reporting windows and max reports that are obtained with consideration + // to install-state and its interaction with configurable report windows and configurable + // max reports. + return convertToDoubleAndLimitDecimal(source.getFlipProbability(mFlags)); } private double convertToDoubleAndLimitDecimal(double probability) { @@ -197,15 +146,6 @@ public class SourceNoiseHandler { .doubleValue(); } - private boolean isVtcDualDestinationModeWithPostInstallEnabled(Source source) { - return !shouldReportCoarseDestinations(source) - && !source.hasManualEventReportWindows() - && source.getMaxEventLevelReports() == null - && source.getSourceType() == Source.SourceType.EVENT - && source.hasWebDestinations() - && isInstallDetectionEnabled(source); - } - /** * Either both app and web destinations can be available or one of them will be available. When * both destinations are available, we double the number of states at noise generation to be @@ -216,7 +156,7 @@ public class SourceNoiseHandler { * @return app or web destination {@link Uri} */ private List<Uri> resolveFakeReportDestinations(Source source, int destinationIdentifier) { - if (shouldReportCoarseDestinations(source)) { + if (source.shouldReportCoarseDestinations(mFlags)) { ImmutableList.Builder<Uri> destinations = new ImmutableList.Builder<>(); Optional.ofNullable(source.getAppDestinations()).ifPresent(destinations::addAll); Optional.ofNullable(source.getWebDestinations()).ifPresent(destinations::addAll); @@ -234,44 +174,13 @@ public class SourceNoiseHandler { : source.getWebDestinations(); } - /** Check if install detection is enabled for the source. */ - public static boolean isInstallDetectionEnabled(@NonNull Source source) { - return source.getInstallCooldownWindow() > 0 && source.hasAppDestinations(); - } - - private boolean shouldReportCoarseDestinations(Source source) { - return mFlags.getMeasurementEnableCoarseEventReportDestinations() - && source.hasCoarseEventReportDestinations(); - } - - private List<Source.FakeReport> generateVtcDualDestinationPostInstallFakeReports( - Source source) { - int[][][] fakeReportsConfig = - ImpressionNoiseUtil.DUAL_DESTINATION_POST_INSTALL_FAKE_REPORT_CONFIG; - int randomIndex = new Random().nextInt(fakeReportsConfig.length); - int[][] reportsConfig = fakeReportsConfig[randomIndex]; - return Arrays.stream(reportsConfig) - .map( - reportConfig -> - new Source.FakeReport( - new UnsignedLong(Long.valueOf(reportConfig[0])), - mEventReportWindowCalcDelegate.getReportingTimeForNoising( - source, - /* window index */ reportConfig[1], - isInstallDetectionEnabled(source)), - resolveFakeReportDestinations(source, reportConfig[2]))) - .collect(Collectors.toList()); - } - @VisibleForTesting ImpressionNoiseParams getImpressionNoiseParams(Source source) { int destinationTypeMultiplier = source.getDestinationTypeMultiplier(mFlags); return new ImpressionNoiseParams( - mEventReportWindowCalcDelegate.getMaxReportCount( - source, isInstallDetectionEnabled(source)), + mEventReportWindowCalcDelegate.getMaxReportCount(source), source.getTriggerDataCardinality(), - mEventReportWindowCalcDelegate.getReportingWindowCountForNoising( - source, isInstallDetectionEnabled(source)), + mEventReportWindowCalcDelegate.getReportingWindowCountForNoising(source), destinationTypeMultiplier); } } diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/registration/AsyncRedirect.java b/adservices/service-core/java/com/android/adservices/service/measurement/registration/AsyncRedirect.java index 1c76ade57..6106b1a52 100644 --- a/adservices/service-core/java/com/android/adservices/service/measurement/registration/AsyncRedirect.java +++ b/adservices/service-core/java/com/android/adservices/service/measurement/registration/AsyncRedirect.java @@ -18,46 +18,31 @@ package com.android.adservices.service.measurement.registration; import android.net.Uri; -import java.util.ArrayList; -import java.util.List; - -/** Wrapper for a list of redirect Uris and a redirect type */ public class AsyncRedirect { - private final List<Uri> mLocationRedirects; - private final List<Uri> mListRedirects; - - public AsyncRedirect() { - mLocationRedirects = new ArrayList<>(); - mListRedirects = new ArrayList<>(); + public enum RedirectBehavior { + /** Do not modify redirect path */ + AS_IS, + /** Prepend well known path prefix to path of redirect url for Location type redirects */ + LOCATION_TO_WELL_KNOWN, } - public AsyncRedirect(List<Uri> locationRedirects, List<Uri> listRedirects) { - mLocationRedirects = locationRedirects; - mListRedirects = listRedirects; - } + private final Uri mUri; + private final RedirectBehavior mRedirectBehavior; - /** The list the redirect Uris */ - public List<Uri> getRedirects() { - List<Uri> allRedirects = new ArrayList<>(mListRedirects); - allRedirects.addAll(mLocationRedirects); - return allRedirects; + public AsyncRedirect(Uri uri, RedirectBehavior redirectBehavior) { + mUri = uri; + mRedirectBehavior = redirectBehavior; } - /** Get list by redirect type */ - public List<Uri> getRedirectsByType(AsyncRegistration.RedirectType redirectType) { - if (redirectType == AsyncRegistration.RedirectType.LOCATION) { - return new ArrayList<>(mLocationRedirects); - } else { - return new ArrayList<>(mListRedirects); - } + public Uri getUri() { + return mUri; } - /** Add to the list the redirect Uris based on type */ - public void addToRedirects(AsyncRegistration.RedirectType redirectType, List<Uri> uris) { - if (redirectType == AsyncRegistration.RedirectType.LOCATION) { - mLocationRedirects.addAll(uris); - } else { - mListRedirects.addAll(uris); - } + public RedirectBehavior getRedirectBehavior() { + return mRedirectBehavior; } + + + + } diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/registration/AsyncRedirects.java b/adservices/service-core/java/com/android/adservices/service/measurement/registration/AsyncRedirects.java new file mode 100644 index 000000000..d05cb2903 --- /dev/null +++ b/adservices/service-core/java/com/android/adservices/service/measurement/registration/AsyncRedirects.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2023 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.registration; + +import android.net.Uri; + +import com.android.adservices.service.Flags; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** Class for handling redirects */ +public class AsyncRedirects { + public static final String HEADER_ATTRIBUTION_REPORTING_REDIRECT_CONFIG = + "Attribution-Reporting-Redirect-Config"; + public static final String REDIRECT_302_TO_WELL_KNOWN = "redirect-302-to-well-known"; + public static final String WELL_KNOWN_PATH_SEGMENT = + ".well-known/attribution-reporting/register-redirect"; + public static final String WELL_KNOWN_QUERY_PARAM = "302_url"; + public static final String REDIRECT_LIST_HEADER_KEY = "Attribution-Reporting-Redirect"; + public static final String REDIRECT_LOCATION_HEADER_KEY = "Location"; + private final List<AsyncRedirect> mLocationRedirects; + private final List<AsyncRedirect> mListRedirects; + + public AsyncRedirects() { + mLocationRedirects = new ArrayList<>(); + mListRedirects = new ArrayList<>(); + } + + /** Return flattened list of {@link AsyncRedirect} */ + public List<AsyncRedirect> getRedirects() { + List<AsyncRedirect> allRedirects = new ArrayList<>(mListRedirects); + allRedirects.addAll(mLocationRedirects); + + return allRedirects; + } + + /** Get list of {@link AsyncRedirect} by redirect type */ + public List<AsyncRedirect> getRedirectsByType(AsyncRegistration.RedirectType redirectType) { + if (redirectType == AsyncRegistration.RedirectType.LOCATION) { + return new ArrayList<>(mLocationRedirects); + } else { + return new ArrayList<>(mListRedirects); + } + } + + /** Process redirects based on the given headers */ + public void configure( + Map<String, List<String>> headers, Flags flags, AsyncRegistration parentRegistration) { + if (!parentRegistration.shouldProcessRedirects()) { + return; + } + + Map<AsyncRegistration.RedirectType, List<Uri>> urisByType = + FetcherUtil.parseRedirects(headers); + + for (Uri locationRedirectUri : urisByType.get(AsyncRegistration.RedirectType.LOCATION)) { + if (shouldRedirect302ToWellKnown(headers, flags, parentRegistration)) { + mLocationRedirects.add( + new AsyncRedirect( + getLocationRedirectToWellKnownUri(locationRedirectUri), + AsyncRedirect.RedirectBehavior.LOCATION_TO_WELL_KNOWN)); + } else { + mLocationRedirects.add( + new AsyncRedirect( + locationRedirectUri, AsyncRedirect.RedirectBehavior.AS_IS)); + } + } + + for (Uri listRedirectUri : urisByType.get(AsyncRegistration.RedirectType.LIST)) { + mListRedirects.add( + new AsyncRedirect(listRedirectUri, AsyncRedirect.RedirectBehavior.AS_IS)); + } + } + + private static boolean shouldRedirect302ToWellKnown( + Map<String, List<String>> headers, Flags flags, AsyncRegistration parentRegistration) { + boolean isParentRegistrationRedirectsToWellKnown = + AsyncRedirect.RedirectBehavior.LOCATION_TO_WELL_KNOWN.equals( + parentRegistration.getRedirectBehavior()); + + return flags.getMeasurementEnableRedirectToWellKnownPath() + && (isParentRegistrationRedirectsToWellKnown + || isRedirect302ToWellKnownPath(headers)); + } + + /** + * Return true if the given headers indicate redirects should prepend well known prefix to the + * path. + */ + private static boolean isRedirect302ToWellKnownPath(Map<String, List<String>> headers) { + if (!headers.containsKey(HEADER_ATTRIBUTION_REPORTING_REDIRECT_CONFIG)) { + return false; + } + List<String> config = headers.get(HEADER_ATTRIBUTION_REPORTING_REDIRECT_CONFIG); + if (config == null || config.size() != 1) { + return false; + } + + return config.get(0).equalsIgnoreCase(REDIRECT_302_TO_WELL_KNOWN); + } + + private Uri getLocationRedirectToWellKnownUri(Uri redirectUri) { + return redirectUri + .buildUpon() + .encodedPath(WELL_KNOWN_PATH_SEGMENT) + .clearQuery() + .appendQueryParameter(WELL_KNOWN_QUERY_PARAM, redirectUri.toString()) + .build(); + } +} diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/registration/AsyncRegistration.java b/adservices/service-core/java/com/android/adservices/service/measurement/registration/AsyncRegistration.java index 98c95bfe0..ade38df69 100644 --- a/adservices/service-core/java/com/android/adservices/service/measurement/registration/AsyncRegistration.java +++ b/adservices/service-core/java/com/android/adservices/service/measurement/registration/AsyncRegistration.java @@ -51,9 +51,9 @@ public class AsyncRegistration { private final boolean mDebugKeyAllowed; private final boolean mAdIdPermission; @Nullable private String mRegistrationId; - @Nullable private final String mPlatformAdId; @Nullable private String mPostBody; + private final AsyncRedirect.RedirectBehavior mRedirectBehavior; public enum RedirectType { LOCATION, @@ -77,6 +77,7 @@ public class AsyncRegistration { mRegistrationId = builder.mRegistrationId; mPlatformAdId = builder.mPlatformAdId; mPostBody = builder.mPostBody; + mRedirectBehavior = builder.mRedirectBehavior; } @Override @@ -99,7 +100,8 @@ public class AsyncRegistration { && mType == that.mType && Objects.equals(mRegistrationId, that.mRegistrationId) && mPlatformAdId.equals(that.mPlatformAdId) - && Objects.equals(mPostBody, that.mPostBody); + && Objects.equals(mPostBody, that.mPostBody) + && Objects.equals(mRedirectBehavior, that.mRedirectBehavior); } @Override @@ -120,7 +122,8 @@ public class AsyncRegistration { mAdIdPermission, mRegistrationId, mPlatformAdId, - mPostBody); + mPostBody, + mRedirectBehavior); } /** Unique identifier for the {@link AsyncRegistration}. */ @@ -214,6 +217,11 @@ public class AsyncRegistration { return mPostBody; } + /** Return the configuration for redirect behavior. */ + public AsyncRedirect.RedirectBehavior getRedirectBehavior() { + return mRedirectBehavior; + } + /** Increments the retry count of the current record. */ public void incrementRetryCount() { ++mRetryCount; @@ -260,9 +268,9 @@ public class AsyncRegistration { private boolean mDebugKeyAllowed; private boolean mAdIdPermission; @Nullable private String mRegistrationId; - @Nullable private String mPlatformAdId; @Nullable private String mPostBody; + private AsyncRedirect.RedirectBehavior mRedirectBehavior; /** See {@link AsyncRegistration#getId()}. */ @NonNull @@ -385,6 +393,12 @@ public class AsyncRegistration { return this; } + /** See {@link AsyncRegistration#getRedirectBehavior()}. */ + public Builder setRedirectBehavior(AsyncRedirect.RedirectBehavior redirectBehavior) { + mRedirectBehavior = redirectBehavior; + return this; + } + /** Build the {@link AsyncRegistration}. */ @NonNull public AsyncRegistration build() { diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/registration/AsyncRegistrationQueueRunner.java b/adservices/service-core/java/com/android/adservices/service/measurement/registration/AsyncRegistrationQueueRunner.java index 207f8b3f0..41f66aea2 100644 --- a/adservices/service-core/java/com/android/adservices/service/measurement/registration/AsyncRegistrationQueueRunner.java +++ b/adservices/service-core/java/com/android/adservices/service/measurement/registration/AsyncRegistrationQueueRunner.java @@ -210,10 +210,11 @@ public class AsyncRegistrationQueueRunner { AsyncRegistration asyncRegistration, Set<Uri> failedOrigins) { AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); asyncFetchStatus.setRetryCount(Long.valueOf(asyncRegistration.getRetryCount()).intValue()); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); long startTime = asyncRegistration.getRequestTime(); Optional<Source> resultSource = - mAsyncSourceFetcher.fetchSource(asyncRegistration, asyncFetchStatus, asyncRedirect); + mAsyncSourceFetcher.fetchSource( + asyncRegistration, asyncFetchStatus, asyncRedirects); long endTime = System.currentTimeMillis(); asyncFetchStatus.setRegistrationDelay(endTime - startTime); @@ -225,7 +226,7 @@ public class AsyncRegistrationQueueRunner { storeSource(resultSource.get(), asyncRegistration, dao); } handleSuccess( - asyncRegistration, asyncFetchStatus, asyncRedirect, dao); + asyncRegistration, asyncFetchStatus, asyncRedirects, dao); } else { handleFailure( asyncRegistration, asyncFetchStatus, failedOrigins, dao); @@ -273,10 +274,11 @@ public class AsyncRegistrationQueueRunner { AsyncRegistration asyncRegistration, Set<Uri> failedOrigins) { AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); asyncFetchStatus.setRetryCount(Long.valueOf(asyncRegistration.getRetryCount()).intValue()); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); long startTime = asyncRegistration.getRequestTime(); - Optional<Trigger> resultTrigger = mAsyncTriggerFetcher.fetchTrigger( - asyncRegistration, asyncFetchStatus, asyncRedirect); + Optional<Trigger> resultTrigger = + mAsyncTriggerFetcher.fetchTrigger( + asyncRegistration, asyncFetchStatus, asyncRedirects); long endTime = System.currentTimeMillis(); asyncFetchStatus.setRegistrationDelay(endTime - startTime); @@ -288,7 +290,7 @@ public class AsyncRegistrationQueueRunner { storeTrigger(resultTrigger.get(), dao); } handleSuccess( - asyncRegistration, asyncFetchStatus, asyncRedirect, dao); + asyncRegistration, asyncFetchStatus, asyncRedirects, dao); } else { handleFailure( asyncRegistration, asyncFetchStatus, failedOrigins, dao); @@ -583,11 +585,11 @@ public class AsyncRegistrationQueueRunner { < FlagsFactory.getFlags().getMeasurementMaxTriggersPerDestination(); } - private static AsyncRegistration createAsyncRegistrationFromRedirect( - AsyncRegistration asyncRegistration, Uri redirectUri) { + private AsyncRegistration createAsyncRegistrationFromRedirect( + AsyncRegistration asyncRegistration, AsyncRedirect asyncRedirect) { return new AsyncRegistration.Builder() .setId(UUID.randomUUID().toString()) - .setRegistrationUri(redirectUri) + .setRegistrationUri(asyncRedirect.getUri()) .setWebDestination(asyncRegistration.getWebDestination()) .setOsDestination(asyncRegistration.getOsDestination()) .setRegistrant(asyncRegistration.getRegistrant()) @@ -600,6 +602,7 @@ public class AsyncRegistrationQueueRunner { .setDebugKeyAllowed(asyncRegistration.getDebugKeyAllowed()) .setAdIdPermission(asyncRegistration.hasAdIdPermission()) .setRegistrationId(asyncRegistration.getRegistrationId()) + .setRedirectBehavior(asyncRedirect.getRedirectBehavior()) .build(); } @@ -690,14 +693,14 @@ public class AsyncRegistrationQueueRunner { private void handleSuccess( AsyncRegistration asyncRegistration, AsyncFetchStatus asyncFetchStatus, - AsyncRedirect asyncRedirect, + AsyncRedirects asyncRedirects, IMeasurementDao dao) throws DatastoreException { // deleteAsyncRegistration will throw an exception & rollback the transaction if the record // is already deleted. This can happen if both fallback & regular job are running at the // same time or if deletion job deletes the records. dao.deleteAsyncRegistration(asyncRegistration.getId()); - if (asyncRedirect.getRedirects().isEmpty()) { + if (asyncRedirects.getRedirects().isEmpty()) { return; } int maxRedirects = FlagsFactory.getFlags().getMeasurementMaxRegistrationRedirects(); @@ -706,16 +709,17 @@ public class AsyncRegistrationQueueRunner { asyncRegistration.getRegistrationId(), DataType.REGISTRATION_REDIRECT_COUNT); int currentCount = keyValueData.getRegistrationRedirectCount(); - if (currentCount == maxRedirects) { + if (currentCount >= maxRedirects) { asyncFetchStatus.setRedirectError(true); return; } - for (Uri uri : asyncRedirect.getRedirects()) { + + for (AsyncRedirect asyncRedirect : asyncRedirects.getRedirects()) { if (currentCount >= maxRedirects) { break; } dao.insertAsyncRegistration( - createAsyncRegistrationFromRedirect(asyncRegistration, uri)); + createAsyncRegistrationFromRedirect(asyncRegistration, asyncRedirect)); currentCount++; } keyValueData.setRegistrationRedirectCount(currentCount); diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/registration/AsyncSourceFetcher.java b/adservices/service-core/java/com/android/adservices/service/measurement/registration/AsyncSourceFetcher.java index c1f3a30c7..26157a01b 100644 --- a/adservices/service-core/java/com/android/adservices/service/measurement/registration/AsyncSourceFetcher.java +++ b/adservices/service-core/java/com/android/adservices/service/measurement/registration/AsyncSourceFetcher.java @@ -492,8 +492,9 @@ public class AsyncSourceFetcher { Source.SourceType sourceType, int maxEventLevelReports, Source.TriggerDataMatching triggerDataMatching) { - List<Pair<Long, Long>> parsedEventReportWindows = Source.getOrDefaultEventReportWindows( - eventReportWindows, sourceType, expiry, mFlags); + List<Pair<Long, Long>> parsedEventReportWindows = + Source.getOrDefaultEventReportWindowsForFlex( + eventReportWindows, sourceType, expiry, mFlags); long defaultStart = parsedEventReportWindows.get(0).first; List<Long> defaultEnds = parsedEventReportWindows.stream().map((x) -> x.second).collect(Collectors.toList()); @@ -799,7 +800,7 @@ public class AsyncSourceFetcher { public Optional<Source> fetchSource( AsyncRegistration asyncRegistration, AsyncFetchStatus asyncFetchStatus, - AsyncRedirect asyncRedirect) { + AsyncRedirects asyncRedirects) { HttpURLConnection urlConnection = null; Map<String, List<String>> headers; if (!asyncRegistration.getRegistrationUri().getScheme().equalsIgnoreCase("https")) { @@ -852,9 +853,7 @@ public class AsyncSourceFetcher { } } - if (asyncRegistration.shouldProcessRedirects()) { - FetcherUtil.parseRedirects(headers).forEach(asyncRedirect::addToRedirects); - } + asyncRedirects.configure(headers, mFlags, asyncRegistration); if (!isSourceHeaderPresent(headers)) { asyncFetchStatus.setEntityStatus(AsyncFetchStatus.EntityStatus.HEADER_MISSING); @@ -864,7 +863,9 @@ public class AsyncSourceFetcher { Optional<String> enrollmentId = mFlags.isDisableMeasurementEnrollmentCheck() - ? Optional.of(Enrollment.FAKE_ENROLLMENT) + ? WebAddresses.topPrivateDomainAndScheme( + asyncRegistration.getRegistrationUri()) + .map(Uri::toString) : Enrollment.getValidEnrollmentId( asyncRegistration.getRegistrationUri(), asyncRegistration.getRegistrant().getAuthority(), diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/registration/AsyncTriggerFetcher.java b/adservices/service-core/java/com/android/adservices/service/measurement/registration/AsyncTriggerFetcher.java index 9fd56f6c6..d7d5e9e95 100644 --- a/adservices/service-core/java/com/android/adservices/service/measurement/registration/AsyncTriggerFetcher.java +++ b/adservices/service-core/java/com/android/adservices/service/measurement/registration/AsyncTriggerFetcher.java @@ -308,7 +308,7 @@ public class AsyncTriggerFetcher { public Optional<Trigger> fetchTrigger( AsyncRegistration asyncRegistration, AsyncFetchStatus asyncFetchStatus, - AsyncRedirect asyncRedirect) { + AsyncRedirects asyncRedirects) { HttpURLConnection urlConnection = null; Map<String, List<String>> headers; if (!asyncRegistration.getRegistrationUri().getScheme().equalsIgnoreCase("https")) { @@ -347,9 +347,7 @@ public class AsyncTriggerFetcher { } } - if (asyncRegistration.shouldProcessRedirects()) { - FetcherUtil.parseRedirects(headers).forEach(asyncRedirect::addToRedirects); - } + asyncRedirects.configure(headers, mFlags, asyncRegistration); if (!isTriggerHeaderPresent(headers)) { asyncFetchStatus.setEntityStatus(AsyncFetchStatus.EntityStatus.HEADER_MISSING); @@ -359,7 +357,9 @@ public class AsyncTriggerFetcher { Optional<String> enrollmentId = mFlags.isDisableMeasurementEnrollmentCheck() - ? Optional.of(Enrollment.FAKE_ENROLLMENT) + ? WebAddresses.topPrivateDomainAndScheme( + asyncRegistration.getRegistrationUri()) + .map(Uri::toString) : Enrollment.getValidEnrollmentId( asyncRegistration.getRegistrationUri(), asyncRegistration.getRegistrant().getAuthority(), @@ -627,8 +627,7 @@ public class AsyncTriggerFetcher { JSONArray aggregateDeduplicationKeys) throws JSONException { JSONArray validAggregateDeduplicationKeys = new JSONArray(); if (aggregateDeduplicationKeys.length() - > FlagsFactory.getFlags() - .getMeasurementMaxAggregateDeduplicationKeysPerRegistration()) { + > mFlags.getMeasurementMaxAggregateDeduplicationKeysPerRegistration()) { LoggerFactory.getMeasurementLogger() .d( "Aggregate deduplication keys have more keys than permitted. %s", diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/registration/EnqueueAsyncRegistration.java b/adservices/service-core/java/com/android/adservices/service/measurement/registration/EnqueueAsyncRegistration.java index 0c8a801b0..f6a7bdfb5 100644 --- a/adservices/service-core/java/com/android/adservices/service/measurement/registration/EnqueueAsyncRegistration.java +++ b/adservices/service-core/java/com/android/adservices/service/measurement/registration/EnqueueAsyncRegistration.java @@ -255,6 +255,7 @@ public class EnqueueAsyncRegistration { .setPlatformAdId(platformAdIdValue) .setPostBody(postBody) .setRegistrationId(registrationId) + .setRedirectBehavior(AsyncRedirect.RedirectBehavior.AS_IS) .build(); dao.insertAsyncRegistration(asyncRegistration); diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/registration/FetcherUtil.java b/adservices/service-core/java/com/android/adservices/service/measurement/registration/FetcherUtil.java index d5d268e7c..50c64886d 100644 --- a/adservices/service-core/java/com/android/adservices/service/measurement/registration/FetcherUtil.java +++ b/adservices/service-core/java/com/android/adservices/service/measurement/registration/FetcherUtil.java @@ -48,15 +48,12 @@ import java.util.regex.Pattern; * @hide */ class FetcherUtil { - static final String REDIRECT_LIST_HEADER_KEY = "Attribution-Reporting-Redirect"; - static final String REDIRECT_LOCATION_HEADER_KEY = "Location"; static final Pattern HEX_PATTERN = Pattern.compile("\\p{XDigit}+"); /** * Determine all redirects. * - * <p>Generates a list of: (url, allows_regular_redirects) tuples. Returns true if all steps - * succeed. Returns false if there are any failures. + * <p>Generates a map of: (redirectType, List<Uri>) */ static Map<AsyncRegistration.RedirectType, List<Uri>> parseRedirects( @NonNull Map<String, List<String>> headers) { @@ -304,7 +301,7 @@ class FetcherUtil { private static List<Uri> parseListRedirects(Map<String, List<String>> headers) { List<Uri> redirects = new ArrayList<>(); - List<String> field = headers.get(REDIRECT_LIST_HEADER_KEY); + List<String> field = headers.get(AsyncRedirects.REDIRECT_LIST_HEADER_KEY); int maxRedirects = FlagsFactory.getFlags().getMeasurementMaxRegistrationRedirects(); if (field != null) { for (int i = 0; i < Math.min(field.size(), maxRedirects); i++) { @@ -316,7 +313,7 @@ class FetcherUtil { private static List<Uri> parseLocationRedirects(Map<String, List<String>> headers) { List<Uri> redirects = new ArrayList<>(); - List<String> field = headers.get(REDIRECT_LOCATION_HEADER_KEY); + List<String> field = headers.get(AsyncRedirects.REDIRECT_LOCATION_HEADER_KEY); if (field != null && !field.isEmpty()) { redirects.add(Uri.parse(field.get(0))); if (field.size() > 1) { diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/reporting/AggregateReportBody.java b/adservices/service-core/java/com/android/adservices/service/measurement/reporting/AggregateReportBody.java index a4d197107..0498d4a85 100644 --- a/adservices/service-core/java/com/android/adservices/service/measurement/reporting/AggregateReportBody.java +++ b/adservices/service-core/java/com/android/adservices/service/measurement/reporting/AggregateReportBody.java @@ -50,7 +50,8 @@ public class AggregateReportBody { private static final String API_NAME = "attribution-reporting"; - private interface PayloadBodyKeys { + @VisibleForTesting + interface PayloadBodyKeys { String SHARED_INFO = "shared_info"; String AGGREGATION_SERVICE_PAYLOADS = "aggregation_service_payloads"; String SOURCE_DEBUG_KEY = "source_debug_key"; @@ -64,7 +65,8 @@ public class AggregateReportBody { String DEBUG_CLEARTEXT_PAYLOAD = "debug_cleartext_payload"; } - private interface SharedInfoKeys { + @VisibleForTesting + interface SharedInfoKeys { String API_NAME = "api"; String ATTRIBUTION_DESTINATION = "attribution_destination"; String REPORT_ID = "report_id"; diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/reporting/AggregateReportingJobHandler.java b/adservices/service-core/java/com/android/adservices/service/measurement/reporting/AggregateReportingJobHandler.java index 4367ee1b8..849ec8164 100644 --- a/adservices/service-core/java/com/android/adservices/service/measurement/reporting/AggregateReportingJobHandler.java +++ b/adservices/service-core/java/com/android/adservices/service/measurement/reporting/AggregateReportingJobHandler.java @@ -16,7 +16,10 @@ package com.android.adservices.service.measurement.reporting; -import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ERROR_CODE_UNSPECIFIED; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__MEASUREMENT_REPORTING_ENCRYPTION_ERROR; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__MEASUREMENT_REPORTING_NETWORK_ERROR; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__MEASUREMENT_REPORTING_PARSING_ERROR; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__MEASUREMENT_REPORTING_UNKNOWN_ERROR; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__MEASUREMENT; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_MESUREMENT_REPORTS_UPLOADED; @@ -289,7 +292,7 @@ public class AggregateReportingJobHandler { // TODO(b/298330312): Change to defined error codes ErrorLogUtil.e( e, - AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ERROR_CODE_UNSPECIFIED, + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__MEASUREMENT_REPORTING_NETWORK_ERROR, AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__MEASUREMENT); return AdServicesStatusUtils.STATUS_IO_ERROR; } catch (JSONException e) { @@ -299,7 +302,7 @@ public class AggregateReportingJobHandler { // TODO(b/298330312): Change to defined error codes ErrorLogUtil.e( e, - AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ERROR_CODE_UNSPECIFIED, + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__MEASUREMENT_REPORTING_PARSING_ERROR, AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__MEASUREMENT); if (mFlags.getMeasurementEnableReportDeletionOnUnrecoverableException()) { // Unrecoverable state - delete the report. @@ -324,7 +327,7 @@ public class AggregateReportingJobHandler { // TODO(b/298330312): Change to defined error codes ErrorLogUtil.e( e, - AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ERROR_CODE_UNSPECIFIED, + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__MEASUREMENT_REPORTING_ENCRYPTION_ERROR, AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__MEASUREMENT); if (mFlags.getMeasurementEnableReportingJobsThrowCryptoException() && ThreadLocalRandom.current().nextFloat() @@ -338,7 +341,7 @@ public class AggregateReportingJobHandler { // TODO(b/298330312): Change to defined error codes ErrorLogUtil.e( e, - AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ERROR_CODE_UNSPECIFIED, + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__MEASUREMENT_REPORTING_UNKNOWN_ERROR, AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__MEASUREMENT); if (mFlags.getMeasurementEnableReportingJobsThrowUnaccountedException() && ThreadLocalRandom.current().nextFloat() @@ -371,8 +374,7 @@ public class AggregateReportingJobHandler { .setTriggerDebugKey(aggregateReport.getTriggerDebugKey()) .setAggregationCoordinatorOrigin(aggregateReport.getAggregationCoordinatorOrigin()) .setDebugMode( - mIsDebugInstance - && aggregateReport.getSourceDebugKey() != null + aggregateReport.getSourceDebugKey() != null && aggregateReport.getTriggerDebugKey() != null ? "enabled" : null) diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/reporting/DebugReportApi.java b/adservices/service-core/java/com/android/adservices/service/measurement/reporting/DebugReportApi.java index c4a6d7201..778570c24 100644 --- a/adservices/service-core/java/com/android/adservices/service/measurement/reporting/DebugReportApi.java +++ b/adservices/service-core/java/com/android/adservices/service/measurement/reporting/DebugReportApi.java @@ -80,6 +80,7 @@ public class DebugReportApi { String TRIGGER_UNKNOWN_ERROR = "trigger-unknown-error"; String TRIGGER_AGGREGATE_STORAGE_LIMIT = "trigger-aggregate-storage-limit"; String TRIGGER_AGGREGATE_EXCESSIVE_REPORTS = "trigger-aggregate-excessive-reports"; + String TRIGGER_EVENT_REPORT_WINDOW_NOT_STARTED = "trigger-event-report-window-not-started"; } /** Defines different verbose debug report body parameters. */ diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/reporting/DebugReportingJobHandler.java b/adservices/service-core/java/com/android/adservices/service/measurement/reporting/DebugReportingJobHandler.java index 0650f67e9..4391b3725 100644 --- a/adservices/service-core/java/com/android/adservices/service/measurement/reporting/DebugReportingJobHandler.java +++ b/adservices/service-core/java/com/android/adservices/service/measurement/reporting/DebugReportingJobHandler.java @@ -16,7 +16,9 @@ package com.android.adservices.service.measurement.reporting; -import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ERROR_CODE_UNSPECIFIED; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__MEASUREMENT_REPORTING_NETWORK_ERROR; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__MEASUREMENT_REPORTING_PARSING_ERROR; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__MEASUREMENT_REPORTING_UNKNOWN_ERROR; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__MEASUREMENT; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_MESUREMENT_REPORTS_UPLOADED; @@ -184,7 +186,7 @@ public class DebugReportingJobHandler { .d(e, "Network error occurred when attempting to deliver debug report."); ErrorLogUtil.e( e, - AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ERROR_CODE_UNSPECIFIED, + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__MEASUREMENT_REPORTING_NETWORK_ERROR, AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__MEASUREMENT); reportingStatus.setFailureStatus(ReportingStatus.FailureStatus.NETWORK); // TODO(b/298330312): Change to defined error codes @@ -195,7 +197,7 @@ public class DebugReportingJobHandler { // TODO(b/298330312): Change to defined error codes ErrorLogUtil.e( e, - AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ERROR_CODE_UNSPECIFIED, + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__MEASUREMENT_REPORTING_PARSING_ERROR, AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__MEASUREMENT); reportingStatus.setFailureStatus(ReportingStatus.FailureStatus.SERIALIZATION_ERROR); if (mFlags.getMeasurementEnableReportDeletionOnUnrecoverableException()) { @@ -216,7 +218,7 @@ public class DebugReportingJobHandler { // TODO(b/298330312): Change to defined error codes ErrorLogUtil.e( e, - AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ERROR_CODE_UNSPECIFIED, + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__MEASUREMENT_REPORTING_UNKNOWN_ERROR, AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__MEASUREMENT); reportingStatus.setFailureStatus(ReportingStatus.FailureStatus.UNKNOWN); if (mFlags.getMeasurementEnableReportingJobsThrowUnaccountedException() diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/reporting/EventReportWindowCalcDelegate.java b/adservices/service-core/java/com/android/adservices/service/measurement/reporting/EventReportWindowCalcDelegate.java index 9d7564c43..93f791385 100644 --- a/adservices/service-core/java/com/android/adservices/service/measurement/reporting/EventReportWindowCalcDelegate.java +++ b/adservices/service-core/java/com/android/adservices/service/measurement/reporting/EventReportWindowCalcDelegate.java @@ -54,36 +54,42 @@ public class EventReportWindowCalcDelegate { } /** - * Max reports count based on conversion destination type and installation state. + * Max reports count given the Source object. * - * @param isInstallCase is app installed + * @param source the Source object * @return maximum number of reports allowed */ - public int getMaxReportCount(@NonNull Source source, boolean isInstallCase) { + public int getMaxReportCount(Source source) { + return getMaxReportCount(source, source.isInstallDetectionEnabled()); + } + + /** + * Max reports count based on conversion destination type. + * + * @param source the Source object + * @param destinationType destination type + * @return maximum number of reports allowed + */ + public int getMaxReportCount(@NonNull Source source, @EventSurfaceType int destinationType) { + return getMaxReportCount(source, isInstallCase(source, destinationType)); + } + + private int getMaxReportCount(@NonNull Source source, boolean isInstallCase) { // TODO(b/290101531): Cleanup flags if (mFlags.getMeasurementFlexLiteApiEnabled() && source.getMaxEventLevelReports() != null) { return source.getMaxEventLevelReports(); } - if (source.getSourceType() == Source.SourceType.EVENT - && mFlags.getMeasurementEnableVtcConfigurableMaxEventReports()) { - // Max VTC event reports are configurable - int configuredMaxReports = mFlags.getMeasurementVtcConfigurableMaxEventReportsCount(); + + if (source.getSourceType() == Source.SourceType.EVENT) { // Additional report essentially for first open + 1 post install conversion. If there // is already more than 1 report allowed, no need to have that additional report. - if (isInstallCase && configuredMaxReports == PrivacyParams.EVENT_SOURCE_MAX_REPORTS) { + if (isInstallCase && !source.hasWebDestinations() && isDefaultConfiguredVtc()) { return PrivacyParams.INSTALL_ATTR_EVENT_SOURCE_MAX_REPORTS; } - return configuredMaxReports; + return mFlags.getMeasurementVtcConfigurableMaxEventReportsCount(); } - if (isInstallCase) { - return source.getSourceType() == Source.SourceType.EVENT - ? PrivacyParams.INSTALL_ATTR_EVENT_SOURCE_MAX_REPORTS - : PrivacyParams.INSTALL_ATTR_NAVIGATION_SOURCE_MAX_REPORTS; - } - return source.getSourceType() == Source.SourceType.EVENT - ? PrivacyParams.EVENT_SOURCE_MAX_REPORTS - : PrivacyParams.NAVIGATION_SOURCE_MAX_REPORTS; + return PrivacyParams.NAVIGATION_SOURCE_MAX_REPORTS; } /** @@ -100,18 +106,14 @@ public class EventReportWindowCalcDelegate { // Cases where source could have both web and app destinations, there if the trigger // destination is an app, and it was installed, then installState should be considered true. - boolean isAppInstalled = isAppInstalled(source, destinationType); - List<Pair<Long, Long>> earlyReportingWindows = - getEarlyReportingWindows(source, isAppInstalled); - for (Pair<Long, Long> window : earlyReportingWindows) { + List<Pair<Long, Long>> reportingWindows = + getEffectiveReportingWindows(source, isInstallCase(source, destinationType)); + for (Pair<Long, Long> window : reportingWindows) { if (isWithinWindow(triggerTime, window)) { return window.second + mFlags.getMeasurementMinEventReportDelayMillis(); } } - Pair<Long, Long> finalWindow = getFinalReportingWindow(source, earlyReportingWindows); - if (isWithinWindow(triggerTime, finalWindow)) { - return finalWindow.second + mFlags.getMeasurementMinEventReportDelayMillis(); - } + return -1; } @@ -120,45 +122,60 @@ public class EventReportWindowCalcDelegate { } /** + * Enum shows trigger time and source window time relationship. It is used to generate different + * verbose debug reports. + */ + public enum MomentPlacement { + BEFORE, + AFTER, + WITHIN; + } + + /** + * @param source source for which the window is calculated + * @param triggerTime time for the trigger + * @param destinationType trigger destination type + * @return how trigger time falls in source windows + */ + public MomentPlacement fallsWithinWindow( + @NonNull Source source, long triggerTime, @EventSurfaceType int destinationType) { + List<Pair<Long, Long>> reportingWindows = + getEffectiveReportingWindows(source, isInstallCase(source, destinationType)); + if (triggerTime < reportingWindows.get(0).first) { + return MomentPlacement.BEFORE; + } + if (triggerTime >= reportingWindows.get(reportingWindows.size() - 1).second) { + return MomentPlacement.AFTER; + } + return MomentPlacement.WITHIN; + } + + /** * Return reporting time by index for noising based on the index * * @param windowIndex index of the reporting window for which * @return reporting time in milliseconds */ public long getReportingTimeForNoising( - @NonNull Source source, int windowIndex, boolean isInstallCase) { - List<Pair<Long, Long>> earlyWindows = getEarlyReportingWindows(source, isInstallCase); - Pair<Long, Long> finalWindow = getFinalReportingWindow(source, earlyWindows); - return windowIndex < earlyWindows.size() - ? earlyWindows.get(windowIndex).second + @NonNull Source source, int windowIndex) { + List<Pair<Long, Long>> reportingWindows = getEffectiveReportingWindows( + source, source.isInstallDetectionEnabled()); + Pair<Long, Long> finalWindow = reportingWindows.get(reportingWindows.size() - 1); + // TODO: (b/288646239) remove this check, confirming noising indexing accuracy. + return windowIndex < reportingWindows.size() + ? reportingWindows.get(windowIndex).second + mFlags.getMeasurementMinEventReportDelayMillis() : finalWindow.second + mFlags.getMeasurementMinEventReportDelayMillis(); } - private Pair<Long, Long> getFinalReportingWindow( - Source source, List<Pair<Long, Long>> earlyWindows) { - if (mFlags.getMeasurementFlexLiteApiEnabled() && source.hasManualEventReportWindows()) { - List<Pair<Long, Long>> windowList = source.parsedProcessedEventReportWindows(); - return windowList.get(windowList.size() - 1); - } - long secondToLastWindowEnd = - !earlyWindows.isEmpty() ? earlyWindows.get(earlyWindows.size() - 1).second : 0; - if (source.getProcessedEventReportWindow() != null) { - return new Pair<>(secondToLastWindowEnd, source.getProcessedEventReportWindow()); - } - return new Pair<>(secondToLastWindowEnd, source.getExpiryTime()); - } - /** - * Returns effective, i.e. the ones that occur before {@link - * Source#getProcessedEventReportWindow()}, event reporting windows count for noising cases. + * Returns effective, that is, the ones that occur before {@link + * Source#getEffectiveEventReportWindow()}, event reporting windows count for noising cases. * * @param source source for which the count is requested - * @param isInstallCase true of cool down window was specified */ - public int getReportingWindowCountForNoising(@NonNull Source source, boolean isInstallCase) { - // Early Count + lastWindow - return getEarlyReportingWindows(source, isInstallCase).size() + 1; + public int getReportingWindowCountForNoising(@NonNull Source source) { + return getEffectiveReportingWindows(source, source.isInstallDetectionEnabled()).size(); } /** @@ -213,47 +230,53 @@ public class EventReportWindowCalcDelegate { return -1L; } - private boolean isAppInstalled(Source source, int destinationType) { + private static boolean isInstallCase(Source source, @EventSurfaceType int destinationType) { return destinationType == EventSurfaceType.APP && source.isInstallAttributed(); } /** * If the flag is enabled and the specified report windows are valid, picks from flag controlled - * configurable early reporting windows. Otherwise, falls back to the statical {@link - * com.android.adservices.service.measurement.PrivacyParams} values. It curtails the windows - * that occur after {@link Source#getProcessedEventReportWindow()} because they would - * effectively be unusable. + * configurable early reporting windows. Otherwise, falls back to the values provided in + * {@code getDefaultEarlyReportingWindowEnds}, which can have install-related custom behaviour. + * It curtails the windows that occur after {@link Source#getEffectiveEventReportWindow()} + * because they would effectively be unusable. */ - private List<Pair<Long, Long>> getEarlyReportingWindows(Source source, boolean installState) { + private List<Pair<Long, Long>> getEffectiveReportingWindows(Source source, + boolean installState) { // TODO(b/290221611) Remove early reporting windows from code, only use them for flags. if (mFlags.getMeasurementFlexLiteApiEnabled() && source.hasManualEventReportWindows()) { - List<Pair<Long, Long>> windows = source.parsedProcessedEventReportWindows(); - // Select early windows only i.e. skip the last element - return windows.subList(0, windows.size() - 1); + return source.parsedProcessedEventReportWindows(); } - List<Long> earlyWindows; - List<Long> defaultEarlyWindows = - getDefaultEarlyReportingWindows(source.getSourceType(), installState); - earlyWindows = - getConfiguredOrDefaultEarlyReportingWindows( - source.getSourceType(), defaultEarlyWindows, true); + List<Long> defaultEarlyWindowEnds = + getDefaultEarlyReportingWindowEnds( + source.getSourceType(), + installState && !source.hasWebDestinations()); + List<Long> earlyWindowEnds = + getConfiguredOrDefaultEarlyReportingWindowEnds( + source.getSourceType(), defaultEarlyWindowEnds); // Add source event time to windows - earlyWindows = - earlyWindows.stream() + earlyWindowEnds = + earlyWindowEnds.stream() .map((x) -> source.getEventTime() + x) .collect(Collectors.toList()); List<Pair<Long, Long>> windowList = new ArrayList<>(); - long windowStart = 0; + long windowStart = 0L; Pair<Long, Long> finalWindow = - getFinalReportingWindow(source, createStartEndWindow(earlyWindows)); - for (long windowEnd : earlyWindows) { - if (finalWindow.second <= windowEnd) { - continue; + getFinalReportingWindow(source, earlyWindowEnds); + + for (long windowEnd : earlyWindowEnds) { + // Start time of `finalWindow` is either 0 or one of `earlyWindowEnds` times; stop + // iterating if we see it, and add `finalWindow`. + if (windowStart == finalWindow.first) { + break; } - windowList.add(new Pair<>(windowStart, windowEnd)); + windowList.add(Pair.create(windowStart, windowEnd)); windowStart = windowEnd; } + + windowList.add(finalWindow); + return ImmutableList.copyOf(windowList); } @@ -261,13 +284,13 @@ public class EventReportWindowCalcDelegate { * Returns the default early reporting windows * * @param sourceType Source's Type - * @param installState Install State of the source + * @param installAttributionEnabled whether windows for install attribution should be provided * @return a list of windows */ - public static List<Long> getDefaultEarlyReportingWindows( - Source.SourceType sourceType, boolean installState) { + public static List<Long> getDefaultEarlyReportingWindowEnds( + Source.SourceType sourceType, boolean installAttributionEnabled) { long[] earlyWindows; - if (installState) { + if (installAttributionEnabled) { earlyWindows = sourceType == Source.SourceType.EVENT ? INSTALL_ATTR_EVENT_EARLY_REPORTING_WINDOW_MILLISECONDS @@ -281,53 +304,34 @@ public class EventReportWindowCalcDelegate { return asList(earlyWindows); } - private List<Pair<Long, Long>> createStartEndWindow(List<Long> windowEnds) { - List<Pair<Long, Long>> windows = new ArrayList<>(); - long start = 0; - for (Long end : windowEnds) { - windows.add(new Pair<>(start, end)); - start = end; - } - return windows; - } - /** * Returns default or configured (via flag) early reporting windows for the SourceType * * @param sourceType Source's Type * @param defaultEarlyWindows default value for early windows - * @param checkEnableFlag set true if configurable window flag should be checked * @return list of windows */ - public List<Long> getConfiguredOrDefaultEarlyReportingWindows( - Source.SourceType sourceType, List<Long> defaultEarlyWindows, boolean checkEnableFlag) { - // TODO(b/290101531): Cleanup flags - if (checkEnableFlag && !mFlags.getMeasurementEnableConfigurableEventReportingWindows()) { - return defaultEarlyWindows; + public List<Long> getConfiguredOrDefaultEarlyReportingWindowEnds( + Source.SourceType sourceType, List<Long> defaultEarlyWindowEnds) { + // `defaultEarlyWindowEnds` may contain custom install-related logic, which we only apply if + // the configurable report windows (and max reports) are in their default state. Without + // this check, we may construct default-value report windows without the custom + // install-related logic applied. + if ((sourceType == Source.SourceType.EVENT && isDefaultConfiguredVtc()) + || (sourceType == Source.SourceType.NAVIGATION && isDefaultConfiguredCtc())) { + return defaultEarlyWindowEnds; } String earlyReportingWindowsString = pickEarlyReportingWindowsConfig(mFlags, sourceType); - if (earlyReportingWindowsString == null) { - LoggerFactory.getMeasurementLogger() - .d("Invalid configurable early reporting windows; null"); - return defaultEarlyWindows; - } - if (earlyReportingWindowsString.isEmpty()) { // No early reporting windows specified. It needs to be handled separately because - // splitting an empty string results into an array containing a single element, - // i.e. "". We want to handle it as an array having no element. - - if (Source.SourceType.EVENT.equals(sourceType)) { - // We need to add a reporting window at 2d for post-install case. Non-install case - // has no early reporting window by default. - return defaultEarlyWindows; - } + // splitting an empty string results in an array containing a single empty string. We + // want to handle it as an empty array. return Collections.emptyList(); } - ImmutableList.Builder<Long> earlyWindows = new ImmutableList.Builder<>(); + ImmutableList.Builder<Long> earlyWindowEnds = new ImmutableList.Builder<>(); String[] split = earlyReportingWindowsString.split(EARLY_REPORTING_WINDOWS_CONFIG_DELIMITER); if (split.length > MAX_CONFIGURABLE_EVENT_REPORT_EARLY_REPORTING_WINDOWS) { @@ -335,22 +339,54 @@ public class EventReportWindowCalcDelegate { .d( "Invalid configurable early reporting window; more than allowed size: " + MAX_CONFIGURABLE_EVENT_REPORT_EARLY_REPORTING_WINDOWS); - return defaultEarlyWindows; + return defaultEarlyWindowEnds; } - for (String window : split) { + for (String windowEnd : split) { try { - earlyWindows.add(TimeUnit.SECONDS.toMillis(Long.parseLong(window))); + earlyWindowEnds.add(TimeUnit.SECONDS.toMillis(Long.parseLong(windowEnd))); } catch (NumberFormatException e) { LoggerFactory.getMeasurementLogger() .d(e, "Configurable early reporting window parsing failed."); - return defaultEarlyWindows; + return defaultEarlyWindowEnds; + } + } + return earlyWindowEnds.build(); + } + + private Pair<Long, Long> getFinalReportingWindow( + Source source, List<Long> earlyWindowEnds) { + // The latest end-time we can associate with a report for this source + long effectiveExpiry = Math.min( + source.getEffectiveEventReportWindow(), source.getExpiryTime()); + // Find the latest end-time that can start a window ending at effectiveExpiry + for (int i = earlyWindowEnds.size() - 1; i >= 0; i--) { + long windowEnd = earlyWindowEnds.get(i); + if (windowEnd < effectiveExpiry) { + return Pair.create(windowEnd, effectiveExpiry); } } - return earlyWindows.build(); + return Pair.create(0L, effectiveExpiry); + } + + /** Indicates whether VTC report windows and max reports are default configured, which can + * affect custom install-related attribution. + */ + public boolean isDefaultConfiguredVtc() { + return mFlags.getMeasurementEventReportsVtcEarlyReportingWindows().isEmpty() + && mFlags.getMeasurementVtcConfigurableMaxEventReportsCount() == 1; + } + + /** Indicates whether CTC report windows are default configured, which can affect custom + * install-related attribution. + */ + private boolean isDefaultConfiguredCtc() { + return mFlags.getMeasurementEventReportsCtcEarlyReportingWindows().equals( + Flags.MEASUREMENT_EVENT_REPORTS_CTC_EARLY_REPORTING_WINDOWS); } - private String pickEarlyReportingWindowsConfig(Flags flags, Source.SourceType sourceType) { + private static String pickEarlyReportingWindowsConfig(Flags flags, + Source.SourceType sourceType) { return sourceType == Source.SourceType.EVENT ? flags.getMeasurementEventReportsVtcEarlyReportingWindows() : flags.getMeasurementEventReportsCtcEarlyReportingWindows(); diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/reporting/EventReportingJobHandler.java b/adservices/service-core/java/com/android/adservices/service/measurement/reporting/EventReportingJobHandler.java index f5042f3af..c67f0acd6 100644 --- a/adservices/service-core/java/com/android/adservices/service/measurement/reporting/EventReportingJobHandler.java +++ b/adservices/service-core/java/com/android/adservices/service/measurement/reporting/EventReportingJobHandler.java @@ -16,7 +16,9 @@ package com.android.adservices.service.measurement.reporting; -import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ERROR_CODE_UNSPECIFIED; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__MEASUREMENT_REPORTING_NETWORK_ERROR; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__MEASUREMENT_REPORTING_PARSING_ERROR; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__MEASUREMENT_REPORTING_UNKNOWN_ERROR; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__MEASUREMENT; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_MESUREMENT_REPORTS_UPLOADED; @@ -270,7 +272,7 @@ public class EventReportingJobHandler { // TODO(b/298330312): Change to defined error codes ErrorLogUtil.e( e, - AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ERROR_CODE_UNSPECIFIED, + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__MEASUREMENT_REPORTING_NETWORK_ERROR, AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__MEASUREMENT); return AdServicesStatusUtils.STATUS_IO_ERROR; } catch (JSONException e) { @@ -281,7 +283,7 @@ public class EventReportingJobHandler { // TODO(b/298330312): Change to defined error codes ErrorLogUtil.e( e, - AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ERROR_CODE_UNSPECIFIED, + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__MEASUREMENT_REPORTING_PARSING_ERROR, AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__MEASUREMENT); if (mFlags.getMeasurementEnableReportDeletionOnUnrecoverableException()) { // Unrecoverable state - delete the report. @@ -305,7 +307,7 @@ public class EventReportingJobHandler { reportingStatus.setFailureStatus(ReportingStatus.FailureStatus.UNKNOWN); ErrorLogUtil.e( e, - AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ERROR_CODE_UNSPECIFIED, + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__MEASUREMENT_REPORTING_UNKNOWN_ERROR, AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__MEASUREMENT); if (mFlags.getMeasurementEnableReportingJobsThrowUnaccountedException() && ThreadLocalRandom.current().nextFloat() diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/reporting/ReportingStatus.java b/adservices/service-core/java/com/android/adservices/service/measurement/reporting/ReportingStatus.java index 8e7b1780f..4e83edb8c 100644 --- a/adservices/service-core/java/com/android/adservices/service/measurement/reporting/ReportingStatus.java +++ b/adservices/service-core/java/com/android/adservices/service/measurement/reporting/ReportingStatus.java @@ -52,7 +52,9 @@ public class ReportingStatus { VERBOSE_DEBUG_TRIGGER_UNKNOWN_ERROR(26), VERBOSE_DEBUG_TRIGGER_AGGREGATE_STORAGE_LIMIT(27), VERBOSE_DEBUG_TRIGGER_AGGREGATE_EXCESSIVE_REPORTS(28), - VERBOSE_DEBUG_UNKNOWN(29); + VERBOSE_DEBUG_TRIGGER_EVENT_REPORT_WINDOW_NOT_STARTED(29), + VERBOSE_DEBUG_UNKNOWN(9999); + private final int mValue; ReportType(int value) { @@ -202,6 +204,10 @@ public class ReportingStatus { mReportType = ReportType.VERBOSE_DEBUG_TRIGGER_AGGREGATE_STORAGE_LIMIT; } else if (reportType.equals(DebugReportApi.Type.TRIGGER_AGGREGATE_EXCESSIVE_REPORTS)) { mReportType = ReportType.VERBOSE_DEBUG_TRIGGER_AGGREGATE_EXCESSIVE_REPORTS; + } else if (reportType.equals(DebugReportApi.Type.TRIGGER_EVENT_REPORT_WINDOW_NOT_STARTED)) { + mReportType = ReportType.VERBOSE_DEBUG_TRIGGER_EVENT_REPORT_WINDOW_NOT_STARTED; + } else { + mReportType = ReportType.VERBOSE_DEBUG_UNKNOWN; } } diff --git a/adservices/service-core/java/com/android/adservices/service/measurement/util/Enrollment.java b/adservices/service-core/java/com/android/adservices/service/measurement/util/Enrollment.java index 7f71d2eb0..f98f138fb 100644 --- a/adservices/service-core/java/com/android/adservices/service/measurement/util/Enrollment.java +++ b/adservices/service-core/java/com/android/adservices/service/measurement/util/Enrollment.java @@ -30,7 +30,6 @@ import java.util.Optional; /** Enrollment utilities for measurement. */ public final class Enrollment { - public static final String FAKE_ENROLLMENT = "fake_enrollment"; public static final String LOCALHOST_ENROLLMENT_ID = "localhost_enrollment_id"; public static final String LOCALHOST_IP_ENROLLMENT_ID = "localhost_ip_enrollment_id"; diff --git a/adservices/service-core/java/com/android/adservices/service/shell/AdServicesShellCommandHandler.java b/adservices/service-core/java/com/android/adservices/service/shell/AdServicesShellCommandHandler.java index ab2227b46..d93eba2db 100644 --- a/adservices/service-core/java/com/android/adservices/service/shell/AdServicesShellCommandHandler.java +++ b/adservices/service-core/java/com/android/adservices/service/shell/AdServicesShellCommandHandler.java @@ -21,6 +21,7 @@ import android.util.Log; import com.android.adservices.service.common.AppManifestConfigHelper; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; import java.io.PrintWriter; import java.util.Arrays; @@ -45,24 +46,30 @@ public final class AdServicesShellCommandHandler { @VisibleForTesting static final String CMD_IS_ALLOWED_ATTRIBUTION_ACCESS = "is-allowed-attribution-access"; + @VisibleForTesting static final String CMD_IS_ALLOWED_CUSTOM_AUDIENCES_ACCESS = "is-allowed-custom-audiences-access"; + @VisibleForTesting static final String CMD_IS_ALLOWED_TOPICS_ACCESS = "is-allowed-topics-access"; + @VisibleForTesting static final String HELP_ECHO = CMD_ECHO + " <message> - prints the given message (useful to check cmd is working)."; + @VisibleForTesting static final String HELP_IS_ALLOWED_ATTRIBUTION_ACCESS = CMD_IS_ALLOWED_ATTRIBUTION_ACCESS + " <package_name> <enrollment_id> - checks if the given enrollment id is" + " allowed to use the Attribution APIs in the given app."; + @VisibleForTesting static final String HELP_IS_ALLOWED_CUSTOM_AUDIENCES_ACCESS = CMD_IS_ALLOWED_CUSTOM_AUDIENCES_ACCESS + " <package_name> <enrollment_id> - checks if the given enrollment id is" + " allowed to use the Custom Audience APIs in the given app."; + @VisibleForTesting static final String HELP_IS_ALLOWED_TOPICS_ACCESS = CMD_IS_ALLOWED_TOPICS_ACCESS @@ -74,6 +81,7 @@ public final class AdServicesShellCommandHandler { @VisibleForTesting static final String ERROR_TEMPLATE_INVALID_ARGS = "Invalid cmd (%s). Syntax: %s"; + // TODO(b/280460130): use adservice helpers for tag name / logging methods private static final String TAG = "AdServicesShellCmd"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -81,23 +89,27 @@ public final class AdServicesShellCommandHandler { private static final int RESULT_GENERIC_ERROR = -1; private final PrintWriter mOut; + private final PrintWriter mErr; private String[] mArgs; private int mArgPos; private String mCurArgData; + /** If PrintWriter {@code err} is not provided, we use {@code out} for the {@code err}. */ public AdServicesShellCommandHandler(PrintWriter out) { + this(out, /* err= */ out); + } + + public AdServicesShellCommandHandler(PrintWriter out, PrintWriter err) { mOut = Objects.requireNonNull(out, "out cannot be null"); + mErr = Objects.requireNonNull(err, "err cannot be null"); } /** Runs the given command ({@code args[0]}) and optional arguments */ public int run(String... args) { Objects.requireNonNull(args, "args cannot be null"); - // TODO(b/303886367): use Preconditions - if (args.length < 1) { - throw new IllegalArgumentException( - "must have at least one argument (the command itself)"); - } + Preconditions.checkArgument( + args.length >= 1, "must have at least one argument (the command itself)"); if (DEBUG) { Log.d(TAG, "run(): " + Arrays.toString(args)); } @@ -112,13 +124,14 @@ public final class AdServicesShellCommandHandler { } } catch (Throwable e) { // TODO(b/308009734): need to test this - mOut.printf("Exception occurred while executing %s\n", Arrays.toString(mArgs)); + mErr.printf("Exception occurred while executing %s\n", Arrays.toString(mArgs)); e.printStackTrace(mOut); } finally { if (DEBUG) { Log.d(TAG, "Flushing output"); } mOut.flush(); + mErr.flush(); } if (DEBUG) { Log.d(TAG, "Sending command result: " + res); @@ -168,7 +181,7 @@ public final class AdServicesShellCommandHandler { } private int invalidArgsError(String syntax) { - mOut.println(String.format(ERROR_TEMPLATE_INVALID_ARGS, Arrays.toString(mArgs), syntax)); + mErr.println(String.format(ERROR_TEMPLATE_INVALID_ARGS, Arrays.toString(mArgs), syntax)); return RESULT_GENERIC_ERROR; } @@ -188,9 +201,9 @@ public final class AdServicesShellCommandHandler { case CMD_SHORT_HELP: case CMD_HELP: onHelp(); - return RESULT_GENERIC_ERROR; + return RESULT_OK; case "": - mOut.println(ERROR_EMPTY_COMMAND); + mErr.println(ERROR_EMPTY_COMMAND); return RESULT_GENERIC_ERROR; case CMD_ECHO: return runEcho(); @@ -199,7 +212,7 @@ public final class AdServicesShellCommandHandler { case CMD_IS_ALLOWED_TOPICS_ACCESS: return runIsAllowedApiAccess(cmd); default: - mOut.printf("Unknown command: %s\n", cmd); + mErr.printf("Unknown command: %s\n", cmd); return RESULT_GENERIC_ERROR; } } diff --git a/adservices/service-core/java/com/android/adservices/service/shell/ShellCommandServiceImpl.java b/adservices/service-core/java/com/android/adservices/service/shell/ShellCommandServiceImpl.java new file mode 100644 index 000000000..285396c15 --- /dev/null +++ b/adservices/service-core/java/com/android/adservices/service/shell/ShellCommandServiceImpl.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2023 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.shell; + +import android.adservices.shell.IShellCommand; +import android.adservices.shell.IShellCommandCallback; +import android.adservices.shell.ShellCommandParam; +import android.adservices.shell.ShellCommandResult; +import android.os.RemoteException; + +import com.android.adservices.LogUtil; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * Implements a service which runs shell command in the AdServices process. + * + * <p>This internally calls {@link AdServicesShellCommandHandler} which has main logic to execute + * the shell command. + * + * @hide + */ +public final class ShellCommandServiceImpl extends IShellCommand.Stub { + @Override + public void runShellCommand(ShellCommandParam param, IShellCommandCallback callback) { + StringWriter outStringWriter = new StringWriter(); + StringWriter ErrStringWriter = new StringWriter(); + + try (PrintWriter outPw = new PrintWriter(outStringWriter); + PrintWriter errPw = new PrintWriter(ErrStringWriter); ) { + AdServicesShellCommandHandler handler = new AdServicesShellCommandHandler(outPw, errPw); + int resultCode = handler.run(param.getCommandArgs()); + ShellCommandResult response = + new ShellCommandResult.Builder() + .setResultCode(resultCode) + .setOut(outStringWriter.toString()) + .setErr(ErrStringWriter.toString()) + .build(); + callback.onResult(response); + } catch (RemoteException e) { + LogUtil.e(e, "Unable to send result to the callback for request: %s", param); + } + } +} diff --git a/adservices/service-core/java/com/android/adservices/service/signals/PeriodicEncodingJobWorker.java b/adservices/service-core/java/com/android/adservices/service/signals/PeriodicEncodingJobWorker.java index 08a6c6525..b792dc773 100644 --- a/adservices/service-core/java/com/android/adservices/service/signals/PeriodicEncodingJobWorker.java +++ b/adservices/service-core/java/com/android/adservices/service/signals/PeriodicEncodingJobWorker.java @@ -24,10 +24,11 @@ import com.android.adservices.LoggerFactory; import com.android.adservices.concurrency.AdServicesExecutors; import com.android.adservices.data.signals.DBEncodedPayload; import com.android.adservices.data.signals.DBEncoderLogicMetadata; +import com.android.adservices.data.signals.DBSignalsUpdateMetadata; import com.android.adservices.data.signals.EncodedPayloadDao; import com.android.adservices.data.signals.EncoderLogicHandler; import com.android.adservices.data.signals.EncoderLogicMetadataDao; -import com.android.adservices.data.signals.EncoderPersistenceDao; +import com.android.adservices.data.signals.ProtectedSignalsDao; import com.android.adservices.data.signals.ProtectedSignalsDatabase; import com.android.adservices.service.Flags; import com.android.adservices.service.FlagsFactory; @@ -75,9 +76,9 @@ public class PeriodicEncodingJobWorker { private final EncoderLogicHandler mEncoderLogicHandler; private final EncoderLogicMetadataDao mEncoderLogicMetadataDao; - private final EncoderPersistenceDao mEncoderPersistenceDao; private final EncodedPayloadDao mEncodedPayloadDao; private final SignalsProvider mSignalsProvider; + private final ProtectedSignalsDao mProtectedSignalsDao; private final AdSelectionScriptEngine mScriptEngine; private final ListeningExecutorService mBackgroundExecutor; private final ListeningExecutorService mLightWeightExecutor; @@ -92,9 +93,9 @@ public class PeriodicEncodingJobWorker { protected PeriodicEncodingJobWorker( @NonNull EncoderLogicHandler encoderLogicHandler, @NonNull EncoderLogicMetadataDao encoderLogicMetadataDao, - @NonNull EncoderPersistenceDao encoderPersistenceDao, @NonNull EncodedPayloadDao encodedPayloadDao, @NonNull SignalsProviderImpl signalStorageManager, + @NonNull ProtectedSignalsDao protectedSignalsDao, @NonNull AdSelectionScriptEngine scriptEngine, @NonNull ListeningExecutorService backgroundExecutor, @NonNull ListeningExecutorService lightWeightExecutor, @@ -102,9 +103,9 @@ public class PeriodicEncodingJobWorker { @NonNull Flags flags) { mEncoderLogicHandler = encoderLogicHandler; mEncoderLogicMetadataDao = encoderLogicMetadataDao; - mEncoderPersistenceDao = encoderPersistenceDao; mEncodedPayloadDao = encodedPayloadDao; mSignalsProvider = signalStorageManager; + mProtectedSignalsDao = protectedSignalsDao; mScriptEngine = scriptEngine; mBackgroundExecutor = backgroundExecutor; mLightWeightExecutor = lightWeightExecutor; @@ -135,9 +136,9 @@ public class PeriodicEncodingJobWorker { new PeriodicEncodingJobWorker( new EncoderLogicHandler(context), signalsDatabase.getEncoderLogicMetadataDao(), - EncoderPersistenceDao.getInstance(context), signalsDatabase.getEncodedPayloadDao(), new SignalsProviderImpl(signalsDatabase.protectedSignalsDao()), + signalsDatabase.protectedSignalsDao(), new AdSelectionScriptEngine( context, () -> @@ -242,12 +243,30 @@ public class PeriodicEncodingJobWorker { FluentFuture<Void> runEncodingPerBuyer( DBEncoderLogicMetadata encoderLogicMetadata, int timeout) { AdTechIdentifier buyer = encoderLogicMetadata.getBuyer(); + Map<String, List<ProtectedSignal>> signals = mSignalsProvider.getSignals(buyer); if (signals.isEmpty()) { mEncoderLogicHandler.deleteEncoderForBuyer(buyer); return FluentFuture.from(Futures.immediateFuture(null)); } + DBSignalsUpdateMetadata signalsUpdateMetadata = + mProtectedSignalsDao.getSignalsUpdateMetadata(buyer); + DBEncodedPayload existingPayload = mEncodedPayloadDao.getEncodedPayload(buyer); + if (signalsUpdateMetadata != null && existingPayload != null) { + boolean isNoNewSignalUpdateAfterLastEncoding = + signalsUpdateMetadata + .getLastSignalsUpdatedTime() + .isBefore(existingPayload.getCreationTime()); + boolean isEncoderLogicNotUpdatedAfterLastEncoding = + encoderLogicMetadata + .getCreationTime() + .isBefore(existingPayload.getCreationTime()); + if (isNoNewSignalUpdateAfterLastEncoding && isEncoderLogicNotUpdatedAfterLastEncoding) { + return FluentFuture.from(Futures.immediateFuture(null)); + } + } + int failedCount = encoderLogicMetadata.getFailedEncodingCount(); if (failedCount >= mEncoderLogicMaximumFailure) { return FluentFuture.from(Futures.immediateFuture(null)); diff --git a/adservices/service-core/java/com/android/adservices/service/signals/ProtectedSignalsServiceImpl.java b/adservices/service-core/java/com/android/adservices/service/signals/ProtectedSignalsServiceImpl.java index 902ac7411..9cac9eef5 100644 --- a/adservices/service-core/java/com/android/adservices/service/signals/ProtectedSignalsServiceImpl.java +++ b/adservices/service-core/java/com/android/adservices/service/signals/ProtectedSignalsServiceImpl.java @@ -58,6 +58,7 @@ import com.android.adservices.service.stats.AdServicesLogger; import com.android.adservices.service.stats.AdServicesLoggerImpl; import com.android.internal.annotations.VisibleForTesting; +import java.time.Clock; import java.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; @@ -104,7 +105,8 @@ public class ProtectedSignalsServiceImpl extends IProtectedSignalsService.Stub { new UpdateProcessorSelector(), new UpdateEncoderEventHandler(context), new SignalEvictionController()), - new AdTechUriValidator(ADTECH_CALLER_NAME, "", CLASS_NAME, FIELD_NAME)), + new AdTechUriValidator(ADTECH_CALLER_NAME, "", CLASS_NAME, FIELD_NAME), + Clock.systemUTC()), FledgeAuthorizationFilter.create(context, AdServicesLoggerImpl.getInstance()), ConsentManager.getInstance(context), DevContextFilter.create(context), @@ -187,7 +189,7 @@ public class ProtectedSignalsServiceImpl extends IProtectedSignalsService.Stub { } // Caller permissions must be checked in the binder thread, before anything else - mFledgeAuthorizationFilter.assertAppDeclaredProtectedSignalsPermission( + mFledgeAuthorizationFilter.assertAppDeclaredPermission( mContext, updateSignalsInput.getCallerPackageName(), apiName); final int callerUid = getCallingUid(apiName); diff --git a/adservices/service-core/java/com/android/adservices/service/signals/SignalsMaintenanceTasksWorker.java b/adservices/service-core/java/com/android/adservices/service/signals/SignalsMaintenanceTasksWorker.java index 2df832c1a..0205389b1 100644 --- a/adservices/service-core/java/com/android/adservices/service/signals/SignalsMaintenanceTasksWorker.java +++ b/adservices/service-core/java/com/android/adservices/service/signals/SignalsMaintenanceTasksWorker.java @@ -109,18 +109,20 @@ public class SignalsMaintenanceTasksWorker { * </ul> */ public void clearInvalidProtectedSignalsData() { - Instant expirationInstant = - mClock.instant().minusSeconds(ProtectedSignal.EXPIRATION_SECONDS); - clearInvalidSignals(expirationInstant); + Instant now = mClock.instant(); + Instant expirationInstant = now.minusSeconds(ProtectedSignal.EXPIRATION_SECONDS); + clearInvalidSignals(expirationInstant, now); clearInvalidEncoders(expirationInstant); clearInvalidEncodedPayloads(expirationInstant); } @VisibleForTesting - void clearInvalidSignals(Instant expirationInstant) { + void clearInvalidSignals(Instant expirationInstant, Instant now) { sLogger.v("Clearing expired signals older than %s", expirationInstant); - int numExpiredSignals = mProtectedSignalsDao.deleteSignalsBeforeTime(expirationInstant); + int numExpiredSignals = + mProtectedSignalsDao.deleteExpiredSignalsAndUpdateSignalsUpdateMetadata( + expirationInstant, now); sLogger.v("Cleared %d expired signals", numExpiredSignals); // Read from flags directly, since this maintenance task worker is attached to a background @@ -138,7 +140,8 @@ public class SignalsMaintenanceTasksWorker { sLogger.v("Clearing signals for disallowed source apps"); int numDisallowedSourceAppSignals = - mProtectedSignalsDao.deleteAllDisallowedPackageSignals(mPackageManager, mFlags); + mProtectedSignalsDao.deleteAllDisallowedPackageSignalsAndUpdateSignalUpdateMetadata( + mPackageManager, mFlags, now); sLogger.v("Cleared %d signals for disallowed source apps", numDisallowedSourceAppSignals); } diff --git a/adservices/service-core/java/com/android/adservices/service/signals/UpdateProcessingOrchestrator.java b/adservices/service-core/java/com/android/adservices/service/signals/UpdateProcessingOrchestrator.java index 9a3c004da..476445b87 100644 --- a/adservices/service-core/java/com/android/adservices/service/signals/UpdateProcessingOrchestrator.java +++ b/adservices/service-core/java/com/android/adservices/service/signals/UpdateProcessingOrchestrator.java @@ -115,7 +115,7 @@ public class UpdateProcessingOrchestrator { mSignalEvictionController.evict(adtech, updatedSignals, combinedUpdates); - writeChanges(adtech, combinedUpdates, devContext); + writeChanges(adtech, creationTime, combinedUpdates, devContext); } catch (JSONException e) { throw new IllegalArgumentException("Couldn't unpack signal updates JSON", e); } @@ -195,11 +195,16 @@ public class UpdateProcessingOrchestrator { } private void writeChanges( - AdTechIdentifier adTech, UpdateOutput combinedUpdates, DevContext devContext) { + AdTechIdentifier adTech, + Instant creationTime, + UpdateOutput combinedUpdates, + DevContext devContext) { /* Modify the DB based on the output of the update processors. Might be worth skipping * this is both signalsToAdd and signalsToDelete are empty. */ mProtectedSignalsDao.insertAndDelete( + adTech, + creationTime, combinedUpdates.getToAdd().stream() .map(DBProtectedSignal.Builder::build) .collect(Collectors.toList()), diff --git a/adservices/service-core/java/com/android/adservices/service/signals/UpdateSignalsOrchestrator.java b/adservices/service-core/java/com/android/adservices/service/signals/UpdateSignalsOrchestrator.java index 24ff9be75..73ad03455 100644 --- a/adservices/service-core/java/com/android/adservices/service/signals/UpdateSignalsOrchestrator.java +++ b/adservices/service-core/java/com/android/adservices/service/signals/UpdateSignalsOrchestrator.java @@ -27,7 +27,7 @@ import com.google.common.util.concurrent.FluentFuture; import org.json.JSONObject; -import java.time.Instant; +import java.time.Clock; import java.util.Objects; import java.util.concurrent.Executor; @@ -38,20 +38,24 @@ public class UpdateSignalsOrchestrator { @NonNull private final UpdatesDownloader mUpdatesDownloader; @NonNull private final UpdateProcessingOrchestrator mUpdateProcessingOrchestrator; @NonNull private final AdTechUriValidator mAdTechUriValidator; + @NonNull private final Clock mClock; public UpdateSignalsOrchestrator( @NonNull Executor backgroundExecutor, @NonNull UpdatesDownloader updatesDownloader, @NonNull UpdateProcessingOrchestrator updateProcessingOrchestrator, - @NonNull AdTechUriValidator adTechUriValidator) { + @NonNull AdTechUriValidator adTechUriValidator, + @NonNull Clock clock) { Objects.requireNonNull(backgroundExecutor); Objects.requireNonNull(updatesDownloader); Objects.requireNonNull(updateProcessingOrchestrator); Objects.requireNonNull(adTechUriValidator); + Objects.requireNonNull(clock); mBackgroundExecutor = backgroundExecutor; mUpdateProcessingOrchestrator = updateProcessingOrchestrator; mUpdatesDownloader = updatesDownloader; mAdTechUriValidator = adTechUriValidator; + mClock = clock; } /** @@ -70,7 +74,7 @@ public class UpdateSignalsOrchestrator { return jsonFuture.transform( x -> { mUpdateProcessingOrchestrator.processUpdates( - adtech, packageName, Instant.now(), x, devContext); + adtech, packageName, mClock.instant(), x, devContext); return null; }, mBackgroundExecutor); diff --git a/adservices/service-core/java/com/android/adservices/service/stats/ConsentMigrationStats.java b/adservices/service-core/java/com/android/adservices/service/stats/ConsentMigrationStats.java index b5437a8da..d877f45dd 100644 --- a/adservices/service-core/java/com/android/adservices/service/stats/ConsentMigrationStats.java +++ b/adservices/service-core/java/com/android/adservices/service/stats/ConsentMigrationStats.java @@ -19,6 +19,8 @@ import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICE import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_CONSENT_MIGRATED__MIGRATION_STATUS__SUCCESS_WITH_SHARED_PREF_NOT_UPDATED; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_CONSENT_MIGRATED__MIGRATION_STATUS__SUCCESS_WITH_SHARED_PREF_UPDATED; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_CONSENT_MIGRATED__MIGRATION_STATUS__UNSPECIFIED_MIGRATION_STATUS; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_CONSENT_MIGRATED__MIGRATION_TYPE__ADEXT_SERVICE_TO_APPSEARCH; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_CONSENT_MIGRATED__MIGRATION_TYPE__ADEXT_SERVICE_TO_SYSTEM_SERVICE; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_CONSENT_MIGRATED__MIGRATION_TYPE__APPSEARCH_TO_SYSTEM_SERVICE; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_CONSENT_MIGRATED__MIGRATION_TYPE__PPAPI_TO_SYSTEM_SERVICE; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_CONSENT_MIGRATED__MIGRATION_TYPE__UNSPECIFIED_MIGRATION_TYPE; @@ -75,7 +77,14 @@ public abstract class ConsentMigrationStats { AD_SERVICES_CONSENT_MIGRATED__MIGRATION_TYPE__PPAPI_TO_SYSTEM_SERVICE), // Migrating consent from App Search to system service APPSEARCH_TO_SYSTEM_SERVICE( - AD_SERVICES_CONSENT_MIGRATED__MIGRATION_TYPE__APPSEARCH_TO_SYSTEM_SERVICE); + AD_SERVICES_CONSENT_MIGRATED__MIGRATION_TYPE__APPSEARCH_TO_SYSTEM_SERVICE), + // Migrating consent from AdServicesExtDataStorageService to System Server + ADEXT_SERVICE_TO_SYSTEM_SERVICE( + AD_SERVICES_CONSENT_MIGRATED__MIGRATION_TYPE__ADEXT_SERVICE_TO_SYSTEM_SERVICE), + // Migrating consent from AdServicesExtDataStorageService to App Search + ADEXT_SERVICE_TO_APPSEARCH( + AD_SERVICES_CONSENT_MIGRATED__MIGRATION_TYPE__ADEXT_SERVICE_TO_APPSEARCH); + private final int mMigrationType; MigrationType(int migrationType) { diff --git a/adservices/service-core/java/com/android/adservices/service/topics/EncryptionManager.java b/adservices/service-core/java/com/android/adservices/service/topics/EncryptionManager.java index a9a56b8aa..4f147fe67 100644 --- a/adservices/service-core/java/com/android/adservices/service/topics/EncryptionManager.java +++ b/adservices/service-core/java/com/android/adservices/service/topics/EncryptionManager.java @@ -116,8 +116,9 @@ public class EncryptionManager { * public key is missing. */ private Optional<String> fetchPublicKeyFor(String sdkName) { - if (mFlags.isDisableTopicsEnrollmentCheck()) { - return Optional.of(TEST_PUBLIC_KEY_BASE64); + if (!mFlags.getTopicsTestEncryptionPublicKey().isEmpty()) { + // Use testing key for encryption if the test key is non-empty. + return Optional.of(mFlags.getTopicsTestEncryptionPublicKey()); } sLogger.v("Fetching EnrollmentData for %s", sdkName); diff --git a/adservices/service-core/java/com/android/adservices/service/topics/TopicsServiceImpl.java b/adservices/service-core/java/com/android/adservices/service/topics/TopicsServiceImpl.java index 5091491c2..683a63d9f 100644 --- a/adservices/service-core/java/com/android/adservices/service/topics/TopicsServiceImpl.java +++ b/adservices/service-core/java/com/android/adservices/service/topics/TopicsServiceImpl.java @@ -31,6 +31,7 @@ import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICE import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__API_CALLBACK_ERROR; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__PACKAGE_NAME_NOT_FOUND_EXCEPTION; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__RATE_LIMIT_CALLBACK_FAILURE; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__TOPICS_REQUEST_EMPTY_SDK_NAME; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__TOPICS; import android.adservices.common.AdServicesStatusUtils; @@ -147,7 +148,7 @@ public class TopicsServiceImpl extends ITopicsService.Stub { // Check if the request is valid. if (!validateRequest(topicsParam, callback)) { // Return early if the request is invalid. - sLogger.d("Invalid request %s", topicsParam); + sLogger.e("Invalid request %s", topicsParam); return; } } @@ -202,6 +203,9 @@ public class TopicsServiceImpl extends ITopicsService.Stub { GetTopicsParam topicsParam, IGetTopicsCallback callback) { // Return false if sdkName is empty or null. if (TextUtils.isEmpty(topicsParam.getSdkName())) { + ErrorLogUtil.e( + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__TOPICS_REQUEST_EMPTY_SDK_NAME, + AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__TOPICS); invokeCallbackWithStatus( callback, STATUS_INVALID_ARGUMENT, diff --git a/adservices/service-core/java/com/android/adservices/service/topics/classifier/ClassifierInputManager.java b/adservices/service-core/java/com/android/adservices/service/topics/classifier/ClassifierInputManager.java index 8e257aff4..139d2d9ba 100644 --- a/adservices/service-core/java/com/android/adservices/service/topics/classifier/ClassifierInputManager.java +++ b/adservices/service-core/java/com/android/adservices/service/topics/classifier/ClassifierInputManager.java @@ -138,7 +138,7 @@ public class ClassifierInputManager { applicationInfo.descriptionRes))); break; default: - LogUtil.e("Invalid input field in config: {}", inputField); + LogUtil.e("Invalid input field in config: %s", inputField); return null; } } @@ -171,7 +171,7 @@ public class ClassifierInputManager { PackageManager packageManager, ApplicationInfo applicationInfo, int resourceId) { if (!SdkLevel.isAtLeastS()) { LogUtil.d( - "English app resource not available for SDK version {} - " + "English app resource not available for SDK version %d - " + "returning localized app resource", Build.VERSION.SDK_INT); return getLocalAppResource(packageManager, applicationInfo, resourceId); @@ -192,7 +192,7 @@ public class ClassifierInputManager { } catch (PackageManager.NameNotFoundException e) { LogUtil.e("No resources returned from packageManager."); } catch (Resources.NotFoundException e) { - LogUtil.e("Resource not found by packageManager - resourceId: {}", resourceId); + LogUtil.e("Resource not found by packageManager - resourceId: %d", resourceId); } return EMPTY_STRING; } @@ -204,7 +204,7 @@ public class ClassifierInputManager { } catch (PackageManager.NameNotFoundException e) { LogUtil.e("No resources returned from packageManager."); } catch (Resources.NotFoundException e) { - LogUtil.e("Resource not found by packageManager - resourceId: {}", resourceId); + LogUtil.e("Resource not found by packageManager - resourceId: %d", resourceId); } return EMPTY_STRING; } diff --git a/adservices/service-core/java/com/android/adservices/service/topics/classifier/ModelManager.java b/adservices/service-core/java/com/android/adservices/service/topics/classifier/ModelManager.java index 3a1f1934a..4a2fda2f4 100644 --- a/adservices/service-core/java/com/android/adservices/service/topics/classifier/ModelManager.java +++ b/adservices/service-core/java/com/android/adservices/service/topics/classifier/ModelManager.java @@ -665,7 +665,7 @@ public class ModelManager { try { inputFields.add(ClassifierInputField.valueOf(line)); } catch (IllegalArgumentException e) { - LogUtil.e("Invalid input field in classifier input config: {}", line); + LogUtil.e("Invalid input field in classifier input config: %s", line); return ClassifierInputConfig.getEmptyConfig(); } } @@ -696,7 +696,7 @@ public class ModelManager { try { String formattedInput = String.format(classifierInputConfig.getInputFormat(), (Object[]) inputFields); - LogUtil.d("Validated classifier input format: {}", formattedInput); + LogUtil.d("Validated classifier input format: %s", formattedInput); } catch (IllegalFormatException e) { LogUtil.e("Classifier input config is incorrectly formatted"); return false; diff --git a/adservices/service-core/java/com/android/adservices/service/ui/data/UxStatesManager.java b/adservices/service-core/java/com/android/adservices/service/ui/data/UxStatesManager.java index ea6c8203f..9079562e3 100644 --- a/adservices/service-core/java/com/android/adservices/service/ui/data/UxStatesManager.java +++ b/adservices/service-core/java/com/android/adservices/service/ui/data/UxStatesManager.java @@ -102,7 +102,7 @@ public class UxStatesManager { /** Returns process statble UX flags. */ public boolean getFlag(String uxFlagKey) { if (!mUxFlags.containsKey(uxFlagKey)) { - LogUtil.e("Key not found in cached UX flags: ", uxFlagKey); + LogUtil.e("Key not found in cached UX flags: %s", uxFlagKey); } Boolean value = mUxFlags.get(uxFlagKey); return value != null ? value : false; diff --git a/adservices/service-core/java/com/android/adservices/service/ui/enrollment/impl/AlreadyEnrolledChannel.java b/adservices/service-core/java/com/android/adservices/service/ui/enrollment/impl/AlreadyEnrolledChannel.java index caea3ca6c..bff7b772c 100644 --- a/adservices/service-core/java/com/android/adservices/service/ui/enrollment/impl/AlreadyEnrolledChannel.java +++ b/adservices/service-core/java/com/android/adservices/service/ui/enrollment/impl/AlreadyEnrolledChannel.java @@ -56,12 +56,9 @@ public class AlreadyEnrolledChannel implements PrivacySandboxEnrollmentChannel { private static boolean isPreNotificationManualUser( ConsentManager consentManager, UxStatesManager uxStatesManager) { - if (uxStatesManager.getFlag(KEY_CONSENT_ALREADY_INTERACTED_FIX_ENABLE) + return uxStatesManager.getFlag(KEY_CONSENT_ALREADY_INTERACTED_FIX_ENABLE) && consentManager.getUserManualInteractionWithConsent() - == MANUAL_INTERACTIONS_RECORDED) { - return true; - } - return false; + == MANUAL_INTERACTIONS_RECORDED; } /** No-Op if the user has already enrolled. */ diff --git a/adservices/service-core/java/com/android/adservices/service/ui/enrollment/impl/ConsentNotificationResetChannel.java b/adservices/service-core/java/com/android/adservices/service/ui/enrollment/impl/ConsentNotificationResetChannel.java index 126e35346..aa26433a8 100644 --- a/adservices/service-core/java/com/android/adservices/service/ui/enrollment/impl/ConsentNotificationResetChannel.java +++ b/adservices/service-core/java/com/android/adservices/service/ui/enrollment/impl/ConsentNotificationResetChannel.java @@ -16,6 +16,8 @@ package com.android.adservices.service.ui.enrollment.impl; +import static com.android.adservices.service.consent.ConsentManager.NO_MANUAL_INTERACTIONS_RECORDED; + import android.content.Context; import android.content.SharedPreferences; import android.os.Build; @@ -72,6 +74,7 @@ public class ConsentNotificationResetChannel implements PrivacySandboxEnrollment /** Perform enrollment logic for the reset channel. */ public void enroll(Context context, ConsentManager consentManager) { + consentManager.recordUserManualInteractionWithConsent(NO_MANUAL_INTERACTIONS_RECORDED); consentManager.recordNotificationDisplayed(false); consentManager.recordGaUxNotificationDisplayed(false); consentManager.setU18NotificationDisplayed(false); diff --git a/adservices/service-core/java/com/android/adservices/service/ui/enrollment/impl/RvcPostOTAChannel.java b/adservices/service-core/java/com/android/adservices/service/ui/enrollment/impl/RvcPostOTAChannel.java index 6d6bc18d6..6d89bd4d3 100644 --- a/adservices/service-core/java/com/android/adservices/service/ui/enrollment/impl/RvcPostOTAChannel.java +++ b/adservices/service-core/java/com/android/adservices/service/ui/enrollment/impl/RvcPostOTAChannel.java @@ -16,7 +16,7 @@ package com.android.adservices.service.ui.enrollment.impl; -import static com.android.adservices.service.FlagsConstants.KEY_RVC_NOTIFICATION_ENABLED; +import static com.android.adservices.service.FlagsConstants.KEY_RVC_POST_OTA_NOTIFICATION_ENABLED; import android.content.Context; import android.os.Build; @@ -40,8 +40,7 @@ public class RvcPostOTAChannel implements PrivacySandboxEnrollmentChannel { ConsentManager consentManager, UxStatesManager uxStatesManager) { // Rvc user should be matched to RvcPostOTAChannel on S+ - // TODO: rename flag to KEY_RVC_POST_OTA_NOTIFICATION_ENABLED - return uxStatesManager.getFlag(KEY_RVC_NOTIFICATION_ENABLED) + return uxStatesManager.getFlag(KEY_RVC_POST_OTA_NOTIFICATION_ENABLED) && consentManager.isOtaAdultUserFromRvc(); } diff --git a/adservices/service-core/schemas/com.android.adservices.data.signals.ProtectedSignalsDatabase/4.json b/adservices/service-core/schemas/com.android.adservices.data.signals.ProtectedSignalsDatabase/4.json new file mode 100644 index 000000000..a060c0d03 --- /dev/null +++ b/adservices/service-core/schemas/com.android.adservices.data.signals.ProtectedSignalsDatabase/4.json @@ -0,0 +1,239 @@ +{ + "formatVersion": 1, + "database": { + "version": 4, + "identityHash": "3bf5a664458869cb5cd6781484a8a229", + "entities": [ + { + "tableName": "protected_signals", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `buyer` TEXT NOT NULL, `key` BLOB NOT NULL, `value` BLOB NOT NULL, `creationTime` INTEGER NOT NULL, `packageName` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "buyer", + "columnName": "buyer", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "creationTime", + "columnName": "creationTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_protected_signals_buyer", + "unique": false, + "columnNames": [ + "buyer" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_protected_signals_buyer` ON `${TABLE_NAME}` (`buyer`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "encoder_endpoints", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`buyer` TEXT NOT NULL, `download_uri` TEXT NOT NULL, `creation_time` INTEGER NOT NULL, PRIMARY KEY(`buyer`))", + "fields": [ + { + "fieldPath": "buyer", + "columnName": "buyer", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "downloadUri", + "columnName": "download_uri", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "creationTime", + "columnName": "creation_time", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "buyer" + ] + }, + "indices": [ + { + "name": "index_encoder_endpoints_creation_time", + "unique": false, + "columnNames": [ + "creation_time" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_encoder_endpoints_creation_time` ON `${TABLE_NAME}` (`creation_time`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "encoder_logics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`buyer` TEXT NOT NULL, `version` INTEGER NOT NULL, `creation_time` INTEGER NOT NULL, `failed_encoding_count` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`buyer`))", + "fields": [ + { + "fieldPath": "buyer", + "columnName": "buyer", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "creationTime", + "columnName": "creation_time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "failedEncodingCount", + "columnName": "failed_encoding_count", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "buyer" + ] + }, + "indices": [ + { + "name": "index_encoder_logics_creation_time", + "unique": false, + "columnNames": [ + "creation_time" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_encoder_logics_creation_time` ON `${TABLE_NAME}` (`creation_time`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "encoded_payload", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`buyer` TEXT NOT NULL, `version` INTEGER NOT NULL, `creation_time` INTEGER NOT NULL, `encoded_payload` BLOB NOT NULL, PRIMARY KEY(`buyer`))", + "fields": [ + { + "fieldPath": "buyer", + "columnName": "buyer", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "creationTime", + "columnName": "creation_time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "encodedPayload", + "columnName": "encoded_payload", + "affinity": "BLOB", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "buyer" + ] + }, + "indices": [ + { + "name": "index_encoded_payload_creation_time", + "unique": false, + "columnNames": [ + "creation_time" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_encoded_payload_creation_time` ON `${TABLE_NAME}` (`creation_time`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "signals_update_metadata", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`buyer` TEXT NOT NULL, `last_signals_updated_time` INTEGER, PRIMARY KEY(`buyer`))", + "fields": [ + { + "fieldPath": "buyer", + "columnName": "buyer", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastSignalsUpdatedTime", + "columnName": "last_signals_updated_time", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "buyer" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '3bf5a664458869cb5cd6781484a8a229')" + ] + } +}
\ No newline at end of file diff --git a/adservices/service/java/com/android/server/adservices/AdServicesManagerService.java b/adservices/service/java/com/android/server/adservices/AdServicesManagerService.java index 19ee994b0..239fabda1 100644 --- a/adservices/service/java/com/android/server/adservices/AdServicesManagerService.java +++ b/adservices/service/java/com/android/server/adservices/AdServicesManagerService.java @@ -767,7 +767,7 @@ public class AdServicesManagerService extends IAdServicesManager.Stub { } LogUtil.v("Executing shell cmd: %s", Arrays.toString(args)); - return new AdServicesShellCommand() + return new AdServicesShellCommand(mContext) .exec( this, in.getFileDescriptor(), diff --git a/adservices/service/java/com/android/server/adservices/AdServicesShellCommand.java b/adservices/service/java/com/android/server/adservices/AdServicesShellCommand.java index 58f41b772..cbdf07537 100644 --- a/adservices/service/java/com/android/server/adservices/AdServicesShellCommand.java +++ b/adservices/service/java/com/android/server/adservices/AdServicesShellCommand.java @@ -18,9 +18,17 @@ package com.android.server.adservices; import static android.app.adservices.AdServicesManager.AD_SERVICES_SYSTEM_SERVICE; +import android.adservices.shell.IShellCommand; +import android.adservices.shell.IShellCommandCallback; +import android.adservices.shell.ShellCommandParam; +import android.adservices.shell.ShellCommandResult; +import android.content.Context; import android.os.Binder; import android.os.Process; +import android.os.RemoteException; +import com.android.adservices.AdServicesCommon; +import com.android.adservices.ServiceBinder; import com.android.internal.annotations.VisibleForTesting; import com.android.modules.utils.BasicShellCommandHandler; @@ -28,7 +36,11 @@ import com.google.errorprone.annotations.FormatMethod; import com.google.errorprone.annotations.FormatString; import java.io.PrintWriter; +import java.util.Arrays; import java.util.Objects; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; /** * Implementation of {@code cmd adservices_manager}. @@ -44,17 +56,23 @@ class AdServicesShellCommand extends BasicShellCommandHandler { static final String WRONG_UID_TEMPLATE = AD_SERVICES_SYSTEM_SERVICE + " shell cmd is only callable by ADB (called by %d)"; + private static final String CMD_IS_SYSTEM_SERVICE_ENABLED = "is-system-service-enabled"; + private final Injector mInjector; private final Flags mFlags; + private final Context mContext; + + private static final int DEFAULT_TIMEOUT_MILLIS = 5_000; - AdServicesShellCommand() { - this(new Injector(), PhFlags.getInstance()); + AdServicesShellCommand(Context context) { + this(new Injector(), PhFlags.getInstance(), context); } @VisibleForTesting - AdServicesShellCommand(Injector injector, Flags flags) { + AdServicesShellCommand(Injector injector, Flags flags, Context context) { mInjector = Objects.requireNonNull(injector); mFlags = Objects.requireNonNull(flags); + mContext = Objects.requireNonNull(context); } @Override @@ -63,17 +81,74 @@ class AdServicesShellCommand extends BasicShellCommandHandler { if (callingUid != Process.ROOT_UID && callingUid != Process.SHELL_UID) { throw new SecurityException(String.format(WRONG_UID_TEMPLATE, callingUid)); } - if (cmd == null || cmd.isEmpty() || cmd.equals("-h") || cmd.equals("help")) { onHelp(); return 0; } switch (cmd) { - case "is-system-service-enabled": + // Below commands are handled by the System Server + case CMD_IS_SYSTEM_SERVICE_ENABLED: return runIsSystemServiceEnabled(); + + // If there is no explicit case is there, we assume we want to run the shell command + // in the adservices process. default: - // Cannot use handleDefaultCommands() because it doesn't show help - return showError("Unsupported commmand: %s", cmd); + // TODO(b/308009734): Check for --user args in the follow-up cl and change + // context and bind to the service accordingly. + return runAdServicesShellCommand(mContext, getAllArgs()); + } + } + + private int runAdServicesShellCommand(Context context, String[] args) { + IShellCommand service = mInjector.getShellCommandService(context); + if (service == null) { + getOutPrintWriter().println("Failed to connect to shell command service"); + return -1; + } + ShellCommandParam param = new ShellCommandParam(args); + CountDownLatch latch = new CountDownLatch(1); + AtomicInteger resultCode = new AtomicInteger(-1); + try { + service.runShellCommand( + param, + new IShellCommandCallback.Stub() { + @Override + public void onResult(ShellCommandResult response) { + if (response.isSuccess()) { + getOutPrintWriter().println(response.getOut()); + resultCode.set(response.getResultCode()); + } else { + showError("%s", response.getErr()); + } + latch.countDown(); + } + }); + } catch (RemoteException e) { + getErrPrintWriter() + .printf( + "Remote exception occurred while executing %s\n", + Arrays.toString(args)); + + latch.countDown(); + } + + // TODO(b/308009734): make the time out configurable with flags and command line argument. + await(latch, DEFAULT_TIMEOUT_MILLIS, getErrPrintWriter()); + + return resultCode.get(); + } + + private void await(CountDownLatch latch, int timeout, PrintWriter pw) { + try { + if (!latch.await(timeout, TimeUnit.MILLISECONDS)) { + pw.printf( + "Elapsed time: %d Millisecond. Timeout occurred , failed to " + + "complete shell command\n", + timeout); + } + } catch (InterruptedException e) { + pw.println("Thread interrupted, failed to complete shell command"); + Thread.currentThread().interrupt(); } } @@ -144,5 +219,14 @@ class AdServicesShellCommand extends BasicShellCommandHandler { int getCallingUid() { return Binder.getCallingUid(); } + + IShellCommand getShellCommandService(Context context) { + ServiceBinder<IShellCommand> serviceBinder = + ServiceBinder.getServiceBinder( + context, + AdServicesCommon.ACTION_SHELL_COMMAND_SERVICE, + IShellCommand.Stub::asInterface); + return serviceBinder.getService(); + } } } diff --git a/adservices/tests/cts/Android.bp b/adservices/tests/cts/Android.bp index 0cc6024c3..3aec78af2 100644 --- a/adservices/tests/cts/Android.bp +++ b/adservices/tests/cts/Android.bp @@ -35,12 +35,12 @@ android_test { "cts", "mts-adservices", "general-tests", + "mcts-adservices", ], plugins: [ "auto_value_plugin", "auto_annotation_plugin" ], - test_mainline_modules: ["com.google.android.adservices.apex"], static_libs: [ "androidx.test.rules", "androidx.test.ext.junit", @@ -81,12 +81,12 @@ android_test { "cts_root", "mts-adservices", "general-tests", + "mcts-adservices", ], plugins: [ "auto_value_plugin", "auto_annotation_plugin" ], - test_mainline_modules: ["com.google.android.adservices.apex"], static_libs: [ "androidx.test.rules", "androidx.test.ext.junit", @@ -102,8 +102,8 @@ android_test { sdk_version: "module_current", min_sdk_version: "Tiramisu", compile_multilib: "both", - test_config: "AndroidTest.Root.xml", manifest: "AndroidManifestRoot.xml", + test_config: "AndroidTest.Root.xml", } android_test { @@ -126,12 +126,12 @@ android_test { "cts", "mts-extservices", "general-tests", + "mcts-extservices", ], plugins: [ "auto_value_plugin", "auto_annotation_plugin" ], - test_mainline_modules: ["com.google.android.extservices.apex"], static_libs: [ "androidx.test.rules", "androidx.test.ext.junit", @@ -174,8 +174,8 @@ android_test { "cts", "mts-adservices", "general-tests", + "mcts-adservices", ], - test_mainline_modules: ["com.google.android.adservices.apex"], static_libs: [ "androidx.room_room-runtime", "androidx.room_room-testing", @@ -216,6 +216,7 @@ android_test { "cts", "mts-extservices", "general-tests", + "mcts-extservices", ], plugins: [ "auto_value_plugin", diff --git a/adservices/tests/cts/AndroidTest.ExtServices.xml b/adservices/tests/cts/AndroidTest.ExtServices.xml index 1e96fba6d..182611f37 100644 --- a/adservices/tests/cts/AndroidTest.ExtServices.xml +++ b/adservices/tests/cts/AndroidTest.ExtServices.xml @@ -67,6 +67,12 @@ <option name="teardown-command" value="setprop debug.adservices.consent_manager_debug_mode false"/> + <!-- Override MDD background job to avoid race condition. --> + <option name="run-command" + value="setprop debug.adservices.mdd_background_task_kill_switch true"/> + <option name="teardown-command" + value="setprop debug.adservices.mdd_background_task_kill_switch false"/> + <!-- Force enable enrollment checks for FLEDGE when testing enrollment --> <option name="run-command" value="setprop debug.adservices.disable_fledge_enrollment_check false"/> diff --git a/adservices/tests/cts/AndroidTest.Root.xml b/adservices/tests/cts/AndroidTest.Root.xml index de174602c..beeba4e1a 100644 --- a/adservices/tests/cts/AndroidTest.Root.xml +++ b/adservices/tests/cts/AndroidTest.Root.xml @@ -20,6 +20,11 @@ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> + + <!-- For this test suite to get picked up in mainline presubmit --> + <option name="config-descriptor:metadata" key="mainline-param" + value="com.google.android.adservices.apex" /> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true" /> <option name="test-file-name" value="CtsAdServicesRootTestCases.apk" /> @@ -44,6 +49,12 @@ <option name="run-command" value="setprop debug.adservices.consent_manager_debug_mode true" /> <option name="teardown-command" value="setprop debug.adservices.consent_manager_debug_mode false"/> + <!-- Override MDD background job to avoid race condition. --> + <option name="run-command" + value="setprop debug.adservices.mdd_background_task_kill_switch true"/> + <option name="teardown-command" + value="setprop debug.adservices.mdd_background_task_kill_switch false"/> + <!-- Force enable enrollment checks for FLEDGE when testing enrollment --> <option name="run-command" value="setprop debug.adservices.disable_fledge_enrollment_check false" /> diff --git a/adservices/tests/cts/AndroidTest.xml b/adservices/tests/cts/AndroidTest.xml index 7fc974f98..61c51b4af 100644 --- a/adservices/tests/cts/AndroidTest.xml +++ b/adservices/tests/cts/AndroidTest.xml @@ -43,6 +43,12 @@ <option name="run-command" value="setprop debug.adservices.consent_manager_debug_mode true" /> <option name="teardown-command" value="setprop debug.adservices.consent_manager_debug_mode false"/> + <!-- Override MDD background job to avoid race condition. --> + <option name="run-command" + value="setprop debug.adservices.mdd_background_task_kill_switch true"/> + <option name="teardown-command" + value="setprop debug.adservices.mdd_background_task_kill_switch false"/> + <!-- Force enable enrollment checks for FLEDGE when testing enrollment --> <option name="run-command" value="setprop debug.adservices.disable_fledge_enrollment_check false" /> diff --git a/adservices/tests/cts/AndroidTestDebuggable.ExtServices.xml b/adservices/tests/cts/AndroidTestDebuggable.ExtServices.xml index 5dd225ba3..04b3850ff 100644 --- a/adservices/tests/cts/AndroidTestDebuggable.ExtServices.xml +++ b/adservices/tests/cts/AndroidTestDebuggable.ExtServices.xml @@ -68,6 +68,12 @@ <option name="teardown-command" value="setprop debug.adservices.consent_manager_debug_mode false"/> + <!-- Override MDD background job to avoid race condition. --> + <option name="run-command" + value="setprop debug.adservices.mdd_background_task_kill_switch true"/> + <option name="teardown-command" + value="setprop debug.adservices.mdd_background_task_kill_switch false"/> + <!-- Increase the allowed API queries per second --> <option name="run-command" value="setprop debug.adservices.sdk_request_permits_per_second 100000"/> diff --git a/adservices/tests/cts/AndroidTestDebuggable.xml b/adservices/tests/cts/AndroidTestDebuggable.xml index 4945c1852..d84b3081e 100644 --- a/adservices/tests/cts/AndroidTestDebuggable.xml +++ b/adservices/tests/cts/AndroidTestDebuggable.xml @@ -45,6 +45,12 @@ <option name="run-command" value="setprop debug.adservices.consent_manager_debug_mode true" /> <option name="teardown-command" value="setprop debug.adservices.consent_manager_debug_mode false"/> + <!-- Override MDD background job to avoid race condition. --> + <option name="run-command" + value="setprop debug.adservices.mdd_background_task_kill_switch true"/> + <option name="teardown-command" + value="setprop debug.adservices.mdd_background_task_kill_switch false"/> + <!-- Increase the allowed API queries per second --> <option name="run-command" value="setprop debug.adservices.sdk_request_permits_per_second 100000" /> <option name="teardown-command" value="setprop debug.adservices.sdk_request_permits_per_second 1" /> diff --git a/adservices/tests/cts/adid/Android.bp b/adservices/tests/cts/adid/Android.bp index f06b1fc98..eeb95fe64 100644 --- a/adservices/tests/cts/adid/Android.bp +++ b/adservices/tests/cts/adid/Android.bp @@ -36,7 +36,8 @@ android_test { test_suites: [ "cts", "general-tests", - "mts-adservices" + "mts-adservices", + "mcts-adservices", ], sdk_version: "module_current", min_sdk_version: "Tiramisu", @@ -63,11 +64,12 @@ android_test { test_suites: [ "cts", "general-tests", - "mts-extservices" + "mts-extservices", + "mcts-extservices", ], sdk_version: "module_current", min_sdk_version: "30", max_sdk_version: "32", test_config: "AndroidTest.ExtServices.xml", manifest: "AndroidManifestExtServices.xml", -} +}
\ No newline at end of file diff --git a/adservices/tests/cts/adid/src/com/android/adservices/tests/adid/AdIdManagerTest.java b/adservices/tests/cts/adid/src/com/android/adservices/tests/adid/AdIdManagerTest.java index 034688c6e..3a5abc4c0 100644 --- a/adservices/tests/cts/adid/src/com/android/adservices/tests/adid/AdIdManagerTest.java +++ b/adservices/tests/cts/adid/src/com/android/adservices/tests/adid/AdIdManagerTest.java @@ -17,8 +17,6 @@ package com.android.adservices.tests.adid; import static com.google.common.truth.Truth.assertWithMessage; -import static org.junit.Assert.fail; - import android.adservices.adid.AdId; import android.adservices.adid.AdIdManager; import android.os.LimitExceededException; @@ -162,19 +160,14 @@ public final class AdIdManagerTest extends CtsAdIdEndToEndTestCase { @Test @RequiresLowRamDevice - public void testAdIdManager_whenDeviceNotSupported_R() { + public void testAdIdManager_whenDeviceNotSupported_R() throws Exception { AdIdManager adIdManager = AdIdManager.get(sContext); assertWithMessage("adIdManager").that(adIdManager).isNotNull(); AdServicesOutcomeReceiverForTests<AdId> receiver = new AdServicesOutcomeReceiverForTests<>(); - // NOTE: cannot use assertThrows() as it would cause a NoSuchClassException on R (as - // JUnit somehow scans the whole class) - try { - adIdManager.getAdId(sCallbackExecutor, receiver); - fail("getAdId() should have thrown IllegalStateException"); - } catch (IllegalStateException e) { - // expected - } + + adIdManager.getAdId(sCallbackExecutor, receiver); + receiver.assertFailure(IllegalStateException.class); } private static String toString(AdId adId) { diff --git a/adservices/tests/cts/appsetid/Android.bp b/adservices/tests/cts/appsetid/Android.bp index 628cb1e8c..65087f5f0 100644 --- a/adservices/tests/cts/appsetid/Android.bp +++ b/adservices/tests/cts/appsetid/Android.bp @@ -36,7 +36,8 @@ android_test { test_suites: [ "cts", "general-tests", - "mts-adservices" + "mts-adservices", + "mcts-adservices", ], javacflags: ["-parameters"], sdk_version: "module_current", @@ -64,7 +65,8 @@ android_test { test_suites: [ "cts", "general-tests", - "mts-extservices" + "mts-extservices", + "mcts-extservices", ], sdk_version: "module_current", min_sdk_version: "31", diff --git a/adservices/tests/cts/assets/scenarios/data/InvalidJson.json b/adservices/tests/cts/assets/scenarios/data/InvalidJson.json new file mode 100644 index 000000000..24913e916 --- /dev/null +++ b/adservices/tests/cts/assets/scenarios/data/InvalidJson.json @@ -0,0 +1 @@ +this{is_not:real}json
\ No newline at end of file diff --git a/adservices/tests/cts/assets/scenarios/remarketing-cuj-034.json b/adservices/tests/cts/assets/scenarios/remarketing-cuj-034.json new file mode 100644 index 000000000..eab9b85cd --- /dev/null +++ b/adservices/tests/cts/assets/scenarios/remarketing-cuj-034.json @@ -0,0 +1,69 @@ +{ + "mocks": [ + { + "request": { + "path": "bidding" + }, + "response": { + "body": "BiddingLogicV2.js" + } + }, + { + "request": { + "path": "scoring" + }, + "response": { + "body": "ScoringLogic.js" + } + }, + { + "request": { + "path": "scoring/trusted" + }, + "response": { + "body": "ScoringSignals.json" + } + }, + { + "request": { + "path": "bidding/trusted" + }, + "response": { + "body": "BiddingSignals.json" + } + }, + { + "request": { + "path": "bidding/daily/shoes" + }, + "response": { + "body": "InvalidJson.json" + }, + "verify_called": true + }, + { + "request": { + "path": "buyer/reportImpression" + }, + "response": { + "body": null + } + }, + { + "request": { + "path": "seller/reportImpression" + }, + "response": { + "body": null + } + } + ], + "substitutions": { + "<daily-update-uri>": "{base_url}/bidding/daily", + "<ad-render-uri-1>": "{base_url}/render_ad/1", + "<ad-render-uri-2>": "{base_url}/render_ad/2", + "<buyer-trusted-bidding-uri>": "{base_url}/bidding/trusted", + "<buyer-reporting-uri>": "{base_url}/buyer/reportImpression", + "<seller-reporting-uri>": "{base_url}/buyer/reportImpression" + } +}
\ No newline at end of file diff --git a/adservices/tests/cts/assets/scenarios/remarketing-cuj-053.json b/adservices/tests/cts/assets/scenarios/remarketing-cuj-053.json new file mode 100644 index 000000000..fe53db76f --- /dev/null +++ b/adservices/tests/cts/assets/scenarios/remarketing-cuj-053.json @@ -0,0 +1,71 @@ +{ + "mocks": [ + { + "request": { + "path": "bidding" + }, + "response": { + "body": "BiddingLogicV2.js" + }, + "verify_called": true + }, + { + "request": { + "path": "scoring" + }, + "response": { + "body": "ScoringLogic.js", + "delay_sec": 30 + } + }, + { + "request": { + "path": "scoring/trusted" + }, + "response": { + "body": "ScoringSignals.json" + } + }, + { + "request": { + "path": "bidding/trusted" + }, + "response": { + "body": "BiddingSignals.json" + }, + "verify_called": true + }, + { + "request": { + "path": "bidding/daily" + }, + "response": { + "body": "DailyUpdateResponse.json" + } + }, + { + "request": { + "path": "buyer/reportImpression" + }, + "response": { + "body": null + } + }, + { + "request": { + "path": "seller/reportImpression" + }, + "response": { + "body": null + } + } + ], + "substitutions": { + "<daily-update-uri>": "{base_url}/bidding/daily", + "<ad-render-uri-1>": "{base_url}/render_ad/1", + "<ad-render-uri-2>": "{base_url}/render_ad/2", + "<buyer-trusted-bidding-uri>": "{base_url}/bidding/trusted", + "<buyer-reporting-uri>": "{base_url}/buyer/reportImpression", + "<seller-reporting-uri>": "{base_url}/buyer/reportImpression" + } +}
\ No newline at end of file diff --git a/adservices/tests/cts/cobalt/Android.bp b/adservices/tests/cts/cobalt/Android.bp index 613eb3f36..3825b828a 100644 --- a/adservices/tests/cts/cobalt/Android.bp +++ b/adservices/tests/cts/cobalt/Android.bp @@ -34,7 +34,8 @@ android_test { test_suites: [ "cts", "general-tests", - "mts-adservices" + "mts-adservices", + "mcts-adservices", ], sdk_version: "module_current", min_sdk_version: "Tiramisu", @@ -61,11 +62,12 @@ android_test { test_suites: [ "cts", "general-tests", - "mts-extservices" + "mts-extservices", + "mcts-extservices", ], sdk_version: "module_current", min_sdk_version: "31", max_sdk_version: "32", test_config: "AndroidTest.ExtServices.xml", manifest: "AndroidManifestExtServices.xml", -} +}
\ No newline at end of file diff --git a/adservices/tests/cts/endtoends/measurement/Android.bp b/adservices/tests/cts/endtoends/measurement/Android.bp index 3b9d0c450..75631bcd9 100644 --- a/adservices/tests/cts/endtoends/measurement/Android.bp +++ b/adservices/tests/cts/endtoends/measurement/Android.bp @@ -32,7 +32,8 @@ android_test { test_suites: [ "cts", "general-tests", - "mts-adservices" + "mts-adservices", + "mcts-adservices", ], // To fix Pre-submit warning, suggested by farivar@ javacflags: ["-parameters"], @@ -57,7 +58,8 @@ android_test { test_suites: [ "cts", "general-tests", - "mts-extservices" + "mts-extservices", + "mcts-extservices", ], javacflags: ["-parameters"], sdk_version: "module_current", diff --git a/adservices/tests/cts/endtoends/permissions/appoptout/Android.bp b/adservices/tests/cts/endtoends/permissions/appoptout/Android.bp index 3c14e003f..6aef11243 100644 --- a/adservices/tests/cts/endtoends/permissions/appoptout/Android.bp +++ b/adservices/tests/cts/endtoends/permissions/appoptout/Android.bp @@ -41,7 +41,8 @@ android_test { test_suites: [ "cts", "general-tests", - "mts-adservices" + "mts-adservices", + "mcts-adservices", ], min_sdk_version: "33", manifest: "AndroidManifest.xml", @@ -73,10 +74,11 @@ android_test { "cts", "general-tests", "mts-extservices", + "mcts-extservices", ], sdk_version: "module_current", min_sdk_version: "31", max_sdk_version: "32", test_config: "AndroidTest.ExtServices.xml", manifest: "AndroidManifestExtServices.xml", -} +}
\ No newline at end of file diff --git a/adservices/tests/cts/endtoends/permissions/noperm/Android.bp b/adservices/tests/cts/endtoends/permissions/noperm/Android.bp index 11698d01e..026ab5116 100644 --- a/adservices/tests/cts/endtoends/permissions/noperm/Android.bp +++ b/adservices/tests/cts/endtoends/permissions/noperm/Android.bp @@ -37,7 +37,8 @@ android_test { test_suites: [ "cts", "general-tests", - "mts-adservices" + "mts-adservices", + "mcts-adservices", ], min_sdk_version: "33", manifest: "AndroidManifest.xml", @@ -65,10 +66,11 @@ android_test { "cts", "general-tests", "mts-extservices", + "mcts-extservices", ], sdk_version: "module_current", min_sdk_version: "31", max_sdk_version: "32", test_config: "AndroidTest.ExtServices.xml", manifest: "AndroidManifestExtServices.xml", -} +}
\ No newline at end of file diff --git a/adservices/tests/cts/endtoends/permissions/notallowed/Android.bp b/adservices/tests/cts/endtoends/permissions/notallowed/Android.bp index d6c6815db..5f3c3eb08 100644 --- a/adservices/tests/cts/endtoends/permissions/notallowed/Android.bp +++ b/adservices/tests/cts/endtoends/permissions/notallowed/Android.bp @@ -37,7 +37,8 @@ android_test { test_suites: [ "cts", "general-tests", - "mts-adservices" + "mts-adservices", + "mcts-adservices", ], min_sdk_version: "33", manifest: "AndroidManifest.xml", @@ -66,7 +67,8 @@ android_test { test_suites: [ "cts", "general-tests", - "mts-extservices" + "mts-extservices", + "mcts-extservices", ], sdk_version: "module_current", min_sdk_version: "31", diff --git a/adservices/tests/cts/endtoends/permissions/valid/Android.bp b/adservices/tests/cts/endtoends/permissions/valid/Android.bp index e4eb9d560..6e12318e9 100644 --- a/adservices/tests/cts/endtoends/permissions/valid/Android.bp +++ b/adservices/tests/cts/endtoends/permissions/valid/Android.bp @@ -41,7 +41,8 @@ android_test { test_suites: [ "cts", "general-tests", - "mts-adservices" + "mts-adservices", + "mcts-adservices", ], min_sdk_version: "33", manifest: "AndroidManifest.xml", @@ -74,10 +75,11 @@ android_test { "cts", "general-tests", "mts-extservices", + "mcts-extservices", ], sdk_version: "module_current", min_sdk_version: "31", max_sdk_version: "32", test_config: "AndroidTest.ExtServices.xml", manifest: "AndroidManifestExtServices.xml", -} +}
\ No newline at end of file diff --git a/adservices/tests/cts/endtoends/topics/Android.bp b/adservices/tests/cts/endtoends/topics/Android.bp index 1f70ae2d9..f74b78aa6 100644 --- a/adservices/tests/cts/endtoends/topics/Android.bp +++ b/adservices/tests/cts/endtoends/topics/Android.bp @@ -42,7 +42,8 @@ android_test { test_suites: [ "cts", "general-tests", - "mts-adservices" + "mts-adservices", + "mcts-adservices", ], min_sdk_version: "Tiramisu", manifest: "CtsAdServicesEndToEndTestsManifest.xml", @@ -56,10 +57,10 @@ android_test { "cts", "general-tests", "mts-extservices", + "mcts-extservices", ], min_sdk_version: "30", max_sdk_version: "32", manifest: "CtsAdExtServicesEndToEndTestsManifest.xml", test_config: "CtsAdExtServicesEndToEndTests.xml", } - diff --git a/adservices/tests/cts/endtoends/topics/appupdate/Android.bp b/adservices/tests/cts/endtoends/topics/appupdate/Android.bp index 3f10fcd70..a57aac3ca 100644 --- a/adservices/tests/cts/endtoends/topics/appupdate/Android.bp +++ b/adservices/tests/cts/endtoends/topics/appupdate/Android.bp @@ -36,7 +36,8 @@ android_test { test_suites: [ "cts", "general-tests", - "mts-adservices" + "mts-adservices", + "mcts-adservices", ], data: [ // Sample App will be installed and uninstalled in the test. @@ -68,6 +69,7 @@ android_test { "cts", "general-tests", "mts-extservices", + "mcts-extservices", ], data: [ // Sample App will be installed and uninstalled in the test. @@ -78,4 +80,4 @@ android_test { max_sdk_version: "32", test_config: "AndroidTest.ExtServices.xml", manifest: "AndroidManifestExtServices.xml", -} +}
\ No newline at end of file diff --git a/adservices/tests/cts/endtoends/topics/connection/Android.bp b/adservices/tests/cts/endtoends/topics/connection/Android.bp index 17f20d501..ec896ecd2 100644 --- a/adservices/tests/cts/endtoends/topics/connection/Android.bp +++ b/adservices/tests/cts/endtoends/topics/connection/Android.bp @@ -36,7 +36,8 @@ android_test { test_suites: [ "cts", "general-tests", - "mts-adservices" + "mts-adservices", + "mcts-adservices", ], sdk_version: "module_current", min_sdk_version: "Tiramisu", @@ -64,10 +65,11 @@ android_test { "cts", "general-tests", "mts-extservices", + "mcts-extservices", ], sdk_version: "module_current", min_sdk_version: "31", max_sdk_version: "32", test_config: "AndroidTest.ExtServices.xml", manifest: "AndroidManifestExtServices.xml", -} +}
\ No newline at end of file diff --git a/adservices/tests/cts/endtoends/topics/mdd/Android.bp b/adservices/tests/cts/endtoends/topics/mdd/Android.bp index fde9c1592..c67c58948 100644 --- a/adservices/tests/cts/endtoends/topics/mdd/Android.bp +++ b/adservices/tests/cts/endtoends/topics/mdd/Android.bp @@ -36,7 +36,8 @@ android_test { test_suites: [ "cts", "general-tests", - "mts-adservices" + "mts-adservices", + "mcts-adservices", ], sdk_version: "module_current", min_sdk_version: "Tiramisu", @@ -64,10 +65,11 @@ android_test { "cts", "general-tests", "mts-extservices", + "mcts-extservices", ], sdk_version: "module_current", min_sdk_version: "31", max_sdk_version: "32", test_config: "AndroidTest.ExtServices.xml", manifest: "AndroidManifestExtServices.xml", -} +}
\ No newline at end of file diff --git a/adservices/tests/cts/endtoends/topics/src/com/android/adservices/tests/cts/topics/TopicsManagerTest.java b/adservices/tests/cts/endtoends/topics/src/com/android/adservices/tests/cts/topics/TopicsManagerTest.java index ab4b43272..9848c49a5 100644 --- a/adservices/tests/cts/endtoends/topics/src/com/android/adservices/tests/cts/topics/TopicsManagerTest.java +++ b/adservices/tests/cts/endtoends/topics/src/com/android/adservices/tests/cts/topics/TopicsManagerTest.java @@ -51,7 +51,7 @@ import java.util.concurrent.Executors; public final class TopicsManagerTest extends CtsTopicsEndToEndTestCase { // Test constants for testing encryption - static final String PUBLIC_KEY_BASE64 = "rSJBSUYG0ebvfW1AXCWO0CMGMJhDzpfQm3eLyw1uxX8="; + static final String TEST_PUBLIC_KEY_BASE64 = "rSJBSUYG0ebvfW1AXCWO0CMGMJhDzpfQm3eLyw1uxX8="; // The JobId of the Epoch Computation. private static final int EPOCH_JOB_ID = 2; @@ -470,6 +470,8 @@ public final class TopicsManagerTest extends CtsTopicsEndToEndTestCase { // Set flags for encryption test flags.setFlag(FlagsConstants.KEY_TOPICS_ENCRYPTION_ENABLED, true); flags.setFlag(FlagsConstants.KEY_ENABLE_DATABASE_SCHEMA_VERSION_9, true); + // Override encryption key for testing + flags.setFlag(FlagsConstants.KEY_TOPICS_TEST_ENCRYPTION_PUBLIC_KEY, TEST_PUBLIC_KEY_BASE64); // The Test App has 1 SDK: sdk6 // sdk6 calls the Topics API. @@ -519,7 +521,7 @@ public final class TopicsManagerTest extends CtsTopicsEndToEndTestCase { assertThat(sdk6Result.getEncryptedTopics()).hasSize(1); assertThat(sdk6Result.getEncryptedTopics().get(0).getEncryptedTopic()).isNotNull(); assertThat(sdk6Result.getEncryptedTopics().get(0).getKeyIdentifier()) - .isEqualTo(PUBLIC_KEY_BASE64); + .isEqualTo(TEST_PUBLIC_KEY_BASE64); assertThat(sdk6Result.getEncryptedTopics().get(0).getEncapsulatedKey()).isNotNull(); } diff --git a/adservices/tests/cts/extdata/Android.bp b/adservices/tests/cts/extdata/Android.bp index 13a5b3f90..0e68e1491 100644 --- a/adservices/tests/cts/extdata/Android.bp +++ b/adservices/tests/cts/extdata/Android.bp @@ -36,7 +36,8 @@ android_test { test_suites: [ "cts", "general-tests", - "mts-adservices" + "mts-adservices", + "mcts-adservices", ], sdk_version: "module_current", min_sdk_version: "Tiramisu", @@ -63,7 +64,8 @@ android_test { test_suites: [ "cts", "general-tests", - "mts-extservices" + "mts-extservices", + "mcts-extservices", ], sdk_version: "module_current", min_sdk_version: "30", diff --git a/adservices/tests/cts/hosttests/Android.bp b/adservices/tests/cts/hosttests/Android.bp index 4d9168f7e..b81adbf3f 100644 --- a/adservices/tests/cts/hosttests/Android.bp +++ b/adservices/tests/cts/hosttests/Android.bp @@ -25,6 +25,7 @@ java_test_host { "cts", "general-tests", "mts-adservices", + "mcts-adservices", ], libs: [ "cts-tradefed", @@ -50,6 +51,7 @@ java_test_host { "cts", "general-tests", "mts-extservices", + "mcts-extservices", ], libs: [ "cts-tradefed", diff --git a/adservices/tests/cts/hosttests/app/Android.bp b/adservices/tests/cts/hosttests/app/Android.bp index 488adf978..486a7072c 100644 --- a/adservices/tests/cts/hosttests/app/Android.bp +++ b/adservices/tests/cts/hosttests/app/Android.bp @@ -42,5 +42,6 @@ android_test_helper_app { "cts", "general-tests", "mts-adservices", + "mcts-adservices", ], } diff --git a/adservices/tests/cts/sandbox/adid/Android.bp b/adservices/tests/cts/sandbox/adid/Android.bp index 8f9d9c0cb..fadb81f05 100644 --- a/adservices/tests/cts/sandbox/adid/Android.bp +++ b/adservices/tests/cts/sandbox/adid/Android.bp @@ -42,5 +42,6 @@ android_test { "cts", "mts-adservices", "general-tests", + "mcts-adservices", ], } diff --git a/adservices/tests/cts/sandbox/appsetid/Android.bp b/adservices/tests/cts/sandbox/appsetid/Android.bp index 78bf40f23..1bafab376 100644 --- a/adservices/tests/cts/sandbox/appsetid/Android.bp +++ b/adservices/tests/cts/sandbox/appsetid/Android.bp @@ -42,5 +42,6 @@ android_test { "cts", "mts-adservices", "general-tests", + "mcts-adservices", ], } diff --git a/adservices/tests/cts/sandbox/fledge/Android.bp b/adservices/tests/cts/sandbox/fledge/Android.bp index c4a64f29f..4df33f5b0 100644 --- a/adservices/tests/cts/sandbox/fledge/Android.bp +++ b/adservices/tests/cts/sandbox/fledge/Android.bp @@ -45,5 +45,6 @@ android_test { "cts", "mts-adservices", "general-tests", + "mcts-adservices", ], } diff --git a/adservices/tests/cts/sandbox/measurement/Android.bp b/adservices/tests/cts/sandbox/measurement/Android.bp index f2b7969be..2bc189122 100644 --- a/adservices/tests/cts/sandbox/measurement/Android.bp +++ b/adservices/tests/cts/sandbox/measurement/Android.bp @@ -43,5 +43,6 @@ android_test { "cts", "mts-adservices", "general-tests", + "mcts-adservices", ], } diff --git a/adservices/tests/cts/sandbox/topics/Android.bp b/adservices/tests/cts/sandbox/topics/Android.bp index 15cf9d41c..b4602dda8 100644 --- a/adservices/tests/cts/sandbox/topics/Android.bp +++ b/adservices/tests/cts/sandbox/topics/Android.bp @@ -43,5 +43,6 @@ android_test { "cts", "mts-adservices", "general-tests", + "mcts-adservices", ], } diff --git a/adservices/tests/cts/src/android/adservices/cts/CustomAudienceApiCtsTest.java b/adservices/tests/cts/src/android/adservices/cts/CustomAudienceApiCtsTest.java index e458b0d50..99b4e4ba7 100644 --- a/adservices/tests/cts/src/android/adservices/cts/CustomAudienceApiCtsTest.java +++ b/adservices/tests/cts/src/android/adservices/cts/CustomAudienceApiCtsTest.java @@ -26,8 +26,14 @@ import static android.adservices.customaudience.CustomAudienceFixture.VALID_NAME import static android.adservices.customaudience.CustomAudienceFixture.VALID_USER_BIDDING_SIGNALS; import static android.adservices.customaudience.CustomAudienceFixture.getValidFetchUriByBuyer; -import static com.android.adservices.service.Flags.FLEDGE_CUSTOM_AUDIENCE_MAX_NAME_SIZE_B; -import static com.android.adservices.service.Flags.FLEDGE_FETCH_CUSTOM_AUDIENCE_MAX_USER_BIDDING_SIGNALS_SIZE_B; +import static com.android.adservices.service.FlagsConstants.KEY_ENABLE_ENROLLMENT_TEST_SEED; +import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_CUSTOM_AUDIENCE_MAX_COUNT; +import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_CUSTOM_AUDIENCE_MAX_NAME_SIZE_B; +import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_CUSTOM_AUDIENCE_MAX_NUM_ADS; +import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_CUSTOM_AUDIENCE_MAX_OWNER_COUNT; +import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_CUSTOM_AUDIENCE_PER_APP_MAX_COUNT; +import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_FETCH_CUSTOM_AUDIENCE_ENABLED; +import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_FETCH_CUSTOM_AUDIENCE_MAX_USER_BIDDING_SIGNALS_SIZE_B; import static com.google.common.truth.Truth.assertThat; @@ -52,16 +58,15 @@ import android.net.Uri; import android.os.Process; import android.util.Pair; +import androidx.test.filters.FlakyTest; import androidx.test.platform.app.InstrumentationRegistry; -import com.android.adservices.common.AdServicesDeviceSupportedRule; -import com.android.adservices.common.AdServicesFlagsSetterRule; import com.android.adservices.common.AdservicesTestHelper; -import com.android.adservices.common.SdkLevelSupportRule; -import com.android.adservices.service.PhFlagsFixture; +import com.android.adservices.common.RequiresSdkLevelAtLeastS; +import com.android.adservices.common.annotations.SetFlagEnabled; +import com.android.adservices.common.annotations.SetIntegerFlag; import com.android.adservices.service.devapi.DevContext; import com.android.adservices.service.devapi.DevContextFilter; -import com.android.modules.utils.build.SdkLevel; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.ListenableFuture; @@ -70,7 +75,6 @@ import com.google.common.util.concurrent.MoreExecutors; import org.junit.After; import org.junit.Assume; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; import java.util.ArrayList; @@ -78,7 +82,10 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -public class CustomAudienceApiCtsTest extends ForegroundCtsTest { +@RequiresSdkLevelAtLeastS // TODO(b/291488819) - Remove SDK Level check if Fledge is enabled on R. +@SetFlagEnabled(KEY_ENABLE_ENROLLMENT_TEST_SEED) +public final class CustomAudienceApiCtsTest extends ForegroundCtsTestCase { + private AdvertisingCustomAudienceClient mClient; private TestAdvertisingCustomAudienceClient mTestClient; @@ -93,24 +100,9 @@ public class CustomAudienceApiCtsTest extends ForegroundCtsTest { private final ArrayList<Pair<AdTechIdentifier, String>> mCustomAudiencesToCleanUp = new ArrayList<>(); - // TODO(b/291488819) - Remove SDK Level check if Fledge is enabled on R. - @Rule(order = 0) - public final SdkLevelSupportRule sdkLevel = SdkLevelSupportRule.forAtLeastS(); - - // Skip the test if it runs on unsupported platforms. - @Rule(order = 1) - public final AdServicesDeviceSupportedRule adServicesDeviceSupportedRule = - new AdServicesDeviceSupportedRule(); - - @Rule(order = 2) - public final AdServicesFlagsSetterRule flags = - AdServicesFlagsSetterRule.forGlobalKillSwitchDisabledTests() - .setCompatModeFlags() - .setPpapiAppAllowList(sContext.getPackageName()); - @Before - public void setup() throws InterruptedException { - if (SdkLevel.isAtLeastT()) { + public void setup() throws Exception { + if (sdkLevel.isAtLeastT()) { assertForegroundActivityStarted(); } @@ -131,16 +123,14 @@ public class CustomAudienceApiCtsTest extends ForegroundCtsTest { InstrumentationRegistry.getInstrumentation() .getUiAutomation() .adoptShellPermissionIdentity(Manifest.permission.WRITE_DEVICE_CONFIG); - PhFlagsFixture.overrideEnableEnrollmentSeed(true); // Kill AdServices process AdservicesTestHelper.killAdservicesProcess(sContext); } @After - public void tearDown() throws ExecutionException, InterruptedException, TimeoutException { + public void tearDown() throws Exception { leaveJoinedCustomAudiences(); - PhFlagsFixture.overrideEnableEnrollmentSeed(false); } @Test @@ -274,27 +264,23 @@ public class CustomAudienceApiCtsTest extends ForegroundCtsTest { } @Test + @SetIntegerFlag(name = KEY_FLEDGE_CUSTOM_AUDIENCE_MAX_NUM_ADS, value = 2) public void testJoinCustomAudience_invalidNumberOfAds_fail() { - PhFlagsFixture.overrideFledgeCustomAudienceMaxNumAds(2); - try { - CustomAudience customAudienceWithInvalidNumberOfAds = - CustomAudienceFixture.getValidBuilderForBuyer(VALID_BUYER_1) - .setAds( - ImmutableList.of( - AdDataFixture.getValidAdDataByBuyer(VALID_BUYER_1, 1), - AdDataFixture.getValidAdDataByBuyer(VALID_BUYER_1, 2), - AdDataFixture.getValidAdDataByBuyer(VALID_BUYER_1, 3))) - .build(); - - Exception exception = - assertThrows( - ExecutionException.class, - () -> joinCustomAudience(customAudienceWithInvalidNumberOfAds)); - assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); - assertThat(exception).hasCauseThat().hasMessageThat().isEqualTo(null); - } finally { - PhFlagsFixture.overrideFledgeCustomAudienceMaxNumAds(100); - } + CustomAudience customAudienceWithInvalidNumberOfAds = + CustomAudienceFixture.getValidBuilderForBuyer(VALID_BUYER_1) + .setAds( + ImmutableList.of( + AdDataFixture.getValidAdDataByBuyer(VALID_BUYER_1, 1), + AdDataFixture.getValidAdDataByBuyer(VALID_BUYER_1, 2), + AdDataFixture.getValidAdDataByBuyer(VALID_BUYER_1, 3))) + .build(); + + Exception exception = + assertThrows( + ExecutionException.class, + () -> joinCustomAudience(customAudienceWithInvalidNumberOfAds)); + assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); + assertThat(exception).hasCauseThat().hasMessageThat().isNull(); } @Test @@ -327,384 +313,347 @@ public class CustomAudienceApiCtsTest extends ForegroundCtsTest { } @Test + @SetIntegerFlag(name = KEY_FLEDGE_CUSTOM_AUDIENCE_MAX_COUNT, value = 2) + @SetIntegerFlag(name = KEY_FLEDGE_CUSTOM_AUDIENCE_PER_APP_MAX_COUNT, value = 1000) + @SetIntegerFlag(name = KEY_FLEDGE_CUSTOM_AUDIENCE_MAX_OWNER_COUNT, value = 1000) public void testJoinCustomAudience_maxTotalCustomAudiences_fail() { - PhFlagsFixture.overrideFledgeCustomAudienceMaxCount(2); - PhFlagsFixture.overrideFledgeCustomAudiencePerAppMaxCount(1000); - PhFlagsFixture.overrideFledgeCustomAudienceMaxOwnerCount(1000); - try { - CustomAudience customAudience1 = - CustomAudienceFixture.getValidBuilderForBuyer(VALID_BUYER_1) - .setName("CA1") - .build(); - CustomAudience customAudience2 = - CustomAudienceFixture.getValidBuilderForBuyer(VALID_BUYER_1) - .setName("CA2") - .build(); - CustomAudience customAudience3 = - CustomAudienceFixture.getValidBuilderForBuyer(VALID_BUYER_1) - .setName("CA3") - .build(); - - Exception exception = - assertThrows( - ExecutionException.class, - () -> { - joinCustomAudience(customAudience1); - joinCustomAudience(customAudience2); - joinCustomAudience(customAudience3); - }); - assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); - assertThat(exception).hasCauseThat().hasMessageThat().isEqualTo(null); - } finally { - PhFlagsFixture.overrideFledgeCustomAudienceMaxCount(4000); - } + CustomAudience customAudience1 = + CustomAudienceFixture.getValidBuilderForBuyer(VALID_BUYER_1).setName("CA1").build(); + CustomAudience customAudience2 = + CustomAudienceFixture.getValidBuilderForBuyer(VALID_BUYER_1).setName("CA2").build(); + CustomAudience customAudience3 = + CustomAudienceFixture.getValidBuilderForBuyer(VALID_BUYER_1).setName("CA3").build(); + + Exception exception = + assertThrows( + ExecutionException.class, + () -> { + joinCustomAudience(customAudience1); + joinCustomAudience(customAudience2); + joinCustomAudience(customAudience3); + }); + assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); + assertThat(exception).hasCauseThat().hasMessageThat().isNull(); } @Test + @SetIntegerFlag(name = KEY_FLEDGE_CUSTOM_AUDIENCE_MAX_COUNT, value = 4000) + @SetIntegerFlag(name = KEY_FLEDGE_CUSTOM_AUDIENCE_PER_APP_MAX_COUNT, value = 2) + @SetIntegerFlag(name = KEY_FLEDGE_CUSTOM_AUDIENCE_MAX_OWNER_COUNT, value = 1000) public void testJoinCustomAudience_maxCustomAudiencesPerApp_fail() { - PhFlagsFixture.overrideFledgeCustomAudienceMaxCount(4000); - PhFlagsFixture.overrideFledgeCustomAudiencePerAppMaxCount(2); - PhFlagsFixture.overrideFledgeCustomAudienceMaxOwnerCount(1000); - try { - CustomAudience customAudience1 = - CustomAudienceFixture.getValidBuilderForBuyer(VALID_BUYER_1) - .setName("CA1") - .build(); - CustomAudience customAudience2 = - CustomAudienceFixture.getValidBuilderForBuyer(VALID_BUYER_1) - .setName("CA2") - .build(); - CustomAudience customAudience3 = - CustomAudienceFixture.getValidBuilderForBuyer(VALID_BUYER_1) - .setName("CA3") - .build(); - - Exception exception = - assertThrows( - ExecutionException.class, - () -> { - joinCustomAudience(customAudience1); - joinCustomAudience(customAudience2); - joinCustomAudience(customAudience3); - }); - assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); - assertThat(exception).hasCauseThat().hasMessageThat().isEqualTo(null); - } finally { - PhFlagsFixture.overrideFledgeCustomAudiencePerAppMaxCount(1000); - } + CustomAudience customAudience1 = + CustomAudienceFixture.getValidBuilderForBuyer(VALID_BUYER_1).setName("CA1").build(); + CustomAudience customAudience2 = + CustomAudienceFixture.getValidBuilderForBuyer(VALID_BUYER_1).setName("CA2").build(); + CustomAudience customAudience3 = + CustomAudienceFixture.getValidBuilderForBuyer(VALID_BUYER_1).setName("CA3").build(); + + Exception exception = + assertThrows( + ExecutionException.class, + () -> { + joinCustomAudience(customAudience1); + joinCustomAudience(customAudience2); + joinCustomAudience(customAudience3); + }); + assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); + assertThat(exception).hasCauseThat().hasMessageThat().isNull(); } @Test + @FlakyTest(bugId = 319330548) public void testFetchAndJoinCustomAudience_validFetchUri_validRequest() { - try { - PhFlagsFixture.overrideFledgeFetchCustomAudienceEnabled(true); - FetchAndJoinCustomAudienceRequest request = - new FetchAndJoinCustomAudienceRequest.Builder( - getValidFetchUriByBuyer(VALID_BUYER_1)) - .build(); - - // Without an actual server to respond to this request, the service will fail while - // executing the HTTP request and throw an IllegalStateException. If a request field was - // invalid, the service will fail before executing the HTTP request and throw an - // IllegalArgumentException. - Exception exception = - assertThrows( - ExecutionException.class, - () -> fetchAndJoinCustomAudience(request, VALID_BUYER_1, VALID_NAME)); - assertThat(exception.getCause()).isInstanceOf(IllegalStateException.class); - } finally { - PhFlagsFixture.overrideFledgeFetchCustomAudienceEnabled(false); - } + // NOTE: not using flag annotations because it's called by other test + flags.setFlag(KEY_FLEDGE_FETCH_CUSTOM_AUDIENCE_ENABLED, true); + FetchAndJoinCustomAudienceRequest request = + new FetchAndJoinCustomAudienceRequest.Builder( + getValidFetchUriByBuyer(VALID_BUYER_1)) + .build(); + + // Without an actual server to respond to this request, the service will fail while + // executing the HTTP request and throw an IllegalStateException. If a request field was + // invalid, the service will fail before executing the HTTP request and throw an + // IllegalArgumentException. + Exception exception = + assertThrows( + ExecutionException.class, + () -> fetchAndJoinCustomAudience(request, VALID_BUYER_1, VALID_NAME)); + assertThat(exception.getCause()).isInstanceOf(IllegalStateException.class); } @Test + @FlakyTest(bugId = 319330548) public void testFetchAndJoinCustomAudience_validFetchUri_validRequest_getMethod() { createClientUsingGetMethod(); testFetchAndJoinCustomAudience_validFetchUri_validRequest(); } @Test + @FlakyTest(bugId = 319330548) public void testFetchAndJoinCustomAudience_unenrolledFetchUri_invalidRequest() { - try { - PhFlagsFixture.overrideFledgeFetchCustomAudienceEnabled(true); - - FetchAndJoinCustomAudienceRequest request = - new FetchAndJoinCustomAudienceRequest.Builder(Uri.parse("invalid-uri.com")) - .build(); - - // Without an actual server to respond to this request, the service will fail while - // executing the HTTP request and throw an IllegalStateException. If a request field was - // invalid, the service will fail before executing the HTTP request and throw an - // IllegalArgumentException. - Exception exception = - assertThrows( - ExecutionException.class, - () -> fetchAndJoinCustomAudience(request, VALID_BUYER_1, VALID_NAME)); - // A valid buyer will not be extracted from an invalid uri, thus failing due to lack of - // authorization. - assertThat(exception).hasCauseThat().isInstanceOf(SecurityException.class); - assertThat(exception) - .hasCauseThat() - .hasMessageThat() - .isEqualTo(SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_ERROR_MESSAGE); - } finally { - PhFlagsFixture.overrideFledgeFetchCustomAudienceEnabled(false); - } + // NOTE: not using flag annotations because it's called by other test + flags.setFlag(KEY_FLEDGE_FETCH_CUSTOM_AUDIENCE_ENABLED, true); + FetchAndJoinCustomAudienceRequest request = + new FetchAndJoinCustomAudienceRequest.Builder(Uri.parse("invalid-uri.com")).build(); + + // Without an actual server to respond to this request, the service will fail while + // executing the HTTP request and throw an IllegalStateException. If a request field was + // invalid, the service will fail before executing the HTTP request and throw an + // IllegalArgumentException. + Exception exception = + assertThrows( + ExecutionException.class, + () -> fetchAndJoinCustomAudience(request, VALID_BUYER_1, VALID_NAME)); + // A valid buyer will not be extracted from an invalid uri, thus failing due to lack of + // authorization. + assertThat(exception).hasCauseThat().isInstanceOf(SecurityException.class); + assertThat(exception) + .hasCauseThat() + .hasMessageThat() + .isEqualTo(SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_ERROR_MESSAGE); } @Test + @FlakyTest(bugId = 319330548) public void testFetchAndJoinCustomAudience_unenrolledFetchUri_invalidRequest_getMethod() { createClientUsingGetMethod(); testFetchAndJoinCustomAudience_unenrolledFetchUri_invalidRequest(); } @Test + @FlakyTest(bugId = 319330548) public void testFetchAndJoinCustomAudience_validName_validRequest() { - try { - PhFlagsFixture.overrideFledgeFetchCustomAudienceEnabled(true); - - FetchAndJoinCustomAudienceRequest request = - new FetchAndJoinCustomAudienceRequest.Builder( - getValidFetchUriByBuyer(VALID_BUYER_1)) - .setName(VALID_NAME) - .build(); - - // Without an actual server to respond to this request, the service will fail while - // executing the HTTP request and throw an IllegalStateException. If a request field was - // invalid, the service will fail before executing the HTTP request and throw an - // IllegalArgumentException. - Exception exception = - assertThrows( - ExecutionException.class, - () -> fetchAndJoinCustomAudience(request, VALID_BUYER_1, VALID_NAME)); - assertThat(exception.getCause()).isInstanceOf(IllegalStateException.class); - } finally { - PhFlagsFixture.overrideFledgeFetchCustomAudienceEnabled(false); - } + // NOTE: not using flag annotations because it's called by other test + flags.setFlag(KEY_FLEDGE_FETCH_CUSTOM_AUDIENCE_ENABLED, true); + FetchAndJoinCustomAudienceRequest request = + new FetchAndJoinCustomAudienceRequest.Builder( + getValidFetchUriByBuyer(VALID_BUYER_1)) + .setName(VALID_NAME) + .build(); + + // Without an actual server to respond to this request, the service will fail while + // executing the HTTP request and throw an IllegalStateException. If a request field was + // invalid, the service will fail before executing the HTTP request and throw an + // IllegalArgumentException. + Exception exception = + assertThrows( + ExecutionException.class, + () -> fetchAndJoinCustomAudience(request, VALID_BUYER_1, VALID_NAME)); + assertThat(exception.getCause()).isInstanceOf(IllegalStateException.class); } @Test + @FlakyTest(bugId = 319330548) public void testFetchAndJoinCustomAudience_validName_validRequest_getMethod() { createClientUsingGetMethod(); testFetchAndJoinCustomAudience_validName_validRequest(); } @Test + @FlakyTest(bugId = 319330548) public void testFetchAndJoinCustomAudience_tooLongName_invalidRequest() { - try { - PhFlagsFixture.overrideFledgeFetchCustomAudienceEnabled(true); - // Use a clearly small size limit. - PhFlagsFixture.overrideFledgeCustomAudienceMaxNameSizeB(1); - FetchAndJoinCustomAudienceRequest request = - new FetchAndJoinCustomAudienceRequest.Builder( - getValidFetchUriByBuyer(VALID_BUYER_1)) - .setName(VALID_NAME) - .build(); - - // Without an actual server to respond to this request, the service will fail while - // executing the HTTP request and throw an IllegalStateException. If a request field was - // invalid, the service will fail before executing the HTTP request and throw an - // IllegalArgumentException. - Exception exception = - assertThrows( - ExecutionException.class, - () -> fetchAndJoinCustomAudience(request, VALID_BUYER_1, VALID_NAME)); - // The name exceeds size limit. - assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); - } finally { - PhFlagsFixture.overrideFledgeFetchCustomAudienceEnabled(false); - PhFlagsFixture.overrideFledgeCustomAudienceMaxNameSizeB( - FLEDGE_CUSTOM_AUDIENCE_MAX_NAME_SIZE_B); - } + // NOTE: not using flag annotations because it's called by other test + flags.setFlag(KEY_FLEDGE_FETCH_CUSTOM_AUDIENCE_ENABLED, true); + // Use a clearly small size limit. + flags.setFlag(KEY_FLEDGE_CUSTOM_AUDIENCE_MAX_NAME_SIZE_B, 1); + FetchAndJoinCustomAudienceRequest request = + new FetchAndJoinCustomAudienceRequest.Builder( + getValidFetchUriByBuyer(VALID_BUYER_1)) + .setName(VALID_NAME) + .build(); + + // Without an actual server to respond to this request, the service will fail while + // executing the HTTP request and throw an IllegalStateException. If a request field was + // invalid, the service will fail before executing the HTTP request and throw an + // IllegalArgumentException. + Exception exception = + assertThrows( + ExecutionException.class, + () -> fetchAndJoinCustomAudience(request, VALID_BUYER_1, VALID_NAME)); + // The name exceeds size limit. + assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); } @Test + @FlakyTest(bugId = 319330548) public void testFetchAndJoinCustomAudience_tooLongName_invalidRequest_getMethod() { createClientUsingGetMethod(); testFetchAndJoinCustomAudience_tooLongName_invalidRequest(); } @Test + @FlakyTest(bugId = 319330548) public void testFetchAndJoinCustomAudience_validActivationTime_validRequest() { - try { - PhFlagsFixture.overrideFledgeFetchCustomAudienceEnabled(true); - FetchAndJoinCustomAudienceRequest request = - new FetchAndJoinCustomAudienceRequest.Builder( - getValidFetchUriByBuyer(VALID_BUYER_1)) - .setActivationTime(VALID_ACTIVATION_TIME) - .build(); - - // Without an actual server to respond to this request, the service will fail while - // executing the HTTP request and throw an IllegalStateException. If a request field was - // invalid, the service will fail before executing the HTTP request and throw an - // IllegalArgumentException. - Exception exception = - assertThrows( - ExecutionException.class, - () -> fetchAndJoinCustomAudience(request, VALID_BUYER_1, VALID_NAME)); - assertThat(exception.getCause()).isInstanceOf(IllegalStateException.class); - } finally { - PhFlagsFixture.overrideFledgeFetchCustomAudienceEnabled(false); - } + // NOTE: not using flag annotations because it's called by other test + flags.setFlag(KEY_FLEDGE_FETCH_CUSTOM_AUDIENCE_ENABLED, true); + + FetchAndJoinCustomAudienceRequest request = + new FetchAndJoinCustomAudienceRequest.Builder( + getValidFetchUriByBuyer(VALID_BUYER_1)) + .setActivationTime(VALID_ACTIVATION_TIME) + .build(); + + // Without an actual server to respond to this request, the service will fail while + // executing the HTTP request and throw an IllegalStateException. If a request field was + // invalid, the service will fail before executing the HTTP request and throw an + // IllegalArgumentException. + Exception exception = + assertThrows( + ExecutionException.class, + () -> fetchAndJoinCustomAudience(request, VALID_BUYER_1, VALID_NAME)); + assertThat(exception.getCause()).isInstanceOf(IllegalStateException.class); } @Test + @FlakyTest(bugId = 319330548) public void testFetchAndJoinCustomAudience_validActivationTime_validRequest_getMethod() { createClientUsingGetMethod(); testFetchAndJoinCustomAudience_validActivationTime_validRequest(); } @Test + @FlakyTest(bugId = 319330548) public void testFetchAndJoinCustomAudience_activationExceedsDelay_invalidRequest() { - try { - PhFlagsFixture.overrideFledgeFetchCustomAudienceEnabled(true); - FetchAndJoinCustomAudienceRequest request = - new FetchAndJoinCustomAudienceRequest.Builder( - getValidFetchUriByBuyer(VALID_BUYER_1)) - .setActivationTime(INVALID_DELAYED_ACTIVATION_TIME) - .build(); - - // Without an actual server to respond to this request, the service will fail while - // executing the HTTP request and throw an IllegalStateException. If a request field was - // invalid, the service will fail before executing the HTTP request and throw an - // IllegalArgumentException. - Exception exception = - assertThrows( - ExecutionException.class, - () -> fetchAndJoinCustomAudience(request, VALID_BUYER_1, VALID_NAME)); - // The activation time exceeds delay limit. - assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); - } finally { - PhFlagsFixture.overrideFledgeFetchCustomAudienceEnabled(false); - } + // NOTE: not using flag annotations because it's called by other test + flags.setFlag(KEY_FLEDGE_FETCH_CUSTOM_AUDIENCE_ENABLED, true); + FetchAndJoinCustomAudienceRequest request = + new FetchAndJoinCustomAudienceRequest.Builder( + getValidFetchUriByBuyer(VALID_BUYER_1)) + .setActivationTime(INVALID_DELAYED_ACTIVATION_TIME) + .build(); + + // Without an actual server to respond to this request, the service will fail while + // executing the HTTP request and throw an IllegalStateException. If a request field was + // invalid, the service will fail before executing the HTTP request and throw an + // IllegalArgumentException. + Exception exception = + assertThrows( + ExecutionException.class, + () -> fetchAndJoinCustomAudience(request, VALID_BUYER_1, VALID_NAME)); + // The activation time exceeds delay limit. + assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); } @Test + @FlakyTest(bugId = 319330548) public void testFetchAndJoinCustomAudience_activationExceedsDelay_invalidRequest_getMethod() { createClientUsingGetMethod(); testFetchAndJoinCustomAudience_activationExceedsDelay_invalidRequest(); } @Test + @FlakyTest(bugId = 319330548) public void testFetchAndJoinCustomAudience_validExpirationTime_validRequest() { - try { - PhFlagsFixture.overrideFledgeFetchCustomAudienceEnabled(true); - FetchAndJoinCustomAudienceRequest request = - new FetchAndJoinCustomAudienceRequest.Builder( - getValidFetchUriByBuyer(VALID_BUYER_1)) - .setExpirationTime(VALID_EXPIRATION_TIME) - .build(); - - // Without an actual server to respond to this request, the service will fail while - // executing the HTTP request and throw an IllegalStateException. If a request field was - // invalid, the service will fail before executing the HTTP request and throw an - // IllegalArgumentException. - Exception exception = - assertThrows( - ExecutionException.class, - () -> fetchAndJoinCustomAudience(request, VALID_BUYER_1, VALID_NAME)); - assertThat(exception.getCause()).isInstanceOf(IllegalStateException.class); - } finally { - PhFlagsFixture.overrideFledgeFetchCustomAudienceEnabled(false); - } + // NOTE: not using flag annotations because it's called by other test + flags.setFlag(KEY_FLEDGE_FETCH_CUSTOM_AUDIENCE_ENABLED, true); + FetchAndJoinCustomAudienceRequest request = + new FetchAndJoinCustomAudienceRequest.Builder( + getValidFetchUriByBuyer(VALID_BUYER_1)) + .setExpirationTime(VALID_EXPIRATION_TIME) + .build(); + + // Without an actual server to respond to this request, the service will fail while + // executing the HTTP request and throw an IllegalStateException. If a request field was + // invalid, the service will fail before executing the HTTP request and throw an + // IllegalArgumentException. + Exception exception = + assertThrows( + ExecutionException.class, + () -> fetchAndJoinCustomAudience(request, VALID_BUYER_1, VALID_NAME)); + assertThat(exception.getCause()).isInstanceOf(IllegalStateException.class); } @Test + @FlakyTest(bugId = 319330548) public void testFetchAndJoinCustomAudience_validExpirationTime_validRequest_getMethod() { createClientUsingGetMethod(); testFetchAndJoinCustomAudience_validExpirationTime_validRequest(); } @Test + @FlakyTest(bugId = 319330548) public void testFetchAndJoinCustomAudience_beyondMaxExpiration_invalidRequest() { - try { - PhFlagsFixture.overrideFledgeFetchCustomAudienceEnabled(true); - - FetchAndJoinCustomAudienceRequest request = - new FetchAndJoinCustomAudienceRequest.Builder( - getValidFetchUriByBuyer(VALID_BUYER_1)) - .setExpirationTime(INVALID_BEYOND_MAX_EXPIRATION_TIME) - .build(); - - // Without an actual server to respond to this request, the service will fail while - // executing the HTTP request and throw an IllegalStateException. If a request field was - // invalid, the service will fail before executing the HTTP request and throw an - // IllegalArgumentException. - Exception exception = - assertThrows( - ExecutionException.class, - () -> fetchAndJoinCustomAudience(request, VALID_BUYER_1, VALID_NAME)); - // The expiration time exceeds max limit. - assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); - } finally { - PhFlagsFixture.overrideFledgeFetchCustomAudienceEnabled(false); - } + // NOTE: not using flag annotations because it's called by other test + flags.setFlag(KEY_FLEDGE_FETCH_CUSTOM_AUDIENCE_ENABLED, true); + FetchAndJoinCustomAudienceRequest request = + new FetchAndJoinCustomAudienceRequest.Builder( + getValidFetchUriByBuyer(VALID_BUYER_1)) + .setExpirationTime(INVALID_BEYOND_MAX_EXPIRATION_TIME) + .build(); + + // Without an actual server to respond to this request, the service will fail while + // executing the HTTP request and throw an IllegalStateException. If a request field was + // invalid, the service will fail before executing the HTTP request and throw an + // IllegalArgumentException. + Exception exception = + assertThrows( + ExecutionException.class, + () -> fetchAndJoinCustomAudience(request, VALID_BUYER_1, VALID_NAME)); + // The expiration time exceeds max limit. + assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); } @Test + @FlakyTest(bugId = 319330548) public void testFetchAndJoinCustomAudience_beyondMaxExpiration_invalidRequest_getMethod() { createClientUsingGetMethod(); testFetchAndJoinCustomAudience_beyondMaxExpiration_invalidRequest(); } @Test + @FlakyTest(bugId = 319330548) public void testFetchAndJoinCustomAudience_validUserBiddingSignals_validRequest() { - try { - PhFlagsFixture.overrideFledgeFetchCustomAudienceEnabled(true); - FetchAndJoinCustomAudienceRequest request = - new FetchAndJoinCustomAudienceRequest.Builder( - getValidFetchUriByBuyer(VALID_BUYER_1)) - .setUserBiddingSignals(VALID_USER_BIDDING_SIGNALS) - .build(); - - // Without an actual server to respond to this request, the service will fail while - // executing the HTTP request and throw an IllegalStateException. If a request field was - // invalid, the service will fail before executing the HTTP request and throw an - // IllegalArgumentException. - Exception exception = - assertThrows( - ExecutionException.class, - () -> fetchAndJoinCustomAudience(request, VALID_BUYER_1, VALID_NAME)); - assertThat(exception.getCause()).isInstanceOf(IllegalStateException.class); - } finally { - PhFlagsFixture.overrideFledgeFetchCustomAudienceEnabled(false); - } + // NOTE: not using flag annotations because it's called by other test + flags.setFlag(KEY_FLEDGE_FETCH_CUSTOM_AUDIENCE_ENABLED, true); + FetchAndJoinCustomAudienceRequest request = + new FetchAndJoinCustomAudienceRequest.Builder( + getValidFetchUriByBuyer(VALID_BUYER_1)) + .setUserBiddingSignals(VALID_USER_BIDDING_SIGNALS) + .build(); + + // Without an actual server to respond to this request, the service will fail while + // executing the HTTP request and throw an IllegalStateException. If a request field was + // invalid, the service will fail before executing the HTTP request and throw an + // IllegalArgumentException. + Exception exception = + assertThrows( + ExecutionException.class, + () -> fetchAndJoinCustomAudience(request, VALID_BUYER_1, VALID_NAME)); + assertThat(exception.getCause()).isInstanceOf(IllegalStateException.class); } @Test + @FlakyTest(bugId = 319330548) public void testFetchAndJoinCustomAudience_validUserBiddingSignals_validRequest_getMethod() { createClientUsingGetMethod(); testFetchAndJoinCustomAudience_validUserBiddingSignals_validRequest(); } @Test + @FlakyTest(bugId = 319330548) public void testFetchAndJoinCustomAudience_tooBigUserBiddingSignals_invalidRequest() { - try { - PhFlagsFixture.overrideFledgeFetchCustomAudienceEnabled(true); - // Use a clearly small size limit. - PhFlagsFixture.overrideFledgeFetchCustomAudienceMaxUserBiddingSignalsSizeB(1); - FetchAndJoinCustomAudienceRequest request = - new FetchAndJoinCustomAudienceRequest.Builder( - getValidFetchUriByBuyer(VALID_BUYER_1)) - .setUserBiddingSignals(VALID_USER_BIDDING_SIGNALS) - .build(); - - // Without an actual server response, we expect an IllegalStateException if the request - // was well-formed and valid. - Exception exception = - assertThrows( - ExecutionException.class, - () -> fetchAndJoinCustomAudience(request, VALID_BUYER_1, VALID_NAME)); - // The user bidding signals exceeds size limit. - assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); - } finally { - PhFlagsFixture.overrideFledgeFetchCustomAudienceEnabled(false); - PhFlagsFixture.overrideFledgeFetchCustomAudienceMaxUserBiddingSignalsSizeB( - FLEDGE_FETCH_CUSTOM_AUDIENCE_MAX_USER_BIDDING_SIGNALS_SIZE_B); - } + // NOTE: not using flag annotations because it's called by other test + flags.setFlag(KEY_FLEDGE_FETCH_CUSTOM_AUDIENCE_ENABLED, true); + // Use a clearly small size limit. + flags.setFlag(KEY_FLEDGE_FETCH_CUSTOM_AUDIENCE_MAX_USER_BIDDING_SIGNALS_SIZE_B, 1); + FetchAndJoinCustomAudienceRequest request = + new FetchAndJoinCustomAudienceRequest.Builder( + getValidFetchUriByBuyer(VALID_BUYER_1)) + .setUserBiddingSignals(VALID_USER_BIDDING_SIGNALS) + .build(); + + // Without an actual server response, we expect an IllegalStateException if the request + // was well-formed and valid. + Exception exception = + assertThrows( + ExecutionException.class, + () -> fetchAndJoinCustomAudience(request, VALID_BUYER_1, VALID_NAME)); + // The user bidding signals exceeds size limit. + assertThat(exception).hasCauseThat().isInstanceOf(IllegalArgumentException.class); } @Test + @FlakyTest(bugId = 319330548) public void testFetchAndJoinCustomAudience_tooBigUserBiddingSignals_invalidRequest_getMethod() { createClientUsingGetMethod(); testFetchAndJoinCustomAudience_tooBigUserBiddingSignals_invalidRequest(); diff --git a/adservices/tests/cts/src/android/adservices/cts/ForegroundCtsTest.java b/adservices/tests/cts/src/android/adservices/cts/ForegroundCtsTestCase.java index 6aeb4be79..7fd36268c 100644 --- a/adservices/tests/cts/src/android/adservices/cts/ForegroundCtsTest.java +++ b/adservices/tests/cts/src/android/adservices/cts/ForegroundCtsTestCase.java @@ -16,11 +16,10 @@ package android.adservices.cts; -import static org.junit.Assert.assertTrue; +import static com.google.common.truth.Truth.assertWithMessage; -import android.content.Context; - -import androidx.test.core.app.ApplicationProvider; +import android.os.Build; +import android.util.Log; import com.android.modules.utils.build.SdkLevel; @@ -30,13 +29,17 @@ import org.junit.BeforeClass; import java.time.Duration; import java.util.concurrent.TimeoutException; -public class ForegroundCtsTest { - // If the context is initialized in the setup method the importance of our foreground - // service will be IMPORTANCE_FOREGROUND_SERVICE (125) instead of +abstract class ForegroundCtsTestCase extends CtsAdServicesDeviceTestCase { + + private static final String TAG = ForegroundCtsTestCase.class.getSimpleName(); + + // NOTICE: if the context used by tests is initialized in the setup method the importance of our + // foreground service will be IMPORTANCE_FOREGROUND_SERVICE (125) instead of // IMPORTANCE_FOREGROUND (100) on some platforms only. - // See http://ag/c/platform/packages/modules/AdServices/+/19607471/comments/e6767fdc_971415d0 - protected static final Context sContext = ApplicationProvider.getApplicationContext(); - private static boolean sSimpleActivityStarted = false; + // This class is indirectly extending AdServicesCtsTestCase - which sets sContext outside any + // JUnit @Before / @BeforeClass method - so the process has the proper importance. + + private static boolean sSimpleActivityStarted; /** * Starts a foreground activity to make the test process a foreground one to pass PPAPI and SDK @@ -45,15 +48,22 @@ public class ForegroundCtsTest { protected static void makeTestProcessForeground() throws TimeoutException { // PPAPI foreground checks are not done on S-, so no need for the SimpleActivity if (SdkLevel.isAtLeastT()) { + Log.d(TAG, "Starting activity on T+ (and waiting for 2s)"); SimpleActivity.startAndWait(sContext, Duration.ofSeconds(2)); + Log.d(TAG, "Activity started"); sSimpleActivityStarted = true; + } else { + Log.d(TAG, "Not starting activity on device running " + Build.VERSION.SDK_INT); } } /** Terminates the SimpleActivity */ protected static void shutdownForegroundActivity() { if (SdkLevel.isAtLeastT()) { + Log.d(TAG, "Stopping activity on T+"); SimpleActivity.stop(sContext); + } else { + Log.d(TAG, "Not stopping activity on device running " + Build.VERSION.SDK_INT); } } @@ -67,7 +77,9 @@ public class ForegroundCtsTest { shutdownForegroundActivity(); } - protected void assertForegroundActivityStarted() { - assertTrue("Foreground activity didn't start successfully", sSimpleActivityStarted); + protected static void assertForegroundActivityStarted() { + assertWithMessage("Foreground activity started successfully") + .that(sSimpleActivityStarted) + .isTrue(); } } diff --git a/adservices/tests/cts/src/android/adservices/cts/TestAdSelectionManagerTest.java b/adservices/tests/cts/src/android/adservices/cts/TestAdSelectionManagerTest.java index 380665aa9..4fe1ffa77 100644 --- a/adservices/tests/cts/src/android/adservices/cts/TestAdSelectionManagerTest.java +++ b/adservices/tests/cts/src/android/adservices/cts/TestAdSelectionManagerTest.java @@ -46,19 +46,17 @@ import android.os.Process; import androidx.test.platform.app.InstrumentationRegistry; import com.android.adservices.LoggerFactory; -import com.android.adservices.common.AdServicesDeviceSupportedRule; -import com.android.adservices.common.AdServicesFlagsSetterRule; import com.android.adservices.common.AdservicesTestHelper; -import com.android.adservices.common.SdkLevelSupportRule; +import com.android.adservices.common.RequiresSdkLevelAtLeastS; +import com.android.adservices.common.annotations.SetFlagDisabled; +import com.android.adservices.common.annotations.SetFlagEnabled; import com.android.adservices.service.devapi.DevContext; import com.android.adservices.service.devapi.DevContextFilter; -import com.android.modules.utils.build.SdkLevel; import com.google.common.util.concurrent.ListenableFuture; import org.junit.Assume; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; import java.util.ArrayList; @@ -67,7 +65,12 @@ import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -public class TestAdSelectionManagerTest extends ForegroundCtsTest { +@RequiresSdkLevelAtLeastS // TODO(b/291488819) - Remove SDK Level check if Fledge is enabled on R. +@SetFlagEnabled(KEY_ENABLE_ENROLLMENT_TEST_SEED) +@SetFlagDisabled(KEY_ENFORCE_ISOLATE_MAX_HEAP_SIZE) +@SetFlagDisabled(KEY_ISOLATE_MAX_HEAP_SIZE_BYTES) +public final class TestAdSelectionManagerTest extends ForegroundCtsTestCase { + private static final LoggerFactory.Logger sLogger = LoggerFactory.getFledgeLogger(); private static final Executor CALLBACK_EXECUTOR = Executors.newCachedThreadPool(); @@ -100,27 +103,10 @@ public class TestAdSelectionManagerTest extends ForegroundCtsTest { private TestAdSelectionClient mTestAdSelectionClient; private boolean mIsDebugMode; - // TODO(b/291488819) - Remove SDK Level check if Fledge is enabled on R. - @Rule(order = 0) - public final SdkLevelSupportRule sdkLevel = SdkLevelSupportRule.forAtLeastS(); - - // Skip the test if it runs on unsupported platforms. - @Rule(order = 1) - public final AdServicesDeviceSupportedRule adServicesDeviceSupportedRule = - new AdServicesDeviceSupportedRule(); - - @Rule(order = 2) - public final AdServicesFlagsSetterRule flags = - AdServicesFlagsSetterRule.forGlobalKillSwitchDisabledTests() - .setCompatModeFlags() - .setPpapiAppAllowList(sContext.getPackageName()) - .setFlag(KEY_ENFORCE_ISOLATE_MAX_HEAP_SIZE, false) - .setFlag(KEY_ISOLATE_MAX_HEAP_SIZE_BYTES, false) - .setFlag(KEY_ENABLE_ENROLLMENT_TEST_SEED, true); - @Before public void setup() { - if (SdkLevel.isAtLeastT()) { + + if (sdkLevel.isAtLeastT()) { assertForegroundActivityStarted(); } diff --git a/adservices/tests/cts/src/android/adservices/debuggablects/AdSelectionTest.java b/adservices/tests/cts/src/android/adservices/debuggablects/AdSelectionTest.java index 686882ee3..1fb43a294 100644 --- a/adservices/tests/cts/src/android/adservices/debuggablects/AdSelectionTest.java +++ b/adservices/tests/cts/src/android/adservices/debuggablects/AdSelectionTest.java @@ -49,6 +49,7 @@ import java.util.List; import java.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class AdSelectionTest extends FledgeScenarioTest { @@ -228,17 +229,17 @@ public class AdSelectionTest extends FledgeScenarioTest { ScenarioDispatcher.fromScenario( "scenarios/remarketing-cuj-default.json", getCacheBusterPrefix()); setupDefaultMockWebServer(dispatcher); + AdSelectionConfig config = makeAdSelectionConfig(); CustomAudience customAudience = makeCustomAudience(SHOES_CA) - .setExpirationTime(Instant.now().plus(1, ChronoUnit.SECONDS)) + .setExpirationTime(Instant.now().plus(5, ChronoUnit.SECONDS)) .build(); - AdSelectionConfig config = makeAdSelectionConfig(); - mCustomAudienceClient.joinCustomAudience(customAudience).get(1, TimeUnit.SECONDS); + mCustomAudienceClient.joinCustomAudience(customAudience).get(5, TimeUnit.SECONDS); Log.d(TAG, "Joined custom audience"); // Make a call to verify ad selection succeeds before timing out. mAdSelectionClient.selectAds(config).get(TIMEOUT, TimeUnit.SECONDS); - Thread.sleep(4000); + Thread.sleep(7000); Exception selectAdsException = assertThrows( @@ -361,6 +362,32 @@ public class AdSelectionTest extends FledgeScenarioTest { .containsAtLeastElementsIn(dispatcher.getVerifyCalledPaths()); } + @Test + public void testAdSelection_withHighLatencyBackend_doesNotWinAuction() throws Exception { + ScenarioDispatcher dispatcher = + ScenarioDispatcher.fromScenario( + "scenarios/remarketing-cuj-053.json", getCacheBusterPrefix()); + setupDefaultMockWebServer(dispatcher); + AdSelectionConfig config = makeAdSelectionConfig(); + + try { + joinCustomAudience(SHIRTS_CA); + Exception selectAdsException = + assertThrows( + ExecutionException.class, + () -> + mAdSelectionClient + .selectAds(config) + .get(TIMEOUT, TimeUnit.SECONDS)); + assertThat(selectAdsException.getCause()).isInstanceOf(TimeoutException.class); + } finally { + leaveCustomAudience(SHIRTS_CA); + } + + assertThat(dispatcher.getCalledPaths()) + .containsAtLeastElementsIn(dispatcher.getVerifyCalledPaths()); + } + private boolean isAdIdSupported() { AdIdCompatibleManager adIdCompatibleManager; AdServicesOutcomeReceiverForTests<AdId> callback = diff --git a/adservices/tests/cts/src/android/adservices/debuggablects/FledgeCtsDebuggableTest.java b/adservices/tests/cts/src/android/adservices/debuggablects/FledgeCtsDebuggableTest.java index 7813cf309..48dddaa92 100644 --- a/adservices/tests/cts/src/android/adservices/debuggablects/FledgeCtsDebuggableTest.java +++ b/adservices/tests/cts/src/android/adservices/debuggablects/FledgeCtsDebuggableTest.java @@ -41,18 +41,23 @@ import android.adservices.adselection.AdSelectionFromOutcomesConfigFixture; import android.adservices.adselection.AdSelectionOutcome; import android.adservices.adselection.AddAdSelectionFromOutcomesOverrideRequest; import android.adservices.adselection.AddAdSelectionOverrideRequest; +import android.adservices.adselection.BuyersDecisionLogic; +import android.adservices.adselection.DecisionLogic; import android.adservices.adselection.GetAdSelectionDataOutcome; import android.adservices.adselection.GetAdSelectionDataRequest; import android.adservices.adselection.PersistAdSelectionResultRequest; import android.adservices.adselection.ReportEventRequest; import android.adservices.adselection.ReportImpressionRequest; import android.adservices.adselection.SetAppInstallAdvertisersRequest; +import android.adservices.adselection.SignedContextualAds; +import android.adservices.adselection.SignedContextualAdsFixture; import android.adservices.adselection.UpdateAdCounterHistogramRequest; import android.adservices.clients.adselection.AdSelectionClient; import android.adservices.clients.adselection.TestAdSelectionClient; import android.adservices.clients.customaudience.AdvertisingCustomAudienceClient; import android.adservices.clients.customaudience.TestAdvertisingCustomAudienceClient; import android.adservices.common.AdData; +import android.adservices.common.AdDataFixture; import android.adservices.common.AdFilters; import android.adservices.common.AdSelectionSignals; import android.adservices.common.AdTechIdentifier; @@ -85,6 +90,7 @@ import com.android.adservices.service.devapi.DevContextFilter; import com.android.modules.utils.build.SdkLevel; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.io.BaseEncoding; import com.google.common.util.concurrent.ListenableFuture; @@ -104,8 +110,10 @@ import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -907,21 +915,21 @@ public class FledgeCtsDebuggableTest extends ForegroundDebuggableCtsTest { .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); } - /* - // TODO(b/267712947) Unhide Contextual Ad flow with App Install API changes @Test public void testFledgeSelectionFlow_WithContextualAds_Success() throws Exception { Assume.assumeTrue(mAccessStatus, mHasAccessToDevOverrides); + PhFlagsFixture.overrideFledgeAdSelectionFilteringEnabled(true); + PhFlagsFixture.overrideFledgeEnrollmentCheck(false); + List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2); List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0); CustomAudience customAudience1 = createCustomAudience(BUYER_1, bidsForBuyer1); - CustomAudience customAudience2 = createCustomAudience(BUYER_2, bidsForBuyer2); // Joining custom audiences, no result to do assertion on. Failures will generate an - // exception." + // exception. joinCustomAudience(customAudience1); joinCustomAudience(customAudience2); @@ -938,10 +946,12 @@ public class FledgeCtsDebuggableTest extends ForegroundDebuggableCtsTest { ); // Adding AdSelection override, no result to do assertion on. Failures will generate an - // exception." + // exception. AddAdSelectionOverrideRequest addAdSelectionOverrideRequest = new AddAdSelectionOverrideRequest( - AD_SELECTION_CONFIG, DEFAULT_DECISION_LOGIC_JS, TRUSTED_SCORING_SIGNALS, + AD_SELECTION_CONFIG, + DEFAULT_DECISION_LOGIC_JS, + TRUSTED_SCORING_SIGNALS, buyersDecisionLogic); mTestAdSelectionClient @@ -964,7 +974,7 @@ public class FledgeCtsDebuggableTest extends ForegroundDebuggableCtsTest { .build(); // Adding Custom audience override, no result to do assertion on. Failures will generate an - // exception." + // exception. mTestCustomAudienceClient .overrideCustomAudienceRemoteInfo(addCustomAudienceOverrideRequest1) .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); @@ -995,7 +1005,7 @@ public class FledgeCtsDebuggableTest extends ForegroundDebuggableCtsTest { "https://%s%s", AdSelectionConfigFixture.SELLER, SELLER_TRUSTED_SIGNAL_URI_PATH))) - .setBuyerContextualAds(createContextualAds()) + .setBuyerSignedContextualAds(createAuthenticatedContextualAds()) .build(); // Running ad selection and asserting that the outcome is returned in < 10 seconds AdSelectionOutcome outcome = @@ -1022,6 +1032,9 @@ public class FledgeCtsDebuggableTest extends ForegroundDebuggableCtsTest { public void testFledgeSelectionFlow_OnlyContextualAds_Success() throws Exception { Assume.assumeTrue(mAccessStatus, mHasAccessToDevOverrides); + PhFlagsFixture.overrideFledgeAdSelectionFilteringEnabled(true); + PhFlagsFixture.overrideFledgeEnrollmentCheck(false); + AdSelectionConfig adSelectionConfigOnlyContextualAds = AdSelectionConfigFixture.anAdSelectionConfigBuilder() // Adding no buyers in config @@ -1038,7 +1051,7 @@ public class FledgeCtsDebuggableTest extends ForegroundDebuggableCtsTest { "https://%s%s", AdSelectionConfigFixture.SELLER, SELLER_TRUSTED_SIGNAL_URI_PATH))) - .setBuyerContextualAds(createContextualAds()) + .setBuyerSignedContextualAds(createAuthenticatedContextualAds()) .build(); BuyersDecisionLogic buyersDecisionLogic = @@ -1054,7 +1067,7 @@ public class FledgeCtsDebuggableTest extends ForegroundDebuggableCtsTest { ); // Adding AdSelection override, no result to do assertion on. Failures will generate an - // exception." + // exception. AddAdSelectionOverrideRequest addAdSelectionOverrideRequest = new AddAdSelectionOverrideRequest( adSelectionConfigOnlyContextualAds, DEFAULT_DECISION_LOGIC_JS, @@ -1093,7 +1106,6 @@ public class FledgeCtsDebuggableTest extends ForegroundDebuggableCtsTest { .reportImpression(reportImpressionRequest) .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); } - */ @Test public void testFledgeAuctionSelectionFlow_overall_register_ad_beacon_Success() @@ -4400,28 +4412,24 @@ public class FledgeCtsDebuggableTest extends ForegroundDebuggableCtsTest { .build(); } - /* - // TODO(b/267712947) Unhisde Contextual Ad flow with App Install API changes - private Map<AdTechIdentifier, ContextualAds> createContextualAds() { - Map<AdTechIdentifier, ContextualAds> buyerContextualAds = new HashMap<>(); + private Map<AdTechIdentifier, SignedContextualAds> createAuthenticatedContextualAds() { + Map<AdTechIdentifier, SignedContextualAds> buyerContextualAds = new HashMap<>(); AdTechIdentifier buyer1 = CommonFixture.VALID_BUYER_1; - ContextualAds contextualAds1 = - ContextualAdsFixture.generateContextualAds( - buyer1, ImmutableList.of(100.0, 200.0, 300.0)) - .build(); + SignedContextualAds contextualAds1 = + SignedContextualAdsFixture.aSignedContextualAds( + buyer1, ImmutableList.of(100.0, 200.0, 300.0)); AdTechIdentifier buyer2 = CommonFixture.VALID_BUYER_2; - ContextualAds contextualAds2 = - ContextualAdsFixture.generateContextualAds(buyer2, ImmutableList.of(400.0, 500.0)) - .build(); + SignedContextualAds contextualAds2 = + SignedContextualAdsFixture.aSignedContextualAds( + buyer2, ImmutableList.of(400.0, 500.0)); buyerContextualAds.put(buyer1, contextualAds1); buyerContextualAds.put(buyer2, contextualAds2); return buyerContextualAds; } - */ private void joinCustomAudience(CustomAudience customAudience) throws ExecutionException, InterruptedException, TimeoutException { diff --git a/adservices/tests/cts/src/android/adservices/debuggablects/MeasurementCtsDebuggableTest.java b/adservices/tests/cts/src/android/adservices/debuggablects/MeasurementCtsDebuggableTest.java index 5f893c176..0e9e8934f 100644 --- a/adservices/tests/cts/src/android/adservices/debuggablects/MeasurementCtsDebuggableTest.java +++ b/adservices/tests/cts/src/android/adservices/debuggablects/MeasurementCtsDebuggableTest.java @@ -659,10 +659,6 @@ public class MeasurementCtsDebuggableTest { // Assume trigger registration can happen within 8 seconds of source registration. getUiDevice().executeShellCommand( "device_config put adservices " - + "measurement_enable_configurable_event_reporting_windows true"); - - getUiDevice().executeShellCommand( - "device_config put adservices " + "measurement_event_reports_vtc_early_reporting_windows 8,15"); getUiDevice().executeShellCommand( @@ -729,11 +725,6 @@ public class MeasurementCtsDebuggableTest { + "measurement_aggregation_coordinator_path null"); // Reset reporting windows - // Assume trigger registration can happen within 8 seconds of source registration. - getUiDevice().executeShellCommand( - "device_config put adservices " - + "measurement_enable_configurable_event_reporting_windows null"); - getUiDevice().executeShellCommand( "device_config put adservices " + "measurement_event_reports_vtc_early_reporting_windows null"); diff --git a/adservices/tests/cts/src/android/adservices/rootcts/CustomAudienceBackgroundFetchTest.java b/adservices/tests/cts/src/android/adservices/rootcts/CustomAudienceBackgroundFetchTest.java index 829132176..4edcc0259 100644 --- a/adservices/tests/cts/src/android/adservices/rootcts/CustomAudienceBackgroundFetchTest.java +++ b/adservices/tests/cts/src/android/adservices/rootcts/CustomAudienceBackgroundFetchTest.java @@ -73,6 +73,32 @@ public class CustomAudienceBackgroundFetchTest extends FledgeScenarioTest { } /** + * Test to ensure that trusted signals are not updated during the daily update if the ads are + * not syntactically valid. + */ + @Test + public void testAdSelection_withInvalidAds_backgroundJobUpdateFails() throws Exception { + ScenarioDispatcher dispatcher = + ScenarioDispatcher.fromScenario( + "scenarios/remarketing-cuj-034.json", getCacheBusterPrefix()); + setupDefaultMockWebServer(dispatcher); + AdSelectionConfig adSelectionConfig = makeAdSelectionConfig(); + + try { + joinCustomAudience(makeCustomAudience(CA_NAME).setAds(List.of()).build()); + assertThrows(ExecutionException.class, () -> doSelectAds(adSelectionConfig)); + assertThat(mBackgroundJobHelper.runJob(FLEDGE_BACKGROUND_FETCH_JOB.getJobId())) + .isTrue(); + assertThrows(ExecutionException.class, () -> doSelectAds(adSelectionConfig)); + } finally { + leaveCustomAudience(CA_NAME); + } + + assertThat(dispatcher.getCalledPaths()) + .containsAtLeastElementsIn(dispatcher.getVerifyCalledPaths()); + } + + /** * Test to ensure that trusted signals are not updated if a daily update server response exceeds * the 30-second timeout. */ diff --git a/adservices/tests/cts/ui/gaux/alreadyenrolledchannel/Android.bp b/adservices/tests/cts/ui/gaux/alreadyenrolledchannel/Android.bp index 62a762d8b..bbb0c9294 100644 --- a/adservices/tests/cts/ui/gaux/alreadyenrolledchannel/Android.bp +++ b/adservices/tests/cts/ui/gaux/alreadyenrolledchannel/Android.bp @@ -19,7 +19,7 @@ package { android_test { name: "AdServicesGaUxAlreadyEnrolledChannelTest", srcs: [ - "src/**/*.java", + "src/com/android/adservices/tests/ui/gaux/alreadyenrolledchannel/GaUxAlreadyEnrolledChannelTest.java", ], static_libs: [ "adservices-assets", @@ -41,3 +41,61 @@ android_test { sdk_version: "module_current", min_sdk_version: "Tiramisu", } + +android_test { + name: "AdExtServicesGaUxAlreadyEnrolledChannelRowTest", + srcs: [ + "src/com/android/adservices/tests/ui/gaux/alreadyenrolledchannel/ExtGaUxAlreadyEnrolledChannelRowTest.java", + ], + static_libs: [ + "adservices-assets", + "adservices-service-core", + "compatibility-device-util-axt", + "truth", + "adservices-test-utility", + "adservices-ui-cts-root-test-lib", + "platform-test-rules", + ], + libs: [ + "android.test.base", + "framework-adservices-lib", + ], + test_suites: [ + "cts_root", + "general-tests", + ], + sdk_version: "module_current", + max_sdk_version: "32", + min_sdk_version: "31", + test_config: "AndroidTest.ExtServices.Row.xml", + manifest: "AndroidManifestExtServices.xml", +} + +android_test { + name: "AdExtServicesGaUxAlreadyEnrolledChannelEuTest", + srcs: [ + "src/com/android/adservices/tests/ui/gaux/alreadyenrolledchannel/ExtGaUxAlreadyEnrolledChannelEuTest.java", + ], + static_libs: [ + "adservices-assets", + "adservices-service-core", + "compatibility-device-util-axt", + "truth", + "adservices-test-utility", + "adservices-ui-cts-root-test-lib", + "platform-test-rules", + ], + libs: [ + "android.test.base", + "framework-adservices-lib", + ], + test_suites: [ + "cts_root", + "general-tests", + ], + sdk_version: "module_current", + max_sdk_version: "32", + min_sdk_version: "31", + test_config: "AndroidTest.ExtServices.Eu.xml", + manifest: "AndroidManifestExtServices.xml", +} diff --git a/adservices/tests/cts/ui/gaux/alreadyenrolledchannel/AndroidManifestExtServices.xml b/adservices/tests/cts/ui/gaux/alreadyenrolledchannel/AndroidManifestExtServices.xml new file mode 100644 index 000000000..16ebe068d --- /dev/null +++ b/adservices/tests/cts/ui/gaux/alreadyenrolledchannel/AndroidManifestExtServices.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.adservices.tests.ui.gaux.alredyenrolledchannel"> + + + <application> + <!-- Used and present only on S- --> + <uses-library android:name="com.google.android.ext.services" android:required="false"/> + + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.google.android.ext.services"> + </instrumentation> +</manifest> diff --git a/adservices/tests/cts/ui/gaux/alreadyenrolledchannel/AndroidTest.ExtServices.Eu.xml b/adservices/tests/cts/ui/gaux/alreadyenrolledchannel/AndroidTest.ExtServices.Eu.xml new file mode 100644 index 000000000..9526af3ed --- /dev/null +++ b/adservices/tests/cts/ui/gaux/alreadyenrolledchannel/AndroidTest.ExtServices.Eu.xml @@ -0,0 +1,114 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 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. + --> +<configuration description="TF Config for AdServices Ui Cts test"> + + <!-- For this test suite to get picked up in presubmit --> + <option name="config-descriptor:metadata" key="mainline-param" + value="com.google.android.extservices.apex" /> + + <!-- Prevent test from running on Android T+ --> + <object type="module_controller" + class="com.android.tradefed.testtype.suite.module.MaxSdkModuleController"> + <option name="max-sdk-level" value="32"/> + </object> + + <!-- Prevent tests from running on Android R- --> + <object type="module_controller" + class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController"/> + + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" value="device_config set_sync_disabled_for_tests persistent"/> + <option name="teardown-command" value="device_config set_sync_disabled_for_tests none"/> + + <option name="run-command" value="setprop log.tag.adservices VERBOSE" /> + <option + name="run-command" + value="device_config put adservices consent_notification_activity_debug_mode false"/> + <option + name="run-command" + value="device_config put adservices ui_ota_strings_feature_enabled false"/> + <option + name="run-command" + value="device_config put adservices is_eea_device_feature_enabled true"/> + <option + name="run-command" + value="device_config put adservices is_eea_device true"/> + <option + name="run-command" + value="device_config put adservices consent_already_interacted_fix_enable false"/> + <option + name="run-command" + value="device_config put adservices ga_ux_enabled true"/> + <option name="run-command" value="device_config put adservices enable_back_compat true"/> + <option name="run-command" value="device_config put adservices global_kill_switch false"/> + <option + name="run-command" + value="device_config put adservices adservice_enable_status true"/> + <option + name="run-command" + value="device_config put adservices adservice_enabled true"/> + <option + name="run-command" + value="device_config put ui_ota_strings_feature_enabled false"/> + <option + name="run-command" + value="device_config put adservices enable_ad_services_system_api true"/> + <option + name="run-command" + value="device_config put adservices eu_notif_flow_change_enabled false"/> + <option + name="run-command" + value="device_config put adservices consent_notification_debug_mode false"/> + <option + name="run-command" + value="device_config put adservices consent_notification_activity_debug_mode false"/> + <!-- override scheduling params so the test is unaffected by time of the day --> + <option + name="run-command" + value="device_config put adservices consent_notification_interval_begin_ms 0"/> + <!-- end of day (24 hours) --> + <option + name="run-command" + value="device_config put adservices consent_notification_interval_end_ms 86400000"/> + <option + name="run-command" + value="device_config put adservices consent_notification_minimal_delay_before_interval_ends 0"/> + </target_preparer> + + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="test-file-name" value="AdExtServicesGaUxAlreadyEnrolledChannelEuTest.apk"/> + </target_preparer> + + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="directory-keys" value="/sdcard/Documents/adservices-tests" /> + <option name="clean-up" value="false" /> + <option name="collect-on-run-ended-only" value="true" /> + </metrics_collector> + + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="directory-keys" value="/data/system/files" /> + <option name="clean-up" value="false" /> + <option name="collect-on-run-ended-only" value="true" /> + </metrics_collector> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.adservices.tests.ui.gaux.alredyenrolledchannel"/> + </test> +</configuration> diff --git a/adservices/tests/cts/ui/gaux/alreadyenrolledchannel/AndroidTest.ExtServices.Row.xml b/adservices/tests/cts/ui/gaux/alreadyenrolledchannel/AndroidTest.ExtServices.Row.xml new file mode 100644 index 000000000..4cf2eca01 --- /dev/null +++ b/adservices/tests/cts/ui/gaux/alreadyenrolledchannel/AndroidTest.ExtServices.Row.xml @@ -0,0 +1,114 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 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. + --> +<configuration description="TF Config for AdServices Ui Cts test"> + + <!-- For this test suite to get picked up in presubmit --> + <option name="config-descriptor:metadata" key="mainline-param" + value="com.google.android.extservices.apex" /> + + <!-- Prevent test from running on Android T+ --> + <object type="module_controller" + class="com.android.tradefed.testtype.suite.module.MaxSdkModuleController"> + <option name="max-sdk-level" value="32"/> + </object> + + <!-- Prevent tests from running on Android R- --> + <object type="module_controller" + class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController"/> + + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" value="device_config set_sync_disabled_for_tests persistent"/> + <option name="teardown-command" value="device_config set_sync_disabled_for_tests none"/> + + <option name="run-command" value="setprop log.tag.adservices VERBOSE" /> + <option + name="run-command" + value="device_config put adservices consent_notification_activity_debug_mode false"/> + <option + name="run-command" + value="device_config put adservices ui_ota_strings_feature_enabled false"/> + <option name="run-command" value="device_config put adservices global_kill_switch false"/> + <option + name="run-command" + value="device_config put adservices adservice_enable_status true"/> + <option + name="run-command" + value="device_config put adservices adservice_enabled true"/> + <option + name="run-command" + value="device_config put ui_ota_strings_feature_enabled false"/> + <option + name="run-command" + value="device_config put adservices enable_ad_services_system_api true"/> + <option + name="run-command" + value="device_config put adservices eu_notif_flow_change_enabled false"/> + <option + name="run-command" + value="device_config put adservices consent_notification_debug_mode false"/> + <option + name="run-command" + value="device_config put adservices is_eea_device_feature_enabled true"/> + <option + name="run-command" + value="device_config put adservices is_eea_device false"/> + <option + name="run-command" + value="device_config put adservices consent_already_interacted_fix_enable false"/> + <option + name="run-command" + value="device_config put adservices ga_ux_enabled true"/> + <option name="run-command" value="device_config put adservices enable_back_compat true"/> + <option + name="run-command" + value="device_config put adservices consent_notification_activity_debug_mode false"/> + <!-- override scheduling params so the test is unaffected by time of the day --> + <option + name="run-command" + value="device_config put adservices consent_notification_interval_begin_ms 0"/> + <!-- end of day (24 hours) --> + <option + name="run-command" + value="device_config put adservices consent_notification_interval_end_ms 86400000"/> + <option + name="run-command" + value="device_config put adservices consent_notification_minimal_delay_before_interval_ends 0"/> + </target_preparer> + + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="test-file-name" value="AdExtServicesGaUxAlreadyEnrolledChannelRowTest.apk"/> + </target_preparer> + + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="directory-keys" value="/sdcard/Documents/adservices-tests" /> + <option name="clean-up" value="false" /> + <option name="collect-on-run-ended-only" value="true" /> + </metrics_collector> + + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="directory-keys" value="/data/system/files" /> + <option name="clean-up" value="false" /> + <option name="collect-on-run-ended-only" value="true" /> + </metrics_collector> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.adservices.tests.ui.gaux.alredyenrolledchannel"/> + </test> +</configuration> diff --git a/adservices/tests/cts/ui/gaux/alreadyenrolledchannel/src/com/android/adservices/tests/ui/gaux/alreadyenrolledchannel/ExtGaUxAlreadyEnrolledChannelEuTest.java b/adservices/tests/cts/ui/gaux/alreadyenrolledchannel/src/com/android/adservices/tests/ui/gaux/alreadyenrolledchannel/ExtGaUxAlreadyEnrolledChannelEuTest.java new file mode 100644 index 000000000..0aa6ad2cd --- /dev/null +++ b/adservices/tests/cts/ui/gaux/alreadyenrolledchannel/src/com/android/adservices/tests/ui/gaux/alreadyenrolledchannel/ExtGaUxAlreadyEnrolledChannelEuTest.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2023 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.tests.ui.gaux.alreadyenrolledchannel; + +import static com.google.common.truth.Truth.assertThat; + +import android.adservices.common.AdServicesCommonManager; +import android.adservices.common.AdServicesStates; +import android.content.Context; +import android.os.OutcomeReceiver; +import android.platform.test.rule.ScreenRecordRule; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.UiDevice; + +import com.android.adservices.common.AdservicesTestHelper; +import com.android.adservices.tests.ui.libs.AdservicesWorkflows; +import com.android.adservices.tests.ui.libs.UiConstants.UX; +import com.android.adservices.tests.ui.libs.UiUtils; + +import com.google.common.util.concurrent.SettableFuture; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.Executors; + +/** Test for verifying user consent notification trigger behaviors. */ +@RunWith(AndroidJUnit4.class) +@ScreenRecordRule.ScreenRecord +public class ExtGaUxAlreadyEnrolledChannelEuTest { + + private AdServicesCommonManager mCommonManager; + + private UiDevice mDevice; + + private String mTestName; + + private OutcomeReceiver<Boolean, Exception> mCallback; + + private static final Context sContext = + InstrumentationRegistry.getInstrumentation().getContext(); + + @Rule public final ScreenRecordRule sScreenRecordRule = new ScreenRecordRule(); + + @Before + public void setUp() throws Exception { + Assume.assumeTrue(AdservicesTestHelper.isDeviceSupported()); + + UiUtils.resetAdServicesConsentData(sContext); + + UiUtils.enableNotificationPermission(); + UiUtils.enableGa(); + UiUtils.disableNotificationFlowV2(); + UiUtils.disableOtaStrings(); + + mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + + mCommonManager = AdServicesCommonManager.get(sContext); + + // General purpose callback used for expected success calls. + mCallback = + new OutcomeReceiver<Boolean, Exception>() { + @Override + public void onResult(Boolean result) { + assertThat(result).isTrue(); + } + + @Override + public void onError(Exception exception) { + Assert.fail(); + } + }; + + // Reset consent and thereby AdServices data before each test. + UiUtils.refreshConsentResetToken(); + + SettableFuture<Boolean> responseFuture = SettableFuture.create(); + + mCommonManager.enableAdServices( + new AdServicesStates.Builder() + .setAdIdEnabled(true) + .setAdultAccount(true) + .setPrivacySandboxUiEnabled(true) + .build(), + Executors.newCachedThreadPool(), + new OutcomeReceiver<Boolean, Exception>() { + @Override + public void onResult(Boolean result) { + responseFuture.set(result); + } + + @Override + public void onError(Exception exception) { + responseFuture.setException(exception); + } + }); + + Boolean response = responseFuture.get(); + assertThat(response).isTrue(); + + mDevice.pressHome(); + } + + @After + public void tearDown() throws Exception { + if (!AdservicesTestHelper.isDeviceSupported()) return; + + UiUtils.takeScreenshot(mDevice, getClass().getSimpleName() + "_" + mTestName + "_"); + } + + /** + * Verify that for GA, EU devices with non zeroed-out AdId, the GA EU notification is displayed. + */ + @Test + public void testGaEuAdIdEnabled() throws Exception { + mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); + + AdServicesStates adServicesStates = + new AdServicesStates.Builder() + .setAdIdEnabled(true) + .setAdultAccount(true) + .setPrivacySandboxUiEnabled(true) + .build(); + + mCommonManager.enableAdServices( + adServicesStates, Executors.newCachedThreadPool(), mCallback); + + AdservicesWorkflows.verifyNotification( + sContext, mDevice, /* isDisplayed */ true, /* isEuTest */ true, UX.GA_UX, true); + + // Notifications should not be shown twice. + mCommonManager.enableAdServices( + adServicesStates, Executors.newCachedThreadPool(), mCallback); + + AdservicesWorkflows.verifyNotification( + sContext, mDevice, /* isDisplayed */ false, /* isEuTest */ true, UX.GA_UX, true); + } + + /** Verify that for GA, EU devices with zeroed-out AdId, the EU notification is displayed. */ + @Test + public void testGaEuAdIdDisabled() throws Exception { + mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); + + UiUtils.setAsEuDevice(); + + AdServicesStates adServicesStates = + new AdServicesStates.Builder() + .setAdIdEnabled(false) + .setAdultAccount(true) + .setPrivacySandboxUiEnabled(true) + .build(); + + mCommonManager.enableAdServices( + adServicesStates, Executors.newCachedThreadPool(), mCallback); + + AdservicesWorkflows.verifyNotification( + sContext, mDevice, /* isDisplayed */ true, /* isEuTest */ true, UX.GA_UX, true); + + // Notifications should not be shown twice. + mCommonManager.enableAdServices( + adServicesStates, Executors.newCachedThreadPool(), mCallback); + + AdservicesWorkflows.verifyNotification( + sContext, mDevice, /* isDisplayed */ false, /* isEuTest */ true, UX.GA_UX, true); + } +} diff --git a/adservices/tests/cts/ui/gaux/alreadyenrolledchannel/src/com/android/adservices/tests/ui/gaux/alreadyenrolledchannel/ExtGaUxAlreadyEnrolledChannelRowTest.java b/adservices/tests/cts/ui/gaux/alreadyenrolledchannel/src/com/android/adservices/tests/ui/gaux/alreadyenrolledchannel/ExtGaUxAlreadyEnrolledChannelRowTest.java new file mode 100644 index 000000000..9688ac720 --- /dev/null +++ b/adservices/tests/cts/ui/gaux/alreadyenrolledchannel/src/com/android/adservices/tests/ui/gaux/alreadyenrolledchannel/ExtGaUxAlreadyEnrolledChannelRowTest.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2023 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.tests.ui.gaux.alreadyenrolledchannel; + +import static com.google.common.truth.Truth.assertThat; + +import android.adservices.common.AdServicesCommonManager; +import android.adservices.common.AdServicesStates; +import android.content.Context; +import android.os.OutcomeReceiver; +import android.platform.test.rule.ScreenRecordRule; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.UiDevice; + +import com.android.adservices.common.AdservicesTestHelper; +import com.android.adservices.tests.ui.libs.AdservicesWorkflows; +import com.android.adservices.tests.ui.libs.UiConstants.UX; +import com.android.adservices.tests.ui.libs.UiUtils; + +import com.google.common.util.concurrent.SettableFuture; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.Executors; + +/** Test for verifying user consent notification trigger behaviors. */ +@RunWith(AndroidJUnit4.class) +@ScreenRecordRule.ScreenRecord +public class ExtGaUxAlreadyEnrolledChannelRowTest { + + private AdServicesCommonManager mCommonManager; + + private UiDevice mDevice; + + private String mTestName; + + private OutcomeReceiver<Boolean, Exception> mCallback; + + private static final Context sContext = + InstrumentationRegistry.getInstrumentation().getContext(); + + @Rule public final ScreenRecordRule sScreenRecordRule = new ScreenRecordRule(); + + @Before + public void setUp() throws Exception { + Assume.assumeTrue(AdservicesTestHelper.isDeviceSupported()); + + UiUtils.resetAdServicesConsentData(sContext); + + UiUtils.enableNotificationPermission(); + UiUtils.enableGa(); + UiUtils.disableNotificationFlowV2(); + UiUtils.disableOtaStrings(); + + mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + + mCommonManager = AdServicesCommonManager.get(sContext); + + // General purpose callback used for expected success calls. + mCallback = + new OutcomeReceiver<Boolean, Exception>() { + @Override + public void onResult(Boolean result) { + assertThat(result).isTrue(); + } + + @Override + public void onError(Exception exception) { + Assert.fail(); + } + }; + + // Reset consent and thereby AdServices data before each test. + UiUtils.refreshConsentResetToken(); + + SettableFuture<Boolean> responseFuture = SettableFuture.create(); + + mCommonManager.enableAdServices( + new AdServicesStates.Builder() + .setAdIdEnabled(true) + .setAdultAccount(true) + .setPrivacySandboxUiEnabled(true) + .build(), + Executors.newCachedThreadPool(), + new OutcomeReceiver<Boolean, Exception>() { + @Override + public void onResult(Boolean result) { + responseFuture.set(result); + } + + @Override + public void onError(Exception exception) { + responseFuture.setException(exception); + } + }); + + Boolean response = responseFuture.get(); + assertThat(response).isTrue(); + + mDevice.pressHome(); + } + + @After + public void tearDown() throws Exception { + if (!AdservicesTestHelper.isDeviceSupported()) return; + + UiUtils.takeScreenshot(mDevice, getClass().getSimpleName() + "_" + mTestName + "_"); + } + + /** + * Verify that for GA, ROW devices with non zeroed-out AdId, the GA ROW notification is + * displayed. + */ + @Test + public void testGaRowAdIdEnabled() throws Exception { + mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); + + AdServicesStates adServicesStates = + new AdServicesStates.Builder() + .setAdIdEnabled(true) + .setAdultAccount(true) + .setPrivacySandboxUiEnabled(true) + .build(); + + mCommonManager.enableAdServices( + adServicesStates, Executors.newCachedThreadPool(), mCallback); + + AdservicesWorkflows.verifyNotification( + sContext, mDevice, /* isDisplayed */ true, /* isEuTest */ false, UX.GA_UX, true); + + // Notifications should not be shown twice. + mCommonManager.enableAdServices( + adServicesStates, Executors.newCachedThreadPool(), mCallback); + + AdservicesWorkflows.verifyNotification( + sContext, mDevice, /* isDisplayed */ false, /* isEuTest */ false, UX.GA_UX, true); + } + + /** + * Verify that for GA, ROW devices with zeroed-out AdId, the GA EU notification is displayed. + */ + @Test + public void testGaRowAdIdDisabled() throws Exception { + mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); + + UiUtils.setAsRowDevice(); + + AdServicesStates adServicesStates = + new AdServicesStates.Builder() + .setAdIdEnabled(false) + .setAdultAccount(true) + .setPrivacySandboxUiEnabled(true) + .build(); + + mCommonManager.enableAdServices( + adServicesStates, Executors.newCachedThreadPool(), mCallback); + + AdservicesWorkflows.verifyNotification( + sContext, mDevice, /* isDisplayed */ true, /* isEuTest */ true, UX.GA_UX, true); + + // Notifications should not be shown twice. + mCommonManager.enableAdServices( + adServicesStates, Executors.newCachedThreadPool(), mCallback); + + AdservicesWorkflows.verifyNotification( + sContext, mDevice, /* isDisplayed */ false, /* isEuTest */ true, UX.GA_UX, true); + } +} diff --git a/adservices/tests/cts/ui/gaux/debugchannel/Android.bp b/adservices/tests/cts/ui/gaux/debugchannel/Android.bp index 08b4ac67c..0162c03b3 100644 --- a/adservices/tests/cts/ui/gaux/debugchannel/Android.bp +++ b/adservices/tests/cts/ui/gaux/debugchannel/Android.bp @@ -19,7 +19,7 @@ package { android_test { name: "AdServicesGaUxDebugChannelCtsRootTest", srcs: [ - "src/**/*.java", + "src/com/android/adservices/tests/ui/gaux/debugchannel/GaUxDebugChannelTest.java", ], static_libs: [ "adservices-assets", @@ -41,3 +41,90 @@ android_test { sdk_version: "module_current", min_sdk_version: "Tiramisu", } + +android_test { + name: "AdExtServicesGaUxDebugChannelCtsRootEuTest", + srcs: [ + "src/com/android/adservices/tests/ui/gaux/debugchannel/ExtGaUxDebugChannelEuTest.java", + ], + static_libs: [ + "adservices-assets", + "adservices-service-core", + "compatibility-device-util-axt", + "truth", + "adservices-test-utility", + "adservices-ui-cts-root-test-lib", + "platform-test-rules", + ], + libs: [ + "android.test.base", + "framework-adservices-lib", + ], + test_suites: [ + "cts_root", + "general-tests", + ], + sdk_version: "module_current", + max_sdk_version: "32", + min_sdk_version: "31", + test_config: "AndroidTest.ExtServices.Eu.xml", + manifest: "AndroidManifestExtServices.xml", +} + +android_test { + name: "AdExtServicesGaUxDebugChannelCtsRootRowTest", + srcs: [ + "src/com/android/adservices/tests/ui/gaux/debugchannel/ExtGaUxDebugChannelRowTest.java", + ], + static_libs: [ + "adservices-assets", + "adservices-service-core", + "compatibility-device-util-axt", + "truth", + "adservices-test-utility", + "adservices-ui-cts-root-test-lib", + "platform-test-rules", + ], + libs: [ + "android.test.base", + "framework-adservices-lib", + ], + test_suites: [ + "cts_root", + "general-tests", + ], + sdk_version: "module_current", + max_sdk_version: "32", + min_sdk_version: "31", + test_config: "AndroidTest.ExtServices.Row.xml", + manifest: "AndroidManifestExtServices.xml", +} + +android_test { + name: "AdExtServicesGaUxDebugChannelCtsRootApiOffTest", + srcs: [ + "src/com/android/adservices/tests/ui/gaux/debugchannel/ExtGaUxDebugChannelApiOffTest.java", + ], + static_libs: [ + "adservices-assets", + "adservices-service-core", + "compatibility-device-util-axt", + "truth", + "adservices-test-utility", + "adservices-ui-cts-root-test-lib", + "platform-test-rules", + ], + libs: [ + "android.test.base", + "framework-adservices-lib", + ], + test_suites: [ + "cts_root", + "general-tests", + ], + sdk_version: "module_current", + max_sdk_version: "32", + min_sdk_version: "31", + test_config: "AndroidTest.ExtServices.ApiOff.xml", + manifest: "AndroidManifestExtServices.xml", +}
\ No newline at end of file diff --git a/adservices/tests/cts/ui/gaux/debugchannel/AndroidManifestExtServices.xml b/adservices/tests/cts/ui/gaux/debugchannel/AndroidManifestExtServices.xml new file mode 100644 index 000000000..2c7347586 --- /dev/null +++ b/adservices/tests/cts/ui/gaux/debugchannel/AndroidManifestExtServices.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.adservices.tests.ui.gaux.debugchannel"> + + <application> + <!-- Used and present only on S- --> + <uses-library android:name="com.google.android.ext.services" android:required="false"/> + </application> + + <!-- instrument into the system server for permission --> + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.google.android.ext.services"> + </instrumentation> +</manifest> diff --git a/adservices/tests/cts/ui/gaux/debugchannel/AndroidTest.ExtServices.ApiOff.xml b/adservices/tests/cts/ui/gaux/debugchannel/AndroidTest.ExtServices.ApiOff.xml new file mode 100644 index 000000000..b2975c00b --- /dev/null +++ b/adservices/tests/cts/ui/gaux/debugchannel/AndroidTest.ExtServices.ApiOff.xml @@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 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. + --> +<configuration description="TF Config for AdServices Ui Cts test"> + <!-- For this test suite to get picked up in presubmit --> + <option name="config-descriptor:metadata" key="mainline-param" + value="com.google.android.extservices.apex" /> + + <!-- Prevent test from running on Android T+ --> + <object type="module_controller" + class="com.android.tradefed.testtype.suite.module.MaxSdkModuleController"> + <option name="max-sdk-level" value="32"/> + </object> + + <!-- Prevent tests from running on Android R- --> + <object type="module_controller" + class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController"/> + + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" value="device_config set_sync_disabled_for_tests persistent"/> + <option name="teardown-command" value="device_config set_sync_disabled_for_tests none"/> + + <option name="run-command" value="setprop log.tag.adservices VERBOSE" /> + <option + name="run-command" + value="device_config put adservices consent_notification_activity_debug_mode false"/> + <option + name="run-command" + value="device_config put adservices ui_ota_strings_feature_enabled false"/> + <option + name="run-command" + value="device_config put adservices is_eea_device_feature_enabled true"/> + <option + name="run-command" + value="device_config put adservices is_eea_device true"/> + <option + name="run-command" + value="device_config put adservices ga_ux_enabled true"/> + <option + name="run-command" + value="device_config put adservices consent_notification_debug_mode true"/> + <option + name="run-command" + value="device_config put adservices consent_already_interacted_fix_enable false"/> + <option name="run-command" value="device_config put adservices global_kill_switch false"/> + <option name="run-command" value="device_config put adservices enable_back_compat true"/> + <option + name="run-command" + value="device_config put adservices adservice_enable_status true"/> + <option + name="run-command" + value="device_config put adservices adservice_enabled true"/> + <option + name="run-command" + value="device_config put adservices enable_ad_services_system_api false"/> + <!-- override scheduling params so the test is unaffected by time of the day --> + <option + name="run-command" + value="device_config put adservices consent_notification_interval_begin_ms 0"/> + <!-- end of day (24 hours) --> + <option + name="run-command" + value="device_config put adservices consent_notification_interval_end_ms 86400000"/> + <option + name="run-command" + value="device_config put adservices consent_notification_minimal_delay_before_interval_ends 0"/> + </target_preparer> + + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="test-file-name" value="AdExtServicesGaUxDebugChannelCtsRootApiOffTest.apk"/> + </target_preparer> + + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="directory-keys" value="/sdcard/Documents/adservices-tests" /> + <option name="clean-up" value="false" /> + <option name="collect-on-run-ended-only" value="true" /> + </metrics_collector> + + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="directory-keys" value="/data/system/files" /> + <option name="clean-up" value="false" /> + <option name="collect-on-run-ended-only" value="true" /> + </metrics_collector> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.adservices.tests.ui.gaux.debugchannel"/> + </test> +</configuration> diff --git a/adservices/tests/cts/ui/gaux/debugchannel/AndroidTest.ExtServices.Eu.xml b/adservices/tests/cts/ui/gaux/debugchannel/AndroidTest.ExtServices.Eu.xml new file mode 100644 index 000000000..d8d94c2ce --- /dev/null +++ b/adservices/tests/cts/ui/gaux/debugchannel/AndroidTest.ExtServices.Eu.xml @@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 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. + --> +<configuration description="TF Config for AdServices Ui Cts test"> + <!-- For this test suite to get picked up in presubmit --> + <option name="config-descriptor:metadata" key="mainline-param" + value="com.google.android.extservices.apex" /> + + <!-- Prevent test from running on Android T+ --> + <object type="module_controller" + class="com.android.tradefed.testtype.suite.module.MaxSdkModuleController"> + <option name="max-sdk-level" value="32"/> + </object> + + <!-- Prevent tests from running on Android R- --> + <object type="module_controller" + class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController"/> + + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" value="device_config set_sync_disabled_for_tests persistent"/> + <option name="teardown-command" value="device_config set_sync_disabled_for_tests none"/> + + <option name="run-command" value="setprop log.tag.adservices VERBOSE" /> + <option + name="run-command" + value="device_config put adservices consent_notification_activity_debug_mode false"/> + <option + name="run-command" + value="device_config put adservices ui_ota_strings_feature_enabled false"/> + <option + name="run-command" + value="device_config put adservices is_eea_device_feature_enabled true"/> + <option + name="run-command" + value="device_config put adservices is_eea_device true"/> + <option + name="run-command" + value="device_config put adservices ga_ux_enabled true"/> + <option + name="run-command" + value="device_config put adservices consent_notification_debug_mode true"/> + <option + name="run-command" + value="device_config put adservices consent_already_interacted_fix_enable false"/> + <option name="run-command" value="device_config put adservices global_kill_switch false"/> + <option name="run-command" value="device_config put adservices enable_back_compat true"/> + <option + name="run-command" + value="device_config put adservices adservice_enable_status true"/> + <option + name="run-command" + value="device_config put adservices adservice_enabled true"/> + <option + name="run-command" + value="device_config put adservices enable_ad_services_system_api true"/> + <!-- override scheduling params so the test is unaffected by time of the day --> + <option + name="run-command" + value="device_config put adservices consent_notification_interval_begin_ms 0"/> + <!-- end of day (24 hours) --> + <option + name="run-command" + value="device_config put adservices consent_notification_interval_end_ms 86400000"/> + <option + name="run-command" + value="device_config put adservices consent_notification_minimal_delay_before_interval_ends 0"/> + </target_preparer> + + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="test-file-name" value="AdExtServicesGaUxDebugChannelCtsRootEuTest.apk"/> + </target_preparer> + + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="directory-keys" value="/sdcard/Documents/adservices-tests" /> + <option name="clean-up" value="false" /> + <option name="collect-on-run-ended-only" value="true" /> + </metrics_collector> + + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="directory-keys" value="/data/system/files" /> + <option name="clean-up" value="false" /> + <option name="collect-on-run-ended-only" value="true" /> + </metrics_collector> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.adservices.tests.ui.gaux.debugchannel"/> + </test> +</configuration> diff --git a/adservices/tests/cts/ui/gaux/debugchannel/AndroidTest.ExtServices.Row.xml b/adservices/tests/cts/ui/gaux/debugchannel/AndroidTest.ExtServices.Row.xml new file mode 100644 index 000000000..a1364c112 --- /dev/null +++ b/adservices/tests/cts/ui/gaux/debugchannel/AndroidTest.ExtServices.Row.xml @@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 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. + --> +<configuration description="TF Config for AdServices Ui Cts test"> + <!-- For this test suite to get picked up in presubmit --> + <option name="config-descriptor:metadata" key="mainline-param" + value="com.google.android.extservices.apex" /> + + <!-- Prevent test from running on Android T+ --> + <object type="module_controller" + class="com.android.tradefed.testtype.suite.module.MaxSdkModuleController"> + <option name="max-sdk-level" value="32"/> + </object> + + <!-- Prevent tests from running on Android R- --> + <object type="module_controller" + class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController"/> + + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" value="device_config set_sync_disabled_for_tests persistent"/> + <option name="teardown-command" value="device_config set_sync_disabled_for_tests none"/> + + <option name="run-command" value="setprop log.tag.adservices VERBOSE" /> + <option + name="run-command" + value="device_config put adservices consent_notification_activity_debug_mode false"/> + <option + name="run-command" + value="device_config put adservices ui_ota_strings_feature_enabled false"/> + <option + name="run-command" + value="device_config put adservices is_eea_device_feature_enabled true"/> + <option + name="run-command" + value="device_config put adservices is_eea_device false"/> + <option + name="run-command" + value="device_config put adservices ga_ux_enabled true"/> + <option + name="run-command" + value="device_config put adservices consent_notification_debug_mode true"/> + <option + name="run-command" + value="device_config put adservices consent_already_interacted_fix_enable false"/> + <option + name="run-command" + value="device_config put adservices eu_notif_flow_change_enabled false"/> + <option name="run-command" value="device_config put adservices global_kill_switch false"/> + <option name="run-command" value="device_config put adservices enable_back_compat true"/> + <option + name="run-command" + value="device_config put adservices adservice_enable_status true"/> + <option + name="run-command" + value="device_config put adservices adservice_enabled true"/> + <option + name="run-command" + value="device_config put adservices enable_ad_services_system_api true"/> + <!-- override scheduling params so the test is unaffected by time of the day --> + <option + name="run-command" + value="device_config put adservices consent_notification_interval_begin_ms 0"/> + <!-- end of day (24 hours) --> + <option + name="run-command" + value="device_config put adservices consent_notification_interval_end_ms 86400000"/> + <option + name="run-command" + value="device_config put adservices consent_notification_minimal_delay_before_interval_ends 0"/> + </target_preparer> + + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="test-file-name" value="AdExtServicesGaUxDebugChannelCtsRootRowTest.apk"/> + </target_preparer> + + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="directory-keys" value="/sdcard/Documents/adservices-tests" /> + <option name="clean-up" value="false" /> + <option name="collect-on-run-ended-only" value="true" /> + </metrics_collector> + + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="directory-keys" value="/data/system/files" /> + <option name="clean-up" value="false" /> + <option name="collect-on-run-ended-only" value="true" /> + </metrics_collector> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.adservices.tests.ui.gaux.debugchannel"/> + </test> +</configuration> diff --git a/adservices/tests/cts/ui/gaux/debugchannel/src/com/android/adservices/tests/ui/gaux/debugchannel/ExtGaUxDebugChannelApiOffTest.java b/adservices/tests/cts/ui/gaux/debugchannel/src/com/android/adservices/tests/ui/gaux/debugchannel/ExtGaUxDebugChannelApiOffTest.java new file mode 100644 index 000000000..ec396ba8b --- /dev/null +++ b/adservices/tests/cts/ui/gaux/debugchannel/src/com/android/adservices/tests/ui/gaux/debugchannel/ExtGaUxDebugChannelApiOffTest.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2023 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.tests.ui.gaux.debugchannel; + +import static com.google.common.truth.Truth.assertThat; + +import android.adservices.common.AdServicesCommonManager; +import android.adservices.common.AdServicesStates; +import android.content.Context; +import android.os.OutcomeReceiver; +import android.platform.test.rule.ScreenRecordRule; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.UiDevice; + +import com.android.adservices.common.AdservicesTestHelper; +import com.android.adservices.tests.ui.libs.AdservicesWorkflows; +import com.android.adservices.tests.ui.libs.UiConstants; +import com.android.adservices.tests.ui.libs.UiUtils; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.Executors; + +/** Test for verifying user consent notification trigger behaviors. */ +@RunWith(AndroidJUnit4.class) +@ScreenRecordRule.ScreenRecord +public class ExtGaUxDebugChannelApiOffTest { + + private AdServicesCommonManager mCommonManager; + + private UiDevice mDevice; + + private String mTestName; + + private static final Context sContext = + InstrumentationRegistry.getInstrumentation().getContext(); + + @Rule public final ScreenRecordRule sScreenRecordRule = new ScreenRecordRule(); + + @Before + public void setUp() throws Exception { + // Skip the test if it runs on unsupported platforms. + Assume.assumeTrue(AdservicesTestHelper.isDeviceSupported()); + + mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + + UiUtils.enableNotificationPermission(); + + mCommonManager = AdServicesCommonManager.get(sContext); + + // consent debug mode is turned on for this test class as we only care about the + // first trigger (API call). + UiUtils.enableConsentDebugMode(); + UiUtils.disableNotificationFlowV2(); + UiUtils.disableOtaStrings(); + + mDevice.pressHome(); + } + + @After + public void tearDown() throws Exception { + if (!AdservicesTestHelper.isDeviceSupported()) return; + + UiUtils.takeScreenshot(mDevice, getClass().getSimpleName() + "_" + mTestName + "_"); + + mDevice.pressHome(); + } + + /** Verify that the API returns false when API is disabled. */ + @Test + public void testApiDisabled() throws Exception { + mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); + + mCommonManager.enableAdServices( + new AdServicesStates.Builder() + .setAdIdEnabled(true) + .setAdultAccount(true) + .setPrivacySandboxUiEnabled(true) + .build(), + Executors.newCachedThreadPool(), + new OutcomeReceiver<>() { + @Override + public void onResult(Boolean result) { + assertThat(result).isFalse(); + } + + @Override + public void onError(Exception exception) { + Assert.fail(); + } + }); + + AdservicesWorkflows.verifyNotification( + sContext, + mDevice, /* isDisplayed */ + false, /* isEuTest */ + false, + UiConstants.UX.GA_UX, + true); + } +} diff --git a/adservices/tests/cts/ui/gaux/debugchannel/src/com/android/adservices/tests/ui/gaux/debugchannel/ExtGaUxDebugChannelEuTest.java b/adservices/tests/cts/ui/gaux/debugchannel/src/com/android/adservices/tests/ui/gaux/debugchannel/ExtGaUxDebugChannelEuTest.java new file mode 100644 index 000000000..72ec7d26a --- /dev/null +++ b/adservices/tests/cts/ui/gaux/debugchannel/src/com/android/adservices/tests/ui/gaux/debugchannel/ExtGaUxDebugChannelEuTest.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2023 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.tests.ui.gaux.debugchannel; + +import static com.google.common.truth.Truth.assertThat; + +import android.adservices.common.AdServicesCommonManager; +import android.adservices.common.AdServicesStates; +import android.content.Context; +import android.os.OutcomeReceiver; +import android.platform.test.rule.ScreenRecordRule; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.UiDevice; + +import com.android.adservices.common.AdservicesTestHelper; +import com.android.adservices.tests.ui.libs.AdservicesWorkflows; +import com.android.adservices.tests.ui.libs.UiConstants; +import com.android.adservices.tests.ui.libs.UiUtils; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.Executors; + +/** Test for verifying user consent notification trigger behaviors. */ +@RunWith(AndroidJUnit4.class) +@ScreenRecordRule.ScreenRecord +public class ExtGaUxDebugChannelEuTest { + + private AdServicesCommonManager mCommonManager; + + private UiDevice mDevice; + + private String mTestName; + + private OutcomeReceiver<Boolean, Exception> mCallback; + + private static final Context sContext = + InstrumentationRegistry.getInstrumentation().getContext(); + + @Rule public final ScreenRecordRule sScreenRecordRule = new ScreenRecordRule(); + + @Before + public void setUp() throws Exception { + // Skip the test if it runs on unsupported platforms. + Assume.assumeTrue(AdservicesTestHelper.isDeviceSupported()); + + UiUtils.resetAdServicesConsentData(sContext); + + mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + + UiUtils.enableNotificationPermission(); + + mCommonManager = AdServicesCommonManager.get(sContext); + + // consent debug mode is turned on for this test class as we only care about the + // first trigger (API call). + UiUtils.enableConsentDebugMode(); + UiUtils.disableNotificationFlowV2(); + UiUtils.disableOtaStrings(); + + mCallback = + new OutcomeReceiver<>() { + @Override + public void onResult(Boolean result) { + assertThat(result).isTrue(); + } + + @Override + public void onError(Exception exception) { + Assert.fail(); + } + }; + + mDevice.pressHome(); + } + + @After + public void tearDown() throws Exception { + if (!AdservicesTestHelper.isDeviceSupported()) return; + + UiUtils.takeScreenshot(mDevice, getClass().getSimpleName() + "_" + mTestName + "_"); + + mDevice.pressHome(); + } + + /** Verify that entry point disabled can not trigger consent notification. */ + @Test + public void testEntryPointDisabled() throws Exception { + mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); + + mCommonManager.enableAdServices( + new AdServicesStates.Builder() + .setAdIdEnabled(true) + .setAdultAccount(true) + .setPrivacySandboxUiEnabled(false) + .build(), + Executors.newCachedThreadPool(), + mCallback); + + AdservicesWorkflows.verifyNotification( + sContext, + mDevice, /* isDisplayed */ + false, /* isEuTest */ + false, /* isGa */ + UiConstants.UX.GA_UX, + true); + } + + /** Verify that when request sent from entry point, we won't trigger notification. */ + @Test + public void testFromEntryPointRequest() throws Exception { + mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); + + mCommonManager.enableAdServices( + new AdServicesStates.Builder() + .setAdIdEnabled(false) + .setAdultAccount(true) + .setPrivacySandboxUiEnabled(true) + .setPrivacySandboxUiRequest(true) + .build(), + Executors.newCachedThreadPool(), + mCallback); + + AdservicesWorkflows.verifyNotification( + sContext, + mDevice, /* isDisplayed */ + false, /* isEuTest */ + true, + UiConstants.UX.GA_UX, + true); + } + + /** Verify that non-adult account can not trigger consent notification. */ + @Test + public void testNonAdultAccount() throws Exception { + mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); + + mCommonManager.enableAdServices( + new AdServicesStates.Builder() + .setAdIdEnabled(true) + .setAdultAccount(false) + .setPrivacySandboxUiEnabled(true) + .build(), + Executors.newCachedThreadPool(), + mCallback); + + AdservicesWorkflows.verifyNotification( + sContext, + mDevice, /* isDisplayed */ + false, /* isEuTest */ + false, + UiConstants.UX.GA_UX, + true); + } + + /** + * Verify that for GA, EU devices with non zeroed-out AdId, the GA EU notification is displayed. + */ + @Test + public void testGaEuAdIdEnabled() throws Exception { + mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); + + mCommonManager.enableAdServices( + new AdServicesStates.Builder() + .setAdIdEnabled(true) + .setAdultAccount(true) + .setPrivacySandboxUiEnabled(true) + .build(), + Executors.newCachedThreadPool(), + mCallback); + + AdservicesWorkflows.verifyNotification( + sContext, + mDevice, /* isDisplayed */ + true, /* isEuTest */ + true, + UiConstants.UX.GA_UX, + true); + } + + /** Verify that for GA, EU devices with zeroed-out AdId, the EU notification is displayed. */ + @Test + public void testGaEuAdIdDisabled() throws Exception { + mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); + + mCommonManager.enableAdServices( + new AdServicesStates.Builder() + .setAdIdEnabled(false) + .setAdultAccount(true) + .setPrivacySandboxUiEnabled(true) + .build(), + Executors.newCachedThreadPool(), + mCallback); + + AdservicesWorkflows.verifyNotification( + sContext, + mDevice, /* isDisplayed */ + true, /* isEuTest */ + true, + UiConstants.UX.GA_UX, + true); + } +} diff --git a/adservices/tests/cts/ui/gaux/debugchannel/src/com/android/adservices/tests/ui/gaux/debugchannel/ExtGaUxDebugChannelRowTest.java b/adservices/tests/cts/ui/gaux/debugchannel/src/com/android/adservices/tests/ui/gaux/debugchannel/ExtGaUxDebugChannelRowTest.java new file mode 100644 index 000000000..782773fbd --- /dev/null +++ b/adservices/tests/cts/ui/gaux/debugchannel/src/com/android/adservices/tests/ui/gaux/debugchannel/ExtGaUxDebugChannelRowTest.java @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2023 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.tests.ui.gaux.debugchannel; + +import static com.google.common.truth.Truth.assertThat; + +import android.adservices.common.AdServicesCommonManager; +import android.adservices.common.AdServicesStates; +import android.content.Context; +import android.os.OutcomeReceiver; +import android.platform.test.rule.ScreenRecordRule; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.UiDevice; + +import com.android.adservices.common.AdservicesTestHelper; +import com.android.adservices.tests.ui.libs.AdservicesWorkflows; +import com.android.adservices.tests.ui.libs.UiConstants; +import com.android.adservices.tests.ui.libs.UiUtils; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.Executors; + +/** Test for verifying user consent notification trigger behaviors. */ +@RunWith(AndroidJUnit4.class) +@ScreenRecordRule.ScreenRecord +public class ExtGaUxDebugChannelRowTest { + + private AdServicesCommonManager mCommonManager; + + private UiDevice mDevice; + + private String mTestName; + + private OutcomeReceiver<Boolean, Exception> mCallback; + + private static final Context sContext = + InstrumentationRegistry.getInstrumentation().getContext(); + + @Rule public final ScreenRecordRule sScreenRecordRule = new ScreenRecordRule(); + + @Before + public void setUp() throws Exception { + // Skip the test if it runs on unsupported platforms. + Assume.assumeTrue(AdservicesTestHelper.isDeviceSupported()); + + UiUtils.resetAdServicesConsentData(sContext); + + mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + + UiUtils.enableNotificationPermission(); + + mCommonManager = AdServicesCommonManager.get(sContext); + + // consent debug mode is turned on for this test class as we only care about the + // first trigger (API call). + UiUtils.enableConsentDebugMode(); + UiUtils.disableNotificationFlowV2(); + UiUtils.disableOtaStrings(); + + mCallback = + new OutcomeReceiver<>() { + @Override + public void onResult(Boolean result) { + assertThat(result).isTrue(); + } + + @Override + public void onError(Exception exception) { + Assert.fail(); + } + }; + + mDevice.pressHome(); + } + + @After + public void tearDown() throws Exception { + if (!AdservicesTestHelper.isDeviceSupported()) return; + + UiUtils.takeScreenshot(mDevice, getClass().getSimpleName() + "_" + mTestName + "_"); + + mDevice.pressHome(); + } + + /** Verify that entry point disabled can not trigger consent notification. */ + @Test + public void testEntryPointDisabled() throws Exception { + mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); + + mCommonManager.enableAdServices( + new AdServicesStates.Builder() + .setAdIdEnabled(true) + .setAdultAccount(true) + .setPrivacySandboxUiEnabled(false) + .build(), + Executors.newCachedThreadPool(), + mCallback); + + AdservicesWorkflows.verifyNotification( + sContext, + mDevice, /* isDisplayed */ + false, /* isEuTest */ + false, /* isGa */ + UiConstants.UX.GA_UX, + true); + } + + /** Verify that when request sent from entry point, we won't trigger notification. */ + @Test + public void testFromEntryPointRequest() throws Exception { + mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); + + mCommonManager.enableAdServices( + new AdServicesStates.Builder() + .setAdIdEnabled(false) + .setAdultAccount(true) + .setPrivacySandboxUiEnabled(true) + .setPrivacySandboxUiRequest(true) + .build(), + Executors.newCachedThreadPool(), + mCallback); + + AdservicesWorkflows.verifyNotification( + sContext, + mDevice, /* isDisplayed */ + false, /* isEuTest */ + false, + UiConstants.UX.GA_UX, + true); + } + + /** Verify that non-adult account can not trigger consent notification. */ + @Test + public void testNonAdultAccount() throws Exception { + mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); + + mCommonManager.enableAdServices( + new AdServicesStates.Builder() + .setAdIdEnabled(true) + .setAdultAccount(false) + .setPrivacySandboxUiEnabled(true) + .build(), + Executors.newCachedThreadPool(), + mCallback); + + AdservicesWorkflows.verifyNotification( + sContext, + mDevice, /* isDisplayed */ + false, /* isEuTest */ + false, + UiConstants.UX.GA_UX, + true); + } + + /** + * Verify that for GA, ROW devices with non zeroed-out AdId, the GA ROW notification is + * displayed. + */ + @Test + public void testGaRowAdIdEnabled() throws Exception { + mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); + + mCommonManager.enableAdServices( + new AdServicesStates.Builder() + .setAdIdEnabled(true) + .setAdultAccount(true) + .setPrivacySandboxUiEnabled(true) + .build(), + Executors.newCachedThreadPool(), + mCallback); + + AdservicesWorkflows.verifyNotification( + sContext, + mDevice, /* isDisplayed */ + true, /* isEuTest */ + false, + UiConstants.UX.GA_UX, + true); + } + + /** + * Verify that for GA, ROW devices with zeroed-out AdId, the GA EU notification is displayed. + */ + @Test + public void testGaRowAdIdDisabled() throws Exception { + mTestName = new Object() {}.getClass().getEnclosingMethod().getName(); + + mCommonManager.enableAdServices( + new AdServicesStates.Builder() + .setAdIdEnabled(false) + .setAdultAccount(true) + .setPrivacySandboxUiEnabled(true) + .build(), + Executors.newCachedThreadPool(), + mCallback); + + AdservicesWorkflows.verifyNotification( + sContext, + mDevice, /* isDisplayed */ + true, /* isEuTest */ + true, + UiConstants.UX.GA_UX, + true); + } +} diff --git a/adservices/tests/cts/ui/libs/src/com/android/adservices/tests/ui/libs/AdservicesWorkflows.java b/adservices/tests/cts/ui/libs/src/com/android/adservices/tests/ui/libs/AdservicesWorkflows.java index c36dde21b..4f2125672 100644 --- a/adservices/tests/cts/ui/libs/src/com/android/adservices/tests/ui/libs/AdservicesWorkflows.java +++ b/adservices/tests/cts/ui/libs/src/com/android/adservices/tests/ui/libs/AdservicesWorkflows.java @@ -135,6 +135,10 @@ public class AdservicesWorkflows { UiUtils.enableGa(); UiUtils.enableU18(); break; + case RVC_UX: + UiUtils.enableGa(); + UiUtils.enableRvc(); + break; } UiUtils.setFlipFlow(isV2); @@ -192,7 +196,7 @@ public class AdservicesWorkflows { boolean isEuDevice, UiConstants.UX ux) throws Exception { - NotificationPages.verifyNotification(context, device, isDisplayed, isEuDevice, ux, false); + NotificationPages.verifyNotification(context, device, isDisplayed, isEuDevice, ux, true); } public static void verifyNotification( diff --git a/adservices/tests/cts/ui/libs/src/com/android/adservices/tests/ui/libs/UiUtils.java b/adservices/tests/cts/ui/libs/src/com/android/adservices/tests/ui/libs/UiUtils.java index e917a5182..6b3d7bb57 100644 --- a/adservices/tests/cts/ui/libs/src/com/android/adservices/tests/ui/libs/UiUtils.java +++ b/adservices/tests/cts/ui/libs/src/com/android/adservices/tests/ui/libs/UiUtils.java @@ -171,9 +171,9 @@ public class UiUtils { forceSetFlag("rvc_ux_enabled", true); } - /** Override flag rvc_notification_enabled in tests to true */ + /** Override flag rvc_post_ota_notification_enabled in tests to true */ public static void enableRvcNotification() throws Exception { - forceSetFlag("rvc_notification_enabled", true); + forceSetFlag("rvc_post_ota_notification_enabled", true); } /** Override flag rvc_ux_enabled in tests to false */ @@ -280,14 +280,17 @@ public class UiUtils { int notificationHeader = -1; switch (ux) { case GA_UX: + // Should match the contentTitle string in ConsentNotificationTrigger.java. notificationTitle = isEuTest - ? R.string.notificationUI_notification_ga_title_eu - : R.string.notificationUI_notification_ga_title; + ? R.string.notificationUI_notification_ga_title_eu_v2 + : R.string.notificationUI_notification_ga_title_v2; + // Should match the text in consent_notification_screen_1_ga_v2_eu.xml and + // consent_notification_screen_1_ga_v2_row.xml, respectively. notificationHeader = isEuTest - ? R.string.notificationUI_header_ga_title_eu - : R.string.notificationUI_header_ga_title; + ? R.string.notificationUI_fledge_measurement_title_v2 + : R.string.notificationUI_header_ga_title_v2; break; case BETA_UX: notificationTitle = @@ -625,23 +628,6 @@ public class UiUtils { scrollView.swipe(Direction.DOWN, 0.7f, 500); } - public static UiObject2 getConsentSwitch(UiDevice device) { - UiObject2 consentSwitch = - device.wait( - Until.findObject(By.clazz("android.widget.Switch")), - PRIMITIVE_UI_OBJECTS_LAUNCH_TIMEOUT_MS); - // Swipe the screen by the width of the toggle so it's not blocked by the nav bar on AOSP - // devices. - device.swipe( - consentSwitch.getVisibleBounds().centerX(), - 500, - consentSwitch.getVisibleBounds().centerX(), - 0, - 100); - - return consentSwitch; - } - public static void performSwitchClick( UiDevice device, Context context, boolean dialogsOn, UiObject2 mainSwitch) { if (dialogsOn && mainSwitch.isChecked()) { @@ -667,7 +653,10 @@ public class UiUtils { public static void gentleSwipe(UiDevice device) { UiObject2 scrollView = device.findObject(By.scrollable(true).clazz(ANDROID_WIDGET_SCROLLVIEW)); - scrollView.scroll(Direction.DOWN, /* percent */ 0.25F); + // Some devices on R is not scrollable + if (scrollView != null) { + scrollView.scroll(Direction.DOWN, /* percent */ 0.25F); + } } public static void setFlipFlow(boolean isFlip) { @@ -700,10 +689,13 @@ public class UiUtils { public static UiObject2 scrollTo(Context context, UiDevice device, int resId) { UiObject2 scrollView = device.findObject(By.scrollable(true).clazz(ANDROID_WIDGET_SCROLLVIEW)); - String targetStr = getString(context, resId); - scrollView.scrollUntil( - Direction.DOWN, - Until.findObject(By.text(Pattern.compile(targetStr, Pattern.CASE_INSENSITIVE)))); + if (scrollView != null) { + String targetStr = getString(context, resId); + scrollView.scrollUntil( + Direction.DOWN, + Until.findObject( + By.text(Pattern.compile(targetStr, Pattern.CASE_INSENSITIVE)))); + } return getElement(context, device, resId); } diff --git a/adservices/tests/cts/ui/libs/src/com/android/adservices/tests/ui/libs/pages/NotificationPages.java b/adservices/tests/cts/ui/libs/src/com/android/adservices/tests/ui/libs/pages/NotificationPages.java index 6a5051d04..0c6b88564 100644 --- a/adservices/tests/cts/ui/libs/src/com/android/adservices/tests/ui/libs/pages/NotificationPages.java +++ b/adservices/tests/cts/ui/libs/src/com/android/adservices/tests/ui/libs/pages/NotificationPages.java @@ -53,16 +53,17 @@ public class NotificationPages { int notificationHeader = -1; switch (ux) { case GA_UX: + // Should match the contentTitle string in ConsentNotificationTrigger.java. notificationTitle = isEuTest - ? R.string.notificationUI_notification_ga_title_eu - : isV2 - ? R.string.notificationUI_notification_ga_title_v2 - : R.string.notificationUI_notification_ga_title; + ? R.string.notificationUI_notification_ga_title_eu_v2 + : R.string.notificationUI_notification_ga_title_v2; + // Should match the text in consent_notification_screen_1_ga_v2_eu.xml and + // consent_notification_screen_1_ga_v2_row.xml, respectively. notificationHeader = isEuTest - ? R.string.notificationUI_header_ga_title_eu - : R.string.notificationUI_header_ga_title; + ? R.string.notificationUI_fledge_measurement_title_v2 + : R.string.notificationUI_header_ga_title_v2; break; case BETA_UX: notificationTitle = diff --git a/adservices/tests/host/room/Android.bp b/adservices/tests/host/room/Android.bp index b6f2bdb98..04b066d8c 100644 --- a/adservices/tests/host/room/Android.bp +++ b/adservices/tests/host/room/Android.bp @@ -26,5 +26,4 @@ java_test_host { "cts-install-lib-host", ], test_suites: ["general-tests"], - test_mainline_modules: ["com.google.android.adservices.apex"], } diff --git a/adservices/tests/host/room/AndroidTest.xml b/adservices/tests/host/room/AndroidTest.xml index 3b520b53c..d70c792cd 100644 --- a/adservices/tests/host/room/AndroidTest.xml +++ b/adservices/tests/host/room/AndroidTest.xml @@ -19,6 +19,10 @@ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app"/> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi"/> + <!-- For this test suite to get picked up in mainline presubmit --> + <option name="config-descriptor:metadata" key="mainline-param" + value="com.google.android.adservices.apex" /> + <test class="com.android.tradefed.testtype.HostTest"> <option name="class" value="com.android.adservices.room.test.RoomDatabaseVersionBumpGuardrailTest"/> diff --git a/adservices/tests/host/room/src/com/android/adservices/room/test/RoomDatabaseVersionBumpGuardrailTest.java b/adservices/tests/host/room/src/com/android/adservices/room/test/RoomDatabaseVersionBumpGuardrailTest.java index 8c3db3124..de345b50f 100644 --- a/adservices/tests/host/room/src/com/android/adservices/room/test/RoomDatabaseVersionBumpGuardrailTest.java +++ b/adservices/tests/host/room/src/com/android/adservices/room/test/RoomDatabaseVersionBumpGuardrailTest.java @@ -21,6 +21,7 @@ import android.cts.install.lib.host.InstallUtilsHost; import com.android.tradefed.config.Option; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import com.android.tradefed.util.FileUtil; import org.junit.Test; import org.junit.runner.RunWith; @@ -70,6 +71,9 @@ public class RoomDatabaseVersionBumpGuardrailTest extends BaseHostJUnit4Test { @Option(name = "new-schema-lib") protected String mNewSchemaLib; + @Option(name = "schema-apk-name") + protected String mSchemaApkName = AD_SERVICES_SERVICE_CORE_UNIT_TESTS_APK_FILE_NAME; + @Test public void roomDatabaseVersionBumpGuardrailTest() throws Exception { @@ -79,15 +83,14 @@ public class RoomDatabaseVersionBumpGuardrailTest extends BaseHostJUnit4Test { Map<String, ZipEntry> baseVersions = extractVersionMap(baseSchemas); Map<String, ZipEntry> newVersions = extractVersionMap(newSchemas); - StringBuilder stringBuilder = new StringBuilder(); + StringBuilder errors = new StringBuilder(); for (Map.Entry<String, ZipEntry> e : baseVersions.entrySet()) { String name = e.getKey(); ZipEntry baseFile = e.getValue(); ZipEntry newFile = newVersions.get(name); if (newFile == null) { - stringBuilder.append( - String.format("Database file '%s' missing in new version!\n", name)); + errors.append(String.format("Database file '%s' missing in new version!\n", name)); continue; } @@ -98,7 +101,7 @@ public class RoomDatabaseVersionBumpGuardrailTest extends BaseHostJUnit4Test { if (!Arrays.equals( baseSchemas.getInputStream(baseFile).readAllBytes(), newSchemas.getInputStream(newFile).readAllBytes())) { - stringBuilder.append( + errors.append( String.format( "Database file '%s' changed between major build. Please bump up" + " DB version.\n", @@ -107,29 +110,33 @@ public class RoomDatabaseVersionBumpGuardrailTest extends BaseHostJUnit4Test { } if (getVersionFromFile(baseFile) > getVersionFromFile(newFile)) { - stringBuilder.append( + errors.append( String.format( "Database %s version was turned down from %d to %d\n", name, baseFileVersion, newFileVersion)); } } - if (stringBuilder.length() != 0) { - throw new IllegalStateException(stringBuilder.toString()); + if (errors.length() != 0) { + throw new IllegalStateException(errors.toString()); } } private ZipFile getTestPackageFromFile(String lib) throws IOException { byte[] bytes; - try (ZipFile zipFile = new ZipFile(mHostUtils.getTestFile(lib))) { - ZipEntry entry = - getZipEntry(zipFile, AD_SERVICES_SERVICE_CORE_UNIT_TESTS_APK_FILE_NAME); - bytes = zipFile.getInputStream(entry).readAllBytes(); + File testPackage = mHostUtils.getTestFile(lib); + if (testPackage.isFile()) { + try (ZipFile zipFile = new ZipFile(testPackage)) { + ZipEntry entry = getZipEntry(zipFile, mSchemaApkName); + bytes = zipFile.getInputStream(entry).readAllBytes(); + } + File tempFile = File.createTempFile("schemaFiles", ".zip"); + tempFile.setWritable(true); + new FileOutputStream(tempFile).write(bytes); + return new ZipFile(tempFile); + } else { + return new ZipFile(FileUtil.findFile(testPackage, mSchemaApkName)); } - File tempFile = File.createTempFile("schemaFiles", ".zip"); - tempFile.setWritable(true); - new FileOutputStream(tempFile).write(bytes); - return new ZipFile(tempFile); } private static ZipEntry getZipEntry(ZipFile zipFile, String fileName) { diff --git a/adservices/tests/jetpack/Android.bp b/adservices/tests/jetpack/Android.bp index 9a6a91221..f506a0013 100644 --- a/adservices/tests/jetpack/Android.bp +++ b/adservices/tests/jetpack/Android.bp @@ -437,7 +437,8 @@ android_test { ], libs: [ "android.test.base", - ], + "framework-sdkextensions", + ], test_suites: [ "general-tests", ], @@ -468,7 +469,8 @@ android_test { ], libs: [ "android.test.base", - ], + "framework-sdkextensions", + ], test_suites: [ "general-tests", ], @@ -499,7 +501,8 @@ android_test { ], libs: [ "android.test.base", - ], + "framework-sdkextensions", + ], test_suites: [ "general-tests", ], @@ -530,7 +533,8 @@ android_test { ], libs: [ "android.test.base", - ], + "framework-sdkextensions", + ], test_suites: [ "general-tests", ], @@ -561,7 +565,8 @@ android_test { ], libs: [ "android.test.base", - ], + "framework-sdkextensions", + ], test_suites: [ "general-tests", ], diff --git a/adservices/tests/jetpack/src/com/android/adservices/TestUtil.java b/adservices/tests/jetpack/src/com/android/adservices/TestUtil.java index fa2f7dbb0..46722078d 100644 --- a/adservices/tests/jetpack/src/com/android/adservices/TestUtil.java +++ b/adservices/tests/jetpack/src/com/android/adservices/TestUtil.java @@ -93,6 +93,7 @@ public class TestUtil { public void overrideAllowlists(boolean override) { String overrideStr = override ? "*" : "null"; runShellCommand("device_config put adservices ppapi_app_allow_list " + overrideStr); + runShellCommand("device_config put adservices msmt_api_app_allow_list " + overrideStr); runShellCommand( "device_config put adservices ppapi_app_signature_allow_list " + overrideStr); runShellCommand( diff --git a/adservices/tests/jetpack/src/com/android/adservices/adid/AdIdManagerJetpackTest.java b/adservices/tests/jetpack/src/com/android/adservices/adid/AdIdManagerJetpackTest.java index f5e9d7459..1fbab9bd8 100644 --- a/adservices/tests/jetpack/src/com/android/adservices/adid/AdIdManagerJetpackTest.java +++ b/adservices/tests/jetpack/src/com/android/adservices/adid/AdIdManagerJetpackTest.java @@ -18,6 +18,8 @@ package com.android.adservices.adid; import static com.google.common.truth.Truth.assertThat; +import android.os.ext.SdkExtensions; + import androidx.privacysandbox.ads.adservices.adid.AdId; import androidx.privacysandbox.ads.adservices.java.adid.AdIdManagerFutures; import androidx.test.core.app.ApplicationProvider; @@ -26,6 +28,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.adservices.TestUtil; import org.junit.After; +import org.junit.Assume; import org.junit.Before; import org.junit.Test; @@ -35,6 +38,8 @@ public class AdIdManagerJetpackTest { @Before public void setup() throws Exception { + Assume.assumeTrue(SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES) >= 4); + mTestUtil.overrideAdIdKillSwitch(true); mTestUtil.overrideKillSwitches(true); mTestUtil.overrideConsentManagerDebugMode(true); diff --git a/adservices/tests/jetpack/src/com/android/adservices/appsetid/AppSetIdManagerJetpackTest.java b/adservices/tests/jetpack/src/com/android/adservices/appsetid/AppSetIdManagerJetpackTest.java index c8169f0c1..ed7c06e91 100644 --- a/adservices/tests/jetpack/src/com/android/adservices/appsetid/AppSetIdManagerJetpackTest.java +++ b/adservices/tests/jetpack/src/com/android/adservices/appsetid/AppSetIdManagerJetpackTest.java @@ -18,6 +18,8 @@ package com.android.adservices.appsetid; import static com.google.common.truth.Truth.assertThat; +import android.os.ext.SdkExtensions; + import androidx.privacysandbox.ads.adservices.appsetid.AppSetId; import androidx.privacysandbox.ads.adservices.java.appsetid.AppSetIdManagerFutures; import androidx.test.core.app.ApplicationProvider; @@ -26,6 +28,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.adservices.TestUtil; import org.junit.After; +import org.junit.Assume; import org.junit.Before; import org.junit.Test; @@ -35,6 +38,8 @@ public class AppSetIdManagerJetpackTest { @Before public void setup() throws Exception { + Assume.assumeTrue(SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES) >= 4); + mTestUtil.overrideAppSetIdKillSwitch(true); mTestUtil.overrideKillSwitches(true); mTestUtil.overrideAllowlists(true); diff --git a/adservices/tests/jetpack/src/com/android/adservices/measurement/MeasurementManagerJetpackTest.java b/adservices/tests/jetpack/src/com/android/adservices/measurement/MeasurementManagerJetpackTest.java index b0a589b0e..0154d334c 100644 --- a/adservices/tests/jetpack/src/com/android/adservices/measurement/MeasurementManagerJetpackTest.java +++ b/adservices/tests/jetpack/src/com/android/adservices/measurement/MeasurementManagerJetpackTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; import android.net.Uri; +import android.os.ext.SdkExtensions; import androidx.privacysandbox.ads.adservices.java.measurement.MeasurementManagerFutures; import androidx.privacysandbox.ads.adservices.measurement.DeletionRequest; @@ -33,6 +34,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.adservices.TestUtil; import org.junit.After; +import org.junit.Assume; import org.junit.Before; import org.junit.Test; @@ -56,6 +58,7 @@ public class MeasurementManagerJetpackTest { @Before public void setup() throws Exception { + Assume.assumeTrue(SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES) >= 5); // To grant access to all pp api app mTestUtil.overrideAllowlists(true); // We need to turn the Consent Manager into debug mode diff --git a/adservices/tests/jetpack/src/com/android/adservices/protectedaudience/FledgeCtsDebuggableJetpackTest.java b/adservices/tests/jetpack/src/com/android/adservices/protectedaudience/FledgeCtsDebuggableJetpackTest.java index 20ad27236..30ff2e2de 100644 --- a/adservices/tests/jetpack/src/com/android/adservices/protectedaudience/FledgeCtsDebuggableJetpackTest.java +++ b/adservices/tests/jetpack/src/com/android/adservices/protectedaudience/FledgeCtsDebuggableJetpackTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertThrows; import android.content.Context; import android.net.Uri; +import android.os.ext.SdkExtensions; import android.util.Log; import androidx.privacysandbox.ads.adservices.adselection.AdSelectionConfig; @@ -35,7 +36,6 @@ import androidx.privacysandbox.ads.adservices.customaudience.CustomAudience; import androidx.privacysandbox.ads.adservices.customaudience.JoinCustomAudienceRequest; import androidx.privacysandbox.ads.adservices.customaudience.LeaveCustomAudienceRequest; import androidx.privacysandbox.ads.adservices.customaudience.TrustedBiddingData; -import androidx.privacysandbox.ads.adservices.internal.AdServicesInfo; import androidx.privacysandbox.ads.adservices.java.adselection.AdSelectionManagerFutures; import androidx.privacysandbox.ads.adservices.java.customaudience.CustomAudienceManagerFutures; import androidx.test.core.app.ApplicationProvider; @@ -195,6 +195,8 @@ public class FledgeCtsDebuggableJetpackTest { @Before public void setup() throws Exception { + Assume.assumeTrue(SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES) >= 4); + mAdSelectionClient = new AdSelectionClient(sContext); mCustomAudienceClient = new CustomAudienceClient(sContext); @@ -209,9 +211,6 @@ public class FledgeCtsDebuggableJetpackTest { @Test public void testFledgeAuctionSelectionFlow_overall_Success() throws Exception { - // Skip the test if SDK extension 4 is not present. - Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4); - List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2); List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0); @@ -248,9 +247,6 @@ public class FledgeCtsDebuggableJetpackTest { @Test public void testAdSelection_etldViolation_failure() throws Exception { - // Skip the test if SDK extension 4 is not present. - Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4); - List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2); List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0); @@ -297,9 +293,6 @@ public class FledgeCtsDebuggableJetpackTest { @Test public void testReportImpression_etldViolation_failure() throws Exception { - // Skip the test if SDK extension 4 is not present. - Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4); - List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2); List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0); @@ -359,9 +352,6 @@ public class FledgeCtsDebuggableJetpackTest { @Test public void testAdSelection_skipAdsMalformedBiddingLogic_success() throws Exception { - // Skip the test if SDK extension 4 is not present. - Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4); - List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2); List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0); @@ -407,9 +397,6 @@ public class FledgeCtsDebuggableJetpackTest { @Test public void testAdSelection_malformedScoringLogic_failure() throws Exception { - // Skip the test if SDK extension 4 is not present. - Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4); - List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2); List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0); @@ -454,9 +441,6 @@ public class FledgeCtsDebuggableJetpackTest { @Test public void testAdSelection_skipAdsFailedGettingBiddingLogic_success() throws Exception { - // Skip the test if SDK extension 4 is not present. - Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4); - List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2); List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0); @@ -502,9 +486,6 @@ public class FledgeCtsDebuggableJetpackTest { @Test public void testAdSelection_errorGettingScoringLogic_failure() throws Exception { - // Skip the test if SDK extension 4 is not present. - Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4); - List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2); List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0); @@ -555,9 +536,6 @@ public class FledgeCtsDebuggableJetpackTest { @Test public void testAdSelectionFlow_skipNonActivatedCA_Success() throws Exception { - // Skip the test if SDK extension 4 is not present. - Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4); - List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2); List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0); @@ -604,9 +582,6 @@ public class FledgeCtsDebuggableJetpackTest { @Test public void testAdSelectionFlow_skipExpiredCA_Success() throws Exception { - // Skip the test if SDK extension 4 is not present. - Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4); - List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2); List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0); @@ -659,9 +634,6 @@ public class FledgeCtsDebuggableJetpackTest { @Test public void testAdSelectionFlow_skipCAsThatTimeoutDuringBidding_Success() throws Exception { - // Skip the test if SDK extension 4 is not present. - Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4); - List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2); List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0); @@ -706,9 +678,6 @@ public class FledgeCtsDebuggableJetpackTest { @Test public void testAdSelection_overallTimeout_Failure() throws Exception { - // Skip the test if SDK extension 4 is not present. - Assume.assumeTrue(AdServicesInfo.INSTANCE.version() >= 4); - List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2); List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0); diff --git a/adservices/tests/jetpack/src/com/android/adservices/topics/TopicsManagerJetpackTest.java b/adservices/tests/jetpack/src/com/android/adservices/topics/TopicsManagerJetpackTest.java index cfb4ad406..4233a8890 100644 --- a/adservices/tests/jetpack/src/com/android/adservices/topics/TopicsManagerJetpackTest.java +++ b/adservices/tests/jetpack/src/com/android/adservices/topics/TopicsManagerJetpackTest.java @@ -18,6 +18,8 @@ package com.android.adservices.topics; import static com.google.common.truth.Truth.assertThat; +import android.os.ext.SdkExtensions; + import androidx.privacysandbox.ads.adservices.java.topics.TopicsManagerFutures; import androidx.privacysandbox.ads.adservices.topics.GetTopicsRequest; import androidx.privacysandbox.ads.adservices.topics.GetTopicsResponse; @@ -28,6 +30,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.adservices.TestUtil; import org.junit.After; +import org.junit.Assume; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; @@ -50,6 +53,8 @@ public class TopicsManagerJetpackTest { @Before public void setup() throws Exception { + Assume.assumeTrue(SdkExtensions.getExtensionVersion(SdkExtensions.AD_SERVICES) >= 4); + mTestUtil.overrideKillSwitches(true); // We need to skip 3 epochs so that if there is any usage from other test runs, it will // not be used for epoch retrieval. diff --git a/adservices/tests/perf/src/android/adservices/test/scenario/adservices/fledge/AdSelectionDataE2ETest.java b/adservices/tests/perf/src/android/adservices/test/scenario/adservices/fledge/AdSelectionDataE2ETest.java index 3fb572f9a..8cee1b4b1 100644 --- a/adservices/tests/perf/src/android/adservices/test/scenario/adservices/fledge/AdSelectionDataE2ETest.java +++ b/adservices/tests/perf/src/android/adservices/test/scenario/adservices/fledge/AdSelectionDataE2ETest.java @@ -73,7 +73,7 @@ public class AdSelectionDataE2ETest { "CustomAudienceServerAuctionFiveBuyersMultipleCa.json"; private static final String CUSTOM_AUDIENCE_NO_AD_RENDER_ID = "CustomAudienceNoAdRenderId.json"; private static final String SELLER = "ba-seller-5jyy5ulagq-uc.a.run.app"; - private static final String SFE_ADDRESS = "https://seller1-nmb.sfe-gcp.com/v1/selectAd"; + private static final String SFE_ADDRESS = "https://seller1-patest.sfe.bas-gcp.pstest.dev/v1/selectAd"; private static final boolean SERVER_RESPONSE_LOGGING_ENABLED = true; private static final String AD_WINNER_DOMAIN = "https://ba-buyer-5jyy5ulagq-uc.a.run.app/"; diff --git a/adservices/tests/perf/src/android/adservices/test/scenario/adservices/fledge/GetAdSelectionDataLatency.java b/adservices/tests/perf/src/android/adservices/test/scenario/adservices/fledge/GetAdSelectionDataLatency.java new file mode 100644 index 000000000..5c7308321 --- /dev/null +++ b/adservices/tests/perf/src/android/adservices/test/scenario/adservices/fledge/GetAdSelectionDataLatency.java @@ -0,0 +1,566 @@ +/* + * Copyright (C) 2023 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 android.adservices.test.scenario.adservices.fledge; + +import android.Manifest; +import android.adservices.adselection.GetAdSelectionDataOutcome; +import android.adservices.adselection.GetAdSelectionDataRequest; +import android.adservices.clients.adselection.AdSelectionClient; +import android.adservices.common.AdTechIdentifier; +import android.adservices.customaudience.CustomAudience; +import android.adservices.customaudience.CustomAudienceFixture; +import android.adservices.test.scenario.adservices.fledge.utils.CustomAudienceTestFixture; +import android.adservices.test.scenario.adservices.utils.SelectAdsFlagRule; +import android.content.Context; +import android.platform.test.microbenchmark.Microbenchmark; +import android.platform.test.rule.CleanPackageRule; +import android.platform.test.rule.KillAppsRule; +import android.platform.test.scenario.annotation.Scenario; +import android.util.Log; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.adservices.common.AdservicesTestHelper; +import com.android.compatibility.common.util.ShellUtils; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.RuleChain; +import org.junit.runner.RunWith; + +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +@Scenario +@RunWith(Microbenchmark.class) +public class GetAdSelectionDataLatency { + private static final Executor CALLBACK_EXECUTOR = Executors.newCachedThreadPool(); + private static final Context CONTEXT = ApplicationProvider.getApplicationContext(); + private static final String CUSTOM_AUDIENCE_ONE_BUYER_ONE_CA_ONE_AD = + "CustomAudienceOneBuyerOneCaOneAd.json"; + private static final String SELLER = "ba-seller-5jyy5ulagq-uc.a.run.app"; + + private static final int API_RESPONSE_TIMEOUT_SECONDS = 100; + private static final AdSelectionClient AD_SELECTION_CLIENT = + new AdSelectionClient.Builder() + .setContext(CONTEXT) + .setExecutor(CALLBACK_EXECUTOR) + .build(); + + @Rule + public RuleChain rules = + RuleChain.outerRule( + new KillAppsRule( + AdservicesTestHelper.getAdServicesPackageName(CONTEXT))) + .around( + // CleanPackageRule should not execute after each test method because + // there's a chance it interferes with ShowmapSnapshotListener snapshot + // at the end of the test, impacting collection of memory metrics for + // AdServices process. + new CleanPackageRule( + AdservicesTestHelper.getAdServicesPackageName(CONTEXT), + /* clearOnStarting = */ true, + /* clearOnFinished = */ false)) + .around(new SelectAdsFlagRule()); + + @BeforeClass + public static void setupBeforeClass() { + InstrumentationRegistry.getInstrumentation() + .getUiAutomation() + .adoptShellPermissionIdentity(Manifest.permission.WRITE_DEVICE_CONFIG); + } + + private static final String TAG = "SelectAds"; + + private String generateLogLabel(String classSimpleName, String testName, long elapsedMs) { + return "(" + + "SELECT_ADS_LATENCY_" + + classSimpleName + + "#" + + testName + + ": " + + elapsedMs + + " ms)"; + } + + private static final long NANO_TO_MILLISECONDS = 1000000; + + @Before + public void warmup() throws Exception { + List<CustomAudience> customAudiences = + CustomAudienceTestFixture.readCustomAudiences( + CUSTOM_AUDIENCE_ONE_BUYER_ONE_CA_ONE_AD); + CustomAudienceTestFixture.joinCustomAudiences(customAudiences); + + GetAdSelectionDataRequest request = + new GetAdSelectionDataRequest.Builder() + .setSeller(AdTechIdentifier.fromString(SELLER)) + .build(); + GetAdSelectionDataOutcome outcome = + AD_SELECTION_CLIENT + .getAdSelectionData(request) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + + CustomAudienceTestFixture.leaveCustomAudience(customAudiences); + } + + @Test + public void test_withoutFiltering_varyingBuyers_1() throws Exception { + List<CustomAudience> customAudiences = + CustomAudienceFixture.getNValidCustomAudiences( + /* nBuyers= */ 1, /* nCAsPerBuyer= */ 1, /* nAdsPerCA= */ 5); + CustomAudienceTestFixture.joinCustomAudiences(customAudiences); + + long startTime = System.nanoTime(); + GetAdSelectionDataRequest request = + new GetAdSelectionDataRequest.Builder() + .setSeller(AdTechIdentifier.fromString(SELLER)) + .build(); + GetAdSelectionDataOutcome outcome = + AD_SELECTION_CLIENT + .getAdSelectionData(request) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + long endTime = System.nanoTime(); + Log.i( + TAG, + generateLogLabel( + getClass().getSimpleName(), + "test_withoutFiltering_varyingBuyers_1", + (endTime - startTime) / NANO_TO_MILLISECONDS)); + + CustomAudienceTestFixture.leaveCustomAudience(customAudiences); + } + + @Test + public void test_withoutFiltering_varyingBuyers_10() throws Exception { + List<CustomAudience> customAudiences = + CustomAudienceFixture.getNValidCustomAudiences( + /* nBuyers= */ 10, /* nCAsPerBuyer= */ 1, /* nAdsPerCA= */ 5); + CustomAudienceTestFixture.joinCustomAudiences(customAudiences); + + long startTime = System.nanoTime(); + GetAdSelectionDataRequest request = + new GetAdSelectionDataRequest.Builder() + .setSeller(AdTechIdentifier.fromString(SELLER)) + .build(); + GetAdSelectionDataOutcome outcome = + AD_SELECTION_CLIENT + .getAdSelectionData(request) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + long endTime = System.nanoTime(); + Log.i( + TAG, + generateLogLabel( + getClass().getSimpleName(), + "test_withoutFiltering_varyingBuyers_10", + (endTime - startTime) / NANO_TO_MILLISECONDS)); + + CustomAudienceTestFixture.leaveCustomAudience(customAudiences); + } + + @Test + public void test_withoutFiltering_varyingBuyers_100() throws Exception { + List<CustomAudience> customAudiences = + CustomAudienceFixture.getNValidCustomAudiences( + /* nBuyers= */ 100, /* nCAsPerBuyer= */ 1, /* nAdsPerCA= */ 5); + CustomAudienceTestFixture.joinCustomAudiences(customAudiences); + + long startTime = System.nanoTime(); + GetAdSelectionDataRequest request = + new GetAdSelectionDataRequest.Builder() + .setSeller(AdTechIdentifier.fromString(SELLER)) + .build(); + GetAdSelectionDataOutcome outcome = + AD_SELECTION_CLIENT + .getAdSelectionData(request) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + long endTime = System.nanoTime(); + Log.i( + TAG, + generateLogLabel( + getClass().getSimpleName(), + "test_withoutFiltering_varyingBuyers_100", + (endTime - startTime) / NANO_TO_MILLISECONDS)); + + CustomAudienceTestFixture.leaveCustomAudience(customAudiences); + } + + @Test + public void test_withoutFiltering_varyingBuyers_500() throws Exception { + increasePayloadBucketSize(); + List<CustomAudience> customAudiences = + CustomAudienceFixture.getNValidCustomAudiences( + /* nBuyers= */ 500, /* nCAsPerBuyer= */ 1, /* nAdsPerCA= */ 5); + CustomAudienceTestFixture.joinCustomAudiences(customAudiences); + + long startTime = System.nanoTime(); + GetAdSelectionDataRequest request = + new GetAdSelectionDataRequest.Builder() + .setSeller(AdTechIdentifier.fromString(SELLER)) + .build(); + GetAdSelectionDataOutcome outcome = + AD_SELECTION_CLIENT + .getAdSelectionData(request) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + long endTime = System.nanoTime(); + Log.i( + TAG, + generateLogLabel( + getClass().getSimpleName(), + "test_withoutFiltering_varyingBuyers_500", + (endTime - startTime) / NANO_TO_MILLISECONDS)); + + CustomAudienceTestFixture.leaveCustomAudience(customAudiences); + resetPayloadBucketSize(); + } + + @Test + public void test_withoutFiltering_varyingBuyers_1000() throws Exception { + increasePayloadBucketSize(); + List<CustomAudience> customAudiences = + CustomAudienceFixture.getNValidCustomAudiences( + /* nBuyers= */ 1000, /* nCAsPerBuyer= */ 1, /* nAdsPerCA= */ 5); + CustomAudienceTestFixture.joinCustomAudiences(customAudiences); + + long startTime = System.nanoTime(); + GetAdSelectionDataRequest request = + new GetAdSelectionDataRequest.Builder() + .setSeller(AdTechIdentifier.fromString(SELLER)) + .build(); + GetAdSelectionDataOutcome outcome = + AD_SELECTION_CLIENT + .getAdSelectionData(request) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + long endTime = System.nanoTime(); + Log.i( + TAG, + generateLogLabel( + getClass().getSimpleName(), + "test_withoutFiltering_varyingBuyers_1000", + (endTime - startTime) / NANO_TO_MILLISECONDS)); + + CustomAudienceTestFixture.leaveCustomAudience(customAudiences); + resetPayloadBucketSize(); + } + + @Test + public void test_withoutFiltering_varyingCAs_1() throws Exception { + List<CustomAudience> customAudiences = + CustomAudienceFixture.getNValidCustomAudiences( + /* nBuyers= */ 4, /* nCAsPerBuyer= */ 1, /* nAdsPerCA= */ 5); + CustomAudienceTestFixture.joinCustomAudiences(customAudiences); + + long startTime = System.nanoTime(); + GetAdSelectionDataRequest request = + new GetAdSelectionDataRequest.Builder() + .setSeller(AdTechIdentifier.fromString(SELLER)) + .build(); + GetAdSelectionDataOutcome outcome = + AD_SELECTION_CLIENT + .getAdSelectionData(request) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + long endTime = System.nanoTime(); + Log.i( + TAG, + generateLogLabel( + getClass().getSimpleName(), + "test_withoutFiltering_varyingCAs_1", + (endTime - startTime) / NANO_TO_MILLISECONDS)); + + CustomAudienceTestFixture.leaveCustomAudience(customAudiences); + } + + @Test + public void test_withoutFiltering_varyingCAs_10() throws Exception { + List<CustomAudience> customAudiences = + CustomAudienceFixture.getNValidCustomAudiences( + /* nBuyers= */ 4, /* nCAsPerBuyer= */ 10, /* nAdsPerCA= */ 5); + CustomAudienceTestFixture.joinCustomAudiences(customAudiences); + + long startTime = System.nanoTime(); + GetAdSelectionDataRequest request = + new GetAdSelectionDataRequest.Builder() + .setSeller(AdTechIdentifier.fromString(SELLER)) + .build(); + GetAdSelectionDataOutcome outcome = + AD_SELECTION_CLIENT + .getAdSelectionData(request) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + long endTime = System.nanoTime(); + Log.i( + TAG, + generateLogLabel( + getClass().getSimpleName(), + "test_withoutFiltering_varyingCAs_10", + (endTime - startTime) / NANO_TO_MILLISECONDS)); + + CustomAudienceTestFixture.leaveCustomAudience(customAudiences); + } + + @Test + public void test_withoutFiltering_varyingCAs_100() throws Exception { + List<CustomAudience> customAudiences = + CustomAudienceFixture.getNValidCustomAudiences( + /* nBuyers= */ 4, /* nCAsPerBuyer= */ 100, /* nAdsPerCA= */ 5); + CustomAudienceTestFixture.joinCustomAudiences(customAudiences); + + long startTime = System.nanoTime(); + GetAdSelectionDataRequest request = + new GetAdSelectionDataRequest.Builder() + .setSeller(AdTechIdentifier.fromString(SELLER)) + .build(); + GetAdSelectionDataOutcome outcome = + AD_SELECTION_CLIENT + .getAdSelectionData(request) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + long endTime = System.nanoTime(); + Log.i( + TAG, + generateLogLabel( + getClass().getSimpleName(), + "test_withoutFiltering_varyingBuyers_100", + (endTime - startTime) / NANO_TO_MILLISECONDS)); + + CustomAudienceTestFixture.leaveCustomAudience(customAudiences); + } + + @Test + public void test_withoutFiltering_varyingCAs_500() throws Exception { + List<CustomAudience> customAudiences = + CustomAudienceFixture.getNValidCustomAudiences( + /* nBuyers= */ 4, /* nCAsPerBuyer= */ 500, /* nAdsPerCA= */ 5); + CustomAudienceTestFixture.joinCustomAudiences(customAudiences); + + long startTime = System.nanoTime(); + GetAdSelectionDataRequest request = + new GetAdSelectionDataRequest.Builder() + .setSeller(AdTechIdentifier.fromString(SELLER)) + .build(); + GetAdSelectionDataOutcome outcome = + AD_SELECTION_CLIENT + .getAdSelectionData(request) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + long endTime = System.nanoTime(); + Log.i( + TAG, + generateLogLabel( + getClass().getSimpleName(), + "test_withoutFiltering_varyingCAs_500", + (endTime - startTime) / NANO_TO_MILLISECONDS)); + + CustomAudienceTestFixture.leaveCustomAudience(customAudiences); + } + + @Test + public void test_withoutFiltering_varyingCAs_1000() throws Exception { + List<CustomAudience> customAudiences = + CustomAudienceFixture.getNValidCustomAudiences( + /* nBuyers= */ 4, /* nCAsPerBuyer= */ 1000, /* nAdsPerCA= */ 5); + CustomAudienceTestFixture.joinCustomAudiences(customAudiences); + + long startTime = System.nanoTime(); + GetAdSelectionDataRequest request = + new GetAdSelectionDataRequest.Builder() + .setSeller(AdTechIdentifier.fromString(SELLER)) + .build(); + GetAdSelectionDataOutcome outcome = + AD_SELECTION_CLIENT + .getAdSelectionData(request) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + long endTime = System.nanoTime(); + Log.i( + TAG, + generateLogLabel( + getClass().getSimpleName(), + "test_withoutFiltering_varyingCAs_1000", + (endTime - startTime) / NANO_TO_MILLISECONDS)); + + CustomAudienceTestFixture.leaveCustomAudience(customAudiences); + } + + @Test + public void test_withoutFiltering_varyingAds_5() throws Exception { + List<CustomAudience> customAudiences = + CustomAudienceFixture.getNValidCustomAudiences( + /* nBuyers= */ 4, /* nCAsPerBuyer= */ 10, /* nAdsPerCA= */ 5); + CustomAudienceTestFixture.joinCustomAudiences(customAudiences); + + long startTime = System.nanoTime(); + GetAdSelectionDataRequest request = + new GetAdSelectionDataRequest.Builder() + .setSeller(AdTechIdentifier.fromString(SELLER)) + .build(); + GetAdSelectionDataOutcome outcome = + AD_SELECTION_CLIENT + .getAdSelectionData(request) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + long endTime = System.nanoTime(); + Log.i( + TAG, + generateLogLabel( + getClass().getSimpleName(), + "test_withoutFiltering_varyingAds_5", + (endTime - startTime) / NANO_TO_MILLISECONDS)); + + CustomAudienceTestFixture.leaveCustomAudience(customAudiences); + } + + @Test + public void test_withoutFiltering_varyingAds_25() throws Exception { + List<CustomAudience> customAudiences = + CustomAudienceFixture.getNValidCustomAudiences( + /* nBuyers= */ 4, /* nCAsPerBuyer= */ 10, /* nAdsPerCA= */ 25); + CustomAudienceTestFixture.joinCustomAudiences(customAudiences); + + long startTime = System.nanoTime(); + GetAdSelectionDataRequest request = + new GetAdSelectionDataRequest.Builder() + .setSeller(AdTechIdentifier.fromString(SELLER)) + .build(); + GetAdSelectionDataOutcome outcome = + AD_SELECTION_CLIENT + .getAdSelectionData(request) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + long endTime = System.nanoTime(); + Log.i( + TAG, + generateLogLabel( + getClass().getSimpleName(), + "test_withoutFiltering_varyingAds_25", + (endTime - startTime) / NANO_TO_MILLISECONDS)); + + CustomAudienceTestFixture.leaveCustomAudience(customAudiences); + } + + @Test + public void test_withoutFiltering_varyingAds_125() throws Exception { + increaseMaximumAds(); + List<CustomAudience> customAudiences = + CustomAudienceFixture.getNValidCustomAudiences( + /* nBuyers= */ 4, /* nCAsPerBuyer= */ 10, /* nAdsPerCA= */ 125); + CustomAudienceTestFixture.joinCustomAudiences(customAudiences); + + long startTime = System.nanoTime(); + GetAdSelectionDataRequest request = + new GetAdSelectionDataRequest.Builder() + .setSeller(AdTechIdentifier.fromString(SELLER)) + .build(); + GetAdSelectionDataOutcome outcome = + AD_SELECTION_CLIENT + .getAdSelectionData(request) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + long endTime = System.nanoTime(); + Log.i( + TAG, + generateLogLabel( + getClass().getSimpleName(), + "test_withoutFiltering_varyingAds_125", + (endTime - startTime) / NANO_TO_MILLISECONDS)); + + CustomAudienceTestFixture.leaveCustomAudience(customAudiences); + resetMaximumAds(); + } + + @Test + public void test_withoutFiltering_varyingAds_500() throws Exception { + increaseMaximumAds(); + List<CustomAudience> customAudiences = + CustomAudienceFixture.getNValidCustomAudiences( + /* nBuyers= */ 4, /* nCAsPerBuyer= */ 10, /* nAdsPerCA= */ 500); + CustomAudienceTestFixture.joinCustomAudiences(customAudiences); + + long startTime = System.nanoTime(); + GetAdSelectionDataRequest request = + new GetAdSelectionDataRequest.Builder() + .setSeller(AdTechIdentifier.fromString(SELLER)) + .build(); + GetAdSelectionDataOutcome outcome = + AD_SELECTION_CLIENT + .getAdSelectionData(request) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + long endTime = System.nanoTime(); + Log.i( + TAG, + generateLogLabel( + getClass().getSimpleName(), + "test_withoutFiltering_varyingAds_500", + (endTime - startTime) / NANO_TO_MILLISECONDS)); + + CustomAudienceTestFixture.leaveCustomAudience(customAudiences); + resetMaximumAds(); + } + + @Test + public void test_withoutFiltering_varyingAds_1000() throws Exception { + increaseMaximumAds(); + List<CustomAudience> customAudiences = + CustomAudienceFixture.getNValidCustomAudiences( + /* nBuyers= */ 4, /* nCAsPerBuyer= */ 10, /* nAdsPerCA= */ 1000); + CustomAudienceTestFixture.joinCustomAudiences(customAudiences); + + long startTime = System.nanoTime(); + GetAdSelectionDataRequest request = + new GetAdSelectionDataRequest.Builder() + .setSeller(AdTechIdentifier.fromString(SELLER)) + .build(); + GetAdSelectionDataOutcome outcome = + AD_SELECTION_CLIENT + .getAdSelectionData(request) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + long endTime = System.nanoTime(); + Log.i( + TAG, + generateLogLabel( + getClass().getSimpleName(), + "test_withoutFiltering_varyingAds_1000", + (endTime - startTime) / NANO_TO_MILLISECONDS)); + + CustomAudienceTestFixture.leaveCustomAudience(customAudiences); + resetMaximumAds(); + } + + void increaseMaximumAds() { + ShellUtils.runShellCommand( + "device_config put adservices fledge_custom_audience_max_num_ads 1000"); + ShellUtils.runShellCommand( + "device_config put adservices fledge_custom_audience_max_ads_size_b 1048576"); + } + + void resetMaximumAds() { + ShellUtils.runShellCommand( + "device_config put adservices fledge_custom_audience_max_num_ads 100"); + ShellUtils.runShellCommand( + "device_config put adservices fledge_custom_audience_max_ads_size_b 10240"); + } + + void increasePayloadBucketSize() { + ShellUtils.runShellCommand( + "device_config put adservices fledge_auction_server_payload_bucket_sizes" + + " 0,1024,2048,4096,8192,16384,32768,65536,131072,262174,524288,1048576"); + } + + void resetPayloadBucketSize() { + ShellUtils.runShellCommand( + "device_config put adservices fledge_auction_server_payload_bucket_sizes" + + " 0,1024,2048,4096,8192,16384,32768,65536"); + } +} diff --git a/adservices/tests/perf/src/android/adservices/test/scenario/adservices/fledge/ServerAuctionOneBuyerLargeCaLatency.java b/adservices/tests/perf/src/android/adservices/test/scenario/adservices/fledge/ServerAuctionOneBuyerLargeCaLatency.java index 62299f741..a4953faed 100644 --- a/adservices/tests/perf/src/android/adservices/test/scenario/adservices/fledge/ServerAuctionOneBuyerLargeCaLatency.java +++ b/adservices/tests/perf/src/android/adservices/test/scenario/adservices/fledge/ServerAuctionOneBuyerLargeCaLatency.java @@ -66,7 +66,7 @@ public class ServerAuctionOneBuyerLargeCaLatency { private static final String AD_WINNER_DOMAIN = "https://ba-buyer-5jyy5ulagq-uc.a.run.app/"; private static final String SELLER = "ba-seller-5jyy5ulagq-uc.a.run.app"; - private static final String SFE_ADDRESS = "https://seller1-paprod.sfe-gcp.com/v1/selectAd"; + private static final String SFE_ADDRESS = "https://seller1-paprod.sfe.bas-gcp.pstest.dev/v1/selectAd"; private static final String CUSTOM_AUDIENCE_ONE_BUYER_ONE_CA_ONE_AD = "CustomAudienceOneBuyerOneCaOneAd.json"; diff --git a/adservices/tests/perf/src/android/adservices/test/scenario/adservices/ui/NotificationLandingPage.java b/adservices/tests/perf/src/android/adservices/test/scenario/adservices/ui/NotificationLandingPage.java index 79b8bf311..77b20f7ea 100644 --- a/adservices/tests/perf/src/android/adservices/test/scenario/adservices/ui/NotificationLandingPage.java +++ b/adservices/tests/perf/src/android/adservices/test/scenario/adservices/ui/NotificationLandingPage.java @@ -19,7 +19,6 @@ package android.adservices.test.scenario.adservices.ui; import android.content.Context; import android.os.Trace; import android.platform.test.scenario.annotation.Scenario; -import android.util.Log; import androidx.test.core.app.ApplicationProvider; import androidx.test.platform.app.InstrumentationRegistry; @@ -27,9 +26,11 @@ import androidx.test.uiautomator.UiDevice; import com.android.adservices.common.AdServicesFlagsSetterRule; import com.android.adservices.common.AdservicesTestHelper; +import com.android.adservices.service.FlagsConstants; import com.android.adservices.tests.ui.libs.AdservicesWorkflows; import com.android.adservices.tests.ui.libs.UiConstants; import com.android.adservices.tests.ui.libs.UiUtils; +import com.android.modules.utils.build.SdkLevel; import org.junit.After; import org.junit.Before; @@ -38,7 +39,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -/** Crystalball test for Topics API to collect System Heath metrics. */ @Scenario @RunWith(JUnit4.class) public class NotificationLandingPage { @@ -50,7 +50,10 @@ public class NotificationLandingPage { @Rule public final AdServicesFlagsSetterRule flags = - AdServicesFlagsSetterRule.forGlobalKillSwitchDisabledTests().setCompatModeFlags(); + AdServicesFlagsSetterRule.forGlobalKillSwitchDisabledTests() + .setCompatModeFlags() + .setDebugUxFlagsForRvcUx() + .setFlag(FlagsConstants.KEY_ENABLE_ADEXT_DATA_SERVICE_DEBUG_PROXY, "false"); @Before public void setup() throws Exception { @@ -70,15 +73,15 @@ public class NotificationLandingPage { @Test public void testNotificationLandingPage() throws Exception { - final long start = System.currentTimeMillis(); + UiConstants.UX ux = UiConstants.UX.GA_UX; + if( SdkLevel.isAtLeastR() && !SdkLevel.isAtLeastS() ) { + ux = UiConstants.UX.RVC_UX; + } Trace.beginSection("NotificationTriggerEvent"); AdservicesWorkflows.testNotificationActivityFlow( - sContext, sDevice, true, UiConstants.UX.GA_UX, false, false, true); + sContext, sDevice, true, ux, true, false, true); Trace.endSection(); - - final long duration = System.currentTimeMillis() - start; - Log.i(TAG, "(" + UI_NOTIFICATION_LATENCY_METRIC + ": " + duration + ")"); } } diff --git a/adservices/tests/perf/src/android/adservices/test/scenario/adservices/ui/UiSettingsMainPage.java b/adservices/tests/perf/src/android/adservices/test/scenario/adservices/ui/UiSettingsMainPage.java index 0c20f376e..a080c42a6 100644 --- a/adservices/tests/perf/src/android/adservices/test/scenario/adservices/ui/UiSettingsMainPage.java +++ b/adservices/tests/perf/src/android/adservices/test/scenario/adservices/ui/UiSettingsMainPage.java @@ -18,7 +18,7 @@ package android.adservices.test.scenario.adservices.ui; import android.content.Context; import android.platform.test.scenario.annotation.Scenario; -import android.util.Log; +import android.os.Trace; import androidx.test.core.app.ApplicationProvider; import androidx.test.platform.app.InstrumentationRegistry; @@ -26,9 +26,11 @@ import androidx.test.uiautomator.UiDevice; import com.android.adservices.common.AdServicesFlagsSetterRule; import com.android.adservices.common.AdservicesTestHelper; +import com.android.adservices.service.FlagsConstants; import com.android.adservices.tests.ui.libs.AdservicesWorkflows; import com.android.adservices.tests.ui.libs.UiConstants; import com.android.adservices.tests.ui.libs.UiUtils; +import com.android.modules.utils.build.SdkLevel; import org.junit.After; import org.junit.Before; @@ -37,7 +39,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; -/** Crystalball test for Topics API to collect System Heath metrics. */ @Scenario @RunWith(JUnit4.class) public class UiSettingsMainPage { @@ -49,7 +50,10 @@ public class UiSettingsMainPage { @Rule public final AdServicesFlagsSetterRule flags = - AdServicesFlagsSetterRule.forGlobalKillSwitchDisabledTests().setCompatModeFlags(); + AdServicesFlagsSetterRule.forGlobalKillSwitchDisabledTests() + .setCompatModeFlags() + .setDebugUxFlagsForRvcUx() + .setFlag(FlagsConstants.KEY_ENABLE_ADEXT_DATA_SERVICE_DEBUG_PROXY, "false"); @Before public void setup() throws Exception { @@ -69,15 +73,19 @@ public class UiSettingsMainPage { @Test public void testSettingsPage() throws Exception { - final long start = System.currentTimeMillis(); + UiConstants.UX ux = UiConstants.UX.GA_UX; + if( SdkLevel.isAtLeastR() && !SdkLevel.isAtLeastS() ) { + ux = UiConstants.UX.RVC_UX; + } + + Trace.beginSection("NotificationTriggerEvent"); AdservicesWorkflows.testSettingsPageFlow( sContext, sDevice, - UiConstants.UX.GA_UX, + ux, /* isOptIn= */ true, /* isFlipConsent= */ true, /* assertOptIn= */ false); - final long duration = System.currentTimeMillis() - start; - Log.i(TAG, "(" + UI_SETTINGS_LATENCY_METRIC + ": " + duration + ")"); + Trace.endSection(); } } diff --git a/adservices/tests/perf/tests/src/android/adservices/test/scenario/adservices/fledge/GetAdSelectionDataLatencyMicroBenchmark.java b/adservices/tests/perf/tests/src/android/adservices/test/scenario/adservices/fledge/GetAdSelectionDataLatencyMicroBenchmark.java new file mode 100644 index 000000000..298fed502 --- /dev/null +++ b/adservices/tests/perf/tests/src/android/adservices/test/scenario/adservices/fledge/GetAdSelectionDataLatencyMicroBenchmark.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2023 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 android.adservices.test.scenario.adservices.fledge; + +public class GetAdSelectionDataLatencyMicroBenchmark extends GetAdSelectionDataLatency {} diff --git a/adservices/tests/test-util/Android.bp b/adservices/tests/test-util/Android.bp index 3c06879e5..24f94cb3d 100644 --- a/adservices/tests/test-util/Android.bp +++ b/adservices/tests/test-util/Android.bp @@ -64,6 +64,7 @@ android_library { "androidx.test.runner", "compatibility-device-util-axt", "modules-utils-preconditions", + "adservices-shared-util", // For checkState method uses varargs. // TODO(b/281577492): should use modules-utils-extended-mockito-rule instead "modules-utils-testable-device-config", ], diff --git a/adservices/tests/test-util/host-side/com/android/adservices/common/TestDeviceHelper.java b/adservices/tests/test-util/host-side/com/android/adservices/common/TestDeviceHelper.java index 0ef8ff9c8..18c3d4562 100644 --- a/adservices/tests/test-util/host-side/com/android/adservices/common/TestDeviceHelper.java +++ b/adservices/tests/test-util/host-side/com/android/adservices/common/TestDeviceHelper.java @@ -97,8 +97,17 @@ public final class TestDeviceHelper { .doesNotContain("Error: Activity not started, unable to resolve Intent"); } - public static void enableComponent(String packageName, String className) { - runShellCommand("pm enable %s/%s", packageName, className); + /** Enable the given component and return the result of the shell command */ + public static String enableComponent(String packageName, String className) { + return runShellCommand("pm enable %s/%s", packageName, className); + } + + /** + * Enable the given component for the given userId only and return the result of the shell + * command + */ + public static String enableComponent(String packageName, String className, int userId) { + return runShellCommand("pm enable --user %d %s/%s", userId, packageName, className); } /** diff --git a/adservices/tests/test-util/java/com/android/adservices/common/ExceptionFailureSyncCallback.java b/adservices/tests/test-util/java/com/android/adservices/common/ExceptionFailureSyncCallback.java index a9d50b690..e41d9aa89 100644 --- a/adservices/tests/test-util/java/com/android/adservices/common/ExceptionFailureSyncCallback.java +++ b/adservices/tests/test-util/java/com/android/adservices/common/ExceptionFailureSyncCallback.java @@ -15,13 +15,12 @@ */ package com.android.adservices.common; +import static com.android.adservices.shared.util.Preconditions.checkState; +import static com.android.internal.util.Preconditions.checkArgument; + import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.Preconditions; - -import com.google.errorprone.annotations.FormatMethod; -import com.google.errorprone.annotations.FormatString; // TODO(b/302757068): add unit tests (and/or convert tests from OutcomeReceiverForTestsTest) /** @@ -103,7 +102,7 @@ public abstract class ExceptionFailureSyncCallback<T> extends SyncCallback<T, Ex */ public <E extends Exception> E assertFailure(Class<E> expectedClass) throws InterruptedException { - Preconditions.checkArgument(expectedClass != null, "expectedClass cannot be null"); + checkArgument(expectedClass != null, "expectedClass cannot be null"); Exception error = assertErrorReceived(); checkState( expectedClass.isInstance(error), @@ -130,15 +129,4 @@ public abstract class ExceptionFailureSyncCallback<T> extends SyncCallback<T, Ex public T getResult() { return getResultReceived(); } - - // TODO(b/303886367): move to common helper - @FormatMethod - private static void checkState( - boolean expression, - @FormatString String messageTemplate, - @Nullable Object... messageArgs) { - if (!expression) { - throw new IllegalStateException(String.format(messageTemplate, messageArgs)); - } - } } diff --git a/adservices/tests/test-util/java/com/android/adservices/mockito/ExtendedMockitoExpectations.java b/adservices/tests/test-util/java/com/android/adservices/mockito/ExtendedMockitoExpectations.java index 98fdfdbbb..3efe757a9 100644 --- a/adservices/tests/test-util/java/com/android/adservices/mockito/ExtendedMockitoExpectations.java +++ b/adservices/tests/test-util/java/com/android/adservices/mockito/ExtendedMockitoExpectations.java @@ -206,7 +206,20 @@ public final class ExtendedMockitoExpectations { * int)}. */ public static void verifyErrorLogUtilErrorWithAnyException(int errorCode, int ppapiName) { - verify(() -> ErrorLogUtil.e(any(), eq(errorCode), eq(ppapiName))); + verifyErrorLogUtilErrorWithAnyException(errorCode, ppapiName, times(1)); + } + + /** + * Verifies {@link ErrorLogUtil#e()} was called with the expected values, using Mockito's {@link + * VerificationMode} to set the number of times (like {@code times(2)} or {@code never}). + * + * <p><b>Note: </b>you must call either {@link #doNothingOnErrorLogUtilError()} or {@link + * #mockErrorLogUtilWithThrowable()} before the test calls {@link ErrorLogUtil#e(Throwable, int, + * int)}. + */ + public static void verifyErrorLogUtilErrorWithAnyException( + int errorCode, int ppapiName, VerificationMode mode) { + verify(() -> ErrorLogUtil.e(any(), eq(errorCode), eq(ppapiName)), mode); } /** diff --git a/adservices/tests/test-util/side-less/com/android/adservices/common/AbstractAdServicesFlagsSetterRule.java b/adservices/tests/test-util/side-less/com/android/adservices/common/AbstractAdServicesFlagsSetterRule.java index 655702791..c0c704da1 100644 --- a/adservices/tests/test-util/side-less/com/android/adservices/common/AbstractAdServicesFlagsSetterRule.java +++ b/adservices/tests/test-util/side-less/com/android/adservices/common/AbstractAdServicesFlagsSetterRule.java @@ -267,6 +267,18 @@ abstract class AbstractAdServicesFlagsSetterRule<T extends AbstractAdServicesFla }); } + public T setDebugUxFlagsForRvcUx() { + return runOrCache( + "setDebugUxFlagsForRvcUx()", + () -> { + if (!isAtLeastS() && isAtLeastR()) { + setFlag(FlagsConstants.KEY_CONSENT_NOTIFICATION_ACTIVITY_DEBUG_MODE, true); + setFlag(FlagsConstants.KEY_DEBUG_UX, "RVC_UX"); + return; + } + }); + } + //////////////////////////////////////////////////////////////////////////////////////////////// // NOTE: DO NOT add new setXyz() methods, unless they need non-trivial logic. Instead, let // // your test call setFlags(flagName) (statically import FlagsConstant.flagName), which will // diff --git a/adservices/tests/test-util/side-less/com/android/adservices/common/AbstractFlagsSetterRule.java b/adservices/tests/test-util/side-less/com/android/adservices/common/AbstractFlagsSetterRule.java index 9d9fa631c..8400a12ee 100644 --- a/adservices/tests/test-util/side-less/com/android/adservices/common/AbstractFlagsSetterRule.java +++ b/adservices/tests/test-util/side-less/com/android/adservices/common/AbstractFlagsSetterRule.java @@ -369,8 +369,12 @@ abstract class AbstractFlagsSetterRule<T extends AbstractFlagsSetterRule<T>> imp /** Gets the device's SDK level. */ protected abstract int getDeviceSdk(); + protected boolean isAtLeastR() { + return getDeviceSdk() >= 30; + } + protected boolean isAtLeastS() { - return getDeviceSdk() > 31; + return getDeviceSdk() >= 31; } protected boolean isAtLeastT() { @@ -385,7 +389,13 @@ abstract class AbstractFlagsSetterRule<T extends AbstractFlagsSetterRule<T>> imp // Set the annotated flags with the specified value for a particular test method. protected void setAnnotatedFlags(Description description) { - for (Annotation annotation : description.getAnnotations()) { + List<Annotation> annotations = getAllFlagAnnotations(description); + + // Apply the annotations in the reverse order. First apply from the super classes, test + // class and then test method. If same annotated flag is present in class and test + // method, test method takes higher priority. + for (int i = annotations.size() - 1; i >= 0; i--) { + Annotation annotation = annotations.get(i); if (annotation instanceof SetFlagEnabled) { setAnnotatedFlag((SetFlagEnabled) annotation); } else if (annotation instanceof SetFlagsEnabled) { @@ -416,7 +426,6 @@ abstract class AbstractFlagsSetterRule<T extends AbstractFlagsSetterRule<T>> imp setAnnotatedFlag((SetStringFlags) annotation); } } - // TODO(b/300146214) Add code to scan class / superclasses flag annotations. } private T setOrCacheFlag(String name, String value) { @@ -542,6 +551,50 @@ abstract class AbstractFlagsSetterRule<T extends AbstractFlagsSetterRule<T>> imp } } + private boolean isFlagAnnotationPresent(Annotation annotation) { + return (annotation instanceof SetFlagEnabled) + || (annotation instanceof SetFlagsEnabled) + || (annotation instanceof SetFlagDisabled) + || (annotation instanceof SetFlagsDisabled) + || (annotation instanceof SetIntegerFlag) + || (annotation instanceof SetIntegerFlags) + || (annotation instanceof SetLongFlag) + || (annotation instanceof SetLongFlags) + || (annotation instanceof SetFloatFlag) + || (annotation instanceof SetFloatFlags) + || (annotation instanceof SetDoubleFlag) + || (annotation instanceof SetDoubleFlags) + || (annotation instanceof SetStringFlag) + || (annotation instanceof SetStringFlags); + } + + private List<Annotation> getAllFlagAnnotations(Description description) { + // TODO(b/318893752): Move this to a helper function to scan test method, class and + // superclasses for annotations. + List<Annotation> result = new ArrayList<>(); + for (Annotation testMethodAnnotation : description.getAnnotations()) { + if (isFlagAnnotationPresent(testMethodAnnotation)) { + result.add(testMethodAnnotation); + } + } + + // Get all the flag based annotations from test class and super classes + Class<?> clazz = description.getTestClass(); + do { + Annotation[] classAnnotations = clazz.getAnnotations(); + if (classAnnotations != null) { + for (Annotation annotation : classAnnotations) { + if (isFlagAnnotationPresent(annotation)) { + result.add(annotation); + } + } + } + clazz = clazz.getSuperclass(); + } while (clazz != null); + + return result; + } + // Single SetFlagEnabled annotations present private void setAnnotatedFlag(SetFlagEnabled annotation) { setFlag(annotation.value(), true); diff --git a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetDoubleFlag.java b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetDoubleFlag.java index 4b24b8443..93988e09f 100644 --- a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetDoubleFlag.java +++ b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetDoubleFlag.java @@ -28,7 +28,7 @@ import java.lang.annotation.Target; * <p>This should be used with {@code AdServicesFlagsSetterRule}. */ @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD}) +@Target({ElementType.METHOD, ElementType.TYPE}) @Repeatable(SetDoubleFlags.class) public @interface SetDoubleFlag { /** Name of the flag. */ diff --git a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetDoubleFlags.java b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetDoubleFlags.java index f15239680..7415bb105 100644 --- a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetDoubleFlags.java +++ b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetDoubleFlags.java @@ -22,7 +22,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD}) +@Target({ElementType.METHOD, ElementType.TYPE}) public @interface SetDoubleFlags { SetDoubleFlag[] value(); } diff --git a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetFlagDisabled.java b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetFlagDisabled.java index a62d8b076..a8d3bddce 100644 --- a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetFlagDisabled.java +++ b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetFlagDisabled.java @@ -29,7 +29,7 @@ import java.lang.annotation.Target; * in the test to false. */ @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD}) +@Target({ElementType.METHOD, ElementType.TYPE}) @Repeatable(SetFlagsDisabled.class) public @interface SetFlagDisabled { /** Name of the flag. */ diff --git a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetFlagEnabled.java b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetFlagEnabled.java index bdc0ae77d..ae041a7d0 100644 --- a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetFlagEnabled.java +++ b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetFlagEnabled.java @@ -29,7 +29,7 @@ import java.lang.annotation.Target; * in the test to true. */ @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD}) +@Target({ElementType.METHOD, ElementType.TYPE}) @Repeatable(SetFlagsEnabled.class) public @interface SetFlagEnabled { /** Name of the flag. */ diff --git a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetFlagsDisabled.java b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetFlagsDisabled.java index 2f470e2a3..361950b09 100644 --- a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetFlagsDisabled.java +++ b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetFlagsDisabled.java @@ -22,7 +22,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD}) +@Target({ElementType.METHOD, ElementType.TYPE}) public @interface SetFlagsDisabled { SetFlagDisabled[] value(); } diff --git a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetFlagsEnabled.java b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetFlagsEnabled.java index bb67d1182..7e197c8de 100644 --- a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetFlagsEnabled.java +++ b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetFlagsEnabled.java @@ -22,7 +22,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD}) +@Target({ElementType.METHOD, ElementType.TYPE}) public @interface SetFlagsEnabled { SetFlagEnabled[] value(); } diff --git a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetFloatFlag.java b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetFloatFlag.java index 4f404404d..421f79cbb 100644 --- a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetFloatFlag.java +++ b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetFloatFlag.java @@ -28,7 +28,7 @@ import java.lang.annotation.Target; * <p>This should be used with {@code AdServicesFlagsSetterRule}. */ @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD}) +@Target({ElementType.METHOD, ElementType.TYPE}) @Repeatable(SetFloatFlags.class) public @interface SetFloatFlag { /** Name of the flag. */ diff --git a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetFloatFlags.java b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetFloatFlags.java index 0e839c2ab..a15f4a578 100644 --- a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetFloatFlags.java +++ b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetFloatFlags.java @@ -22,7 +22,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD}) +@Target({ElementType.METHOD, ElementType.TYPE}) public @interface SetFloatFlags { SetFloatFlag[] value(); } diff --git a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetIntegerFlag.java b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetIntegerFlag.java index e02b72f79..c5895be1d 100644 --- a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetIntegerFlag.java +++ b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetIntegerFlag.java @@ -28,7 +28,7 @@ import java.lang.annotation.Target; * <p>This should be used with {@code AdServicesFlagsSetterRule}. */ @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD}) +@Target({ElementType.METHOD, ElementType.TYPE}) @Repeatable(SetIntegerFlags.class) public @interface SetIntegerFlag { /** Name of the flag. */ diff --git a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetIntegerFlags.java b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetIntegerFlags.java index b8386707f..cc76a947e 100644 --- a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetIntegerFlags.java +++ b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetIntegerFlags.java @@ -22,7 +22,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD}) +@Target({ElementType.METHOD, ElementType.TYPE}) public @interface SetIntegerFlags { SetIntegerFlag[] value(); } diff --git a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetLongFlag.java b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetLongFlag.java index 473758cd2..bee66b142 100644 --- a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetLongFlag.java +++ b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetLongFlag.java @@ -28,7 +28,7 @@ import java.lang.annotation.Target; * <p>This should be used with {@code AdServicesFlagsSetterRule}. */ @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD}) +@Target({ElementType.METHOD, ElementType.TYPE}) @Repeatable(SetLongFlags.class) public @interface SetLongFlag { /** Name of the flag. */ diff --git a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetLongFlags.java b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetLongFlags.java index 46303a573..38309e88d 100644 --- a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetLongFlags.java +++ b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetLongFlags.java @@ -22,7 +22,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD}) +@Target({ElementType.METHOD, ElementType.TYPE}) public @interface SetLongFlags { SetLongFlag[] value(); } diff --git a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetStringFlag.java b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetStringFlag.java index 1b46d431b..bb3685892 100644 --- a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetStringFlag.java +++ b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetStringFlag.java @@ -28,7 +28,7 @@ import java.lang.annotation.Target; * <p>This should be used with {@code AdServicesFlagsSetterRule}. */ @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD}) +@Target({ElementType.METHOD, ElementType.TYPE}) @Repeatable(SetStringFlags.class) public @interface SetStringFlag { /** Name of the flag. */ diff --git a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetStringFlags.java b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetStringFlags.java index bf39a4d60..f4bac410b 100644 --- a/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetStringFlags.java +++ b/adservices/tests/test-util/side-less/com/android/adservices/common/annotations/SetStringFlags.java @@ -22,7 +22,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD}) +@Target({ElementType.METHOD, ElementType.TYPE}) public @interface SetStringFlags { SetStringFlag[] value(); } diff --git a/adservices/tests/unittest/adservices-test-utility/AndroidTest.xml b/adservices/tests/unittest/adservices-test-utility/AndroidTest.xml index 61f6fadac..4893bd4eb 100644 --- a/adservices/tests/unittest/adservices-test-utility/AndroidTest.xml +++ b/adservices/tests/unittest/adservices-test-utility/AndroidTest.xml @@ -28,4 +28,13 @@ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" /> + <!-- Entries below are neceessary so atest can run specific test classes without specifying the + module name (for example: atest AndroidSdkRangeTest) --> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="AdServicesTestUtilityTests.apk" /> + </target_preparer> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.adservices.testutil.tests" /> + </test> </configuration> diff --git a/adservices/tests/unittest/fixtures/java/android/adservices/adselection/AdSelectionConfigFixture.java b/adservices/tests/unittest/fixtures/java/android/adservices/adselection/AdSelectionConfigFixture.java index 0f1b38537..cfc066974 100644 --- a/adservices/tests/unittest/fixtures/java/android/adservices/adselection/AdSelectionConfigFixture.java +++ b/adservices/tests/unittest/fixtures/java/android/adservices/adselection/AdSelectionConfigFixture.java @@ -125,8 +125,9 @@ public class AdSelectionConfigFixture { * for the unit tests, this version of Ad Selection builder includes contextual Ads as well * @hide */ - public static AdSelectionConfig.Builder anAdSelectionConfigWithContextualAdsBuilder() { + public static AdSelectionConfig.Builder anAdSelectionConfigWithSignedContextualAdsBuilder() { return anAdSelectionConfigBuilder() - .setBuyerSignedContextualAds(SignedContextualAdsFixture.getBuyerContextualAdsMap()); + .setBuyerSignedContextualAds( + SignedContextualAdsFixture.getBuyerSignedContextualAdsMap()); } } diff --git a/adservices/tests/unittest/fixtures/java/android/adservices/adselection/SignedContextualAdsFixture.java b/adservices/tests/unittest/fixtures/java/android/adservices/adselection/SignedContextualAdsFixture.java index d1a9a4f55..2988a2aea 100644 --- a/adservices/tests/unittest/fixtures/java/android/adservices/adselection/SignedContextualAdsFixture.java +++ b/adservices/tests/unittest/fixtures/java/android/adservices/adselection/SignedContextualAdsFixture.java @@ -16,15 +16,25 @@ package android.adservices.adselection; +import static com.android.adservices.service.adselection.signature.ProtectedAudienceSignatureManager.PRIVATE_TEST_KEY_STRING; + import android.adservices.common.AdData; import android.adservices.common.AdDataFixture; import android.adservices.common.AdTechIdentifier; import android.adservices.common.CommonFixture; import android.net.Uri; +import com.android.adservices.LoggerFactory; +import com.android.adservices.service.adselection.signature.SignedContextualAdsHashUtil; + import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.Signature; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; import java.util.List; import java.util.stream.Collectors; @@ -33,7 +43,8 @@ import java.util.stream.Collectors; * SignedContextualAds}. */ public class SignedContextualAdsFixture { - public static final byte[] PLACEHOLDER_SIGNATURE = new byte[] {0, 0}; + private static final LoggerFactory.Logger sLogger = LoggerFactory.getFledgeLogger(); + private static final byte[] PLACEHOLDER_EMPTY_SIGNATURE = new byte[] {}; public static final AdTechIdentifier BUYER = CommonFixture.VALID_BUYER_1; public static final AdTechIdentifier BUYER_2 = CommonFixture.VALID_BUYER_2; @@ -51,44 +62,128 @@ public class SignedContextualAdsFixture { public static final List<AdWithBid> ADS_WITH_BID = ImmutableList.of(AD_WITH_BID_1, AD_WITH_BID_2); - public static SignedContextualAds.Builder aSignedContextualAdBuilder() { + /** + * Returns a {@link SignedContextualAds} object with a placeholder signature + * + * <p>This object's signature will not pass the verification + */ + public static SignedContextualAds.Builder aContextualAdsWithEmptySignatureBuilder() { return new SignedContextualAds.Builder() .setBuyer(BUYER) .setDecisionLogicUri(DECISION_LOGIC_URI) .setAdsWithBid(ADS_WITH_BID) - .setSignature(PLACEHOLDER_SIGNATURE); + .setSignature(PLACEHOLDER_EMPTY_SIGNATURE); } - public static SignedContextualAds.Builder generateSignedContextualAds( - AdTechIdentifier buyer, List<Double> bids) { - return new SignedContextualAds.Builder() + /** + * Returns a {@link SignedContextualAds} object with a placeholder signature and the given + * buyer. + * + * <p>This object's signature will not pass the verification + */ + public static SignedContextualAds.Builder aContextualAdsWithEmptySignatureBuilder( + AdTechIdentifier buyer) { + return aContextualAdsWithEmptySignatureBuilder() .setBuyer(buyer) - .setDecisionLogicUri(CommonFixture.getUri(buyer, DECISION_LOGIC_FRAGMENT)) - .setAdsWithBid( - bids.stream() - .map( - bid -> - new AdWithBid( - AdDataFixture.getValidFilterAdDataByBuyer( - buyer, bid.intValue()), - bid)) - .collect(Collectors.toList())) - .setSignature(PLACEHOLDER_SIGNATURE); + .setDecisionLogicUri(CommonFixture.getUri(buyer, DECISION_LOGIC_FRAGMENT)); + } + + /** + * Returns a {@link SignedContextualAds} object with a placeholder signature with given buyer + * and bids. + * + * <p>This object's signature will not pass the verification + */ + public static SignedContextualAds.Builder aContextualAdsWithEmptySignatureBuilder( + AdTechIdentifier buyer, List<Double> bids) { + List<AdWithBid> adsWithBid = + bids.stream() + .map( + bid -> + new AdWithBid( + AdDataFixture.getValidFilterAdDataByBuyer( + buyer, bid.intValue()), + bid)) + .collect(Collectors.toList()); + return aContextualAdsWithEmptySignatureBuilder(buyer).setAdsWithBid(adsWithBid); } - public static SignedContextualAds aSignedContextualAd() { - return aSignedContextualAdBuilder().build(); + /** + * Returns a {@link SignedContextualAds} object that is signed. + * + * <p>This object's signature can be verified using {@link SignedContextualAdsFixture + * #PUBLIC_KEY_STRING}. + */ + public static SignedContextualAds aSignedContextualAds() { + return signContextualAds(aContextualAdsWithEmptySignatureBuilder()); } - public static SignedContextualAds aSignedContextualAd(AdTechIdentifier buyer) { - return aSignedContextualAdBuilder().setBuyer(buyer).build(); + /** + * Returns a {@link SignedContextualAds} object that is signed with given buyer. + * + * <p>This object's signature can be verified using {@link SignedContextualAdsFixture + * #PUBLIC_KEY_STRING}. + */ + public static SignedContextualAds aSignedContextualAds(AdTechIdentifier buyer) { + return signContextualAds(aContextualAdsWithEmptySignatureBuilder(buyer)); } - public static ImmutableMap<AdTechIdentifier, SignedContextualAds> getBuyerContextualAdsMap() { + /** + * Returns a {@link SignedContextualAds} object that is signed with given buyer. + * + * <p>This object's signature can be verified using {@link SignedContextualAdsFixture + * #PUBLIC_KEY_STRING}. + */ + public static SignedContextualAds aSignedContextualAds( + AdTechIdentifier buyer, List<Double> bids) { + return signContextualAds(aContextualAdsWithEmptySignatureBuilder(buyer, bids)); + } + + public static ImmutableMap<AdTechIdentifier, SignedContextualAds> + getBuyerSignedContextualAdsMap() { return ImmutableMap.of( CommonFixture.VALID_BUYER_1, - aSignedContextualAd(CommonFixture.VALID_BUYER_1), + aSignedContextualAds(CommonFixture.VALID_BUYER_1), CommonFixture.VALID_BUYER_2, - aSignedContextualAd(CommonFixture.VALID_BUYER_2)); + aSignedContextualAds(CommonFixture.VALID_BUYER_2)); + } + + /** + * Signs contextual ads using {@link + * com.android.adservices.service.adselection.signature.ProtectedAudienceSignatureManager + * #PRIVATE_KEY_STRING}. + * + * <p>Bundle can be verified using {@link + * com.android.adservices.service.adselection.signature.ProtectedAudienceSignatureManager + * #PUBLIC_KEY_STRING} + */ + public static SignedContextualAds signContextualAds( + SignedContextualAds.Builder notSignedContextualAdsBuilder) { + SignedContextualAds signedContextualAds; + try { + Signature ecdsaSigner = getECDSASignatureInstance(); + ecdsaSigner.update( + new SignedContextualAdsHashUtil() + .serialize(notSignedContextualAdsBuilder.build())); + signedContextualAds = + notSignedContextualAdsBuilder.setSignature(ecdsaSigner.sign()).build(); + } catch (Exception e) { + String errMsg = + String.format( + "Something went wrong during signing a contextual ad bundle: %s", e); + sLogger.v(errMsg); + throw new RuntimeException(errMsg, e); + } + return signedContextualAds; + } + + private static Signature getECDSASignatureInstance() throws Exception { + byte[] privateKeyBytes = Base64.getDecoder().decode(PRIVATE_TEST_KEY_STRING); + PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(privateKeyBytes); + KeyFactory keyFactory = KeyFactory.getInstance("EC"); + PrivateKey privateKey = keyFactory.generatePrivate(spec); + Signature ecdsaSign = Signature.getInstance("SHA256withECDSA"); + ecdsaSign.initSign(privateKey); + return ecdsaSign; } } diff --git a/adservices/tests/unittest/fixtures/java/android/adservices/customaudience/CustomAudienceFixture.java b/adservices/tests/unittest/fixtures/java/android/adservices/customaudience/CustomAudienceFixture.java index bc5425a1c..daaf142e6 100644 --- a/adservices/tests/unittest/fixtures/java/android/adservices/customaudience/CustomAudienceFixture.java +++ b/adservices/tests/unittest/fixtures/java/android/adservices/customaudience/CustomAudienceFixture.java @@ -16,6 +16,10 @@ package android.adservices.customaudience; +import static android.adservices.common.AdDataFixture.getValidFilterAdDataWithAdRenderIdByBuyer; +import static android.adservices.customaudience.TrustedBiddingDataFixture.getValidTrustedBiddingDataByBuyer; + +import android.adservices.common.AdData; import android.adservices.common.AdDataFixture; import android.adservices.common.AdSelectionSignals; import android.adservices.common.AdTechIdentifier; @@ -24,7 +28,9 @@ import android.net.Uri; import java.time.Duration; import java.time.Instant; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; /** Utility class supporting custom audience API unit tests */ public final class CustomAudienceFixture { @@ -96,8 +102,7 @@ public final class CustomAudienceFixture { .setExpirationTime(CustomAudienceFixture.VALID_EXPIRATION_TIME) .setDailyUpdateUri(CustomAudienceFixture.getValidDailyUpdateUriByBuyer(buyer)) .setUserBiddingSignals(CustomAudienceFixture.VALID_USER_BIDDING_SIGNALS) - .setTrustedBiddingData( - TrustedBiddingDataFixture.getValidTrustedBiddingDataByBuyer(buyer)) + .setTrustedBiddingData(getValidTrustedBiddingDataByBuyer(buyer)) .setBiddingLogicUri(CustomAudienceFixture.getValidBiddingLogicUriByBuyer(buyer)) .setAds(AdDataFixture.getValidAdsByBuyer(buyer)); } @@ -134,9 +139,54 @@ public final class CustomAudienceFixture { .setExpirationTime(CustomAudienceFixture.VALID_EXPIRATION_TIME) .setDailyUpdateUri(CustomAudienceFixture.getValidDailyUpdateUriByBuyer(buyer)) .setUserBiddingSignals(CustomAudienceFixture.VALID_USER_BIDDING_SIGNALS) - .setTrustedBiddingData( - TrustedBiddingDataFixture.getValidTrustedBiddingDataByBuyer(buyer)) + .setTrustedBiddingData(getValidTrustedBiddingDataByBuyer(buyer)) .setBiddingLogicUri(CustomAudienceFixture.getValidBiddingLogicUriByBuyer(buyer)) .setAds(AdDataFixture.getValidFilterAdsByBuyer(buyer)); } + + /** Build valid CA with filters and render id */ + public static CustomAudience.Builder getValidBuilderForBuyerFiltersWithAdRenderId( + AdTechIdentifier buyer) { + return new CustomAudience.Builder() + .setBuyer(buyer) + .setName(CustomAudienceFixture.VALID_NAME) + .setActivationTime(CustomAudienceFixture.VALID_ACTIVATION_TIME) + .setExpirationTime(CustomAudienceFixture.VALID_EXPIRATION_TIME) + .setDailyUpdateUri(CustomAudienceFixture.getValidDailyUpdateUriByBuyer(buyer)) + .setUserBiddingSignals(CustomAudienceFixture.VALID_USER_BIDDING_SIGNALS) + .setTrustedBiddingData(getValidTrustedBiddingDataByBuyer(buyer)) + .setBiddingLogicUri(CustomAudienceFixture.getValidBiddingLogicUriByBuyer(buyer)) + .setAds(AdDataFixture.getValidFilterAdsWithAdRenderIdByBuyer(buyer)); + } + + /** Build N valid CAs with filters and render id */ + public static List<CustomAudience> getNValidCustomAudiences( + int nBuyers, int nCAsPerBuyer, int nAdsPerCA) { + List<CustomAudience> customAudiences = new ArrayList<>(); + for (int b = 0; b < nBuyers; b++) { + AdTechIdentifier buyer = AdTechIdentifier.fromString("buyer%d.com".formatted(b)); + for (int c = 0; c < nCAsPerBuyer; c++) { + List<AdData> ads = new ArrayList<>(); + for (int a = 0; a < nAdsPerCA; a++) { + ads.add( + getValidFilterAdDataWithAdRenderIdByBuyer( + buyer, /* sequenceNumber= */ a)); + } + CustomAudience customAudience = + new CustomAudience.Builder() + .setBuyer(buyer) + .setName(VALID_NAME) + .setActivationTime(VALID_ACTIVATION_TIME) + .setExpirationTime(VALID_EXPIRATION_TIME) + .setDailyUpdateUri(getValidDailyUpdateUriByBuyer(buyer)) + .setBiddingLogicUri(getValidBiddingLogicUriByBuyer(buyer)) + .setUserBiddingSignals(VALID_USER_BIDDING_SIGNALS) + .setTrustedBiddingData(getValidTrustedBiddingDataByBuyer(buyer)) + .setAds(ads) + .build(); + customAudiences.add(customAudience); + } + } + return customAudiences; + } } diff --git a/adservices/tests/unittest/fixtures/java/com/android/adservices/service/PhFlagsFixture.java b/adservices/tests/unittest/fixtures/java/com/android/adservices/service/PhFlagsFixture.java index a1ddecae4..d9be9de3f 100644 --- a/adservices/tests/unittest/fixtures/java/com/android/adservices/service/PhFlagsFixture.java +++ b/adservices/tests/unittest/fixtures/java/com/android/adservices/service/PhFlagsFixture.java @@ -28,6 +28,7 @@ import static com.android.adservices.service.Flags.FLEDGE_BACKGROUND_FETCH_NETWO import static com.android.adservices.service.Flags.FLEDGE_REPORT_IMPRESSION_OVERALL_TIMEOUT_MS; import static com.android.adservices.service.Flags.SDK_REQUEST_PERMITS_PER_SECOND; import static com.android.adservices.service.FlagsConstants.KEY_AD_ID_FETCHER_TIMEOUT_MS; +import static com.android.adservices.service.FlagsConstants.KEY_DISABLE_FLEDGE_ENROLLMENT_CHECK; import static com.android.adservices.service.FlagsConstants.KEY_ENFORCE_FOREGROUND_STATUS_FLEDGE_OVERRIDE; import static com.android.adservices.service.FlagsConstants.KEY_ENFORCE_FOREGROUND_STATUS_FLEDGE_REPORT_IMPRESSION; import static com.android.adservices.service.FlagsConstants.KEY_ENFORCE_FOREGROUND_STATUS_FLEDGE_REPORT_INTERACTION; @@ -35,22 +36,12 @@ import static com.android.adservices.service.FlagsConstants.KEY_ENFORCE_FOREGROU import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_AD_SELECTION_FILTERING_ENABLED; import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_AD_SELECTION_PREBUILT_URI_ENABLED; import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_AUCTION_SERVER_KILL_SWITCH; -import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_BACKGROUND_FETCH_ELIGIBLE_UPDATE_BASE_INTERVAL_S; import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_CPC_BILLING_ENABLED; -import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_CUSTOM_AUDIENCE_MAX_COUNT; -import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_CUSTOM_AUDIENCE_MAX_NAME_SIZE_B; -import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_CUSTOM_AUDIENCE_MAX_NUM_ADS; -import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_CUSTOM_AUDIENCE_MAX_OWNER_COUNT; -import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_CUSTOM_AUDIENCE_PER_APP_MAX_COUNT; import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_DATA_VERSION_HEADER_ENABLED; -import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_FETCH_CUSTOM_AUDIENCE_ENABLED; -import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_FETCH_CUSTOM_AUDIENCE_MAX_USER_BIDDING_SIGNALS_SIZE_B; import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_REGISTER_AD_BEACON_ENABLED; import static com.android.adservices.service.FlagsConstants.KEY_PROTECTED_SIGNALS_CLEANUP_ENABLED; import static com.android.adservices.service.FlagsConstants.KEY_SDK_REQUEST_PERMITS_PER_SECOND; -import static org.junit.Assert.assertEquals; - import android.provider.DeviceConfig; /** @@ -97,21 +88,6 @@ public final class PhFlagsFixture { FLEDGE_AUCTION_SERVER_BACKGROUND_KEY_FETCH_NETWORK_CONNECT_TIMEOUT_MS + (int) ADDITIONAL_TIMEOUT; - public static void configureFledgeBackgroundFetchEligibleUpdateBaseIntervalS( - final long phOverridingValue) { - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_ADSERVICES, - KEY_FLEDGE_BACKGROUND_FETCH_ELIGIBLE_UPDATE_BASE_INTERVAL_S, - Long.toString(phOverridingValue), - /* makeDefault */ false); - - Flags phFlags = FlagsFactory.getFlags(); - assertEquals( - "Failed to configure P/H flag", - phOverridingValue, - phFlags.getFledgeBackgroundFetchEligibleUpdateBaseIntervalS()); - } - /** Enables test to override the flag enabling ad selection filtering */ public static void overrideFledgeAdSelectionFilteringEnabled(boolean value) { DeviceConfig.setProperty( @@ -182,19 +158,6 @@ public final class PhFlagsFixture { } /** - * Allows tests to override seed enrollment data flag thereby seeding data into enrollment table - * - * @param enable disable enrollment seed - */ - public static void overrideEnableEnrollmentSeed(boolean enable) { - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_ADSERVICES, - FlagsConstants.KEY_ENABLE_ENROLLMENT_TEST_SEED, - Boolean.toString(enable), - false); - } - - /** * Enables test to override the flag enabling the enrollment check for callers of Fledge APIs. * * @param enable whether enable or disable the check @@ -202,7 +165,7 @@ public final class PhFlagsFixture { public static void overrideFledgeEnrollmentCheck(boolean enable) { DeviceConfig.setProperty( DeviceConfig.NAMESPACE_ADSERVICES, - FlagsConstants.KEY_DISABLE_FLEDGE_ENROLLMENT_CHECK, + KEY_DISABLE_FLEDGE_ENROLLMENT_CHECK, Boolean.toString(!enable), false); } @@ -310,69 +273,6 @@ public final class PhFlagsFixture { true); } - /** Switches the fetchAndJoinCustomAudience API on/off. */ - public static void overrideFledgeFetchCustomAudienceEnabled(boolean value) { - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_ADSERVICES, - KEY_FLEDGE_FETCH_CUSTOM_AUDIENCE_ENABLED, - Boolean.toString(value), - true); - } - - /** Configures the maximum size of a custom audience's name. */ - public static void overrideFledgeCustomAudienceMaxNameSizeB(int value) { - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_ADSERVICES, - KEY_FLEDGE_CUSTOM_AUDIENCE_MAX_NAME_SIZE_B, - Integer.toString(value), - true); - } - - /** Configures the maximum size of a custom audience's user bidding signals. */ - public static void overrideFledgeFetchCustomAudienceMaxUserBiddingSignalsSizeB(int value) { - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_ADSERVICES, - KEY_FLEDGE_FETCH_CUSTOM_AUDIENCE_MAX_USER_BIDDING_SIGNALS_SIZE_B, - Integer.toString(value), - true); - } - - /** Configures the maximum number of ads allowed per custom audience. */ - public static void overrideFledgeCustomAudienceMaxNumAds(int value) { - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_ADSERVICES, - KEY_FLEDGE_CUSTOM_AUDIENCE_MAX_NUM_ADS, - Integer.toString(value), - true); - } - - /** Configures the maximum total number of custom audiences in the datastore. */ - public static void overrideFledgeCustomAudienceMaxCount(int value) { - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_ADSERVICES, - KEY_FLEDGE_CUSTOM_AUDIENCE_MAX_COUNT, - Integer.toString(value), - true); - } - - /** Configures the maximum number of custom audiences per owner application in the datastore. */ - public static void overrideFledgeCustomAudiencePerAppMaxCount(int value) { - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_ADSERVICES, - KEY_FLEDGE_CUSTOM_AUDIENCE_PER_APP_MAX_COUNT, - Integer.toString(value), - true); - } - - /** Configures the maximum number of owner applications in the datastore. */ - public static void overrideFledgeCustomAudienceMaxOwnerCount(int value) { - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_ADSERVICES, - KEY_FLEDGE_CUSTOM_AUDIENCE_MAX_OWNER_COUNT, - Integer.toString(value), - true); - } - /** Overrides whether the {@code registerAdBeacon} feature is enabled. */ public static void overrideFledgeRegisterAdBeaconEnabled(boolean value) { DeviceConfig.setProperty( diff --git a/adservices/tests/unittest/fixtures/java/com/android/adservices/service/measurement/AsyncRegistrationFixture.java b/adservices/tests/unittest/fixtures/java/com/android/adservices/service/measurement/AsyncRegistrationFixture.java index bd7aaf89a..1b5ddcb20 100644 --- a/adservices/tests/unittest/fixtures/java/com/android/adservices/service/measurement/AsyncRegistrationFixture.java +++ b/adservices/tests/unittest/fixtures/java/com/android/adservices/service/measurement/AsyncRegistrationFixture.java @@ -18,6 +18,7 @@ package com.android.adservices.service.measurement; import android.net.Uri; +import com.android.adservices.service.measurement.registration.AsyncRedirect; import com.android.adservices.service.measurement.registration.AsyncRegistration; import java.util.UUID; @@ -40,7 +41,8 @@ public class AsyncRegistrationFixture { .setType(ValidAsyncRegistrationParams.TYPE) .setDebugKeyAllowed(ValidAsyncRegistrationParams.DEBUG_KEY_ALLOWED) .setRegistrationId(ValidAsyncRegistrationParams.REGISTRATION_ID) - .setPostBody(ValidAsyncRegistrationParams.POST_BODY); + .setPostBody(ValidAsyncRegistrationParams.POST_BODY) + .setRedirectBehavior(ValidAsyncRegistrationParams.REDIRECT_BEHAVIOR); } public static AsyncRegistration getValidAsyncRegistration() { @@ -62,5 +64,7 @@ public class AsyncRegistrationFixture { public static final String REGISTRATION_ID = "R1"; public static final String PLATFORM_AD_ID = "test-platform-ad-id"; public static final String POST_BODY = "{\"ad_location\":\"bottom_right\"}"; + public static final AsyncRedirect.RedirectBehavior REDIRECT_BEHAVIOR = + AsyncRedirect.RedirectBehavior.AS_IS; } } diff --git a/adservices/tests/unittest/framework/src/android/adservices/adselection/SignedContextualAdsTest.java b/adservices/tests/unittest/framework/src/android/adservices/adselection/SignedContextualAdsTest.java index f46a51cf6..3362c47c0 100644 --- a/adservices/tests/unittest/framework/src/android/adservices/adselection/SignedContextualAdsTest.java +++ b/adservices/tests/unittest/framework/src/android/adservices/adselection/SignedContextualAdsTest.java @@ -16,9 +16,10 @@ package android.adservices.adselection; -import static junit.framework.Assert.assertEquals; +import static android.adservices.adselection.SignedContextualAdsFixture.AD_WITH_BID_1; + +import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertThrows; import android.adservices.common.CommonFixture; @@ -26,8 +27,11 @@ import android.os.Parcel; import org.junit.Test; +import java.util.Collections; + /** Unit tests for {@link SignedContextualAds} */ public class SignedContextualAdsTest { + public static final byte[] TEST_SIGNATURE = new byte[] {0, 1, 2}; @Test public void testBuildContextualAdsSuccess() { @@ -36,156 +40,141 @@ public class SignedContextualAdsTest { .setBuyer(SignedContextualAdsFixture.BUYER) .setDecisionLogicUri(SignedContextualAdsFixture.DECISION_LOGIC_URI) .setAdsWithBid(SignedContextualAdsFixture.ADS_WITH_BID) - .setSignature(SignedContextualAdsFixture.PLACEHOLDER_SIGNATURE) + .setSignature(TEST_SIGNATURE) .build(); - assertEquals(contextualAds.getBuyer(), SignedContextualAdsFixture.BUYER); - assertEquals( - contextualAds.getDecisionLogicUri(), SignedContextualAdsFixture.DECISION_LOGIC_URI); - assertEquals(contextualAds.getAdsWithBid(), SignedContextualAdsFixture.ADS_WITH_BID); - assertArrayEquals( - contextualAds.getSignature(), SignedContextualAdsFixture.PLACEHOLDER_SIGNATURE); + assertThat(contextualAds.getBuyer()).isEqualTo(SignedContextualAdsFixture.BUYER); + assertThat(contextualAds.getDecisionLogicUri()) + .isEqualTo(SignedContextualAdsFixture.DECISION_LOGIC_URI); + assertThat(contextualAds.getAdsWithBid()) + .isEqualTo(SignedContextualAdsFixture.ADS_WITH_BID); + assertThat(contextualAds.getSignature()).isEqualTo(TEST_SIGNATURE); } @Test public void testParcelValidContextualAdsSuccess() { - SignedContextualAds contextualAds = SignedContextualAdsFixture.aSignedContextualAd(); + SignedContextualAds contextualAds = SignedContextualAdsFixture.aSignedContextualAds(); Parcel p = Parcel.obtain(); contextualAds.writeToParcel(p, 0); p.setDataPosition(0); SignedContextualAds fromParcel = SignedContextualAds.CREATOR.createFromParcel(p); - assertEquals(contextualAds.getBuyer(), fromParcel.getBuyer()); - assertEquals(contextualAds.getDecisionLogicUri(), fromParcel.getDecisionLogicUri()); - assertEquals(contextualAds.getAdsWithBid(), fromParcel.getAdsWithBid()); - assertArrayEquals(contextualAds.getSignature(), fromParcel.getSignature()); + assertThat(fromParcel.getBuyer()).isEqualTo(contextualAds.getBuyer()); + assertThat(fromParcel.getDecisionLogicUri()).isEqualTo(contextualAds.getDecisionLogicUri()); + assertThat(fromParcel.getAdsWithBid()).isEqualTo(contextualAds.getAdsWithBid()); + assertThat(fromParcel.getSignature()).isEqualTo(contextualAds.getSignature()); } @Test public void testSetContextualAdsNullBuyerFailure() { assertThrows( - NullPointerException.class, - () -> { - new SignedContextualAds.Builder().setBuyer(null); - }); + NullPointerException.class, () -> new SignedContextualAds.Builder().setBuyer(null)); } @Test public void testSetContextualAdsNullDecisionLogicUriFailure() { assertThrows( NullPointerException.class, - () -> { - new SignedContextualAds.Builder().setDecisionLogicUri(null); - }); + () -> new SignedContextualAds.Builder().setDecisionLogicUri(null)); } @Test public void testSetContextualAdsNullAdWithBidFailure() { assertThrows( NullPointerException.class, - () -> { - new SignedContextualAds.Builder().setAdsWithBid(null); - }); + () -> new SignedContextualAds.Builder().setAdsWithBid(null)); } @Test public void testSetContextualAdsNullSignatureFailure() { assertThrows( NullPointerException.class, - () -> { - new SignedContextualAds.Builder().setSignature(null); - }); + () -> new SignedContextualAds.Builder().setSignature(null)); } @Test public void testParcelNullDestFailure() { - SignedContextualAds contextualAds = SignedContextualAdsFixture.aSignedContextualAd(); + SignedContextualAds contextualAds = SignedContextualAdsFixture.aSignedContextualAds(); Parcel nullDest = null; - assertThrows( - NullPointerException.class, - () -> { - contextualAds.writeToParcel(nullDest, 0); - }); + assertThrows(NullPointerException.class, () -> contextualAds.writeToParcel(nullDest, 0)); } @Test public void testBuildContextualAdsUnsetBuyerFailure() { assertThrows( NullPointerException.class, - () -> { - new SignedContextualAds.Builder() - .setDecisionLogicUri(SignedContextualAdsFixture.DECISION_LOGIC_URI) - .setAdsWithBid(SignedContextualAdsFixture.ADS_WITH_BID) - .setSignature(SignedContextualAdsFixture.PLACEHOLDER_SIGNATURE) - .build(); - }); + () -> + new SignedContextualAds.Builder() + .setDecisionLogicUri(SignedContextualAdsFixture.DECISION_LOGIC_URI) + .setAdsWithBid(SignedContextualAdsFixture.ADS_WITH_BID) + .setSignature(TEST_SIGNATURE) + .build()); } @Test public void testBuildContextualAdsUnsetDecisionLogicUriFailure() { assertThrows( NullPointerException.class, - () -> { - new SignedContextualAds.Builder() - .setBuyer(SignedContextualAdsFixture.BUYER) - .setAdsWithBid(SignedContextualAdsFixture.ADS_WITH_BID) - .setSignature(SignedContextualAdsFixture.PLACEHOLDER_SIGNATURE) - .build(); - }); + () -> + new SignedContextualAds.Builder() + .setBuyer(SignedContextualAdsFixture.BUYER) + .setAdsWithBid(SignedContextualAdsFixture.ADS_WITH_BID) + .setSignature(TEST_SIGNATURE) + .build()); } @Test public void testBuildContextualAdsUnsetAdWithBidFailure() { assertThrows( NullPointerException.class, - () -> { - new SignedContextualAds.Builder() - .setBuyer(SignedContextualAdsFixture.BUYER) - .setDecisionLogicUri(SignedContextualAdsFixture.DECISION_LOGIC_URI) - .setSignature(SignedContextualAdsFixture.PLACEHOLDER_SIGNATURE) - .build(); - }); + () -> + new SignedContextualAds.Builder() + .setBuyer(SignedContextualAdsFixture.BUYER) + .setDecisionLogicUri(SignedContextualAdsFixture.DECISION_LOGIC_URI) + .setSignature(TEST_SIGNATURE) + .build()); } @Test public void testBuildContextualAdsUnsetSignatureFailure() { assertThrows( NullPointerException.class, - () -> { - new SignedContextualAds.Builder() - .setBuyer(SignedContextualAdsFixture.BUYER) - .setDecisionLogicUri(SignedContextualAdsFixture.DECISION_LOGIC_URI) - .setAdsWithBid(SignedContextualAdsFixture.ADS_WITH_BID) - .build(); - }); + () -> + new SignedContextualAds.Builder() + .setBuyer(SignedContextualAdsFixture.BUYER) + .setDecisionLogicUri(SignedContextualAdsFixture.DECISION_LOGIC_URI) + .setAdsWithBid(SignedContextualAdsFixture.ADS_WITH_BID) + .build()); } @Test public void testContextualAdsDescribeContents() { - SignedContextualAds obj = SignedContextualAdsFixture.aSignedContextualAd(); + SignedContextualAds obj = SignedContextualAdsFixture.aSignedContextualAds(); - assertEquals(obj.describeContents(), 0); + assertThat(obj.describeContents()).isEqualTo(0); } @Test public void testContextualAdsHaveSameHashCode() { - SignedContextualAds obj1 = SignedContextualAdsFixture.aSignedContextualAd(); - SignedContextualAds obj2 = SignedContextualAdsFixture.aSignedContextualAd(); + SignedContextualAds obj1 = + SignedContextualAdsFixture.aContextualAdsWithEmptySignatureBuilder().build(); + SignedContextualAds obj2 = + SignedContextualAdsFixture.aContextualAdsWithEmptySignatureBuilder().build(); CommonFixture.assertHaveSameHashCode(obj1, obj2); } @Test public void testContextualAdsHaveDifferentHashCode() { - SignedContextualAds obj1 = SignedContextualAdsFixture.aSignedContextualAd(); + SignedContextualAds obj1 = SignedContextualAdsFixture.aSignedContextualAds(); SignedContextualAds obj2 = - SignedContextualAdsFixture.aSignedContextualAdBuilder() + SignedContextualAdsFixture.aContextualAdsWithEmptySignatureBuilder() .setBuyer(SignedContextualAdsFixture.BUYER_2) .build(); SignedContextualAds obj3 = - SignedContextualAdsFixture.aSignedContextualAdBuilder() - .setSignature(new byte[] {1, 2, 3}) + SignedContextualAdsFixture.aContextualAdsWithEmptySignatureBuilder() + .setAdsWithBid(Collections.singletonList(AD_WITH_BID_1)) .build(); CommonFixture.assertDifferentHashCode(obj1, obj2, obj3); diff --git a/adservices/tests/unittest/service-core/AndroidManifest.xml b/adservices/tests/unittest/service-core/AndroidManifest.xml index 44a4d197f..7be4ea18e 100644 --- a/adservices/tests/unittest/service-core/AndroidManifest.xml +++ b/adservices/tests/unittest/service-core/AndroidManifest.xml @@ -24,7 +24,6 @@ <uses-permission android:name="android.permission.ACCESS_ADSERVICES_AD_ID" /> <uses-permission android:name="android.permission.ACCESS_ADSERVICES_TOPICS" /> <uses-permission android:name="android.permission.ACCESS_ADSERVICES_CUSTOM_AUDIENCE" /> - <uses-permission android:name="android.permission.ACCESS_ADSERVICES_PROTECTED_SIGNALS" /> <!-- Needed to resolve Intent to ComponentName for measurement verifiedDestination --> <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> diff --git a/adservices/tests/unittest/service-core/appsearch/src/com/android/adservices/service/appsearch/AppSearchConsentWorkerTest.java b/adservices/tests/unittest/service-core/appsearch/src/com/android/adservices/service/appsearch/AppSearchConsentWorkerTest.java index e7344abe1..2bf1221c7 100644 --- a/adservices/tests/unittest/service-core/appsearch/src/com/android/adservices/service/appsearch/AppSearchConsentWorkerTest.java +++ b/adservices/tests/unittest/service-core/appsearch/src/com/android/adservices/service/appsearch/AppSearchConsentWorkerTest.java @@ -64,6 +64,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.FluentFuture; 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.junit.Before; import org.junit.Test; @@ -73,6 +75,9 @@ import org.mockito.Mockito; import java.util.Arrays; import java.util.List; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; @SpyStatic(FlagsFactory.class) @RequiresSdkLevelAtLeastS @@ -87,6 +92,8 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT private static final Topic TOPIC1 = Topic.create(0, 1, 11); private static final Topic TOPIC2 = Topic.create(12, 2, 22); private static final Topic TOPIC3 = Topic.create(123, 3, 33); + private static final int APPSEARCH_WRITE_TIMEOUT_MS = 1000; + private final List<Topic> mTopics = Arrays.asList(TOPIC1, TOPIC2, TOPIC3); @Mock private Flags mMockFlags; @@ -97,6 +104,8 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT when(mMockFlags.getAdservicesApkShaCertificate()) .thenReturn(Flags.ADSERVICES_APK_SHA_CERTIFICATE); when(mMockFlags.getAppsearchWriterAllowListOverride()).thenReturn(""); + // Reduce AppSearch write timeout to speed up the tests. + when(mMockFlags.getAppSearchWriteTimeout()).thenReturn(APPSEARCH_WRITE_TIMEOUT_MS); } @Test @@ -144,6 +153,21 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT @Test @SpyStatic(PlatformStorage.class) + @SpyStatic(UserHandle.class) + public void testSetConsent_failure_timeout() { + initTimeoutResponse(); + + RuntimeException e = + assertThrows( + RuntimeException.class, + () -> AppSearchConsentWorker.getInstance().setConsent(API_TYPE, CONSENTED)); + assertThat(e.getMessage()).isEqualTo(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + assertThat(e.getCause()).isNotNull(); + assertThat(e.getCause()).isInstanceOf(TimeoutException.class); + } + + @Test + @SpyStatic(PlatformStorage.class) @MockStatic(UserHandle.class) public void testSetConsent() { initSuccessResponse(); @@ -285,6 +309,8 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT RuntimeException.class, () -> appSearchConsentWorker.clearAppsWithConsent(TEST)); assertThat(e.getMessage()).isEqualTo(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + assertThat(e.getCause()).isNotNull(); + assertThat(e.getCause()).isInstanceOf(ExecutionException.class); } @Test @@ -408,6 +434,8 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT RuntimeException.class, () -> appSearchConsentWorker.removeAppWithConsent(consentType, TEST)); assertThat(e.getMessage()).isEqualTo(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + assertThat(e.getCause()).isNotNull(); + assertThat(e.getCause()).isInstanceOf(ExecutionException.class); } @Test @@ -488,7 +516,7 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT @Test @SpyStatic(PlatformStorage.class) - public void testRecordGaUxNotificationDisplayed_faiure() { + public void testRecordGaUxNotificationDisplayed_failure() { runRecordNotificationDisplayedTestFailure(/* isBetaUx= */ false); } @@ -511,6 +539,41 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT } @Test + @SpyStatic(PlatformStorage.class) + @SpyStatic(UserHandle.class) + public void testRecordNotificationDisplayed_failure_timeout() { + runRecordNotificationDisplayedTestFailureTimeout(/* isBetaUx= */ true); + } + + @Test + @SpyStatic(PlatformStorage.class) + @SpyStatic(UserHandle.class) + public void testRecordGaUxNotificationDisplayed_failure_timeout() { + runRecordNotificationDisplayedTestFailureTimeout(/* isBetaUx= */ false); + } + + private void runRecordNotificationDisplayedTestFailureTimeout(boolean isBetaUx) { + initTimeoutResponse(); + AppSearchConsentWorker worker = AppSearchConsentWorker.getInstance(); + + RuntimeException e; + if (isBetaUx) { + e = + assertThrows( + RuntimeException.class, () -> worker.recordNotificationDisplayed(true)); + } else { + e = + assertThrows( + RuntimeException.class, + () -> worker.recordGaUxNotificationDisplayed(true)); + } + + assertThat(e.getMessage()).isEqualTo(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + assertThat(e.getCause()).isNotNull(); + assertThat(e.getCause()).isInstanceOf(TimeoutException.class); + } + + @Test @MockStatic(AppSearchNotificationDao.class) @SpyStatic(PlatformStorage.class) @SpyStatic(UserHandle.class) @@ -574,6 +637,24 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT @Test @SpyStatic(PlatformStorage.class) @SpyStatic(UserHandle.class) + public void testSetCurrentPrivacySandboxFeature_failure_timeout() { + initTimeoutResponse(); + + AppSearchConsentWorker worker = AppSearchConsentWorker.getInstance(); + RuntimeException e = + assertThrows( + RuntimeException.class, + () -> + worker.setCurrentPrivacySandboxFeature( + PrivacySandboxFeatureType.PRIVACY_SANDBOX_RECONSENT)); + assertThat(e.getMessage()).isEqualTo(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + assertThat(e.getCause()).isNotNull(); + assertThat(e.getCause()).isInstanceOf(TimeoutException.class); + } + + @Test + @SpyStatic(PlatformStorage.class) + @SpyStatic(UserHandle.class) public void testSetCurrentPrivacySandboxFeature() { initSuccessResponse(); @@ -616,6 +697,27 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT @MockStatic(AppSearchInteractionsDao.class) @SpyStatic(PlatformStorage.class) @SpyStatic(UserHandle.class) + public void testRecordUserManualInteractionWithConsent_failure_timeout() { + initTimeoutResponse(); + + when(AppSearchInteractionsDao.getRowId(any(), any())).thenReturn("" + UID); + + AppSearchConsentWorker worker = AppSearchConsentWorker.getInstance(); + int interactions = ConsentManager.MANUAL_INTERACTIONS_RECORDED; + + RuntimeException e = + assertThrows( + RuntimeException.class, + () -> worker.recordUserManualInteractionWithConsent(interactions)); + assertThat(e.getMessage()).isEqualTo(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + assertThat(e.getCause()).isNotNull(); + assertThat(e.getCause()).isInstanceOf(TimeoutException.class); + } + + @Test + @MockStatic(AppSearchInteractionsDao.class) + @SpyStatic(PlatformStorage.class) + @SpyStatic(UserHandle.class) public void testRecordUserManualInteractionWithConsent() { initSuccessResponse(); @@ -638,7 +740,7 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT @Test @SpyStatic(PlatformStorage.class) - public void testRecordBlockedTopics_failure() { + public void testRecordBlockedTopic_failure() { initFailureResponse(); AppSearchConsentWorker worker = AppSearchConsentWorker.getInstance(); @@ -651,7 +753,27 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT @MockStatic(AppSearchTopicsConsentDao.class) @SpyStatic(PlatformStorage.class) @SpyStatic(UserHandle.class) - public void testRecordBlockedTopics_new() { + public void testRecordBlockedTopic_failure_timeout() { + initTimeoutResponse(); + + String query = "" + UID; + ExtendedMockito.doReturn(query).when(() -> AppSearchTopicsConsentDao.getQuery(any())); + ExtendedMockito.doReturn(null) + .when(() -> AppSearchTopicsConsentDao.readConsentData(any(), any(), any(), any())); + + AppSearchConsentWorker worker = AppSearchConsentWorker.getInstance(); + RuntimeException e = + assertThrows(RuntimeException.class, () -> worker.recordBlockedTopic(TOPIC1)); + assertThat(e.getMessage()).isEqualTo(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + assertThat(e.getCause()).isNotNull(); + assertThat(e.getCause()).isInstanceOf(TimeoutException.class); + } + + @Test + @MockStatic(AppSearchTopicsConsentDao.class) + @SpyStatic(PlatformStorage.class) + @SpyStatic(UserHandle.class) + public void testRecordBlockedTopic_new() { initSuccessResponse(); String query = "" + UID; @@ -667,7 +789,7 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT @MockStatic(AppSearchTopicsConsentDao.class) @SpyStatic(PlatformStorage.class) @SpyStatic(UserHandle.class) - public void testRecordBlockedTopics() throws Exception { + public void testRecordBlockedTopic() { String query = "" + UID; ExtendedMockito.doReturn(query).when(() -> AppSearchTopicsConsentDao.getQuery(any())); AppSearchTopicsConsentDao dao = Mockito.mock(AppSearchTopicsConsentDao.class); @@ -686,7 +808,7 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT @Test @MockStatic(AppSearchTopicsConsentDao.class) @SpyStatic(PlatformStorage.class) - public void testRecordUnblockedTopics_failure() throws Exception { + public void testRecordUnblockedTopic_failure() throws Exception { AppSearchTopicsConsentDao dao = Mockito.mock(AppSearchTopicsConsentDao.class); ExtendedMockito.doReturn(dao) .when(() -> AppSearchTopicsConsentDao.readConsentData(any(), any(), any(), any())); @@ -702,13 +824,21 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT verify(dao).removeBlockedTopic(TOPIC1); verify(dao).writeData(any(), any(), any()); assertThat(e.getMessage()).isEqualTo(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + + Throwable cause = e.getCause(); + assertThat(cause).isNotNull(); + assertThat(cause).isInstanceOf(ExecutionException.class); + + Throwable rootCause = cause.getCause(); + assertThat(rootCause).isNotNull(); + assertThat(rootCause).isInstanceOf(InterruptedException.class); } @Test @MockStatic(AppSearchTopicsConsentDao.class) @SpyStatic(PlatformStorage.class) @SpyStatic(UserHandle.class) - public void testRecordUnblockedTopics_new() { + public void testRecordUnblockedTopic_new() { String query = "" + UID; ExtendedMockito.doReturn(query).when(() -> AppSearchTopicsConsentDao.getQuery(any())); ExtendedMockito.doReturn(null) @@ -722,7 +852,7 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT @MockStatic(AppSearchTopicsConsentDao.class) @SpyStatic(PlatformStorage.class) @SpyStatic(UserHandle.class) - public void testRecordUnblockedTopics() { + public void testRecordUnblockedTopic() { String query = "" + UID; ExtendedMockito.doReturn(query).when(() -> AppSearchTopicsConsentDao.getQuery(any())); AppSearchTopicsConsentDao dao = Mockito.mock(AppSearchTopicsConsentDao.class); @@ -753,6 +883,20 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT @Test @SpyStatic(PlatformStorage.class) @SpyStatic(UserHandle.class) + public void testClearBlockedTopics_failure_timeout() { + initTimeoutResponse(); + + AppSearchConsentWorker worker = AppSearchConsentWorker.getInstance(); + RuntimeException e = + assertThrows(RuntimeException.class, () -> worker.clearBlockedTopics()); + assertThat(e.getMessage()).isEqualTo(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + assertThat(e.getCause()).isNotNull(); + assertThat(e.getCause()).isInstanceOf(TimeoutException.class); + } + + @Test + @SpyStatic(PlatformStorage.class) + @SpyStatic(UserHandle.class) public void testClearBlockedTopics() { initSuccessResponse(); @@ -781,6 +925,26 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT when(mockResponse.getMigrationFailures()).thenReturn(List.of()); } + private void initTimeoutResponse() { + AppSearchSession mockSession = Mockito.mock(AppSearchSession.class); + UserHandle mockUserHandle = Mockito.mock(UserHandle.class); + Mockito.when(UserHandle.getUserHandleForUid(Binder.getCallingUid())) + .thenReturn(mockUserHandle); + Mockito.when(mockUserHandle.getIdentifier()).thenReturn(UID); + ExtendedMockito.doReturn(Futures.immediateFuture(mockSession)) + .when(() -> PlatformStorage.createSearchSessionAsync(any())); + verify(mockSession, atMost(1)).setSchemaAsync(any(SetSchemaRequest.class)); + + SetSchemaResponse mockResponse = Mockito.mock(SetSchemaResponse.class); + when(mockSession.setSchemaAsync(any(SetSchemaRequest.class))) + .thenReturn(Futures.immediateFuture(mockResponse)); + AppSearchBatchResult<String, Void> result = Mockito.mock(AppSearchBatchResult.class); + when(mockSession.putAsync(any())).thenReturn(getLongRunningOperation(result)); + + verify(mockResponse, atMost(1)).getMigrationFailures(); + when(mockResponse.getMigrationFailures()).thenReturn(List.of()); + } + private void initFailureResponse() { AppSearchSession mockSession = Mockito.mock(AppSearchSession.class); ExtendedMockito.doReturn(Futures.immediateFuture(mockSession)) @@ -801,6 +965,17 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT when(mockResponse.getMigrationFailures()).thenReturn(List.of(failure)); } + private <T> ListenableFuture<T> getLongRunningOperation(T result) { + // Wait for a time that's longer than the AppSearch write timeout, then return the result. + ListeningExecutorService ls = + MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()); + return ls.submit( + () -> { + TimeUnit.MILLISECONDS.sleep(APPSEARCH_WRITE_TIMEOUT_MS + 500); + return result; + }); + } + @Test @SpyStatic(AppSearchUxStatesDao.class) public void isAdIdEnabledTest_trueBit() { @@ -826,8 +1001,6 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT @SpyStatic(PlatformStorage.class) @SpyStatic(UserHandle.class) public void setAdIdEnabledTest_success() { - initSuccessResponse(); - String query = "" + UID; ExtendedMockito.doReturn(query).when(() -> AppSearchUxStatesDao.getQuery(any())); AppSearchUxStatesDao dao = Mockito.mock(AppSearchUxStatesDao.class); @@ -845,17 +1018,17 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT @Test @SpyStatic(PlatformStorage.class) - public void setAdIdEnabledTest_trueBit() { - setAdIdEnabledTest(true); + public void setAdIdEnabledTest_failure_trueBit() { + setAdIdEnabledTestFailure(true); } @Test @SpyStatic(PlatformStorage.class) - public void setAdIdEnabledTest_falseBit() { - setAdIdEnabledTest(false); + public void setAdIdEnabledTest_failure_falseBit() { + setAdIdEnabledTestFailure(false); } - private void setAdIdEnabledTest(boolean isAdIdEnabled) { + private void setAdIdEnabledTestFailure(boolean isAdIdEnabled) { initFailureResponse(); AppSearchConsentWorker worker = AppSearchConsentWorker.getInstance(); @@ -865,6 +1038,27 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT } @Test + @MockStatic(AppSearchUxStatesDao.class) + @SpyStatic(PlatformStorage.class) + @SpyStatic(UserHandle.class) + public void setAdIdEnabledTest_timeout() { + String query = "" + UID; + ExtendedMockito.doReturn(query).when(() -> AppSearchUxStatesDao.getQuery(any())); + AppSearchUxStatesDao dao = Mockito.mock(AppSearchUxStatesDao.class); + ExtendedMockito.doReturn(dao) + .when(() -> AppSearchUxStatesDao.readData(any(), any(), any(), any())); + when(dao.writeData(any(), any(), any())) + .thenReturn(FluentFuture.from(getLongRunningOperation(null))); + + AppSearchConsentWorker worker = AppSearchConsentWorker.getInstance(); + RuntimeException e = + assertThrows(RuntimeException.class, () -> worker.setAdIdEnabled(true)); + assertThat(e.getMessage()).isEqualTo(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + assertThat(e.getCause()).isNotNull(); + assertThat(e.getCause()).isInstanceOf(TimeoutException.class); + } + + @Test @SpyStatic(AppSearchUxStatesDao.class) public void isU18AccountTest_trueBit() { isU18AccountTest(true); @@ -889,8 +1083,6 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT @SpyStatic(PlatformStorage.class) @SpyStatic(UserHandle.class) public void setU18AccountTest_success() { - initSuccessResponse(); - String query = "" + UID; ExtendedMockito.doReturn(query).when(() -> AppSearchUxStatesDao.getQuery(any())); AppSearchUxStatesDao dao = Mockito.mock(AppSearchUxStatesDao.class); @@ -928,6 +1120,28 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT } @Test + @MockStatic(AppSearchUxStatesDao.class) + @SpyStatic(PlatformStorage.class) + @SpyStatic(UserHandle.class) + public void setU18AccountTest_timeout() { + String query = "" + UID; + ExtendedMockito.doReturn(query).when(() -> AppSearchUxStatesDao.getQuery(any())); + AppSearchUxStatesDao dao = Mockito.mock(AppSearchUxStatesDao.class); + ExtendedMockito.doReturn(dao) + .when(() -> AppSearchUxStatesDao.readData(any(), any(), any(), any())); + AppSearchBatchResult<String, Void> result = Mockito.mock(AppSearchBatchResult.class); + when(dao.writeData(any(), any(), any())) + .thenReturn(FluentFuture.from(getLongRunningOperation(result))); + + AppSearchConsentWorker worker = AppSearchConsentWorker.getInstance(); + RuntimeException e = + assertThrows(RuntimeException.class, () -> worker.setU18Account(false)); + assertThat(e.getMessage()).isEqualTo(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + assertThat(e.getCause()).isNotNull(); + assertThat(e.getCause()).isInstanceOf(TimeoutException.class); + } + + @Test @SpyStatic(AppSearchUxStatesDao.class) public void isEntryPointEnabledTest_trueBit() { isEntryPointEnabledTest(true); @@ -996,6 +1210,30 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT } @Test + @MockStatic(AppSearchUxStatesDao.class) + @SpyStatic(PlatformStorage.class) + @SpyStatic(UserHandle.class) + public void setEntryPointEnabledTest_timeout() { + initSuccessResponse(); + + String query = "" + UID; + ExtendedMockito.doReturn(query).when(() -> AppSearchUxStatesDao.getQuery(any())); + AppSearchUxStatesDao dao = Mockito.mock(AppSearchUxStatesDao.class); + ExtendedMockito.doReturn(dao) + .when(() -> AppSearchUxStatesDao.readData(any(), any(), any(), any())); + AppSearchBatchResult<String, Void> result = Mockito.mock(AppSearchBatchResult.class); + when(dao.writeData(any(), any(), any())) + .thenReturn(FluentFuture.from(getLongRunningOperation(result))); + + AppSearchConsentWorker worker = AppSearchConsentWorker.getInstance(); + RuntimeException e = + assertThrows(RuntimeException.class, () -> worker.setEntryPointEnabled(true)); + assertThat(e.getMessage()).isEqualTo(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + assertThat(e.getCause()).isNotNull(); + assertThat(e.getCause()).isInstanceOf(TimeoutException.class); + } + + @Test @SpyStatic(AppSearchUxStatesDao.class) public void isAdultAccountTest_trueBit() { isAdultAccountTest(true); @@ -1040,7 +1278,7 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT @MockStatic(AppSearchUxStatesDao.class) @SpyStatic(PlatformStorage.class) @SpyStatic(UserHandle.class) - public void setU18AdultAccountTest_success() { + public void setAdultAccountTest_success() { initSuccessResponse(); String query = "" + UID; @@ -1059,6 +1297,30 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT } @Test + @MockStatic(AppSearchUxStatesDao.class) + @SpyStatic(PlatformStorage.class) + @SpyStatic(UserHandle.class) + public void setAdultAccountTest_timeout() { + initSuccessResponse(); + + String query = "" + UID; + ExtendedMockito.doReturn(query).when(() -> AppSearchUxStatesDao.getQuery(any())); + AppSearchUxStatesDao dao = Mockito.mock(AppSearchUxStatesDao.class); + ExtendedMockito.doReturn(dao) + .when(() -> AppSearchUxStatesDao.readData(any(), any(), any(), any())); + AppSearchBatchResult<String, Void> result = Mockito.mock(AppSearchBatchResult.class); + when(dao.writeData(any(), any(), any())) + .thenReturn(FluentFuture.from(getLongRunningOperation(result))); + + AppSearchConsentWorker worker = AppSearchConsentWorker.getInstance(); + RuntimeException e = + assertThrows(RuntimeException.class, () -> worker.setAdultAccount(true)); + assertThat(e.getMessage()).isEqualTo(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + assertThat(e.getCause()).isNotNull(); + assertThat(e.getCause()).isInstanceOf(TimeoutException.class); + } + + @Test @SpyStatic(AppSearchUxStatesDao.class) public void wasU18NotificationDisplayedTest_trueBit() { wasU18NotificationDisplayedTest(true); @@ -1128,6 +1390,31 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT } @Test + @MockStatic(AppSearchUxStatesDao.class) + @SpyStatic(PlatformStorage.class) + @SpyStatic(UserHandle.class) + public void setU18NotificationDisplayedTest_timeout() { + initSuccessResponse(); + + String query = "" + UID; + ExtendedMockito.doReturn(query).when(() -> AppSearchUxStatesDao.getQuery(any())); + AppSearchUxStatesDao dao = Mockito.mock(AppSearchUxStatesDao.class); + ExtendedMockito.doReturn(dao) + .when(() -> AppSearchUxStatesDao.readData(any(), any(), any(), any())); + AppSearchBatchResult<String, Void> result = Mockito.mock(AppSearchBatchResult.class); + when(dao.writeData(any(), any(), any())) + .thenReturn(FluentFuture.from(getLongRunningOperation(result))); + + AppSearchConsentWorker worker = AppSearchConsentWorker.getInstance(); + RuntimeException e = + assertThrows( + RuntimeException.class, () -> worker.setU18NotificationDisplayed(true)); + assertThat(e.getMessage()).isEqualTo(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + assertThat(e.getCause()).isNotNull(); + assertThat(e.getCause()).isInstanceOf(TimeoutException.class); + } + + @Test @SpyStatic(AppSearchUxStatesDao.class) public void getUxTest_allUxs() { for (PrivacySandboxUxCollection ux : PrivacySandboxUxCollection.values()) { @@ -1155,8 +1442,6 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT @SpyStatic(PlatformStorage.class) @SpyStatic(UserHandle.class) public void setUxTest_allUxsSuccess() { - initSuccessResponse(); - for (PrivacySandboxUxCollection ux : PrivacySandboxUxCollection.values()) { String query = "" + UID; ExtendedMockito.doReturn(query).when(() -> AppSearchUxStatesDao.getQuery(any())); @@ -1175,6 +1460,29 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT } @Test + @MockStatic(AppSearchUxStatesDao.class) + @SpyStatic(PlatformStorage.class) + @SpyStatic(UserHandle.class) + public void setUxTest_allUxs_timeout() { + for (PrivacySandboxUxCollection ux : PrivacySandboxUxCollection.values()) { + String query = "" + UID; + ExtendedMockito.doReturn(query).when(() -> AppSearchUxStatesDao.getQuery(any())); + AppSearchUxStatesDao dao = Mockito.mock(AppSearchUxStatesDao.class); + ExtendedMockito.doReturn(dao) + .when(() -> AppSearchUxStatesDao.readData(any(), any(), any(), any())); + AppSearchBatchResult<String, Void> result = Mockito.mock(AppSearchBatchResult.class); + when(dao.writeData(any(), any(), any())) + .thenReturn(FluentFuture.from(getLongRunningOperation(result))); + + AppSearchConsentWorker worker = AppSearchConsentWorker.getInstance(); + RuntimeException e = assertThrows(RuntimeException.class, () -> worker.setUx(ux)); + assertThat(e.getMessage()).isEqualTo(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + assertThat(e.getCause()).isNotNull(); + assertThat(e.getCause()).isInstanceOf(TimeoutException.class); + } + } + + @Test @SpyStatic(AppSearchUxStatesDao.class) public void getEnrollmentChannelTest_allUxsAllEnrollmentChannels() { for (PrivacySandboxUxCollection ux : PrivacySandboxUxCollection.values()) { @@ -1217,8 +1525,6 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT @SpyStatic(PlatformStorage.class) @SpyStatic(UserHandle.class) public void setEnrollmentChannelTest_allUxsAllEnrollmentChannelsSuccess() { - initSuccessResponse(); - for (PrivacySandboxUxCollection ux : PrivacySandboxUxCollection.values()) { for (PrivacySandboxEnrollmentChannelCollection channel : ux.getEnrollmentChannelCollection()) { @@ -1240,4 +1546,35 @@ public final class AppSearchConsentWorkerTest extends AdServicesExtendedMockitoT } } } + + @Test + @MockStatic(AppSearchUxStatesDao.class) + @SpyStatic(PlatformStorage.class) + @SpyStatic(UserHandle.class) + public void setEnrollmentChannelTest_allUxsAllEnrollmentChannels_timeout() { + for (PrivacySandboxUxCollection ux : PrivacySandboxUxCollection.values()) { + for (PrivacySandboxEnrollmentChannelCollection channel : + ux.getEnrollmentChannelCollection()) { + String query = "" + UID; + ExtendedMockito.doReturn(query).when(() -> AppSearchUxStatesDao.getQuery(any())); + AppSearchUxStatesDao dao = Mockito.mock(AppSearchUxStatesDao.class); + ExtendedMockito.doReturn(dao) + .when(() -> AppSearchUxStatesDao.readData(any(), any(), any(), any())); + AppSearchBatchResult<String, Void> result = + Mockito.mock(AppSearchBatchResult.class); + when(dao.writeData(any(), any(), any())) + .thenReturn(FluentFuture.from(getLongRunningOperation(result))); + + AppSearchConsentWorker worker = AppSearchConsentWorker.getInstance(); + RuntimeException e = + assertThrows( + RuntimeException.class, + () -> worker.setEnrollmentChannel(ux, channel)); + assertThat(e.getMessage()) + .isEqualTo(ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + assertThat(e.getCause()).isNotNull(); + assertThat(e.getCause()).isInstanceOf(TimeoutException.class); + } + } + } } diff --git a/adservices/tests/unittest/service-core/appsearch/src/com/android/adservices/service/appsearch/AppSearchDaoTest.java b/adservices/tests/unittest/service-core/appsearch/src/com/android/adservices/service/appsearch/AppSearchDaoTest.java index 47bc18645..116891316 100644 --- a/adservices/tests/unittest/service-core/appsearch/src/com/android/adservices/service/appsearch/AppSearchDaoTest.java +++ b/adservices/tests/unittest/service-core/appsearch/src/com/android/adservices/service/appsearch/AppSearchDaoTest.java @@ -52,6 +52,8 @@ import com.android.adservices.service.consent.ConsentConstants; import com.google.common.util.concurrent.FluentFuture; 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.junit.Before; import org.junit.Rule; @@ -63,6 +65,8 @@ import org.mockito.MockitoAnnotations; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; @SmallTest public class AppSearchDaoTest { @@ -87,6 +91,8 @@ public class AppSearchDaoTest { new PackageIdentifier( /* packageName= */ TEST, /* sha256= */ new Signature(SHA).toByteArray()); + private static final int APPSEARCH_READ_TIMEOUT_MS = 500; + @Rule public final AdServicesExtendedMockitoRule adServicesExtendedMockitoRule = new AdServicesExtendedMockitoRule.Builder(this).mockStatic(FlagsFactory.class).build(); @@ -95,6 +101,7 @@ public class AppSearchDaoTest { public void before() { MockitoAnnotations.initMocks(this); when(mFlags.getAppsearchWriterAllowListOverride()).thenReturn(""); + when(mFlags.getAppSearchReadTimeout()).thenReturn(APPSEARCH_READ_TIMEOUT_MS); doReturn(mFlags).when(FlagsFactory::getFlags); } @@ -205,6 +212,30 @@ public class AppSearchDaoTest { } @Test + public void testReadConsentData_timeout() { + AppSearchDao result = + AppSearchDao.readConsentData( + AppSearchConsentDao.class, + getLongRunningOperation(mGlobalSearchSession), + mExecutor, + NAMESPACE, + TEST, + mAdServicesPackageName); + assertThat(result).isNull(); + } + + private <T> ListenableFuture<T> getLongRunningOperation(T result) { + // Wait for a time that's longer than the AppSearch read timeout, then return the result. + ListeningExecutorService ls = + MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()); + return ls.submit( + () -> { + TimeUnit.MILLISECONDS.sleep(APPSEARCH_READ_TIMEOUT_MS + 500); + return result; + }); + } + + @Test public void testReadAppSearchData_emptyQuery() { AppSearchDao dao = AppSearchDao.readAppSearchSessionData( @@ -262,7 +293,8 @@ public class AppSearchDaoTest { when(mockSession.setSchemaAsync(any(SetSchemaRequest.class))) .thenReturn(Futures.immediateFuture(mockResponse)); - AppSearchResult mockResult = Mockito.mock(AppSearchResult.class); + AppSearchResult<Void> mockResult = + AppSearchResult.newFailedResult(AppSearchResult.RESULT_INVALID_ARGUMENT, "test"); SetSchemaResponse.MigrationFailure failure = new SetSchemaResponse.MigrationFailure( /* namespace= */ TEST, @@ -278,11 +310,12 @@ public class AppSearchDaoTest { Futures.immediateFuture(mockSession), List.of(PACKAGE_IDENTIFIER), mExecutor); - ExecutionException e = assertThrows(ExecutionException.class, () -> result.get()); + ExecutionException e = assertThrows(ExecutionException.class, result::get); assertThat(e.getMessage()) .isEqualTo( "java.lang.RuntimeException: " - + ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE); + + ConsentConstants.ERROR_MESSAGE_APPSEARCH_FAILURE + + " Migration failure: [FAILURE(3)]: test"); } @Test @@ -337,7 +370,7 @@ public class AppSearchDaoTest { mExecutor, TEST, NAMESPACE); - ExecutionException e = assertThrows(ExecutionException.class, () -> result.get()); + ExecutionException e = assertThrows(ExecutionException.class, result::get); assertThat(e.getMessage()) .isEqualTo( "java.lang.RuntimeException: " diff --git a/adservices/tests/unittest/service-core/appsearch/src/com/android/adservices/service/appsearch/AppSearchMeasurementRollbackWorkerTest.java b/adservices/tests/unittest/service-core/appsearch/src/com/android/adservices/service/appsearch/AppSearchMeasurementRollbackWorkerTest.java index 0765ba527..638ee6714 100644 --- a/adservices/tests/unittest/service-core/appsearch/src/com/android/adservices/service/appsearch/AppSearchMeasurementRollbackWorkerTest.java +++ b/adservices/tests/unittest/service-core/appsearch/src/com/android/adservices/service/appsearch/AppSearchMeasurementRollbackWorkerTest.java @@ -41,6 +41,8 @@ import androidx.test.filters.SmallTest; import com.android.adservices.concurrency.AdServicesExecutors; import com.android.adservices.mockito.AdServicesExtendedMockitoRule; +import com.android.adservices.service.Flags; +import com.android.adservices.service.FlagsFactory; import com.android.adservices.service.common.compat.FileCompatUtils; import com.android.adservices.service.consent.ConsentConstants; @@ -66,7 +68,7 @@ public class AppSearchMeasurementRollbackWorkerTest { FileCompatUtils.getAdservicesFilename("measurement_rollback"); private static final String USERID = "user1"; private static final long APEX_VERSION = 100L; - private static final int FUTURE_TIMEOUT_MILLISECONDS = 3000; + private static final int APPSEARCH_WRITE_TIMEOUT_MS = 1000; private final Context mContext = ApplicationProvider.getApplicationContext(); private final String mAdServicesPackageName = @@ -74,17 +76,22 @@ public class AppSearchMeasurementRollbackWorkerTest { private final Executor mExecutor = AdServicesExecutors.getBackgroundExecutor(); private AppSearchMeasurementRollbackWorker mWorker; @Mock private ListenableFuture<AppSearchSession> mAppSearchSession; + @Mock private Flags mMockFlags; @Rule public final AdServicesExtendedMockitoRule adServicesExtendedMockitoRule = new AdServicesExtendedMockitoRule.Builder(this) .mockStatic(PlatformStorage.class) .mockStatic(AppSearchDao.class) + .mockStatic(FlagsFactory.class) .setStrictness(Strictness.LENIENT) .build(); @Before public void setup() { + doReturn(mMockFlags).when(FlagsFactory::getFlags); + doReturn(APPSEARCH_WRITE_TIMEOUT_MS).when(mMockFlags).getAppSearchWriteTimeout(); + ArgumentCaptor<PlatformStorage.SearchContext> cap = ArgumentCaptor.forClass(PlatformStorage.SearchContext.class); doReturn(mAppSearchSession) @@ -107,7 +114,7 @@ public class AppSearchMeasurementRollbackWorkerTest { @SuppressWarnings("FutureReturnValueIgnored") @Test public void testClearAdServicesDeletionOccurred() { - FluentFuture mockResult = + FluentFuture<AppSearchBatchResult<String, Void>> mockResult = FluentFuture.from( Futures.immediateFuture( new AppSearchBatchResult.Builder<String, Void>().build())); @@ -128,13 +135,14 @@ public class AppSearchMeasurementRollbackWorkerTest { @Test public void testClearAdServicesDeletionOccurred_throwsChecked() { - Callable<Void> callable = + Callable<AppSearchBatchResult<String, Void>> callable = () -> { - TimeUnit.MILLISECONDS.sleep(FUTURE_TIMEOUT_MILLISECONDS); + TimeUnit.MILLISECONDS.sleep(APPSEARCH_WRITE_TIMEOUT_MS + 500); return null; }; - FluentFuture mockResult = FluentFuture.from(Futures.submit(callable, mExecutor)); + FluentFuture<AppSearchBatchResult<String, Void>> mockResult = + FluentFuture.from(Futures.submit(callable, mExecutor)); doReturn(mockResult).when(() -> AppSearchDao.deleteData(any(), any(), any(), any(), any())); RuntimeException e = @@ -234,17 +242,17 @@ public class AppSearchMeasurementRollbackWorkerTest { @Test public void testRecordAdServicesDeletionOccurred_throwsChecked() { - // The manager class waits for 2 seconds on the future.get() call before timing out. So - // creating a future that takes longer than 2 sec to resolve, in order to create a + // The manager class waits for a few seconds on the future.get() call before timing out. So + // creating a future that takes longer than the timeout to resolve, in order to create a // TimeoutException. - Callable<Void> callable = + Callable<AppSearchBatchResult<String, Void>> callable = () -> { - TimeUnit.MILLISECONDS.sleep(FUTURE_TIMEOUT_MILLISECONDS); + TimeUnit.MILLISECONDS.sleep(APPSEARCH_WRITE_TIMEOUT_MS + 500); return null; }; - ListenableFuture<Void> future = Futures.submit(callable, mExecutor); - FluentFuture mockFuture = FluentFuture.from(future); + FluentFuture<AppSearchBatchResult<String, Void>> mockFuture = + FluentFuture.from(Futures.submit(callable, mExecutor)); AppSearchMeasurementRollbackDao dao = mock(AppSearchMeasurementRollbackDao.class); doReturn(mockFuture).when(dao).writeData(any(), any(), any()); diff --git a/adservices/tests/unittest/service-core/assets/attribution_service_test.json b/adservices/tests/unittest/service-core/assets/attribution_service_test.json index 223e9cbf9..7612332a2 100644 --- a/adservices/tests/unittest/service-core/assets/attribution_service_test.json +++ b/adservices/tests/unittest/service-core/assets/attribution_service_test.json @@ -102,7 +102,7 @@ "reportTime": 8730000001, "triggerPriority": 101, "sourceType": "navigation", - "randomizedTriggerRate": 0.0024263, + "randomizedTriggerRate": 0.0001372, "sourceId": "S1", "triggerId": "T1" }], @@ -244,7 +244,7 @@ "reportTime": 8730000001, "triggerPriority": 101, "sourceType": "navigation", - "randomizedTriggerRate": 0.0024263, + "randomizedTriggerRate": 0.0001372, "sourceId": "S1", "triggerId": "T1" }], @@ -423,7 +423,7 @@ "reportTime": 8730000001, "triggerPriority": 101, "sourceType": "navigation", - "randomizedTriggerRate": 0.0024263, + "randomizedTriggerRate": 0.0001372, "sourceId": "S1", "triggerId": "T1" }, @@ -438,7 +438,7 @@ "reportTime": 8730000001, "triggerPriority": 101, "sourceType": "navigation", - "randomizedTriggerRate": 0.0024263, + "randomizedTriggerRate": 0.0001372, "sourceId": "S1", "triggerId": "T2" } @@ -805,7 +805,7 @@ "triggerPriority": 101, "status": 0, "sourceType": "navigation", - "randomizedTriggerRate": 0.0024263, + "randomizedTriggerRate": 0.0001372, "sourceId": "S2", "triggerId": "T1" }], @@ -965,7 +965,7 @@ "triggerPriority": 101, "status": 0, "sourceType": "navigation", - "randomizedTriggerRate": 0.0024263, + "randomizedTriggerRate": 0.0001372, "sourceId": "S2", "triggerId": "T1" }], @@ -1883,7 +1883,7 @@ "reportTime": 8730000001, "triggerPriority": 101, "sourceType": "navigation", - "randomizedTriggerRate": 0.0024263, + "randomizedTriggerRate": 0.0001372, "sourceId": "S1", "triggerId": "T1" }], @@ -2580,7 +2580,7 @@ "reportTime": 8730000001, "triggerPriority": 102, "sourceType": "navigation", - "randomizedTriggerRate": 0.0024263, + "randomizedTriggerRate": 0.0001372, "sourceId": "S1", "triggerId": "T2" }, @@ -2595,7 +2595,7 @@ "reportTime": 8730000001, "triggerPriority": 103, "sourceType": "navigation", - "randomizedTriggerRate": 0.0024263, + "randomizedTriggerRate": 0.0001372, "sourceId": "S1", "triggerId": "T3" }, @@ -2610,7 +2610,7 @@ "reportTime": 8730000001, "triggerPriority": 104, "sourceType": "navigation", - "randomizedTriggerRate": 0.0024263, + "randomizedTriggerRate": 0.0001372, "sourceId": "S1", "triggerId": "T4" } @@ -2875,7 +2875,7 @@ "reportTime": 8730000001, "triggerPriority": 101, "sourceType": "navigation", - "randomizedTriggerRate": 0.0024263, + "randomizedTriggerRate": 0.0001372, "sourceId": "S1", "triggerId": "T1" }, @@ -2890,7 +2890,7 @@ "reportTime": 8730000001, "triggerPriority": 101, "sourceType": "navigation", - "randomizedTriggerRate": 0.0024263, + "randomizedTriggerRate": 0.0001372, "sourceId": "S1", "triggerId": "T2" }, @@ -2905,7 +2905,7 @@ "reportTime": 8730000001, "triggerPriority": 102, "sourceType": "navigation", - "randomizedTriggerRate": 0.0024263, + "randomizedTriggerRate": 0.0001372, "sourceId": "S1", "triggerId": "T4" } @@ -3765,7 +3765,7 @@ "reportTime": 8643600030, "triggerPriority": 0, "sourceType": "navigation", - "randomizedTriggerRate": 0.0024263, + "randomizedTriggerRate": 0.0001372, "sourceId": "S1", "triggerId": "T2" } @@ -3928,7 +3928,7 @@ "reportTime": 8643600030, "triggerPriority": 0, "sourceType": "navigation", - "randomizedTriggerRate": 0.0024263, + "randomizedTriggerRate": 0.0001372, "sourceId": "S1", "triggerId": "T2" } @@ -5052,7 +5052,7 @@ "reportTime": 8730000001, "triggerPriority": 101, "sourceType": "navigation", - "randomizedTriggerRate": 0.0170218, + "randomizedTriggerRate": 0.0008051, "sourceId": "S1", "triggerId": "T1" }], @@ -5401,7 +5401,7 @@ "reportTime": 8730000002, "triggerPriority": 101, "sourceType": "navigation", - "randomizedTriggerRate": 0.0170218, + "randomizedTriggerRate": 0.0008051, "sourceId": "S1", "triggerId": "T1" }], diff --git a/adservices/tests/unittest/service-core/assets/measurement_app_uninstall_deletion_test.json b/adservices/tests/unittest/service-core/assets/measurement_app_uninstall_deletion_test.json index c9e8a4f5e..ce426c9dd 100644 --- a/adservices/tests/unittest/service-core/assets/measurement_app_uninstall_deletion_test.json +++ b/adservices/tests/unittest/service-core/assets/measurement_app_uninstall_deletion_test.json @@ -784,7 +784,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.test", "webDestination": "https://foo.test", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR2-REMOVE", @@ -801,7 +802,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.test", "webDestination": "https://foo.test", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, @@ -822,7 +824,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.test", "webDestination": "https://foo.test", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, @@ -1192,7 +1195,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR2", @@ -1209,7 +1213,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, @@ -1365,7 +1370,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR2", @@ -1382,7 +1388,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, diff --git a/adservices/tests/unittest/service-core/assets/measurement_delete_expired_test.json b/adservices/tests/unittest/service-core/assets/measurement_delete_expired_test.json index b835aeda9..95bbb6199 100644 --- a/adservices/tests/unittest/service-core/assets/measurement_delete_expired_test.json +++ b/adservices/tests/unittest/service-core/assets/measurement_delete_expired_test.json @@ -296,7 +296,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR2", @@ -313,7 +314,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR3", @@ -330,7 +332,8 @@ "retryCount": 5, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR4", @@ -347,7 +350,8 @@ "retryCount": 5, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, @@ -374,7 +378,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, diff --git a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/flex_api_with_no_origin_multiple_domain.json b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/flex_api_with_no_origin_multiple_domain.json index 28000d86a..d11dc5443 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/flex_api_with_no_origin_multiple_domain.json +++ b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/flex_api_with_no_origin_multiple_domain.json @@ -332,7 +332,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR2", @@ -349,7 +350,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, @@ -420,7 +422,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, diff --git a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/registrant_not_found.json b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/registrant_not_found.json index c5e9d4b56..0ee22ebf7 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/registrant_not_found.json +++ b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/registrant_not_found.json @@ -143,7 +143,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR2", @@ -160,7 +161,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, @@ -307,7 +309,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR2", @@ -324,7 +327,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, diff --git a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_multiple_origin_no_domain.json b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_multiple_origin_no_domain.json index c9b7e17e1..5e63f9e8d 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_multiple_origin_no_domain.json +++ b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_multiple_origin_no_domain.json @@ -312,7 +312,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR2", @@ -329,7 +330,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, @@ -488,7 +490,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, diff --git a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_multiple_origin_no_domain_preserve.json b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_multiple_origin_no_domain_preserve.json index 533129bd5..008c0aa91 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_multiple_origin_no_domain_preserve.json +++ b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_multiple_origin_no_domain_preserve.json @@ -325,7 +325,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR2", @@ -342,7 +343,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, @@ -580,7 +582,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, diff --git a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_no_origin_multiple_domain.json b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_no_origin_multiple_domain.json index abd601f82..d99396781 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_no_origin_multiple_domain.json +++ b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_no_origin_multiple_domain.json @@ -325,7 +325,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR2", @@ -342,7 +343,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, @@ -500,7 +502,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, diff --git a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_no_origin_multiple_domain_preserve.json b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_no_origin_multiple_domain_preserve.json index 802e38319..160e84813 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_no_origin_multiple_domain_preserve.json +++ b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_no_origin_multiple_domain_preserve.json @@ -339,7 +339,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR2", @@ -356,7 +357,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, @@ -595,7 +597,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, diff --git a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_no_origin_nor_range.json b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_no_origin_nor_range.json index 5ed787715..58f75087d 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_no_origin_nor_range.json +++ b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_no_origin_nor_range.json @@ -152,7 +152,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR2", @@ -169,7 +170,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, @@ -324,7 +326,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR2", @@ -341,7 +344,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, diff --git a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_origin_but_no_range.json b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_origin_but_no_range.json index 0528d34e8..e6137bd08 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_origin_but_no_range.json +++ b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_origin_but_no_range.json @@ -178,7 +178,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR2", @@ -195,7 +196,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, @@ -288,7 +290,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, diff --git a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_range_but_no_origin.json b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_range_but_no_origin.json index 2f08f9ea8..0abc4ceb0 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_range_but_no_origin.json +++ b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_range_but_no_origin.json @@ -170,7 +170,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR2", @@ -187,7 +188,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, @@ -360,7 +362,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR2", @@ -377,7 +380,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, diff --git a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_range_on_ending_edge_and_origin.json b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_range_on_ending_edge_and_origin.json index 74b7ca85a..0994666c4 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_range_on_ending_edge_and_origin.json +++ b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_range_on_ending_edge_and_origin.json @@ -247,7 +247,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR2", @@ -264,7 +265,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, @@ -423,7 +425,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, diff --git a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_range_on_ending_edge_no_origin.json b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_range_on_ending_edge_no_origin.json index 6aa23b053..b7f45c81c 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_range_on_ending_edge_no_origin.json +++ b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_range_on_ending_edge_no_origin.json @@ -170,7 +170,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR2", @@ -187,7 +188,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, @@ -360,7 +362,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR2", @@ -377,7 +380,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, diff --git a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_range_on_starting_edge_and_origin.json b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_range_on_starting_edge_and_origin.json index bf15f2fe8..ec5354205 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_range_on_starting_edge_and_origin.json +++ b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_range_on_starting_edge_and_origin.json @@ -234,7 +234,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR2", @@ -251,7 +252,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, @@ -397,7 +399,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, diff --git a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_range_on_starting_edge_no_origin.json b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_range_on_starting_edge_no_origin.json index 96e63cfaf..4f0528007 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_range_on_starting_edge_no_origin.json +++ b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_range_on_starting_edge_no_origin.json @@ -170,7 +170,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR2", @@ -187,7 +188,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, @@ -360,7 +362,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR2", @@ -377,7 +380,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, diff --git a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_start_equals_end_and_origin.json b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_start_equals_end_and_origin.json index 540f8cefd..4ff46fea4 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_start_equals_end_and_origin.json +++ b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_start_equals_end_and_origin.json @@ -234,7 +234,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR2", @@ -251,7 +252,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, @@ -397,7 +399,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, diff --git a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_start_equals_end_no_origin_preserve.json b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_start_equals_end_no_origin_preserve.json index 0a4c0a369..e0775912c 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_start_equals_end_no_origin_preserve.json +++ b/adservices/tests/unittest/service-core/assets/msmt_browser_deletion_tests/with_start_equals_end_no_origin_preserve.json @@ -169,7 +169,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" }, { "registrationId": "AR2", @@ -186,7 +187,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, @@ -281,7 +283,8 @@ "retryCount": 1, "verifiedDestination": "https://foo.com", "webDestination": "https://foo.com", - "sourceType": 1 + "sourceType": 1, + "redirectBehavior": "AS_IS" } ] }, diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_debug_source_storage_limit.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_debug_source_storage_limit.json index ff052bee4..b38a24723 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_debug_source_storage_limit.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_debug_source_storage_limit.json @@ -153,7 +153,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263, + "randomized_trigger_rate": 0.0008051, "source_debug_key": "347982378", "trigger_debug_key": "147982378" } @@ -181,7 +181,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263, + "randomized_trigger_rate": 0.0008051, "source_debug_key": "347982378", "trigger_debug_key": "147982378" } diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_large_keys.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_large_keys.json index 167693041..c7bae560b 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_large_keys.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_large_keys.json @@ -103,7 +103,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [{ diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_many_trigger_data.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_many_trigger_data.json index 7210704bb..5fad10144 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_many_trigger_data.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_many_trigger_data.json @@ -121,7 +121,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [{ diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_over_contributions_limit.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_over_contributions_limit.json index c5e8b3cad..20f7179f3 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_over_contributions_limit.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_over_contributions_limit.json @@ -160,7 +160,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }, { @@ -172,7 +172,7 @@ "source_event_id": "1", "trigger_data": "3", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } } ], diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_three_of_three_entries.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_three_of_three_entries.json index f228340d0..bc5b4d132 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_three_of_three_entries.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_three_of_three_entries.json @@ -105,7 +105,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [{ diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions.json index 985a338ed..54739775a 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions.json @@ -103,7 +103,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [{ diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_debug_api.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_debug_api.json index 11df48af0..c619387a7 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_debug_api.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_debug_api.json @@ -107,7 +107,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263, + "randomized_trigger_rate": 0.0008051, "source_debug_key" : "347982378", "trigger_debug_key" : "8971346783" } @@ -135,7 +135,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263, + "randomized_trigger_rate": 0.0008051, "source_debug_key" : "347982378", "trigger_debug_key" : "8971346783" } diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_debug_api_source.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_debug_api_source.json index 13cbac1a2..bfd9f49a5 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_debug_api_source.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_debug_api_source.json @@ -106,7 +106,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263, + "randomized_trigger_rate": 0.0008051, "source_debug_key" : "347982378" } }], diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_debug_api_trigger.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_debug_api_trigger.json index 2d394041f..77e5f09e9 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_debug_api_trigger.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_debug_api_trigger.json @@ -106,7 +106,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263, + "randomized_trigger_rate": 0.0008051, "trigger_debug_key" : "8971346783" } }], diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_no_adid_debug_api.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_no_adid_debug_api.json index a2e78e209..6c4c776b2 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_no_adid_debug_api.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_no_adid_debug_api.json @@ -107,7 +107,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [{ diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_no_adid_debug_api_source.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_no_adid_debug_api_source.json index 9f0ad2801..8bd9dd685 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_no_adid_debug_api_source.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_no_adid_debug_api_source.json @@ -106,7 +106,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [{ diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_no_adid_debug_api_trigger.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_no_adid_debug_api_trigger.json index 7cae580ac..83516309f 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_no_adid_debug_api_trigger.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_no_adid_debug_api_trigger.json @@ -106,7 +106,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [{ diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_null_reports.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_null_reports.json index ba0fae5d7..5b551ae95 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_null_reports.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/aggregatable/aggregatable_two_contributions_null_reports.json @@ -107,7 +107,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [ diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_ctc_w1_3_conversions.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_ctc_w1_3_conversions.json index 5e1ff2b5c..b8d9fe1ab 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_ctc_w1_3_conversions.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_ctc_w1_3_conversions.json @@ -1,7 +1,6 @@ { "description": "Three reporting windows configured for CTC - 2h (w1), 2d (w2) and expiry (w3). Source (app) has T1 (web), T2(web) and T3(web) in W1, where T4 gets ignored due to equal priority", "phflags_override": { - "measurement_enable_configurable_event_reporting_windows": "true", "measurement_event_reports_ctc_early_reporting_windows": "7200,172800", "measurement_flex_api_max_information_gain_dual_destination_event": "9", "measurement_flex_api_max_information_gain_dual_destination_navigation": "14" diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_ctc_w1_w2.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_ctc_w1_w2.json index a6294ab4b..3c6ad0eda 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_ctc_w1_w2.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_ctc_w1_w2.json @@ -1,7 +1,6 @@ { "description": "Three reporting windows configured for CTC - 2h (w1), 2d (w2) and expiry (w3). Source has T1(app) in W1 and T2(app) in W2.", "phflags_override": { - "measurement_enable_configurable_event_reporting_windows": "true", "measurement_event_reports_ctc_early_reporting_windows": "7200,172800" }, "input": { diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_ctc_w1_w2_w3.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_ctc_w1_w2_w3.json index 98fdf514e..670f89e0c 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_ctc_w1_w2_w3.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_ctc_w1_w2_w3.json @@ -1,7 +1,6 @@ { "description": "Three reporting windows configured for CTC - 2h (w1), 2d (w2) and expiry (w3). Source (web) source has T1(app) in W1, T2(web) in W2 and T4(app) in W3 where T3(web) gets replaced by T4 because T4 has higher priority.", "phflags_override": { - "measurement_enable_configurable_event_reporting_windows": "true", "measurement_event_reports_ctc_early_reporting_windows": "7200,172800", "measurement_flex_api_max_information_gain_dual_destination_event": "9", "measurement_flex_api_max_information_gain_dual_destination_navigation": "14" diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_vtc_install_attribution.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_vtc_install_attribution.json index 0bd0470df..dc8e961a0 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_vtc_install_attribution.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_vtc_install_attribution.json @@ -1,7 +1,7 @@ { "description": "Three reporting windows configured for VTC - 1h (w1), 1d (w2) and expiry (w3). Source has 2 app triggers due to install attribution in w1 and w2.", "phflags_override": { - "measurement_enable_configurable_event_reporting_windows": "true", + "measurement_vtc_configurable_max_event_reports_count": "2", "measurement_event_reports_vtc_early_reporting_windows": "3600,86400", "measurement_flex_api_max_information_gain_dual_destination_event": "9", "measurement_flex_api_max_information_gain_dual_destination_navigation": "14" diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_vtc_w1.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_vtc_w1.json index 3aa8dafc2..4641d4e53 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_vtc_w1.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_vtc_w1.json @@ -1,7 +1,6 @@ { "description": "Three reporting windows configured for VTC - 1h (w1), 1d (w2) and expiry (w3). Source has trigger attributed in W1.", "phflags_override": { - "measurement_enable_configurable_event_reporting_windows": "true", "measurement_event_reports_vtc_early_reporting_windows": "3600,86400" }, "input": { diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_vtc_w2.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_vtc_w2.json index 9c6ec1539..ff5af97ef 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_vtc_w2.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_vtc_w2.json @@ -1,7 +1,6 @@ { "description": "Three reporting windows configured for VTC - 1h (w1), 1d (w2) and expiry (w3). Source has trigger attributed in W2 (also has another trigger which gets ignored due to lower priority).", "phflags_override": { - "measurement_enable_configurable_event_reporting_windows": "true", "measurement_event_reports_vtc_early_reporting_windows": "3600,86400" }, "input": { diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_vtc_w3.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_vtc_w3.json index bc775e3a4..224f52c57 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_vtc_w3.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/configurable_reporting/configurable_reporting_event_vtc_w3.json @@ -1,7 +1,6 @@ { "description": "Three reporting windows configured for VTC - 1h (w1), 1d (w2) and expiry (w3). Source(app) has web trigger attributed in W3.", "phflags_override": { - "measurement_enable_configurable_event_reporting_windows": "true", "measurement_event_reports_vtc_early_reporting_windows": "3600,86400" }, "input": { diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_aggregatable_two_contributions.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_aggregatable_two_contributions.json index ebd6ed643..2338ff7f0 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_aggregatable_two_contributions.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_aggregatable_two_contributions.json @@ -151,7 +151,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263, + "randomized_trigger_rate": 0.0008051, "source_debug_key": "347982378" } }], diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_register_web-app_source_destination_limit.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_register_web-app_source_destination_limit.json index a663f249a..280c87b41 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_register_web-app_source_destination_limit.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_register_web-app_source_destination_limit.json @@ -111,7 +111,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } } ], diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_register_web-web_source_destination_limit.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_register_web-web_source_destination_limit.json index d3d1ca334..2e8540bea 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_register_web-web_source_destination_limit.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_register_web-web_source_destination_limit.json @@ -98,7 +98,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "verbose_debug_reports": [ diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger-reporting-origin-limit-app-app.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger-reporting-origin-limit-app-app.json index 39716916d..f4498b61c 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger-reporting-origin-limit-app-app.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger-reporting-origin-limit-app-app.json @@ -120,7 +120,7 @@ "source_type": "navigation", "source_debug_key": "347982378", "trigger_debug_key": "8971346783", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } } ], @@ -136,7 +136,7 @@ "source_type": "navigation", "source_debug_key": "347982378", "trigger_debug_key": "8971346783", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } } ], diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_attributions_per_source_destination_limit_app-app.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_attributions_per_source_destination_limit_app-app.json index 459632d36..a4efff0cc 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_attributions_per_source_destination_limit_app-app.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_attributions_per_source_destination_limit_app-app.json @@ -91,7 +91,7 @@ "source_type": "navigation", "source_debug_key": "347982378", "trigger_debug_key": "8971346783", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [], @@ -106,7 +106,7 @@ "source_type": "navigation", "source_debug_key": "347982378", "trigger_debug_key": "8971346783", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "debug_aggregatable_results": [], diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_attributions_per_source_destination_limit_app-web.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_attributions_per_source_destination_limit_app-web.json index 4fdfa14c0..77ff7530a 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_attributions_per_source_destination_limit_app-web.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_attributions_per_source_destination_limit_app-web.json @@ -102,7 +102,7 @@ "source_type": "navigation", "source_debug_key": "347982378", "trigger_debug_key": "8971346783", - "randomized_trigger_rate": 0.0170218 + "randomized_trigger_rate": 0.0054129 } }], "debug_event_level_results": [{ @@ -116,7 +116,7 @@ "source_type": "navigation", "source_debug_key": "347982378", "trigger_debug_key": "8971346783", - "randomized_trigger_rate": 0.0170218 + "randomized_trigger_rate": 0.0054129 } }], "verbose_debug_reports": [ diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_attributions_per_source_destination_limit_web-app.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_attributions_per_source_destination_limit_web-app.json index bc0de4ec0..6d43ca834 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_attributions_per_source_destination_limit_web-app.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_attributions_per_source_destination_limit_web-app.json @@ -101,7 +101,7 @@ "source_type": "navigation", "source_debug_key": "347982378", "trigger_debug_key": "8971346783", - "randomized_trigger_rate": 0.0170218 + "randomized_trigger_rate": 0.0054129 } }], "debug_event_level_results": [{ @@ -115,7 +115,7 @@ "source_type": "navigation", "source_debug_key": "347982378", "trigger_debug_key": "8971346783", - "randomized_trigger_rate": 0.0170218 + "randomized_trigger_rate": 0.0054129 } }], "verbose_debug_reports": [ diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_attributions_per_source_destination_limit_web-web-different-registrant.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_attributions_per_source_destination_limit_web-web-different-registrant.json index b11caa533..42e17840d 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_attributions_per_source_destination_limit_web-web-different-registrant.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_attributions_per_source_destination_limit_web-web-different-registrant.json @@ -103,7 +103,7 @@ "source_type": "navigation", "source_debug_key": "347982378", "trigger_debug_key": "8971346783", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "debug_event_level_results": [{ @@ -117,7 +117,7 @@ "source_type": "navigation", "source_debug_key": "347982378", "trigger_debug_key": "8971346783", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "verbose_debug_reports": [ diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_attributions_per_source_destination_limit_web-web-same-registrant.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_attributions_per_source_destination_limit_web-web-same-registrant.json index ee2b33f98..3e9241406 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_attributions_per_source_destination_limit_web-web-same-registrant.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_attributions_per_source_destination_limit_web-web-same-registrant.json @@ -99,7 +99,7 @@ "source_type": "navigation", "source_debug_key": "347982378", "trigger_debug_key": "8971346783", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "debug_event_level_results": [{ @@ -113,7 +113,7 @@ "source_type": "navigation", "source_debug_key": "347982378", "trigger_debug_key": "8971346783", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "verbose_debug_reports": [ diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_event_report_window_passed_app-app.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_event_report_window_passed_app-app.json index 422d8a143..4d91151df 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_event_report_window_passed_app-app.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_event_report_window_passed_app-app.json @@ -79,7 +79,7 @@ { "payload": { "attribution_destination": "android-app://example.2d1.test", - "randomized_trigger_rate": 0.0024263, + "randomized_trigger_rate": 0.0001372, "scheduled_report_time": "1643325573", "source_event_id": "1", "source_type": "navigation", @@ -95,7 +95,7 @@ { "payload": { "attribution_destination": "android-app://example.2d1.test", - "randomized_trigger_rate": 0.0024263, + "randomized_trigger_rate": 0.0001372, "scheduled_report_time": "1643325573", "source_event_id": "1", "source_type": "navigation", diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_event_report_window_passed_app-web.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_event_report_window_passed_app-web.json index a18be7506..3182ac4ce 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_event_report_window_passed_app-web.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_event_report_window_passed_app-web.json @@ -98,7 +98,7 @@ "source_type": "navigation", "source_debug_key": "347982378", "trigger_debug_key": "8971346781", - "randomized_trigger_rate": 0.0170218 + "randomized_trigger_rate": 0.0008051 } }], "debug_event_level_results": [{ @@ -112,7 +112,7 @@ "source_type": "navigation", "source_debug_key": "347982378", "trigger_debug_key": "8971346781", - "randomized_trigger_rate": 0.0170218 + "randomized_trigger_rate": 0.0008051 } }], "verbose_debug_reports": [ diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_event_report_window_passed_web-app.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_event_report_window_passed_web-app.json index a425ba0f3..86e4d16d0 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_event_report_window_passed_web-app.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_event_report_window_passed_web-app.json @@ -97,7 +97,7 @@ "source_type": "navigation", "source_debug_key": "347982378", "trigger_debug_key": "8971346781", - "randomized_trigger_rate": 0.0170218 + "randomized_trigger_rate": 0.0008051 } }], "debug_event_level_results": [{ @@ -111,7 +111,7 @@ "source_type": "navigation", "source_debug_key": "347982378", "trigger_debug_key": "8971346781", - "randomized_trigger_rate": 0.0170218 + "randomized_trigger_rate": 0.0008051 } }], "verbose_debug_reports": [ diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_event_report_window_passed_web-web-different-registrant.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_event_report_window_passed_web-web-different-registrant.json index ba15a27bd..29e524120 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_event_report_window_passed_web-web-different-registrant.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_event_report_window_passed_web-web-different-registrant.json @@ -101,7 +101,7 @@ "source_type": "navigation", "source_debug_key": "347982378", "trigger_debug_key": "8971346781", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0001372 } }], "debug_event_level_results": [{ @@ -115,7 +115,7 @@ "source_type": "navigation", "source_debug_key": "347982378", "trigger_debug_key": "8971346781", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0001372 } }], "verbose_debug_reports": [ diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_event_report_window_passed_web-web-same-registrant.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_event_report_window_passed_web-web-same-registrant.json index b0c0aa01e..9eb5b1b4b 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_event_report_window_passed_web-web-same-registrant.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/debug_report/debug_report_trigger_event_report_window_passed_web-web-same-registrant.json @@ -95,7 +95,7 @@ "source_type": "navigation", "source_debug_key": "347982378", "trigger_debug_key": "8971346781", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0001372 } }], "debug_event_level_results": [{ @@ -109,7 +109,7 @@ "source_type": "navigation", "source_debug_key": "347982378", "trigger_debug_key": "8971346781", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0001372 } }], "verbose_debug_reports": [ diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/list_sources/list_sources_3_app_sources_3_web_triggers_3_reports.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/list_sources/list_sources_3_app_sources_3_web_triggers_3_reports.json index 1405c785d..5eaa9d1ac 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/list_sources/list_sources_3_app_sources_3_web_triggers_3_reports.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/list_sources/list_sources_3_app_sources_3_web_triggers_3_reports.json @@ -123,7 +123,7 @@ "source_event_id": "1", "trigger_data": "1", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }, { "report_time": "800176400001", @@ -134,7 +134,7 @@ "source_event_id": "2", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }, { "report_time": "800176400001", @@ -145,7 +145,7 @@ "source_event_id": "3", "trigger_data": "3", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [] diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/list_sources/list_sources_cannot_redirect.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/list_sources/list_sources_cannot_redirect.json index 518fbf142..7fabaaf82 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/list_sources/list_sources_cannot_redirect.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/list_sources/list_sources_cannot_redirect.json @@ -228,7 +228,7 @@ "source_event_id": "1", "trigger_data": "1", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } } ], diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/basic_use_of_redirect.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/basic_use_of_redirect.json index 45fdc162c..975bd064b 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/basic_use_of_redirect.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/basic_use_of_redirect.json @@ -44,9 +44,7 @@ "Attribution-Reporting-Register-Source": { "source_event_id": "3", "destination": "android-app://example.2d1.test", - "priority": "100", - "expiry": "800172800002" - + "priority": "100" }, "Location": null, "Attribution-Reporting-Redirect": null @@ -124,7 +122,7 @@ "source_event_id": "1", "trigger_data": "1", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }, { @@ -136,7 +134,7 @@ "source_event_id": "2", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }, { diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/basic_use_of_redirect_to_well_known.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/basic_use_of_redirect_to_well_known.json new file mode 100644 index 000000000..ceb8bd2ad --- /dev/null +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/basic_use_of_redirect_to_well_known.json @@ -0,0 +1,113 @@ +{ + "description": "Three sources with equal event-time, and three triggers that match. The second and third sources and triggers are obtained by redirect from the first HTTP fetch of each, using redirection to the .well-known path. Event report task within the expiry window sends the data corresponding to the three sources.", + "phflags_override": { + "measurement_enable_redirect_to_well_known_path": "true" + }, + "input": { + "sources": [{ + "registration_request": { + "attribution_src_url": "https://www.ad-tech1.test", + "source_type": "navigation", + "registrant": "example.1s1.test" + }, + "responses": [ + { + "url": "https://www.ad-tech1.test", + "response": { + "Attribution-Reporting-Register-Source": { + "source_event_id": "1", + "destination": "android-app://example.2d1.test", + "priority": "100", + "expiry": "172801" + }, + "Location": "https://www.ad-tech2.test", + "Attribution-Reporting-Redirect-Config": "redirect-302-to-well-known" + } + }, + { + "url": "https://www.ad-tech2.test/.well-known/attribution-reporting/register-redirect?302_url=https%3A%2F%2Fwww.ad-tech2.test", + "response": { + "Attribution-Reporting-Register-Source": { + "source_event_id": "2", + "destination": "android-app://example.2d1.test", + "priority": "100", + "expiry": "172801" + }, + + "Location": null, + "Attribution-Reporting-Redirect": null + } + } + ], + "timestamp": "800000000001" + }], + "triggers": [{ + "registration_request": { + "attribution_src_url": "https://www.ad-tech1.test", + "registrant": "example.2d1.test" + }, + "responses": [ + { + "url": "https://www.ad-tech1.test", + "response": { + "Attribution-Reporting-Register-Trigger": { + "event_trigger_data": [ + { + "trigger_data": "1", + "priority": "101" + } + ] + }, + "Location": "https://www.ad-tech2.test", + "Attribution-Reporting-Redirect-Config": "redirect-302-to-well-known" + } + }, + { + "url": "https://www.ad-tech2.test/.well-known/attribution-reporting/register-redirect?302_url=https%3A%2F%2Fwww.ad-tech2.test", + "response": { + "Attribution-Reporting-Register-Trigger": { + "event_trigger_data": [ + { + "trigger_data": "2", + "priority": "101" + } + ] + }, + "Location": null, + "Attribution-Reporting-Redirect": null + } + } + ], + "timestamp": "800000600001" + }] + }, + "output": { + "event_level_results": [ + { + "report_time": "800176400001", + "report_url": "https://www.ad-tech1.test/.well-known/attribution-reporting/report-event-attribution", + "payload": { + "attribution_destination": "android-app://example.2d1.test", + "scheduled_report_time": "800176400", + "source_event_id": "1", + "trigger_data": "1", + "source_type": "navigation", + "randomized_trigger_rate": 0.0008051 + } + }, + { + "report_time": "800176400001", + "report_url": "https://www.ad-tech2.test/.well-known/attribution-reporting/report-event-attribution", + "payload": { + "attribution_destination": "android-app://example.2d1.test", + "scheduled_report_time": "800176400", + "source_event_id": "2", + "trigger_data": "2", + "source_type": "navigation", + "randomized_trigger_rate": 0.0008051 + } + } + ], + "aggregatable_results": [] + } +} diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/complex_use_of_redirects.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/complex_use_of_redirects.json index 413283b34..2240b5661 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/complex_use_of_redirects.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/complex_use_of_redirects.json @@ -50,9 +50,7 @@ "Attribution-Reporting-Register-Source": { "source_event_id": "3", "destination": "android-app://example.2d1.test", - "priority": "100", - "expiry": "800172800002" - + "priority": "100" }, "Location": "https://www.unenrolled-non-registering2.test", "Attribution-Reporting-Redirect": [ @@ -66,9 +64,7 @@ "Attribution-Reporting-Register-Source": { "source_event_id": "4", "destination": "android-app://example.2d1.test", - "priority": "100", - "expiry": "800172800002" - + "priority": "100" }, "Location": null, "Attribution-Reporting-Redirect": null @@ -80,9 +76,7 @@ "Attribution-Reporting-Register-Source": { "source_event_id": "6", "destination": "android-app://example.2d1.test", - "priority": "100", - "expiry": "800172800002" - + "priority": "100" }, "Attribution-Reporting-Redirect": [ "https://www.ad-tech5.test" @@ -96,9 +90,7 @@ "Attribution-Reporting-Register-Source": { "source_event_id": "5", "destination": "android-app://example.2d1.test", - "priority": "100", - "expiry": "800172800002" - + "priority": "100" }, "Location": null, "Attribution-Reporting-Redirect": null @@ -231,7 +223,7 @@ "source_event_id": "1", "trigger_data": "1", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }, { @@ -243,7 +235,7 @@ "source_event_id": "2", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }, { diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/complex_use_of_redirects_to_well_known.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/complex_use_of_redirects_to_well_known.json new file mode 100644 index 000000000..e9a811d4c --- /dev/null +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/complex_use_of_redirects_to_well_known.json @@ -0,0 +1,287 @@ +{ + "description": "7 (5 registering & 2 non-registering) redirects each for source & triggers. Redirection to .well-known path is enabled intermediately, making all subsequent Location-type registrations redirect to the .well-known path, even without the header. All 5 sources and triggers match. Event report task within the expiry window sends the data corresponding to the 5 sources.", + "phflags_override": { + "measurement_enable_redirect_to_well_known_path": "true" + }, + "input": { + "sources": [{ + "registration_request": { + "attribution_src_url": "https://www.ad-tech1.test", + "source_type": "navigation", + "registrant": "example.1s1.test" + }, + "responses": [ + { + "url": "https://www.ad-tech1.test", + "response": { + "Attribution-Reporting-Register-Source": { + "source_event_id": "1", + "destination": "android-app://example.2d1.test", + "priority": "100", + "expiry": "172801" + }, + "Location": null, + "Attribution-Reporting-Redirect": [ + "https://www.ad-tech2.test", + "https://www.enrolled-non-registering1.test" + ] + } + }, + { + "url": "https://www.ad-tech2.test", + "response": { + "Attribution-Reporting-Register-Source": { + "source_event_id": "2", + "destination": "android-app://example.2d1.test", + "priority": "100", + "expiry": "172801" + }, + + "Location": null, + "Attribution-Reporting-Redirect": null + } + }, + { + "url": "https://www.enrolled-non-registering1.test", + "response": { + "Location": "https://www.ad-tech3.test", + "Attribution-Reporting-Redirect-Config": "redirect-302-to-well-known" + } + }, + { + "url": "https://www.ad-tech3.test/.well-known/attribution-reporting/register-redirect?302_url=https%3A%2F%2Fwww.ad-tech3.test", + "response": { + "Attribution-Reporting-Register-Source": { + "source_event_id": "3", + "destination": "android-app://example.2d1.test", + "priority": "100" + }, + "Location": "https://www.unenrolled-non-registering2.test", + "Attribution-Reporting-Redirect": [ + "https://www.ad-tech4.test" + ] + } + }, + { + "url": "https://www.ad-tech4.test", + "response": { + "Attribution-Reporting-Register-Source": { + "source_event_id": "4", + "destination": "android-app://example.2d1.test", + "priority": "100" + }, + "Location": null, + "Attribution-Reporting-Redirect": null + } + }, + { + "url": "https://www.unenrolled-non-registering2.test/.well-known/attribution-reporting/register-redirect?302_url=https%3A%2F%2Fwww.unenrolled-non-registering2.test", + "response": { + "Attribution-Reporting-Register-Source": { + "source_event_id": "6", + "destination": "android-app://example.2d1.test", + "priority": "100" + }, + "Attribution-Reporting-Redirect": [ + "https://www.ad-tech5.test" + ] + }, + "enroll": false + }, + { + "url": "https://www.ad-tech5.test", + "response": { + "Attribution-Reporting-Register-Source": { + "source_event_id": "5", + "destination": "android-app://example.2d1.test", + "priority": "100", + "expiry": "800172800002" + + }, + "Location": null, + "Attribution-Reporting-Redirect": null + } + } + ], + "timestamp": "800000000001" + }], + "triggers": [{ + "registration_request": { + "attribution_src_url": "https://www.ad-tech1.test", + "registrant": "example.2d1.test" + }, + "responses": [ + { + "url": "https://www.ad-tech1.test", + "response": { + "Attribution-Reporting-Register-Trigger": { + "event_trigger_data": [ + { + "trigger_data": "1", + "priority": "101" + } + ] + }, + "Location": null, + "Attribution-Reporting-Redirect": [ + "https://www.ad-tech2.test", + "https://www.enrolled-non-registering1.test" + ] + } + }, + { + "url": "https://www.ad-tech2.test", + "response": { + "Attribution-Reporting-Register-Trigger": { + "event_trigger_data": [ + { + "trigger_data": "2", + "priority": "101" + } + ] + }, + "Location": null, + "Attribution-Reporting-Redirect": null + } + }, + { + "url": "https://www.enrolled-non-registering1.test", + "response": { + "Location": "https://www.ad-tech3.test", + "Attribution-Reporting-Redirect-Config": "redirect-302-to-well-known" + } + }, + { + "url": "https://www.ad-tech3.test/.well-known/attribution-reporting/register-redirect?302_url=https%3A%2F%2Fwww.ad-tech3.test", + "response": { + "Attribution-Reporting-Register-Trigger": { + "event_trigger_data": [ + { + "trigger_data": "3", + "priority": "101" + } + ] + }, + "Location": "https://www.unenrolled-non-registering2.test", + "Attribution-Reporting-Redirect": [ + "https://www.ad-tech4.test" + ] + } + }, + { + "url": "https://www.ad-tech4.test", + "response": { + "Attribution-Reporting-Register-Trigger": { + "event_trigger_data": [ + { + "trigger_data": "4", + "priority": "101" + } + ] + }, + "Location": null, + "Attribution-Reporting-Redirect": null + } + }, + { + "url": "https://www.unenrolled-non-registering2.test/.well-known/attribution-reporting/register-redirect?302_url=https%3A%2F%2Fwww.unenrolled-non-registering2.test", + "response": { + "Attribution-Reporting-Register-Trigger": { + "event_trigger_data": [ + { + "trigger_data": "6", + "priority": "101" + } + ] + }, + "Attribution-Reporting-Redirect": [ + "https://www.ad-tech5.test" + ] + }, + "enroll": false + }, + { + "url": "https://www.ad-tech5.test", + "response": { + "Attribution-Reporting-Register-Trigger": { + "event_trigger_data": [ + { + "trigger_data": "5", + "priority": "101" + } + ] + }, + "Location": null, + "Attribution-Reporting-Redirect": null + } + } + ], + "timestamp": "800000600001" + }] + }, + "output": { + "event_level_results": [ + { + "report_time": "800176400001", + "report_url": "https://www.ad-tech1.test/.well-known/attribution-reporting/report-event-attribution", + "payload": { + "attribution_destination": "android-app://example.2d1.test", + "scheduled_report_time": "800176400", + "source_event_id": "1", + "trigger_data": "1", + "source_type": "navigation", + "randomized_trigger_rate": 0.0008051 + } + }, + { + "report_time": "800176400001", + "report_url": "https://www.ad-tech2.test/.well-known/attribution-reporting/report-event-attribution", + "payload": { + "attribution_destination": "android-app://example.2d1.test", + "scheduled_report_time": "800176400", + "source_event_id": "2", + "trigger_data": "2", + "source_type": "navigation", + "randomized_trigger_rate": 0.0008051 + } + }, + { + "report_time": "800176400001", + "report_url": "https://www.ad-tech3.test/.well-known/attribution-reporting/report-event-attribution", + "payload": { + "attribution_destination": "android-app://example.2d1.test", + "scheduled_report_time": "800176400", + "source_event_id": "3", + "trigger_data": "3", + "source_type": "navigation", + "randomized_trigger_rate": 0.0024263 + } + }, + { + "report_time": "800176400001", + "report_url": "https://www.ad-tech4.test/.well-known/attribution-reporting/report-event-attribution", + "payload": { + "attribution_destination": "android-app://example.2d1.test", + "scheduled_report_time": "800176400", + "source_event_id": "4", + "trigger_data": "4", + "source_type": "navigation", + "randomized_trigger_rate": 0.0024263 + } + }, + { + "report_time": "800176400001", + "report_url": "https://www.ad-tech5.test/.well-known/attribution-reporting/report-event-attribution", + "payload": { + "attribution_destination": "android-app://example.2d1.test", + "scheduled_report_time": "800176400", + "source_event_id": "5", + "trigger_data": "5", + "source_type": "navigation", + "randomized_trigger_rate": 0.0024263 + } + } + ], + "aggregatable_results": [] + } +} diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/coordinator_origin_1_allowed.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/coordinator_origin_1_allowed.json index a86d67674..f6921321a 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/coordinator_origin_1_allowed.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/coordinator_origin_1_allowed.json @@ -126,7 +126,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [{ diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/coordinator_origin_2_allowed.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/coordinator_origin_2_allowed.json index 587739024..e9228b9c4 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/coordinator_origin_2_allowed.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/coordinator_origin_2_allowed.json @@ -126,7 +126,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [{ diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/coordinator_origin_disabled.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/coordinator_origin_disabled.json index a9752fec1..2da69130d 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/coordinator_origin_disabled.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/coordinator_origin_disabled.json @@ -126,7 +126,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [{ diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/enrollment_flag_disabled.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/enrollment_flag_disabled.json index 0b966e24d..4a17c0eb6 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/enrollment_flag_disabled.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/enrollment_flag_disabled.json @@ -108,7 +108,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [{ diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/event_debug_source_storage_limit.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/event_debug_source_storage_limit.json index 71b47b104..1ba32b6d5 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/event_debug_source_storage_limit.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/event_debug_source_storage_limit.json @@ -88,7 +88,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263, + "randomized_trigger_rate": 0.0008051, "source_debug_key": "347982378", "trigger_debug_key": "147982378" } @@ -102,7 +102,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263, + "randomized_trigger_rate": 0.0008051, "source_debug_key": "347982378", "trigger_debug_key": "147982378" } diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/event_report_window.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/event_report_window.json index 67ef2d38d..615ab4931 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/event_report_window.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/event_report_window.json @@ -121,7 +121,7 @@ "payload": { "attribution_destination": "android-app://destination.test", "scheduled_report_time": "1643325573", - "randomized_trigger_rate": 0.0024263, + "randomized_trigger_rate": 0.0001372, "source_event_id": "123", "source_type": "navigation", "trigger_data": "1" @@ -133,7 +133,7 @@ "payload": { "attribution_destination": "android-app://destination.test", "scheduled_report_time": "1643325573", - "randomized_trigger_rate": 0.0024263, + "randomized_trigger_rate": 0.0001372, "source_event_id": "123", "source_type": "navigation", "trigger_data": "2" diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/event_time_based_source_selection.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/event_time_based_source_selection.json index 6d9b78f24..b84aeab36 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/event_time_based_source_selection.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/event_time_based_source_selection.json @@ -78,7 +78,7 @@ "source_event_id": "2", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [] diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/limit_num_reports_for_click.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/limit_num_reports_for_click.json index 9adf76d18..71dd31cc7 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/limit_num_reports_for_click.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/limit_num_reports_for_click.json @@ -124,7 +124,7 @@ "source_event_id": "1", "trigger_data": "1", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }, { @@ -136,7 +136,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }, { @@ -148,7 +148,7 @@ "source_event_id": "1", "trigger_data": "3", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } } ], diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/localhost_as_reporting_origin.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/localhost_as_reporting_origin.json index f3944e4eb..bef06b6e4 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/localhost_as_reporting_origin.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/localhost_as_reporting_origin.json @@ -55,7 +55,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [] diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/localhost_basic_use_of_redirect.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/localhost_basic_use_of_redirect.json index 944d30722..b519eb9f5 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/localhost_basic_use_of_redirect.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/localhost_basic_use_of_redirect.json @@ -93,7 +93,7 @@ "source_event_id": "1", "trigger_data": "1", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }, { @@ -105,7 +105,7 @@ "source_event_id": "2", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } } ], diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/localhost_ip_as_reporting_origin.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/localhost_ip_as_reporting_origin.json index d7fad3f79..1e0697941 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/localhost_ip_as_reporting_origin.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/localhost_ip_as_reporting_origin.json @@ -55,7 +55,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [] diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/only_one_source_of_multiple_matches_can_be_attributed.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/only_one_source_of_multiple_matches_can_be_attributed.json index 4563f3798..7a5af3e5c 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/only_one_source_of_multiple_matches_can_be_attributed.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/only_one_source_of_multiple_matches_can_be_attributed.json @@ -103,7 +103,7 @@ "source_event_id": "2", "trigger_data": "1", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }, { @@ -115,7 +115,7 @@ "source_event_id": "2", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } } ], diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/priority_based_source_selection.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/priority_based_source_selection.json index 41595f546..fdc35df28 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/priority_based_source_selection.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/priority_based_source_selection.json @@ -78,7 +78,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [] diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWebSource_3_sources_3_triggers_3_reports.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWebSource_3_sources_3_triggers_3_reports.json index 8827e69b5..2c000a83b 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWebSource_3_sources_3_triggers_3_reports.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWebSource_3_sources_3_triggers_3_reports.json @@ -128,7 +128,7 @@ "source_event_id": "1", "trigger_data": "1", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }, { "report_time": "800176400001", @@ -139,7 +139,7 @@ "source_event_id": "2", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }, { "report_time": "800176400001", @@ -150,7 +150,7 @@ "source_event_id": "3", "trigger_data": "3", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [] diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-1_web-web_matching.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-1_web-web_matching.json index eac5f0a52..03fb47d11 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-1_web-web_matching.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-1_web-web_matching.json @@ -64,7 +64,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [] diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-1_web-web_matching_debug_api_ar_debug_different_registrant.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-1_web-web_matching_debug_api_ar_debug_different_registrant.json index ddb67affd..5ef74d0e6 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-1_web-web_matching_debug_api_ar_debug_different_registrant.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-1_web-web_matching_debug_api_ar_debug_different_registrant.json @@ -66,7 +66,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [] diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-1_web-web_matching_debug_api_ar_debug_same_registrant.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-1_web-web_matching_debug_api_ar_debug_same_registrant.json index b712042b8..f0c60f59e 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-1_web-web_matching_debug_api_ar_debug_same_registrant.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-1_web-web_matching_debug_api_ar_debug_same_registrant.json @@ -66,7 +66,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263, + "randomized_trigger_rate": 0.0008051, "source_debug_key" : "347982378", "trigger_debug_key" : "8971346783" } @@ -80,7 +80,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263, + "randomized_trigger_rate": 0.0008051, "source_debug_key" : "347982378", "trigger_debug_key" : "8971346783" } diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-1_web-web_matching_debug_api_no_adid_no_ar_debug.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-1_web-web_matching_debug_api_no_adid_no_ar_debug.json index 15dbcae8f..e43a0c409 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-1_web-web_matching_debug_api_no_adid_no_ar_debug.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-1_web-web_matching_debug_api_no_adid_no_ar_debug.json @@ -64,7 +64,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [] diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-1_web-web_matching_debug_join_disabled_different_registrant.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-1_web-web_matching_debug_join_disabled_different_registrant.json index 061c0c743..8142f2c57 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-1_web-web_matching_debug_join_disabled_different_registrant.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-1_web-web_matching_debug_join_disabled_different_registrant.json @@ -69,7 +69,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [] diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-1_web-web_matching_debug_join_enabled_different_registrant.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-1_web-web_matching_debug_join_enabled_different_registrant.json index ac91e0adf..0917d9434 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-1_web-web_matching_debug_join_enabled_different_registrant.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-1_web-web_matching_debug_join_enabled_different_registrant.json @@ -69,7 +69,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263, + "randomized_trigger_rate": 0.0008051, "source_debug_key" : "347982378", "trigger_debug_key" : "8971346783" } @@ -83,7 +83,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263, + "randomized_trigger_rate": 0.0008051, "source_debug_key" : "347982378", "trigger_debug_key" : "8971346783" } diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-2_app_and_web_matching.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-2_app_and_web_matching.json index 8be0277e0..dcf130f11 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-2_app_and_web_matching.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-2_app_and_web_matching.json @@ -88,7 +88,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0170218 + "randomized_trigger_rate": 0.0054129 } }, { "report_time": "800176400001", @@ -99,7 +99,7 @@ "source_event_id": "1", "trigger_data": "1", "source_type": "navigation", - "randomized_trigger_rate": 0.0170218 + "randomized_trigger_rate": 0.0054129 } }], "aggregatable_results": [] diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-2_web_only_matching.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-2_web_only_matching.json index faa480667..ccb8f3fce 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-2_web_only_matching.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_1-2_web_only_matching.json @@ -83,7 +83,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [] diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_app-web_adid_debug_api.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_app-web_adid_debug_api.json index da1aab976..b047a48a8 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_app-web_adid_debug_api.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_app-web_adid_debug_api.json @@ -83,7 +83,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0170218 + "randomized_trigger_rate": 0.0054129 } }] } diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_app-web_ar_debug_adid_debug_api.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_app-web_ar_debug_adid_debug_api.json index b161cb42f..681d1f66d 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_app-web_ar_debug_adid_debug_api.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_app-web_ar_debug_adid_debug_api.json @@ -83,7 +83,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0170218 + "randomized_trigger_rate": 0.0054129 } }] } diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_app-web_debug_api_ar_debug.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_app-web_debug_api_ar_debug.json index 9fb5654c9..57c247e4e 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_app-web_debug_api_ar_debug.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_app-web_debug_api_ar_debug.json @@ -82,7 +82,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0170218 + "randomized_trigger_rate": 0.0054129 } }] } diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_multiple_destinations.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_multiple_destinations.json index 93f63a51e..ca316b775 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_multiple_destinations.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_multiple_destinations.json @@ -89,7 +89,7 @@ "source_event_id": "1", "trigger_data": "1", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }, { @@ -101,7 +101,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } } ], diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_redirects_ignored.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_redirects_ignored.json index 646b2e2b1..280fc4193 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_redirects_ignored.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_redirects_ignored.json @@ -106,7 +106,7 @@ "source_event_id": "1", "trigger_data": "1", "source_type": "navigation", - "randomized_trigger_rate": 0.0170218 + "randomized_trigger_rate": 0.0054129 } }], "aggregatable_results": [] diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_web_app_matching_debug_api_adid_ar_debug.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_web_app_matching_debug_api_adid_ar_debug.json index 11d36313c..5a229be9b 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_web_app_matching_debug_api_adid_ar_debug.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/registerWeb_web_app_matching_debug_api_adid_ar_debug.json @@ -68,7 +68,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0170218 + "randomized_trigger_rate": 0.0054129 } }], "aggregatable_results": [] diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/register_1-1_app-app_matching.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/register_1-1_app-app_matching.json index 768d5f994..080c62ca1 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/register_1-1_app-app_matching.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/register_1-1_app-app_matching.json @@ -55,7 +55,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [] diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/register_app-app_empty_object_event_trigger_data.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/register_app-app_empty_object_event_trigger_data.json index d2be23319..9fd1eb923 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/register_app-app_empty_object_event_trigger_data.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/register_app-app_empty_object_event_trigger_data.json @@ -50,7 +50,7 @@ "source_event_id": "0", "trigger_data": "0", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [] diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/register_app-app_null_trigger_data_null_source_event_id.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/register_app-app_null_trigger_data_null_source_event_id.json index c36147be6..670c8de80 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/register_app-app_null_trigger_data_null_source_event_id.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/register_app-app_null_trigger_data_null_source_event_id.json @@ -55,7 +55,7 @@ "source_event_id": "0", "trigger_data": "0", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [] diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/register_app-web_dual_destination_source_storage_limit_debug_report.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/register_app-web_dual_destination_source_storage_limit_debug_report.json index 2041a9d2f..22e7b538a 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/register_app-web_dual_destination_source_storage_limit_debug_report.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/register_app-web_dual_destination_source_storage_limit_debug_report.json @@ -99,7 +99,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0170218 + "randomized_trigger_rate": 0.0054129 } }], "verbose_debug_reports": [ diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/register_app-web_source_storage_limit_debug_report.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/register_app-web_source_storage_limit_debug_report.json index dad35eb61..9a88bf402 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/register_app-web_source_storage_limit_debug_report.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/register_app-web_source_storage_limit_debug_report.json @@ -94,7 +94,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "verbose_debug_reports": [ diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/register_web-web_source_storage_limit_debug_report.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/register_web-web_source_storage_limit_debug_report.json index be217c98c..aaa2b6d4c 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/register_web-web_source_storage_limit_debug_report.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/register_web-web_source_storage_limit_debug_report.json @@ -100,7 +100,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "verbose_debug_reports": [ diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/three_sources_one_trigger.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/three_sources_one_trigger.json index 443987f27..9dec79b88 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/three_sources_one_trigger.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/three_sources_one_trigger.json @@ -99,7 +99,7 @@ "source_event_id": "2", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }], "aggregatable_results": [] diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/three_sources_three_triggers_one_dedup.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/three_sources_three_triggers_one_dedup.json index 17a8ecc36..0343b9bf3 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/three_sources_three_triggers_one_dedup.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/three_sources_three_triggers_one_dedup.json @@ -149,7 +149,7 @@ "source_event_id": "2", "trigger_data": "1", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }, { @@ -161,7 +161,7 @@ "source_event_id": "2", "trigger_data": "0", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } } ], diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/two_simple_matches_testing_multiplicity.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/two_simple_matches_testing_multiplicity.json index fa9c81136..aaabe1703 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/two_simple_matches_testing_multiplicity.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/misc/two_simple_matches_testing_multiplicity.json @@ -103,7 +103,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } }, { @@ -115,7 +115,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } } ], diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/xna/xna_disabled_basic.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/xna/xna_disabled_basic.json index d859f3413..8fcd67d55 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/xna/xna_disabled_basic.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/xna/xna_disabled_basic.json @@ -193,7 +193,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } } ], diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/xna/xna_original_source_wins_install_attribution.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/xna/xna_original_source_wins_install_attribution.json index ee188284d..9956388e9 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/xna/xna_original_source_wins_install_attribution.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/xna/xna_original_source_wins_install_attribution.json @@ -373,7 +373,7 @@ "source_event_id": "1", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } } ], diff --git a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/xna/xna_reg_has_original_xna_source_avoids_creating_derived_source.json b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/xna/xna_reg_has_original_xna_source_avoids_creating_derived_source.json index 233e156c5..bd792b74c 100644 --- a/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/xna/xna_reg_has_original_xna_source_avoids_creating_derived_source.json +++ b/adservices/tests/unittest/service-core/assets/msmt_e2e_tests/xna/xna_reg_has_original_xna_source_avoids_creating_derived_source.json @@ -183,7 +183,7 @@ "source_event_id": "2", "trigger_data": "2", "source_type": "navigation", - "randomized_trigger_rate": 0.0024263 + "randomized_trigger_rate": 0.0008051 } } ], diff --git a/adservices/tests/unittest/service-core/assets/msmt_interop_tests/event_report_windows.json b/adservices/tests/unittest/service-core/assets/msmt_interop_tests/event_report_windows.json new file mode 100644 index 000000000..e9a96f2fe --- /dev/null +++ b/adservices/tests/unittest/service-core/assets/msmt_interop_tests/event_report_windows.json @@ -0,0 +1,289 @@ +{ + "description": "Set report windows for navigation type event-level reporting", + "input": { + "registrations": [ + { + "timestamp": "0", + "registration_request": { + "context_origin": "https://source.test", + "attribution_src_url": "https://reporter.test/register-source", + "source_type": "navigation" + }, + "responses": [ + { + "url": "https://reporter.test/register-source", + "debug_permission": true, + "response": { + "Attribution-Reporting-Register-Source": { + "destination": "https://destination.test", + "source_event_id": "123", + "expiry": "1296000", + "event_report_windows": { + "start_time": 3600, + // 2592000 s clamped down to expiry. + "end_times": [86400, 2592000] + } + } + } + } + ] + }, + { + "timestamp": "1000", + "registration_request": { + "context_origin": "https://source.test", + "attribution_src_url": "https://reporter2.test/register-source", + "source_type": "navigation" + }, + "responses": [ + { + "url": "https://reporter2.test/register-source", + "debug_permission": true, + "response": { + "Attribution-Reporting-Register-Source": { + "destination": "https://destination2.test", + "source_event_id": "456", + "event_report_windows": { + "start_time": 0, + // 1800 rounded to 3600 (1 hr). + "end_times": [1800] + } + } + } + } + ] + }, + { + // Should result in an event-level report in the first window + // rounded up to 1 hour (3600 s). + "timestamp": "1900000", + "registration_request": { + "attribution_src_url": "https://reporter2.test/register-trigger", + "context_origin": "https://destination2.test" + }, + "responses": [ + { + "url": "https://reporter2.test/register-trigger", + "debug_permission": true, + "response": { + "Attribution-Reporting-Register-Trigger": { + "debug_reporting": true, + "event_trigger_data": [ + { + "trigger_data": "4" + } + ] + } + } + } + ] + }, + // Should not result in an event-level report as the event report start + // time has passed, + { + // 3,600,000 ms = 1 hour = clamped event_report_window value. + "timestamp": "3590000", + "registration_request": { + "attribution_src_url": "https://reporter.test/register-trigger", + "context_origin": "https://destination.test" + }, + "responses": [ + { + "url": "https://reporter.test/register-trigger", + "debug_permission": true, + "response": { + "Attribution-Reporting-Register-Trigger": { + "debug_reporting": true, + "event_trigger_data": [ + { + "trigger_data": "1" + } + ] + } + } + } + ] + }, + { + // Should result in an event-level report in the first window. + "timestamp": "3600000", + "registration_request": { + "attribution_src_url": "https://reporter.test/register-trigger", + "context_origin": "https://destination.test" + }, + "responses": [ + { + "url": "https://reporter.test/register-trigger", + "debug_permission": true, + "response": { + "Attribution-Reporting-Register-Trigger": { + "debug_reporting": true, + "event_trigger_data": [ + { + "trigger_data": "1" + } + ] + } + } + } + ] + }, + { + // Should not result in an event-level report. + "timestamp": "3700000", + "registration_request": { + "attribution_src_url": "https://reporter2.test/register-trigger", + "context_origin": "https://destination2.test" + }, + "responses": [ + { + "url": "https://reporter2.test/register-trigger", + "debug_permission": true, + "response": { + "Attribution-Reporting-Register-Trigger": { + "debug_reporting": true, + "event_trigger_data": [ + { + "trigger_data": "5" + } + ] + } + } + } + ] + }, + { + // Should result in an event-level report in the last window. + "timestamp": "1209600000", + "registration_request": { + "attribution_src_url": "https://reporter.test/register-trigger", + "context_origin": "https://destination.test" + }, + "responses": [ + { + "url": "https://reporter.test/register-trigger", + "debug_permission": true, + "response": { + "Attribution-Reporting-Register-Trigger": { + "debug_reporting": true, + "event_trigger_data": [ + { + "trigger_data": "2" + } + ] + } + } + } + ] + }, + { + // Should not result in an event-level report as the last window was + // clamped to 1296000 s. + "timestamp": "1296000000", + "registration_request": { + "attribution_src_url": "https://reporter.test/register-trigger", + "context_origin": "https://destination.test" + }, + "responses": [ + { + "url": "https://reporter.test/register-trigger", + "debug_permission": true, + "response": { + "Attribution-Reporting-Register-Trigger": { + "debug_reporting": true, + "event_trigger_data": [ + { + "trigger_data": "3" + } + ] + } + } + } + ] + } + ] + }, + "output": { + "unparsable_registrations": [], + "reports": [ + { + "payload": [ + { + "body": { + "attribution_destination": "https://destination.test", + "source_event_id": "123", + "source_site": "https://source.test" + }, + "type": "trigger-event-report-window-not-started" + } + ], + "report_time": "3590000", + "report_url": "https://reporter.test/.well-known/attribution-reporting/debug/verbose" + }, + { + "payload": { + "attribution_destination": "https://destination2.test", + "randomized_trigger_rate": 0.0001372, + // 3601 s = 3600 s (rounded window) + 1 s (source timestamp). + "scheduled_report_time": "3601", + "source_event_id": "456", + "source_type": "navigation", + "trigger_data": "4" + }, + "report_url": "https://reporter2.test/.well-known/attribution-reporting/report-event-attribution", + "report_time": "3601000" + }, + { + "payload": [ + { + "body": { + "attribution_destination": "https://destination2.test", + "source_event_id": "456", + "source_site": "https://source.test" + }, + "type": "trigger-event-report-window-passed" + } + ], + "report_time": "3700000", + "report_url": "https://reporter2.test/.well-known/attribution-reporting/debug/verbose" + }, + { + "payload": { + "attribution_destination": "https://destination.test", + "randomized_trigger_rate": 0.0008051, + "scheduled_report_time": "86400", + "source_event_id": "123", + "source_type": "navigation", + "trigger_data": "1" + }, + "report_url": "https://reporter.test/.well-known/attribution-reporting/report-event-attribution", + "report_time": "86400000" + }, + { + "payload": { + "attribution_destination": "https://destination.test", + "randomized_trigger_rate": 0.0008051, + "scheduled_report_time": "1296000", + "source_event_id": "123", + "source_type": "navigation", + "trigger_data": "2" + }, + "report_url": "https://reporter.test/.well-known/attribution-reporting/report-event-attribution", + "report_time": "1296000000" + }, + { + "payload": [ + { + "body": { + "attribution_destination": "https://destination.test" + }, + // Source has expired at this point. + "type": "trigger-no-matching-source" + } + ], + "report_time": "1296000000", + "report_url": "https://reporter.test/.well-known/attribution-reporting/debug/verbose" + } + ] + } +}
\ No newline at end of file diff --git a/adservices/tests/unittest/service-core/common/Android.bp b/adservices/tests/unittest/service-core/common/Android.bp new file mode 100644 index 000000000..b128e30ec --- /dev/null +++ b/adservices/tests/unittest/service-core/common/Android.bp @@ -0,0 +1,33 @@ +// Copyright (C) 2024 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test { + name: "AdServicesServiceCoreCommonUnitTests", + srcs: [ + "src/**/*.java", + ], + defaults: ["AdServices-ServiceCoreUnitTest-Defaults"], +} + +android_test { + name: "AdExtServicesServiceCoreCommonUnitTests", + srcs: [ + "src/**/*.java", + ], + defaults: ["AdExtServices-ServiceCoreUnitTest-Defaults"], + test_config: "AndroidTest.ExtServices.xml", +} diff --git a/adservices/tests/unittest/service-core/common/AndroidManifest.xml b/adservices/tests/unittest/service-core/common/AndroidManifest.xml new file mode 100644 index 000000000..cdbd758b5 --- /dev/null +++ b/adservices/tests/unittest/service-core/common/AndroidManifest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.adservices.servicecorecommontest"> + <!-- android:debuggable="true" is needed. See b/228384531 --> + <application android:debuggable="true"> + <uses-library android:name="android.ext.adservices" android:required="false"/> + </application> + + <!-- Entries below are necessary so atest can work without specifying the project. --> + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.adservices.servicecorecommontest"> + </instrumentation> +</manifest> diff --git a/adservices/tests/unittest/service-core/common/AndroidTest.ExtServices.xml b/adservices/tests/unittest/service-core/common/AndroidTest.ExtServices.xml new file mode 100644 index 000000000..900999fa6 --- /dev/null +++ b/adservices/tests/unittest/service-core/common/AndroidTest.ExtServices.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 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. + --> +<configuration + description="Config for AdServices Service-core common test cases in ExtServices"> + <object class="com.android.tradefed.testtype.suite.module.MaxSdkModuleController" + type="module_controller"> + <option name="max-sdk-level" value="32"/> + </object> + + <!-- Prevent test from running on Android T+ --> + <object class="com.android.tradefed.testtype.suite.module.Sdk30ModuleController" + type="module_controller"/> + + <!-- Prevent tests from running on Android Q- --> + <object class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController" + type="module_controller"> + <option name="mainline-module-package-name" value="com.google.android.extservices"/> + </object> + + <option name="test-tag" value="AdExtServicesServiceCoreCommonUnitTests"/> + <option name="config-descriptor:metadata" key="mainline-param" + value="com.google.android.extservices.apex"/> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="check-min-sdk" value="true"/> + <option name="test-file-name" value="AdExtServicesServiceCoreCommonUnitTests.apk"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" value="setprop log.tag.adservices VERBOSE"/> + </target_preparer> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.adservices.servicecorecommontest"/> + </test> +</configuration> diff --git a/adservices/tests/unittest/service-core/common/AndroidTest.xml b/adservices/tests/unittest/service-core/common/AndroidTest.xml new file mode 100644 index 000000000..f9a08c35b --- /dev/null +++ b/adservices/tests/unittest/service-core/common/AndroidTest.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 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. + --> +<configuration description="Config for Ad Services Service-core common test cases"> + <object class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController" + type="module_controller"> + <option name="mainline-module-package-name" value="com.google.android.adservices"/> + </object> + + <option name="test-tag" value="AdServicesServiceCoreCommonUnitTests"/> + <option name="config-descriptor:metadata" key="mainline-param" + value="com.google.android.adservices.apex"/> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="check-min-sdk" value="true"/> + <option name="test-file-name" value="AdServicesServiceCoreCommonUnitTests.apk"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" value="setprop log.tag.adservices VERBOSE"/> + </target_preparer> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.adservices.servicecorecommontest"/> + </test> +</configuration> diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/errorlogging/AdServicesErrorLoggerImplTest.java b/adservices/tests/unittest/service-core/common/src/com/android/adservices/errorlogging/AdServicesErrorLoggerImplTest.java index 847c08e52..bdf20f56d 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/errorlogging/AdServicesErrorLoggerImplTest.java +++ b/adservices/tests/unittest/service-core/common/src/com/android/adservices/errorlogging/AdServicesErrorLoggerImplTest.java @@ -21,10 +21,9 @@ import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICE import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__TOPICS; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.database.sqlite.SQLiteException; @@ -37,11 +36,10 @@ import com.google.common.collect.ImmutableList; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; public final class AdServicesErrorLoggerImplTest extends AdServicesMockitoTestCase { - private AdServicesErrorLoggerImpl mErrorLogger; - // Constants used for tests private static final String CLASS_NAME = "TopicsService"; private static final String METHOD_NAME = "getTopics"; @@ -50,17 +48,19 @@ public final class AdServicesErrorLoggerImplTest extends AdServicesMockitoTestCa private static final String SQ_LITE_EXCEPTION = "SQLiteException"; @Mock private Flags mFlags; - @Mock StatsdAdServicesErrorLogger mStatsdLoggerMock; + @Mock private StatsdAdServicesErrorLogger mStatsdLoggerMock; + + private AdServicesErrorLoggerImpl mErrorLogger; @Before public void setUp() { mErrorLogger = new AdServicesErrorLoggerImpl(mFlags, mStatsdLoggerMock); - doReturn(ImmutableList.of()).when(mFlags).getErrorCodeLoggingDenyList(); + mockErrorCodeLoggingDenyList(ImmutableList.of()); } @Test public void testLogError_errorLoggingFlagDisabled() { - doReturn(false).when(mFlags).getAdServicesErrorLoggingEnabled(); + mockAdServicesErrorLogging(/* enabled= */ false); mErrorLogger.logError( AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CONSENT_REVOKED_ERROR, PPAPI_NAME); @@ -70,10 +70,9 @@ public final class AdServicesErrorLoggerImplTest extends AdServicesMockitoTestCa @Test public void testLogError_errorLoggingFlagEnabled_errorCodeLoggingDenied() { - doReturn(true).when(mFlags).getAdServicesErrorLoggingEnabled(); - doReturn(ImmutableList.of(AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CONSENT_REVOKED_ERROR)) - .when(mFlags) - .getErrorCodeLoggingDenyList(); + mockAdServicesErrorLogging(/* enabled= */ true); + mockErrorCodeLoggingDenyList( + ImmutableList.of(AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CONSENT_REVOKED_ERROR)); mErrorLogger.logError( AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CONSENT_REVOKED_ERROR, PPAPI_NAME); @@ -83,16 +82,23 @@ public final class AdServicesErrorLoggerImplTest extends AdServicesMockitoTestCa @Test public void testLogError_errorLoggingFlagEnabled() { - doReturn(true).when(mFlags).getAdServicesErrorLoggingEnabled(); + mockAdServicesErrorLogging(/* enabled= */ true); + ArgumentCaptor<AdServicesErrorStats> adServicesErrorStatsArgumentCaptor = + ArgumentCaptor.forClass(AdServicesErrorStats.class); + mErrorLogger.logError( AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CONSENT_REVOKED_ERROR, PPAPI_NAME); - verify(mStatsdLoggerMock).logAdServicesError(any()); + verify(mStatsdLoggerMock).logAdServicesError(adServicesErrorStatsArgumentCaptor.capture()); + AdServicesErrorStats adServicesErrorStats = adServicesErrorStatsArgumentCaptor.getValue(); + expect.that(adServicesErrorStats.getErrorCode()) + .isEqualTo(AD_SERVICES_ERROR_REPORTED__ERROR_CODE__CONSENT_REVOKED_ERROR); + expect.that(adServicesErrorStats.getPpapiName()).isEqualTo(PPAPI_NAME); } @Test public void testLogErrorWithExceptionInfo_errorLoggingFlagDisabled() { - doReturn(false).when(mFlags).getAdServicesErrorLoggingEnabled(); + mockAdServicesErrorLogging(/* enabled= */ false); mErrorLogger.logErrorWithExceptionInfo( new Exception(), @@ -104,10 +110,9 @@ public final class AdServicesErrorLoggerImplTest extends AdServicesMockitoTestCa @Test public void testLogErrorWithExceptionInfo_errorLoggingFlagEnabled_errorCodeLoggingDenied() { - doReturn(true).when(mFlags).getAdServicesErrorLoggingEnabled(); - doReturn(ImmutableList.of(AD_SERVICES_ERROR_REPORTED__ERROR_CODE__DATABASE_READ_EXCEPTION)) - .when(mFlags) - .getErrorCodeLoggingDenyList(); + mockAdServicesErrorLogging(/* enabled= */ true); + mockErrorCodeLoggingDenyList( + ImmutableList.of(AD_SERVICES_ERROR_REPORTED__ERROR_CODE__DATABASE_READ_EXCEPTION)); mErrorLogger.logErrorWithExceptionInfo( new Exception(), @@ -119,8 +124,7 @@ public final class AdServicesErrorLoggerImplTest extends AdServicesMockitoTestCa @Test public void testLogErrorWithExceptionInfo_errorLoggingFlagEnabled() { - doReturn(true).when(mFlags).getAdServicesErrorLoggingEnabled(); - + mockAdServicesErrorLogging(/* enabled= */ true); Exception exception = createSQLiteException(CLASS_NAME, METHOD_NAME, LINE_NUMBER); mErrorLogger.logErrorWithExceptionInfo( @@ -138,13 +142,12 @@ public final class AdServicesErrorLoggerImplTest extends AdServicesMockitoTestCa .setLineNumber(LINE_NUMBER) .setLastObservedExceptionName(SQ_LITE_EXCEPTION) .build(); - verify(mStatsdLoggerMock).logAdServicesError(eq(stats)); + verify(mStatsdLoggerMock).logAdServicesError(stats); } @Test public void testLogErrorWithExceptionInfo_fullyQualifiedClassName_errorLoggingFlagEnabled() { - doReturn(true).when(mFlags).getAdServicesErrorLoggingEnabled(); - + mockAdServicesErrorLogging(/* enabled= */ true); String fullClassName = "com.android.adservices.topics.TopicsService"; Exception exception = createSQLiteException(fullClassName, METHOD_NAME, LINE_NUMBER); @@ -163,12 +166,12 @@ public final class AdServicesErrorLoggerImplTest extends AdServicesMockitoTestCa .setLineNumber(LINE_NUMBER) .setLastObservedExceptionName(SQ_LITE_EXCEPTION) .build(); - verify(mStatsdLoggerMock).logAdServicesError(eq(stats)); + verify(mStatsdLoggerMock).logAdServicesError(stats); } @Test public void testLogErrorWithExceptionInfo_emptyClassName_errorLoggingFlagEnabled() { - doReturn(true).when(mFlags).getAdServicesErrorLoggingEnabled(); + mockAdServicesErrorLogging(/* enabled= */ true); Exception exception = createSQLiteException(/* className = */ "", METHOD_NAME, LINE_NUMBER); @@ -186,7 +189,7 @@ public final class AdServicesErrorLoggerImplTest extends AdServicesMockitoTestCa .setLineNumber(LINE_NUMBER) .setLastObservedExceptionName(SQ_LITE_EXCEPTION) .build(); - verify(mStatsdLoggerMock).logAdServicesError(eq(stats)); + verify(mStatsdLoggerMock).logAdServicesError(stats); } Exception createSQLiteException(String className, String methodName, int lineNumber) { @@ -200,17 +203,11 @@ public final class AdServicesErrorLoggerImplTest extends AdServicesMockitoTestCa return exception; } - Exception createSQLiteExceptionwith3StackTraceElements( - String className, String methodName, int lineNumber) { - StackTraceElement[] stackTraceElements = - new StackTraceElement[] { - new StackTraceElement("AdServicesErrorLoggerImpl", "logError", "file", 4), - new StackTraceElement("ErrorLogUtil", "e", "file", 4), - new StackTraceElement(className, methodName, "file", lineNumber) - }; + private void mockAdServicesErrorLogging(boolean enabled) { + when(mFlags.getAdServicesErrorLoggingEnabled()).thenReturn(enabled); + } - Exception exception = new SQLiteException(); - exception.setStackTrace(stackTraceElements); - return exception; + private void mockErrorCodeLoggingDenyList(ImmutableList<Integer> errorCodeLoggingDenyList) { + when(mFlags.getErrorCodeLoggingDenyList()).thenReturn(errorCodeLoggingDenyList); } } diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/data/customaudience/DBCustomAudienceBackgroundFetchDataTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/data/customaudience/DBCustomAudienceBackgroundFetchDataTest.java index e9574eca6..a078f13c0 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/data/customaudience/DBCustomAudienceBackgroundFetchDataTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/data/customaudience/DBCustomAudienceBackgroundFetchDataTest.java @@ -26,26 +26,21 @@ import static org.junit.Assert.assertThrows; import android.adservices.common.CommonFixture; import android.adservices.customaudience.CustomAudienceFixture; +import com.android.adservices.common.AdServicesExtendedMockitoTestCase; import com.android.adservices.customaudience.DBCustomAudienceBackgroundFetchDataFixture; import com.android.adservices.service.Flags; import com.android.adservices.service.FlagsFactory; -import com.android.adservices.service.PhFlagsFixture; import com.android.adservices.service.customaudience.BackgroundFetchRunner; import com.android.adservices.service.customaudience.CustomAudienceUpdatableData; import com.android.adservices.service.customaudience.CustomAudienceUpdatableDataFixture; -import com.android.modules.utils.testing.TestableDeviceConfig; +import com.android.modules.utils.testing.ExtendedMockitoRule.SpyStatic; -import org.json.JSONException; -import org.junit.Rule; import org.junit.Test; import java.time.Instant; -public class DBCustomAudienceBackgroundFetchDataTest { - // This rule is used for configuring P/H flags - @Rule - public final TestableDeviceConfig.TestableDeviceConfigRule mDeviceConfigRule = - new TestableDeviceConfig.TestableDeviceConfigRule(); +public final class DBCustomAudienceBackgroundFetchDataTest + extends AdServicesExtendedMockitoTestCase { @Test public void testBuildFetchDataSuccess() { @@ -179,22 +174,28 @@ public class DBCustomAudienceBackgroundFetchDataTest { @Test public void testComputeNextEligibleUpdateTimeWithPhFlags() { - long configuredBaseIntervalS = 100L; - PhFlagsFixture.configureFledgeBackgroundFetchEligibleUpdateBaseIntervalS( - configuredBaseIntervalS); + Flags flags = FlagsFactory.getFlagsForTest(); + long configuredBaseIntervalS = flags.getFledgeBackgroundFetchEligibleUpdateBaseIntervalS(); Instant expectedEligibleUpdateTime = CommonFixture.FIXED_NOW.plusSeconds(configuredBaseIntervalS); Instant actualEligibleUpdateTime = DBCustomAudienceBackgroundFetchData .computeNextEligibleUpdateTimeAfterSuccessfulUpdate( - CommonFixture.FIXED_NOW); + CommonFixture.FIXED_NOW, flags); assertEquals(expectedEligibleUpdateTime, actualEligibleUpdateTime); } @Test - public void testCopyWithFullSuccessfulUpdatableDataResetsFailureCounts() throws JSONException { + @SpyStatic(FlagsFactory.class) + public void testCopyWithFullSuccessfulUpdatableDataResetsFailureCounts() throws Exception { + // NOTE: copyWithUpdatableData() will eventually call the + // computeNextEligibleUpdateTimeAfterSuccessfulUpdate() method that calls + // FlagsFactory.getInstance(), so we need to mock that method (otherwise it would call + // DeviceConfig and fail due to lack of permissions) + extendedMockito.mockGetFlags(FlagsFactory.getFlagsForTest()); + DBCustomAudienceBackgroundFetchData originalFetchData = DBCustomAudienceBackgroundFetchDataFixture.getValidBuilderByBuyer( CommonFixture.VALID_BUYER_1) @@ -206,8 +207,7 @@ public class DBCustomAudienceBackgroundFetchDataTest { Instant attemptedUpdateTime = CommonFixture.FIXED_NOW.plusSeconds(10); Instant expectedEligibleUpdateTime = DBCustomAudienceBackgroundFetchData - .computeNextEligibleUpdateTimeAfterSuccessfulUpdate( - attemptedUpdateTime, FlagsFactory.getFlagsForTest()); + .computeNextEligibleUpdateTimeAfterSuccessfulUpdate(attemptedUpdateTime); CustomAudienceUpdatableData updatableData = CustomAudienceUpdatableDataFixture.getValidBuilderFullSuccessfulResponse() diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/data/enrollment/EnrollmentDaoTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/data/enrollment/EnrollmentDaoTest.java index 5c16e32d8..aa0ac3f7c 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/data/enrollment/EnrollmentDaoTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/data/enrollment/EnrollmentDaoTest.java @@ -79,7 +79,7 @@ public class EnrollmentDaoTest { @Mock private EnrollmentUtil mEnrollmentUtil; @Mock private SharedDbHelper mMockDbHelper; - private static final EnrollmentData ENROLLMENT_DATA1 = + public static final EnrollmentData ENROLLMENT_DATA1 = new EnrollmentData.Builder() .setEnrollmentId("1") .setCompanyId("1001") diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/AbstractDbIntegrationTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/AbstractDbIntegrationTest.java index 3cea7026f..b55f82855 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/AbstractDbIntegrationTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/AbstractDbIntegrationTest.java @@ -666,6 +666,9 @@ public abstract class AbstractDbIntegrationTest { values.put( MeasurementTables.AsyncRegistrationContract.PLATFORM_AD_ID, asyncRegistration.getPlatformAdId()); + values.put( + MeasurementTables.AsyncRegistrationContract.REDIRECT_BEHAVIOR, + asyncRegistration.getRedirectBehavior().name()); long rowId = db.insert( MeasurementTables.AsyncRegistrationContract.TABLE, diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/DbState.java b/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/DbState.java index 24139fe83..f6c071288 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/DbState.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/DbState.java @@ -30,6 +30,7 @@ import com.android.adservices.service.measurement.Source; import com.android.adservices.service.measurement.Trigger; import com.android.adservices.service.measurement.aggregation.AggregateEncryptionKey; import com.android.adservices.service.measurement.aggregation.AggregateReport; +import com.android.adservices.service.measurement.registration.AsyncRedirect; import com.android.adservices.service.measurement.registration.AsyncRegistration; import com.android.adservices.service.measurement.reporting.DebugReport; import com.android.adservices.service.measurement.util.UnsignedLong; @@ -609,6 +610,8 @@ public class DbState { .setVerifiedDestination(Uri.parse(aJSON.getString("verifiedDestination"))) .setWebDestination(Uri.parse(aJSON.getString("webDestination"))) .setSourceType(Source.SourceType.values()[aJSON.getInt("sourceType")]) + .setRedirectBehavior( + AsyncRedirect.RedirectBehavior.valueOf(aJSON.getString("redirectBehavior"))) .build(); } diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/MeasurementDaoTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/MeasurementDaoTest.java index f12e36b93..fec0f7ce1 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/MeasurementDaoTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/MeasurementDaoTest.java @@ -4887,44 +4887,6 @@ public class MeasurementDaoTest { .getCount()); } - private static AsyncRegistration buildAsyncRegistration(String id) { - - return new AsyncRegistration.Builder() - .setId(id) - .setOsDestination(Uri.parse("android-app://installed-app-destination")) - .setRegistrant(INSTALLED_REGISTRANT) - .setTopOrigin(INSTALLED_REGISTRANT) - .setAdIdPermission(false) - .setType(AsyncRegistration.RegistrationType.APP_SOURCE) - .setRegistrationId(UUID.randomUUID().toString()) - .build(); - } - - private static AsyncRegistration buildAsyncRegistrationWithNotDestination(String id) { - return new AsyncRegistration.Builder() - .setId(id) - .setOsDestination(Uri.parse("android-app://not-installed-app-destination")) - .setRegistrant(INSTALLED_REGISTRANT) - .setTopOrigin(INSTALLED_REGISTRANT) - .setAdIdPermission(false) - .setType(AsyncRegistration.RegistrationType.APP_SOURCE) - .setRequestTime(Long.MAX_VALUE) - .setRegistrationId(UUID.randomUUID().toString()) - .build(); - } - - private static AsyncRegistration buildAsyncRegistrationWithNotRegistrant(String id) { - return new AsyncRegistration.Builder() - .setId(id) - .setOsDestination(Uri.parse("android-app://installed-app-destination")) - .setRegistrant(NOT_INSTALLED_REGISTRANT) - .setTopOrigin(NOT_INSTALLED_REGISTRANT) - .setAdIdPermission(false) - .setType(AsyncRegistration.RegistrationType.APP_SOURCE) - .setRegistrationId(UUID.randomUUID().toString()) - .build(); - } - @Test public void testDeleteDebugReport() { SQLiteDatabase db = MeasurementDbHelper.getInstance(sContext).safeGetWritableDatabase(); @@ -6163,6 +6125,9 @@ public class MeasurementDaoTest { assertEquals( asyncRegistration.getPlatformAdId(), validAsyncRegistration.getPlatformAdId()); assertEquals(asyncRegistration.getPostBody(), validAsyncRegistration.getPostBody()); + assertEquals( + asyncRegistration.getRedirectBehavior(), + validAsyncRegistration.getRedirectBehavior()); } } diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/MeasurementDbSchemaTrail.java b/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/MeasurementDbSchemaTrail.java index 526eb9595..35d0321a2 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/MeasurementDbSchemaTrail.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/MeasurementDbSchemaTrail.java @@ -2036,6 +2036,46 @@ public class MeasurementDbSchemaTrail { + " TEXT " + ")"; + public static final String CREATE_TABLE_ASYNC_REGISTRATION_V31 = + "CREATE TABLE " + + AsyncRegistrationContract.TABLE + + " (" + + AsyncRegistrationContract.ID + + " TEXT PRIMARY KEY NOT NULL, " + + AsyncRegistrationContract.REGISTRATION_URI + + " TEXT, " + + AsyncRegistrationContract.WEB_DESTINATION + + " TEXT, " + + AsyncRegistrationContract.OS_DESTINATION + + " TEXT, " + + AsyncRegistrationContract.VERIFIED_DESTINATION + + " TEXT, " + + AsyncRegistrationContract.TOP_ORIGIN + + " TEXT, " + + AsyncRegistrationContract.SOURCE_TYPE + + " INTEGER, " + + AsyncRegistrationContract.REGISTRANT + + " TEXT, " + + AsyncRegistrationContract.REQUEST_TIME + + " INTEGER, " + + AsyncRegistrationContract.RETRY_COUNT + + " INTEGER, " + + AsyncRegistrationContract.TYPE + + " INTEGER, " + + AsyncRegistrationContract.DEBUG_KEY_ALLOWED + + " INTEGER, " + + AsyncRegistrationContract.AD_ID_PERMISSION + + " INTEGER, " + + AsyncRegistrationContract.REGISTRATION_ID + + " TEXT NOT NULL," + + AsyncRegistrationContract.PLATFORM_AD_ID + + " TEXT, " + + AsyncRegistrationContract.REQUEST_POST_BODY + + " TEXT, " + + AsyncRegistrationContract.REDIRECT_BEHAVIOR + + " TEXT " + + ")"; + private static final String CREATE_TABLE_DEBUG_REPORT_V6 = "CREATE TABLE IF NOT EXISTS " + DebugReportContract.TABLE @@ -2489,6 +2529,12 @@ public class MeasurementDbSchemaTrail { return createStatements; } + private static Map<String, String> getCreateStatementByTableV31() { + Map<String, String> createStatements = new HashMap<>(getCreateStatementByTableV30()); + createStatements.put(AsyncRegistrationContract.TABLE, CREATE_TABLE_ASYNC_REGISTRATION_V31); + return createStatements; + } + private static Map<String, String> getCreateIndexesV7() { Map<String, String> createIndexes = new HashMap<>(); createIndexes.putAll(CREATE_INDEXES_V6); @@ -2595,6 +2641,10 @@ public class MeasurementDbSchemaTrail { return getCreateIndexesV29(); } + private static Map<String, String> getCreateIndexesV31() { + return getCreateIndexesV30(); + } + private static final Map<Integer, Collection<String>> CREATE_TABLES_STATEMENTS_BY_VERSION = new ImmutableMap.Builder<Integer, Collection<String>>() .put(6, CREATE_STATEMENT_BY_TABLE_V6.values()) @@ -2622,6 +2672,7 @@ public class MeasurementDbSchemaTrail { .put(28, getCreateStatementByTableV28().values()) .put(29, getCreateStatementByTableV29().values()) .put(30, getCreateStatementByTableV30().values()) + .put(31, getCreateStatementByTableV31().values()) .build(); private static final Map<Integer, Collection<String>> CREATE_INDEXES_STATEMENTS_BY_VERSION = @@ -2651,6 +2702,7 @@ public class MeasurementDbSchemaTrail { .put(28, getCreateIndexesV28().values()) .put(29, getCreateIndexesV29().values()) .put(30, getCreateIndexesV30().values()) + .put(31, getCreateIndexesV31().values()) .build(); /** diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/ContentValueFixtures.java b/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/ContentValueFixtures.java index a12ea65f9..0b3893126 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/ContentValueFixtures.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/ContentValueFixtures.java @@ -536,6 +536,15 @@ public class ContentValueFixtures { return asyncRegistration; } + /** + * Get content values for V31 migration + * + * @return ContentValues for AsyncRegistration table + */ + public static ContentValues generateAsyncRegistrationContentValuesV31() { + return generateAsyncRegistrationContentValuesV24(); + } + public static ContentValues generateSourceContentValuesV1() { ContentValues source = new ContentValues(); diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV30Test.java b/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV30Test.java index ca859a498..d076d277b 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV30Test.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV30Test.java @@ -63,7 +63,6 @@ public class MeasurementDbMigratorV30Test extends MeasurementDbMigratorTestBase MeasurementTables.SourceContract.TABLE, Set.of(MeasurementTables.SourceContract.ID)); MigrationTestHelper.verifyDataInDb(db, fakeData, new HashMap<>(), columnsToBeSkipped); - // Check that new columns are initialized with empty string List<Pair<String, String>> tableAndNewColumnPairs = new ArrayList<>(); tableAndNewColumnPairs.add( new Pair<>( diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV31Test.java b/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV31Test.java new file mode 100644 index 000000000..d41fb695b --- /dev/null +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/data/measurement/migration/MeasurementDbMigratorV31Test.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2023 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.data.measurement.migration; + +import static com.android.adservices.data.DbTestUtil.getDbHelperForTest; + +import static org.junit.Assert.assertEquals; + +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; + +import com.android.adservices.data.measurement.MeasurementDbHelper; +import com.android.adservices.data.measurement.MeasurementTables; +import com.android.adservices.service.measurement.registration.AsyncRedirect; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@RunWith(MockitoJUnitRunner.class) +public class MeasurementDbMigratorV31Test extends MeasurementDbMigratorTestBase { + @Test + public void performMigration_v30ToV31WithData_maintainsDataIntegrity() { + // Setup + MeasurementDbHelper dbHelper = + new MeasurementDbHelper( + sContext, + MEASUREMENT_DATABASE_NAME_FOR_MIGRATION, + 30, + getDbHelperForTest()); + SQLiteDatabase db = dbHelper.getWritableDatabase(); + Map<String, List<ContentValues>> testData = createFakeDataV30(); + MigrationTestHelper.populateDb(db, testData); + + // Execution + getTestSubject().performMigration(db, 30, 31); + + // Assertion + MigrationTestHelper.verifyDataInDb(db, testData); + + String[] columns = + new String[] { + MeasurementTables.AsyncRegistrationContract.REDIRECT_BEHAVIOR, + }; + try (Cursor cursor = + db.query( + MeasurementTables.AsyncRegistrationContract.TABLE, + columns, /* selection */ + null, /* selectionArgs */ + null, /* groupBy */ + null, /* having */ + null, /* orderBy */ + null)) { + assertEquals(1, cursor.getCount()); + while (cursor.moveToNext()) { + assertEquals( + AsyncRedirect.RedirectBehavior.AS_IS.name(), + cursor.getString(cursor.getColumnIndex(columns[0]))); + } + } + } + + private Map<String, List<ContentValues>> createFakeDataV30() { + Map<String, List<ContentValues>> tableRowsMap = new LinkedHashMap<>(); + + // AsyncRegistration table + List<ContentValues> asyncRegistrationRows = new ArrayList<>(); + ContentValues asyncRegistration = + ContentValueFixtures.generateAsyncRegistrationContentValuesV24(); + asyncRegistration.put( + MeasurementTables.AsyncRegistrationContract.ID, UUID.randomUUID().toString()); + asyncRegistrationRows.add(asyncRegistration); + tableRowsMap.put(MeasurementTables.AsyncRegistrationContract.TABLE, asyncRegistrationRows); + + return tableRowsMap; + } + + @Override + int getTargetVersion() { + return 31; + } + + @Override + AbstractMeasurementDbMigrator getTestSubject() { + return new MeasurementDbMigratorV31(); + } +} diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/data/signals/DBSignalsUpdateMetadataTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/data/signals/DBSignalsUpdateMetadataTest.java new file mode 100644 index 000000000..f18839359 --- /dev/null +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/data/signals/DBSignalsUpdateMetadataTest.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2023 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.data.signals; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertThrows; + +import android.adservices.common.AdTechIdentifier; +import android.adservices.common.CommonFixture; + +import org.junit.Test; + +public class DBSignalsUpdateMetadataTest { + + @Test + public void testCreateSignalsUpdateMetadata() { + AdTechIdentifier buyer = CommonFixture.VALID_BUYER_1; + DBSignalsUpdateMetadata signalsUpdateMetadata = + DBSignalsUpdateMetadata.create(buyer, CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI); + assertEquals(buyer, signalsUpdateMetadata.getBuyer()); + assertEquals( + CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI, + signalsUpdateMetadata.getLastSignalsUpdatedTime()); + } + + @Test + public void testBuildSignalsUpdateMetadata() { + AdTechIdentifier buyer = CommonFixture.VALID_BUYER_1; + DBSignalsUpdateMetadata signalsUpdatedTime = + DBSignalsUpdateMetadata.builder() + .setBuyer(buyer) + .setLastSignalsUpdatedTime(CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI) + .build(); + assertEquals(buyer, signalsUpdatedTime.getBuyer()); + assertEquals( + CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI, + signalsUpdatedTime.getLastSignalsUpdatedTime()); + } + + @Test + public void testNullFails() { + assertThrows( + IllegalStateException.class, + () -> { + DBSignalsUpdateMetadata.builder().build(); + }); + } + + @Test + public void testEquals() { + AdTechIdentifier buyer = CommonFixture.VALID_BUYER_1; + DBSignalsUpdateMetadata signalsUpdateMetadata1 = + DBSignalsUpdateMetadata.builder() + .setBuyer(buyer) + .setLastSignalsUpdatedTime(CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI) + .build(); + DBSignalsUpdateMetadata signalsUpdateMetadata2 = + DBSignalsUpdateMetadata.builder() + .setBuyer(buyer) + .setLastSignalsUpdatedTime(CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI) + .build(); + assertEquals(signalsUpdateMetadata1, signalsUpdateMetadata2); + } + + @Test + public void testNotEquals() { + AdTechIdentifier buyer1 = CommonFixture.VALID_BUYER_1; + AdTechIdentifier buyer2 = CommonFixture.VALID_BUYER_2; + DBSignalsUpdateMetadata signalsUpdateMetadata1 = + DBSignalsUpdateMetadata.builder() + .setBuyer(buyer1) + .setLastSignalsUpdatedTime(CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI) + .build(); + DBSignalsUpdateMetadata signalsUpdateMetadata2 = + DBSignalsUpdateMetadata.builder() + .setBuyer(buyer2) + .setLastSignalsUpdatedTime(CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI) + .build(); + assertNotEquals(signalsUpdateMetadata1, signalsUpdateMetadata2); + } + + @Test + public void testHashCode() { + AdTechIdentifier buyer = CommonFixture.VALID_BUYER_1; + DBSignalsUpdateMetadata signalsUpdateMetadata1 = + DBSignalsUpdateMetadata.builder() + .setBuyer(buyer) + .setLastSignalsUpdatedTime(CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI) + .build(); + DBSignalsUpdateMetadata signalsUpdateMetadata2 = + DBSignalsUpdateMetadata.builder() + .setBuyer(buyer) + .setLastSignalsUpdatedTime(CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI) + .build(); + assertEquals(signalsUpdateMetadata1.hashCode(), signalsUpdateMetadata2.hashCode()); + } +} diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/data/signals/EncoderLogicHandlerTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/data/signals/EncoderLogicHandlerTest.java index f27a31a7f..98b5af305 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/data/signals/EncoderLogicHandlerTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/data/signals/EncoderLogicHandlerTest.java @@ -76,6 +76,8 @@ public class EncoderLogicHandlerTest { @Mock private AdServicesHttpsClient mAdServicesHttpsClient; + @Mock private ProtectedSignalsDao mProtectedSignalsDao; + @Captor ArgumentCaptor<DBEncoderLogicMetadata> mDBEncoderLogicArgumentCaptor; private ListeningExecutorService mExecutorService = MoreExecutors.newDirectExecutorService(); @@ -90,6 +92,7 @@ public class EncoderLogicHandlerTest { mEncoderPersistenceDao, mEncoderEndpointsDao, mEncoderLogicMetadataDao, + mProtectedSignalsDao, mAdServicesHttpsClient, mExecutorService); } @@ -273,6 +276,7 @@ public class EncoderLogicHandlerTest { verify(mEncoderLogicMetadataDao).deleteEncoder(buyer); verify(mEncoderPersistenceDao).deleteEncoder(buyer); verify(mEncoderEndpointsDao).deleteEncoderEndpoint(buyer); + verify(mProtectedSignalsDao).deleteSignalsUpdateMetadata(buyer); } @Test @@ -290,6 +294,9 @@ public class EncoderLogicHandlerTest { verify(mEncoderEndpointsDao).deleteEncoderEndpoint(buyer1); verify(mEncoderEndpointsDao).deleteEncoderEndpoint(buyer2); + + verify(mProtectedSignalsDao).deleteSignalsUpdateMetadata(buyer1); + verify(mProtectedSignalsDao).deleteSignalsUpdateMetadata(buyer2); } @SuppressWarnings("FutureReturnValueIgnored") diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/data/signals/ProtectedSignalsDaoTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/data/signals/ProtectedSignalsDaoTest.java index fbf954608..c7668d4d3 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/data/signals/ProtectedSignalsDaoTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/data/signals/ProtectedSignalsDaoTest.java @@ -20,7 +20,9 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -48,6 +50,7 @@ import org.mockito.Mock; import org.mockito.MockitoSession; import java.time.Duration; +import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -176,16 +179,27 @@ public class ProtectedSignalsDaoTest { public void testInsertAndDelete() { // Insert two signals mProtectedSignalsDao.insertAndDelete( + BUYER_1, + CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI, Arrays.asList(DBProtectedSignalFixture.SIGNAL, DBProtectedSignalFixture.SIGNAL), Collections.emptyList()); // Get all the signals for the test buyer List<DBProtectedSignal> readResult = mProtectedSignalsDao.getSignalsByBuyer(BUYER_1); + assertEquals( + CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI, + mProtectedSignalsDao.getSignalsUpdateMetadata(BUYER_1).getLastSignalsUpdatedTime()); // Delete one of the signals and insert two more mProtectedSignalsDao.insertAndDelete( + BUYER_1, + CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI, Arrays.asList(DBProtectedSignalFixture.SIGNAL, DBProtectedSignalFixture.SIGNAL), readResult.subList(0, 1)); // Check that the deletions and insertion occurred readResult = mProtectedSignalsDao.getSignalsByBuyer(BUYER_1); + assertEquals( + CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI, + mProtectedSignalsDao.getSignalsUpdateMetadata(BUYER_1).getLastSignalsUpdatedTime()); + assertEquals(3, readResult.size()); assertNotNull(readResult.get(0).getId()); assertEqualsExceptId(DBProtectedSignalFixture.SIGNAL, readResult.get(0)); @@ -197,17 +211,32 @@ public class ProtectedSignalsDaoTest { public void testDeleteSignalsBeforeTime() { // Insert two signals mProtectedSignalsDao.insertAndDelete( + BUYER_1, + CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI, Arrays.asList( DBProtectedSignalFixture.SIGNAL, DBProtectedSignalFixture.LATER_TIME_SIGNAL), Collections.emptyList()); + assertEquals( + DBSignalsUpdateMetadata.builder() + .setBuyer(BUYER_1) + .setLastSignalsUpdatedTime(CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI) + .build(), + mProtectedSignalsDao.getSignalsUpdateMetadata(BUYER_1)); // Delete the older signal assertEquals( 1, - mProtectedSignalsDao.deleteSignalsBeforeTime( + mProtectedSignalsDao.deleteExpiredSignalsAndUpdateSignalsUpdateMetadata( DBProtectedSignalFixture.SIGNAL .getCreationTime() - .plus(Duration.ofMillis(1)))); + .plus(Duration.ofMillis(1)), + CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI)); + assertEquals( + DBSignalsUpdateMetadata.builder() + .setBuyer(BUYER_1) + .setLastSignalsUpdatedTime(CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI) + .build(), + mProtectedSignalsDao.getSignalsUpdateMetadata(BUYER_1)); // Check that the deletions and insertion occurred List<DBProtectedSignal> readResult = mProtectedSignalsDao.getSignalsByBuyer(BUYER_1); assertEquals(1, readResult.size()); @@ -226,7 +255,10 @@ public class ProtectedSignalsDaoTest { DBProtectedSignal signal2 = DBProtectedSignalFixture.getBuilder().setBuyer(BUYER_2).build(); // Insert two signals mProtectedSignalsDao.insertAndDelete( - Arrays.asList(signal1, signal2), Collections.emptyList()); + BUYER_1, + CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI, + Arrays.asList(signal1, signal2), + Collections.emptyList()); when(mEnrollmentDaoMock.getAllFledgeEnrolledAdTechs()) .thenReturn(new HashSet<>(Arrays.asList(BUYER_1, BUYER_2))); assertEquals(0, mProtectedSignalsDao.deleteDisallowedBuyerSignals(mEnrollmentDaoMock)); @@ -235,10 +267,17 @@ public class ProtectedSignalsDaoTest { assertEquals(1, readResult.size()); assertNotNull(readResult.get(0).getId()); assertEqualsExceptId(signal1, readResult.get(0)); + assertEquals( + DBSignalsUpdateMetadata.builder() + .setBuyer(BUYER_1) + .setLastSignalsUpdatedTime(CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI) + .build(), + mProtectedSignalsDao.getSignalsUpdateMetadata(BUYER_1)); readResult = mProtectedSignalsDao.getSignalsByBuyer(BUYER_2); assertEquals(1, readResult.size()); assertNotNull(readResult.get(0).getId()); assertEqualsExceptId(signal2, readResult.get(0)); + assertNull(mProtectedSignalsDao.getSignalsUpdateMetadata(BUYER_2)); } @Test @@ -249,10 +288,37 @@ public class ProtectedSignalsDaoTest { DBProtectedSignalFixture.getBuilder().setBuyer(CommonFixture.VALID_BUYER_2).build(); // Insert two signals mProtectedSignalsDao.insertAndDelete( - Arrays.asList(signal1, signal2), Collections.emptyList()); + BUYER_1, + CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI, + List.of(signal1), + Collections.emptyList()); + mProtectedSignalsDao.insertAndDelete( + BUYER_2, + CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI, + List.of(signal2), + Collections.emptyList()); + assertEquals( + DBSignalsUpdateMetadata.builder() + .setBuyer(BUYER_1) + .setLastSignalsUpdatedTime(CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI) + .build(), + mProtectedSignalsDao.getSignalsUpdateMetadata(BUYER_1)); + assertEquals( + DBSignalsUpdateMetadata.builder() + .setBuyer(BUYER_2) + .setLastSignalsUpdatedTime(CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI) + .build(), + mProtectedSignalsDao.getSignalsUpdateMetadata(BUYER_2)); when(mEnrollmentDaoMock.getAllFledgeEnrolledAdTechs()) .thenReturn(Collections.singleton(CommonFixture.VALID_BUYER_1)); assertEquals(1, mProtectedSignalsDao.deleteDisallowedBuyerSignals(mEnrollmentDaoMock)); + assertEquals( + DBSignalsUpdateMetadata.builder() + .setBuyer(BUYER_1) + .setLastSignalsUpdatedTime(CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI) + .build(), + mProtectedSignalsDao.getSignalsUpdateMetadata(BUYER_1)); + assertNull(mProtectedSignalsDao.getSignalsUpdateMetadata(BUYER_2)); // Check that the correct deletion occurred List<DBProtectedSignal> readResult = mProtectedSignalsDao.getSignalsByBuyer(BUYER_1); assertEquals(1, readResult.size()); @@ -264,8 +330,10 @@ public class ProtectedSignalsDaoTest { public void testDeleteDisallowedPackageSignalsNoSignals() { assertEquals( 0, - mProtectedSignalsDao.deleteAllDisallowedPackageSignals( - mPackageManagerMock, mFlagsMock)); + mProtectedSignalsDao.deleteAllDisallowedPackageSignalsAndUpdateSignalUpdateMetadata( + mPackageManagerMock, + mFlagsMock, + CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI)); } @Test @@ -292,8 +360,10 @@ public class ProtectedSignalsDaoTest { assertEquals(2, readResult1.size()); assertEquals( 0, - mProtectedSignalsDao.deleteAllDisallowedPackageSignals( - mPackageManagerMock, new FlagsWithAllAppsAllowed())); + mProtectedSignalsDao.deleteAllDisallowedPackageSignalsAndUpdateSignalUpdateMetadata( + mPackageManagerMock, + new FlagsWithAllAppsAllowed(), + CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI)); // Check that no deletion occurred List<DBProtectedSignal> readResult2 = mProtectedSignalsDao.getSignalsByBuyer(BUYER_1); assertEquals(2, readResult2.size()); @@ -322,13 +392,107 @@ public class ProtectedSignalsDaoTest { assertEquals(2, readResult1.size()); assertEquals( 1, - mProtectedSignalsDao.deleteAllDisallowedPackageSignals( - CONTEXT.getPackageManager(), new FlagsThatAllowOneApp())); + mProtectedSignalsDao.deleteAllDisallowedPackageSignalsAndUpdateSignalUpdateMetadata( + CONTEXT.getPackageManager(), + new FlagsThatAllowOneApp(), + CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI)); List<DBProtectedSignal> readResult2 = mProtectedSignalsDao.getSignalsByBuyer(BUYER_1); assertEquals(1, readResult2.size()); assertEquals(PACKAGE_1, readResult2.get(0).getPackageName()); } + @Test + public void testPersistMetadata() { + assertNull( + "Initial state of the table should be empty", + mProtectedSignalsDao.getSignalsUpdateMetadata(BUYER_1)); + + DBSignalsUpdateMetadata signalsUpdateMetadata = + DBSignalsUpdateMetadata.builder() + .setBuyer(BUYER_1) + .setLastSignalsUpdatedTime(CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI) + .build(); + assertEquals( + "One entry should have been inserted", + 1, + mProtectedSignalsDao.persistSignalsUpdateMetadata(signalsUpdateMetadata)); + } + + @Test + public void testDeleteMetadata() { + assertNull( + "Initial state of the table should be empty", + mProtectedSignalsDao.getSignalsUpdateMetadata(BUYER_1)); + + DBSignalsUpdateMetadata signalsUpdateMetadata = + DBSignalsUpdateMetadata.builder() + .setBuyer(BUYER_1) + .setLastSignalsUpdatedTime(CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI) + .build(); + assertEquals( + "One entry should have been inserted", + 1, + mProtectedSignalsDao.persistSignalsUpdateMetadata(signalsUpdateMetadata)); + + mProtectedSignalsDao.deleteSignalsUpdateMetadata(BUYER_1); + assertNull( + "Metadata should have been deleted", + mProtectedSignalsDao.getSignalsUpdateMetadata(BUYER_1)); + } + + @Test + public void testQuerySignalsUpdateMetadata() { + assertNull( + "Initial state of the table should be empty", + mProtectedSignalsDao.getSignalsUpdateMetadata(BUYER_1)); + + DBSignalsUpdateMetadata signalsUpdateMetadata = + DBSignalsUpdateMetadata.builder() + .setBuyer(BUYER_1) + .setLastSignalsUpdatedTime(CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI) + .build(); + assertEquals( + "One entry should have been inserted", + 1, + mProtectedSignalsDao.persistSignalsUpdateMetadata(signalsUpdateMetadata)); + DBSignalsUpdateMetadata retrieved = mProtectedSignalsDao.getSignalsUpdateMetadata(BUYER_1); + + assertEquals(signalsUpdateMetadata.getBuyer(), retrieved.getBuyer()); + assertEquals( + signalsUpdateMetadata.getLastSignalsUpdatedTime(), + retrieved.getLastSignalsUpdatedTime()); + } + + @Test + public void testPersistMetadataReplacesExisting() { + assertNull( + "Initial state of the table should be empty", + mProtectedSignalsDao.getSignalsUpdateMetadata(BUYER_1)); + + DBSignalsUpdateMetadata.Builder anBuilder = + DBSignalsUpdateMetadata.builder().setBuyer(BUYER_1); + DBSignalsUpdateMetadata previous = + anBuilder + .setLastSignalsUpdatedTime(CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI) + .build(); + mProtectedSignalsDao.persistSignalsUpdateMetadata(previous); + + DBSignalsUpdateMetadata retrieved = mProtectedSignalsDao.getSignalsUpdateMetadata(BUYER_1); + assertEquals(previous.getLastSignalsUpdatedTime(), retrieved.getLastSignalsUpdatedTime()); + + DBSignalsUpdateMetadata updated = + anBuilder + .setLastSignalsUpdatedTime( + CommonFixture.FIXED_NEXT_ONE_DAY.truncatedTo(ChronoUnit.MILLIS)) + .build(); + + mProtectedSignalsDao.persistSignalsUpdateMetadata(updated); + retrieved = mProtectedSignalsDao.getSignalsUpdateMetadata(BUYER_1); + assertNotEquals( + previous.getLastSignalsUpdatedTime(), retrieved.getLastSignalsUpdatedTime()); + assertEquals(updated.getLastSignalsUpdatedTime(), retrieved.getLastSignalsUpdatedTime()); + } + private void assertEqualsExceptId(DBProtectedSignal expected, DBProtectedSignal actual) { assertEquals(expected.getBuyer(), actual.getBuyer()); assertArrayEquals(expected.getKey(), actual.getKey()); diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/data/signals/ProtectedSignalsDatabaseMigrationTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/data/signals/ProtectedSignalsDatabaseMigrationTest.java index 2c668cca7..82fa411ef 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/data/signals/ProtectedSignalsDatabaseMigrationTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/data/signals/ProtectedSignalsDatabaseMigrationTest.java @@ -57,7 +57,7 @@ public class ProtectedSignalsDatabaseMigrationTest { assertFalse(tables.contains(DBEncodedPayload.TABLE_NAME)); assertFalse(tables.contains(DBEncoderEndpoint.TABLE_NAME)); } - // Re-open the database with version 3. + // Re-open the database with version 2. try (SupportSQLiteDatabase db = helper.runMigrationsAndValidate(TEST_DB, 2, true)) { List<String> tables = listTables(db); assertTrue(tables.contains(DBProtectedSignal.TABLE_NAME)); @@ -119,4 +119,25 @@ public class ProtectedSignalsDatabaseMigrationTest { CommonFixture.VALID_BUYER_2.toString(), c.getString(c.getColumnIndex("buyer"))); } } + + @Test + public void testMigration3To4() throws IOException { + try (SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 3)) { + List<String> tables = listTables(db); + assertTrue(tables.contains(DBProtectedSignal.TABLE_NAME)); + assertTrue(tables.contains(DBEncoderLogicMetadata.TABLE_NAME)); + assertTrue(tables.contains(DBEncodedPayload.TABLE_NAME)); + assertTrue(tables.contains(DBEncoderEndpoint.TABLE_NAME)); + assertFalse(tables.contains(DBSignalsUpdateMetadata.TABLE_NAME)); + } + // Re-open the database with version 4. + try (SupportSQLiteDatabase db = helper.runMigrationsAndValidate(TEST_DB, 4, true)) { + List<String> tables = listTables(db); + assertTrue(tables.contains(DBProtectedSignal.TABLE_NAME)); + assertTrue(tables.contains(DBEncoderLogicMetadata.TABLE_NAME)); + assertTrue(tables.contains(DBEncodedPayload.TABLE_NAME)); + assertTrue(tables.contains(DBEncoderEndpoint.TABLE_NAME)); + assertTrue(tables.contains(DBSignalsUpdateMetadata.TABLE_NAME)); + } + } } diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/PhFlagsTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/PhFlagsTest.java index c5e9716b3..f86269555 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/PhFlagsTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/PhFlagsTest.java @@ -50,17 +50,19 @@ import static com.android.adservices.service.Flags.DEFAULT_ADSERVICES_CONSENT_MI import static com.android.adservices.service.Flags.DEFAULT_ADSERVICES_ENABLEMENT_CHECK_ENABLED; import static com.android.adservices.service.Flags.DEFAULT_ADSERVICES_VERSION_MAPPINGS; import static com.android.adservices.service.Flags.DEFAULT_AD_ID_FETCHER_TIMEOUT_MS; +import static com.android.adservices.service.Flags.DEFAULT_APPSEARCH_READ_TIMEOUT_MS; +import static com.android.adservices.service.Flags.DEFAULT_APPSEARCH_WRITE_TIMEOUT_MS; import static com.android.adservices.service.Flags.DEFAULT_AUCTION_SERVER_AD_ID_FETCHER_TIMEOUT_MS; import static com.android.adservices.service.Flags.DEFAULT_BACKGROUND_JOB_SAMPLING_LOGGING_RATE; import static com.android.adservices.service.Flags.DEFAULT_BLOCKED_TOPICS_SOURCE_OF_TRUTH; import static com.android.adservices.service.Flags.DEFAULT_CLASSIFIER_TYPE; import static com.android.adservices.service.Flags.DEFAULT_COMPUTE_VERSION_FROM_MAPPINGS_ENABLED; +import static com.android.adservices.service.Flags.DEFAULT_CONSENT_MANAGER_OTA_DEBUG_MODE; import static com.android.adservices.service.Flags.DEFAULT_CONSENT_SOURCE_OF_TRUTH; import static com.android.adservices.service.Flags.DEFAULT_ENABLE_ADEXT_DATA_SERVICE_APIS; import static com.android.adservices.service.Flags.DEFAULT_ENABLE_ADEXT_SERVICE_DEBUG_PROXY; import static com.android.adservices.service.Flags.DEFAULT_ENABLE_ADSERVICES_API_ENABLED; import static com.android.adservices.service.Flags.DEFAULT_ENABLE_AD_SERVICES_SYSTEM_API; -import static com.android.adservices.service.Flags.DEFAULT_EU_NOTIF_FLOW_CHANGE_ENABLED; import static com.android.adservices.service.Flags.DEFAULT_MAINLINE_TRAIN_VERSION; import static com.android.adservices.service.Flags.DEFAULT_MEASUREMENT_ASYNC_REGISTRATION_JOB_TRIGGER_MAX_DELAY_MS; import static com.android.adservices.service.Flags.DEFAULT_MEASUREMENT_ASYNC_REGISTRATION_JOB_TRIGGER_MIN_DELAY_MS; @@ -68,7 +70,6 @@ import static com.android.adservices.service.Flags.DEFAULT_MEASUREMENT_ATTRIBUTI import static com.android.adservices.service.Flags.DEFAULT_MEASUREMENT_DEBUG_JOIN_KEY_ENROLLMENT_ALLOWLIST; import static com.android.adservices.service.Flags.DEFAULT_MEASUREMENT_DEBUG_JOIN_KEY_HASH_LIMIT; import static com.android.adservices.service.Flags.DEFAULT_MEASUREMENT_ENABLE_COARSE_EVENT_REPORT_DESTINATIONS; -import static com.android.adservices.service.Flags.DEFAULT_MEASUREMENT_ENABLE_VTC_CONFIGURABLE_MAX_EVENT_REPORTS; import static com.android.adservices.service.Flags.DEFAULT_MEASUREMENT_MAX_AGGREGATE_DEDUPLICATION_KEYS_PER_REGISTRATION; import static com.android.adservices.service.Flags.DEFAULT_MEASUREMENT_MAX_AGGREGATE_REPORT_UPLOAD_RETRY_WINDOW_MS; import static com.android.adservices.service.Flags.DEFAULT_MEASUREMENT_MAX_ATTRIBUTIONS_PER_INVOCATION; @@ -83,9 +84,8 @@ import static com.android.adservices.service.Flags.DEFAULT_MEASUREMENT_PLATFORM_ import static com.android.adservices.service.Flags.DEFAULT_MEASUREMENT_PLATFORM_DEBUG_AD_ID_MATCHING_LIMIT; import static com.android.adservices.service.Flags.DEFAULT_MEASUREMENT_VTC_CONFIGURABLE_MAX_EVENT_REPORTS_COUNT; import static com.android.adservices.service.Flags.DEFAULT_NOTIFICATION_DISMISSED_ON_CLICK; -import static com.android.adservices.service.Flags.DEFAULT_RVC_NOTIFICATION_ENABLED; +import static com.android.adservices.service.Flags.DEFAULT_RVC_POST_OTA_NOTIFICATION_ENABLED; import static com.android.adservices.service.Flags.DEFAULT_RVC_POST_OTA_NOTIF_AGE_CHECK; -import static com.android.adservices.service.Flags.DEFAULT_CONSENT_MANAGER_OTA_DEBUG_MODE; import static com.android.adservices.service.Flags.DEFAULT_U18_UX_ENABLED; import static com.android.adservices.service.Flags.DISABLE_FLEDGE_ENROLLMENT_CHECK; import static com.android.adservices.service.Flags.DISABLE_MEASUREMENT_ENROLLMENT_CHECK; @@ -283,15 +283,12 @@ import static com.android.adservices.service.Flags.MEASUREMENT_DELETE_EXPIRED_JO import static com.android.adservices.service.Flags.MEASUREMENT_DELETE_UNINSTALLED_JOB_PERIOD_MS; import static com.android.adservices.service.Flags.MEASUREMENT_DELETE_UNINSTALLED_JOB_PERSISTED; import static com.android.adservices.service.Flags.MEASUREMENT_DESTINATION_RATE_LIMIT_WINDOW; -import static com.android.adservices.service.Flags.MEASUREMENT_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY; -import static com.android.adservices.service.Flags.MEASUREMENT_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY; import static com.android.adservices.service.Flags.MEASUREMENT_ENABLE_AGGREGATABLE_REPORT_PAYLOAD_PADDING; import static com.android.adservices.service.Flags.MEASUREMENT_ENABLE_API_STATUS_ALLOW_LIST_CHECK; import static com.android.adservices.service.Flags.MEASUREMENT_ENABLE_APP_PACKAGE_NAME_LOGGING; import static com.android.adservices.service.Flags.MEASUREMENT_ENABLE_ARA_DEDUPLICATION_ALIGNMENT_V1; import static com.android.adservices.service.Flags.MEASUREMENT_ENABLE_ARA_PARSING_ALIGNMENT_V1; import static com.android.adservices.service.Flags.MEASUREMENT_ENABLE_CONFIGURABLE_AGGREGATE_REPORT_DELAY; -import static com.android.adservices.service.Flags.MEASUREMENT_ENABLE_CONFIGURABLE_EVENT_REPORTING_WINDOWS; import static com.android.adservices.service.Flags.MEASUREMENT_ENABLE_DATASTORE_MANAGER_THROW_DATASTORE_EXCEPTION; import static com.android.adservices.service.Flags.MEASUREMENT_ENABLE_DEBUG_REPORT; import static com.android.adservices.service.Flags.MEASUREMENT_ENABLE_DELETE_REPORTS_ON_UNRECOVERABLE_EXCEPTION; @@ -299,6 +296,7 @@ import static com.android.adservices.service.Flags.MEASUREMENT_ENABLE_DESTINATIO import static com.android.adservices.service.Flags.MEASUREMENT_ENABLE_LOOKBACK_WINDOW_FILTER; import static com.android.adservices.service.Flags.MEASUREMENT_ENABLE_MAX_AGGREGATE_REPORTS_PER_SOURCE; import static com.android.adservices.service.Flags.MEASUREMENT_ENABLE_PREINSTALL_CHECK; +import static com.android.adservices.service.Flags.MEASUREMENT_ENABLE_REDIRECT_TO_WELL_KNOWN_PATH; import static com.android.adservices.service.Flags.MEASUREMENT_ENABLE_REPORTING_JOBS_THROW_JSON_EXCEPTION; import static com.android.adservices.service.Flags.MEASUREMENT_ENABLE_REPORTING_JOBS_THROW_UNACCOUNTED_EXCEPTION; import static com.android.adservices.service.Flags.MEASUREMENT_ENABLE_SCOPED_ATTRIBUTION_RATE_LIMIT; @@ -323,7 +321,6 @@ import static com.android.adservices.service.Flags.MEASUREMENT_EVENT_FALLBACK_RE import static com.android.adservices.service.Flags.MEASUREMENT_EVENT_FALLBACK_REPORTING_JOB_REQUIRED_BATTERY_NOT_LOW; import static com.android.adservices.service.Flags.MEASUREMENT_EVENT_FALLBACK_REPORTING_JOB_REQUIRED_NETWORK_TYPE; import static com.android.adservices.service.Flags.MEASUREMENT_EVENT_MAIN_REPORTING_JOB_PERIOD_MS; -import static com.android.adservices.service.Flags.MEASUREMENT_EVENT_NOISE_PROBABILITY; import static com.android.adservices.service.Flags.MEASUREMENT_EVENT_REPORTING_JOB_PERSISTED; import static com.android.adservices.service.Flags.MEASUREMENT_EVENT_REPORTING_JOB_REQUIRED_BATTERY_NOT_LOW; import static com.android.adservices.service.Flags.MEASUREMENT_EVENT_REPORTING_JOB_REQUIRED_NETWORK_TYPE; @@ -338,10 +335,6 @@ import static com.android.adservices.service.Flags.MEASUREMENT_FLEX_API_MAX_INFO import static com.android.adservices.service.Flags.MEASUREMENT_FLEX_API_MAX_INFORMATION_GAIN_NAVIGATION; import static com.android.adservices.service.Flags.MEASUREMENT_FLEX_API_MAX_TRIGGER_DATA_CARDINALITY; import static com.android.adservices.service.Flags.MEASUREMENT_FLEX_LITE_API_ENABLED; -import static com.android.adservices.service.Flags.MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY; -import static com.android.adservices.service.Flags.MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY; -import static com.android.adservices.service.Flags.MEASUREMENT_INSTALL_ATTR_EVENT_NOISE_PROBABILITY; -import static com.android.adservices.service.Flags.MEASUREMENT_INSTALL_ATTR_NAVIGATION_NOISE_PROBABILITY; import static com.android.adservices.service.Flags.MEASUREMENT_IS_CLICK_VERIFICATION_ENABLED; import static com.android.adservices.service.Flags.MEASUREMENT_IS_CLICK_VERIFIED_BY_INPUT_EVENT; import static com.android.adservices.service.Flags.MEASUREMENT_JOB_AGGREGATE_FALLBACK_REPORTING_KILL_SWITCH; @@ -386,7 +379,6 @@ import static com.android.adservices.service.Flags.MEASUREMENT_MIN_INSTALL_ATTRI import static com.android.adservices.service.Flags.MEASUREMENT_MIN_POST_INSTALL_EXCLUSIVITY_WINDOW; import static com.android.adservices.service.Flags.MEASUREMENT_MIN_REPORTING_ORIGIN_UPDATE_WINDOW; import static com.android.adservices.service.Flags.MEASUREMENT_MIN_REPORTING_REGISTER_SOURCE_EXPIRATION_IN_SECONDS; -import static com.android.adservices.service.Flags.MEASUREMENT_NAVIGATION_NOISE_PROBABILITY; import static com.android.adservices.service.Flags.MEASUREMENT_NETWORK_CONNECT_TIMEOUT_MS; import static com.android.adservices.service.Flags.MEASUREMENT_NETWORK_READ_TIMEOUT_MS; import static com.android.adservices.service.Flags.MEASUREMENT_RATE_LIMIT_WINDOW_MILLISECONDS; @@ -442,6 +434,7 @@ import static com.android.adservices.service.Flags.TOPICS_NUMBER_OF_TOP_TOPICS; import static com.android.adservices.service.Flags.TOPICS_ON_DEVICE_CLASSIFIER_KILL_SWITCH; import static com.android.adservices.service.Flags.TOPICS_PERCENTAGE_FOR_RANDOM_TOPIC; import static com.android.adservices.service.Flags.TOPICS_PRIVACY_BUDGET_FOR_TOPIC_ID_DISTRIBUTION; +import static com.android.adservices.service.Flags.TOPICS_TEST_ENCRYPTION_PUBLIC_KEY; import static com.android.adservices.service.Flags.UI_DIALOG_FRAGMENT; import static com.android.adservices.service.Flags.UI_EEA_COUNTRIES; import static com.android.adservices.service.Flags.UI_FEATURE_TYPE_LOGGING_ENABLED; @@ -474,8 +467,8 @@ import static com.android.adservices.service.FlagsConstants.KEY_COBALT_LOGGING_E import static com.android.adservices.service.FlagsConstants.KEY_COBALT_LOGGING_JOB_PERIOD_MS; import static com.android.adservices.service.FlagsConstants.KEY_COBALT_UPLOAD_SERVICE_UNBIND_DELAY_MS; import static com.android.adservices.service.FlagsConstants.KEY_COMPAT_LOGGING_KILL_SWITCH; -import static com.android.adservices.service.FlagsConstants.KEY_CONSENT_MANAGER_OTA_DEBUG_MODE; import static com.android.adservices.service.FlagsConstants.KEY_CONSENT_ALREADY_INTERACTED_FIX_ENABLE; +import static com.android.adservices.service.FlagsConstants.KEY_CONSENT_MANAGER_OTA_DEBUG_MODE; import static com.android.adservices.service.FlagsConstants.KEY_CONSENT_NOTIFICATION_ACTIVITY_DEBUG_MODE; import static com.android.adservices.service.FlagsConstants.KEY_CONSENT_NOTIFICATION_RESET_TOKEN; import static com.android.adservices.service.FlagsConstants.KEY_CONSENT_NOTIFIED_DEBUG_MODE; @@ -513,7 +506,6 @@ import static com.android.adservices.service.FlagsConstants.KEY_ENROLLMENT_BLOCK import static com.android.adservices.service.FlagsConstants.KEY_ENROLLMENT_ENABLE_LIMITED_LOGGING; import static com.android.adservices.service.FlagsConstants.KEY_ENROLLMENT_MDD_RECORD_DELETION_ENABLED; import static com.android.adservices.service.FlagsConstants.KEY_ERROR_CODE_LOGGING_DENY_LIST; -import static com.android.adservices.service.FlagsConstants.KEY_EU_NOTIF_FLOW_CHANGE_ENABLED; import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_AD_COUNTER_HISTOGRAM_ABSOLUTE_MAX_PER_BUYER_EVENT_COUNT; import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_AD_COUNTER_HISTOGRAM_ABSOLUTE_MAX_TOTAL_EVENT_COUNT; import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_AD_COUNTER_HISTOGRAM_LOWER_MAX_PER_BUYER_EVENT_COUNT; @@ -676,8 +668,6 @@ import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_DELE import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_DELETE_UNINSTALLED_JOB_PERIOD_MS; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_DELETE_UNINSTALLED_JOB_PERSISTED; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_DESTINATION_RATE_LIMIT_WINDOW; -import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY; -import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENABLE_AGGREGATABLE_REPORT_PAYLOAD_PADDING; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENABLE_API_STATUS_ALLOW_LIST_CHECK; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENABLE_APP_PACKAGE_NAME_LOGGING; @@ -685,7 +675,6 @@ import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENAB import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENABLE_ARA_PARSING_ALIGNMENT_V1; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENABLE_COARSE_EVENT_REPORT_DESTINATIONS; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENABLE_CONFIGURABLE_AGGREGATE_REPORT_DELAY; -import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENABLE_CONFIGURABLE_EVENT_REPORTING_WINDOWS; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENABLE_DATASTORE_MANAGER_THROW_DATASTORE_EXCEPTION; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENABLE_DEBUG_REPORT; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENABLE_DELETE_REPORTS_ON_UNRECOVERABLE_EXCEPTION; @@ -693,6 +682,7 @@ import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENAB import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENABLE_LOOKBACK_WINDOW_FILTER; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENABLE_MAX_AGGREGATE_REPORTS_PER_SOURCE; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENABLE_PREINSTALL_CHECK; +import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENABLE_REDIRECT_TO_WELL_KNOWN_PATH; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENABLE_REPORTING_JOBS_THROW_CRYPTO_EXCEPTION; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENABLE_REPORTING_JOBS_THROW_JSON_EXCEPTION; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENABLE_REPORTING_JOBS_THROW_UNACCOUNTED_EXCEPTION; @@ -703,7 +693,6 @@ import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENAB import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENABLE_SOURCE_DEBUG_REPORT; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENABLE_TRIGGER_DATA_MATCHING; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENABLE_TRIGGER_DEBUG_REPORT; -import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENABLE_VTC_CONFIGURABLE_MAX_EVENT_REPORTS; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENABLE_XNA; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENFORCE_ENROLLMENT_ORIGIN_MATCH; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_ENFORCE_FOREGROUND_STATUS_DELETE_REGISTRATIONS; @@ -718,7 +707,6 @@ import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_EVEN import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_EVENT_FALLBACK_REPORTING_JOB_REQUIRED_BATTERY_NOT_LOW; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_EVENT_FALLBACK_REPORTING_JOB_REQUIRED_NETWORK_TYPE; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_EVENT_MAIN_REPORTING_JOB_PERIOD_MS; -import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_EVENT_NOISE_PROBABILITY; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_EVENT_REPORTING_JOB_PERSISTED; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_EVENT_REPORTING_JOB_REQUIRED_BATTERY_NOT_LOW; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_EVENT_REPORTING_JOB_REQUIRED_NETWORK_TYPE; @@ -733,10 +721,6 @@ import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_FLEX import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_FLEX_API_MAX_INFORMATION_GAIN_NAVIGATION; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_FLEX_API_MAX_TRIGGER_DATA_CARDINALITY; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_FLEX_LITE_API_ENABLED; -import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY; -import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY; -import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_INSTALL_ATTR_EVENT_NOISE_PROBABILITY; -import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_INSTALL_ATTR_NAVIGATION_NOISE_PROBABILITY; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_IS_CLICK_VERIFICATION_ENABLED; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_IS_CLICK_VERIFIED_BY_INPUT_EVENT; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_JOB_AGGREGATE_FALLBACK_REPORTING_KILL_SWITCH; @@ -791,7 +775,6 @@ import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_MIN_ import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_MIN_POST_INSTALL_EXCLUSIVITY_WINDOW; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_MIN_REPORTING_ORIGIN_UPDATE_WINDOW; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_MIN_REPORTING_REGISTER_SOURCE_EXPIRATION_IN_SECONDS; -import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_NAVIGATION_NOISE_PROBABILITY; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_NETWORK_CONNECT_TIMEOUT_MS; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_NETWORK_READ_TIMEOUT_MS; import static com.android.adservices.service.FlagsConstants.KEY_MEASUREMENT_RATE_LIMIT_WINDOW_MILLISECONDS; @@ -828,7 +811,7 @@ import static com.android.adservices.service.FlagsConstants.KEY_PROTECTED_SIGNAL import static com.android.adservices.service.FlagsConstants.KEY_PROTECTED_SIGNALS_PERIODIC_ENCODING_JOB_PERIOD_MS; import static com.android.adservices.service.FlagsConstants.KEY_PROTECTED_SIGNALS_SERVICE_KILL_SWITCH; import static com.android.adservices.service.FlagsConstants.KEY_RECORD_MANUAL_INTERACTION_ENABLED; -import static com.android.adservices.service.FlagsConstants.KEY_RVC_NOTIFICATION_ENABLED; +import static com.android.adservices.service.FlagsConstants.KEY_RVC_POST_OTA_NOTIFICATION_ENABLED; import static com.android.adservices.service.FlagsConstants.KEY_RVC_POST_OTA_NOTIF_AGE_CHECK; import static com.android.adservices.service.FlagsConstants.KEY_RVC_UX_ENABLED; import static com.android.adservices.service.FlagsConstants.KEY_SDK_REQUEST_PERMITS_PER_SECOND; @@ -847,6 +830,7 @@ import static com.android.adservices.service.FlagsConstants.KEY_TOPICS_NUMBER_OF import static com.android.adservices.service.FlagsConstants.KEY_TOPICS_ON_DEVICE_CLASSIFIER_KILL_SWITCH; import static com.android.adservices.service.FlagsConstants.KEY_TOPICS_PERCENTAGE_FOR_RANDOM_TOPIC; import static com.android.adservices.service.FlagsConstants.KEY_TOPICS_PRIVACY_BUDGET_FOR_TOPIC_ID_DISTRIBUTION; +import static com.android.adservices.service.FlagsConstants.KEY_TOPICS_TEST_ENCRYPTION_PUBLIC_KEY; import static com.android.adservices.service.FlagsConstants.KEY_U18_UX_ENABLED; import static com.android.adservices.service.FlagsConstants.KEY_UI_DIALOG_FRAGMENT_ENABLED; import static com.android.adservices.service.FlagsConstants.KEY_UI_EEA_COUNTRIES; @@ -854,7 +838,6 @@ import static com.android.adservices.service.FlagsConstants.KEY_UI_FEATURE_TYPE_ import static com.android.adservices.service.FlagsConstants.KEY_UI_OTA_STRINGS_MANIFEST_FILE_URL; import static com.android.adservices.service.FlagsConstants.KEY_UI_TOGGLE_SPEED_BUMP_ENABLED; - import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; @@ -1125,6 +1108,23 @@ public class PhFlagsTest { } @Test + public void testGetTopicsTestEncryptionPublicKey() { + assertThat(mPhFlags.getTopicsTestEncryptionPublicKey()) + .isEqualTo(TOPICS_TEST_ENCRYPTION_PUBLIC_KEY); + assertThat(mPhFlags.getTopicsTestEncryptionPublicKey()).isEmpty(); + + // Now overriding with the value from PH. + String phOverridingValue = "NewKey"; + DeviceConfig.setProperty( + DeviceConfig.NAMESPACE_ADSERVICES, + KEY_TOPICS_TEST_ENCRYPTION_PUBLIC_KEY, + phOverridingValue, + /* makeDefault */ false); + + assertThat(mPhFlags.getTopicsTestEncryptionPublicKey()).isEqualTo(phOverridingValue); + } + + @Test public void testClassifierType() { // Without any overriding, the value is the hard coded constant. assertThat(mPhFlags.getClassifierType()).isEqualTo(DEFAULT_CLASSIFIER_TYPE); @@ -5284,24 +5284,6 @@ public class PhFlagsTest { } @Test - public void testGetMeasurementEnableConfigurableEventReportingWindows() { - // Without any overriding, the value is the hard coded constant. - assertThat(mPhFlags.getMeasurementEnableConfigurableEventReportingWindows()) - .isEqualTo(MEASUREMENT_ENABLE_CONFIGURABLE_EVENT_REPORTING_WINDOWS); - - boolean phOverridingValue = !MEASUREMENT_ENABLE_CONFIGURABLE_EVENT_REPORTING_WINDOWS; - - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_ADSERVICES, - KEY_MEASUREMENT_ENABLE_CONFIGURABLE_EVENT_REPORTING_WINDOWS, - Boolean.toString(phOverridingValue), - /* makeDefault */ false); - - assertThat(mPhFlags.getMeasurementEnableConfigurableEventReportingWindows()) - .isEqualTo(phOverridingValue); - } - - @Test public void testGetMeasurementEventReportsVtcEarlyReportingWindows() { // Without any overriding, the value is the hard coded constant. assertThat(mPhFlags.getMeasurementEventReportsVtcEarlyReportingWindows()) @@ -7351,22 +7333,6 @@ public class PhFlagsTest { } @Test - public void testEuNotifFlowChangeEnabled() { - // Without any overriding, the value is the hard coded constant. - assertThat(mPhFlags.getEuNotifFlowChangeEnabled()) - .isEqualTo(DEFAULT_EU_NOTIF_FLOW_CHANGE_ENABLED); - - boolean phOverridingValue = !DEFAULT_EU_NOTIF_FLOW_CHANGE_ENABLED; - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_ADSERVICES, - KEY_EU_NOTIF_FLOW_CHANGE_ENABLED, - Boolean.toString(phOverridingValue), - /* makeDefault */ false); - - assertThat(mPhFlags.getEuNotifFlowChangeEnabled()).isEqualTo(phOverridingValue); - } - - @Test public void testGetRecordManualInteractionEnabled() { // Without any overriding, the value is the hard coded constant. assertThat(mPhFlags.getRecordManualInteractionEnabled()) @@ -7677,7 +7643,8 @@ public class PhFlagsTest { boolean adServicesSystemApi, boolean phOverridingValue, boolean expected) { // Without any overriding, the value is the hard coded constant. - assertThat(mPhFlags.getEnableRvcNotification()).isEqualTo(DEFAULT_RVC_NOTIFICATION_ENABLED); + assertThat(mPhFlags.getEnableRvcPostOtaNotification()) + .isEqualTo(DEFAULT_RVC_POST_OTA_NOTIFICATION_ENABLED); DeviceConfig.setProperty( DeviceConfig.NAMESPACE_ADSERVICES, @@ -7686,11 +7653,11 @@ public class PhFlagsTest { /* makeDefault */ false); DeviceConfig.setProperty( DeviceConfig.NAMESPACE_ADSERVICES, - KEY_RVC_NOTIFICATION_ENABLED, + KEY_RVC_POST_OTA_NOTIFICATION_ENABLED, Boolean.toString(phOverridingValue), /* makeDefault */ false); - assertThat(mPhFlags.getEnableRvcNotification()).isEqualTo(expected); + assertThat(mPhFlags.getEnableRvcPostOtaNotification()).isEqualTo(expected); } @Test @@ -7919,144 +7886,6 @@ public class PhFlagsTest { } @Test - public void testGetMeasurementInstallAttrDualDestinationEventNoiseProbability() { - assertThat(mPhFlags.getMeasurementInstallAttrDualDestinationEventNoiseProbability()) - .isEqualTo(MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY); - - // Now overriding with the value from PH. - float phOverridingValue = - MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY + .0001f; - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_ADSERVICES, - KEY_MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY, - Float.toString(phOverridingValue), - /* makeDefault */ false); - - assertThat(mPhFlags.getMeasurementInstallAttrDualDestinationEventNoiseProbability()) - .isEqualTo(phOverridingValue); - } - - @Test - public void testGetMeasurementDualDestinationNavigationNoiseProbability() { - assertThat(mPhFlags.getMeasurementDualDestinationNavigationNoiseProbability()) - .isEqualTo(MEASUREMENT_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY); - - // Now overriding with the value from PH. - float phOverridingValue = - MEASUREMENT_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY + .0001f; - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_ADSERVICES, - KEY_MEASUREMENT_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY, - Float.toString(phOverridingValue), - /* makeDefault */ false); - - assertThat(mPhFlags.getMeasurementDualDestinationNavigationNoiseProbability()) - .isEqualTo(phOverridingValue); - } - - @Test - public void testGetMeasurementInstallAttrDualDestinationNavigationNoiseProbability() { - assertThat(mPhFlags.getMeasurementInstallAttrDualDestinationNavigationNoiseProbability()) - .isEqualTo(MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY); - - // Now overriding with the value from PH. - float phOverridingValue = - MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY + .0001f; - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_ADSERVICES, - KEY_MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY, - Float.toString(phOverridingValue), - /* makeDefault */ false); - - assertThat(mPhFlags.getMeasurementInstallAttrDualDestinationNavigationNoiseProbability()) - .isEqualTo(phOverridingValue); - } - - @Test - public void testGetMeasurementDualDestinationEventNoiseProbability() { - assertThat(mPhFlags.getMeasurementDualDestinationEventNoiseProbability()) - .isEqualTo(MEASUREMENT_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY); - - // Now overriding with the value from PH. - float phOverridingValue = MEASUREMENT_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY + .0001f; - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_ADSERVICES, - KEY_MEASUREMENT_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY, - Float.toString(phOverridingValue), - /* makeDefault */ false); - - assertThat(mPhFlags.getMeasurementDualDestinationEventNoiseProbability()) - .isEqualTo(phOverridingValue); - } - - @Test - public void testGetMeasurementInstallAttrEventNoiseProbability() { - assertThat(mPhFlags.getMeasurementInstallAttrEventNoiseProbability()) - .isEqualTo(MEASUREMENT_INSTALL_ATTR_EVENT_NOISE_PROBABILITY); - - // Now overriding with the value from PH. - float phOverridingValue = MEASUREMENT_INSTALL_ATTR_EVENT_NOISE_PROBABILITY + .0001f; - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_ADSERVICES, - KEY_MEASUREMENT_INSTALL_ATTR_EVENT_NOISE_PROBABILITY, - Float.toString(phOverridingValue), - /* makeDefault */ false); - - assertThat(mPhFlags.getMeasurementInstallAttrEventNoiseProbability()) - .isEqualTo(phOverridingValue); - } - - @Test - public void testGetMeasurementInstallAttrNavigationNoiseProbability() { - assertThat(mPhFlags.getMeasurementInstallAttrNavigationNoiseProbability()) - .isEqualTo(MEASUREMENT_INSTALL_ATTR_NAVIGATION_NOISE_PROBABILITY); - - // Now overriding with the value from PH. - float phOverridingValue = MEASUREMENT_INSTALL_ATTR_NAVIGATION_NOISE_PROBABILITY + .0001f; - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_ADSERVICES, - KEY_MEASUREMENT_INSTALL_ATTR_NAVIGATION_NOISE_PROBABILITY, - Float.toString(phOverridingValue), - /* makeDefault */ false); - - assertThat(mPhFlags.getMeasurementInstallAttrNavigationNoiseProbability()) - .isEqualTo(phOverridingValue); - } - - @Test - public void testGetMeasurementEventNoiseProbability() { - assertThat(mPhFlags.getMeasurementEventNoiseProbability()) - .isEqualTo(MEASUREMENT_EVENT_NOISE_PROBABILITY); - - // Now overriding with the value from PH. - float phOverridingValue = MEASUREMENT_EVENT_NOISE_PROBABILITY + .0001f; - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_ADSERVICES, - KEY_MEASUREMENT_EVENT_NOISE_PROBABILITY, - Float.toString(phOverridingValue), - /* makeDefault */ false); - - assertThat(mPhFlags.getMeasurementEventNoiseProbability()).isEqualTo(phOverridingValue); - } - - @Test - public void testGetMeasurementNavigationNoiseProbability() { - assertThat(mPhFlags.getMeasurementNavigationNoiseProbability()) - .isEqualTo(MEASUREMENT_NAVIGATION_NOISE_PROBABILITY); - - // Now overriding with the value from PH. - float phOverridingValue = MEASUREMENT_NAVIGATION_NOISE_PROBABILITY + .0001f; - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_ADSERVICES, - KEY_MEASUREMENT_NAVIGATION_NOISE_PROBABILITY, - Float.toString(phOverridingValue), - /* makeDefault */ false); - - assertThat(mPhFlags.getMeasurementNavigationNoiseProbability()) - .isEqualTo(phOverridingValue); - } - - @Test public void testGetMeasurementEnablePreinstallCheck() { // The priority of applying the flag values: PH (DeviceConfig) and then hard-coded value. assertThat(mPhFlags.getMeasurementEnablePreinstallCheck()) @@ -8182,24 +8011,6 @@ public class PhFlagsTest { } @Test - public void testGetMeasurementEnableVtcConfigurableMaxEventReports() { - // Without any overriding, the value is the hard coded constant. - assertThat(mPhFlags.getMeasurementEnableVtcConfigurableMaxEventReports()) - .isEqualTo(DEFAULT_MEASUREMENT_ENABLE_VTC_CONFIGURABLE_MAX_EVENT_REPORTS); - - boolean phOverridingValue = !DEFAULT_MEASUREMENT_ENABLE_VTC_CONFIGURABLE_MAX_EVENT_REPORTS; - - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_ADSERVICES, - KEY_MEASUREMENT_ENABLE_VTC_CONFIGURABLE_MAX_EVENT_REPORTS, - Boolean.toString(phOverridingValue), - /* makeDefault */ false); - - assertThat(mPhFlags.getMeasurementEnableVtcConfigurableMaxEventReports()) - .isEqualTo(phOverridingValue); - } - - @Test public void testGetMeasurementVtcConfigurableMaxEventReportsCount() { // Without any overriding, the value is the hard coded constant. assertThat(mPhFlags.getMeasurementVtcConfigurableMaxEventReportsCount()) @@ -9710,6 +9521,22 @@ public class PhFlagsTest { } @Test + public void testGetMeasurementEnableRedirectToWellKnownPath() { + assertThat(mPhFlags.getMeasurementEnableRedirectToWellKnownPath()) + .isEqualTo(MEASUREMENT_ENABLE_REDIRECT_TO_WELL_KNOWN_PATH); + + boolean phOverridingValue = !MEASUREMENT_ENABLE_REDIRECT_TO_WELL_KNOWN_PATH; + DeviceConfig.setProperty( + DeviceConfig.NAMESPACE_ADSERVICES, + KEY_MEASUREMENT_ENABLE_REDIRECT_TO_WELL_KNOWN_PATH, + Boolean.toString(phOverridingValue), + /* makeDefault */ false); + + assertThat(mPhFlags.getMeasurementEnableRedirectToWellKnownPath()) + .isEqualTo(phOverridingValue); + } + + @Test public void testGetMeasurementEnableMaxAggregateReportsPerSource() { assertThat(mPhFlags.getMeasurementEnableMaxAggregateReportsPerSource()) .isEqualTo(MEASUREMENT_ENABLE_MAX_AGGREGATE_REPORTS_PER_SOURCE); @@ -10140,6 +9967,37 @@ public class PhFlagsTest { assertThrows(IllegalArgumentException.class, mPhFlags::getBackgroundJobSamplingLoggingRate); } + @Test + public void testGetAppSearchWriteTimeout() { + // Without any overriding, the value is the hard coded constant. + assertThat(mPhFlags.getAppSearchWriteTimeout()) + .isEqualTo(DEFAULT_APPSEARCH_WRITE_TIMEOUT_MS); + + int phOverridingValue = DEFAULT_APPSEARCH_WRITE_TIMEOUT_MS + 1000; + DeviceConfig.setProperty( + DeviceConfig.NAMESPACE_ADSERVICES, + FlagsConstants.KEY_APPSEARCH_WRITE_TIMEOUT_MS, + Integer.toString(phOverridingValue), + /* makeDefault */ false); + + assertThat(mPhFlags.getAppSearchWriteTimeout()).isEqualTo(phOverridingValue); + } + + @Test + public void testGetAppSearchReadTimeout() { + // Without any overriding, the value is the hard coded constant. + assertThat(mPhFlags.getAppSearchReadTimeout()).isEqualTo(DEFAULT_APPSEARCH_READ_TIMEOUT_MS); + + int phOverridingValue = DEFAULT_APPSEARCH_READ_TIMEOUT_MS + 500; + DeviceConfig.setProperty( + DeviceConfig.NAMESPACE_ADSERVICES, + FlagsConstants.KEY_APPSEARCH_READ_TIMEOUT_MS, + Integer.toString(phOverridingValue), + /* makeDefault */ false); + + assertThat(mPhFlags.getAppSearchReadTimeout()).isEqualTo(phOverridingValue); + } + private void overrideBackgroundJobSamplingLoggingRate(int phOverridingValue) { DeviceConfig.setProperty( DeviceConfig.NAMESPACE_ADSERVICES, diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdFiltererImplTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdFiltererImplTest.java index 490e585d4..ef9ba9f17 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdFiltererImplTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdFiltererImplTest.java @@ -92,7 +92,7 @@ public class AdFiltererImplTest { .build(); private static final SignedContextualAds.Builder CONTEXTUAL_ADS_BUILDER = - SignedContextualAdsFixture.aSignedContextualAdBuilder() + SignedContextualAdsFixture.aContextualAdsWithEmptySignatureBuilder() .setAdsWithBid(ImmutableList.of(new AdWithBid(AD_DATA, 1.0))) .setBuyer(CommonFixture.VALID_BUYER_1) .setDecisionLogicUri( diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdFiltererNoOpImplTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdFiltererNoOpImplTest.java index 7e3e358d9..cc7aed1ef 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdFiltererNoOpImplTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdFiltererNoOpImplTest.java @@ -48,7 +48,7 @@ public class AdFiltererNoOpImplTest { AdDataFixture.getValidFilterAdDataBuilderByBuyer(CommonFixture.VALID_BUYER_1, 0); private static final SignedContextualAds.Builder CONTEXTUAL_ADS_BUILDER = - SignedContextualAdsFixture.aSignedContextualAdBuilder() + SignedContextualAdsFixture.aContextualAdsWithEmptySignatureBuilder() .setAdsWithBid(ImmutableList.of(new AdWithBid(AD_DATA_BUILDER.build(), 1.0))) .setBuyer(CommonFixture.VALID_BUYER_1) .setDecisionLogicUri( diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdSelectionConfigValidatorTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdSelectionConfigValidatorTest.java index a66caecaf..074da6b50 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdSelectionConfigValidatorTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdSelectionConfigValidatorTest.java @@ -16,8 +16,6 @@ package com.android.adservices.service.adselection; -import static android.adservices.adselection.SignedContextualAdsFixture.PLACEHOLDER_SIGNATURE; - import static com.android.adservices.service.adselection.AdSelectionConfigValidator.DECISION_LOGIC_URI_TYPE; import static com.android.adservices.service.adselection.AdSelectionConfigValidator.TRUSTED_SCORING_SIGNALS_URI_TYPE; @@ -351,7 +349,7 @@ public class AdSelectionConfigValidatorTest { Map<AdTechIdentifier, SignedContextualAds> buyerContextualAds = new HashMap<>(); AdTechIdentifier buyer2 = CommonFixture.VALID_BUYER_2; SignedContextualAds contextualAds2 = - SignedContextualAdsFixture.generateSignedContextualAds( + SignedContextualAdsFixture.aContextualAdsWithEmptySignatureBuilder( buyer2, ImmutableList.of(100.0, 200.0)) .setDecisionLogicUri( CommonFixture.getUri( @@ -380,7 +378,7 @@ public class AdSelectionConfigValidatorTest { AdTechIdentifier buyer2 = CommonFixture.VALID_BUYER_2; ImmutableList<Double> bids = ImmutableList.of(100.0, 200.0); SignedContextualAds contextualAds2 = - SignedContextualAdsFixture.generateSignedContextualAds(buyer2, bids) + SignedContextualAdsFixture.aContextualAdsWithEmptySignatureBuilder(buyer2, bids) .setDecisionLogicUri( CommonFixture.getUri(buyer2, BUYER_BIDDING_LOGIC_URI_PATH)) .setAdsWithBid( @@ -446,14 +444,9 @@ public class AdSelectionConfigValidatorTest { Map<AdTechIdentifier, SignedContextualAds> buyerContextualAds = new HashMap<>(); buyerContextualAds.put( CommonFixture.VALID_BUYER_1, - new SignedContextualAds.Builder() - .setBuyer(CommonFixture.VALID_BUYER_1) - .setDecisionLogicUri( - CommonFixture.getUri( - CommonFixture.VALID_BUYER_1, - SignedContextualAdsFixture.DECISION_LOGIC_FRAGMENT)) + SignedContextualAdsFixture.aContextualAdsWithEmptySignatureBuilder( + CommonFixture.VALID_BUYER_1) .setAdsWithBid(adsWithBids) - .setSignature(PLACEHOLDER_SIGNATURE) .build()); AdSelectionConfig adSelectionConfig = diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdSelectionE2ETest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdSelectionE2ETest.java index 229eca16b..bbef9eb67 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdSelectionE2ETest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdSelectionE2ETest.java @@ -28,6 +28,8 @@ import static android.adservices.common.AdServicesStatusUtils.STATUS_UNAUTHORIZE import static android.adservices.common.AdServicesStatusUtils.STATUS_USER_CONSENT_REVOKED; import static com.android.adservices.data.adselection.AdSelectionDatabase.DATABASE_NAME; +import static com.android.adservices.data.encryptionkey.EncryptionKeyDaoTest.ENCRYPTION_KEY1; +import static com.android.adservices.data.enrollment.EnrollmentDaoTest.ENROLLMENT_DATA1; import static com.android.adservices.service.PhFlagsFixture.EXTENDED_FLEDGE_AD_SELECTION_BIDDING_TIMEOUT_PER_CA_MS; import static com.android.adservices.service.PhFlagsFixture.EXTENDED_FLEDGE_AD_SELECTION_FROM_OUTCOMES_OVERALL_TIMEOUT_MS; import static com.android.adservices.service.PhFlagsFixture.EXTENDED_FLEDGE_AD_SELECTION_OVERALL_TIMEOUT_MS; @@ -51,6 +53,7 @@ import static com.android.adservices.service.adselection.PrebuiltLogicGenerator. import static com.android.adservices.service.adselection.PrebuiltLogicGenerator.AD_SELECTION_PREBUILT_SCHEMA; import static com.android.adservices.service.adselection.PrebuiltLogicGenerator.AD_SELECTION_USE_CASE; import static com.android.adservices.service.adselection.PrebuiltLogicGenerator.PREBUILT_FEATURE_IS_DISABLED; +import static com.android.adservices.service.adselection.signature.ProtectedAudienceSignatureManager.PUBLIC_TEST_KEY_STRING; import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.DB_AD_SELECTION_FILE_SIZE; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS; import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; @@ -119,12 +122,9 @@ import com.android.adservices.data.adselection.AdSelectionDatabase; import com.android.adservices.data.adselection.AdSelectionDebugReportDao; import com.android.adservices.data.adselection.AdSelectionDebugReportingDatabase; import com.android.adservices.data.adselection.AdSelectionEntryDao; -import com.android.adservices.data.adselection.AdSelectionServerDatabase; import com.android.adservices.data.adselection.AppInstallDao; import com.android.adservices.data.adselection.DBAdSelectionOverride; import com.android.adservices.data.adselection.DBBuyerDecisionOverride; -import com.android.adservices.data.adselection.EncryptionContextDao; -import com.android.adservices.data.adselection.EncryptionKeyDao; import com.android.adservices.data.adselection.FrequencyCapDao; import com.android.adservices.data.adselection.SharedStorageDatabase; import com.android.adservices.data.common.DBAdData; @@ -133,6 +133,7 @@ import com.android.adservices.data.customaudience.CustomAudienceDatabase; import com.android.adservices.data.customaudience.DBCustomAudience; import com.android.adservices.data.customaudience.DBCustomAudienceOverride; import com.android.adservices.data.customaudience.DBTrustedBiddingData; +import com.android.adservices.data.encryptionkey.EncryptionKeyDao; import com.android.adservices.data.enrollment.EnrollmentDao; import com.android.adservices.data.signals.EncodedPayloadDao; import com.android.adservices.data.signals.ProtectedSignalsDatabase; @@ -151,6 +152,7 @@ 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.encryptionkey.EncryptionKey; import com.android.adservices.service.exception.FilterException; import com.android.adservices.service.js.JSScriptEngine; import com.android.adservices.service.stats.AdServicesLogger; @@ -636,8 +638,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase private EncodedPayloadDao mEncodedPayloadDao; private AppInstallDao mAppInstallDao; private FrequencyCapDao mFrequencyCapDao; + private EnrollmentDao mEnrollmentDao; private EncryptionKeyDao mEncryptionKeyDao; - private EncryptionContextDao mEncryptionContextDao; @Spy private AdSelectionEntryDao mAdSelectionEntryDaoSpy; private AdServicesHttpsClient mAdServicesHttpsClient; private AdSelectionConfig mAdSelectionConfig; @@ -655,6 +657,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Before public void setUp() throws Exception { + doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); + mAdSelectionEntryDaoSpy = Room.inMemoryDatabaseBuilder(mContext, AdSelectionDatabase.class) .build() @@ -667,10 +671,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase Room.inMemoryDatabaseBuilder(mContext, SharedStorageDatabase.class) .build() .frequencyCapDao(); - AdSelectionServerDatabase serverDb = - Room.inMemoryDatabaseBuilder(mContext, AdSelectionServerDatabase.class).build(); - mEncryptionContextDao = serverDb.encryptionContextDao(); - mEncryptionKeyDao = serverDb.encryptionKeyDao(); + mEncryptionKeyDao = EncryptionKeyDao.getInstance(mContext); + mEnrollmentDao = EnrollmentDao.getInstance(mContext); mAdFilteringFeatureFactory = new AdFilteringFeatureFactory(mAppInstallDao, mFrequencyCapDao, mFlags); @@ -711,8 +713,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -867,7 +869,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionSuccess_preV3BiddingLogic() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(3); doAnswer( @@ -963,8 +964,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -1089,8 +1090,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -1235,8 +1236,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -1377,8 +1378,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -1500,8 +1501,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -1607,7 +1608,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionSuccess_prebuiltScoringLogic() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(3); doAnswer( @@ -1703,7 +1703,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionSuccess_prebuiltFeatureDisabled_failure() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); Flags prebuiltDisabledFlags = new AdSelectionE2ETestFlags() { @Override @@ -1719,8 +1718,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -1818,8 +1817,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -1913,7 +1912,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionSuccess_v3BiddingLogic() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(3); doAnswer( @@ -2007,8 +2005,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -2131,8 +2129,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -2276,8 +2274,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -2417,8 +2415,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -2539,8 +2537,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -2645,7 +2643,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionSuccess_preV3BiddingLogicWithV3Header_scriptFail() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(2); doAnswer( @@ -2714,7 +2711,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelection_getTooHighHeader_failWithError() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(2); doAnswer( @@ -2783,7 +2779,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionWithOverride_getTooHighHeader_failWithError() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(2); doAnswer( @@ -2879,7 +2874,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionContextualAds_Success() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(3); doAnswer( @@ -2905,7 +2899,7 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase .logRunAdSelectionProcessReportedStats(any()); AdSelectionConfig adSelectionConfig = - AdSelectionConfigFixture.anAdSelectionConfigWithContextualAdsBuilder() + AdSelectionConfigFixture.anAdSelectionConfigWithSignedContextualAdsBuilder() .setCustomAudienceBuyers(ImmutableList.of(BUYER_1, BUYER_2, BUYER_3)) .setSeller(mSeller) .setDecisionLogicUri( @@ -2976,7 +2970,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @FlakyTest(bugId = 304764127) @Test public void testRunAdSelectionContextualAds_UseOverrides_Success() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(3); doAnswer( @@ -3002,7 +2995,7 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase .logRunAdSelectionProcessReportedStats(any()); AdSelectionConfig adSelectionConfig = - AdSelectionConfigFixture.anAdSelectionConfigWithContextualAdsBuilder() + AdSelectionConfigFixture.anAdSelectionConfigWithSignedContextualAdsBuilder() .setCustomAudienceBuyers(ImmutableList.of(BUYER_1, BUYER_2, BUYER_3)) .setSeller(mSeller) .setDecisionLogicUri( @@ -3091,8 +3084,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -3152,7 +3145,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionContextualAds_Disabled_Success() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(3); doAnswer( @@ -3178,7 +3170,7 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase .logRunAdSelectionProcessReportedStats(any()); AdSelectionConfig adSelectionConfig = - AdSelectionConfigFixture.anAdSelectionConfigWithContextualAdsBuilder() + AdSelectionConfigFixture.anAdSelectionConfigWithSignedContextualAdsBuilder() .setCustomAudienceBuyers(ImmutableList.of(BUYER_1, BUYER_2, BUYER_3)) .setSeller(mSeller) .setDecisionLogicUri( @@ -3230,8 +3222,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -3277,7 +3269,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionOnlyContextualAds_NoBuyers_Success() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(2); doAnswer( @@ -3296,7 +3287,7 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase .logRunAdSelectionProcessReportedStats(any()); AdSelectionConfig adSelectionConfig = - AdSelectionConfigFixture.anAdSelectionConfigWithContextualAdsBuilder() + AdSelectionConfigFixture.anAdSelectionConfigWithSignedContextualAdsBuilder() .setCustomAudienceBuyers(ImmutableList.of()) .setSeller(mSeller) .setDecisionLogicUri( @@ -3338,8 +3329,45 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase } @Test + public void testRunAdSelectionOnlyContextualAds_unauthenticatedContextualAdsRemoved_Success() + throws Exception { + // Logger calls come after the callback is returned + CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(1); + doAnswer( + unusedInvocation -> { + runAdSelectionProcessLoggerLatch.countDown(); + return null; + }) + .when(mAdServicesLoggerMock) + .logRunAdSelectionProcessReportedStats(any()); + + AdSelectionConfig adSelectionConfig = + AdSelectionConfigFixture.anAdSelectionConfigWithSignedContextualAdsBuilder() + .setCustomAudienceBuyers(ImmutableList.of()) + .setSeller(mSeller) + .setDecisionLogicUri( + mMockWebServerRule.uriForPath(SELLER_DECISION_LOGIC_URI_PATH)) + .setTrustedScoringSignalsUri( + mMockWebServerRule.uriForPath(SELLER_TRUSTED_SIGNAL_URI_PATH)) + .setBuyerSignedContextualAds(createUnauthenticatedContextualAds()) + .build(); + + mMockWebServerRule.startMockWebServer(mDispatcher); + + AdSelectionTestCallback resultsCallback = + invokeSelectAds(mAdSelectionService, adSelectionConfig, CALLER_PACKAGE_NAME); + runAdSelectionProcessLoggerLatch.await(); + assertCallbackFailed(resultsCallback); + assertTrue( + resultsCallback + .mFledgeErrorResponse + .getErrorMessage() + .contains(ERROR_NO_BUYERS_OR_CONTEXTUAL_ADS_AVAILABLE)); + assertEquals(STATUS_INVALID_ARGUMENT, resultsCallback.mFledgeErrorResponse.getStatusCode()); + } + + @Test public void testRunAdSelectionOnlyContextualAds_NoCAs_Success() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(2); doAnswer( @@ -3365,7 +3393,7 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase .logRunAdSelectionProcessReportedStats(any()); AdSelectionConfig adSelectionConfig = - AdSelectionConfigFixture.anAdSelectionConfigWithContextualAdsBuilder() + AdSelectionConfigFixture.anAdSelectionConfigWithSignedContextualAdsBuilder() .setCustomAudienceBuyers(ImmutableList.of(BUYER_1, BUYER_2, BUYER_3)) .setSeller(mSeller) .setDecisionLogicUri( @@ -3417,7 +3445,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test @FlakyTest(bugId = 304764127) public void testRunAdSelectionOnlyContextualAds_NoCAsNoNetworkCall_Success() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(2); doAnswer( @@ -3457,7 +3484,7 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase paramKey, paramValue)); AdSelectionConfig adSelectionConfig = - AdSelectionConfigFixture.anAdSelectionConfigWithContextualAdsBuilder() + AdSelectionConfigFixture.anAdSelectionConfigWithSignedContextualAdsBuilder() .setCustomAudienceBuyers(Collections.emptyList()) .setSeller(mSeller) .setDecisionLogicUri(prebuiltUri) @@ -3508,7 +3535,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test @FlakyTest(bugId = 315521295) public void testRunAdSelectionNoContextualAds_NoCAs_Failure() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(2); doAnswer( @@ -3534,7 +3560,7 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase .logRunAdSelectionProcessReportedStats(any()); AdSelectionConfig adSelectionConfig = - AdSelectionConfigFixture.anAdSelectionConfigWithContextualAdsBuilder() + AdSelectionConfigFixture.anAdSelectionConfigWithSignedContextualAdsBuilder() .setCustomAudienceBuyers(ImmutableList.of(BUYER_1, BUYER_2, BUYER_3)) .setSeller(mSeller) .setDecisionLogicUri( @@ -3577,8 +3603,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionWithRevokedUserConsentSuccess() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); - doThrow(new FilterException(new ConsentManager.RevokedConsentException())) .when(mAdSelectionServiceFilter) .filterRequest( @@ -3653,7 +3677,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionMultipleCAsSuccess_preV3BiddingLogic() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(3); doAnswer( @@ -3767,7 +3790,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionMultipleCAsSuccess_v3BiddingLogic() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(3); doAnswer( @@ -3881,8 +3903,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionMultipleCAsNoCachingSuccess_preV3BiddingLogic() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); - MockWebServer server = mMockWebServerRule.startMockWebServer(mDispatcher); List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2); List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0); @@ -3962,8 +3982,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, httpClientWithNoCaching, mDevContextFilter, mLightweightExecutorService, @@ -4002,8 +4022,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test @FlakyTest(bugId = 304764127) public void testRunAdSelectionMultipleCAsNoCachingSuccess_v3BiddingLogic() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); - MockWebServer server = mMockWebServerRule.startMockWebServer(DISPATCHER_V3_BIDDING_LOGIC); List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2); List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0); @@ -4083,8 +4101,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, httpClientWithNoCaching, mDevContextFilter, mLightweightExecutorService, @@ -4122,8 +4140,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionMultipleCAsJSCachedSuccess_preV3BiddingLogic() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); - MockWebServer server = mMockWebServerRule.startMockWebServer(mDispatcher); List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2); List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0); @@ -4205,8 +4221,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, httpClientWithCaching, mDevContextFilter, mLightweightExecutorService, @@ -4247,8 +4263,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionMultipleCAsJSCachedSuccess_v3BiddingLogic() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); - MockWebServer server = mMockWebServerRule.startMockWebServer(DISPATCHER_V3_BIDDING_LOGIC); List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2); List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0); @@ -4330,8 +4344,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, httpClientWithCaching, mDevContextFilter, mLightweightExecutorService, @@ -4373,7 +4387,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test @FlakyTest(bugId = 304764127) public void testRunAdSelectionSucceedsWithOverride_preV3BiddingLogic() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(3); doAnswer( @@ -4455,8 +4468,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -4511,7 +4524,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test @FlakyTest(bugId = 304764127) public void testRunAdSelectionSucceedsWithOverride_v3BiddingLogic() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(3); doAnswer( @@ -4595,8 +4607,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -4650,7 +4662,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionActiveCAs() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(3); doAnswer( @@ -4743,7 +4754,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionNoCAsActive() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(2); doAnswer( @@ -4926,7 +4936,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionPartialAdsExcludedBidding() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(3); doAnswer( @@ -5034,7 +5043,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionMissingBiddingLogicFailure() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(2); doAnswer( @@ -5140,7 +5148,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionMissingScoringLogicFailure() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(3); doAnswer( @@ -5254,7 +5261,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionErrorFetchingScoringLogicFailure() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(3); doAnswer( @@ -5367,7 +5373,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionPartialMissingBiddingLogic() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(3); doAnswer( @@ -5485,7 +5490,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionPartialNonPositiveScoring() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(3); doAnswer( @@ -5608,7 +5612,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionNonPositiveScoringFailure() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(3); doAnswer( @@ -5728,7 +5731,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionBiddingTimesOutForCA() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(3); doAnswer( @@ -5786,8 +5788,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -5907,7 +5909,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionImposesPerBuyerBiddingTimeout_preV3BiddingLogic() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(6); doAnswer( @@ -6039,8 +6040,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -6115,8 +6116,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -6177,7 +6178,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @FlakyTest(bugId = 304764127) @Test public void testRunAdSelectionImposesPerBuyerBiddingTimeout_v3BiddingLogic() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(6); doAnswer( @@ -6309,8 +6309,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -6385,8 +6385,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -6446,7 +6446,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionScoringTimesOut() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(3); doAnswer( @@ -6504,8 +6503,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -6677,7 +6676,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionMissingBiddingSignalsFailure() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(2); doAnswer( @@ -6770,7 +6768,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test @FlakyTest(bugId = 315521295) public void testRunAdSelectionMissingScoringSignalsFailure() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(3); doAnswer( @@ -6872,7 +6869,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionMissingPartialBiddingSignalsSuccess() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); // Logger calls come after the callback is returned CountDownLatch runAdSelectionProcessLoggerLatch = new CountDownLatch(3); doAnswer( @@ -6988,8 +6984,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase @Test public void testRunAdSelectionFailsWithInvalidPackageName() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); - String invalidPackageName = CALLER_PACKAGE_NAME + "invalidPackageName"; doThrow(new FilterException(new FledgeAuthorizationFilter.CallerMismatchException())) @@ -7007,7 +7001,7 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase // Bypass the permission check since it's enforced before the package name check doNothing() .when(mFledgeAuthorizationFilterSpy) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( mContext, invalidPackageName, AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS); // Logger calls come after the callback is returned @@ -7075,13 +7069,12 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase geq(0)); verify(mFledgeAuthorizationFilterSpy) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( mContext, invalidPackageName, AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS); } @Test public void testRunAdSelectionFailsWhenAppCannotUsePPApi() throws Exception { - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); doThrow(new FilterException(new FledgeAuthorizationFilter.AdTechNotAllowedException())) .when(mAdSelectionServiceFilter) .filterRequest( @@ -7193,8 +7186,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -7327,8 +7320,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -7462,8 +7455,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -7561,8 +7554,6 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase // A null package means WebView is not installed doReturn(null).when(WebView::getCurrentWebViewPackage); - doReturn(new AdSelectionE2ETestFlags()).when(FlagsFactory::getFlags); - // Shut down any running JSScriptEngine to ensure the new singleton gets picked up JSScriptEngine.getInstance(mContext, LoggerFactory.getFledgeLogger()).shutdown(); @@ -7575,8 +7566,8 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -7803,6 +7794,21 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase adCost); } + private Map<AdTechIdentifier, SignedContextualAds> createUnauthenticatedContextualAds() { + Map<AdTechIdentifier, SignedContextualAds> authenticatedContextualAds = + createContextualAds(); + Map<AdTechIdentifier, SignedContextualAds> unauthenticatedContextualAds = new HashMap<>(); + byte[] invalidSignatures = new byte[] {1, 2, 3}; + for (Map.Entry<AdTechIdentifier, SignedContextualAds> buyersBundle : + authenticatedContextualAds.entrySet()) { + AdTechIdentifier buyer = buyersBundle.getKey(); + SignedContextualAds ads = buyersBundle.getValue(); + unauthenticatedContextualAds.put( + buyer, ads.cloneToBuilder().setSignature(invalidSignatures).build()); + } + return unauthenticatedContextualAds; + } + private Map<AdTechIdentifier, SignedContextualAds> createContextualAds() { Map<AdTechIdentifier, SignedContextualAds> buyerContextualAds = new HashMap<>(); @@ -7813,17 +7819,50 @@ public final class AdSelectionE2ETest extends AdServicesExtendedMockitoTestCase .uriForPath(BUYER_BIDDING_LOGIC_URI_PATH + BUYER_2) .getHost()); SignedContextualAds contextualAds2 = - SignedContextualAdsFixture.generateSignedContextualAds( - buyer2, ImmutableList.of(100.0, 200.0, 300.0, 400.0, 500.0)) - .setDecisionLogicUri( - mMockWebServerRule.uriForPath( - BUYER_BIDDING_LOGIC_URI_PATH + BUYER_2)) - .build(); + SignedContextualAdsFixture.signContextualAds( + SignedContextualAdsFixture.aContextualAdsWithEmptySignatureBuilder( + buyer2, ImmutableList.of(100.0, 200.0, 300.0, 400.0, 500.0)) + .setDecisionLogicUri( + mMockWebServerRule.uriForPath( + BUYER_BIDDING_LOGIC_URI_PATH + BUYER_2))); buyerContextualAds.put(buyer2, contextualAds2); - + persistEncryptionKeyInDb(buyerContextualAds); return buyerContextualAds; } + private void persistEncryptionKeyInDb( + Map<AdTechIdentifier, SignedContextualAds> buyerContextualAds) { + for (AdTechIdentifier adTech : buyerContextualAds.keySet()) { + String enrollmentId = adTech + "-enrollment-id"; + String sourceRegistration = CommonFixture.getUri(adTech, "/source").toString(); + String triggerRegistration = CommonFixture.getUri(adTech, "/trigger").toString(); + String attributionReporting = CommonFixture.getUri(adTech, "/attrReport").toString(); + String responseBasedRegistration = CommonFixture.getUri(adTech, "/response").toString(); + String encryptionKeyUrl = CommonFixture.getUri(adTech, "/keys").toString(); + mEnrollmentDao.insert( + ENROLLMENT_DATA1 + .cloneToBuilder() + .setEnrollmentId(enrollmentId) + .setAttributionSourceRegistrationUrl(sourceRegistration) + .setAttributionTriggerRegistrationUrl(triggerRegistration) + .setAttributionReportingUrl(attributionReporting) + .setRemarketingResponseBasedRegistrationUrl(responseBasedRegistration) + .setEncryptionKeyUrl(encryptionKeyUrl) + .build()); + Uri reportingOriginUrl = CommonFixture.getUri(adTech, "/reportingOriginUrl"); + mEncryptionKeyDao.insert( + ENCRYPTION_KEY1 + .cloneToBuilder() + .setEnrollmentId(enrollmentId) + .setKeyType(EncryptionKey.KeyType.SIGNING) + .setEncryptionKeyUrl(encryptionKeyUrl) + .setProtocolType(EncryptionKey.ProtocolType.ECDSA) + .setBody(PUBLIC_TEST_KEY_STRING) + .setReportingOrigin(reportingOriginUrl) + .build()); + } + } + private void verifyErrorMessageIsCorrect( final String actualErrorMassage, final String expectedErrorReason) { assertTrue( diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdSelectionFromOutcomesE2ETest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdSelectionFromOutcomesE2ETest.java index 47e2550af..b82d2b0a2 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdSelectionFromOutcomesE2ETest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdSelectionFromOutcomesE2ETest.java @@ -76,8 +76,6 @@ import com.android.adservices.data.adselection.AdSelectionServerDatabase; import com.android.adservices.data.adselection.AppInstallDao; import com.android.adservices.data.adselection.CustomAudienceSignals; import com.android.adservices.data.adselection.DBAdSelection; -import com.android.adservices.data.adselection.EncryptionContextDao; -import com.android.adservices.data.adselection.EncryptionKeyDao; import com.android.adservices.data.adselection.FrequencyCapDao; import com.android.adservices.data.adselection.SharedStorageDatabase; import com.android.adservices.data.adselection.datahandlers.AdSelectionInitialization; @@ -86,6 +84,7 @@ import com.android.adservices.data.adselection.datahandlers.WinningCustomAudienc import com.android.adservices.data.customaudience.CustomAudienceDao; import com.android.adservices.data.customaudience.CustomAudienceDatabase; import com.android.adservices.data.customaudience.DBCustomAudience; +import com.android.adservices.data.encryptionkey.EncryptionKeyDao; import com.android.adservices.data.enrollment.EnrollmentDao; import com.android.adservices.data.signals.EncodedPayloadDao; import com.android.adservices.data.signals.ProtectedSignalsDatabase; @@ -212,9 +211,9 @@ public class AdSelectionFromOutcomesE2ETest { private EncodedPayloadDao mEncodedPayloadDao; private AppInstallDao mAppInstallDao; private FrequencyCapDao mFrequencyCapDao; - private EncryptionKeyDao mEncryptionKeyDao; - private EncryptionContextDao mEncryptionContextDao; @Spy private AdSelectionEntryDao mAdSelectionEntryDaoSpy; + private EnrollmentDao mEnrollmentDao; + private EncryptionKeyDao mEncryptionKeyDao; private AdServicesHttpsClient mAdServicesHttpsClient; private AdSelectionService mAdSelectionService; private Dispatcher mDispatcher; @@ -259,8 +258,8 @@ public class AdSelectionFromOutcomesE2ETest { mFrequencyCapDao = sharedDb.frequencyCapDao(); AdSelectionServerDatabase serverDb = Room.inMemoryDatabaseBuilder(mContextSpy, AdSelectionServerDatabase.class).build(); - mEncryptionContextDao = serverDb.encryptionContextDao(); - mEncryptionKeyDao = serverDb.encryptionKeyDao(); + mEnrollmentDao = EnrollmentDao.getInstance(mContextSpy); + mEncryptionKeyDao = EncryptionKeyDao.getInstance(mContextSpy); mAdFilteringFeatureFactory = new AdFilteringFeatureFactory(mAppInstallDao, mFrequencyCapDao, mFlags); mAdServicesHttpsClient = @@ -280,8 +279,8 @@ public class AdSelectionFromOutcomesE2ETest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -411,8 +410,8 @@ public class AdSelectionFromOutcomesE2ETest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, @@ -468,8 +467,8 @@ public class AdSelectionFromOutcomesE2ETest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilter, mLightweightExecutorService, diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdSelectionServiceImplTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdSelectionServiceImplTest.java index 48cd9327d..b3ceaa5be 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdSelectionServiceImplTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdSelectionServiceImplTest.java @@ -114,7 +114,6 @@ import com.android.adservices.concurrency.AdServicesExecutors; import com.android.adservices.data.adselection.AdSelectionDatabase; import com.android.adservices.data.adselection.AdSelectionDebugReportDao; import com.android.adservices.data.adselection.AdSelectionEntryDao; -import com.android.adservices.data.adselection.AdSelectionServerDatabase; import com.android.adservices.data.adselection.AppInstallDao; import com.android.adservices.data.adselection.CustomAudienceSignals; import com.android.adservices.data.adselection.DBAdSelection; @@ -122,17 +121,18 @@ import com.android.adservices.data.adselection.DBAdSelectionFromOutcomesOverride import com.android.adservices.data.adselection.DBAdSelectionOverride; import com.android.adservices.data.adselection.DBBuyerDecisionLogic; import com.android.adservices.data.adselection.DBReportingComputationInfo; -import com.android.adservices.data.adselection.EncryptionContextDao; -import com.android.adservices.data.adselection.EncryptionKeyDao; import com.android.adservices.data.adselection.FrequencyCapDao; import com.android.adservices.data.adselection.SharedStorageDatabase; import com.android.adservices.data.adselection.datahandlers.AdSelectionInitialization; import com.android.adservices.data.customaudience.CustomAudienceDao; import com.android.adservices.data.customaudience.CustomAudienceDatabase; import com.android.adservices.data.customaudience.DBCustomAudience; +import com.android.adservices.data.encryptionkey.EncryptionKeyDao; +import com.android.adservices.data.enrollment.EnrollmentDao; import com.android.adservices.data.signals.EncodedPayloadDao; import com.android.adservices.data.signals.ProtectedSignalsDatabase; import com.android.adservices.service.Flags; +import com.android.adservices.service.FlagsFactory; import com.android.adservices.service.adselection.AppInstallAdvertisersSetterTest.SetAppInstallAdvertisersTestCallback; import com.android.adservices.service.adselection.encryption.ObliviousHttpEncryptor; import com.android.adservices.service.common.AdSelectionServiceFilter; @@ -300,7 +300,7 @@ public class AdSelectionServiceImplTest { private AppInstallDao mAppInstallDao; private FrequencyCapDao mFrequencyCapDao; private EncryptionKeyDao mEncryptionKeyDao; - private EncryptionContextDao mEncryptionContextDao; + private EnrollmentDao mEnrollmentDao; private AdSelectionConfig.Builder mAdSelectionConfigBuilder; private Uri mBiddingLogicUri; private CustomAudienceSignals mCustomAudienceSignals; @@ -323,13 +323,14 @@ public class AdSelectionServiceImplTest { .spyStatic(JSScriptEngine.class) .spyStatic(WebView.class) .mockStatic(ConsentManager.class) + .mockStatic(FlagsFactory.class) .mockStatic(MeasurementImpl.class) .mockStatic(AppImportanceFilter.class) // mAdServicesLoggerMock is not referenced in many tests .strictness(Strictness.LENIENT) .initMocks(this) .startMocking(); - + doReturn(mFlags).when(FlagsFactory::getFlags); mCustomAudienceDao = Room.inMemoryDatabaseBuilder(CONTEXT, CustomAudienceDatabase.class) .addTypeConverter(new DBCustomAudience.Converters(true, true)) @@ -354,13 +355,8 @@ public class AdSelectionServiceImplTest { mAppInstallDao = sharedDb.appInstallDao(); mFrequencyCapDao = sharedDb.frequencyCapDao(); - AdSelectionServerDatabase serverDb = - Room.inMemoryDatabaseBuilder( - ApplicationProvider.getApplicationContext(), - AdSelectionServerDatabase.class) - .build(); - mEncryptionContextDao = serverDb.encryptionContextDao(); - mEncryptionKeyDao = serverDb.encryptionKeyDao(); + mEncryptionKeyDao = EncryptionKeyDao.getInstance(CONTEXT); + mEnrollmentDao = EnrollmentDao.getInstance(CONTEXT); mAdFilteringFeatureFactory = new AdFilteringFeatureFactory(mAppInstallDao, mFrequencyCapDao, mFlags); @@ -498,8 +494,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -619,8 +615,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -750,8 +746,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -882,8 +878,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -1002,8 +998,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -1125,8 +1121,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -1240,8 +1236,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -1341,8 +1337,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -1449,8 +1445,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -1572,8 +1568,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -1693,8 +1689,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -1819,8 +1815,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -1993,8 +1989,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -2137,8 +2133,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -2299,8 +2295,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -2467,8 +2463,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -2626,8 +2622,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -2774,8 +2770,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -2926,8 +2922,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -3094,8 +3090,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -3269,8 +3265,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -3413,8 +3409,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -3520,8 +3516,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -3626,8 +3622,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -3738,8 +3734,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -3845,8 +3841,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -3947,8 +3943,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -4048,8 +4044,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -4150,8 +4146,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -4272,8 +4268,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -4419,8 +4415,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -4516,8 +4512,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -4578,8 +4574,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -4635,8 +4631,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -4697,8 +4693,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -4770,8 +4766,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -4839,8 +4835,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -4912,8 +4908,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -4986,8 +4982,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -5101,8 +5097,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -5218,8 +5214,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -5331,8 +5327,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -5439,8 +5435,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -5476,8 +5472,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -5527,8 +5523,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -5594,8 +5590,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -5662,8 +5658,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -5721,8 +5717,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -5782,8 +5778,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -5837,8 +5833,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -5896,8 +5892,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -5996,8 +5992,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -6112,8 +6108,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -6230,8 +6226,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -6354,8 +6350,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -6459,8 +6455,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -6565,8 +6561,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -6699,8 +6695,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -6813,8 +6809,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -6929,8 +6925,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -7067,8 +7063,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -7198,8 +7194,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -7314,8 +7310,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -7443,8 +7439,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -7510,8 +7506,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -7575,8 +7571,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -7634,8 +7630,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -7699,8 +7695,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -7776,8 +7772,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -7847,8 +7843,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -7922,8 +7918,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -7996,8 +7992,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -8119,8 +8115,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -8236,8 +8232,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -8358,8 +8354,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -8498,8 +8494,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -9172,8 +9168,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -9234,8 +9230,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -9277,8 +9273,8 @@ public class AdSelectionServiceImplTest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mClientSpy, mDevContextFilterMock, mLightweightExecutorService, diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdsScoreGeneratorImplTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdsScoreGeneratorImplTest.java index 7a4088a37..342db92c0 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdsScoreGeneratorImplTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AdsScoreGeneratorImplTest.java @@ -817,7 +817,7 @@ public class AdsScoreGeneratorImplTest { Map<AdTechIdentifier, SignedContextualAds> contextualAdsMap = createContextualAds(); mAdSelectionConfig = - AdSelectionConfigFixture.anAdSelectionConfigWithContextualAdsBuilder() + AdSelectionConfigFixture.anAdSelectionConfigWithSignedContextualAdsBuilder() .setDecisionLogicUri(decisionLogicUri) .setTrustedScoringSignalsUri( mMockWebServerRule.uriForPath(mTrustedScoringSignalsPath)) @@ -938,7 +938,7 @@ public class AdsScoreGeneratorImplTest { Map<AdTechIdentifier, SignedContextualAds> contextualAdsMap = createContextualAds(); mAdSelectionConfig = - AdSelectionConfigFixture.anAdSelectionConfigWithContextualAdsBuilder() + AdSelectionConfigFixture.anAdSelectionConfigWithSignedContextualAdsBuilder() .setDecisionLogicUri(decisionLogicUri) .setTrustedScoringSignalsUri( mMockWebServerRule.uriForPath(mTrustedScoringSignalsPath)) @@ -1086,7 +1086,7 @@ public class AdsScoreGeneratorImplTest { Map<AdTechIdentifier, SignedContextualAds> contextualAdsMap = createContextualAds(); mAdSelectionConfig = - AdSelectionConfigFixture.anAdSelectionConfigWithContextualAdsBuilder() + AdSelectionConfigFixture.anAdSelectionConfigWithSignedContextualAdsBuilder() .setDecisionLogicUri(decisionLogicUri) .setTrustedScoringSignalsUri( mMockWebServerRule.uriForPath(mTrustedScoringSignalsPath)) @@ -1257,7 +1257,7 @@ public class AdsScoreGeneratorImplTest { Map<AdTechIdentifier, SignedContextualAds> contextualAdsMap = createContextualAds(); mAdSelectionConfig = - AdSelectionConfigFixture.anAdSelectionConfigWithContextualAdsBuilder() + AdSelectionConfigFixture.anAdSelectionConfigWithSignedContextualAdsBuilder() .setDecisionLogicUri(decisionLogicUri) .setTrustedScoringSignalsUri( mMockWebServerRule.uriForPath(mTrustedScoringSignalsPath)) @@ -1904,13 +1904,13 @@ public class AdsScoreGeneratorImplTest { AdTechIdentifier buyer1 = BUYER_1; SignedContextualAds contextualAds1 = - SignedContextualAdsFixture.generateSignedContextualAds( + SignedContextualAdsFixture.aContextualAdsWithEmptySignatureBuilder( buyer1, ImmutableList.of(100.0, 200.0, 300.0)) .build(); AdTechIdentifier buyer2 = CommonFixture.VALID_BUYER_2; SignedContextualAds contextualAds2 = - SignedContextualAdsFixture.generateSignedContextualAds( + SignedContextualAdsFixture.aContextualAdsWithEmptySignatureBuilder( buyer2, ImmutableList.of(400.0, 500.0)) .build(); diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AuctionServerE2ETest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AuctionServerE2ETest.java index 8a36e3ee4..f29355fda 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AuctionServerE2ETest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/AuctionServerE2ETest.java @@ -104,6 +104,7 @@ import com.android.adservices.data.common.DBAdData; import com.android.adservices.data.customaudience.CustomAudienceDao; import com.android.adservices.data.customaudience.CustomAudienceDatabase; import com.android.adservices.data.customaudience.DBCustomAudience; +import com.android.adservices.data.enrollment.EnrollmentDao; import com.android.adservices.data.signals.EncodedPayloadDao; import com.android.adservices.data.signals.ProtectedSignalsDatabase; import com.android.adservices.service.Flags; @@ -254,7 +255,9 @@ public class AuctionServerE2ETest { private AdSelectionEntryDao mAdSelectionEntryDao; private AppInstallDao mAppInstallDao; private FrequencyCapDao mFrequencyCapDaoSpy; - private EncryptionKeyDao mEncryptionKeyDao; + private com.android.adservices.data.encryptionkey.EncryptionKeyDao mEncryptionKeyDao; + private EncryptionKeyDao mAuctionServerEncryptionKeyDao; + private EnrollmentDao mEnrollmentDao; private EncryptionContextDao mEncryptionContextDao; @Mock private ObliviousHttpEncryptor mObliviousHttpEncryptorMock; @Mock private AdSelectionServiceFilter mAdSelectionServiceFilterMock; @@ -305,8 +308,11 @@ public class AuctionServerE2ETest { mFrequencyCapDaoSpy = spy(sharedDb.frequencyCapDao()); AdSelectionServerDatabase serverDb = Room.inMemoryDatabaseBuilder(mContext, AdSelectionServerDatabase.class).build(); + mEncryptionKeyDao = + com.android.adservices.data.encryptionkey.EncryptionKeyDao.getInstance(mContext); + mEnrollmentDao = EnrollmentDao.getInstance(mContext); + mAuctionServerEncryptionKeyDao = serverDb.encryptionKeyDao(); mEncryptionContextDao = serverDb.encryptionContextDao(); - mEncryptionKeyDao = serverDb.encryptionKeyDao(); mAdFilteringFeatureFactory = new AdFilteringFeatureFactory(mAppInstallDao, mFrequencyCapDaoSpy, mFlags); when(ConsentManager.getInstance(mContext)).thenReturn(mConsentManagerMock); @@ -820,7 +826,7 @@ public class AuctionServerE2ETest { .setEncryptionKeyType(ENCRYPTION_KEY_TYPE_AUCTION) .setExpiryTtlSeconds(TimeUnit.DAYS.toSeconds(7)) .build(); - mEncryptionKeyDao.insertAllKeys(ImmutableList.of(dbEncryptionKey)); + mAuctionServerEncryptionKeyDao.insertAllKeys(ImmutableList.of(dbEncryptionKey)); String seed = "wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww"; byte[] seedBytes = seed.getBytes(StandardCharsets.US_ASCII); @@ -831,8 +837,8 @@ public class AuctionServerE2ETest { mCustomAudienceDaoSpy, mEncodedPayloadDaoSpy, mFrequencyCapDaoSpy, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -848,7 +854,7 @@ public class AuctionServerE2ETest { mConsentManagerMock, new ObliviousHttpEncryptorWithSeedImpl( new AdSelectionEncryptionKeyManager( - mEncryptionKeyDao, + mAuctionServerEncryptionKeyDao, mFlags, mAdServicesHttpsClientSpy, mLightweightExecutorService), @@ -1455,7 +1461,7 @@ public class AuctionServerE2ETest { .setEncryptionKeyType(ENCRYPTION_KEY_TYPE_AUCTION) .setExpiryTtlSeconds(TimeUnit.DAYS.toSeconds(7)) .build(); - mEncryptionKeyDao.insertAllKeys(ImmutableList.of(dbEncryptionKey)); + mAuctionServerEncryptionKeyDao.insertAllKeys(ImmutableList.of(dbEncryptionKey)); String seed = "wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww"; byte[] seedBytes = seed.getBytes(StandardCharsets.US_ASCII); @@ -1466,8 +1472,8 @@ public class AuctionServerE2ETest { mCustomAudienceDaoSpy, mEncodedPayloadDaoSpy, mFrequencyCapDaoSpy, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClientSpy, mDevContextFilterMock, mLightweightExecutorService, @@ -1483,7 +1489,7 @@ public class AuctionServerE2ETest { mConsentManagerMock, new ObliviousHttpEncryptorWithSeedImpl( new AdSelectionEncryptionKeyManager( - mEncryptionKeyDao, + mAuctionServerEncryptionKeyDao, mFlags, mAdServicesHttpsClientSpy, mLightweightExecutorService), @@ -1537,8 +1543,8 @@ public class AuctionServerE2ETest { mCustomAudienceDaoSpy, mEncodedPayloadDaoSpy, mFrequencyCapDaoSpy, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClientSpy, mDevContextFilterMock, mLightweightExecutorService, diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/FrequencyCapFilteringE2ETest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/FrequencyCapFilteringE2ETest.java index a18905bbe..76d93ab08 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/FrequencyCapFilteringE2ETest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/FrequencyCapFilteringE2ETest.java @@ -57,18 +57,16 @@ import com.android.adservices.data.DbTestUtil; import com.android.adservices.data.adselection.AdSelectionDatabase; import com.android.adservices.data.adselection.AdSelectionDebugReportDao; import com.android.adservices.data.adselection.AdSelectionEntryDao; -import com.android.adservices.data.adselection.AdSelectionServerDatabase; import com.android.adservices.data.adselection.AppInstallDao; import com.android.adservices.data.adselection.DBAdSelection; import com.android.adservices.data.adselection.DBAdSelectionHistogramInfo; -import com.android.adservices.data.adselection.EncryptionContextDao; -import com.android.adservices.data.adselection.EncryptionKeyDao; import com.android.adservices.data.adselection.FrequencyCapDao; import com.android.adservices.data.adselection.SharedStorageDatabase; import com.android.adservices.data.common.DBAdData; import com.android.adservices.data.customaudience.CustomAudienceDao; import com.android.adservices.data.customaudience.CustomAudienceDatabase; import com.android.adservices.data.customaudience.DBCustomAudience; +import com.android.adservices.data.encryptionkey.EncryptionKeyDao; import com.android.adservices.data.enrollment.EnrollmentDao; import com.android.adservices.data.signals.EncodedPayloadDao; import com.android.adservices.data.signals.ProtectedSignalsDatabase; @@ -118,7 +116,6 @@ import java.util.concurrent.TimeUnit; @SpyStatic(FlagsFactory.class) public final class FrequencyCapFilteringE2ETest extends AdServicesExtendedMockitoTestCase { - private static final int CALLBACK_WAIT_MS = 500; private static final int SELECT_ADS_CALLBACK_WAIT_MS = 10_000; private static final long AD_SELECTION_ID_BUYER_1 = 20; @@ -196,7 +193,7 @@ public final class FrequencyCapFilteringE2ETest extends AdServicesExtendedMockit private AppInstallDao mAppInstallDao; private FrequencyCapDao mFrequencyCapDaoSpy; private EncryptionKeyDao mEncryptionKeyDao; - private EncryptionContextDao mEncryptionContextDao; + private EnrollmentDao mEnrollmentDao; private ExecutorService mLightweightExecutorService; private ExecutorService mBackgroundExecutorService; private ScheduledThreadPoolExecutor mScheduledExecutor; @@ -234,16 +231,16 @@ public final class FrequencyCapFilteringE2ETest extends AdServicesExtendedMockit Room.inMemoryDatabaseBuilder(mContextSpy, SharedStorageDatabase.class) .build() .frequencyCapDao()); - AdSelectionServerDatabase serverDb = - Room.inMemoryDatabaseBuilder(mContextSpy, AdSelectionServerDatabase.class).build(); - mEncryptionContextDao = serverDb.encryptionContextDao(); - mEncryptionKeyDao = serverDb.encryptionKeyDao(); + + Flags flagsEnablingAdFiltering = new FlagsOverridingAdFiltering(true); + doReturn(flagsEnablingAdFiltering).when(FlagsFactory::getFlags); + + mEncryptionKeyDao = EncryptionKeyDao.getInstance(mContextSpy); + mEnrollmentDao = EnrollmentDao.getInstance(mContextSpy); mLightweightExecutorService = AdServicesExecutors.getLightWeightExecutor(); mBackgroundExecutorService = AdServicesExecutors.getBackgroundExecutor(); mScheduledExecutor = AdServicesExecutors.getScheduler(); - Flags flagsEnablingAdFiltering = new FlagsOverridingAdFiltering(true); - mFledgeAuthorizationFilterSpy = ExtendedMockito.spy( new FledgeAuthorizationFilter( @@ -265,8 +262,8 @@ public final class FrequencyCapFilteringE2ETest extends AdServicesExtendedMockit mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDaoSpy, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClientMock, mDevContextFilterMock, mLightweightExecutorService, @@ -380,7 +377,7 @@ public final class FrequencyCapFilteringE2ETest extends AdServicesExtendedMockit // Bypass the permission check since it's enforced before the package name check doNothing() .when(mFledgeAuthorizationFilterSpy) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( mContextSpy, CommonFixture.TEST_PACKAGE_NAME_1, AD_SERVICES_API_CALLED__API_NAME__UPDATE_AD_COUNTER_HISTOGRAM); @@ -404,7 +401,7 @@ public final class FrequencyCapFilteringE2ETest extends AdServicesExtendedMockit verifyNoMoreInteractions(mFrequencyCapDaoSpy); verify(mFledgeAuthorizationFilterSpy) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( mContextSpy, CommonFixture.TEST_PACKAGE_NAME_1, AD_SERVICES_API_CALLED__API_NAME__UPDATE_AD_COUNTER_HISTOGRAM); @@ -425,8 +422,8 @@ public final class FrequencyCapFilteringE2ETest extends AdServicesExtendedMockit mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDaoSpy, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClientMock, mDevContextFilterMock, mLightweightExecutorService, @@ -488,8 +485,8 @@ public final class FrequencyCapFilteringE2ETest extends AdServicesExtendedMockit mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDaoSpy, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClientMock, mDevContextFilterMock, mLightweightExecutorService, @@ -746,8 +743,8 @@ public final class FrequencyCapFilteringE2ETest extends AdServicesExtendedMockit mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDaoSpy, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClientMock, mDevContextFilterMock, mLightweightExecutorService, @@ -862,8 +859,8 @@ public final class FrequencyCapFilteringE2ETest extends AdServicesExtendedMockit mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDaoSpy, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClientMock, mDevContextFilterMock, mLightweightExecutorService, diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/OnDeviceAdSelectionRunnerTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/OnDeviceAdSelectionRunnerTest.java index 56f4e55ae..ce83b09c8 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/OnDeviceAdSelectionRunnerTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/OnDeviceAdSelectionRunnerTest.java @@ -18,6 +18,7 @@ package com.android.adservices.service.adselection; import static android.adservices.adselection.CustomAudienceBiddingInfoFixture.DATA_VERSION_1; import static android.adservices.adselection.CustomAudienceBiddingInfoFixture.DATA_VERSION_2; +import static android.adservices.adselection.SignedContextualAdsFixture.signContextualAds; import static android.adservices.common.AdServicesStatusUtils.ILLEGAL_STATE_BACKGROUND_CALLER_ERROR_MESSAGE; import static android.adservices.common.AdServicesStatusUtils.RATE_LIMIT_REACHED_ERROR_MESSAGE; import static android.adservices.common.AdServicesStatusUtils.STATUS_BACKGROUND_CALLER; @@ -44,6 +45,7 @@ import static com.android.adservices.service.adselection.AdSelectionRunner.ERROR import static com.android.adservices.service.adselection.AdSelectionRunner.ERROR_NO_WINNING_AD_FOUND; import static com.android.adservices.service.adselection.AdSelectionRunner.ON_DEVICE_AUCTION_KILL_SWITCH_ENABLED; import static com.android.adservices.service.adselection.AdSelectionScriptEngine.NUM_BITS_STOCHASTIC_ROUNDING; +import static com.android.adservices.service.adselection.signature.ProtectedAudienceSignatureManager.PUBLIC_TEST_KEY_STRING; import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.BIDDING_STAGE_END_TIMESTAMP; import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.BIDDING_STAGE_START_TIMESTAMP; import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.DB_AD_SELECTION_FILE_SIZE; @@ -128,6 +130,8 @@ import com.android.adservices.data.common.DBAdData; import com.android.adservices.data.customaudience.CustomAudienceDao; import com.android.adservices.data.customaudience.CustomAudienceDatabase; import com.android.adservices.data.customaudience.DBCustomAudience; +import com.android.adservices.data.encryptionkey.EncryptionKeyDao; +import com.android.adservices.data.enrollment.EnrollmentDao; import com.android.adservices.service.Flags; import com.android.adservices.service.FlagsFactory; import com.android.adservices.service.common.AdSelectionServiceFilter; @@ -141,6 +145,8 @@ import com.android.adservices.service.common.httpclient.AdServicesHttpClientResp import com.android.adservices.service.common.httpclient.AdServicesHttpsClient; import com.android.adservices.service.consent.ConsentManager; import com.android.adservices.service.devapi.DevContext; +import com.android.adservices.service.encryptionkey.EncryptionKey; +import com.android.adservices.service.enrollment.EnrollmentData; import com.android.adservices.service.exception.FilterException; import com.android.adservices.service.stats.AdSelectionExecutionLogger; import com.android.adservices.service.stats.AdServicesLogger; @@ -194,8 +200,6 @@ import java.util.stream.Collectors; * mocked and provide expected mock responses when invoked with desired input */ public class OnDeviceAdSelectionRunnerTest { - private static final String TAG = OnDeviceAdSelectionRunnerTest.class.getName(); - private static final AdTechIdentifier BUYER_1 = AdSelectionConfigFixture.BUYER_1; private static final AdTechIdentifier BUYER_2 = AdSelectionConfigFixture.BUYER_2; private static final Long AD_SELECTION_ID = 1234L; @@ -234,6 +238,8 @@ public class OnDeviceAdSelectionRunnerTest { @Mock private AdCounterHistogramUpdater mAdCounterHistogramUpdaterMock; @Mock private DebugReporting mDebugReportingMock; @Mock private DebugReportSenderStrategy mDebugReportSenderMock; + @Mock private EnrollmentDao mEnrollmentDaoMock; + @Mock private EncryptionKeyDao mEncryptionKeyDaoMock; @Captor ArgumentCaptor<RunAdSelectionProcessReportedStats> @@ -396,6 +402,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -501,6 +509,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -673,6 +683,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -798,6 +810,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -945,6 +959,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -1145,6 +1161,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -1245,6 +1263,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -1359,6 +1379,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -1434,6 +1456,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -1500,6 +1524,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -1563,6 +1589,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -1617,6 +1645,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -1767,6 +1797,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -1886,6 +1918,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -1974,7 +2008,7 @@ public class OnDeviceAdSelectionRunnerTest { // Getting ScoringOutcome-ForBuyerX corresponding to each BiddingOutcome-forBuyerX // In this case assuming we get an empty result when(mMockAdsScoreGenerator.runAdScoring(mAdBiddingOutcomeList, adSelectionConfig)) - .thenReturn((FluentFuture.from(Futures.immediateFuture(Collections.EMPTY_LIST)))); + .thenReturn((FluentFuture.from(Futures.immediateFuture(Collections.emptyList())))); mockAdSelectionExecutionLoggerSpyWithFailedAdSelectionDuringScoring(); @@ -1983,6 +2017,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -2085,6 +2121,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -2190,6 +2228,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -2317,6 +2357,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -2387,6 +2429,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -2482,6 +2526,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -2628,6 +2674,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -2715,6 +2763,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -2765,6 +2815,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -2849,6 +2901,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -2926,6 +2980,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -2981,6 +3037,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mMockHttpClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -3022,7 +3080,7 @@ public class OnDeviceAdSelectionRunnerTest { mAdSelectionConfigBuilder .build() .cloneToBuilder() - .setCustomAudienceBuyers(Collections.EMPTY_LIST) + .setCustomAudienceBuyers(Collections.emptyList()) .setBuyerSignedContextualAds(signedContextualAdsMap) .build(); @@ -3055,6 +3113,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -3093,7 +3153,7 @@ public class OnDeviceAdSelectionRunnerTest { mAdSelectionConfigBuilder .build() .cloneToBuilder() - .setCustomAudienceBuyers(Collections.EMPTY_LIST) + .setCustomAudienceBuyers(Collections.emptyList()) // Despite populating Contextual Ads, they will be removed .setBuyerSignedContextualAds(createContextualAds()) .build(); @@ -3121,6 +3181,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -3163,7 +3225,7 @@ public class OnDeviceAdSelectionRunnerTest { mAdSelectionConfigBuilder .build() .cloneToBuilder() - .setCustomAudienceBuyers(Collections.EMPTY_LIST) + .setCustomAudienceBuyers(Collections.emptyList()) .setBuyerSignedContextualAds(contextualAdsMap) .build(); @@ -3191,6 +3253,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -3215,14 +3279,14 @@ public class OnDeviceAdSelectionRunnerTest { .thenReturn(contextualAdsMap.get(CommonFixture.VALID_BUYER_1)); when(mMockAdFilterer.filterContextualAds(contextualAdsMap.get(CommonFixture.VALID_BUYER_2))) .thenReturn( - SignedContextualAdsFixture.aSignedContextualAdBuilder() - .setBuyer(CommonFixture.VALID_BUYER_2) - .setDecisionLogicUri( - contextualAdsMap - .get(CommonFixture.VALID_BUYER_2) - .getDecisionLogicUri()) - .setAdsWithBid(Collections.EMPTY_LIST) - .build()); + signContextualAds( + SignedContextualAdsFixture.aContextualAdsWithEmptySignatureBuilder() + .setBuyer(CommonFixture.VALID_BUYER_2) + .setDecisionLogicUri( + contextualAdsMap + .get(CommonFixture.VALID_BUYER_2) + .getDecisionLogicUri()) + .setAdsWithBid(Collections.emptyList()))); invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME); verify(mMockAdsScoreGenerator) .runAdScoring( @@ -3237,7 +3301,7 @@ public class OnDeviceAdSelectionRunnerTest { .getAdsWithBid()); assertEquals( "The contextual ads should have been filtered for Buyer 2", - Collections.EMPTY_LIST, + Collections.emptyList(), mAdSelectionConfigArgumentCaptor .getValue() .getBuyerSignedContextualAds() @@ -3254,6 +3318,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -3343,6 +3409,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -3425,6 +3493,8 @@ public class OnDeviceAdSelectionRunnerTest { mContextSpy, mCustomAudienceDao, mAdSelectionEntryDaoSpy, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, mAdServicesHttpsClient, mLightweightExecutorService, mBackgroundExecutorService, @@ -3881,23 +3951,39 @@ public class OnDeviceAdSelectionRunnerTest { AdTechIdentifier buyer1 = CommonFixture.VALID_BUYER_1; SignedContextualAds contextualAds1 = - SignedContextualAdsFixture.generateSignedContextualAds( - buyer1, ImmutableList.of(100.0, 200.0, 300.0)) - .setDecisionLogicUri( - CommonFixture.getUri(BUYER_1, BUYER_BIDDING_LOGIC_URI_PATH)) - .build(); + signContextualAds( + SignedContextualAdsFixture.aContextualAdsWithEmptySignatureBuilder( + buyer1, ImmutableList.of(100.0, 200.0, 300.0)) + .setDecisionLogicUri( + CommonFixture.getUri( + BUYER_1, BUYER_BIDDING_LOGIC_URI_PATH))); AdTechIdentifier buyer2 = CommonFixture.VALID_BUYER_2; SignedContextualAds contextualAds2 = - SignedContextualAdsFixture.generateSignedContextualAds( - buyer2, ImmutableList.of(400.0, 500.0)) - .setDecisionLogicUri( - CommonFixture.getUri(BUYER_2, BUYER_BIDDING_LOGIC_URI_PATH)) - .build(); + signContextualAds( + SignedContextualAdsFixture.aContextualAdsWithEmptySignatureBuilder( + buyer2, ImmutableList.of(400.0, 500.0)) + .setDecisionLogicUri( + CommonFixture.getUri( + BUYER_2, BUYER_BIDDING_LOGIC_URI_PATH))); buyerContextualAds.put(buyer1, contextualAds1); buyerContextualAds.put(buyer2, contextualAds2); + for (AdTechIdentifier adTech : buyerContextualAds.keySet()) { + doReturn(new EnrollmentData.Builder().setEnrollmentId(adTech.toString()).build()) + .when(mEnrollmentDaoMock) + .getEnrollmentDataForFledgeByAdTechIdentifier(adTech); + doReturn( + Collections.singletonList( + new EncryptionKey.Builder() + .setBody(PUBLIC_TEST_KEY_STRING) + .build())) + .when(mEncryptionKeyDaoMock) + .getEncryptionKeyFromEnrollmentIdAndKeyType( + adTech.toString(), EncryptionKey.KeyType.SIGNING); + } + return buyerContextualAds; } } diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/ReportAndRegisterEventE2ETest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/ReportAndRegisterEventE2ETest.java index f564af8ec..d1b3cd9dc 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/ReportAndRegisterEventE2ETest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/ReportAndRegisterEventE2ETest.java @@ -66,18 +66,16 @@ import com.android.adservices.data.DbTestUtil; import com.android.adservices.data.adselection.AdSelectionDatabase; import com.android.adservices.data.adselection.AdSelectionDebugReportDao; import com.android.adservices.data.adselection.AdSelectionEntryDao; -import com.android.adservices.data.adselection.AdSelectionServerDatabase; import com.android.adservices.data.adselection.AppInstallDao; import com.android.adservices.data.adselection.CustomAudienceSignals; import com.android.adservices.data.adselection.DBAdSelection; import com.android.adservices.data.adselection.DBRegisteredAdInteraction; -import com.android.adservices.data.adselection.EncryptionContextDao; -import com.android.adservices.data.adselection.EncryptionKeyDao; import com.android.adservices.data.adselection.FrequencyCapDao; import com.android.adservices.data.adselection.SharedStorageDatabase; import com.android.adservices.data.customaudience.CustomAudienceDao; import com.android.adservices.data.customaudience.CustomAudienceDatabase; import com.android.adservices.data.customaudience.DBCustomAudience; +import com.android.adservices.data.encryptionkey.EncryptionKeyDao; import com.android.adservices.data.enrollment.EnrollmentDao; import com.android.adservices.data.measurement.DatastoreException; import com.android.adservices.data.measurement.DatastoreManager; @@ -163,7 +161,7 @@ public class ReportAndRegisterEventE2ETest { private AppInstallDao mAppInstallDao; private FrequencyCapDao mFrequencyCapDao; private EncryptionKeyDao mEncryptionKeyDao; - private EncryptionContextDao mEncryptionContextDao; + private EnrollmentDao mEnrollmentDao; private AdFilteringFeatureFactory mAdFilteringFeatureFactory; @Mock private AdSelectionServiceFilter mAdSelectionServiceFilterMock; @Mock private ObliviousHttpEncryptor mObliviousHttpEncryptorMock; @@ -284,13 +282,8 @@ public class ReportAndRegisterEventE2ETest { mAppInstallDao = sharedDb.appInstallDao(); mFrequencyCapDao = sharedDb.frequencyCapDao(); - AdSelectionServerDatabase serverDb = - Room.inMemoryDatabaseBuilder( - ApplicationProvider.getApplicationContext(), - AdSelectionServerDatabase.class) - .build(); - mEncryptionContextDao = serverDb.encryptionContextDao(); - mEncryptionKeyDao = serverDb.encryptionKeyDao(); + mEncryptionKeyDao = EncryptionKeyDao.getInstance(CONTEXT); + mEnrollmentDao = EnrollmentDao.getInstance(CONTEXT); mAdFilteringFeatureFactory = new AdFilteringFeatureFactory(mAppInstallDao, mFrequencyCapDao, mFlags); @@ -986,8 +979,8 @@ public class ReportAndRegisterEventE2ETest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mHttpsClientSpy, mDevContextFilterMock, mLightweightExecutorService, diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/ReportImpressionScriptEngineTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/ReportImpressionScriptEngineTest.java index 9e2771e10..9ebccfd10 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/ReportImpressionScriptEngineTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/ReportImpressionScriptEngineTest.java @@ -267,6 +267,7 @@ public class ReportImpressionScriptEngineTest { } @Test + @FlakyTest(bugId = 317817375) public void testReportResultSuccessfulCaseWithCallingRegisterAdBeacon() throws Exception { String jsScript = "function reportResult(ad_selection_config, render_uri, bid, contextual_signals) " diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/TrustedServerAdSelectionRunnerTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/TrustedServerAdSelectionRunnerTest.java index f0cfbaa60..d3385c13e 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/TrustedServerAdSelectionRunnerTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/TrustedServerAdSelectionRunnerTest.java @@ -50,6 +50,8 @@ import com.android.adservices.data.adselection.AdSelectionEntryDao; import com.android.adservices.data.adselection.DBAdSelection; import com.android.adservices.data.customaudience.CustomAudienceDao; import com.android.adservices.data.customaudience.DBCustomAudience; +import com.android.adservices.data.encryptionkey.EncryptionKeyDao; +import com.android.adservices.data.enrollment.EnrollmentDao; import com.android.adservices.service.Flags; import com.android.adservices.service.FlagsFactory; import com.android.adservices.service.adselection.AdSelectionRunner.AdSelectionOrchestrationResult; @@ -150,6 +152,8 @@ public class TrustedServerAdSelectionRunnerTest { @Mock private AdServicesLogger mAdServicesLoggerSpy; @Mock private CustomAudienceDao mCustomAudienceDao; @Mock private AdSelectionEntryDao mAdSelectionEntryDao; + @Mock private EnrollmentDao mEnrollmentDaoMock; + @Mock private EncryptionKeyDao mEncryptionKeyDaoMock; @Mock private JsFetcher mJsFetcher; @Mock private CustomAudienceDevOverridesHelper mCustomAudienceDevOverridesHelper; @Mock private AdSelectionIdGenerator mMockAdSelectionIdGenerator; @@ -215,6 +219,8 @@ public class TrustedServerAdSelectionRunnerTest { mContext, mCustomAudienceDao, mAdSelectionEntryDao, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, sLightweightExecutorService, sBackgroundExecutorService, sScheduledExecutor, @@ -276,6 +282,8 @@ public class TrustedServerAdSelectionRunnerTest { mContext, mCustomAudienceDao, mAdSelectionEntryDao, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, sLightweightExecutorService, sBackgroundExecutorService, sScheduledExecutor, @@ -339,6 +347,8 @@ public class TrustedServerAdSelectionRunnerTest { mContext, mCustomAudienceDao, mAdSelectionEntryDao, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, sLightweightExecutorService, sBackgroundExecutorService, sScheduledExecutor, @@ -408,6 +418,8 @@ public class TrustedServerAdSelectionRunnerTest { mContext, mCustomAudienceDao, mAdSelectionEntryDao, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, sLightweightExecutorService, sBackgroundExecutorService, sScheduledExecutor, @@ -461,6 +473,8 @@ public class TrustedServerAdSelectionRunnerTest { mContext, mCustomAudienceDao, mAdSelectionEntryDao, + mEncryptionKeyDaoMock, + mEnrollmentDaoMock, sLightweightExecutorService, sBackgroundExecutorService, sScheduledExecutor, diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/signature/BinarySerializerSignedContextualAdsTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/signature/BinarySerializerSignedContextualAdsTest.java index ad751d71d..1533ff53a 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/signature/BinarySerializerSignedContextualAdsTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/signature/BinarySerializerSignedContextualAdsTest.java @@ -16,8 +16,6 @@ package com.android.adservices.service.adselection.signature; -import static android.adservices.adselection.SignedContextualAdsFixture.PLACEHOLDER_SIGNATURE; - import static com.google.common.truth.Truth.assertThat; import android.adservices.adselection.AdWithBid; @@ -42,6 +40,8 @@ import java.util.Set; import java.util.stream.Collectors; public class BinarySerializerSignedContextualAdsTest { + public static final byte[] TEST_SIGNATURE = new byte[] {0, 1, 2}; + private SignedContextualAdsHashUtil mSerializer; @Before @@ -131,7 +131,7 @@ public class BinarySerializerSignedContextualAdsTest { .setRenderUri(Uri.parse(adRenderUri)) .build(), Double.parseDouble(bid)))) - .setSignature(PLACEHOLDER_SIGNATURE) + .setSignature(TEST_SIGNATURE) .build(); byte[] serialized = new SignedContextualAdsHashUtil().serialize(contextualAds); @@ -203,7 +203,7 @@ public class BinarySerializerSignedContextualAdsTest { .setRenderUri(Uri.parse(adRenderUri)) .build(), Double.parseDouble(bid)))) - .setSignature(PLACEHOLDER_SIGNATURE) + .setSignature(TEST_SIGNATURE) .build(); byte[] serialized = new SignedContextualAdsHashUtil().serialize(contextualAds); diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/signature/ProtectedAudienceSignatureManagerTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/signature/ProtectedAudienceSignatureManagerTest.java index 774da48a8..a44359664 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/signature/ProtectedAudienceSignatureManagerTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/signature/ProtectedAudienceSignatureManagerTest.java @@ -16,14 +16,17 @@ package com.android.adservices.service.adselection.signature; +import static android.adservices.adselection.SignedContextualAdsFixture.aSignedContextualAds; + +import static com.android.adservices.service.adselection.signature.ProtectedAudienceSignatureManager.PUBLIC_TEST_KEY_STRING; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.doReturn; +import android.adservices.adselection.SignedContextualAds; import android.adservices.common.AdTechIdentifier; -import android.content.Context; -import androidx.test.core.app.ApplicationProvider; import com.android.adservices.data.encryptionkey.EncryptionKeyDao; import com.android.adservices.data.enrollment.EnrollmentDao; @@ -35,39 +38,102 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Base64; import java.util.Collections; import java.util.List; public class ProtectedAudienceSignatureManagerTest { - private Context mContext = ApplicationProvider.getApplicationContext(); - @Mock private EnrollmentDao mEnrollmentDao; - @Mock private EncryptionKeyDao mEncryptionKeyDao; + @Mock private EnrollmentDao mEnrollmentDaoMock; + @Mock private EncryptionKeyDao mEncryptionKeyDaoMock; + private ProtectedAudienceSignatureManager mNoOpSignatureManager; + private ProtectedAudienceSignatureManager mSignatureManager; @Before public void setup() { MockitoAnnotations.initMocks(this); + SignatureVerifier noOpSignatureVerifier = + new SignatureVerifier() { + @Override + public boolean verify(byte[] publicKey, byte[] data, byte[] signature) { + return true; + } + }; + boolean enrollmentEnabled = false; + mSignatureManager = + new ProtectedAudienceSignatureManager( + mEnrollmentDaoMock, mEncryptionKeyDaoMock, enrollmentEnabled); + mNoOpSignatureManager = + new ProtectedAudienceSignatureManager( + mEnrollmentDaoMock, mEncryptionKeyDaoMock, noOpSignatureVerifier); + } + + @Test + public void testVerifySignature_validSignature_returnTrue() { + SignedContextualAds signedContextualAds = aSignedContextualAds(); + String enrollmentId = "enrollment1"; + AdTechIdentifier buyer = signedContextualAds.getBuyer(); + + doReturn(new EnrollmentData.Builder().setEnrollmentId(enrollmentId).build()) + .when(mEnrollmentDaoMock) + .getEnrollmentDataForFledgeByAdTechIdentifier(buyer); + doReturn( + Collections.singletonList( + new EncryptionKey.Builder() + .setBody(PUBLIC_TEST_KEY_STRING) + .build())) + .when(mEncryptionKeyDaoMock) + .getEncryptionKeyFromEnrollmentIdAndKeyType( + enrollmentId, EncryptionKey.KeyType.SIGNING); + + boolean isVerified = mSignatureManager.isVerified(buyer, signedContextualAds); + + assertThat(isVerified).isTrue(); + } + + @Test + public void testVerifySignature_invalidSignature_returnFalse() { + byte[] invalidSignature = new byte[] {1, 2, 3}; + SignedContextualAds signedContextualAds = + aSignedContextualAds().cloneToBuilder().setSignature(invalidSignature).build(); + String enrollmentId = "enrollment1"; + AdTechIdentifier buyer = signedContextualAds.getBuyer(); + + doReturn(new EnrollmentData.Builder().setEnrollmentId(enrollmentId).build()) + .when(mEnrollmentDaoMock) + .getEnrollmentDataForFledgeByAdTechIdentifier(buyer); + doReturn( + Collections.singletonList( + new EncryptionKey.Builder() + .setBody(PUBLIC_TEST_KEY_STRING) + .build())) + .when(mEncryptionKeyDaoMock) + .getEncryptionKeyFromEnrollmentIdAndKeyType( + enrollmentId, EncryptionKey.KeyType.SIGNING); + + boolean isVerified = mSignatureManager.isVerified(buyer, signedContextualAds); + + assertThat(isVerified).isFalse(); } @Test public void testFetchKeys_validAdTech_success() { AdTechIdentifier adTech = AdTechIdentifier.fromString("example.com"); - String publicKey = "test-key"; + byte[] publicKeyBytes = new byte[] {1, 2, 3, 4, 5}; + String publicKey = Base64.getEncoder().encodeToString(publicKeyBytes); String enrollmentId = "enrollment1"; doReturn(new EnrollmentData.Builder().setEnrollmentId(enrollmentId).build()) - .when(mEnrollmentDao) + .when(mEnrollmentDaoMock) .getEnrollmentDataForFledgeByAdTechIdentifier(adTech); doReturn(Collections.singletonList(new EncryptionKey.Builder().setBody(publicKey).build())) - .when(mEncryptionKeyDao) + .when(mEncryptionKeyDaoMock) .getEncryptionKeyFromEnrollmentIdAndKeyType( enrollmentId, EncryptionKey.KeyType.SIGNING); - ProtectedAudienceSignatureManager signatureManager = - new ProtectedAudienceSignatureManager(mContext, mEnrollmentDao, mEncryptionKeyDao); - - List<String> signingKeys = signatureManager.fetchPublicKeyForAdTech(adTech); + List<byte[]> signingKeys = mNoOpSignatureManager.fetchPublicKeyForAdTech(adTech); - assertThat(signingKeys).isEqualTo(Collections.singletonList(publicKey)); + assertThat(signingKeys.size()).isEqualTo(1); + assertThat(signingKeys.get(0)).isEqualTo(publicKeyBytes); } @Test @@ -77,8 +143,10 @@ public class ProtectedAudienceSignatureManagerTest { EnrollmentData enrollment = new EnrollmentData.Builder().setEnrollmentId(enrollmentId).build(); - String publicKey1 = "test-key1"; - String publicKey2 = "test-key2"; + byte[] publicKeyBytes1 = new byte[] {1, 2, 3, 4, 5}; + String publicKey1 = Base64.getEncoder().encodeToString(publicKeyBytes1); + byte[] publicKeyBytes2 = new byte[] {6, 7, 8, 9, 10}; + String publicKey2 = Base64.getEncoder().encodeToString(publicKeyBytes2); long expiration1 = 0L; long expiration2 = 1L; EncryptionKey encKey1 = @@ -88,32 +156,28 @@ public class ProtectedAudienceSignatureManagerTest { List<EncryptionKey> encKeysToPersistInReverseOrder = List.of(encKey2, encKey1); doReturn(enrollment) - .when(mEnrollmentDao) + .when(mEnrollmentDaoMock) .getEnrollmentDataForFledgeByAdTechIdentifier(adTech); doReturn(encKeysToPersistInReverseOrder) - .when(mEncryptionKeyDao) + .when(mEncryptionKeyDaoMock) .getEncryptionKeyFromEnrollmentIdAndKeyType( enrollmentId, EncryptionKey.KeyType.SIGNING); - ProtectedAudienceSignatureManager signatureManager = - new ProtectedAudienceSignatureManager(mContext, mEnrollmentDao, mEncryptionKeyDao); - - List<String> signingKeys = signatureManager.fetchPublicKeyForAdTech(adTech); + List<byte[]> signingKeys = mNoOpSignatureManager.fetchPublicKeyForAdTech(adTech); assertThat(signingKeys.size()).isEqualTo(2); - assertThat(signingKeys.get(0)).isEqualTo(publicKey1); - assertThat(signingKeys.get(1)).isEqualTo(publicKey2); + assertThat(signingKeys.get(0)).isEqualTo(publicKeyBytes1); + assertThat(signingKeys.get(1)).isEqualTo(publicKeyBytes2); } @Test public void testFetchKeys_notEnrolledAdTech_returnsEmptyList() { AdTechIdentifier adTech = AdTechIdentifier.fromString("example.com"); - doReturn(null).when(mEnrollmentDao).getEnrollmentDataForFledgeByAdTechIdentifier(adTech); - - ProtectedAudienceSignatureManager signatureManager = - new ProtectedAudienceSignatureManager(mContext, mEnrollmentDao, mEncryptionKeyDao); + doReturn(null) + .when(mEnrollmentDaoMock) + .getEnrollmentDataForFledgeByAdTechIdentifier(adTech); - List<String> signingKeys = signatureManager.fetchPublicKeyForAdTech(adTech); + List<byte[]> signingKeys = mNoOpSignatureManager.fetchPublicKeyForAdTech(adTech); assertThat(signingKeys).isEqualTo(Collections.emptyList()); } @@ -122,13 +186,10 @@ public class ProtectedAudienceSignatureManagerTest { public void testFetchKeys_enrolledAdTechWithNullId_returnsEmptyList() { AdTechIdentifier adTech = AdTechIdentifier.fromString("example.com"); doReturn(new EnrollmentData.Builder().build()) - .when(mEnrollmentDao) + .when(mEnrollmentDaoMock) .getEnrollmentDataForFledgeByAdTechIdentifier(adTech); - ProtectedAudienceSignatureManager signatureManager = - new ProtectedAudienceSignatureManager(mContext, mEnrollmentDao, mEncryptionKeyDao); - - List<String> signingKeys = signatureManager.fetchPublicKeyForAdTech(adTech); + List<byte[]> signingKeys = mNoOpSignatureManager.fetchPublicKeyForAdTech(adTech); assertThat(signingKeys).isEqualTo(Collections.emptyList()); } diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/signature/SafeNoCopyByteArrayOutputStreamTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/signature/ThreadUnsafeByteArrayOutputStreamTest.java index 2c10dbb66..8f7622fce 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/signature/SafeNoCopyByteArrayOutputStreamTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/signature/ThreadUnsafeByteArrayOutputStreamTest.java @@ -21,7 +21,7 @@ import static com.google.common.truth.Truth.assertThat; import org.junit.Before; import org.junit.Test; -public class SafeNoCopyByteArrayOutputStreamTest { +public class ThreadUnsafeByteArrayOutputStreamTest { private ThreadUnsafeByteArrayOutputStream mOutputStream; @Before diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/AppManifestConfigCallTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/AppManifestConfigCallTest.java new file mode 100644 index 000000000..495b5c3c8 --- /dev/null +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/AppManifestConfigCallTest.java @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2023 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.common; + +import static com.android.adservices.service.common.AppManifestConfigCall.API_ATTRIBUTION; +import static com.android.adservices.service.common.AppManifestConfigCall.API_CUSTOM_AUDIENCES; +import static com.android.adservices.service.common.AppManifestConfigCall.API_TOPICS; +import static com.android.adservices.service.common.AppManifestConfigCall.API_UNSPECIFIED; +import static com.android.adservices.service.common.AppManifestConfigCall.INVALID_API_TEMPLATE; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_ALLOWED_APP_ALLOWS_ALL; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_ALLOWED_APP_ALLOWS_SPECIFIC_ID; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_ALLOWED_BY_DEFAULT_APP_DOES_NOT_HAVE_CONFIG; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_ALLOWED_BY_DEFAULT_APP_HAS_CONFIG_WITHOUT_API_SECTION; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_DISALLOWED_APP_CONFIG_PARSING_ERROR; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_DISALLOWED_APP_DOES_NOT_EXIST; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_DISALLOWED_APP_DOES_NOT_HAVE_CONFIG; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_DISALLOWED_APP_HAS_CONFIG_WITHOUT_API_SECTION; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_DISALLOWED_BY_APP; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_DISALLOWED_GENERIC_ERROR; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_UNSPECIFIED; +import static com.android.adservices.service.common.AppManifestConfigCall.isAllowed; +import static com.android.adservices.service.common.AppManifestConfigCall.resultToString; +import static com.android.adservices.service.common.AppManifestConfigCall.apiToString; + +import static org.junit.Assert.assertThrows; + +import com.android.adservices.common.AdServicesUnitTestCase; + +import org.junit.Test; + +public final class AppManifestConfigCallTest extends AdServicesUnitTestCase { + + private static final String PKG_NAME = "pkg.I.am"; + private static final String PKG_NAME2 = "or.not"; + + @Test + public void testInvalidConstructor() { + assertThrows( + NullPointerException.class, + () -> new AppManifestConfigCall(/* packageName= */ null, API_TOPICS)); + + IllegalArgumentException e = + assertThrows( + IllegalArgumentException.class, + () -> new AppManifestConfigCall(PKG_NAME, API_UNSPECIFIED)); + expect.withMessage("e.getMessage()") + .that(e) + .hasMessageThat() + .isEqualTo(String.format(INVALID_API_TEMPLATE, API_UNSPECIFIED)); + + e = + assertThrows( + IllegalArgumentException.class, + () -> new AppManifestConfigCall(PKG_NAME, -42)); + expect.withMessage("e.getMessage()") + .that(e) + .hasMessageThat() + .isEqualTo(String.format(INVALID_API_TEMPLATE, -42)); + } + + @Test + public void testValidConstructors() { + AppManifestConfigCall topics = new AppManifestConfigCall(PKG_NAME, API_TOPICS); + expect.withMessage("pkg on %s", topics).that(topics.packageName).isEqualTo(PKG_NAME); + expect.withMessage("api on %s", topics).that(topics.api).isEqualTo(API_TOPICS); + + AppManifestConfigCall customAudience = + new AppManifestConfigCall(PKG_NAME, API_CUSTOM_AUDIENCES); + expect.withMessage("pkg on %s", customAudience) + .that(customAudience.packageName) + .isEqualTo(PKG_NAME); + expect.withMessage("api on %s", customAudience) + .that(customAudience.api) + .isEqualTo(API_CUSTOM_AUDIENCES); + + AppManifestConfigCall attribution = new AppManifestConfigCall(PKG_NAME, API_ATTRIBUTION); + expect.withMessage("pkg on %s", attribution) + .that(attribution.packageName) + .isEqualTo(PKG_NAME); + expect.withMessage("api on %s", attribution) + .that(attribution.api) + .isEqualTo(API_ATTRIBUTION); + } + + @Test + public void testEqualsHashCode() { + AppManifestConfigCall pkg1api1 = new AppManifestConfigCall(PKG_NAME, API_TOPICS); + AppManifestConfigCall pkg1api2 = new AppManifestConfigCall(PKG_NAME, API_ATTRIBUTION); + AppManifestConfigCall pkg2api1 = new AppManifestConfigCall(PKG_NAME2, API_TOPICS); + AppManifestConfigCall pkg2api2 = new AppManifestConfigCall(PKG_NAME2, API_ATTRIBUTION); + + AppManifestConfigCall otherPkg1api1 = new AppManifestConfigCall(PKG_NAME, API_TOPICS); + AppManifestConfigCall otherPkg1api2 = new AppManifestConfigCall(PKG_NAME, API_ATTRIBUTION); + AppManifestConfigCall otherPkg2api1 = new AppManifestConfigCall(PKG_NAME2, API_TOPICS); + AppManifestConfigCall otherPkg2api2 = new AppManifestConfigCall(PKG_NAME2, API_ATTRIBUTION); + + expectEquals(pkg1api1, pkg1api1); + expectEquals(pkg1api1, otherPkg1api1); + expectEquals(pkg1api2, pkg1api2); + expectEquals(pkg1api2, otherPkg1api2); + expectEquals(pkg2api1, pkg2api1); + expectEquals(pkg2api1, otherPkg2api1); + expectEquals(pkg2api2, pkg2api2); + expectEquals(pkg2api2, otherPkg2api2); + + expectNotEquals(pkg1api1, pkg1api2); + expectNotEquals(pkg1api1, pkg2api1); + expectNotEquals(pkg1api1, pkg2api2); + + // Adds result + otherPkg1api1.result = RESULT_ALLOWED_APP_ALLOWS_ALL; + expectNotEquals(pkg1api1, otherPkg1api1); + pkg1api1.result = RESULT_ALLOWED_APP_ALLOWS_ALL; + expectEquals(pkg1api1, otherPkg1api1); + } + + private void expectEquals(AppManifestConfigCall call1, AppManifestConfigCall call2) { + expect.withMessage("equals()").that(call1).isEqualTo(call2); + expect.withMessage("equals()").that(call2).isEqualTo(call1); + expect.withMessage("hashcode(%s, %s)", call1, call2) + .that(call1.hashCode()) + .isEqualTo(call2.hashCode()); + } + + private void expectNotEquals(AppManifestConfigCall call1, AppManifestConfigCall call2) { + expect.withMessage("equals()").that(call1).isNotEqualTo(call2); + expect.withMessage("equals()").that(call2).isNotEqualTo(call1); + expect.withMessage("hashcode(%s, %s)", call1, call2) + .that(call1.hashCode()) + .isNotEqualTo(call2.hashCode()); + } + + @Test + public void testResultToString() { + expect.withMessage("resultToString(%s)", RESULT_UNSPECIFIED) + .that(resultToString(RESULT_UNSPECIFIED)) + .isEqualTo("UNSPECIFIED"); + expect.withMessage("resultToString(%s)", RESULT_ALLOWED_BY_DEFAULT_APP_DOES_NOT_HAVE_CONFIG) + .that(resultToString(RESULT_ALLOWED_BY_DEFAULT_APP_DOES_NOT_HAVE_CONFIG)) + .isEqualTo("ALLOWED_BY_DEFAULT_APP_DOES_NOT_HAVE_CONFIG"); + expect.withMessage( + "resultToString(%s)", + RESULT_ALLOWED_BY_DEFAULT_APP_HAS_CONFIG_WITHOUT_API_SECTION) + .that(resultToString(RESULT_ALLOWED_BY_DEFAULT_APP_HAS_CONFIG_WITHOUT_API_SECTION)) + .isEqualTo("ALLOWED_BY_DEFAULT_APP_HAS_CONFIG_WITHOUT_API_SECTION"); + expect.withMessage("resultToString(%s)", RESULT_ALLOWED_APP_ALLOWS_ALL) + .that(resultToString(RESULT_ALLOWED_APP_ALLOWS_ALL)) + .isEqualTo("ALLOWED_APP_ALLOWS_ALL"); + expect.withMessage("resultToString(%s)", RESULT_ALLOWED_APP_ALLOWS_SPECIFIC_ID) + .that(resultToString(RESULT_ALLOWED_APP_ALLOWS_SPECIFIC_ID)) + .isEqualTo("ALLOWED_APP_ALLOWS_SPECIFIC_ID"); + expect.withMessage("resultToString(%s)", RESULT_DISALLOWED_APP_DOES_NOT_EXIST) + .that(resultToString(RESULT_DISALLOWED_APP_DOES_NOT_EXIST)) + .isEqualTo("DISALLOWED_APP_DOES_NOT_EXIST"); + expect.withMessage("resultToString(%s)", RESULT_DISALLOWED_APP_CONFIG_PARSING_ERROR) + .that(resultToString(RESULT_DISALLOWED_APP_CONFIG_PARSING_ERROR)) + .isEqualTo("DISALLOWED_APP_CONFIG_PARSING_ERROR"); + expect.withMessage("resultToString(%s)", RESULT_DISALLOWED_APP_DOES_NOT_HAVE_CONFIG) + .that(resultToString(RESULT_DISALLOWED_APP_DOES_NOT_HAVE_CONFIG)) + .isEqualTo("DISALLOWED_APP_DOES_NOT_HAVE_CONFIG"); + expect.withMessage( + "resultToString(%s)", RESULT_DISALLOWED_APP_HAS_CONFIG_WITHOUT_API_SECTION) + .that(resultToString(RESULT_DISALLOWED_APP_HAS_CONFIG_WITHOUT_API_SECTION)) + .isEqualTo("DISALLOWED_APP_HAS_CONFIG_WITHOUT_API_SECTION"); + expect.withMessage("resultToString(%s)", RESULT_DISALLOWED_BY_APP) + .that(resultToString(RESULT_DISALLOWED_BY_APP)) + .isEqualTo("DISALLOWED_BY_APP"); + expect.withMessage("resultToString(%s)", RESULT_DISALLOWED_GENERIC_ERROR) + .that(resultToString(RESULT_DISALLOWED_GENERIC_ERROR)) + .isEqualTo("DISALLOWED_GENERIC_ERROR"); + expect.withMessage("resultToString(42)").that(resultToString(42)).isEqualTo("INVALID-42"); + } + + @Test + public void testApiToString() { + expect.withMessage("apiToString(%s)", API_UNSPECIFIED) + .that(apiToString(API_UNSPECIFIED)) + .isEqualTo("UNSPECIFIED"); + expect.withMessage("apiToString(%s)", API_TOPICS) + .that(apiToString(API_TOPICS)) + .isEqualTo("TOPICS"); + expect.withMessage("apiToString(%s)", API_CUSTOM_AUDIENCES) + .that(apiToString(API_CUSTOM_AUDIENCES)) + .isEqualTo("CUSTOM_AUDIENCES"); + expect.withMessage("apiToString(%s)", API_ATTRIBUTION) + .that(apiToString(API_ATTRIBUTION)) + .isEqualTo("ATTRIBUTION"); + expect.withMessage("apiToString(42)").that(apiToString(42)).isEqualTo("INVALID-42"); + } + + @Test + public void testIsAllowed() { + expect.withMessage("isAllowed(%s)", resultToString(RESULT_UNSPECIFIED)) + .that(isAllowed(RESULT_UNSPECIFIED)) + .isFalse(); + expect.withMessage( + "isAllowed(%s)", + resultToString(RESULT_ALLOWED_BY_DEFAULT_APP_DOES_NOT_HAVE_CONFIG)) + .that(isAllowed(RESULT_ALLOWED_BY_DEFAULT_APP_DOES_NOT_HAVE_CONFIG)) + .isTrue(); + expect.withMessage( + "isAllowed(%s)", + resultToString( + RESULT_ALLOWED_BY_DEFAULT_APP_HAS_CONFIG_WITHOUT_API_SECTION)) + .that(isAllowed(RESULT_ALLOWED_BY_DEFAULT_APP_HAS_CONFIG_WITHOUT_API_SECTION)) + .isTrue(); + expect.withMessage("isAllowed(%s)", resultToString(RESULT_ALLOWED_APP_ALLOWS_ALL)) + .that(isAllowed(RESULT_ALLOWED_APP_ALLOWS_ALL)) + .isTrue(); + expect.withMessage("isAllowed(%s)", resultToString(RESULT_ALLOWED_APP_ALLOWS_SPECIFIC_ID)) + .that(isAllowed(RESULT_ALLOWED_APP_ALLOWS_SPECIFIC_ID)) + .isTrue(); + expect.withMessage("isAllowed(%s)", resultToString(RESULT_DISALLOWED_APP_DOES_NOT_EXIST)) + .that(isAllowed(RESULT_DISALLOWED_APP_DOES_NOT_EXIST)) + .isFalse(); + expect.withMessage( + "isAllowed(%s)", resultToString(RESULT_DISALLOWED_APP_CONFIG_PARSING_ERROR)) + .that(isAllowed(RESULT_DISALLOWED_APP_CONFIG_PARSING_ERROR)) + .isFalse(); + expect.withMessage( + "isAllowed(%s)", resultToString(RESULT_DISALLOWED_APP_DOES_NOT_HAVE_CONFIG)) + .that(isAllowed(RESULT_DISALLOWED_APP_DOES_NOT_HAVE_CONFIG)) + .isFalse(); + expect.withMessage( + "isAllowed(%s)", + resultToString(RESULT_DISALLOWED_APP_HAS_CONFIG_WITHOUT_API_SECTION)) + .that(isAllowed(RESULT_DISALLOWED_APP_HAS_CONFIG_WITHOUT_API_SECTION)) + .isFalse(); + expect.withMessage("isAllowed(%s)", resultToString(RESULT_DISALLOWED_BY_APP)) + .that(isAllowed(RESULT_DISALLOWED_BY_APP)) + .isFalse(); + expect.withMessage("isAllowed(%s)", resultToString(RESULT_DISALLOWED_GENERIC_ERROR)) + .that(isAllowed(RESULT_DISALLOWED_GENERIC_ERROR)) + .isFalse(); + } +} diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/AppManifestConfigHelperTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/AppManifestConfigHelperTest.java index b9adf151e..592635a9f 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/AppManifestConfigHelperTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/AppManifestConfigHelperTest.java @@ -19,13 +19,16 @@ package com.android.adservices.service.common; import static com.android.adservices.mockito.ExtendedMockitoExpectations.mockIsAtLeastS; import static com.android.adservices.mockito.ExtendedMockitoExpectations.doNothingOnErrorLogUtilError; import static com.android.adservices.mockito.ExtendedMockitoExpectations.verifyErrorLogUtilError; -import static com.android.adservices.service.common.AppManifestConfigMetricsLoggerTest.appManifestConfigCall; -import static com.android.adservices.service.common.AppManifestConfigMetricsLoggerTest.APP_DOES_NOT_EXIST; -import static com.android.adservices.service.common.AppManifestConfigMetricsLoggerTest.APP_DOES_NOT_HAVE_CONFIG; -import static com.android.adservices.service.common.AppManifestConfigMetricsLoggerTest.APP_EXISTS; -import static com.android.adservices.service.common.AppManifestConfigMetricsLoggerTest.APP_HAS_CONFIG; -import static com.android.adservices.service.common.AppManifestConfigMetricsLoggerTest.ENABLED_BY_DEFAULT; -import static com.android.adservices.service.common.AppManifestConfigMetricsLoggerTest.NOT_ENABLED_BY_DEFAULT; +import static com.android.adservices.service.common.AppManifestConfigCall.API_ATTRIBUTION; +import static com.android.adservices.service.common.AppManifestConfigCall.API_CUSTOM_AUDIENCES; +import static com.android.adservices.service.common.AppManifestConfigCall.API_TOPICS; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_ALLOWED_APP_ALLOWS_ALL; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_ALLOWED_APP_ALLOWS_SPECIFIC_ID; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_ALLOWED_BY_DEFAULT_APP_DOES_NOT_HAVE_CONFIG; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_DISALLOWED_APP_CONFIG_PARSING_ERROR; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_DISALLOWED_APP_DOES_NOT_EXIST; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_DISALLOWED_APP_DOES_NOT_HAVE_CONFIG; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_DISALLOWED_BY_APP; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__APP_MANIFEST_CONFIG_PARSING_ERROR; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__COMMON; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; @@ -82,6 +85,9 @@ public final class AppManifestConfigHelperTest extends AdServicesExtendedMockito private static final String PACKAGE_NAME = "TEST_PACKAGE"; private static final String ENROLLMENT_ID = "ENROLLMENT_ID"; + // Constants for generic allowed / disallowed calls - the "type" doesn't matter + private static final int RESULT_ALLOWED = RESULT_ALLOWED_APP_ALLOWS_ALL; + @Mock private AppManifestConfig mMockAppManifestConfig; @Mock private AppManifestIncludesSdkLibraryConfig mMockSdkLibraryConfig; @Mock private Context mMockContext; @@ -106,14 +112,14 @@ public final class AppManifestConfigHelperTest extends AdServicesExtendedMockito public void testIsAllowedAttributionAccess_sPlus() throws Exception { mockGetPropertySucceeds(PACKAGE_NAME, AD_SERVICES_CONFIG_PROPERTY, RESOURCE_ID); mockAppManifestConfigParserGetConfigSucceeds(); - mockIsAllowedAttributionAccess(ENROLLMENT_ID, true); + mockIsAllowedAttributionAccess(ENROLLMENT_ID, RESULT_ALLOWED); assertWithMessage("isAllowedAttributionAccess(ctx, %s, %s)", PACKAGE_NAME, ENROLLMENT_ID) .that( AppManifestConfigHelper.isAllowedAttributionAccess( PACKAGE_NAME, ENROLLMENT_ID)) .isTrue(); - verifyLogUsage(APP_EXISTS, APP_HAS_CONFIG, NOT_ENABLED_BY_DEFAULT); + verifyLogUsage(API_ATTRIBUTION, RESULT_ALLOWED); } @Test @@ -121,14 +127,14 @@ public final class AppManifestConfigHelperTest extends AdServicesExtendedMockito mockSdkLevelR(); mockGetAssetSucceeds(PACKAGE_NAME, RESOURCE_ID); mockAppManifestConfigParserGetConfigSucceeds(); - mockIsAllowedAttributionAccess(ENROLLMENT_ID, true); + mockIsAllowedAttributionAccess(ENROLLMENT_ID, RESULT_ALLOWED); assertWithMessage("isAllowedAttributionAccess(ctx, %s, %s)", PACKAGE_NAME, ENROLLMENT_ID) .that( AppManifestConfigHelper.isAllowedAttributionAccess( PACKAGE_NAME, ENROLLMENT_ID)) .isTrue(); - verifyLogUsage(APP_EXISTS, APP_HAS_CONFIG, NOT_ENABLED_BY_DEFAULT); + verifyLogUsage(API_ATTRIBUTION, RESULT_ALLOWED); } @Test @@ -136,7 +142,7 @@ public final class AppManifestConfigHelperTest extends AdServicesExtendedMockito public void testIsAllowedCustomAudiencesAccess_sPlus() throws Exception { mockGetPropertySucceeds(PACKAGE_NAME, AD_SERVICES_CONFIG_PROPERTY, RESOURCE_ID); mockAppManifestConfigParserGetConfigSucceeds(); - mockIsAllowedCustomAudiencesAccess(ENROLLMENT_ID, true); + mockIsAllowedCustomAudiencesAccess(ENROLLMENT_ID, RESULT_ALLOWED); assertWithMessage( "isAllowedCustomAudiencesAccess(ctx, %s, %s)", PACKAGE_NAME, ENROLLMENT_ID) .that( @@ -144,7 +150,7 @@ public final class AppManifestConfigHelperTest extends AdServicesExtendedMockito PACKAGE_NAME, ENROLLMENT_ID)) .isTrue(); - verifyLogUsage(APP_EXISTS, APP_HAS_CONFIG, NOT_ENABLED_BY_DEFAULT); + verifyLogUsage(API_CUSTOM_AUDIENCES, RESULT_ALLOWED); } @Test @@ -152,7 +158,7 @@ public final class AppManifestConfigHelperTest extends AdServicesExtendedMockito mockSdkLevelR(); mockGetAssetSucceeds(PACKAGE_NAME, RESOURCE_ID); mockAppManifestConfigParserGetConfigSucceeds(); - mockIsAllowedCustomAudiencesAccess(ENROLLMENT_ID, true); + mockIsAllowedCustomAudiencesAccess(ENROLLMENT_ID, RESULT_ALLOWED); assertWithMessage( "isAllowedCustomAudiencesAccess(ctx, %s, %s)", PACKAGE_NAME, ENROLLMENT_ID) .that( @@ -160,7 +166,7 @@ public final class AppManifestConfigHelperTest extends AdServicesExtendedMockito PACKAGE_NAME, ENROLLMENT_ID)) .isTrue(); - verifyLogUsage(APP_EXISTS, APP_HAS_CONFIG, NOT_ENABLED_BY_DEFAULT); + verifyLogUsage(API_CUSTOM_AUDIENCES, RESULT_ALLOWED); } @Test @@ -285,7 +291,9 @@ public final class AppManifestConfigHelperTest extends AdServicesExtendedMockito } mockAppManifestConfigParserGetConfigSucceeds(); mockContainsSdk(ENROLLMENT_ID, containsSdk); - mockIsAllowedTopicsAccess(ENROLLMENT_ID, topicsAllowed); + int result = + expectedAllowed ? RESULT_ALLOWED_APP_ALLOWS_SPECIFIC_ID : RESULT_DISALLOWED_BY_APP; + mockIsAllowedTopicsAccess(ENROLLMENT_ID, result); assertWithMessage("isAllowedTopicsAccess(ctx, %s, %s)", PACKAGE_NAME, ENROLLMENT_ID) .that( AppManifestConfigHelper.isAllowedTopicsAccess( @@ -294,7 +302,7 @@ public final class AppManifestConfigHelperTest extends AdServicesExtendedMockito ENROLLMENT_ID)) .isEqualTo(expectedAllowed); - verifyLogUsage(APP_EXISTS, APP_HAS_CONFIG, NOT_ENABLED_BY_DEFAULT); + verifyLogUsage(API_TOPICS, result); } @Test @@ -306,7 +314,7 @@ public final class AppManifestConfigHelperTest extends AdServicesExtendedMockito assertNoAccessAllowed(); verifyErrorLogUtilErrorLogged(e, times(4)); // Called once for each API - verifyLogUsage(APP_EXISTS, APP_HAS_CONFIG, NOT_ENABLED_BY_DEFAULT, times(4)); + verifyLogUsageForAllApis(RESULT_DISALLOWED_APP_CONFIG_PARSING_ERROR); } @Test @@ -318,7 +326,7 @@ public final class AppManifestConfigHelperTest extends AdServicesExtendedMockito assertNoAccessAllowed(); verifyErrorLogUtilErrorLogged(e, times(4)); // Called once for each API - verifyLogUsage(APP_EXISTS, APP_HAS_CONFIG, NOT_ENABLED_BY_DEFAULT, times(4)); + verifyLogUsageForAllApis(RESULT_DISALLOWED_APP_CONFIG_PARSING_ERROR); } @Test @@ -332,7 +340,7 @@ public final class AppManifestConfigHelperTest extends AdServicesExtendedMockito assertNoAccessAllowed(); verifyErrorLogUtilErrorLogged(e, times(4)); // Called once for each API - verifyLogUsage(APP_EXISTS, APP_HAS_CONFIG, ENABLED_BY_DEFAULT, times(4)); + verifyLogUsageForAllApis(RESULT_DISALLOWED_APP_CONFIG_PARSING_ERROR); } @Test @@ -346,7 +354,7 @@ public final class AppManifestConfigHelperTest extends AdServicesExtendedMockito assertNoAccessAllowed(); verifyErrorLogUtilErrorLogged(e, times(4)); // Called once for each API - verifyLogUsage(APP_EXISTS, APP_HAS_CONFIG, ENABLED_BY_DEFAULT, times(4)); + verifyLogUsageForAllApis(RESULT_DISALLOWED_APP_CONFIG_PARSING_ERROR); } @Test @@ -355,8 +363,7 @@ public final class AppManifestConfigHelperTest extends AdServicesExtendedMockito assertNoAccessAllowed(); - verifyLogUsage( - APP_DOES_NOT_EXIST, APP_DOES_NOT_HAVE_CONFIG, NOT_ENABLED_BY_DEFAULT, times(4)); + verifyLogUsageForAllApis(RESULT_DISALLOWED_APP_DOES_NOT_EXIST); } @Test @@ -366,7 +373,7 @@ public final class AppManifestConfigHelperTest extends AdServicesExtendedMockito assertNoAccessAllowed(); - verifyLogUsage(APP_DOES_NOT_EXIST, APP_DOES_NOT_HAVE_CONFIG, ENABLED_BY_DEFAULT, times(4)); + verifyLogUsageForAllApis(RESULT_DISALLOWED_APP_DOES_NOT_EXIST); } @Test @@ -377,7 +384,7 @@ public final class AppManifestConfigHelperTest extends AdServicesExtendedMockito assertNoAccessAllowed(); - verifyLogUsage(APP_EXISTS, APP_DOES_NOT_HAVE_CONFIG, NOT_ENABLED_BY_DEFAULT, times(4)); + verifyLogUsageForAllApis(RESULT_DISALLOWED_APP_DOES_NOT_HAVE_CONFIG); } @Test @@ -388,7 +395,7 @@ public final class AppManifestConfigHelperTest extends AdServicesExtendedMockito assertNoAccessAllowed(); - verifyLogUsage(APP_EXISTS, APP_DOES_NOT_HAVE_CONFIG, NOT_ENABLED_BY_DEFAULT, times(4)); + verifyLogUsageForAllApis(RESULT_DISALLOWED_APP_DOES_NOT_HAVE_CONFIG); } @Test @@ -401,7 +408,7 @@ public final class AppManifestConfigHelperTest extends AdServicesExtendedMockito assertAllAccessAllowed(); - verifyLogUsage(APP_EXISTS, APP_DOES_NOT_HAVE_CONFIG, ENABLED_BY_DEFAULT, times(4)); + verifyLogUsageForAllApis(RESULT_ALLOWED_BY_DEFAULT_APP_DOES_NOT_HAVE_CONFIG); } @Test @@ -414,7 +421,7 @@ public final class AppManifestConfigHelperTest extends AdServicesExtendedMockito assertAllAccessAllowed(); - verifyLogUsage(APP_EXISTS, APP_DOES_NOT_HAVE_CONFIG, ENABLED_BY_DEFAULT, times(4)); + verifyLogUsageForAllApis(RESULT_ALLOWED_BY_DEFAULT_APP_DOES_NOT_HAVE_CONFIG); } private void mockSdkLevelR() { @@ -485,16 +492,16 @@ public final class AppManifestConfigHelperTest extends AdServicesExtendedMockito return e; } - private void mockIsAllowedAttributionAccess(String partnerId, boolean value) { - when(mMockAppManifestConfig.isAllowedAttributionAccess(partnerId)).thenReturn(value); + private void mockIsAllowedAttributionAccess(String partnerId, int result) { + when(mMockAppManifestConfig.isAllowedAttributionAccess(partnerId)).thenReturn(result); } - private void mockIsAllowedCustomAudiencesAccess(String partnerId, boolean value) { - when(mMockAppManifestConfig.isAllowedCustomAudiencesAccess(partnerId)).thenReturn(value); + private void mockIsAllowedCustomAudiencesAccess(String partnerId, int result) { + when(mMockAppManifestConfig.isAllowedCustomAudiencesAccess(partnerId)).thenReturn(result); } - private void mockIsAllowedTopicsAccess(String partnerId, boolean value) { - when(mMockAppManifestConfig.isAllowedTopicsAccess(partnerId)).thenReturn(value); + private void mockIsAllowedTopicsAccess(String partnerId, int result) { + when(mMockAppManifestConfig.isAllowedTopicsAccess(partnerId)).thenReturn(result); } private void mockContainsSdk(String partnerId, boolean value) { @@ -549,9 +556,7 @@ public final class AppManifestConfigHelperTest extends AdServicesExtendedMockito expect.withMessage("isAllowedTopicsAccess(ctx, %s, %s)", PACKAGE_NAME, ENROLLMENT_ID) .that( AppManifestConfigHelper.isAllowedTopicsAccess( - /* useSandboxCheck= */ false, - PACKAGE_NAME, - ENROLLMENT_ID)) + /* useSandboxCheck= */ false, PACKAGE_NAME, ENROLLMENT_ID)) .isTrue(); verifyErrorLogUtilErrorLogged(any(), never()); @@ -567,21 +572,23 @@ public final class AppManifestConfigHelperTest extends AdServicesExtendedMockito mode); } - private void verifyLogUsage(boolean appExists, boolean appHasConfig, boolean enabledByDefault) { - verifyLogUsage(appExists, appHasConfig, enabledByDefault, times(1)); + private void verifyLogUsage(int api, int result) { + verifyLogUsage(api, result, times(1)); } - private void verifyLogUsage( - boolean appExists, - boolean appHasConfig, - boolean enabledByDefault, - VerificationMode mode) { - verify( - () -> - AppManifestConfigMetricsLogger.logUsage( - appManifestConfigCall( - PACKAGE_NAME, appExists, appHasConfig, enabledByDefault)), - mode); + private void verifyLogUsage(int api, int result, VerificationMode mode) { + AppManifestConfigCall call = new AppManifestConfigCall(PACKAGE_NAME, api); + call.result = result; + + verify(() -> AppManifestConfigMetricsLogger.logUsage(call), mode); + } + + private void verifyLogUsageForAllApis(int result) { + // Cannot use anyInt() for the APIs as logUsage() uses a custom object / matcher - it would + // be too coplicate to create a generic one for it + verifyLogUsage(API_TOPICS, result, times(2)); + verifyLogUsage(API_ATTRIBUTION, result); + verifyLogUsage(API_CUSTOM_AUDIENCES, result); } private void setEnabledByDefault(boolean value) { diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/AppManifestConfigMetricsLoggerTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/AppManifestConfigMetricsLoggerTest.java index 0f4b2795a..800027b5d 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/AppManifestConfigMetricsLoggerTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/AppManifestConfigMetricsLoggerTest.java @@ -18,8 +18,20 @@ package com.android.adservices.service.common; import static com.android.adservices.mockito.ExtendedMockitoExpectations.mockErrorLogUtilWithThrowable; import static com.android.adservices.mockito.ExtendedMockitoExpectations.mockErrorLogUtilWithoutThrowable; +import static com.android.adservices.mockito.ExtendedMockitoExpectations.verifyErrorLogUtilError; +import static com.android.adservices.mockito.ExtendedMockitoExpectations.verifyErrorLogUtilErrorWithAnyException; +import static com.android.adservices.service.common.AppManifestConfigCall.API_ATTRIBUTION; +import static com.android.adservices.service.common.AppManifestConfigCall.API_TOPICS; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_ALLOWED_APP_ALLOWS_ALL; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_ALLOWED_BY_DEFAULT_APP_DOES_NOT_HAVE_CONFIG; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_DISALLOWED_BY_APP; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_UNSPECIFIED; +import static com.android.adservices.service.common.AppManifestConfigCall.apiToString; +import static com.android.adservices.service.common.AppManifestConfigCall.resultToString; import static com.android.adservices.service.common.AppManifestConfigMetricsLogger.dump; +import static com.android.adservices.service.common.AppManifestConfigMetricsLogger.PREFS_KEY_TEMPLATE; import static com.android.adservices.service.common.AppManifestConfigMetricsLogger.PREFS_NAME; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__APP_MANIFEST_CONFIG_LOGGING_ERROR; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__SHARED_PREF_UPDATE_FAILURE; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__SHARED_PREF_EXCEPTION; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__COMMON; @@ -29,7 +41,7 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.when; import android.content.Context; @@ -37,8 +49,6 @@ import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.util.Log; -import androidx.test.filters.FlakyTest; - import com.android.adservices.common.AdServicesExtendedMockitoTestCase; import com.android.adservices.common.Nullable; import com.android.adservices.common.SyncCallback; @@ -46,12 +56,13 @@ import com.android.adservices.errorlogging.ErrorLogUtil; import com.android.adservices.mockito.ExtendedMockitoExpectations.ErrorLogUtilCallback; import com.android.adservices.service.Flags; import com.android.adservices.service.FlagsFactory; +import com.android.adservices.service.common.AppManifestConfigCall.ApiType; +import com.android.adservices.service.common.AppManifestConfigCall.Result; import com.android.adservices.shared.testing.common.DumpHelper; import com.android.modules.utils.testing.ExtendedMockitoRule.SpyStatic; import org.junit.Before; import org.junit.Test; -import org.mockito.ArgumentMatcher; import org.mockito.Mock; import java.io.File; @@ -59,6 +70,7 @@ import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; @@ -71,14 +83,11 @@ public final class AppManifestConfigMetricsLoggerTest extends AdServicesExtended private static final String PKG_NAME = "pkg.I.am"; private static final String PKG_NAME2 = "or.not"; - static final boolean APP_EXISTS = true; - static final boolean APP_DOES_NOT_EXIST = false; - - static final boolean APP_HAS_CONFIG = true; - static final boolean APP_DOES_NOT_HAVE_CONFIG = false; + // Generic API - exact value doesn't matter + private static final @ApiType int API = API_TOPICS; - static final boolean ENABLED_BY_DEFAULT = true; - static final boolean NOT_ENABLED_BY_DEFAULT = false; + private static final String KEY_PKG_NAME_API = + String.format(Locale.US, PREFS_KEY_TEMPLATE, PKG_NAME, API); @Mock private Context mMockContext; @Mock private Flags mMockFlags; @@ -101,119 +110,79 @@ public final class AppManifestConfigMetricsLoggerTest extends AdServicesExtended public void testLogUsage_nullArgs() throws Exception { assertThrows( NullPointerException.class, - () -> - logUsageAndDontWait( - /* packageName= */ null, - APP_EXISTS, - APP_HAS_CONFIG, - ENABLED_BY_DEFAULT)); + () -> logUsageAndDontWait(/* packageName= */ null, RESULT_ALLOWED_APP_ALLOWS_ALL)); + } + + @Test + public void testLogUsage_callWithInvalidResult() throws Exception { + AppManifestConfigCall call = new AppManifestConfigCall(PKG_NAME, API); + call.result = RESULT_UNSPECIFIED; + mPrefs.onEditThrows(); // will throw if edit() is called + + AppManifestConfigMetricsLogger.logUsage(call); + + verifyErrorLogUtilError( + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__APP_MANIFEST_CONFIG_LOGGING_ERROR, + AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__COMMON); + assertEditNotCalled(); } @Test public void testLogUsage_firstTime() throws Exception { - logUsageAndWait(APP_EXISTS, APP_HAS_CONFIG, ENABLED_BY_DEFAULT); + logUsageAndWait(PKG_NAME, RESULT_ALLOWED_APP_ALLOWS_ALL); Map<String, ?> allProps = mPrefs.getAll(); assertWithMessage("allProps").that(allProps).hasSize(1); - assertWithMessage("properties keys").that(allProps.keySet()).containsExactly(PKG_NAME); + assertWithMessage("properties keys") + .that(allProps.keySet()) + .containsExactly(KEY_PKG_NAME_API); } @Test - public void testLogUsage_secondTimeSameArgs() throws Exception { + public void testLogUsage_secondTimeSameResult() throws Exception { // 1st time is fine - logUsageAndWait(APP_EXISTS, APP_HAS_CONFIG, ENABLED_BY_DEFAULT); + logUsageAndWait(PKG_NAME, RESULT_ALLOWED_APP_ALLOWS_ALL); // 2nd time should not call edit mPrefs.onEditThrows(); // will throw if edit() is called - logUsageAndDontWait(PKG_NAME, APP_EXISTS, APP_HAS_CONFIG, ENABLED_BY_DEFAULT); + logUsageAndDontWait(PKG_NAME, RESULT_ALLOWED_APP_ALLOWS_ALL); Map<String, ?> allProps = mPrefs.getAll(); assertWithMessage("allProps").that(allProps).hasSize(1); - assertWithMessage("properties keys").that(allProps.keySet()).containsExactly(PKG_NAME); - } + assertWithMessage("properties keys") + .that(allProps.keySet()) + .containsExactly(KEY_PKG_NAME_API); - @FlakyTest(bugId = 315979774, detail = "Might need to split it into multiple tests") - @Test - public void testLogUsage_secondTimeDifferentArgs() throws Exception { - callOnceWithAllTrueThenSecondWith(APP_EXISTS, APP_HAS_CONFIG, NOT_ENABLED_BY_DEFAULT); - callOnceWithAllTrueThenSecondWith(APP_EXISTS, APP_DOES_NOT_HAVE_CONFIG, ENABLED_BY_DEFAULT); - callOnceWithAllTrueThenSecondWith( - APP_EXISTS, APP_DOES_NOT_HAVE_CONFIG, NOT_ENABLED_BY_DEFAULT); - callOnceWithAllTrueThenSecondWith(APP_DOES_NOT_EXIST, APP_HAS_CONFIG, ENABLED_BY_DEFAULT); - callOnceWithAllTrueThenSecondWith( - APP_DOES_NOT_EXIST, APP_HAS_CONFIG, NOT_ENABLED_BY_DEFAULT); - callOnceWithAllTrueThenSecondWith( - APP_DOES_NOT_EXIST, APP_DOES_NOT_HAVE_CONFIG, ENABLED_BY_DEFAULT); - callOnceWithAllTrueThenSecondWith( - APP_DOES_NOT_EXIST, APP_DOES_NOT_HAVE_CONFIG, NOT_ENABLED_BY_DEFAULT); + assertEditNotCalled(); } - private void callOnceWithAllTrueThenSecondWith( - boolean appExists, boolean appHasConfig, boolean enabledByDefault) throws Exception { - Log.i( - mTag, - "callOnceWithAllTrueThenSecondWith(appExists=" - + appExists - + ", appHasConfig=" - + appHasConfig - + ", enabledByDefault=" - + enabledByDefault - + ")"); - // Need to use a new prefs because it's called multiple times (so it starts in a clean - // state) - life would be so much easier if JUnit provided an easy way to run parameterized - // tests per method (not class) - FakeSharedPreferences prefs = new FakeSharedPreferences(); - when(mMockContext.getSharedPreferences(any(String.class), anyInt())).thenReturn(prefs); - + @Test + public void testLogUsage_secondTimeDifferentResult() throws Exception { + int result = RESULT_ALLOWED_APP_ALLOWS_ALL; // 1st call - Log.d( - mTag, - "1st call: appExists=" - + APP_EXISTS - + ", appHasConfig=" - + APP_HAS_CONFIG - + ", enabledByDefault=" - + ENABLED_BY_DEFAULT - + ")"); - logUsageAndWait(prefs, PKG_NAME, APP_EXISTS, APP_HAS_CONFIG, ENABLED_BY_DEFAULT); - - int valueBefore = prefs.getInt(PKG_NAME, -1); - expect.withMessage( - "stored value of %s after 1st call (appExists=%s, appHasConfig=%s," - + " enabledByDefault=%s)", - PKG_NAME, APP_EXISTS, APP_EXISTS, ENABLED_BY_DEFAULT) + Log.d(mTag, "1st call: result=" + result); + logUsageAndWait(PKG_NAME, result); + + int valueBefore = mPrefs.getInt(KEY_PKG_NAME_API, RESULT_UNSPECIFIED); + expect.withMessage("stored value of %s after 1st call (result=%s)", PKG_NAME, result) .that(valueBefore) - .isNotEqualTo(-1); + .isEqualTo(RESULT_ALLOWED_APP_ALLOWS_ALL); // 2nd call - Log.d( - mTag, - "2nd call: appExists=" - + appExists - + ", appHasConfig=" - + appHasConfig - + ", enabledByDefault=" - + enabledByDefault - + ")"); - logUsageAndWait(prefs, PKG_NAME, appExists, appHasConfig, enabledByDefault); - - Map<String, ?> allProps = prefs.getAll(); + result = RESULT_ALLOWED_BY_DEFAULT_APP_DOES_NOT_HAVE_CONFIG; + Log.d(mTag, "2nd call: result=" + result); + logUsageAndWait(PKG_NAME, result); + + Map<String, ?> allProps = mPrefs.getAll(); expect.withMessage("allProps").that(allProps).hasSize(1); - expect.withMessage("properties keys").that(allProps.keySet()).containsExactly(PKG_NAME); + expect.withMessage("properties keys") + .that(allProps.keySet()) + .containsExactly(KEY_PKG_NAME_API); - int valueAfter = prefs.getInt(PKG_NAME, -1); - expect.withMessage( - "stored value of %s after 2nd call (appExists=%s, appHasConfig=%s," - + " enabledByDefault=%s)", - PKG_NAME, appExists, appHasConfig, enabledByDefault) + int valueAfter = mPrefs.getInt(KEY_PKG_NAME_API, RESULT_UNSPECIFIED); + expect.withMessage("stored value of %s after 2nd call (result=%s)", PKG_NAME, result) .that(valueAfter) - .isNotEqualTo(-1); - expect.withMessage( - "stored value of %s after 2nd call (appExists=%s, appHasConfig=%s," - + " enabledByDefault=%s)", - PKG_NAME, appExists, appHasConfig, enabledByDefault) - .that(valueAfter) - .isNotEqualTo(valueBefore); + .isEqualTo(RESULT_ALLOWED_BY_DEFAULT_APP_DOES_NOT_HAVE_CONFIG); } @Test @@ -222,7 +191,7 @@ public final class AppManifestConfigMetricsLoggerTest extends AdServicesExtended when(mMockContext.getSharedPreferences(any(String.class), anyInt())).thenThrow(exception); - logUsageAndDontWait(PKG_NAME, APP_EXISTS, APP_HAS_CONFIG, ENABLED_BY_DEFAULT); + logUsageAndDontWait(PKG_NAME, RESULT_ALLOWED_APP_ALLOWS_ALL); mErrorLogUtilWithThrowableCallback.assertReceived( expect, @@ -235,7 +204,7 @@ public final class AppManifestConfigMetricsLoggerTest extends AdServicesExtended public void testLogUsage_commitFailed() throws Exception { mPrefs.onCommitReturns(/* result= */ false); - logUsageAndDontWait(PKG_NAME, APP_EXISTS, APP_HAS_CONFIG, ENABLED_BY_DEFAULT); + logUsageAndDontWait(PKG_NAME, RESULT_ALLOWED_APP_ALLOWS_ALL); Map<String, ?> allProps = mPrefs.getAll(); assertWithMessage("allProps").that(allProps).isEmpty(); @@ -258,7 +227,7 @@ public final class AppManifestConfigMetricsLoggerTest extends AdServicesExtended return mPrefs; }); - logUsageAndWait(mPrefs, PKG_NAME, APP_EXISTS, APP_HAS_CONFIG, ENABLED_BY_DEFAULT); + logUsageAndWait(PKG_NAME, RESULT_ALLOWED_APP_ALLOWS_ALL); assertWithMessage("execution thread") .that(executionThread.get()) @@ -288,63 +257,58 @@ public final class AppManifestConfigMetricsLoggerTest extends AdServicesExtended @Test public void testDump_multipleEntries() throws Exception { - logUsageAndWait(mPrefs, PKG_NAME, APP_EXISTS, APP_HAS_CONFIG, ENABLED_BY_DEFAULT); + logUsageAndWait(PKG_NAME, API_TOPICS, RESULT_ALLOWED_APP_ALLOWS_ALL); + logUsageAndWait(PKG_NAME, API_ATTRIBUTION, RESULT_DISALLOWED_BY_APP); logUsageAndWait( - mPrefs, - PKG_NAME2, - APP_DOES_NOT_EXIST, - APP_DOES_NOT_HAVE_CONFIG, - NOT_ENABLED_BY_DEFAULT); + PKG_NAME2, API_ATTRIBUTION, RESULT_ALLOWED_BY_DEFAULT_APP_DOES_NOT_HAVE_CONFIG); String dump = DumpHelper.dump(pw -> AppManifestConfigMetricsLogger.dump(mMockContext, pw)); + String entry1 = + ".*" + + PKG_NAME + + "-" + + apiToString(API_TOPICS) + + ": " + + resultToString(RESULT_ALLOWED_APP_ALLOWS_ALL) + + ".*\n"; + String entry2 = + ".*" + + PKG_NAME + + "-" + + apiToString(API_ATTRIBUTION) + + ": " + + resultToString(RESULT_DISALLOWED_BY_APP) + + ".*\n"; + String entry3 = + ".*" + + PKG_NAME2 + + "-" + + apiToString(API_ATTRIBUTION) + + ": " + + resultToString(RESULT_ALLOWED_BY_DEFAULT_APP_DOES_NOT_HAVE_CONFIG) + + ".*\n"; expect.withMessage("dump") .that(dump) .matches( Pattern.compile( - ".*2 entries.*\n" - + ".*" - + PKG_NAME - + ":.*appExists=" - + APP_EXISTS - + ".*appHasConfig=" - + APP_HAS_CONFIG - + ".*enabledByDefault=" - + ENABLED_BY_DEFAULT - + "\n" - + ".*" - + PKG_NAME2 - + ":.*appExists=" - + APP_DOES_NOT_EXIST - + ".*appHasConfig=" - + APP_DOES_NOT_HAVE_CONFIG - + ".*enabledByDefault=" - + NOT_ENABLED_BY_DEFAULT - + "\n", - Pattern.DOTALL)); + ".*3 entries.*\n" + entry1 + entry2 + entry3, Pattern.DOTALL)); } // Needs to wait until the shared prefs is committed() as it happens in a separated thread - private void logUsageAndWait(boolean appExists, boolean appHasConfig, boolean enabledByDefault) + private void logUsageAndWait(String appName, @Result int callResult) throws InterruptedException { - logUsageAndWait(mPrefs, PKG_NAME, appExists, appHasConfig, enabledByDefault); + logUsageAndWait(appName, API, callResult); } // Needs to wait until the shared prefs is committed() as it happens in a separated thread - private void logUsageAndWait( - SharedPreferences prefs, - String appName, - boolean appExists, - boolean appHasConfig, - boolean enabledByDefault) + private void logUsageAndWait(String appName, @ApiType int api, @Result int callResult) throws InterruptedException { SyncOnSharedPreferenceChangeListener listener = new SyncOnSharedPreferenceChangeListener(); - prefs.registerOnSharedPreferenceChangeListener(listener); + mPrefs.registerOnSharedPreferenceChangeListener(listener); try { - AppManifestConfigCall call = new AppManifestConfigCall(appName); - call.appExists = appExists; - call.appHasConfig = appHasConfig; - call.enabledByDefault = enabledByDefault; + AppManifestConfigCall call = new AppManifestConfigCall(appName, api); + call.result = callResult; Log.v(mTag, "logUsageAndWait(call=" + call + ", listener=" + listener + ")"); AppManifestConfigMetricsLogger.logUsage(call); @@ -357,61 +321,24 @@ public final class AppManifestConfigMetricsLoggerTest extends AdServicesExtended // Should only be used in cases where the call is expect to not change the shared preferences // (in which case a listener would not be called) - private void logUsageAndDontWait( - String appName, boolean appExists, boolean appHasConfig, boolean enabledByDefault) { - AppManifestConfigCall call = new AppManifestConfigCall(appName); - call.appExists = appExists; - call.appHasConfig = appHasConfig; - call.enabledByDefault = enabledByDefault; + private void logUsageAndDontWait(String appName, @Result int callResult) { + AppManifestConfigCall call = new AppManifestConfigCall(appName, API); + call.result = callResult; Log.v(mTag, "logUsageAndDontWait(call=" + call + ")"); AppManifestConfigMetricsLogger.logUsage(call); } - /** Gets a custom Mockito matcher for a {@link AppManifestConfigCall}, without the result. */ - static AppManifestConfigCall appManifestConfigCall( - String packageName, boolean appExists, boolean appHasConfig, boolean enabledByDefault) { - return argThat( - new AppManifestConfigCallMatcher( - packageName, appExists, appHasConfig, enabledByDefault)); - } - - private static final class AppManifestConfigCallMatcher - implements ArgumentMatcher<AppManifestConfigCall> { - - private final String mPackageName; - private final boolean mAppExists; - private final boolean mAppHasConfig; - private final boolean mEnabledByDefault; - - private AppManifestConfigCallMatcher( - String packageName, - boolean appExists, - boolean appHasConfig, - boolean enabledByDefault) { - mPackageName = packageName; - mAppExists = appExists; - mAppHasConfig = appHasConfig; - mEnabledByDefault = enabledByDefault; - } - - @Override - public boolean matches(AppManifestConfigCall arg) { - return arg != null - && arg.packageName.equals(mPackageName) - && arg.appExists == mAppExists - && arg.appHasConfig == mAppHasConfig - && arg.enabledByDefault == mEnabledByDefault; - } + // Must call mPrefs.onEditThrows() first + private void assertEditNotCalled() { + sleep( + 1_000, + "waiting to make sure edit() was not called in the background (which would " + + "have thrown an exception)"); - @Override - public String toString() { - AppManifestConfigCall call = new AppManifestConfigCall(mPackageName); - call.appExists = mAppExists; - call.appHasConfig = mAppHasConfig; - call.enabledByDefault = mEnabledByDefault; - - return call.toString() + " {NOT CHECING RESULT}"; - } + verifyErrorLogUtilErrorWithAnyException( + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__SHARED_PREF_EXCEPTION, + AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__COMMON, + never()); } // TODO(b/309857141): move to its own class / common package (it will be done in a later CL so diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/AppManifestConfigParserTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/AppManifestConfigParserTest.java index 10fd81c8a..724b1087c 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/AppManifestConfigParserTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/AppManifestConfigParserTest.java @@ -16,6 +16,12 @@ package com.android.adservices.service.common; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_ALLOWED_APP_ALLOWS_SPECIFIC_ID; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_ALLOWED_BY_DEFAULT_APP_HAS_CONFIG_WITHOUT_API_SECTION; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_DISALLOWED_APP_HAS_CONFIG_WITHOUT_API_SECTION; +import static com.android.adservices.service.common.AppManifestConfigCall.RESULT_DISALLOWED_BY_APP; +import static com.android.adservices.service.common.AppManifestConfigCall.resultToString; + import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertThrows; @@ -82,10 +88,10 @@ public final class AppManifestConfigParserTest extends AdServicesUnitTestCase { // Verify Attribution tags. expect.withMessage("getAttributionConfig().getAllowAdPartnersToAccess()") .that(appManifestConfig.isAllowedAttributionAccess("1234")) - .isTrue(); + .isEqualTo(RESULT_ALLOWED_APP_ALLOWS_SPECIFIC_ID); expect.withMessage("isAllowedAttributionAccess()") .that(appManifestConfig.isAllowedAttributionAccess("108")) - .isFalse(); + .isEqualTo(RESULT_DISALLOWED_BY_APP); AppManifestAttributionConfig attributionConfig = appManifestConfig.getAttributionConfig(); expect.withMessage("getAttributionConfig()").that(attributionConfig).isNotNull(); if (attributionConfig != null) { @@ -100,7 +106,7 @@ public final class AppManifestConfigParserTest extends AdServicesUnitTestCase { // Verify Custom Audience tags. expect.withMessage("isAllowedCustomAudiencesAccess()") .that(appManifestConfig.isAllowedCustomAudiencesAccess("108")) - .isFalse(); + .isEqualTo(RESULT_DISALLOWED_BY_APP); AppManifestCustomAudiencesConfig customAudiencesConfig = appManifestConfig.getCustomAudiencesConfig(); expect.withMessage("getCustomAudiencesConfig()").that(customAudiencesConfig).isNotNull(); @@ -119,10 +125,10 @@ public final class AppManifestConfigParserTest extends AdServicesUnitTestCase { // Verify Topics tags. expect.withMessage("1234567()") .that(appManifestConfig.isAllowedTopicsAccess("1234567")) - .isTrue(); + .isEqualTo(RESULT_ALLOWED_APP_ALLOWS_SPECIFIC_ID); expect.withMessage("isAllowedTopicsAccess()") .that(appManifestConfig.isAllowedTopicsAccess("108")) - .isFalse(); + .isEqualTo(RESULT_DISALLOWED_BY_APP); AppManifestTopicsConfig topicsConfig = appManifestConfig.getTopicsConfig(); expect.withMessage("getTopicsConfig()").that(topicsConfig).isNotNull(); if (topicsConfig != null) { @@ -137,10 +143,10 @@ public final class AppManifestConfigParserTest extends AdServicesUnitTestCase { // Verify AppId tags. expect.withMessage("isAllowedAdIdAccess()") .that(appManifestConfig.isAllowedAdIdAccess("42")) - .isTrue(); + .isEqualTo(RESULT_ALLOWED_APP_ALLOWS_SPECIFIC_ID); expect.withMessage("isAllowedAdIdAccess()") .that(appManifestConfig.isAllowedAdIdAccess("108")) - .isFalse(); + .isEqualTo(RESULT_DISALLOWED_BY_APP); AppManifestAdIdConfig adIdConfig = appManifestConfig.getAdIdConfig(); expect.withMessage("getAdIdConfig()").that(adIdConfig).isNotNull(); if (adIdConfig != null) { @@ -155,10 +161,10 @@ public final class AppManifestConfigParserTest extends AdServicesUnitTestCase { // Verify AppSetId tags. expect.withMessage("isAllowedAppSetIdAccess()") .that(appManifestConfig.isAllowedAppSetIdAccess("42")) - .isTrue(); + .isEqualTo(RESULT_ALLOWED_APP_ALLOWS_SPECIFIC_ID); expect.withMessage("isAllowedAppSetIdAccess()") .that(appManifestConfig.isAllowedAppSetIdAccess("108")) - .isFalse(); + .isEqualTo(RESULT_DISALLOWED_BY_APP); AppManifestAppSetIdConfig appSetIdConfig = appManifestConfig.getAppSetIdConfig(); expect.withMessage("getAppSetIdConfig()").that(appSetIdConfig).isNotNull(); if (appSetIdConfig != null) { @@ -216,34 +222,39 @@ public final class AppManifestConfigParserTest extends AdServicesUnitTestCase { AppManifestAttributionConfig attributionConfig = appManifestConfig.getAttributionConfig(); expect.withMessage("getAttributionConfig()").that(attributionConfig).isNull(); - expect.withMessage("isAllowedAttributionAccess()") - .that(appManifestConfig.isAllowedAttributionAccess("not actually there")) - .isFalse(); + assertResult( + "isAllowedAttributionAccess()", + appManifestConfig.isAllowedAttributionAccess("not actually there"), + RESULT_DISALLOWED_APP_HAS_CONFIG_WITHOUT_API_SECTION); AppManifestCustomAudiencesConfig customAudiencesConfig = appManifestConfig.getCustomAudiencesConfig(); expect.withMessage("getCustomAudiencesConfig()").that(attributionConfig).isNull(); - expect.withMessage("isAllowedCustomAudiencesAccess()") - .that(appManifestConfig.isAllowedCustomAudiencesAccess("not actually there")) - .isFalse(); + assertResult( + "isAllowedCustomAudiencesAccess()", + appManifestConfig.isAllowedCustomAudiencesAccess("not actually there"), + RESULT_DISALLOWED_APP_HAS_CONFIG_WITHOUT_API_SECTION); AppManifestTopicsConfig topicsConfig = appManifestConfig.getTopicsConfig(); expect.withMessage("getTopicsConfig()").that(topicsConfig).isNull(); - expect.withMessage("isAllowedTopicsAccess()") - .that(appManifestConfig.isAllowedTopicsAccess("not actually there")) - .isFalse(); + assertResult( + "isAllowedTopicsAccess()", + appManifestConfig.isAllowedTopicsAccess("not actually there"), + RESULT_DISALLOWED_APP_HAS_CONFIG_WITHOUT_API_SECTION); AppManifestAdIdConfig adIdConfig = appManifestConfig.getAdIdConfig(); expect.withMessage("getAdIdConfig()").that(adIdConfig).isNull(); - expect.withMessage("isAllowedAdIdAccess()") - .that(appManifestConfig.isAllowedAdIdAccess("not actually there")) - .isFalse(); + assertResult( + "isAllowedAdIdAccess()", + appManifestConfig.isAllowedTopicsAccess("not actually there"), + RESULT_DISALLOWED_APP_HAS_CONFIG_WITHOUT_API_SECTION); AppManifestAppSetIdConfig appSetIdConfig = appManifestConfig.getAppSetIdConfig(); expect.withMessage("getAppSetIdConfig()").that(appSetIdConfig).isNull(); - expect.withMessage("isAllowedAppSetIdAccess()") - .that(appManifestConfig.isAllowedAppSetIdAccess("not actually there")) - .isFalse(); + assertResult( + "isAllowedAppSetIdAccess()", + appManifestConfig.isAllowedAppSetIdAccess("not actually there"), + RESULT_DISALLOWED_APP_HAS_CONFIG_WITHOUT_API_SECTION); } @Test @@ -254,16 +265,21 @@ public final class AppManifestConfigParserTest extends AdServicesUnitTestCase { .getXml(R.xml.ad_services_config_missing_tags); AppManifestConfig appManifestConfig = AppManifestConfigParser.getConfig(parser, /* enabledByDefault= */ true); - assertWithMessage("manifest for ad_services_config_all_false_missing_attribution") + assertWithMessage("manifest for ad_services_config_missing_tags") .that(appManifestConfig) .isNotNull(); assertSdkLibraryConfigIsEmpty(appManifestConfig, /* containsByDefault= */ true); - assertAttributionConfigIsDefault(appManifestConfig); - assertCustomAudiencesConfigIsDefault(appManifestConfig); - assertTopicsConfigIsDefault(appManifestConfig); - assertAdIdConfigIsDefault(appManifestConfig); - assertAppSetIdConfigIsDefault(appManifestConfig); + assertAttributionConfigIsAllowed( + appManifestConfig, RESULT_ALLOWED_BY_DEFAULT_APP_HAS_CONFIG_WITHOUT_API_SECTION); + assertCustomAudienceConfigIsAllowed( + appManifestConfig, RESULT_ALLOWED_BY_DEFAULT_APP_HAS_CONFIG_WITHOUT_API_SECTION); + assertTopicsConfigIsAllowed( + appManifestConfig, RESULT_ALLOWED_BY_DEFAULT_APP_HAS_CONFIG_WITHOUT_API_SECTION); + assertAdIdConfigIsAllowed( + appManifestConfig, RESULT_ALLOWED_BY_DEFAULT_APP_HAS_CONFIG_WITHOUT_API_SECTION); + assertAppSetIdConfigIsAllowed( + appManifestConfig, RESULT_ALLOWED_BY_DEFAULT_APP_HAS_CONFIG_WITHOUT_API_SECTION); } @Test @@ -279,11 +295,12 @@ public final class AppManifestConfigParserTest extends AdServicesUnitTestCase { .isNotNull(); assertSdkLibraryConfigIsEmpty(appManifestConfig, /* containsByDefault= */ true); - assertAttributionConfigIsDefault(appManifestConfig); - assertCustomAudiencesConfigIsFalse(appManifestConfig); - assertTopicsConfigIsFalse(appManifestConfig); - assertAdIdConfigIsFalse(appManifestConfig); - assertAppSetIdConfigIsFalse(appManifestConfig); + assertAttributionConfigIsAllowed( + appManifestConfig, RESULT_ALLOWED_BY_DEFAULT_APP_HAS_CONFIG_WITHOUT_API_SECTION); + assertCustomAudienceConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertTopicsConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertAdIdConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertAppSetIdConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); } @Test @@ -299,11 +316,12 @@ public final class AppManifestConfigParserTest extends AdServicesUnitTestCase { .isNotNull(); assertSdkLibraryConfigIsEmpty(appManifestConfig, /* containsByDefault= */ true); - assertAttributionConfigIsFalse(appManifestConfig); - assertCustomAudiencesConfigIsDefault(appManifestConfig); - assertTopicsConfigIsFalse(appManifestConfig); - assertAdIdConfigIsFalse(appManifestConfig); - assertAppSetIdConfigIsFalse(appManifestConfig); + assertAttributionConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertCustomAudienceConfigIsAllowed( + appManifestConfig, RESULT_ALLOWED_BY_DEFAULT_APP_HAS_CONFIG_WITHOUT_API_SECTION); + assertTopicsConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertAdIdConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertAppSetIdConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); } @Test @@ -319,11 +337,12 @@ public final class AppManifestConfigParserTest extends AdServicesUnitTestCase { .isNotNull(); assertSdkLibraryConfigIsEmpty(appManifestConfig, /* containsByDefault= */ true); - assertAttributionConfigIsFalse(appManifestConfig); - assertCustomAudiencesConfigIsFalse(appManifestConfig); - assertTopicsConfigIsDefault(appManifestConfig); - assertAdIdConfigIsFalse(appManifestConfig); - assertAppSetIdConfigIsFalse(appManifestConfig); + assertAttributionConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertCustomAudienceConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertTopicsConfigIsAllowed( + appManifestConfig, RESULT_ALLOWED_BY_DEFAULT_APP_HAS_CONFIG_WITHOUT_API_SECTION); + assertAdIdConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertAppSetIdConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); } @Test @@ -339,11 +358,13 @@ public final class AppManifestConfigParserTest extends AdServicesUnitTestCase { .isNotNull(); assertSdkLibraryConfigIsEmpty(appManifestConfig, /* containsByDefault= */ true); - assertAttributionConfigIsFalse(appManifestConfig); - assertCustomAudiencesConfigIsFalse(appManifestConfig); - assertTopicsConfigIsFalse(appManifestConfig); - assertAdIdConfigIsDefault(appManifestConfig); - assertAppSetIdConfigIsFalse(appManifestConfig); + assertAttributionConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertCustomAudienceConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertTopicsConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertTopicsConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertAdIdConfigIsAllowed( + appManifestConfig, RESULT_ALLOWED_BY_DEFAULT_APP_HAS_CONFIG_WITHOUT_API_SECTION); + assertAppSetIdConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); } @Test @@ -359,11 +380,12 @@ public final class AppManifestConfigParserTest extends AdServicesUnitTestCase { .isNotNull(); assertSdkLibraryConfigIsEmpty(appManifestConfig, /* containsByDefault= */ true); - assertAttributionConfigIsFalse(appManifestConfig); - assertCustomAudiencesConfigIsFalse(appManifestConfig); - assertTopicsConfigIsFalse(appManifestConfig); - assertAdIdConfigIsFalse(appManifestConfig); - assertAppSetIdConfigIsDefault(appManifestConfig); + assertAttributionConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertCustomAudienceConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertTopicsConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertAdIdConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertAppSetIdConfigIsAllowed( + appManifestConfig, RESULT_ALLOWED_BY_DEFAULT_APP_HAS_CONFIG_WITHOUT_API_SECTION); } @Test @@ -379,11 +401,11 @@ public final class AppManifestConfigParserTest extends AdServicesUnitTestCase { .isNotNull(); assertSdkLibraryConfigIsEmpty(appManifestConfig, /* containsByDefault= */ true); - assertAttributionConfigIsFalse(appManifestConfig); - assertCustomAudiencesConfigIsFalse(appManifestConfig); - assertTopicsConfigIsFalse(appManifestConfig); - assertAdIdConfigIsFalse(appManifestConfig); - assertAppSetIdConfigIsFalse(appManifestConfig); + assertAttributionConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertCustomAudienceConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertTopicsConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertAdIdConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertAppSetIdConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); } @Test @@ -415,11 +437,11 @@ public final class AppManifestConfigParserTest extends AdServicesUnitTestCase { .that(sdkLibrary.contains("4815162342")) .isFalse(); - assertAttributionConfigIsFalse(appManifestConfig); - assertCustomAudiencesConfigIsFalse(appManifestConfig); - assertTopicsConfigIsFalse(appManifestConfig); - assertAdIdConfigIsFalse(appManifestConfig); - assertAppSetIdConfigIsFalse(appManifestConfig); + assertAttributionConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertCustomAudienceConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertTopicsConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertAdIdConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertAppSetIdConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); } @Test @@ -434,11 +456,11 @@ public final class AppManifestConfigParserTest extends AdServicesUnitTestCase { .isNotNull(); assertSdkLibraryConfigIsEmpty(appManifestConfig, /* containsByDefault= */ false); - assertAttributionConfigIsFalse(appManifestConfig); - assertCustomAudiencesConfigIsFalse(appManifestConfig); - assertTopicsConfigIsFalse(appManifestConfig); - assertAdIdConfigIsFalse(appManifestConfig); - assertAppSetIdConfigIsFalse(appManifestConfig); + assertAttributionConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertCustomAudienceConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertTopicsConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertAdIdConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); + assertAppSetIdConfigIsAllowed(appManifestConfig, RESULT_DISALLOWED_BY_APP); } @Test @@ -517,86 +539,46 @@ public final class AppManifestConfigParserTest extends AdServicesUnitTestCase { } } - private void assertAttributionConfigIsDefault(AppManifestConfig appManifestConfig) { - AppManifestAttributionConfig attributionConfig = appManifestConfig.getAttributionConfig(); - assertApiConfigIsDefault("getAttributionConfig()", attributionConfig); - expect.withMessage("isAllowedAttributionAccess()") - .that(appManifestConfig.isAllowedAttributionAccess("not actually there")) - .isTrue(); - } - - private void assertAttributionConfigIsFalse(AppManifestConfig appManifestConfig) { - AppManifestAttributionConfig attributionConfig = appManifestConfig.getAttributionConfig(); - assertApiConfigIsFalse("getAttributionConfig()", attributionConfig); - expect.withMessage("isAllowedAttributionAccess()") - .that(appManifestConfig.isAllowedAttributionAccess("not actually there")) - .isFalse(); - } - - private void assertCustomAudiencesConfigIsDefault(AppManifestConfig appManifestConfig) { - AppManifestCustomAudiencesConfig customAudiencesConfig = - appManifestConfig.getCustomAudiencesConfig(); - assertApiConfigIsDefault("getCustomAudiencesConfig()", customAudiencesConfig); - expect.withMessage("isAllowedCustomAudiencesAccess()") - .that(appManifestConfig.isAllowedCustomAudiencesAccess("not actually there")) - .isTrue(); + private void assertResult(String method, int actualResult, int expectedResult) { + expect.withMessage( + "%s (where %s=%s and %s=%s)", + method, + actualResult, + resultToString(actualResult), + expectedResult, + resultToString(expectedResult)) + .that(actualResult) + .isEqualTo(expectedResult); } - private void assertCustomAudiencesConfigIsFalse(AppManifestConfig appManifestConfig) { - AppManifestCustomAudiencesConfig customAudiencesConfig = - appManifestConfig.getCustomAudiencesConfig(); - assertApiConfigIsFalse("getCustomAudiencesConfig()", customAudiencesConfig); - expect.withMessage("isAllowedCustomAudiencesAccess()") - .that(appManifestConfig.isAllowedCustomAudiencesAccess("not actually there")) - .isFalse(); + private void assertAttributionConfigIsAllowed( + AppManifestConfig appManifestConfig, int expectedResult) { + int actualResult = appManifestConfig.isAllowedAttributionAccess("not actually there"); + assertResult("getAttributionConfig()", actualResult, expectedResult); } - private void assertTopicsConfigIsDefault(AppManifestConfig appManifestConfig) { - AppManifestTopicsConfig topicsConfig = appManifestConfig.getTopicsConfig(); - assertApiConfigIsDefault("getTopicsConfig()", topicsConfig); - expect.withMessage("isAllowedTopicsAccess()") - .that(appManifestConfig.isAllowedTopicsAccess("not actually there")) - .isTrue(); + private void assertCustomAudienceConfigIsAllowed( + AppManifestConfig appManifestConfig, int expectedResult) { + int actualResult = appManifestConfig.isAllowedCustomAudiencesAccess("not actually there"); + assertResult("getCustomAudiencesConfig()", actualResult, expectedResult); } - private void assertTopicsConfigIsFalse(AppManifestConfig appManifestConfig) { - AppManifestTopicsConfig topicsConfig = appManifestConfig.getTopicsConfig(); - assertApiConfigIsFalse("getTopicsConfig()", topicsConfig); - expect.withMessage("isAllowedTopicsAccess()") - .that(appManifestConfig.isAllowedTopicsAccess("not actually there")) - .isFalse(); + private void assertTopicsConfigIsAllowed( + AppManifestConfig appManifestConfig, int expectedResult) { + int actualResult = appManifestConfig.isAllowedTopicsAccess("not actually there"); + assertResult("getTopicsConfig()", actualResult, expectedResult); } - private void assertAdIdConfigIsDefault(AppManifestConfig appManifestConfig) { - AppManifestAdIdConfig adIdConfig = appManifestConfig.getAdIdConfig(); - assertApiConfigIsDefault("getAdIdConfig()", adIdConfig); - expect.withMessage("isAllowedAdIdAccess()") - .that(appManifestConfig.isAllowedAdIdAccess("not actually there")) - .isTrue(); - } - - private void assertAdIdConfigIsFalse(AppManifestConfig appManifestConfig) { - AppManifestAdIdConfig adIdConfig = appManifestConfig.getAdIdConfig(); - assertApiConfigIsFalse("getAdIdConfig()", adIdConfig); - expect.withMessage("isAllowedAdIdAccess()") - .that(appManifestConfig.isAllowedAdIdAccess("not actually there")) - .isFalse(); + private void assertAdIdConfigIsAllowed( + AppManifestConfig appManifestConfig, int expectedResult) { + int actualResult = appManifestConfig.isAllowedAdIdAccess("not actually there"); + assertResult("getAdIdConfig()", actualResult, expectedResult); } - private void assertAppSetIdConfigIsDefault(AppManifestConfig appManifestConfig) { - AppManifestAppSetIdConfig appSetIdConfig = appManifestConfig.getAppSetIdConfig(); - assertApiConfigIsDefault("getAppSetIdConfig()", appSetIdConfig); - expect.withMessage("isAllowedAppSetIdAccess()") - .that(appManifestConfig.isAllowedAppSetIdAccess("not actually there")) - .isTrue(); - } - - private void assertAppSetIdConfigIsFalse(AppManifestConfig appManifestConfig) { - AppManifestAppSetIdConfig appSetIdConfig = appManifestConfig.getAppSetIdConfig(); - assertApiConfigIsFalse("getAppSetIdConfig()", appSetIdConfig); - expect.withMessage("isAllowedAppSetIdAccess()") - .that(appManifestConfig.isAllowedAppSetIdAccess("not actually there")) - .isFalse(); + private void assertAppSetIdConfigIsAllowed( + AppManifestConfig appManifestConfig, int expectedResult) { + int actualResult = appManifestConfig.isAllowedAppSetIdAccess("not actually there"); + assertResult("getAppSetIdConfig()", actualResult, expectedResult); } private void assertApiConfigIsDefault(String name, AppManifestApiConfig config) { diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/FledgeAuthorizationFilterTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/FledgeAuthorizationFilterTest.java index c4c187946..5460b2273 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/FledgeAuthorizationFilterTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/FledgeAuthorizationFilterTest.java @@ -17,7 +17,6 @@ package com.android.adservices.service.common; import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE; -import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_PROTECTED_SIGNALS; import static android.adservices.common.AdServicesStatusUtils.STATUS_CALLER_NOT_ALLOWED; import static android.adservices.common.AdServicesStatusUtils.STATUS_PERMISSION_NOT_REQUESTED; import static android.adservices.common.AdServicesStatusUtils.STATUS_UNAUTHORIZED; @@ -165,7 +164,7 @@ public final class FledgeAuthorizationFilterTest extends AdServicesExtendedMocki } @Test - public void testAssertAppHasCaPermission_appHasPermission() + public void testAssertAppHasPermission_appHasPermission() throws PackageManager.NameNotFoundException { PackageInfo packageInfoGrant = new PackageInfo(); packageInfoGrant.requestedPermissions = new String[] {ACCESS_ADSERVICES_CUSTOM_AUDIENCE}; @@ -177,26 +176,7 @@ public final class FledgeAuthorizationFilterTest extends AdServicesExtendedMocki when(PermissionHelper.hasCustomAudiencesPermission(CONTEXT, CONTEXT.getPackageName())) .thenReturn(true); - mChecker.assertAppDeclaredCustomAudiencePermission( - CONTEXT, CustomAudienceFixture.VALID_OWNER, API_NAME_LOGGING_ID); - - verifyZeroInteractions(mPackageManagerMock, mEnrollmentDaoMock, mAdServicesLoggerMock); - } - - @Test - public void testAssertAppHasPasPermission_appHasPermission() - throws PackageManager.NameNotFoundException { - PackageInfo packageInfoGrant = new PackageInfo(); - packageInfoGrant.requestedPermissions = new String[] {ACCESS_ADSERVICES_PROTECTED_SIGNALS}; - doReturn(packageInfoGrant) - .when(mPackageManagerMock) - .getPackageInfo( - eq(CustomAudienceFixture.VALID_OWNER), eq(PackageManager.GET_PERMISSIONS)); - - when(PermissionHelper.hasProtectedSignalsPermission(CONTEXT, CONTEXT.getPackageName())) - .thenReturn(true); - - mChecker.assertAppDeclaredProtectedSignalsPermission( + mChecker.assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, API_NAME_LOGGING_ID); verifyZeroInteractions(mPackageManagerMock, mEnrollmentDaoMock, mAdServicesLoggerMock); @@ -216,7 +196,7 @@ public final class FledgeAuthorizationFilterTest extends AdServicesExtendedMocki assertThrows( SecurityException.class, () -> - mChecker.assertAppDeclaredCustomAudiencePermission( + mChecker.assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, API_NAME_LOGGING_ID)); @@ -232,7 +212,7 @@ public final class FledgeAuthorizationFilterTest extends AdServicesExtendedMocki } @Test - public void testAssertAppHasCaPermission_mismatchedAppPackageName_throwSecurityException() + public void testAssertAppHasPermission_mismatchedAppPackageName_throwSecurityException() throws PackageManager.NameNotFoundException { doReturn(new PackageInfo()) .when(mPackageManagerMock) @@ -245,34 +225,7 @@ public final class FledgeAuthorizationFilterTest extends AdServicesExtendedMocki assertThrows( SecurityException.class, () -> - mChecker.assertAppDeclaredCustomAudiencePermission( - CONTEXT, "mismatchedAppPackageName", API_NAME_LOGGING_ID)); - - assertEquals( - AdServicesStatusUtils.SECURITY_EXCEPTION_PERMISSION_NOT_REQUESTED_ERROR_MESSAGE, - exception.getMessage()); - verify(mAdServicesLoggerMock) - .logFledgeApiCallStats( - eq(API_NAME_LOGGING_ID), eq(STATUS_PERMISSION_NOT_REQUESTED), anyInt()); - verifyNoMoreInteractions(mAdServicesLoggerMock); - verifyZeroInteractions(mPackageManagerMock, mEnrollmentDaoMock); - } - - @Test - public void testAssertAppHasPasPermission_mismatchedAppPackageName_throwSecurityException() - throws PackageManager.NameNotFoundException { - doReturn(new PackageInfo()) - .when(mPackageManagerMock) - .getPackageInfo( - eq(CustomAudienceFixture.VALID_OWNER), eq(PackageManager.GET_PERMISSIONS)); - when(PermissionHelper.hasProtectedSignalsPermission(CONTEXT, CONTEXT.getPackageName())) - .thenReturn(false); - - SecurityException exception = - assertThrows( - SecurityException.class, - () -> - mChecker.assertAppDeclaredProtectedSignalsPermission( + mChecker.assertAppDeclaredPermission( CONTEXT, "mismatchedAppPackageName", API_NAME_LOGGING_ID)); assertEquals( @@ -286,22 +239,11 @@ public final class FledgeAuthorizationFilterTest extends AdServicesExtendedMocki } @Test - public void testAssertAppHasCaPermission_nullContext_throwNpe() { - assertThrows( - NullPointerException.class, - () -> - mChecker.assertAppDeclaredCustomAudiencePermission( - null, CustomAudienceFixture.VALID_OWNER, API_NAME_LOGGING_ID)); - - verifyZeroInteractions(mPackageManagerMock, mEnrollmentDaoMock, mAdServicesLoggerMock); - } - - @Test - public void testAssertAppHasPasPermission_nullContext_throwNpe() { + public void testAssertAppHasPermission_nullContext_throwNpe() { assertThrows( NullPointerException.class, () -> - mChecker.assertAppDeclaredProtectedSignalsPermission( + mChecker.assertAppDeclaredPermission( null, CustomAudienceFixture.VALID_OWNER, API_NAME_LOGGING_ID)); verifyZeroInteractions(mPackageManagerMock, mEnrollmentDaoMock, mAdServicesLoggerMock); diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/FledgeE2ETest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/FledgeE2ETest.java index e0cd17846..8e77cfdc3 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/FledgeE2ETest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/FledgeE2ETest.java @@ -122,7 +122,6 @@ import com.android.adservices.data.adselection.AdSelectionServerDatabase; import com.android.adservices.data.adselection.AppInstallDao; import com.android.adservices.data.adselection.DBAdSelectionDebugReport; import com.android.adservices.data.adselection.EncryptionContextDao; -import com.android.adservices.data.adselection.EncryptionKeyDao; import com.android.adservices.data.adselection.FrequencyCapDao; import com.android.adservices.data.adselection.SharedStorageDatabase; import com.android.adservices.data.common.DBAdData; @@ -132,6 +131,8 @@ import com.android.adservices.data.customaudience.CustomAudienceDao; import com.android.adservices.data.customaudience.CustomAudienceDatabase; import com.android.adservices.data.customaudience.DBCustomAudience; import com.android.adservices.data.customaudience.DBTrustedBiddingData; +import com.android.adservices.data.encryptionkey.EncryptionKeyDao; +import com.android.adservices.data.enrollment.EnrollmentDao; import com.android.adservices.data.signals.EncodedPayloadDao; import com.android.adservices.data.signals.ProtectedSignalsDatabase; import com.android.adservices.service.Flags; @@ -323,6 +324,7 @@ public class FledgeE2ETest { private AppInstallDao mAppInstallDao; private FrequencyCapDao mFrequencyCapDao; private EncryptionKeyDao mEncryptionKeyDao; + private EnrollmentDao mEnrollmentDao; private EncryptionContextDao mEncryptionContextDao; private ExecutorService mLightweightExecutorService; private ExecutorService mBackgroundExecutorService; @@ -389,8 +391,8 @@ public class FledgeE2ETest { mFrequencyCapDao = sharedDb.frequencyCapDao(); AdSelectionServerDatabase serverDb = Room.inMemoryDatabaseBuilder(CONTEXT_SPY, AdSelectionServerDatabase.class).build(); - mEncryptionContextDao = serverDb.encryptionContextDao(); - mEncryptionKeyDao = serverDb.encryptionKeyDao(); + mEncryptionKeyDao = EncryptionKeyDao.getInstance(CONTEXT_SPY); + mEnrollmentDao = EnrollmentDao.getInstance(CONTEXT_SPY); mAdFilteringFeatureFactory = new AdFilteringFeatureFactory(mAppInstallDao, mFrequencyCapDao, DEFAULT_FLAGS); @@ -1232,8 +1234,8 @@ public class FledgeE2ETest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilterMock, mLightweightExecutorService, @@ -1393,8 +1395,8 @@ public class FledgeE2ETest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilterMock, mLightweightExecutorService, @@ -2990,6 +2992,35 @@ public class FledgeE2ETest { @Test public void testFledgeFlowSuccessWithMockServer_ContextualAdsFlow() throws Exception { + // Reinitializing service so default flags are used + // Create an instance of AdSelection Service with real dependencies + mAdSelectionService = + new AdSelectionServiceImpl( + mAdSelectionEntryDao, + mAppInstallDao, + mCustomAudienceDao, + mEncodedPayloadDao, + mFrequencyCapDao, + mEncryptionKeyDao, + mEnrollmentDao, + mAdServicesHttpsClient, + mDevContextFilterMock, + mLightweightExecutorService, + mBackgroundExecutorService, + mScheduledExecutor, + CONTEXT_SPY, + mAdServicesLogger, + DEFAULT_FLAGS, + CallingAppUidSupplierProcessImpl.create(), + mFledgeAuthorizationFilterMock, + mAdSelectionServiceFilterMock, + mAdFilteringFeatureFactory, + mConsentManagerMock, + mObliviousHttpEncryptorMock, + mAdSelectionDebugReportDao, + mAdIdFetcher, + false); + doReturn(AdServicesApiConsent.GIVEN) .when(mConsentManagerMock) .getConsent(AdServicesApiType.FLEDGE); @@ -4335,8 +4366,8 @@ public class FledgeE2ETest { mCustomAudienceDao, mEncodedPayloadDao, mFrequencyCapDao, - mEncryptionContextDao, mEncryptionKeyDao, + mEnrollmentDao, mAdServicesHttpsClient, mDevContextFilterMock, mLightweightExecutorService, @@ -4955,11 +4986,12 @@ public class FledgeE2ETest { AdTechIdentifier.fromString( mockWebServerRule.uriForPath(BUYER_BIDDING_LOGIC_URI_PATH).getHost()); SignedContextualAds contextualAds = - SignedContextualAdsFixture.generateSignedContextualAds( - buyer, ImmutableList.of(100.0, 200.0, 300.0, 400.0, 500.0)) - .setDecisionLogicUri( - mockWebServerRule.uriForPath(BUYER_BIDDING_LOGIC_URI_PATH)) - .build(); + SignedContextualAdsFixture.signContextualAds( + SignedContextualAdsFixture.aContextualAdsWithEmptySignatureBuilder( + buyer, ImmutableList.of(100.0, 200.0, 300.0, 400.0, 500.0)) + .setDecisionLogicUri( + mockWebServerRule.uriForPath( + BUYER_BIDDING_LOGIC_URI_PATH))); buyerContextualAds.put(buyer, contextualAds); return buyerContextualAds; } diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/PackageChangedReceiverTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/PackageChangedReceiverTest.java index 03635db38..7c4f4297c 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/PackageChangedReceiverTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/PackageChangedReceiverTest.java @@ -16,6 +16,8 @@ package com.android.adservices.service.common; +import static android.adservices.common.CommonFixture.doSleep; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyLong; @@ -64,6 +66,7 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.modules.utils.build.SdkLevel; import org.junit.After; +import org.junit.Assume; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -326,6 +329,7 @@ public class PackageChangedReceiverTest { @Test public void testReceivePackageFullyRemoved_consent_noPackageUid() throws InterruptedException, IOException { + Assume.assumeTrue(SdkLevel.isAtLeastS()); Intent intent = createIntentSentByAdServiceSystemService( PackageChangedReceiver.PACKAGE_FULLY_REMOVED); @@ -342,6 +346,7 @@ public class PackageChangedReceiverTest { @Test public void testReceivePackageFullyRemoved_consent_packageUidIsExplicitlyDefault() throws InterruptedException, IOException { + Assume.assumeTrue(SdkLevel.isAtLeastS()); Intent intent = createIntentSentByAdServiceSystemService( PackageChangedReceiver.PACKAGE_FULLY_REMOVED); @@ -354,6 +359,7 @@ public class PackageChangedReceiverTest { @Test public void testReceivePackageFullyRemoved_consent_noPackageUid_backCompat() throws InterruptedException, IOException { + Assume.assumeTrue(SdkLevel.isAtLeastS()); Intent intent = createIntentSentBySystem(Intent.ACTION_PACKAGE_FULLY_REMOVED); intent.removeExtra(Intent.EXTRA_UID); @@ -364,6 +370,7 @@ public class PackageChangedReceiverTest { @Test public void testReceivePackageFullyRemoved_consent_packageUidIsExplicitlyDefault_backCompat() throws InterruptedException, IOException { + Assume.assumeTrue(SdkLevel.isAtLeastS()); Intent intent = createIntentSentBySystem(Intent.ACTION_PACKAGE_FULLY_REMOVED); intent.putExtra(Intent.EXTRA_UID, DEFAULT_PACKAGE_UID); @@ -711,6 +718,7 @@ public class PackageChangedReceiverTest { } private void runPackageFullyRemovedForConsent(Intent intent) throws Exception { + Assume.assumeTrue(SdkLevel.isAtLeastS()); // Start a mockitoSession to mock static method // Lenient added to allow easy disabling of other APIs' methods MockitoSession session = @@ -1159,4 +1167,35 @@ public class PackageChangedReceiverTest { mMockitoSession.finishMocking(); } } + + @Test + public void testAppConsentDeletion_onR() throws Exception { + MockitoSession mMockitoSession = + ExtendedMockito.mockitoSession() + .mockStatic(SdkLevel.class) + .mockStatic(ConsentManager.class) + .strictness(Strictness.LENIENT) + .initMocks(this) + .startMocking(); + try { + ExtendedMockito.doReturn(false).when(SdkLevel::isAtLeastS); + doReturn(mConsentManager).when(() -> ConsentManager.getInstance(any())); + PackageChangedReceiver spyReceiver = createSpyPackageReceiverForConsent(); + Intent intent = + createIntentSentByAdServiceSystemService( + PackageChangedReceiver.PACKAGE_FULLY_REMOVED); + doReturn(false).when(spyReceiver).isPackageStillInstalled(any(), anyString()); + + // Invoke the onReceive method to test the behavior + spyReceiver.onReceive(sContext, intent); + + verify(spyReceiver).consentOnPackageFullyRemoved(any(), any(), anyInt()); + doSleep(BACKGROUND_THREAD_TIMEOUT_MS); + + // On R App consent clear should not be called as it is not supported + verify(mConsentManager, never()).clearConsentForUninstalledApp(any(), anyInt()); + } finally { + mMockitoSession.finishMocking(); + } + } } diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/PermissionHelperTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/PermissionHelperTest.java index e723d48ab..652ba36e6 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/PermissionHelperTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/common/PermissionHelperTest.java @@ -19,7 +19,6 @@ package com.android.adservices.service.common; import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID; import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION; import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_CUSTOM_AUDIENCE; -import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_PROTECTED_SIGNALS; import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_TOPICS; import static com.google.common.truth.Truth.assertThat; @@ -67,8 +66,7 @@ public class PermissionHelperTest { ACCESS_ADSERVICES_TOPICS, ACCESS_ADSERVICES_AD_ID, ACCESS_ADSERVICES_ATTRIBUTION, - ACCESS_ADSERVICES_CUSTOM_AUDIENCE, - ACCESS_ADSERVICES_PROTECTED_SIGNALS + ACCESS_ADSERVICES_CUSTOM_AUDIENCE }; doReturn(packageInfoGrant) .when(mMockPackageManagerGrant) @@ -170,10 +168,6 @@ public class PermissionHelperTest { getMockContext(ACCESS_ADSERVICES_CUSTOM_AUDIENCE, mMockPackageManagerGrant); assertThat(PermissionHelper.hasCustomAudiencesPermission(mockContext3, APP_PACKAGE_NAME)) .isTrue(); - Context mockContext4 = - getMockContext(ACCESS_ADSERVICES_PROTECTED_SIGNALS, mMockPackageManagerGrant); - assertThat(PermissionHelper.hasProtectedSignalsPermission(mockContext4, APP_PACKAGE_NAME)) - .isTrue(); } @Test @@ -191,8 +185,6 @@ public class PermissionHelperTest { .isFalse(); assertThat(PermissionHelper.hasCustomAudiencesPermission(mockContext, APP_PACKAGE_NAME)) .isFalse(); - assertThat(PermissionHelper.hasProtectedSignalsPermission(mockContext, APP_PACKAGE_NAME)) - .isFalse(); assertThat(PermissionHelper.hasAccessAdServicesStatePermission(mockContext)).isFalse(); assertThat(PermissionHelper.hasModifyAdServicesStatePermission(mockContext)).isFalse(); } diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/consent/ConsentMigrationUtilsTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/consent/ConsentMigrationUtilsTest.java index 3525f21ad..fd4e96d75 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/consent/ConsentMigrationUtilsTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/consent/ConsentMigrationUtilsTest.java @@ -24,6 +24,7 @@ import static android.adservices.extdata.AdServicesExtDataParams.STATE_UNKNOWN; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.never; @@ -42,6 +43,8 @@ import com.android.adservices.data.common.BooleanFileDatastore; import com.android.adservices.mockito.AdServicesExtendedMockitoRule; import com.android.adservices.service.appsearch.AppSearchConsentManager; import com.android.adservices.service.extdata.AdServicesExtDataStorageServiceManager; +import com.android.adservices.service.stats.ConsentMigrationStats; +import com.android.adservices.service.stats.StatsdAdServicesLogger; import com.android.modules.utils.build.SdkLevel; import org.junit.Rule; @@ -68,9 +71,14 @@ public class ConsentMigrationUtilsTest { .setManualInteractionWithConsentStatus(STATE_UNKNOWN) .build(); + private static final int REGION_ROW_CODE = 2; + @Rule public final AdServicesExtendedMockitoRule mExtendedMockitoRule = - new AdServicesExtendedMockitoRule.Builder(this).spyStatic(SdkLevel.class).build(); + new AdServicesExtendedMockitoRule.Builder(this) + .spyStatic(SdkLevel.class) + .spyStatic(DeviceRegionProvider.class) + .build(); @Spy private final Context mContextSpy = ApplicationProvider.getApplicationContext(); @Mock private BooleanFileDatastore mDatastoreMock; @@ -79,6 +87,7 @@ public class ConsentMigrationUtilsTest { @Mock private SharedPreferences mSharedPreferencesMock; @Mock private SharedPreferences.Editor mSharedPreferencesEditorMock; @Mock private AdServicesExtDataParams mAdServicesExtDataParamsMock; + @Mock private StatsdAdServicesLogger mStatsdAdServicesLoggerMock; @Test public void testHandleConsentMigrationToAppSearchIfNeeded_onR_skipsMigration() { @@ -89,10 +98,12 @@ public class ConsentMigrationUtilsTest { mContextSpy, mDatastoreMock, mAppSearchConsentManagerMock, - mAdServicesExtDataManagerMock); + mAdServicesExtDataManagerMock, + mStatsdAdServicesLoggerMock); verifyZeroInteractions(mAppSearchConsentManagerMock); verifyZeroInteractions(mAdServicesExtDataManagerMock); + verifyZeroInteractions(mStatsdAdServicesLoggerMock); } @Test @@ -103,10 +114,12 @@ public class ConsentMigrationUtilsTest { mContextSpy, mDatastoreMock, mAppSearchConsentManagerMock, - mAdServicesExtDataManagerMock); + mAdServicesExtDataManagerMock, + mStatsdAdServicesLoggerMock); verifyZeroInteractions(mAppSearchConsentManagerMock); verifyZeroInteractions(mAdServicesExtDataManagerMock); + verifyZeroInteractions(mStatsdAdServicesLoggerMock); } @Test @@ -115,10 +128,15 @@ public class ConsentMigrationUtilsTest { mockSDevice(); ConsentMigrationUtils.handleConsentMigrationToAppSearchIfNeeded( - mContextSpy, mDatastoreMock, mAppSearchConsentManagerMock, null); + mContextSpy, + mDatastoreMock, + mAppSearchConsentManagerMock, + null, + mStatsdAdServicesLoggerMock); verifyZeroInteractions(mAdServicesExtDataManagerMock); verifyZeroInteractions(mAppSearchConsentManagerMock); + verifyZeroInteractions(mStatsdAdServicesLoggerMock); } @Test @@ -136,10 +154,12 @@ public class ConsentMigrationUtilsTest { mContextSpy, mDatastoreMock, mAppSearchConsentManagerMock, - mAdServicesExtDataManagerMock); + mAdServicesExtDataManagerMock, + mStatsdAdServicesLoggerMock); verifyZeroInteractions(mAppSearchConsentManagerMock); verifyZeroInteractions(mAdServicesExtDataManagerMock); + verifyZeroInteractions(mStatsdAdServicesLoggerMock); } @Test @@ -157,12 +177,14 @@ public class ConsentMigrationUtilsTest { mContextSpy, mDatastoreMock, mAppSearchConsentManagerMock, - mAdServicesExtDataManagerMock); + mAdServicesExtDataManagerMock, + mStatsdAdServicesLoggerMock); verify(mAppSearchConsentManagerMock).wasU18NotificationDisplayed(); verifyNoMoreInteractions(mAppSearchConsentManagerMock); verifyZeroInteractions(mAdServicesExtDataManagerMock); + verifyZeroInteractions(mStatsdAdServicesLoggerMock); } @Test @@ -183,7 +205,8 @@ public class ConsentMigrationUtilsTest { mContextSpy, mDatastoreMock, mAppSearchConsentManagerMock, - mAdServicesExtDataManagerMock); + mAdServicesExtDataManagerMock, + mStatsdAdServicesLoggerMock); verify(mAppSearchConsentManagerMock).wasU18NotificationDisplayed(); verify(mAppSearchConsentManagerMock).wasGaUxNotificationDisplayed(); @@ -192,6 +215,7 @@ public class ConsentMigrationUtilsTest { verify(mAdServicesExtDataManagerMock).getAdServicesExtData(); verifyNoMoreInteractions(mAdServicesExtDataManagerMock); + verifyZeroInteractions(mStatsdAdServicesLoggerMock); } @Test @@ -211,11 +235,14 @@ public class ConsentMigrationUtilsTest { when(mSharedPreferencesMock.edit()).thenReturn(mSharedPreferencesEditorMock); when(mSharedPreferencesEditorMock.commit()).thenReturn(true); + doReturn(false).when(() -> DeviceRegionProvider.isEuDevice(any())); + ConsentMigrationUtils.handleConsentMigrationToAppSearchIfNeeded( mContextSpy, mDatastoreMock, mAppSearchConsentManagerMock, - mAdServicesExtDataManagerMock); + mAdServicesExtDataManagerMock, + mStatsdAdServicesLoggerMock); verify(mAppSearchConsentManagerMock).wasU18NotificationDisplayed(); verify(mAppSearchConsentManagerMock).wasGaUxNotificationDisplayed(); @@ -241,6 +268,21 @@ public class ConsentMigrationUtilsTest { verify(mAdServicesExtDataManagerMock).getAdServicesExtData(); verify(mAdServicesExtDataManagerMock).clearDataOnOtaAsync(); verifyNoMoreInteractions(mAdServicesExtDataManagerMock); + + ConsentMigrationStats consentMigrationStats = + ConsentMigrationStats.builder() + .setTopicsConsent(false) + .setFledgeConsent(false) + .setMsmtConsent(false) + .setDefaultConsent(true) + .setMigrationStatus( + ConsentMigrationStats.MigrationStatus + .SUCCESS_WITH_SHARED_PREF_UPDATED) + .setMigrationType( + ConsentMigrationStats.MigrationType.ADEXT_SERVICE_TO_APPSEARCH) + .setRegion(REGION_ROW_CODE) + .build(); + verify(mStatsdAdServicesLoggerMock).logConsentMigrationStats(consentMigrationStats); } @Test @@ -260,11 +302,14 @@ public class ConsentMigrationUtilsTest { when(mSharedPreferencesMock.edit()).thenReturn(mSharedPreferencesEditorMock); when(mSharedPreferencesEditorMock.commit()).thenReturn(false); + doReturn(false).when(() -> DeviceRegionProvider.isEuDevice(any())); + ConsentMigrationUtils.handleConsentMigrationToAppSearchIfNeeded( mContextSpy, mDatastoreMock, mAppSearchConsentManagerMock, - mAdServicesExtDataManagerMock); + mAdServicesExtDataManagerMock, + mStatsdAdServicesLoggerMock); verify(mAppSearchConsentManagerMock).wasU18NotificationDisplayed(); verify(mAppSearchConsentManagerMock).wasGaUxNotificationDisplayed(); @@ -291,6 +336,21 @@ public class ConsentMigrationUtilsTest { verify(mAdServicesExtDataManagerMock).getAdServicesExtData(); verify(mAdServicesExtDataManagerMock).clearDataOnOtaAsync(); verifyNoMoreInteractions(mAdServicesExtDataManagerMock); + + ConsentMigrationStats consentMigrationStats = + ConsentMigrationStats.builder() + .setTopicsConsent(false) + .setFledgeConsent(false) + .setMsmtConsent(true) + .setDefaultConsent(false) + .setMigrationStatus( + ConsentMigrationStats.MigrationStatus + .SUCCESS_WITH_SHARED_PREF_NOT_UPDATED) + .setMigrationType( + ConsentMigrationStats.MigrationType.ADEXT_SERVICE_TO_APPSEARCH) + .setRegion(REGION_ROW_CODE) + .build(); + verify(mStatsdAdServicesLoggerMock).logConsentMigrationStats(consentMigrationStats); } private void mockNoNotifOnS() { diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/customaudience/CustomAudienceServiceEndToEndTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/customaudience/CustomAudienceServiceEndToEndTest.java index a6b65749a..6652bb25f 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/customaudience/CustomAudienceServiceEndToEndTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/customaudience/CustomAudienceServiceEndToEndTest.java @@ -359,7 +359,7 @@ public class CustomAudienceServiceEndToEndTest { // Bypass the permission check since it's enforced before the package name check doNothing() .when(mFledgeAuthorizationFilterSpy) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, otherOwnerPackageName, AD_SERVICES_API_CALLED__API_NAME__JOIN_CUSTOM_AUDIENCE); @@ -381,7 +381,7 @@ public class CustomAudienceServiceEndToEndTest { CommonFixture.VALID_BUYER_1, VALID_NAME)); verify(mFledgeAuthorizationFilterSpy) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, otherOwnerPackageName, AD_SERVICES_API_CALLED__API_NAME__JOIN_CUSTOM_AUDIENCE); @@ -752,7 +752,7 @@ public class CustomAudienceServiceEndToEndTest { // Bypass the permission check since it's enforced before the package name check doNothing() .when(mFledgeAuthorizationFilterSpy) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, otherOwnerPackageName, AD_SERVICES_API_CALLED__API_NAME__LEAVE_CUSTOM_AUDIENCE); @@ -766,7 +766,7 @@ public class CustomAudienceServiceEndToEndTest { AdServicesStatusUtils.SECURITY_EXCEPTION_CALLER_NOT_ALLOWED_ON_BEHALF_ERROR_MESSAGE, callback.getException().getMessage()); verify(mFledgeAuthorizationFilterSpy) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, otherOwnerPackageName, AD_SERVICES_API_CALLED__API_NAME__LEAVE_CUSTOM_AUDIENCE); @@ -971,7 +971,7 @@ public class CustomAudienceServiceEndToEndTest { // Bypass the permission check since it's enforced before the package name check doNothing() .when(mFledgeAuthorizationFilterSpy) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, MY_APP_PACKAGE_NAME, AD_SERVICES_API_CALLED__API_NAME__OVERRIDE_CUSTOM_AUDIENCE_REMOTE_INFO); @@ -995,7 +995,7 @@ public class CustomAudienceServiceEndToEndTest { mCustomAudienceDao.doesCustomAudienceOverrideExist(otherOwner, BUYER_1, NAME_1)); verify(mFledgeAuthorizationFilterSpy) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, MY_APP_PACKAGE_NAME, AD_SERVICES_API_CALLED__API_NAME__OVERRIDE_CUSTOM_AUDIENCE_REMOTE_INFO); @@ -1107,7 +1107,7 @@ public class CustomAudienceServiceEndToEndTest { // Bypass the permission check since it's enforced before the package name check doNothing() .when(mFledgeAuthorizationFilterSpy) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, incorrectPackageName, AD_SERVICES_API_CALLED__API_NAME__REMOVE_CUSTOM_AUDIENCE_REMOTE_INFO_OVERRIDE); @@ -1135,7 +1135,7 @@ public class CustomAudienceServiceEndToEndTest { mCustomAudienceDao.doesCustomAudienceOverrideExist( MY_APP_PACKAGE_NAME, BUYER_1, NAME_1)); verify(mFledgeAuthorizationFilterSpy) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, incorrectPackageName, AD_SERVICES_API_CALLED__API_NAME__REMOVE_CUSTOM_AUDIENCE_REMOTE_INFO_OVERRIDE); @@ -1289,7 +1289,7 @@ public class CustomAudienceServiceEndToEndTest { // Bypass the permission check since it's enforced before the package name check doNothing() .when(mFledgeAuthorizationFilterSpy) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, incorrectPackageName, AD_SERVICES_API_CALLED__API_NAME__RESET_ALL_CUSTOM_AUDIENCE_OVERRIDES); @@ -1334,7 +1334,7 @@ public class CustomAudienceServiceEndToEndTest { mCustomAudienceDao.doesCustomAudienceOverrideExist( MY_APP_PACKAGE_NAME, BUYER_2, NAME_2)); verify(mFledgeAuthorizationFilterSpy) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, incorrectPackageName, AD_SERVICES_API_CALLED__API_NAME__RESET_ALL_CUSTOM_AUDIENCE_OVERRIDES); diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/customaudience/CustomAudienceServiceImplTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/customaudience/CustomAudienceServiceImplTest.java index 1073c08da..709c6bd74 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/customaudience/CustomAudienceServiceImplTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/customaudience/CustomAudienceServiceImplTest.java @@ -212,7 +212,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki CustomAudienceFixture.VALID_OWNER, mICustomAudienceCallbackMock); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, AD_SERVICES_API_CALLED__API_NAME__JOIN_CUSTOM_AUDIENCE); @@ -260,7 +260,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki CustomAudienceFixture.VALID_OWNER, mICustomAudienceCallbackMock); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( any(), eq(CustomAudienceFixture.VALID_OWNER), eq(AD_SERVICES_API_CALLED__API_NAME__JOIN_CUSTOM_AUDIENCE)); @@ -326,7 +326,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki mICustomAudienceCallbackMock)); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, AD_SERVICES_API_CALLED__API_NAME__JOIN_CUSTOM_AUDIENCE); @@ -349,7 +349,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki mICustomAudienceCallbackMock); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, AD_SERVICES_API_CALLED__API_NAME__JOIN_CUSTOM_AUDIENCE); @@ -416,7 +416,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki mICustomAudienceCallbackMock); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, AD_SERVICES_API_CALLED__API_NAME__JOIN_CUSTOM_AUDIENCE); @@ -464,7 +464,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki mICustomAudienceCallbackMock); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, AD_SERVICES_API_CALLED__API_NAME__JOIN_CUSTOM_AUDIENCE); @@ -513,7 +513,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki CustomAudienceFixture.VALID_OWNER, mICustomAudienceCallbackMock); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, AD_SERVICES_API_CALLED__API_NAME__JOIN_CUSTOM_AUDIENCE); @@ -620,7 +620,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki mFetchAndJoinCustomAudienceCallbackMock)); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, AD_SERVICES_API_CALLED__API_NAME__FETCH_AND_JOIN_CUSTOM_AUDIENCE); @@ -638,7 +638,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki mICustomAudienceCallbackMock); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( any(), eq(CustomAudienceFixture.VALID_OWNER), eq(AD_SERVICES_API_CALLED__API_NAME__LEAVE_CUSTOM_AUDIENCE)); @@ -682,7 +682,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki mICustomAudienceCallbackMock); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( any(), eq(CustomAudienceFixture.VALID_OWNER), eq(AD_SERVICES_API_CALLED__API_NAME__LEAVE_CUSTOM_AUDIENCE)); @@ -749,7 +749,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki mICustomAudienceCallbackMock)); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( any(), eq(CustomAudienceFixture.VALID_OWNER), eq(AD_SERVICES_API_CALLED__API_NAME__LEAVE_CUSTOM_AUDIENCE)); @@ -773,7 +773,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki mICustomAudienceCallbackMock); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( any(), eq(CustomAudienceFixture.VALID_OWNER), eq(AD_SERVICES_API_CALLED__API_NAME__LEAVE_CUSTOM_AUDIENCE)); @@ -862,7 +862,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki mICustomAudienceCallbackMock); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( any(), eq(CustomAudienceFixture.VALID_OWNER), eq(AD_SERVICES_API_CALLED__API_NAME__LEAVE_CUSTOM_AUDIENCE)); @@ -910,7 +910,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki .assertCallerIsInForeground( MY_UID, AD_SERVICES_API_CALLED__API_NAME__LEAVE_CUSTOM_AUDIENCE, null); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( any(), eq(CustomAudienceFixture.VALID_OWNER), eq(AD_SERVICES_API_CALLED__API_NAME__LEAVE_CUSTOM_AUDIENCE)); @@ -954,7 +954,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki mICustomAudienceCallbackMock); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, AD_SERVICES_API_CALLED__API_NAME__JOIN_CUSTOM_AUDIENCE); @@ -1003,7 +1003,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki CustomAudienceFixture.VALID_OWNER, mICustomAudienceCallbackMock); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, AD_SERVICES_API_CALLED__API_NAME__JOIN_CUSTOM_AUDIENCE); @@ -1050,7 +1050,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki mICustomAudienceCallbackMock); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, AD_SERVICES_API_CALLED__API_NAME__LEAVE_CUSTOM_AUDIENCE); @@ -1102,7 +1102,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki mICustomAudienceCallbackMock); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, AD_SERVICES_API_CALLED__API_NAME__LEAVE_CUSTOM_AUDIENCE); @@ -1158,7 +1158,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki mCustomAudienceOverrideCallbackMock); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, "", AD_SERVICES_API_CALLED__API_NAME__OVERRIDE_CUSTOM_AUDIENCE_REMOTE_INFO); @@ -1222,7 +1222,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki mCustomAudienceOverrideCallbackMock); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, AD_SERVICES_API_CALLED__API_NAME__OVERRIDE_CUSTOM_AUDIENCE_REMOTE_INFO); @@ -1268,8 +1268,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki CustomAudienceFixture.VALID_NAME, mCustomAudienceOverrideCallbackMock); - verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission(CONTEXT, "", apiName); + verify(mFledgeAuthorizationFilterMock).assertAppDeclaredPermission(CONTEXT, "", apiName); verify(mDevContextFilterMock).createDevContext(); verify(mCustomAudienceImplMock).getCustomAudienceDao(); verify(mAppImportanceFilterMock) @@ -1322,8 +1321,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki mCustomAudienceOverrideCallbackMock); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( - CONTEXT, CustomAudienceFixture.VALID_OWNER, apiName); + .assertAppDeclaredPermission(CONTEXT, CustomAudienceFixture.VALID_OWNER, apiName); verify(mDevContextFilterMock).createDevContext(); verify(mCustomAudienceImplMock).getCustomAudienceDao(); verify(mConsentManagerMock).isFledgeConsentRevokedForApp(CustomAudienceFixture.VALID_OWNER); @@ -1352,8 +1350,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki mService.resetAllCustomAudienceOverrides(mCustomAudienceOverrideCallbackMock); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( - CONTEXT, CustomAudienceFixture.VALID_OWNER, apiName); + .assertAppDeclaredPermission(CONTEXT, CustomAudienceFixture.VALID_OWNER, apiName); verify(mDevContextFilterMock).createDevContext(); verify(mCustomAudienceImplMock).getCustomAudienceDao(); verify(mAppImportanceFilterMock).assertCallerIsInForeground(Process.myUid(), apiName, null); @@ -1400,7 +1397,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki mService.resetAllCustomAudienceOverrides(mCustomAudienceOverrideCallbackMock); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, AD_SERVICES_API_CALLED__API_NAME__RESET_ALL_CUSTOM_AUDIENCE_OVERRIDES); @@ -1419,7 +1416,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki public void testAppManifestPermissionNotRequested_joinCustomAudience_fails() { doThrow(SecurityException.class) .when(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, AD_SERVICES_API_CALLED__API_NAME__JOIN_CUSTOM_AUDIENCE); @@ -1437,7 +1434,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki public void testAppManifestPermissionNotRequested_fetchCustomAudience_fails() { doThrow(SecurityException.class) .when(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, AD_SERVICES_API_CALLED__API_NAME__FETCH_AND_JOIN_CUSTOM_AUDIENCE); @@ -1458,7 +1455,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki public void testAppManifestPermissionNotRequested_leaveCustomAudience_fails() { doThrow(SecurityException.class) .when(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, AD_SERVICES_API_CALLED__API_NAME__LEAVE_CUSTOM_AUDIENCE); @@ -1483,7 +1480,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki .build()); doThrow(SecurityException.class) .when(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, AD_SERVICES_API_CALLED__API_NAME__OVERRIDE_CUSTOM_AUDIENCE_REMOTE_INFO); @@ -1513,8 +1510,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki int apiName = AD_SERVICES_API_CALLED__API_NAME__REMOVE_CUSTOM_AUDIENCE_REMOTE_INFO_OVERRIDE; doThrow(SecurityException.class) .when(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( - CONTEXT, CustomAudienceFixture.VALID_OWNER, apiName); + .assertAppDeclaredPermission(CONTEXT, CustomAudienceFixture.VALID_OWNER, apiName); assertThrows( SecurityException.class, @@ -1536,7 +1532,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki .build()); doThrow(SecurityException.class) .when(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, AD_SERVICES_API_CALLED__API_NAME__RESET_ALL_CUSTOM_AUDIENCE_OVERRIDES); @@ -1565,7 +1561,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki mICustomAudienceCallbackMock); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, AD_SERVICES_API_CALLED__API_NAME__JOIN_CUSTOM_AUDIENCE); @@ -1621,7 +1617,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki CustomAudienceFixture.VALID_OWNER, mICustomAudienceCallbackMock); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, AD_SERVICES_API_CALLED__API_NAME__JOIN_CUSTOM_AUDIENCE); @@ -1667,7 +1663,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki mICustomAudienceCallbackMock); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, AD_SERVICES_API_CALLED__API_NAME__LEAVE_CUSTOM_AUDIENCE); @@ -1725,7 +1721,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki mICustomAudienceCallbackMock); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( any(), eq(CustomAudienceFixture.VALID_OWNER), eq(AD_SERVICES_API_CALLED__API_NAME__LEAVE_CUSTOM_AUDIENCE)); @@ -1771,7 +1767,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki CommonFixture.VALID_BUYER_1, AD_SERVICES_API_CALLED__API_NAME__JOIN_CUSTOM_AUDIENCE); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, AD_SERVICES_API_CALLED__API_NAME__JOIN_CUSTOM_AUDIENCE); @@ -1811,7 +1807,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki CommonFixture.VALID_BUYER_1, AD_SERVICES_API_CALLED__API_NAME__LEAVE_CUSTOM_AUDIENCE); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, AD_SERVICES_API_CALLED__API_NAME__LEAVE_CUSTOM_AUDIENCE); @@ -1850,7 +1846,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki RATE_LIMIT_REACHED_ERROR_MESSAGE, actualResponseCaptor.getValue().getErrorMessage()); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, AD_SERVICES_API_CALLED__API_NAME__JOIN_CUSTOM_AUDIENCE); @@ -1881,7 +1877,7 @@ public final class CustomAudienceServiceImplTest extends AdServicesExtendedMocki RATE_LIMIT_REACHED_ERROR_MESSAGE, actualResponseCaptor.getValue().getErrorMessage()); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredCustomAudiencePermission( + .assertAppDeclaredPermission( CONTEXT, CustomAudienceFixture.VALID_OWNER, AD_SERVICES_API_CALLED__API_NAME__LEAVE_CUSTOM_AUDIENCE); diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/E2EInteropMockTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/E2EInteropMockTest.java index 0e5544e83..74f8761d6 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/E2EInteropMockTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/E2EInteropMockTest.java @@ -21,7 +21,6 @@ import static java.util.Map.entry; import android.adservices.measurement.RegistrationRequest; import android.net.Uri; import android.os.RemoteException; -import android.provider.DeviceConfig; import com.android.adservices.service.measurement.actions.Action; import com.android.adservices.service.measurement.actions.RegisterSource; @@ -35,7 +34,6 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.junit.Assert; -import org.junit.Before; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -160,17 +158,6 @@ public class E2EInteropMockTest extends E2EMockTest { mFlags); } - @Before - public void setup() { - // Chromium does not have a flag at dynamic noising based on expiry but Android does, so it - // needs to be enabled. - DeviceConfig.setProperty( - DeviceConfig.NAMESPACE_ADSERVICES, - "measurement_enable_configurable_event_reporting_windows", - "true", - false); - } - @Override void processAction(RegisterSource sourceRegistration) throws JSONException, IOException { RegistrationRequest request = sourceRegistration.mRegistrationRequest; diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/EventReportTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/EventReportTest.java index f0be0f676..ca528c9a3 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/EventReportTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/EventReportTest.java @@ -15,11 +15,7 @@ */ package com.android.adservices.service.measurement; -import static com.android.adservices.service.Flags.MEASUREMENT_EVENT_NOISE_PROBABILITY; -import static com.android.adservices.service.Flags.MEASUREMENT_INSTALL_ATTR_EVENT_NOISE_PROBABILITY; -import static com.android.adservices.service.Flags.MEASUREMENT_INSTALL_ATTR_NAVIGATION_NOISE_PROBABILITY; import static com.android.adservices.service.Flags.MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS; -import static com.android.adservices.service.Flags.MEASUREMENT_NAVIGATION_NOISE_PROBABILITY; import static com.android.adservices.service.measurement.PrivacyParams.INSTALL_ATTR_NAVIGATION_EARLY_REPORTING_WINDOW_MILLISECONDS; import static com.android.adservices.service.measurement.PrivacyParams.NAVIGATION_EARLY_REPORTING_WINDOW_MILLISECONDS; import static com.android.adservices.service.measurement.SourceFixture.ValidSourceParams; @@ -67,6 +63,11 @@ public final class EventReportTest { public final TestableDeviceConfig.TestableDeviceConfigRule mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule(); + private static final double EVENT_NOISE_PROBABILITY = 0.0000025D; + private static final double INSTALL_ATTR_EVENT_NOISE_PROBABILITY = 0.0000125D; + private static final double INSTALL_ATTR_NAVIGATION_NOISE_PROBABILITY = 0.0024263D; + private static final double NAVIGATION_NOISE_PROBABILITY = 0.0024263D; + private static final double DOUBLE_MAX_DELTA = 0.0000001D; private static final long TRIGGER_PRIORITY = 345678L; @@ -288,7 +289,7 @@ public final class EventReportTest { assertEquals(source.getEventReportWindow() + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, report.getReportTime()); assertEquals(source.getSourceType(), report.getSourceType()); - assertEquals(MEASUREMENT_EVENT_NOISE_PROBABILITY, report.getRandomizedTriggerRate(), DOUBLE_MAX_DELTA); + assertEquals(EVENT_NOISE_PROBABILITY, report.getRandomizedTriggerRate(), DOUBLE_MAX_DELTA); assertEquals(SOURCE_ID, report.getSourceId()); assertEquals(TRIGGER_ID, report.getTriggerId()); assertEquals(REGISTRATION_ORIGIN, report.getRegistrationOrigin()); @@ -297,7 +298,6 @@ public final class EventReportTest { @Test public void populate_eventSourceAppDestWithoutInstallConfigured() throws JSONException { long baseTime = System.currentTimeMillis(); - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); String earlyReportingWindows1h1d = String.join( ",", @@ -306,7 +306,6 @@ public final class EventReportTest { doReturn(earlyReportingWindows1h1d) .when(mFlags) .getMeasurementEventReportsVtcEarlyReportingWindows(); - doReturn(true).when(mFlags).getMeasurementEnableVtcConfigurableMaxEventReports(); doReturn(3).when(mFlags).getMeasurementVtcConfigurableMaxEventReportsCount(); Source source = createSourceForTest( @@ -390,7 +389,7 @@ public final class EventReportTest { report.getReportTime()); assertEquals(Source.SourceType.NAVIGATION, report.getSourceType()); assertEquals( - MEASUREMENT_NAVIGATION_NOISE_PROBABILITY, report.getRandomizedTriggerRate(), DOUBLE_MAX_DELTA); + NAVIGATION_NOISE_PROBABILITY, report.getRandomizedTriggerRate(), DOUBLE_MAX_DELTA); assertEquals(SOURCE_ID, report.getSourceId()); assertEquals(TRIGGER_ID, report.getTriggerId()); assertEquals(REGISTRATION_ORIGIN, report.getRegistrationOrigin()); @@ -430,7 +429,7 @@ public final class EventReportTest { assertEquals(source.getEventReportWindow() + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, report.getReportTime()); assertEquals(Source.SourceType.EVENT, report.getSourceType()); - assertEquals(MEASUREMENT_EVENT_NOISE_PROBABILITY, report.getRandomizedTriggerRate(), DOUBLE_MAX_DELTA); + assertEquals(EVENT_NOISE_PROBABILITY, report.getRandomizedTriggerRate(), DOUBLE_MAX_DELTA); assertEquals(SOURCE_ID, report.getSourceId()); assertEquals(TRIGGER_ID, report.getTriggerId()); assertEquals(REGISTRATION_ORIGIN, report.getRegistrationOrigin()); @@ -468,7 +467,7 @@ public final class EventReportTest { report.getReportTime()); assertEquals(Source.SourceType.EVENT, report.getSourceType()); assertEquals( - MEASUREMENT_INSTALL_ATTR_EVENT_NOISE_PROBABILITY, + INSTALL_ATTR_EVENT_NOISE_PROBABILITY, report.getRandomizedTriggerRate(), DOUBLE_MAX_DELTA); assertEquals(SOURCE_ID, report.getSourceId()); @@ -507,7 +506,7 @@ public final class EventReportTest { assertEquals(source.getEventReportWindow() + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, report.getReportTime()); assertEquals(Source.SourceType.EVENT, report.getSourceType()); - assertEquals(MEASUREMENT_EVENT_NOISE_PROBABILITY, report.getRandomizedTriggerRate(), DOUBLE_MAX_DELTA); + assertEquals(EVENT_NOISE_PROBABILITY, report.getRandomizedTriggerRate(), DOUBLE_MAX_DELTA); assertEquals(SOURCE_ID, report.getSourceId()); assertEquals(TRIGGER_ID, report.getTriggerId()); assertEquals(REGISTRATION_ORIGIN, report.getRegistrationOrigin()); @@ -548,7 +547,7 @@ public final class EventReportTest { report.getReportTime()); assertEquals(Source.SourceType.NAVIGATION, report.getSourceType()); assertEquals( - MEASUREMENT_NAVIGATION_NOISE_PROBABILITY, report.getRandomizedTriggerRate(), DOUBLE_MAX_DELTA); + NAVIGATION_NOISE_PROBABILITY, report.getRandomizedTriggerRate(), DOUBLE_MAX_DELTA); assertEquals(SOURCE_ID, report.getSourceId()); assertEquals(TRIGGER_ID, report.getTriggerId()); assertEquals(REGISTRATION_ORIGIN, report.getRegistrationOrigin()); @@ -589,7 +588,7 @@ public final class EventReportTest { report.getReportTime()); assertEquals(source.getSourceType(), report.getSourceType()); assertEquals( - MEASUREMENT_NAVIGATION_NOISE_PROBABILITY, report.getRandomizedTriggerRate(), DOUBLE_MAX_DELTA); + NAVIGATION_NOISE_PROBABILITY, report.getRandomizedTriggerRate(), DOUBLE_MAX_DELTA); assertEquals(SOURCE_ID, report.getSourceId()); assertEquals(TRIGGER_ID, report.getTriggerId()); assertEquals(REGISTRATION_ORIGIN, report.getRegistrationOrigin()); @@ -633,7 +632,7 @@ public final class EventReportTest { report.getReportTime()); assertEquals(Source.SourceType.NAVIGATION, report.getSourceType()); assertEquals( - MEASUREMENT_INSTALL_ATTR_NAVIGATION_NOISE_PROBABILITY, + INSTALL_ATTR_NAVIGATION_NOISE_PROBABILITY, report.getRandomizedTriggerRate(), DOUBLE_MAX_DELTA); assertEquals(SOURCE_ID, report.getSourceId()); @@ -679,7 +678,7 @@ public final class EventReportTest { report.getReportTime()); assertEquals(source.getSourceType(), report.getSourceType()); assertEquals( - MEASUREMENT_NAVIGATION_NOISE_PROBABILITY, report.getRandomizedTriggerRate(), DOUBLE_MAX_DELTA); + NAVIGATION_NOISE_PROBABILITY, report.getRandomizedTriggerRate(), DOUBLE_MAX_DELTA); assertEquals(SOURCE_ID, report.getSourceId()); assertEquals(TRIGGER_ID, report.getTriggerId()); assertEquals(REGISTRATION_ORIGIN, report.getRegistrationOrigin()); @@ -698,6 +697,7 @@ public final class EventReportTest { .setEnrollmentId("enrollment-id") .setAppDestinations(Collections.singletonList(APP_DESTINATION)) .setWebDestinations(Collections.singletonList(WEB_DESTINATION)) + .setExpiryTime(baseTime + TimeUnit.DAYS.toMillis(30)) .setEventReportWindow(baseTime + TimeUnit.DAYS.toMillis(10)) .setCoarseEventReportDestinations(true) .build(); @@ -738,7 +738,7 @@ public final class EventReportTest { assertEquals(source.getEventReportWindow() + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, report.getReportTime()); assertEquals(Source.SourceType.EVENT, report.getSourceType()); - assertEquals(MEASUREMENT_EVENT_NOISE_PROBABILITY, report.getRandomizedTriggerRate(), DOUBLE_MAX_DELTA); + assertEquals(EVENT_NOISE_PROBABILITY, report.getRandomizedTriggerRate(), DOUBLE_MAX_DELTA); assertEquals(SOURCE_ID, report.getSourceId()); assertEquals(TRIGGER_ID, report.getTriggerId()); assertEquals(REGISTRATION_ORIGIN, report.getRegistrationOrigin()); @@ -855,6 +855,7 @@ public final class EventReportTest { .setEnrollmentId("enrollment-id") .setAppDestinations(getNullableUriList(appDestination)) .setWebDestinations(getNullableUriList(webDestination)) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(30)) .setEventReportWindow(eventTime + TimeUnit.DAYS.toMillis(10)) .build(); } diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/SourceTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/SourceTest.java index 458efd4f0..f75c65e24 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/SourceTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/SourceTest.java @@ -1521,13 +1521,13 @@ public class SourceTest { } @Test - public void getOrDefaultEventReportWindows() throws JSONException { + public void getOrDefaultEventReportWindowsForFlex() throws JSONException { Flags flags = mock(Flags.class); JSONObject windowsObj = new JSONObject("{'start_time': '2000000', 'end_times': " + "[3600000, 86400000, 172000000]}"); // Provided Windows List<Pair<Long, Long>> eventReportWindows = - Source.getOrDefaultEventReportWindows( + Source.getOrDefaultEventReportWindowsForFlex( windowsObj, Source.SourceType.EVENT, 8640000, @@ -1543,7 +1543,7 @@ public class SourceTest { when(flags.getMeasurementEventReportsCtcEarlyReportingWindows()) .thenReturn("172800,604800"); eventReportWindows = - Source.getOrDefaultEventReportWindows( + Source.getOrDefaultEventReportWindowsForFlex( null, Source.SourceType.EVENT, TimeUnit.DAYS.toMillis(15), flags); assertNotNull(eventReportWindows); assertEquals(2, eventReportWindows.size()); @@ -1552,7 +1552,7 @@ public class SourceTest { // Default Windows - Navigation eventReportWindows = - Source.getOrDefaultEventReportWindows( + Source.getOrDefaultEventReportWindowsForFlex( null, Source.SourceType.NAVIGATION, TimeUnit.DAYS.toMillis(15), flags); assertNotNull(eventReportWindows); assertEquals(3, eventReportWindows.size()); @@ -1608,29 +1608,31 @@ public class SourceTest { } @Test - public void getProcessedEventReportWindow() { + public void getEffectiveEventReportWindow() { + long expiryTime = 7654321L; // null eventReportWindow Source sourceNullEventReportWindow = SourceFixture.getMinimalValidSourceBuilder() .setEventTime(10) + .setExpiryTime(expiryTime) .setEventReportWindow(null) .build(); - assertNull(sourceNullEventReportWindow.getProcessedEventReportWindow()); + assertEquals(expiryTime, sourceNullEventReportWindow.getEffectiveEventReportWindow()); // eventReportWindow Value < eventTime Source sourceNewEventReportWindow = SourceFixture.getMinimalValidSourceBuilder() - .setEventTime(10) + .setEventTime(10L) .setEventReportWindow(4L) .build(); - assertEquals(Long.valueOf(14L), sourceNewEventReportWindow.getProcessedEventReportWindow()); + assertEquals(14L, sourceNewEventReportWindow.getEffectiveEventReportWindow()); // eventReportWindow Value > eventTime Source sourceOldEventReportWindow = SourceFixture.getMinimalValidSourceBuilder() - .setEventTime(10) + .setEventTime(10L) .setEventReportWindow(15L) .build(); - assertEquals(Long.valueOf(15L), sourceOldEventReportWindow.getProcessedEventReportWindow()); + assertEquals(15L, sourceOldEventReportWindow.getEffectiveEventReportWindow()); } } diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/attribution/AttributionJobHandlerIntegrationTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/attribution/AttributionJobHandlerIntegrationTest.java index 6b8e4567a..39f46f31e 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/attribution/AttributionJobHandlerIntegrationTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/attribution/AttributionJobHandlerIntegrationTest.java @@ -77,7 +77,7 @@ public class AttributionJobHandlerIntegrationTest extends AbstractDbIntegrationT (new AttributionJobHandler( datastoreManager, FlagsFactory.getFlags(), - new DebugReportApi(sContext, FlagsFactory.getFlagsForTest()), + new DebugReportApi(sContext, FlagsFactory.getFlags()), new EventReportWindowCalcDelegate(FlagsFactory.getFlags()), new SourceNoiseHandler(FlagsFactory.getFlags()), mLogger, diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/attribution/AttributionJobHandlerTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/attribution/AttributionJobHandlerTest.java index b34950e84..c1cbe2ba8 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/attribution/AttributionJobHandlerTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/attribution/AttributionJobHandlerTest.java @@ -218,6 +218,12 @@ public class AttributionJobHandlerTest { when(mFlags.getMeasurementNullAggReportRateInclSourceRegistrationTime()).thenReturn(0f); when(mFlags.getMeasurementMinEventReportDelayMillis()) .thenReturn(Flags.MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS); + when(mFlags.getMeasurementVtcConfigurableMaxEventReportsCount()) + .thenReturn(Flags.DEFAULT_MEASUREMENT_VTC_CONFIGURABLE_MAX_EVENT_REPORTS_COUNT); + when(mFlags.getMeasurementEventReportsVtcEarlyReportingWindows()) + .thenReturn(Flags.MEASUREMENT_EVENT_REPORTS_VTC_EARLY_REPORTING_WINDOWS); + when(mFlags.getMeasurementEventReportsCtcEarlyReportingWindows()) + .thenReturn(Flags.MEASUREMENT_EVENT_REPORTS_CTC_EARLY_REPORTING_WINDOWS); } @Test @@ -659,7 +665,6 @@ public class AttributionJobHandlerTest { public void performPendingAttributions_vtcWithConfiguredReportsCount_attributeUptoConfigLimit() throws DatastoreException { // Setup - doReturn(true).when(mFlags).getMeasurementEnableVtcConfigurableMaxEventReports(); doReturn(3).when(mFlags).getMeasurementVtcConfigurableMaxEventReportsCount(); Source source = SourceFixture.getMinimalValidSourceBuilder() @@ -1385,6 +1390,9 @@ public class AttributionJobHandlerTest { doReturn(5L) .when(mEventReportWindowCalcDelegate) .getReportingTime(any(Source.class), anyLong(), anyInt()); + doReturn(EventReportWindowCalcDelegate.MomentPlacement.WITHIN) + .when(mEventReportWindowCalcDelegate) + .fallsWithinWindow(any(Source.class), anyLong(), anyInt()); when(mMeasurementDao.getPendingTriggerIds()) .thenReturn(Collections.singletonList(trigger.getId())); when(mMeasurementDao.getTrigger(trigger.getId())).thenReturn(trigger); @@ -1658,6 +1666,7 @@ public class AttributionJobHandlerTest { .setAttributionMode(Source.AttributionMode.TRUTHFULLY) .setInstallAttributed(true) .setInstallCooldownWindow(TimeUnit.DAYS.toMillis(10)) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(28)) .setEventTime(eventTime - TimeUnit.DAYS.toMillis(2)) .setEventReportWindow(triggerTime + 1L) .setAggregatableReportWindow(triggerTime + 1L) @@ -1737,6 +1746,7 @@ public class AttributionJobHandlerTest { .setInstallAttributed(true) .setInstallCooldownWindow(TimeUnit.DAYS.toMillis(3)) .setEventTime(eventTime - TimeUnit.DAYS.toMillis(2)) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(28)) .setEventReportWindow(triggerTime + 1L) .setAggregatableReportWindow(triggerTime + 1L) .build(); @@ -1747,6 +1757,7 @@ public class AttributionJobHandlerTest { .setPriority(200L) .setAttributionMode(Source.AttributionMode.TRUTHFULLY) .setEventTime(eventTime) + .setExpiryTime(triggerTime + TimeUnit.DAYS.toMillis(28)) .setEventReportWindow(triggerTime + 1L) .build(); when(mMeasurementDao.getPendingTriggerIds()) @@ -3341,6 +3352,7 @@ public class AttributionJobHandlerTest { + " \"key_2\": [\"value_1\", \"value_2\"]\n" + "}\n") .setId("sourceId") + .setExpiryTime(triggerTime + TimeUnit.DAYS.toMillis(28)) .setEventReportWindow(triggerTime + 1L) .setAggregatableReportWindow(triggerTime + 1L) .build(); @@ -3436,6 +3448,7 @@ public class AttributionJobHandlerTest { + " \"key_2\": [\"value_1\", \"value_2\"]\n" + "}\n") .setId("sourceId") + .setExpiryTime(triggerTime + TimeUnit.DAYS.toMillis(28)) .setEventReportWindow(triggerTime + 1L) .setAggregatableReportWindow(triggerTime + 1L) .build(); @@ -3533,6 +3546,7 @@ public class AttributionJobHandlerTest { + " \"key_2\": [\"value_1\", \"value_2\"]\n" + "}\n") .setId("sourceId") + .setExpiryTime(triggerTime + TimeUnit.DAYS.toMillis(28)) .setEventReportWindow(triggerTime + 1L) .setAggregatableReportWindow(triggerTime + 1L) .build(); @@ -3628,6 +3642,7 @@ public class AttributionJobHandlerTest { + " \"key_2\": [\"value_1\", \"value_2\"]\n" + "}\n") .setId("sourceId") + .setExpiryTime(triggerTime + TimeUnit.DAYS.toMillis(28)) .setEventReportWindow(triggerTime + 1L) .setAggregatableReportWindow(triggerTime + 1L) .build(); @@ -3726,6 +3741,7 @@ public class AttributionJobHandlerTest { + "}\n") .setId("sourceId") .setSourceType(Source.SourceType.NAVIGATION) + .setExpiryTime(triggerTime + TimeUnit.DAYS.toMillis(28)) .setEventReportWindow(triggerTime + 1L) .setAggregatableReportWindow(triggerTime + 1L) .build(); @@ -3961,6 +3977,7 @@ public class AttributionJobHandlerTest { + " \"key_1\": [\"value_1\", \"value_2\"],\n" + " \"key_2\": [\"value_1\", \"value_2\"]\n" + "}\n") + .setExpiryTime(triggerTime + TimeUnit.DAYS.toMillis(28)) .setEventReportWindow(triggerTime + 1L) .setAggregatableReportWindow(triggerTime + 1L) .build(); @@ -4039,6 +4056,7 @@ public class AttributionJobHandlerTest { + " \"key_1\": [\"value_1\", \"value_2\"],\n" + " \"key_2\": [\"value_1\", \"value_2\"]\n" + "}\n") + .setExpiryTime(triggerTime + TimeUnit.DAYS.toMillis(28)) .setEventReportWindow(triggerTime + 1L) .setAggregatableReportWindow(triggerTime + 1L) .build(); @@ -4116,6 +4134,7 @@ public class AttributionJobHandlerTest { + " \"key_1\": [\"value_1\", \"value_2\"],\n" + " \"key_2\": [\"value_1\", \"value_2\"]\n" + "}\n") + .setExpiryTime(triggerTime + TimeUnit.DAYS.toMillis(28)) .setEventReportWindow(triggerTime + 1L) .setAggregatableReportWindow(triggerTime + 1L) .build(); @@ -4338,6 +4357,7 @@ public class AttributionJobHandlerTest { + " \"key_1\": [\"value_1\", \"value_2\"],\n" + " \"key_2\": [\"value_1\", \"value_2\"]\n" + "}\n") + .setExpiryTime(triggerTime + TimeUnit.DAYS.toMillis(28)) .setEventReportWindow(triggerTime + 1L) .setAggregatableReportWindow(triggerTime + 1L) .build(); @@ -6129,6 +6149,7 @@ public class AttributionJobHandlerTest { .setId("sourceId") .setEventTime(TRIGGER_TIME - TimeUnit.SECONDS.toMillis(LOOKBACK_WINDOW - 1)) .setSourceType(Source.SourceType.NAVIGATION) + .setExpiryTime(TRIGGER_TIME + TimeUnit.DAYS.toMillis(28)) .setEventReportWindow(TRIGGER_TIME + 1) .setAggregatableReportWindow(TRIGGER_TIME + 1) .build(); diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/noising/SourceNoiseHandlerAttributionProbabilityTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/noising/SourceNoiseHandlerAttributionProbabilityTest.java index ef24154ac..a9fa26909 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/noising/SourceNoiseHandlerAttributionProbabilityTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/noising/SourceNoiseHandlerAttributionProbabilityTest.java @@ -16,14 +16,6 @@ package com.android.adservices.service.measurement.noising; -import static com.android.adservices.service.Flags.MEASUREMENT_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY; -import static com.android.adservices.service.Flags.MEASUREMENT_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY; -import static com.android.adservices.service.Flags.MEASUREMENT_EVENT_NOISE_PROBABILITY; -import static com.android.adservices.service.Flags.MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY; -import static com.android.adservices.service.Flags.MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY; -import static com.android.adservices.service.Flags.MEASUREMENT_INSTALL_ATTR_EVENT_NOISE_PROBABILITY; -import static com.android.adservices.service.Flags.MEASUREMENT_INSTALL_ATTR_NAVIGATION_NOISE_PROBABILITY; -import static com.android.adservices.service.Flags.MEASUREMENT_NAVIGATION_NOISE_PROBABILITY; import static com.android.adservices.service.measurement.SourceFixture.ValidSourceParams.ATTRIBUTION_DESTINATIONS; import static com.android.adservices.service.measurement.SourceFixture.ValidSourceParams.WEB_DESTINATIONS; @@ -61,18 +53,22 @@ public class SourceNoiseHandlerAttributionProbabilityTest { private static final String DELIMITER = ","; private static final long CURRENT_TIME = System.currentTimeMillis(); + private static final double INSTALL_ATTR_EVENT_NOISE_PROBABILITY = 0.0000125D; + private static final double INSTALL_ATTR_NAVIGATION_NOISE_PROBABILITY = 0.0024263D; + private static final double EVENT_NOISE_PROBABILITY = 0.0000025D; + private static final double NAVIGATION_NOISE_PROBABILITY = 0.0024263D; + private static final double INSTALL_ATTR_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY = + 0.0170218D; + private final String mDescription; - private final boolean mIsEnableConfigurableEventReportingWindows; private final Source mSource; private final Long[] mEarlyReportingWindows; private final double mExpectedProbability; - private final boolean mEnableConfiguredMaxEventReports; private final int mConfiguredMaxEventReportsCount; /** - * The data format is measurement_enable_configurable_event_reporting_windows flag, sourceType, - * sourceEventReportWindow (limit), cooldown window, appDestination, webDestination - * configuredEarlyReportingWindows, coarse destination, enable configured conversions, + * The data format is sourceType, sourceEventReportWindow (limit), cooldown window, + * appDestination, webDestination, configuredEarlyReportingWindows, coarse destination, * configured conversions and expectedProbability. Each test description has numbers like 1-1-1, * 2-1-2, 3-3-3 etc. These signify max reports, trigger data bits and reporting windows count * respectively. For e.g., 2-1-2 stands for 2 maximum conversions, 1 trigger data bit (0 or 1) @@ -85,7 +81,6 @@ public class SourceNoiseHandlerAttributionProbabilityTest { { "non-configured reporting windows, EVENT, 1-1-1, app, fine " + "destinations", - false, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type DAYS.toMillis(10), // source event report window 0, // install cooldown window @@ -93,15 +88,13 @@ public class SourceNoiseHandlerAttributionProbabilityTest { null, // web destination new Long[] {}, // early reporting windows false, // coarse destinations - false, // enable configured max reports 1, // configured max reports - MEASUREMENT_EVENT_NOISE_PROBABILITY, // probability + EVENT_NOISE_PROBABILITY, // probability }, { "non-configured reporting windows, EVENT, 2-1-2, app, install " + "detection, fine " + "destinations", - false, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type DAYS.toMillis(10), // source event report window DAYS.toMillis(1), // install cooldown window @@ -109,14 +102,12 @@ public class SourceNoiseHandlerAttributionProbabilityTest { null, // web destination new Long[] {}, // early reporting windows false, // coarse destinations - false, // enable configured max reports 1, // configured max reports - MEASUREMENT_INSTALL_ATTR_EVENT_NOISE_PROBABILITY, // probability + INSTALL_ATTR_EVENT_NOISE_PROBABILITY, // probability }, { "non-configured reporting windows, EVENT, 1-1-1, web, fine " + "destinations", - false, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type DAYS.toMillis(10), // source event report window 0, // install cooldown window @@ -124,14 +115,12 @@ public class SourceNoiseHandlerAttributionProbabilityTest { WEB_DESTINATIONS, // web destination new Long[] {}, // early reporting windows false, // coarse destinations - false, // enable configured max reports 1, // configured max reports - MEASUREMENT_EVENT_NOISE_PROBABILITY, // probability + EVENT_NOISE_PROBABILITY, // probability }, { "non-configured reporting windows, EVENT, 1-1-1, app and web, " + "fine destinations", - false, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type DAYS.toMillis(10), // source event report window 0, // install cooldown window @@ -139,155 +128,56 @@ public class SourceNoiseHandlerAttributionProbabilityTest { WEB_DESTINATIONS, // web destination new Long[] {}, // early reporting windows false, // coarse destinations - false, // enable configured max reports 1, // configured max reports - MEASUREMENT_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY, // probability + 0.0000042, // probability }, { - "non-configured reporting windows, EVENT, 2-1-2, app & web, " + "non-configured reporting windows, EVENT, 2-1-2, app, " + "install " + "detection, fine" + " destinations", - false, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type DAYS.toMillis(10), // source event report window DAYS.toMillis(1), // install cooldown window ATTRIBUTION_DESTINATIONS, // app destination - WEB_DESTINATIONS, // app destination + null, // web destination new Long[] {}, // early reporting windows false, // coarse destinations - false, // enable configured max reports 1, // configured max reports - MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY, + 0.0000125, }, { - "non-configured reporting windows, EVENT, 2-1-2, app & web, " + "non-configured reporting windows, EVENT, 2-1-1, app, " + "install " - + "detection, coarse" + + "detection, fine" + " destinations", - false, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type - DAYS.toMillis(10), // source event report window + DAYS.toMillis(1), // source event report window DAYS.toMillis(1), // install cooldown window ATTRIBUTION_DESTINATIONS, // app destination - WEB_DESTINATIONS, // app destination - new Long[] {}, // early reporting windows - true, // coarse destinations - false, // enable configurable max reports - 1, // configured max reports - MEASUREMENT_INSTALL_ATTR_EVENT_NOISE_PROBABILITY, - }, - { - "non-configured reporting windows, NAVIGATION, 3-3-3, app, fine " - + "destinations", - false, // measurement_enable_configurable_event_reporting_windows - Source.SourceType.NAVIGATION, // source type - DAYS.toMillis(10), // source event report window - 0, // install cooldown window - ATTRIBUTION_DESTINATIONS, // app destination null, // web destination new Long[] {}, // early reporting windows false, // coarse destinations - false, // enable configured max reports - 3, // configured max reports - MEASUREMENT_NAVIGATION_NOISE_PROBABILITY, // probability + 1, // configured max reports + 0.000005, }, { - "non-configured reporting windows, NAVIGATION, 3-3-3, app, install " - + "detection, fine " - + "destinations", - false, // measurement_enable_configurable_event_reporting_windows - Source.SourceType.NAVIGATION, // source type + "non-configured reporting windows, EVENT, 2-1-2, app, " + + "install " + + "detection, coarse" + + " destinations", + Source.SourceType.EVENT, // source type DAYS.toMillis(10), // source event report window DAYS.toMillis(1), // install cooldown window ATTRIBUTION_DESTINATIONS, // app destination null, // web destination new Long[] {}, // early reporting windows - false, // coarse destinations - false, // enable configured max reports - 3, // configured max reports - MEASUREMENT_INSTALL_ATTR_NAVIGATION_NOISE_PROBABILITY, // probability - }, - { - "non-configured reporting windows, NAVIGATION, 3-3-3, web, fine " - + "destinations", - false, // measurement_enable_configurable_event_reporting_windows - Source.SourceType.NAVIGATION, // source type - DAYS.toMillis(10), // source event report window - 0, // install cooldown window - null, - WEB_DESTINATIONS, // web destination - new Long[] {}, // early reporting windows - false, // coarse destinations - false, // enable configurable max reports - 3, // configured max reports - MEASUREMENT_NAVIGATION_NOISE_PROBABILITY, // probability - }, - { - "non-configured reporting windows, NAVIGATION, 3-3-3, app & web, " - + "fine destinations", - false, // measurement_enable_configurable_event_reporting_windows - Source.SourceType.NAVIGATION, // source type - DAYS.toMillis(10), // source event report window - 0, // install cooldown window - ATTRIBUTION_DESTINATIONS, // app destination - WEB_DESTINATIONS, // web destination - new Long[] {}, // early reporting windows - false, // coarse destinations - false, // enable configurable max reports - 3, // configured max reports - MEASUREMENT_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY, // probability - }, - { - "non-configured reporting windows, NAVIGATION, 3-3-3, app & web, " - + "coarse destinations", - false, // measurement_enable_configurable_event_reporting_windows - Source.SourceType.NAVIGATION, // source type - DAYS.toMillis(10), // source event report window - 0, // install cooldown window - ATTRIBUTION_DESTINATIONS, // app destination - WEB_DESTINATIONS, // web destination - new Long[] {}, // early reporting windows true, // coarse destinations - false, // enable configurable max reports - 3, // configured max reports - MEASUREMENT_NAVIGATION_NOISE_PROBABILITY, // probability - }, - { - "non-configured reporting windows, NAVIGATION, 3-3-3, app & web, " - + "install detection," - + " fine destinations", - false, // measurement_enable_configurable_event_reporting_windows - Source.SourceType.NAVIGATION, // source type - DAYS.toMillis(10), // source event report window - DAYS.toMillis(1), // install cooldown window - ATTRIBUTION_DESTINATIONS, // app destination - WEB_DESTINATIONS, // web destination - new Long[] {}, // early reporting windows - false, // coarse destinations - false, // enable configurable max reports - 3, // configured max reports - MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY, - }, - { - "non-configured reporting windows, NAVIGATION, 3-3-3, app & web, " - + "install detection," - + " coarse destinations", - false, // measurement_enable_configurable_event_reporting_windows - Source.SourceType.NAVIGATION, // source type - DAYS.toMillis(10), // source event report window - DAYS.toMillis(1), // install cooldown window - ATTRIBUTION_DESTINATIONS, // app destination - WEB_DESTINATIONS, // web destination - new Long[] {}, // early reporting windows - true, // coarse destinations - false, // enable configurable max reports - 3, // configured max reports - MEASUREMENT_INSTALL_ATTR_NAVIGATION_NOISE_PROBABILITY, + 1, // configured max reports + INSTALL_ATTR_EVENT_NOISE_PROBABILITY, }, { "configured reporting windows, EVENT, 1-1-1, app, fine " + "destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type DAYS.toMillis(10), // source event report window 0, // install cooldown window @@ -295,13 +185,11 @@ public class SourceNoiseHandlerAttributionProbabilityTest { null, // web destination new Long[] {}, // early reporting windows false, // coarse destinations - false, // enable configurable max reports 1, // configured max reports - MEASUREMENT_EVENT_NOISE_PROBABILITY, // probability + EVENT_NOISE_PROBABILITY, // probability }, { "configured reporting windows, EVENT, 1-1-2, app, fine " + "destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type DAYS.toMillis(10), // source event report window 0, // install cooldown window @@ -309,13 +197,11 @@ public class SourceNoiseHandlerAttributionProbabilityTest { null, // web destination new Long[] {HOURS.toSeconds(1)}, // early reporting windows false, // coarse destinations - false, // enable configurable max reports 1, // configured max reports 0.0000042, }, { "configured reporting windows, EVENT, 1-1-3, app, fine " + "destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type DAYS.toMillis(10), // source event report window 0, // install cooldown window @@ -323,7 +209,6 @@ public class SourceNoiseHandlerAttributionProbabilityTest { null, // web destination new Long[] {HOURS.toSeconds(1), DAYS.toSeconds(1)}, false, // coarse destinations - false, // enable configurable max reports 1, // configured max reports 0.0000058, // probability }, @@ -331,7 +216,6 @@ public class SourceNoiseHandlerAttributionProbabilityTest { "configured reporting windows, EVENT, 1-1-2(1 effective window), " + "app, fine " + "destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type DAYS.toMillis(10), // source event report window 0, // install cooldown window @@ -339,15 +223,13 @@ public class SourceNoiseHandlerAttributionProbabilityTest { null, // web destination new Long[] {DAYS.toSeconds(15)}, // early reporting windows false, // coarse destinations - false, // enable configurable max reports 1, // configured max reports - MEASUREMENT_EVENT_NOISE_PROBABILITY, // probability + EVENT_NOISE_PROBABILITY, // probability }, { "configured reporting windows, EVENT, 1-1-3(2 effective window), " + "app, fine " + "destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type HOURS.toMillis(6), // source event report window 0, // install cooldown window @@ -355,15 +237,13 @@ public class SourceNoiseHandlerAttributionProbabilityTest { null, // web destination new Long[] {HOURS.toSeconds(1), DAYS.toSeconds(1)}, false, // coarse destinations - false, // enable configurable max reports 1, // configured max reports 0.0000042, }, { - "configured reporting windows, EVENT, 2-1-3(2 effective windows), " + "configured reporting windows, EVENT, 1-1-2 (2 effective window), " + "app, install " + "detection, fine destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type HOURS.toMillis(6), // source event report window DAYS.toMillis(1), // install cooldown window @@ -373,15 +253,13 @@ public class SourceNoiseHandlerAttributionProbabilityTest { HOURS.toSeconds(1), DAYS.toSeconds(1) }, // early reporting windows false, // coarse destinations - false, // enable configurable max reports 1, // configured max reports - MEASUREMENT_INSTALL_ATTR_EVENT_NOISE_PROBABILITY, // probability + 0.0000042, // probability }, { - "configured reporting windows, EVENT, 2-1-3, app, install " + "configured reporting windows, EVENT, 1-1-3, app, install " + "detection, fine " + "destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type DAYS.toMillis(6), // source event report window DAYS.toMillis(1), // install cooldown window @@ -391,15 +269,13 @@ public class SourceNoiseHandlerAttributionProbabilityTest { HOURS.toSeconds(1), DAYS.toSeconds(1) }, // early reporting windows false, // coarse destinations - false, // enable configurable max reports 1, // configured max reports - 0.0000233, // probability + 0.0000058, // probability }, { - "configured reporting windows, EVENT, 2-1-3, app & web, install " + "configured reporting windows, EVENT, 1-1-3, app & web, install " + "detection, fine " + "destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type DAYS.toMillis(6), // source event report window DAYS.toMillis(1), // install cooldown window @@ -409,15 +285,13 @@ public class SourceNoiseHandlerAttributionProbabilityTest { HOURS.toSeconds(1), DAYS.toSeconds(1) }, // early reporting windows false, // coarse destinations - false, // enable configurable max reports 1, // configured max reports - 0.0000757, // probability + 0.0000108, // probability }, { - "configured reporting windows, EVENT, 2-1-3, app & web, install " + "configured reporting windows, EVENT, 1-1-3, app & web, install " + "detection, coarse " + "destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type DAYS.toMillis(6), // source event report window DAYS.toMillis(1), // install cooldown window @@ -427,16 +301,14 @@ public class SourceNoiseHandlerAttributionProbabilityTest { HOURS.toSeconds(1), DAYS.toSeconds(1) }, // early reporting windows true, // coarse destinations - false, // enable configurable max reports 1, // configured max reports - 0.0000233, // probability + 0.0000058, // probability }, { "configured reporting windows, EVENT, 1-1-1, web, (install " + "cooldown -" + " unused), fine" + " destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type DAYS.toMillis(10), // source event report window DAYS.toMillis(1), // install cooldown window @@ -444,21 +316,13 @@ public class SourceNoiseHandlerAttributionProbabilityTest { WEB_DESTINATIONS, // web destination new Long[] {}, // early reporting windows false, // coarse destinations - false, // enable configurable max reports 1, // configured max reports - MEASUREMENT_EVENT_NOISE_PROBABILITY, // probability + EVENT_NOISE_PROBABILITY, // probability }, { - // It is different from "non-configured reporting windows, 2-1-2, - // app - // & web, install - // detection" because we reject 20 states resulting into only 25 - // states in - // that case. Here we assume all 45 states to be valid. - "configured reporting windows, EVENT, 2-1-2, app & web, install " + "configured reporting windows, EVENT, 1-1-2, app & web, install " + "detection, fine " + "destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type DAYS.toMillis(10), // source event report window DAYS.toMillis(1), // install cooldown window @@ -466,21 +330,13 @@ public class SourceNoiseHandlerAttributionProbabilityTest { WEB_DESTINATIONS, // web destination new Long[] {HOURS.toSeconds(1)}, // early reporting windows false, // coarse destinations - false, // enable configurable max reports 1, // configured max reports - 0.0000374, // probability + 0.0000075, // probability }, { - // It is different from "non-configured reporting windows, 2-1-2, - // app - // & web, install - // detection, coarse destinations" because we reject 20 states - // resulting into only 25 states in that case. Here we assume all - // 45 states to be valid. - "configured reporting windows, EVENT, 2-1-2, app & web, install " + "configured reporting windows, EVENT, 1-1-2, app & web, install " + "detection, coarse " + "destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type DAYS.toMillis(10), // source event report window DAYS.toMillis(1), // install cooldown window @@ -488,14 +344,12 @@ public class SourceNoiseHandlerAttributionProbabilityTest { WEB_DESTINATIONS, // web destination new Long[] {HOURS.toSeconds(1)}, // early reporting windows true, // coarse destinations - false, // enable configurable max reports 1, // configured max reports - 0.0000125, // probability + 0.0000042, // probability }, { "configured (ignored due to empty), EVENT, 2-1-2, app, install " + "detection, fine destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type DAYS.toMillis(10), // source event report window DAYS.toMillis(1), // install cooldown window @@ -503,14 +357,12 @@ public class SourceNoiseHandlerAttributionProbabilityTest { null, // web destination new Long[] {}, // early reporting windows false, // coarse destinations - false, // enable configurable max reports 1, // configured max reports - MEASUREMENT_INSTALL_ATTR_EVENT_NOISE_PROBABILITY, // probability + INSTALL_ATTR_EVENT_NOISE_PROBABILITY, // probability }, { "configured reporting windows, EVENT, 1-1-1, app & web, fine " + "destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type DAYS.toMillis(10), // source event report window 0, // install cooldown window @@ -518,14 +370,12 @@ public class SourceNoiseHandlerAttributionProbabilityTest { WEB_DESTINATIONS, // app destination new Long[] {}, // early reporting windows false, // coarse destinations - false, // enable configurable max reports 1, // configured max reports - MEASUREMENT_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY, // probability + 0.0000042, // probability }, { "configured reporting windows, EVENT, 1-1-1, app & web, coarse " + "destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type DAYS.toMillis(10), // source event report window 0, // install cooldown window @@ -533,14 +383,12 @@ public class SourceNoiseHandlerAttributionProbabilityTest { WEB_DESTINATIONS, // app destination new Long[] {}, // early reporting windows true, // coarse destinations - false, // enable configurable max reports 1, // configured max reports - MEASUREMENT_EVENT_NOISE_PROBABILITY, // probability + EVENT_NOISE_PROBABILITY, // probability }, { "configured reporting windows, NAVIGATION, 3-3-1, app, fine " + "destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.NAVIGATION, // source type DAYS.toMillis(10), // source event report window 0, // install cooldown window @@ -548,14 +396,12 @@ public class SourceNoiseHandlerAttributionProbabilityTest { null, // web destination new Long[] {}, // early reporting windows false, // coarse destinations - false, // enable configurable max reports 3, // configured max reports 0.0001372, // probability }, { "configured reporting windows, NAVIGATION, 3-3-2, app, fine " + "destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.NAVIGATION, // source type DAYS.toMillis(10), // source event report window 0, // install cooldown window @@ -563,14 +409,12 @@ public class SourceNoiseHandlerAttributionProbabilityTest { null, // web destination new Long[] {HOURS.toSeconds(1)}, // early reporting windows false, // coarse destinations - false, // enable configurable max reports 3, // configured max reports 0.0008051, // probability }, { "configured reporting windows, NAVIGATION, 3-3-3, app, fine " + "destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.NAVIGATION, // source type DAYS.toMillis(10), // source event report window 0, // install cooldown window @@ -578,16 +422,14 @@ public class SourceNoiseHandlerAttributionProbabilityTest { null, // web destination new Long[] {HOURS.toSeconds(1), DAYS.toSeconds(1)}, false, // coarse destinations - false, // enable configurable max reports 3, // configured max reports - MEASUREMENT_NAVIGATION_NOISE_PROBABILITY, // probability + NAVIGATION_NOISE_PROBABILITY, // probability }, { "configured reporting windows, NAVIGATION, 3-3-2 (1 effective " + "window)" + ", app, fine " + "destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.NAVIGATION, // source type DAYS.toMillis(2), // source event report window 0, // install cooldown window @@ -595,7 +437,6 @@ public class SourceNoiseHandlerAttributionProbabilityTest { null, // web destination new Long[] {DAYS.toMillis(3)}, // early reporting windows false, // coarse destinations - false, // enable configurable max reports 3, // configured max reports 0.0001372, // probability }, @@ -603,7 +444,6 @@ public class SourceNoiseHandlerAttributionProbabilityTest { "configured reporting windows, NAVIGATION, 3-3-3 (2 effective " + "windows), app, fine " + "destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.NAVIGATION, // source type HOURS.toMillis(6), // source event report window 0, // install cooldown window @@ -611,7 +451,6 @@ public class SourceNoiseHandlerAttributionProbabilityTest { null, // web destination new Long[] {HOURS.toSeconds(1), DAYS.toSeconds(1)}, false, // coarse destinations - false, // enable configurable max reports 3, // configured max reports 0.0008051, // probability }, @@ -619,7 +458,6 @@ public class SourceNoiseHandlerAttributionProbabilityTest { "configured reporting windows, NAVIGATION, 3-3-1, app, install " + "detection, fine " + "destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.NAVIGATION, // source type DAYS.toMillis(10), // source event report window DAYS.toMillis(1), // install cooldown window @@ -627,7 +465,6 @@ public class SourceNoiseHandlerAttributionProbabilityTest { null, // web destination new Long[] {}, // early reporting windows false, // coarse destinations - false, // enable configurable max reports 3, // configured max reports 0.0001372, // probability }, @@ -635,7 +472,6 @@ public class SourceNoiseHandlerAttributionProbabilityTest { "configured reporting windows, NAVIGATION, 3-3-1, web, install " + "detection, fine " + "destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.NAVIGATION, // source type DAYS.toMillis(10), // source event report window DAYS.toMillis(1), // install cooldown window @@ -643,14 +479,12 @@ public class SourceNoiseHandlerAttributionProbabilityTest { WEB_DESTINATIONS, // app destination new Long[] {}, // early reporting windows false, // coarse destinations - false, // enable configurable max reports 3, // configured max reports 0.0001372, // probability }, { "configured reporting windows, NAVIGATION, 3-3-1, app & web, fine" + " destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.NAVIGATION, // source type DAYS.toMillis(10), // source event report window 0, // install cooldown window @@ -658,14 +492,12 @@ public class SourceNoiseHandlerAttributionProbabilityTest { WEB_DESTINATIONS, // app destination new Long[] {}, // early reporting windows false, // coarse destinations - false, // enable configurable max reports 3, // configured max reports 0.0008051, // probability }, { "configured reporting windows, NAVIGATION, 3-3-1, app & web, " + "coarse destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.NAVIGATION, // source type DAYS.toMillis(10), // source event report window 0, // install cooldown window @@ -673,7 +505,6 @@ public class SourceNoiseHandlerAttributionProbabilityTest { WEB_DESTINATIONS, // app destination new Long[] {}, // early reporting windows true, // coarse destinations - false, // enable configurable max reports 3, // configured max reports 0.0001372, // probability }, @@ -682,7 +513,6 @@ public class SourceNoiseHandlerAttributionProbabilityTest { + "install " + "detection, " + "fine destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.NAVIGATION, // source type DAYS.toMillis(10), // source event report window DAYS.toMillis(1), // install cooldown window @@ -690,7 +520,6 @@ public class SourceNoiseHandlerAttributionProbabilityTest { WEB_DESTINATIONS, // app destination new Long[] {}, // early reporting windows false, // coarse destinations - false, // enable configurable max reports 3, // configured max reports 0.0008051, // probability }, @@ -699,7 +528,6 @@ public class SourceNoiseHandlerAttributionProbabilityTest { + "install " + "detection, " + "coarse destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.NAVIGATION, // source type DAYS.toMillis(10), // source event report window DAYS.toMillis(1), // install cooldown window @@ -707,7 +535,6 @@ public class SourceNoiseHandlerAttributionProbabilityTest { WEB_DESTINATIONS, // app destination new Long[] {}, // early reporting windows true, // coarse destinations - false, // enable configurable max reports 3, // configured max reports 0.0001372, // probability }, @@ -716,7 +543,6 @@ public class SourceNoiseHandlerAttributionProbabilityTest { + "install " + "detection, fine" + " destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.NAVIGATION, // source type DAYS.toMillis(10), // source event report window DAYS.toMillis(1), // install cooldown window @@ -724,16 +550,14 @@ public class SourceNoiseHandlerAttributionProbabilityTest { WEB_DESTINATIONS, // web destination new Long[] {HOURS.toSeconds(2), DAYS.toSeconds(2)}, false, // coarse destinations - false, // enable configurable max reports 3, // configured max reports - MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY, + INSTALL_ATTR_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY, }, { "configured reporting windows, NAVIGATION, 3-3-3, app & web, " + "install " + "detection, " + "coarse destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.NAVIGATION, // source type DAYS.toMillis(10), // source event report window DAYS.toMillis(1), // install cooldown window @@ -741,14 +565,12 @@ public class SourceNoiseHandlerAttributionProbabilityTest { WEB_DESTINATIONS, // web destination new Long[] {HOURS.toSeconds(2), DAYS.toSeconds(2)}, true, // coarse destinations - false, // enable configurable max reports 3, // configured max reports - MEASUREMENT_INSTALL_ATTR_NAVIGATION_NOISE_PROBABILITY, + INSTALL_ATTR_NAVIGATION_NOISE_PROBABILITY, }, { "configured reporting windows, NAVIGATION, 3-3-3, app, install " + "detection, fine destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.NAVIGATION, // source type DAYS.toMillis(10), // source event report window DAYS.toMillis(1), // install cooldown window @@ -756,14 +578,12 @@ public class SourceNoiseHandlerAttributionProbabilityTest { null, // web destination new Long[] {HOURS.toSeconds(2), DAYS.toSeconds(2)}, false, // coarse destinations - false, // enable configurable max reports 3, // configured max reports - MEASUREMENT_INSTALL_ATTR_NAVIGATION_NOISE_PROBABILITY, // probability + INSTALL_ATTR_NAVIGATION_NOISE_PROBABILITY, // probability }, { "configured reporting windows, NAVIGATION, 3-3-3, app & web, fine" + " destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.NAVIGATION, // source type DAYS.toMillis(10), // source event report window 0, // install cooldown window @@ -771,14 +591,12 @@ public class SourceNoiseHandlerAttributionProbabilityTest { WEB_DESTINATIONS, // web destination new Long[] {HOURS.toSeconds(2), DAYS.toSeconds(2)}, false, // coarse destinations - false, // enable configurable max reports 3, // configured max reports - MEASUREMENT_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY, // probability + 0.0170218, // probability }, { "configured reporting windows, NAVIGATION, 3-3-3, app & web, " + "coarse destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.NAVIGATION, // source type DAYS.toMillis(10), // source event report window 0, // install cooldown window @@ -786,14 +604,12 @@ public class SourceNoiseHandlerAttributionProbabilityTest { WEB_DESTINATIONS, // web destination new Long[] {HOURS.toSeconds(2), DAYS.toSeconds(2)}, true, // coarse destinations - false, // enable configurable max reports 3, // configured max reports - MEASUREMENT_NAVIGATION_NOISE_PROBABILITY, // probability + NAVIGATION_NOISE_PROBABILITY, // probability }, { "non-configured reporting windows, EVENT, 3-1-1, app, fine " + "destinations", - false, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type DAYS.toMillis(10), // source event report window 0, // install cooldown window @@ -801,13 +617,11 @@ public class SourceNoiseHandlerAttributionProbabilityTest { null, // web destination new Long[] {}, // early reporting windows false, // coarse destinations - true, // enable configured max reports 3, // configured max reports 0.0000083, // probability }, { "configured reporting windows, EVENT, 3-1-3, app, fine " + "destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type DAYS.toMillis(10), // source event report window 0, // install cooldown window @@ -815,13 +629,11 @@ public class SourceNoiseHandlerAttributionProbabilityTest { null, // web destination new Long[] {HOURS.toSeconds(1), DAYS.toSeconds(1)}, false, // coarse destinations - true, // enable configurable max reports 3, // configured max reports 0.0000698, // probability }, { "configured reporting windows, EVENT, 2-1-3, app, fine " + "destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type DAYS.toMillis(10), // source event report window 0, // install cooldown window @@ -829,7 +641,6 @@ public class SourceNoiseHandlerAttributionProbabilityTest { null, // web destination new Long[] {HOURS.toSeconds(1), DAYS.toSeconds(1)}, false, // coarse destinations - true, // enable configurable max reports 2, // configured max reports 0.0000233, // probability }, @@ -837,7 +648,6 @@ public class SourceNoiseHandlerAttributionProbabilityTest { "configured reporting windows, EVENT, 3-1-3, app, install " + "detection, fine " + "destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type DAYS.toMillis(6), // source event report window // not honored because conversions and windows are overridden @@ -848,7 +658,6 @@ public class SourceNoiseHandlerAttributionProbabilityTest { HOURS.toSeconds(1), DAYS.toSeconds(1) }, // early reporting windows false, // coarse destinations - true, // enable configurable max reports 3, // configured max reports 0.0000698, // probability }, @@ -856,7 +665,6 @@ public class SourceNoiseHandlerAttributionProbabilityTest { "configured reporting windows, EVENT, 3-1-3, app & web, install " + "detection, fine " + "destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type DAYS.toMillis(6), // source event report window DAYS.toMillis(1), // install cooldown window @@ -866,7 +674,6 @@ public class SourceNoiseHandlerAttributionProbabilityTest { HOURS.toSeconds(1), DAYS.toSeconds(1) }, // early reporting windows false, // coarse destinations - true, // enable configurable max reports 3, // configured max reports 0.0003782, // probability }, @@ -874,7 +681,6 @@ public class SourceNoiseHandlerAttributionProbabilityTest { "configured reporting windows, EVENT, 3-1-3, app & web, install " + "detection, coarse " + "destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type DAYS.toMillis(6), // source event report window DAYS.toMillis(1), // install cooldown window @@ -884,13 +690,11 @@ public class SourceNoiseHandlerAttributionProbabilityTest { HOURS.toSeconds(1), DAYS.toSeconds(1) }, // early reporting windows true, // coarse destinations - true, // enable configurable max reports 3, // configured max reports 0.0000698, // probability }, { "configured reporting windows, EVENT, 1-1-3, app, fine" + " destinations", - true, // measurement_enable_configurable_event_reporting_windows Source.SourceType.EVENT, // source type DAYS.toMillis(10), // source event report window 0, // install cooldown window @@ -898,7 +702,6 @@ public class SourceNoiseHandlerAttributionProbabilityTest { null, // web destination new Long[] {HOURS.toSeconds(1), DAYS.toSeconds(1)}, false, // coarse destinations - true, // enable configurable max reports 1, // configured max reports 0.0000058, // probability }, @@ -907,7 +710,6 @@ public class SourceNoiseHandlerAttributionProbabilityTest { public SourceNoiseHandlerAttributionProbabilityTest( String description, - boolean isEnableConfigurableEventReportingWindows, Source.SourceType sourceType, long sourceEventReportWindow, long coolDownWindow, @@ -915,11 +717,9 @@ public class SourceNoiseHandlerAttributionProbabilityTest { List<Uri> webDestinations, Long[] earlyReportingWindows, boolean coarseDestination, - boolean isEnableConfigurableMaxEventReports, int configuredMaxEventReportsCount, double expectedProbability) { mDescription = description; - mIsEnableConfigurableEventReportingWindows = isEnableConfigurableEventReportingWindows; mSource = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(sourceType) @@ -936,7 +736,6 @@ public class SourceNoiseHandlerAttributionProbabilityTest { BigDecimal.valueOf(expectedProbability) .setScale(7, RoundingMode.HALF_UP) .doubleValue(); - mEnableConfiguredMaxEventReports = isEnableConfigurableMaxEventReports; mConfiguredMaxEventReportsCount = configuredMaxEventReportsCount; } @@ -944,9 +743,6 @@ public class SourceNoiseHandlerAttributionProbabilityTest { public void getRandomAttributionProbability_withParameterizedData() { // Setup Flags flags = mock(Flags.class); - doReturn(mIsEnableConfigurableEventReportingWindows) - .when(flags) - .getMeasurementEnableConfigurableEventReportingWindows(); doReturn(convertEarlyReportingWindowFlagString(mEarlyReportingWindows)) .when(flags) .getMeasurementEventReportsVtcEarlyReportingWindows(); @@ -954,36 +750,9 @@ public class SourceNoiseHandlerAttributionProbabilityTest { .when(flags) .getMeasurementEventReportsCtcEarlyReportingWindows(); doReturn(true).when(flags).getMeasurementEnableCoarseEventReportDestinations(); - doReturn(mEnableConfiguredMaxEventReports) - .when(flags) - .getMeasurementEnableVtcConfigurableMaxEventReports(); doReturn(mConfiguredMaxEventReportsCount) .when(flags) .getMeasurementVtcConfigurableMaxEventReportsCount(); - doReturn(MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY) - .when(flags) - .getMeasurementInstallAttrDualDestinationEventNoiseProbability(); - doReturn(MEASUREMENT_INSTALL_ATTR_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY) - .when(flags) - .getMeasurementInstallAttrDualDestinationNavigationNoiseProbability(); - doReturn(MEASUREMENT_DUAL_DESTINATION_NAVIGATION_NOISE_PROBABILITY) - .when(flags) - .getMeasurementDualDestinationNavigationNoiseProbability(); - doReturn(MEASUREMENT_DUAL_DESTINATION_EVENT_NOISE_PROBABILITY) - .when(flags) - .getMeasurementDualDestinationEventNoiseProbability(); - doReturn(MEASUREMENT_INSTALL_ATTR_EVENT_NOISE_PROBABILITY) - .when(flags) - .getMeasurementInstallAttrEventNoiseProbability(); - doReturn(MEASUREMENT_INSTALL_ATTR_NAVIGATION_NOISE_PROBABILITY) - .when(flags) - .getMeasurementInstallAttrNavigationNoiseProbability(); - doReturn(MEASUREMENT_EVENT_NOISE_PROBABILITY) - .when(flags) - .getMeasurementEventNoiseProbability(); - doReturn(MEASUREMENT_NAVIGATION_NOISE_PROBABILITY) - .when(flags) - .getMeasurementNavigationNoiseProbability(); // Execution double actualProbability = diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/noising/SourceNoiseHandlerTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/noising/SourceNoiseHandlerTest.java index 06a3dc6ae..c9d0cf23a 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/noising/SourceNoiseHandlerTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/noising/SourceNoiseHandlerTest.java @@ -50,49 +50,17 @@ public class SourceNoiseHandlerTest { @Before public void setup() { mFlags = mock(Flags.class); - doReturn(false).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); + doReturn(Flags.DEFAULT_MEASUREMENT_VTC_CONFIGURABLE_MAX_EVENT_REPORTS_COUNT) + .when(mFlags).getMeasurementVtcConfigurableMaxEventReportsCount(); + doReturn(Flags.MEASUREMENT_EVENT_REPORTS_VTC_EARLY_REPORTING_WINDOWS) + .when(mFlags).getMeasurementEventReportsVtcEarlyReportingWindows(); + doReturn(Flags.MEASUREMENT_EVENT_REPORTS_CTC_EARLY_REPORTING_WINDOWS) + .when(mFlags).getMeasurementEventReportsCtcEarlyReportingWindows(); mSourceNoiseHandler = spy(new SourceNoiseHandler(mFlags, new EventReportWindowCalcDelegate(mFlags))); } @Test - public void fakeReports_eventSourceDualDestPostInstallMode_generatesFromStaticReportStates() { - long expiry = System.currentTimeMillis(); - Source source = - SourceFixture.getMinimalValidSourceBuilder() - .setSourceType(Source.SourceType.EVENT) - .setWebDestinations(SourceFixture.ValidSourceParams.WEB_DESTINATIONS) - .setEventReportWindow(expiry) - .setInstallCooldownWindow( - SourceFixture.ValidSourceParams.INSTALL_COOLDOWN_WINDOW) - .build(); - // Force increase the probability of random attribution. - doReturn(0.50D).when(mSourceNoiseHandler).getRandomAttributionProbability(source); - int falseCount = 0; - int neverCount = 0; - int truthCount = 0; - for (int i = 0; i < 500; i++) { - List<Source.FakeReport> fakeReports = - mSourceNoiseHandler.assignAttributionModeAndGenerateFakeReports(source); - if (source.getAttributionMode() == Source.AttributionMode.FALSELY) { - falseCount++; - assertNotEquals(0, fakeReports.size()); - assertTrue( - isValidEventSourceDualDestPostInstallModeFakeReportState( - source, fakeReports)); - } else if (source.getAttributionMode() == Source.AttributionMode.NEVER) { - neverCount++; - assertEquals(0, fakeReports.size()); - } else { - truthCount++; - } - } - assertNotEquals(0, falseCount); - assertNotEquals(0, neverCount); - assertNotEquals(0, truthCount); - } - - @Test public void fakeReports_flexEventReport_generatesFromStaticReportStates() { Source source = SourceFixture.getValidSourceWithFlexEventReportWithFewerState(); // Force increase the probability of random attribution. @@ -167,6 +135,7 @@ public class SourceNoiseHandlerTest { SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.NAVIGATION) .setEventTime(eventTime) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(30)) .setEventReportWindow(eventTime + TimeUnit.DAYS.toMillis(30)) .build(); assertEquals( @@ -181,6 +150,7 @@ public class SourceNoiseHandlerTest { SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.NAVIGATION) .setEventTime(eventTime) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(7)) .setEventReportWindow(eventTime + TimeUnit.DAYS.toMillis(7)) .build(); assertEquals( @@ -195,6 +165,7 @@ public class SourceNoiseHandlerTest { SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.NAVIGATION) .setEventTime(eventTime) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(2)) .setEventReportWindow(eventTime + TimeUnit.DAYS.toMillis(2)) .build(); assertEquals( @@ -302,6 +273,7 @@ public class SourceNoiseHandlerTest { .setInstallCooldownWindow(TimeUnit.DAYS.toMillis(2)) .setInstallAttributionWindow(TimeUnit.DAYS.toMillis(10)) .setEventTime(eventTime) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(30)) .setEventReportWindow(eventTime + TimeUnit.DAYS.toMillis(30)) .build(); assertEquals( @@ -318,6 +290,7 @@ public class SourceNoiseHandlerTest { .setInstallCooldownWindow(TimeUnit.DAYS.toMillis(2)) .setInstallAttributionWindow(TimeUnit.DAYS.toMillis(10)) .setEventTime(eventTime) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(7)) .setEventReportWindow(eventTime + TimeUnit.DAYS.toMillis(7)) .build(); assertEquals( @@ -334,6 +307,7 @@ public class SourceNoiseHandlerTest { .setInstallCooldownWindow(TimeUnit.DAYS.toMillis(2)) .setInstallAttributionWindow(TimeUnit.DAYS.toMillis(10)) .setEventTime(eventTime) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(2)) .setEventReportWindow(eventTime + TimeUnit.DAYS.toMillis(2)) .build(); assertEquals( @@ -350,6 +324,7 @@ public class SourceNoiseHandlerTest { .setInstallCooldownWindow(TimeUnit.DAYS.toMillis(2)) .setInstallAttributionWindow(TimeUnit.DAYS.toMillis(10)) .setEventTime(eventTime) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(30)) .setEventReportWindow(eventTime + TimeUnit.DAYS.toMillis(30)) .build(); assertEquals( @@ -366,6 +341,7 @@ public class SourceNoiseHandlerTest { .setInstallCooldownWindow(TimeUnit.DAYS.toMillis(2)) .setInstallAttributionWindow(TimeUnit.DAYS.toMillis(10)) .setEventTime(eventTime) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(7)) .setEventReportWindow(eventTime + TimeUnit.DAYS.toMillis(7)) .build(); assertEquals( @@ -382,6 +358,7 @@ public class SourceNoiseHandlerTest { .setInstallCooldownWindow(TimeUnit.DAYS.toMillis(2)) .setInstallAttributionWindow(TimeUnit.DAYS.toMillis(10)) .setEventTime(eventTime) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(2)) .setEventReportWindow(eventTime + TimeUnit.DAYS.toMillis(2)) .build(); assertEquals( @@ -393,11 +370,11 @@ public class SourceNoiseHandlerTest { mSourceNoiseHandler.getImpressionNoiseParams(navigationSource2dExpiry)); Source eventSourceWith2Destinations30dExpiry = SourceFixture.getMinimalValidSourceBuilder() - .setWebDestinations(SourceFixture.ValidSourceParams.WEB_DESTINATIONS) .setSourceType(Source.SourceType.EVENT) .setInstallCooldownWindow(TimeUnit.DAYS.toMillis(2)) .setInstallAttributionWindow(TimeUnit.DAYS.toMillis(10)) .setEventTime(eventTime) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(30)) .setEventReportWindow(eventTime + TimeUnit.DAYS.toMillis(30)) .build(); assertEquals( @@ -405,7 +382,7 @@ public class SourceNoiseHandlerTest { /* reportCount= */ 2, /* triggerDataCardinality= */ 2, /* reportingWindowCount= */ 2, - /* destinationMultiplier */ 2), + /* destinationMultiplier */ 1), mSourceNoiseHandler.getImpressionNoiseParams( eventSourceWith2Destinations30dExpiry)); } @@ -491,14 +468,6 @@ public class SourceNoiseHandlerTest { PrivacyParams.EVENT_TRIGGER_DATA_CARDINALITY); } - private boolean isValidEventSourceDualDestPostInstallModeFakeReportState( - Source source, List<Source.FakeReport> fakeReportsState) { - // Generated fake reports state matches one of the states - return Arrays.stream(ImpressionNoiseUtil.DUAL_DESTINATION_POST_INSTALL_FAKE_REPORT_CONFIG) - .map(reportsState -> convertToReportsState(reportsState, source)) - .anyMatch(fakeReportsState::equals); - } - private List<Source.FakeReport> convertToReportsState(int[][] reportsState, Source source) { return Arrays.stream(reportsState) .map( @@ -507,7 +476,7 @@ public class SourceNoiseHandlerTest { new UnsignedLong(Long.valueOf(reportState[0])), new EventReportWindowCalcDelegate(mFlags) .getReportingTimeForNoising( - source, reportState[1], true), + source, reportState[1]), reportState[2] == 0 ? source.getAppDestinations() : source.getWebDestinations())) diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/registration/AsyncRegistrationQueueJobServiceTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/registration/AsyncRegistrationQueueJobServiceTest.java index ee87776c5..632838e41 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/registration/AsyncRegistrationQueueJobServiceTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/registration/AsyncRegistrationQueueJobServiceTest.java @@ -20,7 +20,6 @@ import static com.android.adservices.mockito.ExtendedMockitoExpectations.mockGet import static com.android.adservices.mockito.ExtendedMockitoExpectations.mockGetFlags; import static com.android.adservices.mockito.MockitoExpectations.mockBackgroundJobsLoggingKillSwitch; import static com.android.adservices.mockito.MockitoExpectations.syncLogExecutionStats; -import static com.android.adservices.mockito.MockitoExpectations.syncPersistJobExecutionData; import static com.android.adservices.mockito.MockitoExpectations.verifyBackgroundJobsSkipLogged; import static com.android.adservices.mockito.MockitoExpectations.verifyLoggingNotHappened; import static com.android.adservices.spe.AdservicesJobInfo.MEASUREMENT_ASYNC_REGISTRATION_JOB; @@ -33,7 +32,6 @@ import static org.junit.Assert.assertTrue; 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.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; @@ -79,7 +77,6 @@ import org.mockito.internal.stubbing.answers.CallsRealMethods; import org.mockito.quality.Strictness; import java.util.Optional; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class AsyncRegistrationQueueJobServiceTest { @@ -168,9 +165,6 @@ public class AsyncRegistrationQueueJobServiceTest { runWithMocks( () -> { mockBackgroundJobsLoggingKillSwitch(mMockFlags, /* overrideValue= */ false); - JobServiceLoggingCallback onStartJobCallback = - syncPersistJobExecutionData(mSpyLogger); - JobServiceLoggingCallback onJobDoneCallback = syncLogExecutionStats(mSpyLogger); doReturn( AsyncRegistrationQueueRunner.ProcessingResult .SUCCESS_ALL_RECORDS_PROCESSED) @@ -179,8 +173,7 @@ public class AsyncRegistrationQueueJobServiceTest { onStartJob_killSwitchOff(); - verify(mSpyLogger).recordOnStartJob(anyInt()); - verify(mSpyLogger).persistJobExecutionData(anyInt(), anyLong()); + verify(mSpyLogger, timeout(WAIT_IN_MILLIS)).recordOnStartJob(anyInt()); }); } @@ -364,7 +357,7 @@ public class AsyncRegistrationQueueJobServiceTest { // Validate jobInfo params to run immediately ArgumentCaptor<JobInfo> captorJobInfo = ArgumentCaptor.forClass(JobInfo.class); - verify(jobScheduler, times(1)).schedule(captorJobInfo.capture()); + verify(jobScheduler, timeout(WAIT_IN_MILLIS).times(1)).schedule(captorJobInfo.capture()); JobInfo jobInfo = captorJobInfo.getValue(); assertNotNull(jobInfo); assertNull(jobInfo.getTriggerContentUris()); @@ -559,8 +552,8 @@ public class AsyncRegistrationQueueJobServiceTest { // Validate ExtendedMockito.verify( () -> AsyncRegistrationQueueJobService.schedule(any(), any()), - times(1)); - verify(mMockJobScheduler, times(1)) + timeout(WAIT_IN_MILLIS).times(1)); + verify(mMockJobScheduler, timeout(WAIT_IN_MILLIS).times(1)) .getPendingJob(eq(MEASUREMENT_ASYNC_REGISTRATION_JOB_ID)); }); } @@ -589,7 +582,8 @@ public class AsyncRegistrationQueueJobServiceTest { times(1)); ArgumentCaptor<JobInfo> jobInfoArgumentCaptor = ArgumentCaptor.forClass(JobInfo.class); - verify(mMockJobScheduler, times(1)).schedule(jobInfoArgumentCaptor.capture()); + verify(mMockJobScheduler, timeout(WAIT_IN_MILLIS).times(1)) + .schedule(jobInfoArgumentCaptor.capture()); JobInfo job = jobInfoArgumentCaptor.getValue(); assertEquals(JOB_TRIGGER_MIN_DELAY_MS, job.getTriggerContentUpdateDelay()); assertEquals(JOB_TRIGGER_MAX_DELAY_MS, job.getTriggerContentMaxDelay()); @@ -612,7 +606,8 @@ public class AsyncRegistrationQueueJobServiceTest { boolean onStopJobResult = mSpyService.onStopJob(Mockito.mock(JobParameters.class)); - verify(mSpyService, times(0)).jobFinished(any(), anyBoolean()); + verify(mSpyService, timeout(WAIT_IN_MILLIS).times(0)) + .jobFinished(any(), anyBoolean()); assertTrue(onStopJobResult); assertTrue(mSpyService.getFutureForTesting().isCancelled()); }); @@ -633,8 +628,9 @@ public class AsyncRegistrationQueueJobServiceTest { assertFalse(result); // Allow background thread to execute Thread.sleep(WAIT_IN_MILLIS); - verify(mSpyService, times(1)).jobFinished(any(), eq(false)); - verify(mMockJobScheduler, times(1)).cancel(eq(MEASUREMENT_ASYNC_REGISTRATION_JOB_ID)); + verify(mSpyService, timeout(WAIT_IN_MILLIS).times(1)).jobFinished(any(), eq(false)); + verify(mMockJobScheduler, timeout(WAIT_IN_MILLIS).times(1)) + .cancel(eq(MEASUREMENT_ASYNC_REGISTRATION_JOB_ID)); } private void onStartJob_killSwitchOn() throws Exception { @@ -646,10 +642,9 @@ public class AsyncRegistrationQueueJobServiceTest { // Validate assertFalse(result); - // Allow background thread to execute - Thread.sleep(WAIT_IN_MILLIS); - verify(mSpyService, times(1)).jobFinished(any(), eq(false)); - verify(mMockJobScheduler, times(1)).cancel(eq(MEASUREMENT_ASYNC_REGISTRATION_JOB_ID)); + verify(mSpyService, timeout(WAIT_IN_MILLIS).times(1)).jobFinished(any(), eq(false)); + verify(mMockJobScheduler, timeout(WAIT_IN_MILLIS).times(1)) + .cancel(eq(MEASUREMENT_ASYNC_REGISTRATION_JOB_ID)); } private void onStartJob_killSwitchOff() throws Exception { @@ -701,9 +696,4 @@ public class AsyncRegistrationQueueJobServiceTest { ExtendedMockito.doReturn(mMockFlags).when(FlagsFactory::getFlags); ExtendedMockito.doReturn(value).when(mMockFlags).getAsyncRegistrationJobQueueKillSwitch(); } - - private Object countDown(CountDownLatch countDownLatch) { - countDownLatch.countDown(); - return null; - } } diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/registration/AsyncRegistrationQueueRunnerTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/registration/AsyncRegistrationQueueRunnerTest.java index 3322190dd..2cfc9e6c4 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/registration/AsyncRegistrationQueueRunnerTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/registration/AsyncRegistrationQueueRunnerTest.java @@ -33,7 +33,6 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.adservices.measurement.RegistrationRequest; import android.adservices.measurement.WebSourceParams; import android.adservices.measurement.WebSourceRegistrationRequest; import android.content.ContentProviderClient; @@ -96,6 +95,7 @@ import org.mockito.stubbing.Answer; import java.io.IOException; import java.net.URL; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -125,6 +125,10 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo private static final String LIST_TYPE_REDIRECT_URI_1 = WebUtil.validUrl("https://foo.test"); private static final String LIST_TYPE_REDIRECT_URI_2 = WebUtil.validUrl("https://bar.test"); private static final String LOCATION_TYPE_REDIRECT_URI = WebUtil.validUrl("https://baz.test"); + private static final String LOCATION_TYPE_REDIRECT_URI_2 = WebUtil.validUrl("https://qux.test"); + private static final String LOCATION_TYPE_REDIRECT_URI_3 = + WebUtil.validUrl("https://quux.test"); + private static final Uri WEB_DESTINATION = WebUtil.validUri("https://web-destination.test"); private static final Uri APP_DESTINATION = Uri.parse("android-app://com.app_destination"); private static final Source SOURCE_1 = @@ -265,6 +269,12 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo when(mFlags.getMeasurementFlexApiMaxInformationGainDualDestinationNavigation()) .thenReturn(Flags .MEASUREMENT_FLEX_API_MAX_INFORMATION_GAIN_DUAL_DESTINATION_NAVIGATION); + when(mFlags.getMeasurementVtcConfigurableMaxEventReportsCount()) + .thenReturn(Flags.DEFAULT_MEASUREMENT_VTC_CONFIGURABLE_MAX_EVENT_REPORTS_COUNT); + when(mFlags.getMeasurementEventReportsVtcEarlyReportingWindows()) + .thenReturn(Flags.MEASUREMENT_EVENT_REPORTS_VTC_EARLY_REPORTING_WINDOWS); + when(mFlags.getMeasurementEventReportsCtcEarlyReportingWindows()) + .thenReturn(Flags.MEASUREMENT_EVENT_REPORTS_CTC_EARLY_REPORTING_WINDOWS); when(mMeasurementDao.insertSource(any())).thenReturn(DEFAULT_SOURCE_ID); mContext = spy(sDefaultContext); when(mContext.getPackageManager()).thenReturn(mPackageManager); @@ -277,17 +287,18 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo getSpyAsyncRegistrationQueueRunner(); AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppSource(); - + Map<String, List<String>> redirectHeaders = + getRedirectHeaders( + AsyncRegistration.RedirectType.LIST, + List.of( + WebUtil.validUri("https://example.test/sF1").toString(), + WebUtil.validUri("https://example.test/sF2").toString())); Answer<?> answerAsyncSourceFetcher = invocation -> { AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1); asyncFetchStatus.setResponseStatus(AsyncFetchStatus.ResponseStatus.SUCCESS); - AsyncRedirect asyncRedirect = invocation.getArgument(2); - asyncRedirect.addToRedirects( - AsyncRegistration.RedirectType.LIST, - List.of( - WebUtil.validUri("https://example.test/sF1"), - WebUtil.validUri("https://example.test/sF2"))); + AsyncRedirects asyncRedirects = invocation.getArgument(2); + asyncRedirects.configure(redirectHeaders, mFlags, validAsyncRegistration); return Optional.of(mMockedSource); }; doAnswer(answerAsyncSourceFetcher) @@ -305,14 +316,7 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyInt(), any())) .thenReturn(validAsyncRegistration) .thenReturn(null); - KeyValueData redirectCount = - new KeyValueData.Builder() - .setDataType(KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT) - .setKey( - AsyncRegistrationFixture.ValidAsyncRegistrationParams - .REGISTRATION_ID) - .setValue(null) // Should default to 1 - .build(); + KeyValueData redirectCount = getKeyValueDataRedirectCount(); when(mMeasurementDao.getKeyValueData(anyString(), any())).thenReturn(redirectCount); // Execution @@ -339,17 +343,18 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo getSpyAsyncRegistrationQueueRunner(); AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppSource(); - + Map<String, List<String>> redirectHeaders = + getRedirectHeaders( + AsyncRegistration.RedirectType.LIST, + List.of( + WebUtil.validUri("https://example.test/sF1").toString(), + WebUtil.validUri("https://example.test/sF2").toString())); Answer<?> answerAsyncSourceFetcher = invocation -> { AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1); asyncFetchStatus.setResponseStatus(AsyncFetchStatus.ResponseStatus.SUCCESS); - AsyncRedirect asyncRedirect = invocation.getArgument(2); - asyncRedirect.addToRedirects( - AsyncRegistration.RedirectType.LIST, - List.of( - WebUtil.validUri("https://example.test/sF1"), - WebUtil.validUri("https://example.test/sF2"))); + AsyncRedirects asyncRedirects = invocation.getArgument(2); + asyncRedirects.configure(redirectHeaders, mFlags, validAsyncRegistration); return Optional.of(mMockedSource); }; doAnswer(answerAsyncSourceFetcher) @@ -367,14 +372,7 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyInt(), any())) .thenReturn(validAsyncRegistration) .thenReturn(null); - KeyValueData redirectCount = - new KeyValueData.Builder() - .setDataType(KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT) - .setKey( - AsyncRegistrationFixture.ValidAsyncRegistrationParams - .REGISTRATION_ID) - .setValue(null) // Should default to 1 - .build(); + KeyValueData redirectCount = getKeyValueDataRedirectCount(); when(mMeasurementDao.getKeyValueData(anyString(), any())).thenReturn(redirectCount); Thread.currentThread().interrupt(); @@ -420,16 +418,16 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo AsyncRegistrationQueueRunner asyncRegistrationQueueRunner = getSpyAsyncRegistrationQueueRunner(); AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppSource(); + Map<String, List<String>> redirectHeaders = + getRedirectHeaders( + AsyncRegistration.RedirectType.LIST, + List.of(LIST_TYPE_REDIRECT_URI_1, LIST_TYPE_REDIRECT_URI_2)); Answer<Optional<Source>> answerAsyncSourceFetcher = invocation -> { AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1); asyncFetchStatus.setResponseStatus(AsyncFetchStatus.ResponseStatus.SUCCESS); - AsyncRedirect asyncRedirect = invocation.getArgument(2); - asyncRedirect.addToRedirects( - AsyncRegistration.RedirectType.LIST, - List.of( - Uri.parse(LIST_TYPE_REDIRECT_URI_1), - Uri.parse(LIST_TYPE_REDIRECT_URI_2))); + AsyncRedirects asyncRedirects = invocation.getArgument(2); + asyncRedirects.configure(redirectHeaders, mFlags, validAsyncRegistration); return Optional.of(mMockedSource); }; doAnswer(answerAsyncSourceFetcher) @@ -444,14 +442,7 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyInt(), any())) .thenReturn(validAsyncRegistration) .thenReturn(null); - KeyValueData redirectCount = - new KeyValueData.Builder() - .setDataType(KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT) - .setKey( - AsyncRegistrationFixture.ValidAsyncRegistrationParams - .REGISTRATION_ID) - .setValue(null) // Should default to 1 - .build(); + KeyValueData redirectCount = getKeyValueDataRedirectCount(); when(mMeasurementDao.getKeyValueData(anyString(), any())).thenReturn(redirectCount); // Execution @@ -496,14 +487,16 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo AsyncRegistrationQueueRunner asyncRegistrationQueueRunner = getSpyAsyncRegistrationQueueRunner(); AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppSource(); + Map<String, List<String>> redirectHeaders = + getRedirectHeaders( + AsyncRegistration.RedirectType.LOCATION, + List.of(LOCATION_TYPE_REDIRECT_URI)); Answer<Optional<Source>> answerAsyncSourceFetcher = invocation -> { AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1); asyncFetchStatus.setResponseStatus(AsyncFetchStatus.ResponseStatus.SUCCESS); - AsyncRedirect asyncRedirect = invocation.getArgument(2); - asyncRedirect.addToRedirects( - AsyncRegistration.RedirectType.LOCATION, - List.of(Uri.parse(LOCATION_TYPE_REDIRECT_URI))); + AsyncRedirects asyncRedirects = invocation.getArgument(2); + asyncRedirects.configure(redirectHeaders, mFlags, validAsyncRegistration); return Optional.of(mMockedSource); }; doAnswer(answerAsyncSourceFetcher) @@ -518,14 +511,7 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyInt(), any())) .thenReturn(validAsyncRegistration) .thenReturn(null); - KeyValueData redirectCount = - new KeyValueData.Builder() - .setDataType(KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT) - .setKey( - AsyncRegistrationFixture.ValidAsyncRegistrationParams - .REGISTRATION_ID) - .setValue(null) // Should default to 1 - .build(); + KeyValueData redirectCount = getKeyValueDataRedirectCount(); when(mMeasurementDao.getKeyValueData(anyString(), any())).thenReturn(redirectCount); // Execution @@ -562,14 +548,16 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo AsyncRegistrationQueueRunner asyncRegistrationQueueRunner = getSpyAsyncRegistrationQueueRunner(); AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppSource(); + Map<String, List<String>> redirectHeaders = + getRedirectHeaders( + AsyncRegistration.RedirectType.LOCATION, + List.of(LOCATION_TYPE_REDIRECT_URI)); Answer<Optional<Source>> answerAsyncSourceFetcher = invocation -> { AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1); asyncFetchStatus.setResponseStatus(AsyncFetchStatus.ResponseStatus.SUCCESS); - AsyncRedirect asyncRedirect = invocation.getArgument(2); - asyncRedirect.addToRedirects( - AsyncRegistration.RedirectType.LOCATION, - List.of(Uri.parse(LOCATION_TYPE_REDIRECT_URI))); + AsyncRedirects asyncRedirects = invocation.getArgument(2); + asyncRedirects.configure(redirectHeaders, mFlags, validAsyncRegistration); return Optional.of(mMockedSource); }; doAnswer(answerAsyncSourceFetcher) @@ -621,6 +609,338 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo } @Test + public void runAsyncRegistrationQueueWorker_appSrc_defaultReg_redirectWellKnown_typeLocation() + throws DatastoreException { + // Setup + AsyncRegistrationQueueRunner asyncRegistrationQueueRunner = + getSpyAsyncRegistrationQueueRunner(); + AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppSource(); + Answer<Optional<Source>> answerAsyncSourceFetcher = + getAsyncSourceAnswerForLocationTypeRedirectToWellKnown( + LOCATION_TYPE_REDIRECT_URI, validAsyncRegistration); + doAnswer(answerAsyncSourceFetcher) + .when(mAsyncSourceFetcher) + .fetchSource(any(), any(), any()); + when(mFlags.getMeasurementEnableRedirectToWellKnownPath()).thenReturn(true); + + List<Source.FakeReport> eventReportList = + Collections.singletonList( + new Source.FakeReport(new UnsignedLong(1L), 1L, List.of(APP_DESTINATION))); + when(mSourceNoiseHandler.assignAttributionModeAndGenerateFakeReports(mMockedSource)) + .thenReturn(eventReportList); + when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyInt(), any())) + .thenReturn(validAsyncRegistration); + KeyValueData redirectCount = getKeyValueDataRedirectCount(); + when(mMeasurementDao.getKeyValueData(anyString(), any())).thenReturn(redirectCount); + + // Execution + asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(); + + ArgumentCaptor<AsyncRegistration> asyncRegistrationArgumentCaptor = + ArgumentCaptor.forClass(AsyncRegistration.class); + + // Assertions + verify(mAsyncSourceFetcher, times(1)) + .fetchSource(any(AsyncRegistration.class), any(), any()); + verify(mMeasurementDao, times(1)).insertEventReport(any(EventReport.class)); + verify(mMeasurementDao, times(1)).insertSource(any(Source.class)); + verify(mMeasurementDao, times(1)) + .insertAsyncRegistration(asyncRegistrationArgumentCaptor.capture()); + + Assert.assertEquals(1, asyncRegistrationArgumentCaptor.getAllValues().size()); + AsyncRegistration asyncReg = asyncRegistrationArgumentCaptor.getAllValues().get(0); + Assert.assertEquals( + getRegistrationRedirectToWellKnownUri( + Uri.parse(LOCATION_TYPE_REDIRECT_URI), LOCATION_TYPE_REDIRECT_URI), + asyncReg.getRegistrationUri()); + + ArgumentCaptor<KeyValueData> redirectCountCaptor = + ArgumentCaptor.forClass(KeyValueData.class); + verify(mMeasurementDao, times(1)).insertOrUpdateKeyValueData(redirectCountCaptor.capture()); + assertEquals(2, redirectCountCaptor.getValue().getRegistrationRedirectCount()); + + verify(mMeasurementDao, times(1)).deleteAsyncRegistration(any(String.class)); + } + + @Test + public void runAsyncRegistrationQueueWorker_appSrc_defaultReg_redirectChain_typeLocation() + throws DatastoreException { + // Setup + AsyncRegistrationQueueRunner asyncRegistrationQueueRunner = + getSpyAsyncRegistrationQueueRunner(); + AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppSource(); + Answer<Optional<Source>> answerAsyncSourceFetcher = + getAsyncSourceAnswerForLocationTypeRedirectToWellKnown( + LOCATION_TYPE_REDIRECT_URI, validAsyncRegistration); + doAnswer(answerAsyncSourceFetcher) + .when(mAsyncSourceFetcher) + .fetchSource(any(), any(), any()); + when(mFlags.getMeasurementEnableRedirectToWellKnownPath()).thenReturn(true); + + List<Source.FakeReport> eventReportList = + Collections.singletonList( + new Source.FakeReport(new UnsignedLong(1L), 1L, List.of(APP_DESTINATION))); + when(mSourceNoiseHandler.assignAttributionModeAndGenerateFakeReports(mMockedSource)) + .thenReturn(eventReportList); + when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyInt(), any())) + .thenReturn(validAsyncRegistration); + KeyValueData redirectCount = getKeyValueDataRedirectCount(); + when(mMeasurementDao.getKeyValueData(anyString(), any())).thenReturn(redirectCount); + + // Execution + asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(); + // Set up the second invocation. + Answer<Optional<Source>> answerAsyncSourceFetcher2 = + getAsyncSourceAnswerForLocationTypeRedirectToWellKnown( + LOCATION_TYPE_REDIRECT_URI_2, validAsyncRegistration); + doAnswer(answerAsyncSourceFetcher2) + .when(mAsyncSourceFetcher) + .fetchSource(any(), any(), any()); + + asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(); + + // Set up the third invocation. + Answer<Optional<Source>> answerAsyncSourceFetcher3 = + getAsyncSourceAnswerForLocationTypeRedirectToWellKnown( + LOCATION_TYPE_REDIRECT_URI_3, validAsyncRegistration); + + doAnswer(answerAsyncSourceFetcher3) + .when(mAsyncSourceFetcher) + .fetchSource(any(), any(), any()); + + asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(); + + // Assertions for all invocations + ArgumentCaptor<AsyncRegistration> asyncRegistrationArgumentCaptor = + ArgumentCaptor.forClass(AsyncRegistration.class); + verify(mMeasurementDao, times(3)) + .insertAsyncRegistration(asyncRegistrationArgumentCaptor.capture()); + Assert.assertEquals(3, asyncRegistrationArgumentCaptor.getAllValues().size()); + + ArgumentCaptor<KeyValueData> redirectCountCaptor = + ArgumentCaptor.forClass(KeyValueData.class); + verify(mMeasurementDao, times(3)).insertOrUpdateKeyValueData(redirectCountCaptor.capture()); + assertEquals(4, redirectCountCaptor.getValue().getRegistrationRedirectCount()); + + // Assertions for first invocation + assertRepeatedAsyncRegistration( + asyncRegistrationArgumentCaptor, + 0, + getRegistrationRedirectToWellKnownUri( + Uri.parse(LOCATION_TYPE_REDIRECT_URI), + LOCATION_TYPE_REDIRECT_URI.toString())); + + // Assertions for second invocation + assertRepeatedAsyncRegistration( + asyncRegistrationArgumentCaptor, + 1, + getRegistrationRedirectToWellKnownUri( + Uri.parse(LOCATION_TYPE_REDIRECT_URI_2), + LOCATION_TYPE_REDIRECT_URI_2.toString())); + + // Assertions for third invocation + assertRepeatedAsyncRegistration( + asyncRegistrationArgumentCaptor, + 2, + getRegistrationRedirectToWellKnownUri( + Uri.parse(LOCATION_TYPE_REDIRECT_URI_3), + LOCATION_TYPE_REDIRECT_URI_3.toString())); + } + + @Test + public void runAsyncRegistrationQueueWorker_appSrc_defaultReg_redirectWithExistingPathAndQuery() + throws DatastoreException { + // Setup + AsyncRegistrationQueueRunner asyncRegistrationQueueRunner = + getSpyAsyncRegistrationQueueRunner(); + AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppSource(); + String redirectWithExistingPath = LOCATION_TYPE_REDIRECT_URI + "/path?key=value"; + Answer<Optional<Source>> answerAsyncSourceFetcher = + getAsyncSourceAnswerForLocationTypeRedirectToWellKnown( + redirectWithExistingPath, validAsyncRegistration); + doAnswer(answerAsyncSourceFetcher) + .when(mAsyncSourceFetcher) + .fetchSource(any(), any(), any()); + when(mFlags.getMeasurementEnableRedirectToWellKnownPath()).thenReturn(true); + + List<Source.FakeReport> eventReportList = + Collections.singletonList( + new Source.FakeReport(new UnsignedLong(1L), 1L, List.of(APP_DESTINATION))); + when(mSourceNoiseHandler.assignAttributionModeAndGenerateFakeReports(mMockedSource)) + .thenReturn(eventReportList); + when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyInt(), any())) + .thenReturn(validAsyncRegistration); + KeyValueData redirectCount = getKeyValueDataRedirectCount(); + when(mMeasurementDao.getKeyValueData(anyString(), any())).thenReturn(redirectCount); + + // Execution + + asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(); + + ArgumentCaptor<AsyncRegistration> asyncRegistrationArgumentCaptor = + ArgumentCaptor.forClass(AsyncRegistration.class); + + // Assertions + verify(mAsyncSourceFetcher, times(1)) + .fetchSource(any(AsyncRegistration.class), any(), any()); + verify(mMeasurementDao, times(1)).insertEventReport(any(EventReport.class)); + verify(mMeasurementDao, times(1)).insertSource(any(Source.class)); + verify(mMeasurementDao, times(1)) + .insertAsyncRegistration(asyncRegistrationArgumentCaptor.capture()); + + Assert.assertEquals(1, asyncRegistrationArgumentCaptor.getAllValues().size()); + AsyncRegistration asyncReg = asyncRegistrationArgumentCaptor.getAllValues().get(0); + + Uri expectedUri = + Uri.parse( + LOCATION_TYPE_REDIRECT_URI + + "/" + + AsyncRedirects.WELL_KNOWN_PATH_SEGMENT + + "?" + + AsyncRedirects.WELL_KNOWN_QUERY_PARAM + + "=" + + Uri.encode(redirectWithExistingPath)); + Assert.assertEquals(expectedUri, asyncReg.getRegistrationUri()); + } + + @Test + public void runAsyncRegistrationQueueWorker_noSourceReg_RedirectHasSource() + throws DatastoreException { + // Setup + AsyncRegistrationQueueRunner asyncRegistrationQueueRunner = + getSpyAsyncRegistrationQueueRunner(); + AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppSource(); + + // Create an empty response for fetchSource. This is necessary because we need to mock the + // addition of a redirect, despite a failing initial source registration. + Map<String, List<String>> redirectHeaders = + getRedirectHeaders( + AsyncRegistration.RedirectType.LOCATION, + List.of(LOCATION_TYPE_REDIRECT_URI)); + redirectHeaders.put( + AsyncRedirects.HEADER_ATTRIBUTION_REPORTING_REDIRECT_CONFIG, + List.of(AsyncRedirects.REDIRECT_302_TO_WELL_KNOWN)); + Answer<Optional<Source>> answerEmptySource = + invocation -> { + AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1); + asyncFetchStatus.setResponseStatus(AsyncFetchStatus.ResponseStatus.SUCCESS); + AsyncRedirects asyncRedirects = invocation.getArgument(2); + asyncRedirects.configure(redirectHeaders, mFlags, validAsyncRegistration); + return Optional.empty(); + }; + + doAnswer(answerEmptySource).when(mAsyncSourceFetcher).fetchSource(any(), any(), any()); + + when(mFlags.getMeasurementEnableRedirectToWellKnownPath()).thenReturn(true); + + List<Source.FakeReport> eventReportList = + Collections.singletonList( + new Source.FakeReport(new UnsignedLong(1L), 1L, List.of(APP_DESTINATION))); + when(mSourceNoiseHandler.assignAttributionModeAndGenerateFakeReports(mMockedSource)) + .thenReturn(eventReportList); + when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyInt(), any())) + .thenReturn(validAsyncRegistration); + KeyValueData redirectCount = getKeyValueDataRedirectCount(); + when(mMeasurementDao.getKeyValueData(anyString(), any())).thenReturn(redirectCount); + + // Execution + + asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(); + + // Set up second invocation of runner. This time, do return a valid source. + Answer<Optional<Source>> answerAsyncSourceFetcher = + getAsyncSourceAnswerForLocationTypeRedirectToWellKnown( + LOCATION_TYPE_REDIRECT_URI_2, validAsyncRegistration); + + doAnswer(answerAsyncSourceFetcher) + .when(mAsyncSourceFetcher) + .fetchSource(any(), any(), any()); + + asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(); + + ArgumentCaptor<AsyncRegistration> asyncRegistrationArgumentCaptor = + ArgumentCaptor.forClass(AsyncRegistration.class); + + // Assertions + verify(mAsyncSourceFetcher, times(2)) + .fetchSource(any(AsyncRegistration.class), any(), any()); + verify(mMeasurementDao, times(1)).insertEventReport(any(EventReport.class)); + verify(mMeasurementDao, times(1)).insertSource(any(Source.class)); + verify(mMeasurementDao, times(2)) + .insertAsyncRegistration(asyncRegistrationArgumentCaptor.capture()); + + Assert.assertEquals(2, asyncRegistrationArgumentCaptor.getAllValues().size()); + AsyncRegistration asyncReg = asyncRegistrationArgumentCaptor.getAllValues().get(0); + + // Assert first invocation's redirect + Uri expectedUri = + getRegistrationRedirectToWellKnownUri( + Uri.parse(LOCATION_TYPE_REDIRECT_URI), + LOCATION_TYPE_REDIRECT_URI.toString()); + Assert.assertEquals(expectedUri, asyncReg.getRegistrationUri()); + + AsyncRegistration asyncReg2 = asyncRegistrationArgumentCaptor.getAllValues().get(1); + + // Assert second invocation's redirect + expectedUri = + getRegistrationRedirectToWellKnownUri( + Uri.parse(LOCATION_TYPE_REDIRECT_URI_2), + LOCATION_TYPE_REDIRECT_URI_2.toString()); + Assert.assertEquals(expectedUri, asyncReg2.getRegistrationUri()); + } + + @Test + public void runAsyncRegistrationQueueWorker_appSrc_defaultReg_redirectAlreadyWellKnown() + throws DatastoreException { + // Setup + AsyncRegistrationQueueRunner asyncRegistrationQueueRunner = + getSpyAsyncRegistrationQueueRunner(); + AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppSource(); + String redirectAlreadyWellKnown = + LOCATION_TYPE_REDIRECT_URI + "/" + AsyncRedirects.WELL_KNOWN_PATH_SEGMENT; + Answer<Optional<Source>> answerAsyncSourceFetcher = + getAsyncSourceAnswerForLocationTypeRedirectToWellKnown( + redirectAlreadyWellKnown, validAsyncRegistration); + doAnswer(answerAsyncSourceFetcher) + .when(mAsyncSourceFetcher) + .fetchSource(any(), any(), any()); + when(mFlags.getMeasurementEnableRedirectToWellKnownPath()).thenReturn(true); + + List<Source.FakeReport> eventReportList = + Collections.singletonList( + new Source.FakeReport(new UnsignedLong(1L), 1L, List.of(APP_DESTINATION))); + when(mSourceNoiseHandler.assignAttributionModeAndGenerateFakeReports(mMockedSource)) + .thenReturn(eventReportList); + when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyInt(), any())) + .thenReturn(validAsyncRegistration); + KeyValueData redirectCount = getKeyValueDataRedirectCount(); + when(mMeasurementDao.getKeyValueData(anyString(), any())).thenReturn(redirectCount); + + // Execution + + asyncRegistrationQueueRunner.runAsyncRegistrationQueueWorker(); + + ArgumentCaptor<AsyncRegistration> asyncRegistrationArgumentCaptor = + ArgumentCaptor.forClass(AsyncRegistration.class); + + // Assertions + verify(mAsyncSourceFetcher, times(1)) + .fetchSource(any(AsyncRegistration.class), any(), any()); + verify(mMeasurementDao, times(1)).insertEventReport(any(EventReport.class)); + verify(mMeasurementDao, times(1)).insertSource(any(Source.class)); + verify(mMeasurementDao, times(1)) + .insertAsyncRegistration(asyncRegistrationArgumentCaptor.capture()); + + Assert.assertEquals(1, asyncRegistrationArgumentCaptor.getAllValues().size()); + AsyncRegistration asyncReg = asyncRegistrationArgumentCaptor.getAllValues().get(0); + // Assert .well-known isn't duplicated in path. + Assert.assertEquals( + getRegistrationRedirectToWellKnownUri( + Uri.parse(LOCATION_TYPE_REDIRECT_URI), redirectAlreadyWellKnown.toString()), + asyncReg.getRegistrationUri()); + } + + @Test public void runAsyncRegistrationQueueWorker_appInstalled_markToBeDeleted() throws DatastoreException, PackageManager.NameNotFoundException { // Setup @@ -639,17 +959,18 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo mLogger); AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppSource(); - + Map<String, List<String>> redirectHeaders = + getRedirectHeaders( + AsyncRegistration.RedirectType.LIST, + List.of( + WebUtil.validUri("https://example.test/sF1").toString(), + WebUtil.validUri("https://example.test/sF2").toString())); Answer<?> answerAsyncSourceFetcher = invocation -> { AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1); asyncFetchStatus.setResponseStatus(AsyncFetchStatus.ResponseStatus.SUCCESS); - AsyncRedirect asyncRedirect = invocation.getArgument(2); - asyncRedirect.addToRedirects( - AsyncRegistration.RedirectType.LIST, - List.of( - WebUtil.validUri("https://example.test/sF1"), - WebUtil.validUri("https://example.test/sF2"))); + AsyncRedirects asyncRedirects = invocation.getArgument(2); + asyncRedirects.configure(redirectHeaders, mFlags, validAsyncRegistration); return Optional.of( SourceFixture.getValidSourceBuilder() .setDropSourceIfInstalled(true) @@ -670,14 +991,7 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyInt(), any())) .thenReturn(validAsyncRegistration) .thenReturn(null); - KeyValueData redirectCount = - new KeyValueData.Builder() - .setDataType(KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT) - .setKey( - AsyncRegistrationFixture.ValidAsyncRegistrationParams - .REGISTRATION_ID) - .setValue(null) // Should default to 1 - .build(); + KeyValueData redirectCount = getKeyValueDataRedirectCount(); when(mMeasurementDao.getKeyValueData(anyString(), any())).thenReturn(redirectCount); // Execution @@ -713,17 +1027,18 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo mLogger); AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppSource(); - + Map<String, List<String>> redirectHeaders = + getRedirectHeaders( + AsyncRegistration.RedirectType.LIST, + List.of( + WebUtil.validUri("https://example.test/sF1").toString(), + WebUtil.validUri("https://example.test/sF2").toString())); Answer<?> answerAsyncSourceFetcher = invocation -> { AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1); asyncFetchStatus.setResponseStatus(AsyncFetchStatus.ResponseStatus.SUCCESS); - AsyncRedirect asyncRedirect = invocation.getArgument(2); - asyncRedirect.addToRedirects( - AsyncRegistration.RedirectType.LIST, - List.of( - WebUtil.validUri("https://example.test/sF1"), - WebUtil.validUri("https://example.test/sF2"))); + AsyncRedirects asyncRedirects = invocation.getArgument(2); + asyncRedirects.configure(redirectHeaders, mFlags, validAsyncRegistration); return Optional.of( SourceFixture.getValidSourceBuilder() .setDropSourceIfInstalled(true) @@ -744,14 +1059,7 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyInt(), any())) .thenReturn(validAsyncRegistration) .thenReturn(null); - KeyValueData redirectCount = - new KeyValueData.Builder() - .setDataType(KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT) - .setKey( - AsyncRegistrationFixture.ValidAsyncRegistrationParams - .REGISTRATION_ID) - .setValue(null) // Should default to 1 - .build(); + KeyValueData redirectCount = getKeyValueDataRedirectCount(); when(mMeasurementDao.getKeyValueData(anyString(), any())).thenReturn(redirectCount); // Execution @@ -786,17 +1094,18 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo mLogger); AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppSource(); - + Map<String, List<String>> redirectHeaders = + getRedirectHeaders( + AsyncRegistration.RedirectType.LIST, + List.of( + WebUtil.validUri("https://example.test/sF1").toString(), + WebUtil.validUri("https://example.test/sF2").toString())); Answer<?> answerAsyncSourceFetcher = invocation -> { AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1); asyncFetchStatus.setResponseStatus(AsyncFetchStatus.ResponseStatus.SUCCESS); - AsyncRedirect asyncRedirect = invocation.getArgument(2); - asyncRedirect.addToRedirects( - AsyncRegistration.RedirectType.LIST, - List.of( - WebUtil.validUri("https://example.test/sF1"), - WebUtil.validUri("https://example.test/sF2"))); + AsyncRedirects asyncRedirects = invocation.getArgument(2); + asyncRedirects.configure(redirectHeaders, mFlags, validAsyncRegistration); return Optional.of( SourceFixture.getValidSourceBuilder() .setDropSourceIfInstalled(false) @@ -817,14 +1126,7 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyInt(), any())) .thenReturn(validAsyncRegistration) .thenReturn(null); - KeyValueData redirectCount = - new KeyValueData.Builder() - .setDataType(KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT) - .setKey( - AsyncRegistrationFixture.ValidAsyncRegistrationParams - .REGISTRATION_ID) - .setValue(null) // Should default to 1 - .build(); + KeyValueData redirectCount = getKeyValueDataRedirectCount(); when(mMeasurementDao.getKeyValueData(anyString(), any())).thenReturn(redirectCount); // Execution @@ -860,17 +1162,18 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo mLogger); AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppSource(); - + Map<String, List<String>> redirectHeaders = + getRedirectHeaders( + AsyncRegistration.RedirectType.LIST, + List.of( + WebUtil.validUri("https://example.test/sF1").toString(), + WebUtil.validUri("https://example.test/sF2").toString())); Answer<?> answerAsyncSourceFetcher = invocation -> { AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1); asyncFetchStatus.setResponseStatus(AsyncFetchStatus.ResponseStatus.SUCCESS); - AsyncRedirect asyncRedirect = invocation.getArgument(2); - asyncRedirect.addToRedirects( - AsyncRegistration.RedirectType.LIST, - List.of( - WebUtil.validUri("https://example.test/sF1"), - WebUtil.validUri("https://example.test/sF2"))); + AsyncRedirects asyncRedirects = invocation.getArgument(2); + asyncRedirects.configure(redirectHeaders, mFlags, validAsyncRegistration); return Optional.of( SourceFixture.getValidSourceBuilder() .setDropSourceIfInstalled(false) @@ -891,14 +1194,7 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyInt(), any())) .thenReturn(validAsyncRegistration) .thenReturn(null); - KeyValueData redirectCount = - new KeyValueData.Builder() - .setDataType(KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT) - .setKey( - AsyncRegistrationFixture.ValidAsyncRegistrationParams - .REGISTRATION_ID) - .setValue(null) // Should default to 1 - .build(); + KeyValueData redirectCount = getKeyValueDataRedirectCount(); when(mMeasurementDao.getKeyValueData(anyString(), any())).thenReturn(redirectCount); // Execution @@ -922,17 +1218,16 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo getSpyAsyncRegistrationQueueRunner(); AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppTrigger(); - + Map<String, List<String>> redirectHeaders = + getRedirectHeaders( + AsyncRegistration.RedirectType.LIST, + List.of(LIST_TYPE_REDIRECT_URI_1, LIST_TYPE_REDIRECT_URI_2)); Answer<Optional<Trigger>> answerAsyncTriggerFetcher = invocation -> { AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1); asyncFetchStatus.setResponseStatus(AsyncFetchStatus.ResponseStatus.SUCCESS); - AsyncRedirect asyncRedirect = invocation.getArgument(2); - asyncRedirect.addToRedirects( - AsyncRegistration.RedirectType.LIST, - List.of( - Uri.parse(LIST_TYPE_REDIRECT_URI_1), - Uri.parse(LIST_TYPE_REDIRECT_URI_2))); + AsyncRedirects asyncRedirects = invocation.getArgument(2); + asyncRedirects.configure(redirectHeaders, mFlags, validAsyncRegistration); return Optional.of(mMockedTrigger); }; doAnswer(answerAsyncTriggerFetcher) @@ -942,14 +1237,7 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyInt(), any())) .thenReturn(validAsyncRegistration) .thenReturn(null); - KeyValueData redirectCount = - new KeyValueData.Builder() - .setDataType(KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT) - .setKey( - AsyncRegistrationFixture.ValidAsyncRegistrationParams - .REGISTRATION_ID) - .setValue(null) // Should default to 1 - .build(); + KeyValueData redirectCount = getKeyValueDataRedirectCount(); when(mMeasurementDao.getKeyValueData(anyString(), any())).thenReturn(redirectCount); // Execution @@ -996,15 +1284,16 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo getSpyAsyncRegistrationQueueRunner(); AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppTrigger(); - + Map<String, List<String>> redirectHeaders = + getRedirectHeaders( + AsyncRegistration.RedirectType.LOCATION, + List.of(LOCATION_TYPE_REDIRECT_URI)); Answer<Optional<Trigger>> answerAsyncTriggerFetcher = invocation -> { AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1); asyncFetchStatus.setResponseStatus(AsyncFetchStatus.ResponseStatus.SUCCESS); - AsyncRedirect asyncRedirect = invocation.getArgument(2); - asyncRedirect.addToRedirects( - AsyncRegistration.RedirectType.LOCATION, - List.of(Uri.parse(LOCATION_TYPE_REDIRECT_URI))); + AsyncRedirects asyncRedirects = invocation.getArgument(2); + asyncRedirects.configure(redirectHeaders, mFlags, validAsyncRegistration); return Optional.of(mMockedTrigger); }; doAnswer(answerAsyncTriggerFetcher) @@ -1014,14 +1303,7 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo when(mMeasurementDao.fetchNextQueuedAsyncRegistration(anyInt(), any())) .thenReturn(validAsyncRegistration) .thenReturn(null); - KeyValueData redirectCount = - new KeyValueData.Builder() - .setDataType(KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT) - .setKey( - AsyncRegistrationFixture.ValidAsyncRegistrationParams - .REGISTRATION_ID) - .setValue(null) // Should default to 1 - .build(); + KeyValueData redirectCount = getKeyValueDataRedirectCount(); when(mMeasurementDao.getKeyValueData(anyString(), any())).thenReturn(redirectCount); // Execution @@ -1059,15 +1341,16 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo getSpyAsyncRegistrationQueueRunner(); AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppTrigger(); - + Map<String, List<String>> redirectHeaders = + getRedirectHeaders( + AsyncRegistration.RedirectType.LOCATION, + List.of(LOCATION_TYPE_REDIRECT_URI)); Answer<Optional<Trigger>> answerAsyncTriggerFetcher = invocation -> { AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1); asyncFetchStatus.setResponseStatus(AsyncFetchStatus.ResponseStatus.SUCCESS); - AsyncRedirect asyncRedirect = invocation.getArgument(2); - asyncRedirect.addToRedirects( - AsyncRegistration.RedirectType.LOCATION, - List.of(Uri.parse(LOCATION_TYPE_REDIRECT_URI))); + AsyncRedirects asyncRedirects = invocation.getArgument(2); + asyncRedirects.configure(redirectHeaders, mFlags, validAsyncRegistration); return Optional.of(mMockedTrigger); }; doAnswer(answerAsyncTriggerFetcher) @@ -1120,17 +1403,18 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo getSpyAsyncRegistrationQueueRunner(); AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppTrigger(); - + Map<String, List<String>> redirectHeaders = + getRedirectHeaders( + AsyncRegistration.RedirectType.LIST, + IntStream.range(1, 10) + .mapToObj((i) -> LIST_TYPE_REDIRECT_URI_1 + "/" + i) + .collect(Collectors.toList())); Answer<Optional<Trigger>> answerAsyncTriggerFetcher = invocation -> { AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1); asyncFetchStatus.setResponseStatus(AsyncFetchStatus.ResponseStatus.SUCCESS); - AsyncRedirect asyncRedirect = invocation.getArgument(2); - asyncRedirect.addToRedirects( - AsyncRegistration.RedirectType.LIST, - IntStream.range(1, 10) - .mapToObj((i) -> Uri.parse(LIST_TYPE_REDIRECT_URI_1 + "/" + i)) - .collect(Collectors.toList())); + AsyncRedirects asyncRedirects = invocation.getArgument(2); + asyncRedirects.configure(redirectHeaders, mFlags, validAsyncRegistration); return Optional.of(mMockedTrigger); }; doAnswer(answerAsyncTriggerFetcher) @@ -1189,15 +1473,16 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo getSpyAsyncRegistrationQueueRunner(); AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppTrigger(); - + Map<String, List<String>> redirectHeaders = + getRedirectHeaders( + AsyncRegistration.RedirectType.LOCATION, + Collections.singletonList((LOCATION_TYPE_REDIRECT_URI))); Answer<Optional<Trigger>> answerAsyncTriggerFetcher = invocation -> { AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1); asyncFetchStatus.setResponseStatus(AsyncFetchStatus.ResponseStatus.SUCCESS); - AsyncRedirect asyncRedirect = invocation.getArgument(2); - asyncRedirect.addToRedirects( - AsyncRegistration.RedirectType.LOCATION, - Collections.singletonList(Uri.parse(LOCATION_TYPE_REDIRECT_URI))); + AsyncRedirects asyncRedirects = invocation.getArgument(2); + asyncRedirects.configure(redirectHeaders, mFlags, validAsyncRegistration); return Optional.of(mMockedTrigger); }; doAnswer(answerAsyncTriggerFetcher) @@ -1433,17 +1718,18 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo getSpyAsyncRegistrationQueueRunner(); AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppTrigger(); - + Map<String, List<String>> redirectHeaders = + getRedirectHeaders( + AsyncRegistration.RedirectType.LIST, + List.of( + WebUtil.validUri("https://example.test/sF1").toString(), + WebUtil.validUri("https://example.test/sF2").toString())); Answer<?> answerAsyncTriggerFetcher = invocation -> { AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1); asyncFetchStatus.setResponseStatus(AsyncFetchStatus.ResponseStatus.SUCCESS); - AsyncRedirect asyncRedirect = invocation.getArgument(2); - asyncRedirect.addToRedirects( - AsyncRegistration.RedirectType.LIST, - List.of( - WebUtil.validUri("https://example.test/sF1"), - WebUtil.validUri("https://example.test/sF2"))); + AsyncRedirects asyncRedirects = invocation.getArgument(2); + asyncRedirects.configure(redirectHeaders, mFlags, validAsyncRegistration); return Optional.of(mMockedTrigger); }; doAnswer(answerAsyncTriggerFetcher) @@ -1610,18 +1896,19 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo getSpyAsyncRegistrationQueueRunner(); AsyncRegistration validAsyncRegistration = createAsyncRegistrationForAppTrigger(); - + Map<String, List<String>> redirectHeaders = + getRedirectHeaders( + AsyncRegistration.RedirectType.LIST, + List.of( + WebUtil.validUri("https://example.test/sF1").toString(), + WebUtil.validUri("https://example.test/sF2").toString())); Answer<?> answerAsyncTriggerFetcher = invocation -> { AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1); asyncFetchStatus.setResponseStatus(AsyncFetchStatus.ResponseStatus.SUCCESS); asyncFetchStatus.setEntityStatus(AsyncFetchStatus.EntityStatus.PARSING_ERROR); - AsyncRedirect asyncRedirect = invocation.getArgument(2); - asyncRedirect.addToRedirects( - AsyncRegistration.RedirectType.LIST, - List.of( - WebUtil.validUri("https://example.test/sF1"), - WebUtil.validUri("https://example.test/sF2"))); + AsyncRedirects asyncRedirects = invocation.getArgument(2); + asyncRedirects.configure(redirectHeaders, mFlags, validAsyncRegistration); return Optional.empty(); }; doAnswer(answerAsyncTriggerFetcher) @@ -3285,12 +3572,56 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo assertTrue(status); } - private RegistrationRequest buildRequest(String registrationUri) { - return new RegistrationRequest.Builder( - RegistrationRequest.REGISTER_SOURCE, - Uri.parse(registrationUri), - sDefaultContext.getAttributionSource().getPackageName(), - SDK_PACKAGE_NAME) + private static KeyValueData getKeyValueDataRedirectCount() { + return new KeyValueData.Builder() + .setDataType(KeyValueData.DataType.REGISTRATION_REDIRECT_COUNT) + .setKey(AsyncRegistrationFixture.ValidAsyncRegistrationParams.REGISTRATION_ID) + .setValue(null) // Should default to 1 + .build(); + } + + private Map<String, List<String>> getRedirectHeaders( + AsyncRegistration.RedirectType redirectType, List<String> uris) { + Map<String, List<String>> headers = new HashMap<>(); + if (redirectType.equals(AsyncRegistration.RedirectType.LOCATION)) { + headers.put(AsyncRedirects.REDIRECT_LOCATION_HEADER_KEY, uris); + } else { + headers.put(AsyncRedirects.REDIRECT_LIST_HEADER_KEY, uris); + } + + return headers; + } + + private void assertRepeatedAsyncRegistration( + ArgumentCaptor<AsyncRegistration> asyncRegistrationArgumentCaptor, + int index, + Uri redirectUri) { + AsyncRegistration asyncReg = asyncRegistrationArgumentCaptor.getAllValues().get(index); + Assert.assertEquals(redirectUri, asyncReg.getRegistrationUri()); + } + + private Answer<Optional<Source>> getAsyncSourceAnswerForLocationTypeRedirectToWellKnown( + String redirectUri, AsyncRegistration asyncRegistration) { + Map<String, List<String>> redirectHeaders = + getRedirectHeaders(AsyncRegistration.RedirectType.LOCATION, List.of(redirectUri)); + redirectHeaders.put( + AsyncRedirects.HEADER_ATTRIBUTION_REPORTING_REDIRECT_CONFIG, + List.of(AsyncRedirects.REDIRECT_302_TO_WELL_KNOWN)); + return invocation -> { + AsyncFetchStatus asyncFetchStatus = invocation.getArgument(1); + asyncFetchStatus.setResponseStatus(AsyncFetchStatus.ResponseStatus.SUCCESS); + AsyncRedirects asyncRedirects = invocation.getArgument(2); + asyncRedirects.configure(redirectHeaders, mFlags, asyncRegistration); + return Optional.of(mMockedSource); + }; + } + + private Uri getRegistrationRedirectToWellKnownUri( + Uri registrationUri, String originalUriString) { + return registrationUri + .buildUpon() + .encodedPath(AsyncRedirects.WELL_KNOWN_PATH_SEGMENT) + .appendQueryParameter(AsyncRedirects.WELL_KNOWN_QUERY_PARAM, originalUriString) .build(); } @@ -3316,7 +3647,7 @@ public final class AsyncRegistrationQueueRunnerTest extends AdServicesExtendedMo new Source.FakeReport( new UnsignedLong(0L), new EventReportWindowCalcDelegate(mFlags) - .getReportingTimeForNoising(source, 0, false), + .getReportingTimeForNoising(source, 0), destinations)) .collect(Collectors.toList()); } diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/registration/AsyncSourceFetcherTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/registration/AsyncSourceFetcherTest.java index 0489c91f5..aa07077e8 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/registration/AsyncSourceFetcherTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/registration/AsyncSourceFetcherTest.java @@ -122,6 +122,15 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC private static final String LIST_TYPE_REDIRECT_URI = WebUtil.validUrl("https://bar.test"); private static final String LOCATION_TYPE_REDIRECT_URI = WebUtil.validUrl("https://example.test"); + private static final String LOCATION_TYPE_REDIRECT_WELLKNOWN_URI = + WebUtil.validUrl( + LOCATION_TYPE_REDIRECT_URI + + "/" + + AsyncRedirects.WELL_KNOWN_PATH_SEGMENT + + "?" + + AsyncRedirects.WELL_KNOWN_QUERY_PARAM + + "=" + + Uri.encode(LOCATION_TYPE_REDIRECT_URI)); private static final long ALT_EVENT_ID = 123456789; private static final long ALT_EXPIRY = 456790; private static final Uri REGISTRATION_URI_1 = WebUtil.validUri("https://subdomain.foo.test"); @@ -226,6 +235,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC .thenReturn(Flags.MEASUREMENT_MAX_INSTALL_ATTRIBUTION_WINDOW); when(mFlags.getMeasurementMinInstallAttributionWindow()) .thenReturn(Flags.MEASUREMENT_MIN_INSTALL_ATTRIBUTION_WINDOW); + when(mFlags.getMeasurementVtcConfigurableMaxEventReportsCount()) + .thenReturn(Flags.DEFAULT_MEASUREMENT_VTC_CONFIGURABLE_MAX_EVENT_REPORTS_COUNT); + when(mFlags.getMeasurementEventReportsVtcEarlyReportingWindows()) + .thenReturn(Flags.MEASUREMENT_EVENT_REPORTS_VTC_EARLY_REPORTING_WINDOWS); + when(mFlags.getMeasurementEventReportsCtcEarlyReportingWindows()) + .thenReturn(Flags.MEASUREMENT_EVENT_REPORTS_CTC_EARLY_REPORTING_WINDOWS); } @Test @@ -260,13 +275,13 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "}\n"))); when(mFlags.getMeasurementEnableXNA()).thenReturn(true); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); asyncFetchStatus.setRegistrationDelay(0L); // Execution AsyncRegistration asyncRegistration = appSourceRegistrationRequest(request); Optional<Source> fetch = - mFetcher.fetchSource(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchSource(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -319,7 +334,7 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC when(mUrlConnection.getHeaderFields()) .thenReturn( Map.of( - "Attribution-Reporting-Redirect", + AsyncRedirects.REDIRECT_LIST_HEADER_KEY, List.of(LIST_TYPE_REDIRECT_URI), "Attribution-Reporting-Register-Source", List.of( @@ -340,19 +355,20 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEBUG_KEY + "\"\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion // Redirects should be parsed & added verify(mFetcher, times(1)).openUrl(any()); assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); - assertEquals(1, asyncRedirect.getRedirects().size()); - assertEquals(LIST_TYPE_REDIRECT_URI, asyncRedirect.getRedirects().get(0).toString()); + assertEquals(1, asyncRedirects.getRedirects().size()); + assertEquals( + LIST_TYPE_REDIRECT_URI, asyncRedirects.getRedirects().get(0).getUri().toString()); // Source shouldn't be created assertEquals( @@ -385,12 +401,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"expiry\": \"432000\"," + "\"source_event_id\": \"987654321\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -433,12 +449,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"expiry\": \"432000\"," + "\"source_event_id\": \"987654321\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -476,12 +492,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"expiry\": \"432000\"," + "\"source_event_id\": \"987654321\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -520,12 +536,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"expiry\": \"432000\"," + "\"source_event_id\": \"987654321\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -555,12 +571,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -597,12 +613,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"install_attribution_window\": null,\n" + " \"post_install_exclusivity_window\": null\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -646,12 +662,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\":" + " \"9876543210\"\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -678,12 +694,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC WebUtil.validUri("http://foo.test"), sContext.getPackageName(), SDK_PACKAGE_NAME); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals( AsyncFetchStatus.ResponseStatus.INVALID_URL, asyncFetchStatus.getResponseStatus()); @@ -698,12 +714,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC WebUtil.validUri("bad-schema://foo.test"), sContext.getPackageName(), SDK_PACKAGE_NAME); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals( AsyncFetchStatus.ResponseStatus.INVALID_URL, asyncFetchStatus.getResponseStatus()); @@ -716,12 +732,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC doThrow(new IOException("Bad internet things")) .when(mFetcher) .openUrl(new URL(DEFAULT_REGISTRATION)); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals( AsyncFetchStatus.ResponseStatus.NETWORK_ERROR, @@ -743,12 +759,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"source_event_id\": \"" + DEFAULT_EVENT_ID + "\""))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals( AsyncFetchStatus.EntityStatus.PARSING_ERROR, asyncFetchStatus.getEntityStatus()); @@ -762,12 +778,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC doReturn(mUrlConnection).when(mFetcher).openUrl(new URL(DEFAULT_REGISTRATION)); when(mUrlConnection.getResponseCode()).thenReturn(200); when(mUrlConnection.getHeaderFields()).thenReturn(Collections.emptyMap()); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -790,12 +806,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"destination\": \"" + DEFAULT_DESTINATION + "\""))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals( AsyncFetchStatus.EntityStatus.PARSING_ERROR, asyncFetchStatus.getEntityStatus()); @@ -821,12 +837,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEFAULT_EVENT_ID + "\"\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -861,12 +877,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"source_event_id\":\"" + DEFAULT_EVENT_ID + "\"," + "\"expiry\":\"-15\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -893,12 +909,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"," + "\"expiry\":\"86399\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -933,12 +949,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"," + "\"expiry\":\"2592001\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -980,12 +996,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + TimeUnit.DAYS.toSeconds(1) / 2L) + "\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -1025,12 +1041,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC - 1L) + "\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -1067,12 +1083,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + String.valueOf(TimeUnit.HOURS.toSeconds(25)) + "\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -1107,12 +1123,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"," + "\"expiry\":\"172800\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -1149,12 +1165,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"event_report_window\":\"86400\"," + "\"aggregatable_report_window\":\"86400\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -1192,12 +1208,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"event_report_window\":\"2000\"," + "\"aggregatable_report_window\":\"1728\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -1236,12 +1252,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"event_report_window\":\"2000\"," + "\"aggregatable_report_window\":\"1728\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -1279,12 +1295,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"event_report_window\":\"172801\"," + "\"aggregatable_report_window\":\"172801\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -1323,12 +1339,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"event_report_window\":\"2592001\"," + "\"aggregatable_report_window\":\"2592001\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -1362,12 +1378,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"event_report_window\":86400," + "\"aggregatable_report_window\":86400" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -1397,12 +1413,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"event_report_window\":\"-86400\"," + "\"aggregatable_report_window\":\"86400\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -1428,12 +1444,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"event_report_window\":\"86400\"," + "\"aggregatable_report_window\":\"-86400\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -1457,12 +1473,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"source_event_id\":\"" + DEFAULT_EVENT_ID + "\"," + "\"priority\":15" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -1486,13 +1502,13 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEFAULT_DESTINATION + "\"," + "\"source_event_id\":35}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); @@ -1517,13 +1533,13 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEFAULT_DESTINATION + "\"," + "\"source_event_id\":\"-35\"}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); @@ -1546,13 +1562,13 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEFAULT_DESTINATION + "\"," + "\"source_event_id\":\"-35\"}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); @@ -1577,13 +1593,13 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEFAULT_DESTINATION + "\",\"source_event_id\":\"" + "18446744073709551616\"}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); @@ -1606,13 +1622,13 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEFAULT_DESTINATION + "\",\"source_event_id\":\"" + "18446744073709551616\"}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); @@ -1637,13 +1653,13 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEFAULT_DESTINATION + "\"," + "\"source_event_id\":\"8l2\"}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); @@ -1666,13 +1682,13 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEFAULT_DESTINATION + "\"," + "\"source_event_id\":\"8l2\"}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); @@ -1713,13 +1729,13 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "}\n"))); when(mFlags.getMeasurementEnableXNA()).thenReturn(false); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); @@ -1751,13 +1767,13 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEFAULT_DESTINATION + "\",\"source_event_id\":\"" + "18446744073709551615\"}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); @@ -1793,13 +1809,13 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEFAULT_EVENT_ID + "\"," + "\"debug_key\":18}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); @@ -1835,13 +1851,13 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEFAULT_EVENT_ID + "\"," + "\"debug_key\":\"-18\"}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); @@ -1878,13 +1894,13 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"," + "\"debug_key\":\"18446744073709551616\"}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); @@ -1920,13 +1936,13 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEFAULT_EVENT_ID + "\"," + "\"debug_key\":\"987fs\"}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); @@ -1962,13 +1978,13 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEFAULT_EVENT_ID + "\"," + "\"debug_key\":\"18446744073709551615\"}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); @@ -2005,12 +2021,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"expiry\": null\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -2047,12 +2063,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\",\n" + "\"expiry\": 1" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -2089,12 +2105,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\",\n" + "\"expiry\": \"1\"" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -2131,12 +2147,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\",\n" + "\"expiry\": 2678400" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -2173,12 +2189,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\",\n" + "\"expiry\": \"2678400\"" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -2213,13 +2229,13 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEFAULT_EVENT_ID + "\"," + "\"debug_reporting\":\"true\"}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); @@ -2254,13 +2270,13 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEFAULT_EVENT_ID + "\"," + "\"debug_reporting\":\"invalid\"}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); @@ -2295,13 +2311,13 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEFAULT_EVENT_ID + "\"," + "\"debug_reporting\":\"null\"}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); @@ -2336,13 +2352,13 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEFAULT_EVENT_ID + "\"," + "\"debug_reporting\":null}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); @@ -2376,13 +2392,13 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"source_event_id\":\"" + DEFAULT_EVENT_ID + "\"}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); @@ -2421,12 +2437,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "" + "}\n")); when(mUrlConnection.getHeaderFields()).thenReturn(headersSecondRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals( AsyncFetchStatus.ResponseStatus.SERVER_UNAVAILABLE, @@ -2442,7 +2458,8 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC when(mUrlConnection.getResponseCode()).thenReturn(200); Map<String, List<String>> headersFirstRequest = new HashMap<>(); headersFirstRequest.put("Attribution-Reporting-Register-Source", List.of("{}")); - headersFirstRequest.put("Attribution-Reporting-Redirect", List.of(LIST_TYPE_REDIRECT_URI)); + headersFirstRequest.put( + AsyncRedirects.REDIRECT_LIST_HEADER_KEY, List.of(LIST_TYPE_REDIRECT_URI)); Map<String, List<String>> headersSecondRequest = new HashMap<>(); headersSecondRequest.put( "Attribution-Reporting-Register-Source", @@ -2461,16 +2478,17 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC when(mUrlConnection.getHeaderFields()) .thenReturn(headersFirstRequest) .thenReturn(headersSecondRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); - assertEquals(1, asyncRedirect.getRedirects().size()); - assertEquals(LIST_TYPE_REDIRECT_URI, asyncRedirect.getRedirects().get(0).toString()); + assertEquals(1, asyncRedirects.getRedirects().size()); + assertEquals( + LIST_TYPE_REDIRECT_URI, asyncRedirects.getRedirects().get(0).getUri().toString()); assertEquals( AsyncFetchStatus.EntityStatus.PARSING_ERROR, asyncFetchStatus.getEntityStatus()); assertFalse(fetch.isPresent()); @@ -2500,14 +2518,15 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEFAULT_EXPIRY + "" + "}\n")); - headersFirstRequest.put("Attribution-Reporting-Redirect", List.of(LIST_TYPE_REDIRECT_URI)); + headersFirstRequest.put( + AsyncRedirects.REDIRECT_LIST_HEADER_KEY, List.of(LIST_TYPE_REDIRECT_URI)); when(mUrlConnection.getHeaderFields()).thenReturn(headersFirstRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals(AsyncFetchStatus.EntityStatus.SUCCESS, asyncFetchStatus.getEntityStatus()); @@ -2520,8 +2539,9 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC assertEquals( result.getEventTime() + TimeUnit.SECONDS.toMillis(DEFAULT_EXPIRY_ROUNDED), result.getExpiryTime()); - assertEquals(1, asyncRedirect.getRedirects().size()); - assertEquals(LIST_TYPE_REDIRECT_URI, asyncRedirect.getRedirects().get(0).toString()); + assertEquals(1, asyncRedirects.getRedirects().size()); + assertEquals( + LIST_TYPE_REDIRECT_URI, asyncRedirects.getRedirects().get(0).getUri().toString()); assertEquals(DEFAULT_REGISTRATION, result.getRegistrationOrigin().toString()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); } @@ -2549,14 +2569,15 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEFAULT_EXPIRY + "\"" + "}\n")); - headersFirstRequest.put("Attribution-Reporting-Redirect", List.of(LIST_TYPE_REDIRECT_URI)); + headersFirstRequest.put( + AsyncRedirects.REDIRECT_LIST_HEADER_KEY, List.of(LIST_TYPE_REDIRECT_URI)); when(mUrlConnection.getHeaderFields()).thenReturn(headersFirstRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals(AsyncFetchStatus.EntityStatus.SUCCESS, asyncFetchStatus.getEntityStatus()); @@ -2569,8 +2590,9 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC assertEquals( result.getEventTime() + TimeUnit.SECONDS.toMillis(DEFAULT_EXPIRY_ROUNDED), result.getExpiryTime()); - assertEquals(1, asyncRedirect.getRedirects().size()); - assertEquals(LIST_TYPE_REDIRECT_URI, asyncRedirect.getRedirects().get(0).toString()); + assertEquals(1, asyncRedirects.getRedirects().size()); + assertEquals( + LIST_TYPE_REDIRECT_URI, asyncRedirects.getRedirects().get(0).getUri().toString()); assertEquals(DEFAULT_REGISTRATION, result.getRegistrationOrigin().toString()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); } @@ -2585,33 +2607,36 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC Map<String, List<String>> headers = getDefaultHeaders(); // Populate both 'list' and 'location' type headers - headers.put("Attribution-Reporting-Redirect", List.of(LIST_TYPE_REDIRECT_URI)); - headers.put("Location", List.of(LOCATION_TYPE_REDIRECT_URI)); + headers.put(AsyncRedirects.REDIRECT_LIST_HEADER_KEY, List.of(LIST_TYPE_REDIRECT_URI)); + headers.put( + AsyncRedirects.REDIRECT_LOCATION_HEADER_KEY, List.of(LOCATION_TYPE_REDIRECT_URI)); when(mUrlConnection.getHeaderFields()).thenReturn(headers); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); Source result = fetch.get(); assertDefaultSourceRegistration(result); - assertEquals(2, asyncRedirect.getRedirects().size()); + assertEquals(2, asyncRedirects.getRedirects().size()); assertEquals( LIST_TYPE_REDIRECT_URI, - asyncRedirect + asyncRedirects .getRedirectsByType(AsyncRegistration.RedirectType.LIST) .get(0) + .getUri() .toString()); assertEquals( LOCATION_TYPE_REDIRECT_URI, - asyncRedirect + asyncRedirects .getRedirectsByType(AsyncRegistration.RedirectType.LOCATION) .get(0) + .getUri() .toString()); assertEquals(DEFAULT_REGISTRATION, result.getRegistrationOrigin().toString()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2625,22 +2650,25 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC Map<String, List<String>> headers = getDefaultHeaders(); // Populate only 'location' type header - headers.put("Location", List.of(LOCATION_TYPE_REDIRECT_URI)); + headers.put( + AsyncRedirects.REDIRECT_LOCATION_HEADER_KEY, List.of(LOCATION_TYPE_REDIRECT_URI)); when(mUrlConnection.getHeaderFields()).thenReturn(headers); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); Source result = fetch.get(); assertDefaultSourceRegistration(result); - assertEquals(1, asyncRedirect.getRedirects().size()); - assertEquals(LOCATION_TYPE_REDIRECT_URI, asyncRedirect.getRedirects().get(0).toString()); + assertEquals(1, asyncRedirects.getRedirects().size()); + assertEquals( + LOCATION_TYPE_REDIRECT_URI, + asyncRedirects.getRedirects().get(0).getUri().toString()); assertEquals(DEFAULT_REGISTRATION, result.getRegistrationOrigin().toString()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); } @@ -2652,25 +2680,237 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC when(mUrlConnection.getResponseCode()).thenReturn(302); Map<String, List<String>> headers = getDefaultHeaders(); - headers.put("Attribution-Reporting-Redirect", List.of(LIST_TYPE_REDIRECT_URI)); + headers.put(AsyncRedirects.REDIRECT_LIST_HEADER_KEY, List.of(LIST_TYPE_REDIRECT_URI)); + + when(mUrlConnection.getHeaderFields()).thenReturn(headers); + AsyncRedirects asyncRedirects = new AsyncRedirects(); + AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); + // Execution + Optional<Source> fetch = + mFetcher.fetchSource( + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); + // Assertion + assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); + assertTrue(fetch.isPresent()); + Source result = fetch.get(); + assertDefaultSourceRegistration(result); + assertEquals(1, asyncRedirects.getRedirects().size()); + assertEquals( + LIST_TYPE_REDIRECT_URI, asyncRedirects.getRedirects().get(0).getUri().toString()); + assertEquals(DEFAULT_REGISTRATION, result.getRegistrationOrigin().toString()); + verify(mUrlConnection, times(1)).setRequestMethod("POST"); + } + + @Test + public void testRedirects_locationRedirectHeadersToWellKnown() throws Exception { + RegistrationRequest request = buildRequest(DEFAULT_REGISTRATION); + doReturn(mUrlConnection).when(mFetcher).openUrl(any(URL.class)); + when(mUrlConnection.getResponseCode()).thenReturn(302); + when(mFlags.getMeasurementEnableRedirectToWellKnownPath()).thenReturn(true); + Map<String, List<String>> headers = getDefaultHeaders(); + + headers.put( + AsyncRedirects.REDIRECT_LOCATION_HEADER_KEY, List.of(LOCATION_TYPE_REDIRECT_URI)); + headers.put( + AsyncRedirects.HEADER_ATTRIBUTION_REPORTING_REDIRECT_CONFIG, + List.of(AsyncRedirects.REDIRECT_302_TO_WELL_KNOWN)); + + when(mUrlConnection.getHeaderFields()).thenReturn(headers); + AsyncRedirects asyncRedirects = new AsyncRedirects(); + AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); + // Execution + Optional<Source> fetch = + mFetcher.fetchSource( + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); + // Assertion + assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); + assertTrue(fetch.isPresent()); + Source result = fetch.get(); + assertDefaultSourceRegistration(result); + assertEquals(1, asyncRedirects.getRedirects().size()); + assertEquals( + LOCATION_TYPE_REDIRECT_WELLKNOWN_URI, + asyncRedirects.getRedirects().get(0).getUri().toString()); + assertEquals(DEFAULT_REGISTRATION, result.getRegistrationOrigin().toString()); + verify(mUrlConnection, times(1)).setRequestMethod("POST"); + } + + @Test + public void testRedirects_bothHeaders_locationTypeToWellKnown() throws Exception { + RegistrationRequest request = buildRequest(DEFAULT_REGISTRATION); + doReturn(mUrlConnection).when(mFetcher).openUrl(any(URL.class)); + when(mUrlConnection.getResponseCode()).thenReturn(200); + when(mFlags.getMeasurementEnableRedirectToWellKnownPath()).thenReturn(true); + Map<String, List<String>> headers = getDefaultHeaders(); + + headers.put( + AsyncRedirects.REDIRECT_LOCATION_HEADER_KEY, List.of(LOCATION_TYPE_REDIRECT_URI)); + headers.put(AsyncRedirects.REDIRECT_LIST_HEADER_KEY, List.of(LIST_TYPE_REDIRECT_URI)); + headers.put( + AsyncRedirects.HEADER_ATTRIBUTION_REPORTING_REDIRECT_CONFIG, + List.of(AsyncRedirects.REDIRECT_302_TO_WELL_KNOWN)); when(mUrlConnection.getHeaderFields()).thenReturn(headers); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); Source result = fetch.get(); assertDefaultSourceRegistration(result); - assertEquals(1, asyncRedirect.getRedirects().size()); - assertEquals(LIST_TYPE_REDIRECT_URI, asyncRedirect.getRedirects().get(0).toString()); + assertEquals(2, asyncRedirects.getRedirects().size()); + assertEquals( + LOCATION_TYPE_REDIRECT_WELLKNOWN_URI, + asyncRedirects + .getRedirectsByType(AsyncRegistration.RedirectType.LOCATION) + .get(0) + .getUri() + .toString()); + assertEquals( + LIST_TYPE_REDIRECT_URI, + asyncRedirects + .getRedirectsByType(AsyncRegistration.RedirectType.LIST) + .get(0) + .getUri() + .toString()); + assertEquals(DEFAULT_REGISTRATION, result.getRegistrationOrigin().toString()); + verify(mUrlConnection, times(1)).setRequestMethod("POST"); + } + + @Test + public void testRedirects_wellKnownHeader_noop() throws Exception { + RegistrationRequest request = buildRequest(DEFAULT_REGISTRATION); + doReturn(mUrlConnection).when(mFetcher).openUrl(any(URL.class)); + when(mUrlConnection.getResponseCode()).thenReturn(302); + when(mFlags.getMeasurementEnableRedirectToWellKnownPath()).thenReturn(true); + Map<String, List<String>> headers = getDefaultHeaders(); + + // Populate only redirect behavior header with no actual redirect + headers.put( + AsyncRedirects.HEADER_ATTRIBUTION_REPORTING_REDIRECT_CONFIG, + List.of(AsyncRedirects.REDIRECT_302_TO_WELL_KNOWN)); + + when(mUrlConnection.getHeaderFields()).thenReturn(headers); + AsyncRedirects asyncRedirects = new AsyncRedirects(); + AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); + // Execution + Optional<Source> fetch = + mFetcher.fetchSource( + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); + // Assertion + assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); + assertTrue(fetch.isPresent()); + Source result = fetch.get(); + assertDefaultSourceRegistration(result); + assertEquals(0, asyncRedirects.getRedirects().size()); + assertEquals(DEFAULT_REGISTRATION, result.getRegistrationOrigin().toString()); + verify(mUrlConnection, times(1)).setRequestMethod("POST"); + } + + @Test + public void testRedirects_wellKnownHeader_acceptsListTypeWithNoLocationType() throws Exception { + RegistrationRequest request = buildRequest(DEFAULT_REGISTRATION); + doReturn(mUrlConnection).when(mFetcher).openUrl(any(URL.class)); + when(mUrlConnection.getResponseCode()).thenReturn(200); + when(mFlags.getMeasurementEnableRedirectToWellKnownPath()).thenReturn(true); + Map<String, List<String>> headers = getDefaultHeaders(); + + headers.put(AsyncRedirects.REDIRECT_LIST_HEADER_KEY, List.of(LIST_TYPE_REDIRECT_URI)); + headers.put( + AsyncRedirects.HEADER_ATTRIBUTION_REPORTING_REDIRECT_CONFIG, + List.of(AsyncRedirects.REDIRECT_302_TO_WELL_KNOWN)); + + when(mUrlConnection.getHeaderFields()).thenReturn(headers); + AsyncRedirects asyncRedirects = new AsyncRedirects(); + AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); + // Execution + Optional<Source> fetch = + mFetcher.fetchSource( + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); + // Assertion + assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); + assertTrue(fetch.isPresent()); + Source result = fetch.get(); + assertDefaultSourceRegistration(result); + assertEquals(1, asyncRedirects.getRedirects().size()); + assertEquals( + LIST_TYPE_REDIRECT_URI, + asyncRedirects + .getRedirectsByType(AsyncRegistration.RedirectType.LIST) + .get(0) + .getUri() + .toString()); assertEquals(DEFAULT_REGISTRATION, result.getRegistrationOrigin().toString()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); } + + @Test + public void testRedirects_locationRedirectHeaders_wellKnownHeaderMisconfigured() + throws Exception { + RegistrationRequest request = buildRequest(DEFAULT_REGISTRATION); + doReturn(mUrlConnection).when(mFetcher).openUrl(any(URL.class)); + when(mUrlConnection.getResponseCode()).thenReturn(302); + Map<String, List<String>> headers = getDefaultHeaders(); + + headers.put( + AsyncRedirects.REDIRECT_LOCATION_HEADER_KEY, List.of(LOCATION_TYPE_REDIRECT_URI)); + headers.put(AsyncRedirects.HEADER_ATTRIBUTION_REPORTING_REDIRECT_CONFIG, List.of("BAD")); + + when(mUrlConnection.getHeaderFields()).thenReturn(headers); + AsyncRedirects asyncRedirects = new AsyncRedirects(); + AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); + // Execution + Optional<Source> fetch = + mFetcher.fetchSource( + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); + // Assertion + assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); + assertTrue(fetch.isPresent()); + Source result = fetch.get(); + assertDefaultSourceRegistration(result); + assertEquals(1, asyncRedirects.getRedirects().size()); + assertEquals( + LOCATION_TYPE_REDIRECT_URI, + asyncRedirects.getRedirects().get(0).getUri().toString()); + assertEquals(DEFAULT_REGISTRATION, result.getRegistrationOrigin().toString()); + verify(mUrlConnection, times(1)).setRequestMethod("POST"); + } + + @Test + public void testRedirects_locationRedirectHeaders_wellKnownHeaderNull() throws Exception { + RegistrationRequest request = buildRequest(DEFAULT_REGISTRATION); + doReturn(mUrlConnection).when(mFetcher).openUrl(any(URL.class)); + when(mUrlConnection.getResponseCode()).thenReturn(302); + Map<String, List<String>> headers = getDefaultHeaders(); + + headers.put( + AsyncRedirects.REDIRECT_LOCATION_HEADER_KEY, List.of(LOCATION_TYPE_REDIRECT_URI)); + headers.put(AsyncRedirects.HEADER_ATTRIBUTION_REPORTING_REDIRECT_CONFIG, null); + + when(mUrlConnection.getHeaderFields()).thenReturn(headers); + AsyncRedirects asyncRedirects = new AsyncRedirects(); + AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); + // Execution + Optional<Source> fetch = + mFetcher.fetchSource( + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); + // Assertion + assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); + assertTrue(fetch.isPresent()); + Source result = fetch.get(); + assertDefaultSourceRegistration(result); + assertEquals(1, asyncRedirects.getRedirects().size()); + assertEquals( + LOCATION_TYPE_REDIRECT_URI, + asyncRedirects.getRedirects().get(0).getUri().toString()); + assertEquals(DEFAULT_REGISTRATION, result.getRegistrationOrigin().toString()); + verify(mUrlConnection, times(1)).setRequestMethod("POST"); + } + // End tests for redirect types @Test @@ -2693,12 +2933,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"source_event_id\": \"987654321\",\n" + filterData + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -2736,12 +2976,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"source_event_id\": \"987654321\",\n" + filterData + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -2770,12 +3010,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"source_event_id\": \"987654321\",\n" + filterData + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2807,12 +3047,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"source_event_id\": \"987654321\",\n" + filterData + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2841,12 +3081,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"source_event_id\": \"987654321\",\n" + filterData + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2882,12 +3122,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"source_event_id\": \"987654321\",\n" + filterData + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2916,12 +3156,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"source_event_id\": \"987654321\",\n" + filterData + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2949,12 +3189,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"source_event_id\": \"987654321\",\n" + filterData + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2967,8 +3207,10 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC doReturn(mUrlConnection).when(mFetcher).openUrl(any(URL.class)); when(mUrlConnection.getResponseCode()).thenReturn(200); when(mUrlConnection.getHeaderFields()) - .thenReturn(Map.of( - "Attribution-Reporting-Redirect", List.of(LIST_TYPE_REDIRECT_URI))) + .thenReturn( + Map.of( + AsyncRedirects.REDIRECT_LIST_HEADER_KEY, + List.of(LIST_TYPE_REDIRECT_URI))) .thenReturn( Map.of( "Attribution-Reporting-Register-Source", @@ -2987,22 +3229,23 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEFAULT_EVENT_ID + "\"\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals( AsyncFetchStatus.EntityStatus.HEADER_MISSING, asyncFetchStatus.getEntityStatus()); assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); - assertEquals(1, asyncRedirect.getRedirects().size()); + assertEquals(1, asyncRedirects.getRedirects().size()); assertEquals( LIST_TYPE_REDIRECT_URI, - asyncRedirect + asyncRedirects .getRedirectsByType(AsyncRegistration.RedirectType.LIST) .get(0) + .getUri() .toString()); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -3027,12 +3270,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"aggregation_keys\": {\"campaignCounts\" :" + " \"0x159\", \"geoValue\" : \"0x5\"}\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -3065,12 +3308,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"aggregation_keys\": {\"campaignCounts\" :" + " \"0x159G\", \"geoValue\" : \"0x5\"}\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -3098,12 +3341,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"aggregation_keys\": {\"campaignCounts\" :" + " \"0x159G\", \"geoValue\" : \"0x5\"}\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -3140,12 +3383,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"source_event_id\":" + " \"987654321\",\"aggregation_keys\": " + tooManyKeys))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals( AsyncFetchStatus.EntityStatus.PARSING_ERROR, asyncFetchStatus.getEntityStatus()); @@ -3176,12 +3419,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"source_event_id\":" + " \"987654321\",\"aggregation_keys\": " + tooManyKeys))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection).setRequestMethod("POST"); @@ -3207,12 +3450,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"aggregation_keys\": [\"campaignCounts\"," + " \"0x159\", \"geoValue\", \"0x5\"]\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -3240,12 +3483,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\": \"0x159\"," + "\"geoValue\": \"0x5\"}\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -3272,12 +3515,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"aggregation_keys\": {\"campaignCounts\" :" + " \"0159\", \"geoValue\" : \"0x5\"}\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -3305,12 +3548,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + LONG_AGGREGATE_KEY_PIECE + "\"}\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -3341,17 +3584,17 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEBUG_KEY + "\"\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution AsyncRegistration asyncRegistration = webSourceRegistrationRequest(request, true); Optional<Source> fetch = - mFetcher.fetchSource(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchSource(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); Source result = fetch.get(); - assertEquals(0, asyncRedirect.getRedirects().size()); + assertEquals(0, asyncRedirects.getRedirects().size()); assertEquals(ENROLLMENT_ID, result.getEnrollmentId()); assertEquals(Uri.parse(DEFAULT_DESTINATION), result.getAppDestinations().get(0)); assertEquals(EVENT_ID_1, result.getEventId()); @@ -3392,14 +3635,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"expiry\": \"432000\"," + "\"source_event_id\": \"987654321\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -3443,14 +3686,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"expiry\": \"432000\"," + "\"source_event_id\": \"987654321\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -3489,14 +3732,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"expiry\": \"432000\"," + "\"source_event_id\": \"987654321\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -3536,14 +3779,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"expiry\": \"432000\"," + "\"source_event_id\": \"987654321\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -3573,14 +3816,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"," + "\"expiry\":\"2000\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -3612,14 +3855,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"," + "\"expiry\":\"2592001\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -3659,14 +3902,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + TimeUnit.DAYS.toSeconds(1) / 2L) + "\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true, Source.SourceType.EVENT), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -3704,14 +3947,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC - 1L) + "\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true, Source.SourceType.EVENT), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -3746,14 +3989,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + String.valueOf(TimeUnit.HOURS.toSeconds(25)) + "\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true, Source.SourceType.NAVIGATION), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -3786,14 +4029,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"," + "\"expiry\":\"172800\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -3828,14 +4071,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"event_report_window\":\"86400\"," + "\"aggregatable_report_window\":\"86400\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -3872,14 +4115,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"event_report_window\":\"2000\"," + "\"aggregatable_report_window\":\"1728\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -3916,14 +4159,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"event_report_window\":\"2000\"," + "\"aggregatable_report_window\":\"1728\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -3959,14 +4202,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"event_report_window\":\"172801\"," + "\"aggregatable_report_window\":\"172801\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -4002,14 +4245,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"event_report_window\":\"2592001\"," + "\"aggregatable_report_window\":\"2592001\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -4048,19 +4291,19 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEBUG_KEY + "\"\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); Source result = fetch.get(); - assertEquals(0, asyncRedirect.getRedirects().size()); + assertEquals(0, asyncRedirects.getRedirects().size()); assertEquals(ENROLLMENT_ID, result.getEnrollmentId()); assertEquals(Uri.parse(DEFAULT_DESTINATION), result.getAppDestinations().get(0)); assertEquals(EVENT_ID_1, result.getEventId()); @@ -4100,18 +4343,18 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEBUG_KEY + "\"\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, false), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Source result = fetch.get(); - assertEquals(0, asyncRedirect.getRedirects().size()); + assertEquals(0, asyncRedirects.getRedirects().size()); assertEquals(ENROLLMENT_ID, result.getEnrollmentId()); assertEquals(Uri.parse(DEFAULT_DESTINATION), result.getAppDestinations().get(0)); assertEquals(EVENT_ID_1, result.getEventId()); @@ -4154,14 +4397,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEBUG_KEY + "\"\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals( AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); @@ -4201,19 +4444,19 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + aggregateSource + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); Source result = fetch.get(); - assertEquals(0, asyncRedirect.getRedirects().size()); + assertEquals(0, asyncRedirects.getRedirects().size()); assertEquals(ENROLLMENT_ID, result.getEnrollmentId()); assertEquals(OS_DESTINATION, result.getAppDestinations().get(0)); assertEquals(filterData, result.getFilterDataString()); @@ -4257,24 +4500,24 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"aggregation_keys\": " + aggregateSource + "}"), - "Attribution-Reporting-Redirect", + AsyncRedirects.REDIRECT_LIST_HEADER_KEY, List.of(LIST_TYPE_REDIRECT_URI), - "Location", + AsyncRedirects.REDIRECT_LOCATION_HEADER_KEY, List.of(LOCATION_TYPE_REDIRECT_URI))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); Source result = fetch.get(); - assertEquals(0, asyncRedirect.getRedirects().size()); + assertEquals(0, asyncRedirects.getRedirects().size()); assertEquals(ENROLLMENT_ID, result.getEnrollmentId()); assertEquals(Uri.parse(DEFAULT_DESTINATION), result.getAppDestinations().get(0)); assertEquals(filterData, result.getFilterDataString()); @@ -4318,14 +4561,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"filter_data\": " + filterData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -4362,19 +4605,19 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEBUG_JOIN_KEY + "\"\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); Source result = fetch.get(); - assertEquals(0, asyncRedirect.getRedirects().size()); + assertEquals(0, asyncRedirects.getRedirects().size()); assertEquals(ENROLLMENT_ID, result.getEnrollmentId()); assertEquals(Uri.parse(DEFAULT_DESTINATION), result.getAppDestinations().get(0)); assertEquals(EVENT_ID_1, result.getEventId()); @@ -4419,19 +4662,19 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEBUG_JOIN_KEY + "\"\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); Source result = fetch.get(); - assertEquals(0, asyncRedirect.getRedirects().size()); + assertEquals(0, asyncRedirects.getRedirects().size()); assertEquals(ENROLLMENT_ID, result.getEnrollmentId()); assertEquals(Uri.parse(DEFAULT_DESTINATION), result.getAppDestinations().get(0)); assertEquals(EVENT_ID_1, result.getEventId()); @@ -4483,14 +4726,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"filter_data\": " + filterData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -4529,20 +4772,20 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + WEB_DESTINATION + "\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); Source result = fetch.get(); - assertEquals(0, asyncRedirect.getRedirects().size()); + assertEquals(0, asyncRedirects.getRedirects().size()); assertEquals(ENROLLMENT_ID, result.getEnrollmentId()); assertEquals(OS_DESTINATION, result.getAppDestinations().get(0)); assertNull(result.getFilterDataString()); @@ -4585,20 +4828,20 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); Source result = fetch.get(); - assertEquals(0, asyncRedirect.getRedirects().size()); + assertEquals(0, asyncRedirects.getRedirects().size()); assertEquals(ENROLLMENT_ID, result.getEnrollmentId()); assertEquals(OS_DESTINATION, result.getAppDestinations().get(0)); assertNull(result.getFilterDataString()); @@ -4638,19 +4881,19 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); Source result = fetch.get(); - assertEquals(0, asyncRedirect.getRedirects().size()); + assertEquals(0, asyncRedirects.getRedirects().size()); assertEquals(ENROLLMENT_ID, result.getEnrollmentId()); assertEquals(OS_DESTINATION, result.getAppDestinations().get(0)); assertNull(result.getFilterDataString()); @@ -4683,14 +4926,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEFAULT_EVENT_ID + "\"\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals( AsyncFetchStatus.EntityStatus.PARSING_ERROR, asyncFetchStatus.getEntityStatus()); @@ -4723,19 +4966,19 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + EVENT_ID_1 + "\"\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); Source result = fetch.get(); - assertEquals(0, asyncRedirect.getRedirects().size()); + assertEquals(0, asyncRedirects.getRedirects().size()); assertEquals(ENROLLMENT_ID, result.getEnrollmentId()); assertEquals(Uri.parse(DEFAULT_DESTINATION), result.getAppDestinations().get(0)); assertNull(result.getFilterDataString()); @@ -4776,14 +5019,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + EVENT_ID_1 + "\"\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -4813,13 +5056,13 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); asyncFetchStatus.setRegistrationDelay(0L); AsyncRegistration asyncRegistration = appSourceRegistrationRequest(request); // Execution Optional<Source> fetch = - mFetcher.fetchSource(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchSource(asyncRegistration, asyncFetchStatus, asyncRedirects); assertTrue(fetch.isPresent()); FetcherUtil.emitHeaderMetrics(mFlags, mLogger, asyncRegistration, asyncFetchStatus); @@ -4873,13 +5116,13 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEBUG_JOIN_KEY + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); @@ -4929,13 +5172,13 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEBUG_JOIN_KEY + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); @@ -4977,7 +5220,7 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC AsyncRegistration asyncRegistration = appSourceRegistrationRequest(request); // Execution Optional<Source> fetch = - mFetcher.fetchSource(asyncRegistration, asyncFetchStatus, new AsyncRedirect()); + mFetcher.fetchSource(asyncRegistration, asyncFetchStatus, new AsyncRedirects()); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -5013,7 +5256,7 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC AsyncRegistration asyncRegistration = appSourceRegistrationRequest(request); // Execution Optional<Source> fetch = - mFetcher.fetchSource(asyncRegistration, asyncFetchStatus, new AsyncRedirect()); + mFetcher.fetchSource(asyncRegistration, asyncFetchStatus, new AsyncRedirects()); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -5053,7 +5296,7 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution @@ -5061,7 +5304,7 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC mFetcher.fetchSource( appSourceRegistrationRequestWithAdId(request), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); @@ -5105,19 +5348,19 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEBUG_AD_ID_VALUE + "\"\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); Source result = fetch.get(); - assertEquals(0, asyncRedirect.getRedirects().size()); + assertEquals(0, asyncRedirects.getRedirects().size()); assertEquals(ENROLLMENT_ID, result.getEnrollmentId()); assertEquals(Uri.parse(DEFAULT_DESTINATION), result.getAppDestinations().get(0)); assertEquals(EVENT_ID_1, result.getEventId()); @@ -5162,19 +5405,19 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEBUG_AD_ID_VALUE + "\"\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); Source result = fetch.get(); - assertEquals(0, asyncRedirect.getRedirects().size()); + assertEquals(0, asyncRedirects.getRedirects().size()); assertEquals(ENROLLMENT_ID, result.getEnrollmentId()); assertEquals(Uri.parse(DEFAULT_DESTINATION), result.getAppDestinations().get(0)); assertEquals(EVENT_ID_1, result.getEventId()); @@ -5218,19 +5461,19 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEBUG_AD_ID_VALUE + "\"\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); Source result = fetch.get(); - assertEquals(0, asyncRedirect.getRedirects().size()); + assertEquals(0, asyncRedirects.getRedirects().size()); assertEquals(ENROLLMENT_ID, result.getEnrollmentId()); assertEquals(Uri.parse(DEFAULT_DESTINATION), result.getAppDestinations().get(0)); assertEquals(EVENT_ID_1, result.getEventId()); @@ -5246,7 +5489,7 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC } @Test - public void fetchSource_setsFakeEnrollmentId_whenDisableEnrollmentFlagIsTrue() + public void fetchSource_setsSiteEnrollmentId_whenDisableEnrollmentFlagIsTrue() throws Exception { String uri = "https://test1.example.com:8081"; RegistrationRequest request = buildRequest(uri); @@ -5270,13 +5513,87 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC AsyncRegistration asyncRegistration = appSourceRegistrationRequest(request); // Execution Optional<Source> fetch = - mFetcher.fetchSource(asyncRegistration, asyncFetchStatus, new AsyncRedirect()); + mFetcher.fetchSource(asyncRegistration, asyncFetchStatus, new AsyncRedirects()); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); Source result = fetch.get(); assertEquals("https://test1.example.com:8081", result.getRegistrationOrigin().toString()); - assertEquals(Enrollment.FAKE_ENROLLMENT, result.getEnrollmentId()); + assertEquals("https://example.com", result.getEnrollmentId()); + assertEquals(DEFAULT_DESTINATION, result.getAppDestinations().get(0).toString()); + assertEquals(DEFAULT_EVENT_ID, result.getEventId()); + verify(mUrlConnection).setRequestMethod("POST"); + } + + @Test + public void fetchSource_setsSiteEnrollmentId_whenDisableEnrollmentFlagIsTrueForIP() + throws Exception { + String uri = "https://127.0.0.1:8081"; + RegistrationRequest request = buildRequest(uri); + doReturn(mUrlConnection).when(mFetcher).openUrl(new URL(uri)); + doReturn(true).when(mFlags).isDisableMeasurementEnrollmentCheck(); + when(mUrlConnection.getResponseCode()).thenReturn(200); + when(mUrlConnection.getHeaderFields()) + .thenReturn( + Map.of( + "Attribution-Reporting-Register-Source", + List.of( + "{\n" + + "\"destination\": \"" + + DEFAULT_DESTINATION + + "\",\n" + + "\"source_event_id\": \"" + + DEFAULT_EVENT_ID + + "\"\n" + + "}\n"))); + AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); + AsyncRegistration asyncRegistration = appSourceRegistrationRequest(request); + // Execution + Optional<Source> fetch = + mFetcher.fetchSource(asyncRegistration, asyncFetchStatus, new AsyncRedirects()); + // Assertion + assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); + assertTrue(fetch.isPresent()); + Source result = fetch.get(); + assertEquals("https://127.0.0.1:8081", result.getRegistrationOrigin().toString()); + assertEquals("https://127.0.0.1", result.getEnrollmentId()); + assertEquals(DEFAULT_DESTINATION, result.getAppDestinations().get(0).toString()); + assertEquals(DEFAULT_EVENT_ID, result.getEventId()); + verify(mUrlConnection).setRequestMethod("POST"); + } + + @Test + public void fetchSource_setsSiteEnrollmentId_whenDisableEnrollmentFlagIsTrueForLocalhost() + throws Exception { + String uri = "https://localhost:8081"; + RegistrationRequest request = buildRequest(uri); + doReturn(mUrlConnection).when(mFetcher).openUrl(new URL(uri)); + doReturn(true).when(mFlags).isDisableMeasurementEnrollmentCheck(); + when(mUrlConnection.getResponseCode()).thenReturn(200); + when(mUrlConnection.getHeaderFields()) + .thenReturn( + Map.of( + "Attribution-Reporting-Register-Source", + List.of( + "{\n" + + "\"destination\": \"" + + DEFAULT_DESTINATION + + "\",\n" + + "\"source_event_id\": \"" + + DEFAULT_EVENT_ID + + "\"\n" + + "}\n"))); + AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); + AsyncRegistration asyncRegistration = appSourceRegistrationRequest(request); + // Execution + Optional<Source> fetch = + mFetcher.fetchSource(asyncRegistration, asyncFetchStatus, new AsyncRedirects()); + // Assertion + assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); + assertTrue(fetch.isPresent()); + Source result = fetch.get(); + assertEquals("https://localhost:8081", result.getRegistrationOrigin().toString()); + assertEquals("https://localhost", result.getEnrollmentId()); assertEquals(DEFAULT_DESTINATION, result.getAppDestinations().get(0).toString()); assertEquals(DEFAULT_EVENT_ID, result.getEventId()); verify(mUrlConnection).setRequestMethod("POST"); @@ -5307,12 +5624,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -5345,12 +5662,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -5384,12 +5701,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -5423,12 +5740,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -5463,12 +5780,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -5502,12 +5819,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"\n" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -5537,12 +5854,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"post_install_exclusivity_window\": " + "\"987654\"" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -5574,12 +5891,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"trigger_data_matching\": \"INVALID\"," + "\"post_install_exclusivity_window\": \"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isEmpty()); } @@ -5623,12 +5940,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -5693,12 +6010,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -5775,12 +6092,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -5837,12 +6154,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals( AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); @@ -5887,12 +6204,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -5954,14 +6271,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -6040,12 +6357,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals( AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); @@ -6092,12 +6409,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isEmpty()); } @@ -6142,12 +6459,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isEmpty()); } @@ -6193,12 +6510,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -6248,12 +6565,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -6321,12 +6638,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -6376,12 +6693,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -6447,12 +6764,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isEmpty()); } @@ -6499,14 +6816,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertTrue(fetch.isEmpty()); } @@ -6551,13 +6868,13 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isEmpty()); } @@ -6603,7 +6920,7 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution @@ -6611,7 +6928,7 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertTrue(fetch.isEmpty()); } @@ -6667,13 +6984,13 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isEmpty()); } @@ -6731,7 +7048,7 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution @@ -6739,7 +7056,7 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertTrue(fetch.isEmpty()); } @@ -6795,12 +7112,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -6878,14 +7195,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}\n"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -6951,12 +7268,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -7006,14 +7323,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( webSourceRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -7081,12 +7398,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -7154,12 +7471,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -7226,12 +7543,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -7261,12 +7578,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"source_event_id\": \"987654321\"," + " \"max_event_level_reports\": 21" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -7295,12 +7612,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"source_event_id\": \"987654321\"," + " \"max_event_level_reports\": -3" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -7330,12 +7647,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"source_event_id\": \"987654321\"," + " \"max_event_level_reports\": \"3\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -7372,12 +7689,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"event_report_windows\":" + eventReportWindows + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -7413,12 +7730,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"event_report_windows\":" + eventReportWindows + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -7454,12 +7771,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"event_report_windows\":" + eventReportWindows + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -7506,12 +7823,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"event_report_windows\":" + eventReportWindows + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -7554,12 +7871,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"event_report_windows\":" + eventReportWindows + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -7591,12 +7908,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"event_report_windows\":" + eventReportWindows + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -7628,12 +7945,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"event_report_windows\":" + eventReportWindows + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -7665,12 +7982,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"event_report_windows\":" + eventReportWindows + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -7705,12 +8022,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "," + " \"max_event_level_reports\": 5" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -7744,12 +8061,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "," + " \"max_event_level_reports\": 5" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -7783,12 +8100,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "," + " \"max_event_level_reports\": 5" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -7822,12 +8139,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "," + " \"max_event_level_reports\": 5" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -7874,12 +8191,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); @@ -7927,12 +8244,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + " \"post_install_exclusivity_window\": " + "\"987654\"" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); @@ -7974,12 +8291,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "," + " \"max_event_level_reports\": 5" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -8023,12 +8340,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "," + " \"max_event_level_reports\": 5" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -8068,12 +8385,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "," + " \"max_event_level_reports\": 5" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -8113,12 +8430,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "," + " \"max_event_level_reports\": 5" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -8144,14 +8461,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEFAULT_DESTINATION + "\"," + "\"source_event_id\":\"35\"}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( appSourceRegistrationRequestWithPostBody(request, POST_BODY), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(POST_BODY, outputStream.toString()); assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); @@ -8176,14 +8493,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEFAULT_DESTINATION + "\"," + "\"source_event_id\":\"35\"}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( appSourceRegistrationRequestWithPostBody(request, null), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -8209,7 +8526,7 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEFAULT_DESTINATION + "\"," + "\"source_event_id\":\"35\"}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution String emptyPostBody = ""; @@ -8217,7 +8534,7 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC mFetcher.fetchSource( appSourceRegistrationRequestWithPostBody(request, emptyPostBody), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(emptyPostBody, outputStream.toString()); assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); @@ -8242,14 +8559,14 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + DEFAULT_DESTINATION + "\"," + "\"source_event_id\":\"35\"}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( appSourceRegistrationRequestWithPostBody(request, POST_BODY), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -8276,12 +8593,12 @@ public final class AsyncSourceFetcherTest extends AdServicesExtendedMockitoTestC + "\"," + "\"source_event_id\":\"35\"," + "\"drop_source_if_installed\":true}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Source> fetch = mFetcher.fetchSource( - appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appSourceRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/registration/AsyncTriggerFetcherTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/registration/AsyncTriggerFetcherTest.java index f22a19eb9..41605bc53 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/registration/AsyncTriggerFetcherTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/registration/AsyncTriggerFetcherTest.java @@ -145,6 +145,15 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest WebUtil.validUrl("https://subdomain.bar.test"); private static final String LOCATION_TYPE_REDIRECT_URI = WebUtil.validUrl("https://example.test"); + private static final String LOCATION_TYPE_REDIRECT_WELLKNOWN_URI = + WebUtil.validUrl( + LOCATION_TYPE_REDIRECT_URI + + "/" + + AsyncRedirects.WELL_KNOWN_PATH_SEGMENT + + "?" + + AsyncRedirects.WELL_KNOWN_QUERY_PARAM + + "=" + + Uri.encode(LOCATION_TYPE_REDIRECT_URI)); private static final Uri REGISTRATION_URI_1 = WebUtil.validUri("https://subdomain.foo.test"); private static final WebTriggerParams TRIGGER_REGISTRATION_1 = new WebTriggerParams.Builder(REGISTRATION_URI_1).setDebugKeyAllowed(true).build(); @@ -248,13 +257,13 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest List.of("{\"event_trigger_data\":" + EVENT_TRIGGERS_1 + "}"))); doReturn(5000L).when(mFlags).getMaxResponseBasedRegistrationPayloadSizeBytes(); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); asyncFetchStatus.setRegistrationDelay(0L); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -325,13 +334,13 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "}"))); doReturn(5000L).when(mFlags).getMaxResponseBasedRegistrationPayloadSizeBytes(); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); asyncFetchStatus.setRegistrationDelay(0L); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -385,12 +394,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + aggregateDedupKeys + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals( AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); @@ -439,12 +448,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + aggregateDedupKeys + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -488,12 +497,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + aggregateDedupKeys + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals( AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); @@ -541,12 +550,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + aggregateDedupKeys + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -590,12 +599,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + aggregateDedupKeys + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals( AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); @@ -643,12 +652,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + aggregateDedupKeys + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -692,12 +701,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + aggregateDedupKeys + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals( AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); @@ -745,12 +754,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + aggregateDedupKeys + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -792,12 +801,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + aggregateDedupKeys + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -816,34 +825,37 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map<String, List<String>> headers = getDefaultHeaders(); // Populate both 'list' and 'location' type headers - headers.put("Attribution-Reporting-Redirect", List.of(LIST_TYPE_REDIRECT_URI)); - headers.put("Location", List.of(LOCATION_TYPE_REDIRECT_URI)); + headers.put(AsyncRedirects.REDIRECT_LIST_HEADER_KEY, List.of(LIST_TYPE_REDIRECT_URI)); + headers.put( + AsyncRedirects.REDIRECT_LOCATION_HEADER_KEY, List.of(LOCATION_TYPE_REDIRECT_URI)); when(mUrlConnection.getHeaderFields()).thenReturn(headers); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); Trigger result = fetch.get(); assertTriggerRegistration(asyncRegistration, result); - assertEquals(2, asyncRedirect.getRedirects().size()); + assertEquals(2, asyncRedirects.getRedirects().size()); assertEquals( LIST_TYPE_REDIRECT_URI, - asyncRedirect + asyncRedirects .getRedirectsByType(AsyncRegistration.RedirectType.LIST) .get(0) + .getUri() .toString()); assertEquals( LOCATION_TYPE_REDIRECT_URI, - asyncRedirect + asyncRedirects .getRedirectsByType(AsyncRegistration.RedirectType.LOCATION) .get(0) + .getUri() .toString()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -857,68 +869,295 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map<String, List<String>> headers = getDefaultHeaders(); // Populate only 'location' type header - headers.put("Location", List.of(LOCATION_TYPE_REDIRECT_URI)); + headers.put( + AsyncRedirects.REDIRECT_LOCATION_HEADER_KEY, List.of(LOCATION_TYPE_REDIRECT_URI)); when(mUrlConnection.getHeaderFields()).thenReturn(headers); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); Trigger result = fetch.get(); assertTriggerRegistration(asyncRegistration, result); - assertEquals(1, asyncRedirect.getRedirects().size()); - assertEquals(LOCATION_TYPE_REDIRECT_URI, asyncRedirect.getRedirects().get(0).toString()); + assertEquals(1, asyncRedirects.getRedirects().size()); + assertEquals( + LOCATION_TYPE_REDIRECT_URI, + asyncRedirects.getRedirects().get(0).getUri().toString()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); } @Test - public void testRedirectType_locationRedirectType_ignoresListType() throws Exception { + public void testRedirectType_locationRedirectType_acceptsListType() throws Exception { RegistrationRequest request = buildRequest(TRIGGER_URI); doReturn(mUrlConnection).when(mFetcher).openUrl(new URL(TRIGGER_URI)); when(mUrlConnection.getResponseCode()).thenReturn(302); Map<String, List<String>> headers = getDefaultHeaders(); // Populate both 'list' and 'location' type headers - headers.put("Attribution-Reporting-Redirect", List.of(LIST_TYPE_REDIRECT_URI)); - headers.put("Location", List.of(LOCATION_TYPE_REDIRECT_URI)); + headers.put(AsyncRedirects.REDIRECT_LIST_HEADER_KEY, List.of(LIST_TYPE_REDIRECT_URI)); + headers.put( + AsyncRedirects.REDIRECT_LOCATION_HEADER_KEY, List.of(LOCATION_TYPE_REDIRECT_URI)); when(mUrlConnection.getHeaderFields()).thenReturn(headers); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); Trigger result = fetch.get(); assertTriggerRegistration(asyncRegistration, result); - assertEquals(2, asyncRedirect.getRedirects().size()); + assertEquals(2, asyncRedirects.getRedirects().size()); assertEquals( LOCATION_TYPE_REDIRECT_URI, - asyncRedirect + asyncRedirects + .getRedirectsByType(AsyncRegistration.RedirectType.LOCATION) + .get(0) + .getUri() + .toString()); + assertEquals( + LIST_TYPE_REDIRECT_URI, + asyncRedirects + .getRedirectsByType(AsyncRegistration.RedirectType.LIST) + .get(0) + .getUri() + .toString()); + + verify(mUrlConnection, times(1)).setRequestMethod("POST"); + } + + @Test + public void testRedirectType_locationRedirectHeaderType_wellKnownPath() throws Exception { + RegistrationRequest request = buildRequest(TRIGGER_URI); + doReturn(mUrlConnection).when(mFetcher).openUrl(new URL(TRIGGER_URI)); + when(mUrlConnection.getResponseCode()).thenReturn(302); + when(mFlags.getMeasurementEnableRedirectToWellKnownPath()).thenReturn(true); + Map<String, List<String>> headers = getDefaultHeaders(); + + // Populate only 'location' type header + headers.put( + AsyncRedirects.REDIRECT_LOCATION_HEADER_KEY, List.of(LOCATION_TYPE_REDIRECT_URI)); + headers.put( + AsyncRedirects.HEADER_ATTRIBUTION_REPORTING_REDIRECT_CONFIG, + List.of(AsyncRedirects.REDIRECT_302_TO_WELL_KNOWN)); + + when(mUrlConnection.getHeaderFields()).thenReturn(headers); + AsyncRedirects asyncRedirects = new AsyncRedirects(); + AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); + AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); + // Execution + Optional<Trigger> fetch = + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); + // Assertion + assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); + assertTrue(fetch.isPresent()); + Trigger result = fetch.get(); + assertTriggerRegistration(asyncRegistration, result); + + assertEquals(1, asyncRedirects.getRedirects().size()); + assertEquals( + LOCATION_TYPE_REDIRECT_WELLKNOWN_URI, + asyncRedirects.getRedirects().get(0).getUri().toString()); + + verify(mUrlConnection, times(1)).setRequestMethod("POST"); + } + + @Test + public void testRedirectType_locationRedirectType_wellKnownPath_acceptsList() throws Exception { + RegistrationRequest request = buildRequest(TRIGGER_URI); + doReturn(mUrlConnection).when(mFetcher).openUrl(new URL(TRIGGER_URI)); + when(mUrlConnection.getResponseCode()).thenReturn(302); + when(mFlags.getMeasurementEnableRedirectToWellKnownPath()).thenReturn(true); + Map<String, List<String>> headers = getDefaultHeaders(); + + headers.put( + AsyncRedirects.REDIRECT_LOCATION_HEADER_KEY, List.of(LOCATION_TYPE_REDIRECT_URI)); + headers.put(AsyncRedirects.REDIRECT_LIST_HEADER_KEY, List.of(LIST_TYPE_REDIRECT_URI)); + headers.put( + AsyncRedirects.HEADER_ATTRIBUTION_REPORTING_REDIRECT_CONFIG, + List.of(AsyncRedirects.REDIRECT_302_TO_WELL_KNOWN)); + + when(mUrlConnection.getHeaderFields()).thenReturn(headers); + AsyncRedirects asyncRedirects = new AsyncRedirects(); + AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); + AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); + // Execution + Optional<Trigger> fetch = + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); + // Assertion + assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); + assertTrue(fetch.isPresent()); + Trigger result = fetch.get(); + assertTriggerRegistration(asyncRegistration, result); + + assertEquals(2, asyncRedirects.getRedirects().size()); + assertEquals( + LOCATION_TYPE_REDIRECT_WELLKNOWN_URI, + asyncRedirects .getRedirectsByType(AsyncRegistration.RedirectType.LOCATION) .get(0) + .getUri() .toString()); assertEquals( LIST_TYPE_REDIRECT_URI, - asyncRedirect + asyncRedirects .getRedirectsByType(AsyncRegistration.RedirectType.LIST) .get(0) + .getUri() .toString()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); } + @Test + public void testWellKnownPathHeader_noop() throws Exception { + RegistrationRequest request = buildRequest(TRIGGER_URI); + doReturn(mUrlConnection).when(mFetcher).openUrl(new URL(TRIGGER_URI)); + when(mUrlConnection.getResponseCode()).thenReturn(200); + when(mFlags.getMeasurementEnableRedirectToWellKnownPath()).thenReturn(true); + Map<String, List<String>> headers = getDefaultHeaders(); + + // Populate behavior config header, but no redirects. + headers.put( + AsyncRedirects.HEADER_ATTRIBUTION_REPORTING_REDIRECT_CONFIG, + List.of(AsyncRedirects.REDIRECT_302_TO_WELL_KNOWN)); + + when(mUrlConnection.getHeaderFields()).thenReturn(headers); + AsyncRedirects asyncRedirects = new AsyncRedirects(); + AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); + AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); + // Execution + Optional<Trigger> fetch = + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); + // Assertion + assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); + assertTrue(fetch.isPresent()); + Trigger result = fetch.get(); + assertTriggerRegistration(asyncRegistration, result); + + assertEquals(0, asyncRedirects.getRedirects().size()); + + verify(mUrlConnection, times(1)).setRequestMethod("POST"); + } + + @Test + public void testWellKnownPathHeader_acceptsListTypeWithNoLocationType() throws Exception { + RegistrationRequest request = buildRequest(TRIGGER_URI); + doReturn(mUrlConnection).when(mFetcher).openUrl(new URL(TRIGGER_URI)); + when(mUrlConnection.getResponseCode()).thenReturn(200); + when(mFlags.getMeasurementEnableRedirectToWellKnownPath()).thenReturn(true); + Map<String, List<String>> headers = getDefaultHeaders(); + + // Populate behavior config header, but no Location type redirect. + headers.put( + AsyncRedirects.HEADER_ATTRIBUTION_REPORTING_REDIRECT_CONFIG, + List.of(AsyncRedirects.REDIRECT_302_TO_WELL_KNOWN)); + headers.put(AsyncRedirects.REDIRECT_LIST_HEADER_KEY, List.of(LIST_TYPE_REDIRECT_URI)); + + when(mUrlConnection.getHeaderFields()).thenReturn(headers); + AsyncRedirects asyncRedirects = new AsyncRedirects(); + AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); + AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); + // Execution + Optional<Trigger> fetch = + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); + // Assertion + assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); + assertTrue(fetch.isPresent()); + Trigger result = fetch.get(); + assertTriggerRegistration(asyncRegistration, result); + + assertEquals(1, asyncRedirects.getRedirects().size()); + + assertEquals( + LIST_TYPE_REDIRECT_URI, + asyncRedirects + .getRedirectsByType(AsyncRegistration.RedirectType.LIST) + .get(0) + .getUri() + .toString()); + + verify(mUrlConnection, times(1)).setRequestMethod("POST"); + } + + @Test + public void testRedirectType_locationRedirectHeaderType_wellKnownHeaderMisconfigured() + throws Exception { + RegistrationRequest request = buildRequest(TRIGGER_URI); + doReturn(mUrlConnection).when(mFetcher).openUrl(new URL(TRIGGER_URI)); + when(mUrlConnection.getResponseCode()).thenReturn(302); + when(mFlags.getMeasurementEnableRedirectToWellKnownPath()).thenReturn(true); + Map<String, List<String>> headers = getDefaultHeaders(); + + headers.put( + AsyncRedirects.REDIRECT_LOCATION_HEADER_KEY, List.of(LOCATION_TYPE_REDIRECT_URI)); + headers.put(AsyncRedirects.HEADER_ATTRIBUTION_REPORTING_REDIRECT_CONFIG, List.of("BAD")); + + when(mUrlConnection.getHeaderFields()).thenReturn(headers); + AsyncRedirects asyncRedirects = new AsyncRedirects(); + AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); + AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); + // Execution + Optional<Trigger> fetch = + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); + // Assertion + assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); + assertTrue(fetch.isPresent()); + Trigger result = fetch.get(); + assertTriggerRegistration(asyncRegistration, result); + + assertEquals(1, asyncRedirects.getRedirects().size()); + assertEquals( + LOCATION_TYPE_REDIRECT_URI, + asyncRedirects.getRedirects().get(0).getUri().toString()); + + verify(mUrlConnection, times(1)).setRequestMethod("POST"); + } + + @Test + public void testRedirectType_locationRedirectHeaderType_wellKnownHeaderNull() throws Exception { + RegistrationRequest request = buildRequest(TRIGGER_URI); + doReturn(mUrlConnection).when(mFetcher).openUrl(new URL(TRIGGER_URI)); + when(mUrlConnection.getResponseCode()).thenReturn(302); + when(mFlags.getMeasurementEnableRedirectToWellKnownPath()).thenReturn(true); + Map<String, List<String>> headers = getDefaultHeaders(); + + headers.put( + AsyncRedirects.REDIRECT_LOCATION_HEADER_KEY, List.of(LOCATION_TYPE_REDIRECT_URI)); + headers.put(AsyncRedirects.HEADER_ATTRIBUTION_REPORTING_REDIRECT_CONFIG, null); + + when(mUrlConnection.getHeaderFields()).thenReturn(headers); + AsyncRedirects asyncRedirects = new AsyncRedirects(); + AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); + AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); + // Execution + Optional<Trigger> fetch = + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); + // Assertion + assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); + assertTrue(fetch.isPresent()); + Trigger result = fetch.get(); + assertTriggerRegistration(asyncRegistration, result); + + assertEquals(1, asyncRedirects.getRedirects().size()); + assertEquals( + LOCATION_TYPE_REDIRECT_URI, + asyncRedirects.getRedirects().get(0).getUri().toString()); + + verify(mUrlConnection, times(1)).setRequestMethod("POST"); + } + // End tests for redirect types @Test @@ -935,12 +1174,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -966,12 +1205,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggers + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -991,12 +1230,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"debug_key\": \"" + DEBUG_KEY + "\"}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -1021,12 +1260,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -1055,12 +1294,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggers + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -1080,15 +1319,15 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion - assertEquals(AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, - asyncFetchStatus.getEntityStatus()); + assertEquals( + AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -1107,15 +1346,15 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion - assertEquals(AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, - asyncFetchStatus.getEntityStatus()); + assertEquals( + AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -1135,12 +1374,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -1165,15 +1404,15 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion - assertEquals(AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, - asyncFetchStatus.getEntityStatus()); + assertEquals( + AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -1194,12 +1433,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -1223,15 +1462,15 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion - assertEquals(AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, - asyncFetchStatus.getEntityStatus()); + assertEquals( + AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -1251,12 +1490,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -1280,12 +1519,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -1309,12 +1548,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -1338,15 +1577,15 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion - assertEquals(AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, - asyncFetchStatus.getEntityStatus()); + assertEquals( + AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -1366,15 +1605,15 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion - assertEquals(AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, - asyncFetchStatus.getEntityStatus()); + assertEquals( + AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -1394,15 +1633,15 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion - assertEquals(AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, - asyncFetchStatus.getEntityStatus()); + assertEquals( + AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -1422,12 +1661,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -1452,12 +1691,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -1482,15 +1721,15 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion - assertEquals(AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, - asyncFetchStatus.getEntityStatus()); + assertEquals( + AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -1510,15 +1749,15 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion - assertEquals(AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, - asyncFetchStatus.getEntityStatus()); + assertEquals( + AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -1539,12 +1778,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -1568,15 +1807,15 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion - assertEquals(AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, - asyncFetchStatus.getEntityStatus()); + assertEquals( + AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -1596,12 +1835,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -1628,15 +1867,15 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion - assertEquals(AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, - asyncFetchStatus.getEntityStatus()); + assertEquals( + AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -1658,15 +1897,15 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion - assertEquals(AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, - asyncFetchStatus.getEntityStatus()); + assertEquals( + AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -1689,12 +1928,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -1721,15 +1960,15 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion - assertEquals(AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, - asyncFetchStatus.getEntityStatus()); + assertEquals( + AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -1752,12 +1991,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -1784,15 +2023,15 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion - assertEquals(AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, - asyncFetchStatus.getEntityStatus()); + assertEquals( + AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -1815,12 +2054,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -1845,12 +2084,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -1875,12 +2114,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -1905,12 +2144,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -1935,12 +2174,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -1961,12 +2200,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -1995,12 +2234,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2021,12 +2260,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2047,12 +2286,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -2080,12 +2319,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2113,12 +2352,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2142,12 +2381,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2179,12 +2418,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2208,12 +2447,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + eventTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2234,12 +2473,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"filters\":" + filters + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -2265,12 +2504,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"filters\":" + filters + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2289,12 +2528,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"filters\":" + filters + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2325,12 +2564,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_deduplication_keys\":" + tooManyEntries + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2362,12 +2601,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_deduplication_keys\":" + deduplicationKeys + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2421,12 +2660,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + ",\"aggregatable_deduplication_keys\":" + deduplicationKeys + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2481,12 +2720,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_deduplication_keys\":" + deduplicationKeys + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2509,12 +2748,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "," + "\"aggregatable_deduplication_keys\":{}" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2537,12 +2776,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "," + "\"aggregatable_deduplication_keys\":" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2565,12 +2804,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"filters\":" + filters + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2589,12 +2828,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"filters\":" + filters + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2621,12 +2860,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"filters\":" + filters + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2645,12 +2884,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"filters\":" + filters + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2669,12 +2908,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"filters\":" + filters + "}"))); when(mFlags.getMeasurementEnableLookbackWindowFilter()).thenReturn(true); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2693,12 +2932,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"filters\":" + filters + "}"))); when(mFlags.getMeasurementEnableLookbackWindowFilter()).thenReturn(true); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2717,12 +2956,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"filters\":" + filters + "}"))); when(mFlags.getMeasurementEnableLookbackWindowFilter()).thenReturn(true); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2741,12 +2980,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"filters\":" + filters + "}"))); when(mFlags.getMeasurementEnableLookbackWindowFilter()).thenReturn(true); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2767,12 +3006,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"not_filters\":" + notFilters + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -2798,12 +3037,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"not_filters\":" + notFilters + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2822,12 +3061,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"not_filters\":" + notFilters + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2850,12 +3089,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"not_filters\":" + notFilters + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2874,12 +3113,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"not_filters\":" + notFilters + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2906,12 +3145,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"not_filters\":" + notFilters + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2930,12 +3169,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"not_filters\":" + notFilters + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); @@ -2956,21 +3195,21 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest when(mUrlConnection.getHeaderFields()) .thenReturn( Map.of( - "Attribution-Reporting-Redirect", + AsyncRedirects.REDIRECT_LIST_HEADER_KEY, List.of(DEFAULT_REDIRECT), "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + EVENT_TRIGGERS_1 + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion verify(mFetcher, times(1)).openUrl(any()); assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); - assertEquals(1, asyncRedirect.getRedirects().size()); - assertEquals(DEFAULT_REDIRECT, asyncRedirect.getRedirects().get(0).toString()); + assertEquals(1, asyncRedirects.getRedirects().size()); + assertEquals(DEFAULT_REDIRECT, asyncRedirects.getRedirects().get(0).getUri().toString()); assertEquals( AsyncFetchStatus.EntityStatus.INVALID_ENROLLMENT, asyncFetchStatus.getEntityStatus()); @@ -2994,12 +3233,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"" + "}")); when(mUrlConnection.getHeaderFields()).thenReturn(headersRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -3030,12 +3269,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest when(mUrlConnection.getHeaderFields()).thenReturn(headersRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -3062,12 +3301,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest when(mUrlConnection.getHeaderFields()).thenReturn(headersRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -3094,12 +3333,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest when(mUrlConnection.getHeaderFields()).thenReturn(headersRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -3126,12 +3365,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest when(mUrlConnection.getHeaderFields()).thenReturn(headersRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -3149,12 +3388,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest WebUtil.validUri("http://foo.test"), CONTEXT.getPackageName(), SDK_PACKAGE_NAME); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals( AsyncFetchStatus.ResponseStatus.INVALID_URL, asyncFetchStatus.getResponseStatus()); @@ -3178,11 +3417,11 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest false, false, null); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(registration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(registration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals( AsyncFetchStatus.ResponseStatus.INVALID_URL, asyncFetchStatus.getResponseStatus()); @@ -3195,12 +3434,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest doThrow(new IOException("Bad internet things")) .when(mFetcher) .openUrl(new URL(TRIGGER_URI)); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals( AsyncFetchStatus.ResponseStatus.NETWORK_ERROR, @@ -3219,12 +3458,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + EVENT_TRIGGERS_1 + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals( AsyncFetchStatus.ResponseStatus.SERVER_UNAVAILABLE, @@ -3243,12 +3482,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\": [{}]}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -3271,14 +3510,14 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest headersFirstRequest.put( "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + EVENT_TRIGGERS_1 + "}")); - headersFirstRequest.put("Attribution-Reporting-Redirect", List.of(DEFAULT_REDIRECT)); + headersFirstRequest.put(AsyncRedirects.REDIRECT_LIST_HEADER_KEY, List.of(DEFAULT_REDIRECT)); when(mUrlConnection.getHeaderFields()).thenReturn(headersFirstRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -3298,21 +3537,22 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest doReturn(mUrlConnection).when(mFetcher).openUrl(any(URL.class)); when(mUrlConnection.getResponseCode()).thenReturn(200); when(mUrlConnection.getHeaderFields()) - .thenReturn(Map.of("Attribution-Reporting-Redirect", List.of(DEFAULT_REDIRECT))) + .thenReturn( + Map.of(AsyncRedirects.REDIRECT_LIST_HEADER_KEY, List.of(DEFAULT_REDIRECT))) .thenReturn( Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + EVENT_TRIGGERS_1 + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); - assertEquals(1, asyncRedirect.getRedirects().size()); - assertEquals(DEFAULT_REDIRECT, asyncRedirect.getRedirects().get(0).toString()); + assertEquals(1, asyncRedirects.getRedirects().size()); + assertEquals(DEFAULT_REDIRECT, asyncRedirects.getRedirects().get(0).getUri().toString()); assertEquals( AsyncFetchStatus.EntityStatus.HEADER_MISSING, asyncFetchStatus.getEntityStatus()); @@ -3342,12 +3582,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_trigger_data\": " + aggregateTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -3383,12 +3623,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_trigger_data\": " + aggregateTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals( AsyncFetchStatus.EntityStatus.PARSING_ERROR, asyncFetchStatus.getEntityStatus()); @@ -3418,12 +3658,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_trigger_data\": " + aggregateTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -3460,12 +3700,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_trigger_data\": " + aggregateTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -3504,12 +3744,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "{\"aggregatable_trigger_data\":" + aggregateTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals( AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); @@ -3543,12 +3783,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "{\"aggregatable_trigger_data\":" + aggregateTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals( AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); @@ -3572,12 +3812,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_values\": " + aggregatableValues + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -3603,12 +3843,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"aggregatable_values\": " + tooManyKeys + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals( AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); @@ -3632,12 +3872,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"filters\": " + filters + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -3668,14 +3908,14 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"" + "}")); when(mUrlConnection1.getHeaderFields()).thenReturn(headersRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( webTriggerRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -3722,14 +3962,14 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + aggregatableTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( webTriggerRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -3758,24 +3998,24 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\": " + EVENT_TRIGGERS_1 + "}"), - "Attribution-Reporting-Redirect", + AsyncRedirects.REDIRECT_LIST_HEADER_KEY, List.of(LIST_TYPE_REDIRECT_URI), - "Location", + AsyncRedirects.REDIRECT_LOCATION_HEADER_KEY, List.of(LOCATION_TYPE_REDIRECT_URI))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( webTriggerRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); Trigger result = fetch.get(); - assertEquals(0, asyncRedirect.getRedirects().size()); + assertEquals(0, asyncRedirects.getRedirects().size()); assertEquals(new JSONArray(EVENT_TRIGGERS_1).toString(), result.getEventTriggers()); assertEquals(REGISTRATION_URI_1, result.getRegistrationOrigin()); verify(mUrlConnection).setRequestMethod("POST"); @@ -3802,11 +4042,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_trigger_data\": " + aggregatableTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution - Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + Optional<Trigger> fetch = + mFetcher.fetchTrigger( + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -3833,12 +4074,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_trigger_data\": " + aggregatableTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -3867,12 +4108,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_trigger_data\": " + aggregatableTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -3907,12 +4148,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_trigger_data\": " + aggregatableTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -3942,12 +4183,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_trigger_data\": " + aggregatableTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -3974,12 +4215,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_trigger_data\": " + aggregatableTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -4016,12 +4257,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_trigger_data\": " + aggregatableTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -4049,12 +4290,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_trigger_data\": " + aggregatableTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -4082,12 +4323,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_trigger_data\": " + aggregatableTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -4118,12 +4359,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_trigger_data\": " + aggregatableTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -4157,12 +4398,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_trigger_data\": " + aggregatableTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -4191,12 +4432,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_trigger_data\": " + aggregatableTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -4234,12 +4475,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_trigger_data\": " + aggregatableTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -4268,12 +4509,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_trigger_data\": " + aggregatableTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -4307,12 +4548,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_trigger_data\": " + aggregatableTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -4342,12 +4583,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_trigger_data\": " + aggregatableTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -4385,12 +4626,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_trigger_data\": " + aggregatableTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -4420,12 +4661,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_trigger_data\": " + aggregatableTriggerData + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -4447,12 +4688,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest when(mUrlConnection.getHeaderFields()).thenReturn(headersRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -4479,12 +4720,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest when(mUrlConnection.getHeaderFields()).thenReturn(headersRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -4511,12 +4752,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest when(mUrlConnection.getHeaderFields()).thenReturn(headersRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -4543,12 +4784,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest when(mUrlConnection.getHeaderFields()).thenReturn(headersRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -4572,12 +4813,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest when(mUrlConnection.getHeaderFields()).thenReturn(headersRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -4603,12 +4844,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_values\": " + aggregatableValues + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertTrue(fetch.isPresent()); Trigger result = fetch.get(); assertEquals(ENROLLMENT_ID, result.getEnrollmentId()); @@ -4633,12 +4874,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_values\": " + aggregatableValues + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertEquals( AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); assertFalse(fetch.isPresent()); @@ -4662,12 +4903,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_values\": " + aggregatableValues + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertTrue(fetch.isPresent()); Trigger result = fetch.get(); assertEquals(ENROLLMENT_ID, result.getEnrollmentId()); @@ -4692,12 +4933,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_values\": " + aggregatableValues + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertEquals( AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); assertFalse(fetch.isPresent()); @@ -4721,12 +4962,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_values\": " + aggregatableValues + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertTrue(fetch.isPresent()); Trigger result = fetch.get(); assertEquals(ENROLLMENT_ID, result.getEnrollmentId()); @@ -4753,12 +4994,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_values\": " + aggregatableValues + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertEquals( AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); assertFalse(fetch.isPresent()); @@ -4784,12 +5025,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_values\": " + aggregatableValues + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertTrue(fetch.isPresent()); Trigger result = fetch.get(); assertEquals(ENROLLMENT_ID, result.getEnrollmentId()); @@ -4816,12 +5057,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_values\": " + aggregatableValues + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertEquals( AsyncFetchStatus.EntityStatus.VALIDATION_ERROR, asyncFetchStatus.getEntityStatus()); assertFalse(fetch.isPresent()); @@ -4847,12 +5088,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_values\": " + aggregatableValues + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertTrue(fetch.isPresent()); Trigger result = fetch.get(); assertEquals(ENROLLMENT_ID, result.getEnrollmentId()); @@ -4876,12 +5117,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_values\": " + aggregatableValues + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -4905,12 +5146,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{\"aggregatable_values\": " + tooManyKeys + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -4932,12 +5173,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"aggregatable_values\": " + aggregatableValues + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( - appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirect); + appTriggerRegistrationRequest(request), asyncFetchStatus, asyncRedirects); assertFalse(fetch.isPresent()); verify(mUrlConnection, times(1)).setRequestMethod("POST"); verify(mFetcher, times(1)).openUrl(any()); @@ -4970,14 +5211,14 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest "Attribution-Reporting-Register-Trigger", List.of("{\"event_trigger_data\":" + EVENT_TRIGGERS_1 + "}"))); doReturn(5L).when(mFlags).getMaxResponseBasedRegistrationPayloadSizeBytes(); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); asyncFetchStatus.setRegistrationDelay(0L); asyncFetchStatus.setRetryCount(0); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); assertTrue(fetch.isPresent()); Trigger result = fetch.get(); assertEquals(ENROLLMENT_ID, result.getEnrollmentId()); @@ -5073,7 +5314,7 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest attributionConfig1.serializeAsJson(mFlags), attributionConfig2.serializeAsJson(mFlags))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); @@ -5081,7 +5322,7 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); @@ -5180,7 +5421,7 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest attributionConfig1.serializeAsJson(mFlags), attributionConfig2.serializeAsJson(mFlags))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = webTriggerRegistrationRequest(request, true); @@ -5188,7 +5429,7 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); @@ -5245,7 +5486,7 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "}"))); doReturn(5000L).when(mFlags).getMaxResponseBasedRegistrationPayloadSizeBytes(); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = webTriggerRegistrationRequest(request, true); @@ -5253,7 +5494,7 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); @@ -5289,7 +5530,7 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, new AsyncRedirect()); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, new AsyncRedirects()); // Assertion assertEquals( @@ -5318,7 +5559,7 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "}"))); doReturn(5000L).when(mFlags).getMaxResponseBasedRegistrationPayloadSizeBytes(); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); @@ -5326,7 +5567,7 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); @@ -5361,7 +5602,7 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "}"))); doReturn(5000L).when(mFlags).getMaxResponseBasedRegistrationPayloadSizeBytes(); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); @@ -5369,7 +5610,7 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); @@ -5426,7 +5667,7 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "}"))); doReturn(5000L).when(mFlags).getMaxResponseBasedRegistrationPayloadSizeBytes(); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); @@ -5434,7 +5675,7 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); @@ -5469,14 +5710,14 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"" + "}")); when(mUrlConnection1.getHeaderFields()).thenReturn(headersRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( webTriggerRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -5514,14 +5755,14 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"" + "}")); when(mUrlConnection1.getHeaderFields()).thenReturn(headersRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( webTriggerRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -5552,12 +5793,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest when(mUrlConnection.getHeaderFields()).thenReturn(headersRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -5591,12 +5832,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest when(mUrlConnection.getHeaderFields()).thenReturn(headersRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -5627,12 +5868,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"" + "}")); when(mUrlConnection.getHeaderFields()).thenReturn(headersRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -5665,12 +5906,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"" + "}")); when(mUrlConnection.getHeaderFields()).thenReturn(headersRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -5702,12 +5943,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest when(mUrlConnection.getHeaderFields()).thenReturn(headersRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequestWithAdId(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertTrue(fetch.isPresent()); Trigger result = fetch.get(); @@ -5742,14 +5983,14 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"" + "}")); when(mUrlConnection1.getHeaderFields()).thenReturn(headersRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( webTriggerRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -5786,14 +6027,14 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"" + "}")); when(mUrlConnection1.getHeaderFields()).thenReturn(headersRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( webTriggerRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -5829,14 +6070,14 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"" + "}")); when(mUrlConnection1.getHeaderFields()).thenReturn(headersRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); // Execution Optional<Trigger> fetch = mFetcher.fetchTrigger( webTriggerRegistrationRequest(request, true), asyncFetchStatus, - asyncRedirect); + asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -5847,7 +6088,7 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest } @Test - public void fetchTrigger_setsFakeEnrollmentId_whenDisableEnrollmentFlagIsTrue() + public void fetchTrigger_setsSiteEnrollmentId_whenDisableEnrollmentFlagIsTrue() throws Exception { String uri = WebUtil.validUrl("https://test1.example.test:8081"); RegistrationRequest request = buildRequest(uri); @@ -5866,12 +6107,12 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "\"" + "}")); when(mUrlConnection.getHeaderFields()).thenReturn(headersRequest); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertTrue(fetch.isPresent()); @@ -5879,7 +6120,85 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest assertEquals( WebUtil.validUrl("https://test1.example.test:8081"), result.getRegistrationOrigin().toString()); - assertEquals(Enrollment.FAKE_ENROLLMENT, result.getEnrollmentId()); + assertEquals("https://example.com", result.getEnrollmentId()); + assertEquals(new JSONArray(EVENT_TRIGGERS_1).toString(), result.getEventTriggers()); + assertEquals(DEBUG_KEY, result.getDebugKey()); + verify(mUrlConnection).setRequestMethod("POST"); + } + + @Test + public void fetchTrigger_setsSiteEnrollmentId_whenDisableEnrollmentFlagIsTrueForLocalhost() + throws Exception { + String uri = WebUtil.validUrl("https://localhost:8081"); + RegistrationRequest request = buildRequest(uri); + doReturn(mUrlConnection).when(mFetcher).openUrl(new URL(uri)); + when(mUrlConnection.getResponseCode()).thenReturn(200); + doReturn(true).when(mFlags).isDisableMeasurementEnrollmentCheck(); + Map<String, List<String>> headersRequest = new HashMap<>(); + headersRequest.put( + "Attribution-Reporting-Register-Trigger", + List.of( + "{" + + "\"event_trigger_data\": " + + EVENT_TRIGGERS_1 + + ", \"debug_key\": \"" + + DEBUG_KEY + + "\"" + + "}")); + when(mUrlConnection.getHeaderFields()).thenReturn(headersRequest); + AsyncRedirects asyncRedirects = new AsyncRedirects(); + AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); + AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); + // Execution + Optional<Trigger> fetch = + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); + // Assertion + assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); + assertTrue(fetch.isPresent()); + Trigger result = fetch.get(); + assertEquals( + WebUtil.validUrl("https://localhost:8081"), + result.getRegistrationOrigin().toString()); + assertEquals("https://localhost", result.getEnrollmentId()); + assertEquals(new JSONArray(EVENT_TRIGGERS_1).toString(), result.getEventTriggers()); + assertEquals(DEBUG_KEY, result.getDebugKey()); + verify(mUrlConnection).setRequestMethod("POST"); + } + + @Test + public void fetchTrigger_setsSiteEnrollmentId_whenDisableEnrollmentFlagIsTrueForIP() + throws Exception { + String uri = WebUtil.validUrl("https://127.0.0.1:8081"); + RegistrationRequest request = buildRequest(uri); + doReturn(mUrlConnection).when(mFetcher).openUrl(new URL(uri)); + when(mUrlConnection.getResponseCode()).thenReturn(200); + doReturn(true).when(mFlags).isDisableMeasurementEnrollmentCheck(); + Map<String, List<String>> headersRequest = new HashMap<>(); + headersRequest.put( + "Attribution-Reporting-Register-Trigger", + List.of( + "{" + + "\"event_trigger_data\": " + + EVENT_TRIGGERS_1 + + ", \"debug_key\": \"" + + DEBUG_KEY + + "\"" + + "}")); + when(mUrlConnection.getHeaderFields()).thenReturn(headersRequest); + AsyncRedirects asyncRedirects = new AsyncRedirects(); + AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); + AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); + // Execution + Optional<Trigger> fetch = + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); + // Assertion + assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); + assertTrue(fetch.isPresent()); + Trigger result = fetch.get(); + assertEquals( + WebUtil.validUrl("https://127.0.0.1:8081"), + result.getRegistrationOrigin().toString()); + assertEquals("https://127.0.0.1", result.getEnrollmentId()); assertEquals(new JSONArray(EVENT_TRIGGERS_1).toString(), result.getEventTriggers()); assertEquals(DEBUG_KEY, result.getDebugKey()); verify(mUrlConnection).setRequestMethod("POST"); @@ -5900,7 +6219,7 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "'aggregation_coordinator_origin': " + "'https://cloud.coordination.test'" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); when(mFlags.getMeasurementAggregationCoordinatorOriginEnabled()).thenReturn(true); @@ -5908,7 +6227,7 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest .thenReturn("https://cloud.coordination.test"); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals(AsyncFetchStatus.EntityStatus.SUCCESS, asyncFetchStatus.getEntityStatus()); @@ -5941,7 +6260,7 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "'aggregation_coordinator_origin': " + "'https://invalid.cloud.coordination.test'" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); when(mFlags.getMeasurementAggregationCoordinatorOriginEnabled()).thenReturn(true); @@ -5949,7 +6268,7 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest .thenReturn("https://cloud.coordination.test"); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -5968,13 +6287,13 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest Map.of( "Attribution-Reporting-Register-Trigger", List.of("{" + "\"event_trigger_data\": " + "[{}]" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); when(mFlags.getMeasurementAggregationCoordinatorOriginEnabled()).thenReturn(true); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals(AsyncFetchStatus.EntityStatus.SUCCESS, asyncFetchStatus.getEntityStatus()); @@ -6005,7 +6324,7 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest + "'aggregation_coordinator_origin': " + "'https://invalid.cloud.coordination.test'" + "}"))); - AsyncRedirect asyncRedirect = new AsyncRedirect(); + AsyncRedirects asyncRedirects = new AsyncRedirects(); AsyncFetchStatus asyncFetchStatus = new AsyncFetchStatus(); AsyncRegistration asyncRegistration = appTriggerRegistrationRequest(request); when(mFlags.getMeasurementAggregationCoordinatorOriginEnabled()).thenReturn(true); @@ -6013,7 +6332,7 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest .thenReturn("https://cloud.coordination.test"); // Execution Optional<Trigger> fetch = - mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirect); + mFetcher.fetchTrigger(asyncRegistration, asyncFetchStatus, asyncRedirects); // Assertion assertEquals(AsyncFetchStatus.ResponseStatus.SUCCESS, asyncFetchStatus.getResponseStatus()); assertEquals( @@ -6050,26 +6369,6 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest private static AsyncRegistration appTriggerRegistrationRequest( RegistrationRequest registrationRequest) { - // Necessary for testing - String enrollmentId = ""; - if (EnrollmentDao.getInstance(CONTEXT) - .getEnrollmentDataFromMeasurementUrl( - registrationRequest - .getRegistrationUri() - .buildUpon() - .clearQuery() - .build()) - != null) { - enrollmentId = - EnrollmentDao.getInstance(CONTEXT) - .getEnrollmentDataFromMeasurementUrl( - registrationRequest - .getRegistrationUri() - .buildUpon() - .clearQuery() - .build()) - .getEnrollmentId(); - } return createAsyncRegistration( UUID.randomUUID().toString(), registrationRequest.getRegistrationUri(), @@ -6091,26 +6390,6 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest private static AsyncRegistration appTriggerRegistrationRequestWithAdId( RegistrationRequest registrationRequest) { - // Necessary for testing - String enrollmentId = ""; - if (EnrollmentDao.getInstance(CONTEXT) - .getEnrollmentDataFromMeasurementUrl( - registrationRequest - .getRegistrationUri() - .buildUpon() - .clearQuery() - .build()) - != null) { - enrollmentId = - EnrollmentDao.getInstance(CONTEXT) - .getEnrollmentDataFromMeasurementUrl( - registrationRequest - .getRegistrationUri() - .buildUpon() - .clearQuery() - .build()) - .getEnrollmentId(); - } return createAsyncRegistration( UUID.randomUUID().toString(), registrationRequest.getRegistrationUri(), @@ -6136,28 +6415,6 @@ public final class AsyncTriggerFetcherTest extends AdServicesExtendedMockitoTest if (webTriggerRegistrationRequest.getTriggerParams().size() > 0) { WebTriggerParams webTriggerParams = webTriggerRegistrationRequest.getTriggerParams().get(0); - // Necessary for testing - String enrollmentId = ""; - if (EnrollmentDao.getInstance(CONTEXT) - .getEnrollmentDataFromMeasurementUrl( - webTriggerRegistrationRequest - .getTriggerParams() - .get(0) - .getRegistrationUri() - .buildUpon() - .clearQuery() - .build()) - != null) { - enrollmentId = - EnrollmentDao.getInstance(CONTEXT) - .getEnrollmentDataFromMeasurementUrl( - webTriggerParams - .getRegistrationUri() - .buildUpon() - .clearQuery() - .build()) - .getEnrollmentId(); - } return createAsyncRegistration( UUID.randomUUID().toString(), webTriggerParams.getRegistrationUri(), diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/registration/EnqueueAsyncRegistrationTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/registration/EnqueueAsyncRegistrationTest.java index 847dc6ab3..7b6e78903 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/registration/EnqueueAsyncRegistrationTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/registration/EnqueueAsyncRegistrationTest.java @@ -243,6 +243,8 @@ public class EnqueueAsyncRegistrationTest { Assert.assertNotNull(asyncRegistration.getType()); Assert.assertEquals( AsyncRegistration.RegistrationType.APP_SOURCE, asyncRegistration.getType()); + Assert.assertEquals( + AsyncRedirect.RedirectBehavior.AS_IS, asyncRegistration.getRedirectBehavior()); } } @@ -300,6 +302,8 @@ public class EnqueueAsyncRegistrationTest { Assert.assertNotNull(asyncRegistration.getType()); Assert.assertEquals( AsyncRegistration.RegistrationType.APP_SOURCE, asyncRegistration.getType()); + Assert.assertEquals( + AsyncRedirect.RedirectBehavior.AS_IS, asyncRegistration.getRedirectBehavior()); } } @@ -390,6 +394,8 @@ public class EnqueueAsyncRegistrationTest { Assert.assertEquals( Uri.parse("android-app://test.destination"), asyncRegistration.getRegistrant()); Assert.assertNull(asyncRegistration.getSourceType()); + Assert.assertEquals( + AsyncRedirect.RedirectBehavior.AS_IS, asyncRegistration.getRedirectBehavior()); } } @@ -523,6 +529,8 @@ public class EnqueueAsyncRegistrationTest { VALID_WEB_SOURCE_REGISTRATION_NULL_INPUT_EVENT.getTopOriginUri(), asyncRegistration1.getTopOrigin()); assertEqualsWebSourceRegistrationCommon(asyncRegistration1); + Assert.assertEquals( + AsyncRedirect.RedirectBehavior.AS_IS, asyncRegistration1.getRedirectBehavior()); Assert.assertTrue(cursor.moveToNext()); AsyncRegistration asyncRegistration2 = @@ -533,6 +541,8 @@ public class EnqueueAsyncRegistrationTest { VALID_WEB_SOURCE_REGISTRATION_NULL_INPUT_EVENT.getTopOriginUri(), asyncRegistration2.getTopOrigin()); assertEqualsWebSourceRegistrationCommon(asyncRegistration2); + Assert.assertEquals( + AsyncRedirect.RedirectBehavior.AS_IS, asyncRegistration2.getRedirectBehavior()); Assert.assertEquals( asyncRegistration1.getRegistrationId(), @@ -591,6 +601,8 @@ public class EnqueueAsyncRegistrationTest { validWebSourceRegistration.getTopOriginUri(), asyncRegistration1.getTopOrigin()); assertEqualsWebSourceRegistrationCommon(asyncRegistration1); + Assert.assertEquals( + AsyncRedirect.RedirectBehavior.AS_IS, asyncRegistration1.getRedirectBehavior()); Assert.assertTrue(cursor.moveToNext()); AsyncRegistration asyncRegistration2 = @@ -601,6 +613,8 @@ public class EnqueueAsyncRegistrationTest { validWebSourceRegistration.getTopOriginUri(), asyncRegistration2.getTopOrigin()); assertEqualsWebSourceRegistrationCommon(asyncRegistration2); + Assert.assertEquals( + AsyncRedirect.RedirectBehavior.AS_IS, asyncRegistration2.getRedirectBehavior()); Assert.assertEquals( asyncRegistration1.getRegistrationId(), @@ -642,12 +656,16 @@ public class EnqueueAsyncRegistrationTest { SqliteObjectMapper.constructAsyncRegistration(cursor); Assert.assertEquals(REGISTRATION_URI_1, asyncRegistration1.getRegistrationUri()); assertEqualsWebTriggerRegistrationCommon(asyncRegistration1); + Assert.assertEquals( + AsyncRedirect.RedirectBehavior.AS_IS, asyncRegistration1.getRedirectBehavior()); Assert.assertTrue(cursor.moveToNext()); AsyncRegistration asyncRegistration2 = SqliteObjectMapper.constructAsyncRegistration(cursor); Assert.assertEquals(REGISTRATION_URI_2, asyncRegistration2.getRegistrationUri()); assertEqualsWebTriggerRegistrationCommon(asyncRegistration2); + Assert.assertEquals( + AsyncRedirect.RedirectBehavior.AS_IS, asyncRegistration2.getRedirectBehavior()); Assert.assertEquals( asyncRegistration1.getRegistrationId(), @@ -656,7 +674,7 @@ public class EnqueueAsyncRegistrationTest { } @Test - public void testRunInTransactionFail_inValid() { + public void testRunInTransactionFail_invalid() { when(mDatastoreManagerMock.runInTransaction(any())).thenReturn(false); Assert.assertFalse( EnqueueAsyncRegistration.webTriggerRegistrationRequest( @@ -722,6 +740,8 @@ public class EnqueueAsyncRegistrationTest { Assert.assertNotNull(asyncRegistration.getType()); Assert.assertEquals( AsyncRegistration.RegistrationType.APP_SOURCE, asyncRegistration.getType()); + Assert.assertEquals( + AsyncRedirect.RedirectBehavior.AS_IS, asyncRegistration.getRedirectBehavior()); } } @@ -773,6 +793,8 @@ public class EnqueueAsyncRegistrationTest { assertEqualsAppSourcesRegistrationCommon(asyncRegistration1); Assert.assertEquals(REGISTRATION_URI_1, asyncRegistration1.getRegistrationUri()); Assert.assertEquals(Source.SourceType.EVENT, asyncRegistration1.getSourceType()); + Assert.assertEquals( + AsyncRedirect.RedirectBehavior.AS_IS, asyncRegistration1.getRedirectBehavior()); Assert.assertTrue(cursor.moveToNext()); AsyncRegistration asyncRegistration2 = @@ -780,6 +802,8 @@ public class EnqueueAsyncRegistrationTest { assertEqualsAppSourcesRegistrationCommon(asyncRegistration2); Assert.assertEquals(REGISTRATION_URI_2, asyncRegistration2.getRegistrationUri()); Assert.assertEquals(Source.SourceType.EVENT, asyncRegistration2.getSourceType()); + Assert.assertEquals( + AsyncRedirect.RedirectBehavior.AS_IS, asyncRegistration2.getRedirectBehavior()); Assert.assertEquals( asyncRegistration1.getRegistrationId(), asyncRegistration2.getRegistrationId()); @@ -835,6 +859,8 @@ public class EnqueueAsyncRegistrationTest { assertEqualsAppSourcesRegistrationCommon(asyncRegistration1); Assert.assertEquals(REGISTRATION_URI_1, asyncRegistration1.getRegistrationUri()); Assert.assertEquals(Source.SourceType.NAVIGATION, asyncRegistration1.getSourceType()); + Assert.assertEquals( + AsyncRedirect.RedirectBehavior.AS_IS, asyncRegistration1.getRedirectBehavior()); Assert.assertTrue(cursor.moveToNext()); AsyncRegistration asyncRegistration2 = @@ -842,6 +868,8 @@ public class EnqueueAsyncRegistrationTest { assertEqualsAppSourcesRegistrationCommon(asyncRegistration2); Assert.assertEquals(REGISTRATION_URI_2, asyncRegistration2.getRegistrationUri()); Assert.assertEquals(Source.SourceType.NAVIGATION, asyncRegistration2.getSourceType()); + Assert.assertEquals( + AsyncRedirect.RedirectBehavior.AS_IS, asyncRegistration2.getRedirectBehavior()); Assert.assertEquals( asyncRegistration1.getRegistrationId(), asyncRegistration2.getRegistrationId()); @@ -898,6 +926,8 @@ public class EnqueueAsyncRegistrationTest { Assert.assertEquals(REGISTRATION_URI_1, asyncRegistration1.getRegistrationUri()); Assert.assertEquals(Source.SourceType.NAVIGATION, asyncRegistration1.getSourceType()); Assert.assertNull(asyncRegistration1.getPostBody()); + Assert.assertEquals( + AsyncRedirect.RedirectBehavior.AS_IS, asyncRegistration1.getRedirectBehavior()); Assert.assertTrue(cursor.moveToNext()); AsyncRegistration asyncRegistration2 = @@ -906,6 +936,8 @@ public class EnqueueAsyncRegistrationTest { Assert.assertEquals(REGISTRATION_URI_2, asyncRegistration2.getRegistrationUri()); Assert.assertEquals(Source.SourceType.NAVIGATION, asyncRegistration2.getSourceType()); Assert.assertNull(asyncRegistration2.getPostBody()); + Assert.assertEquals( + AsyncRedirect.RedirectBehavior.AS_IS, asyncRegistration2.getRedirectBehavior()); Assert.assertEquals( asyncRegistration1.getRegistrationId(), asyncRegistration2.getRegistrationId()); diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/reporting/AggregateReportingJobHandlerTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/reporting/AggregateReportingJobHandlerTest.java index e5700638e..fd7668d93 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/reporting/AggregateReportingJobHandlerTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/reporting/AggregateReportingJobHandlerTest.java @@ -54,6 +54,7 @@ import com.android.adservices.service.measurement.aggregation.AggregateCryptoFix import com.android.adservices.service.measurement.aggregation.AggregateEncryptionKey; import com.android.adservices.service.measurement.aggregation.AggregateEncryptionKeyManager; import com.android.adservices.service.measurement.aggregation.AggregateReport; +import com.android.adservices.service.measurement.aggregation.AggregateReportFixture; import com.android.adservices.service.measurement.util.UnsignedLong; import com.android.adservices.service.stats.AdServicesLogger; import com.android.adservices.service.stats.MeasurementReportsStats; @@ -989,6 +990,164 @@ public class AggregateReportingJobHandlerTest { verify(mTransaction, times(1)).end(); } + @Test + public void performReport_normalReportWithDebugKeys_hasDebugModeEnabled() + throws DatastoreException, IOException, JSONException { + AggregateReport aggregateReport = + AggregateReportFixture.getValidAggregateReportBuilder() + .setSourceDebugKey( + AggregateReportFixture.ValidAggregateReportParams.SOURCE_DEBUG_KEY) + .setTriggerDebugKey( + AggregateReportFixture.ValidAggregateReportParams.TRIGGER_DEBUG_KEY) + .build(); + executeDebugModeVerification(aggregateReport, mSpyAggregateReportingJobHandler, "enabled"); + verify(mMeasurementDao, times(1)) + .markAggregateReportStatus( + eq(aggregateReport.getId()), eq(AggregateReport.Status.DELIVERED)); + } + + @Test + public void performReport_normalReportWithOnlySourceDebugKey_hasDebugModeNull() + throws DatastoreException, IOException, JSONException { + // Setup + AggregateReport aggregateReport = + AggregateReportFixture.getValidAggregateReportBuilder() + .setSourceDebugKey( + AggregateReportFixture.ValidAggregateReportParams.SOURCE_DEBUG_KEY) + .setTriggerDebugKey(null) + .build(); + executeDebugModeVerification(aggregateReport, mSpyAggregateReportingJobHandler, ""); + verify(mMeasurementDao, times(1)) + .markAggregateReportStatus( + eq(aggregateReport.getId()), eq(AggregateReport.Status.DELIVERED)); + } + + @Test + public void performReport_normalReportWithOnlyTriggerDebugKey_hasDebugModeNull() + throws DatastoreException, IOException, JSONException { + AggregateReport aggregateReport = + AggregateReportFixture.getValidAggregateReportBuilder() + .setSourceDebugKey(null) + .setTriggerDebugKey( + AggregateReportFixture.ValidAggregateReportParams.TRIGGER_DEBUG_KEY) + .build(); + executeDebugModeVerification(aggregateReport, mSpyAggregateReportingJobHandler, ""); + verify(mMeasurementDao, times(1)) + .markAggregateReportStatus( + eq(aggregateReport.getId()), eq(AggregateReport.Status.DELIVERED)); + } + + @Test + public void performReport_normalReportWithNoDebugKey_hasDebugModeNull() + throws DatastoreException, IOException, JSONException { + AggregateReport aggregateReport = + AggregateReportFixture.getValidAggregateReportBuilder() + .setSourceDebugKey(null) + .setTriggerDebugKey(null) + .build(); + executeDebugModeVerification(aggregateReport, mSpyAggregateReportingJobHandler, ""); + verify(mMeasurementDao, times(1)) + .markAggregateReportStatus( + eq(aggregateReport.getId()), eq(AggregateReport.Status.DELIVERED)); + } + + @Test + public void performReport_debugReportWithDebugKeys_hasDebugModeEnabled() + throws DatastoreException, IOException, JSONException { + AggregateReport aggregateReport = + AggregateReportFixture.getValidAggregateReportBuilder() + .setSourceDebugKey( + AggregateReportFixture.ValidAggregateReportParams.SOURCE_DEBUG_KEY) + .setTriggerDebugKey( + AggregateReportFixture.ValidAggregateReportParams.TRIGGER_DEBUG_KEY) + .build(); + executeDebugModeVerification( + aggregateReport, mSpyDebugAggregateReportingJobHandler, "enabled"); + verify(mMeasurementDao, times(1)) + .markAggregateDebugReportDelivered(eq(aggregateReport.getId())); + } + + @Test + public void performReport_debugReportWithOnlySourceDebugKey_hasDebugModeNull() + throws DatastoreException, IOException, JSONException { + AggregateReport aggregateReport = + AggregateReportFixture.getValidAggregateReportBuilder() + .setSourceDebugKey( + AggregateReportFixture.ValidAggregateReportParams.SOURCE_DEBUG_KEY) + .setTriggerDebugKey(null) + .build(); + executeDebugModeVerification(aggregateReport, mSpyDebugAggregateReportingJobHandler, ""); + verify(mMeasurementDao, times(1)) + .markAggregateDebugReportDelivered(eq(aggregateReport.getId())); + } + + @Test + public void performReport_debugReportWithOnlyTriggerDebugKey_hasDebugModeNull() + throws DatastoreException, IOException, JSONException { + AggregateReport aggregateReport = + AggregateReportFixture.getValidAggregateReportBuilder() + .setSourceDebugKey(null) + .setTriggerDebugKey( + AggregateReportFixture.ValidAggregateReportParams.TRIGGER_DEBUG_KEY) + .build(); + executeDebugModeVerification(aggregateReport, mSpyDebugAggregateReportingJobHandler, ""); + } + + @Test + public void performReport_debugReportWithNoDebugKey_hasDebugModeNull() + throws DatastoreException, IOException, JSONException { + AggregateReport aggregateReport = + AggregateReportFixture.getValidAggregateReportBuilder() + .setSourceDebugKey(null) + .setTriggerDebugKey(null) + .build(); + executeDebugModeVerification(aggregateReport, mSpyDebugAggregateReportingJobHandler, ""); + verify(mMeasurementDao, times(1)) + .markAggregateDebugReportDelivered(eq(aggregateReport.getId())); + } + + private void executeDebugModeVerification( + AggregateReport aggregateReport, + AggregateReportingJobHandler aggregateReportingJobHandler, + String expectedDebugMode) + throws DatastoreException, IOException, JSONException { + when(mMeasurementDao.getAggregateReport(aggregateReport.getId())) + .thenReturn(aggregateReport); + doReturn(HttpURLConnection.HTTP_OK) + .when(aggregateReportingJobHandler) + .makeHttpPostRequest(eq(REPORTING_URI), Mockito.any()); + + doNothing() + .when(mMeasurementDao) + .markAggregateReportStatus( + aggregateReport.getId(), AggregateReport.Status.DELIVERED); + ArgumentCaptor<JSONObject> aggregateReportBodyCaptor = + ArgumentCaptor.forClass(JSONObject.class); + + // Execution + Assert.assertEquals( + AdServicesStatusUtils.STATUS_SUCCESS, + aggregateReportingJobHandler.performReport( + aggregateReport.getId(), + AggregateCryptoFixture.getKey(), + new ReportingStatus())); + + // Assertion + verify(aggregateReportingJobHandler) + .makeHttpPostRequest(eq(REPORTING_URI), aggregateReportBodyCaptor.capture()); + verify(mTransaction, times(2)).begin(); + verify(mTransaction, times(2)).end(); + + JSONObject aggregateReportBody = aggregateReportBodyCaptor.getValue(); + JSONObject sharedInfo = + new JSONObject( + aggregateReportBody.getString( + AggregateReportBody.PayloadBodyKeys.SHARED_INFO)); + assertEquals( + expectedDebugMode, + sharedInfo.optString(AggregateReportBody.SharedInfoKeys.DEBUG_MODE)); + } + private static JSONObject createASampleAggregateReportBody(AggregateReport aggregateReport) throws JSONException { return new AggregateReportBody.Builder() diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/reporting/DebugReportApiTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/reporting/DebugReportApiTest.java index 8e1a178ab..5656ba007 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/reporting/DebugReportApiTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/reporting/DebugReportApiTest.java @@ -92,6 +92,12 @@ public final class DebugReportApiTest { when(mFlags.getMeasurementEnableDebugReport()).thenReturn(true); when(mFlags.getMeasurementEnableSourceDebugReport()).thenReturn(true); when(mFlags.getMeasurementEnableTriggerDebugReport()).thenReturn(true); + when(mFlags.getMeasurementVtcConfigurableMaxEventReportsCount()) + .thenReturn(Flags.DEFAULT_MEASUREMENT_VTC_CONFIGURABLE_MAX_EVENT_REPORTS_COUNT); + when(mFlags.getMeasurementEventReportsVtcEarlyReportingWindows()) + .thenReturn(Flags.MEASUREMENT_EVENT_REPORTS_VTC_EARLY_REPORTING_WINDOWS); + when(mFlags.getMeasurementEventReportsCtcEarlyReportingWindows()) + .thenReturn(Flags.MEASUREMENT_EVENT_REPORTS_CTC_EARLY_REPORTING_WINDOWS); } @Test @@ -3650,6 +3656,194 @@ public final class DebugReportApiTest { verify(mMeasurementDao, never()).insertDebugReport(any()); } + @Test + public void testScheduleTriggerEventWindowNotStartedDebugReport_triggerNotOpIn_dontSchedule() + throws Exception { + Source source = + SourceFixture.getMinimalValidSourceBuilder() + .setEventId(SOURCE_EVENT_ID) + .setIsDebugReporting(true) + .setAdIdPermission(true) + .build(); + Trigger trigger = + TriggerFixture.getValidTriggerBuilder() + .setIsDebugReporting(false) + .setAdIdPermission(true) + .build(); + ExtendedMockito.doNothing() + .when(() -> VerboseDebugReportingJobService.scheduleIfNeeded(any(), anyBoolean())); + + mDebugReportApi.scheduleTriggerDebugReport( + source, + trigger, + /* limit =*/ null, + mMeasurementDao, + DebugReportApi.Type.TRIGGER_EVENT_REPORT_WINDOW_NOT_STARTED); + verify(mMeasurementDao, never()).insertDebugReport(any()); + } + + @Test + public void testScheduleTriggerEventWindowNotStartedDebugReport_sourceNoAdId_dontSchedule() + throws Exception { + Source source = + SourceFixture.getMinimalValidSourceBuilder() + .setEventId(SOURCE_EVENT_ID) + .setIsDebugReporting(true) + .setAdIdPermission(false) + .build(); + Trigger trigger = + TriggerFixture.getValidTriggerBuilder() + .setIsDebugReporting(true) + .setAdIdPermission(true) + .build(); + ExtendedMockito.doNothing() + .when(() -> VerboseDebugReportingJobService.scheduleIfNeeded(any(), anyBoolean())); + + mDebugReportApi.scheduleTriggerDebugReport( + source, + trigger, + /* limit =*/ null, + mMeasurementDao, + DebugReportApi.Type.TRIGGER_EVENT_REPORT_WINDOW_NOT_STARTED); + verify(mMeasurementDao, never()).insertDebugReport(any()); + } + + @Test + public void testScheduleTriggerEventWindowNotStartedDebugReport_sourceNoArDebug_dontSchedule() + throws Exception { + Source source = + SourceFixture.getMinimalValidSourceBuilder() + .setEventId(SOURCE_EVENT_ID) + .setIsDebugReporting(true) + .setPublisherType(EventSurfaceType.WEB) + .setPublisher(SourceFixture.ValidSourceParams.WEB_PUBLISHER) + .setAppDestinations(null) + .setWebDestinations(SourceFixture.ValidSourceParams.WEB_DESTINATIONS) + .setArDebugPermission(false) + .build(); + Trigger trigger = + TriggerFixture.getValidTriggerBuilder() + .setIsDebugReporting(true) + .setDestinationType(EventSurfaceType.WEB) + .setAttributionDestination( + SourceFixture.ValidSourceParams.WEB_DESTINATIONS.get(0)) + .setArDebugPermission(true) + .build(); + ExtendedMockito.doNothing() + .when(() -> VerboseDebugReportingJobService.scheduleIfNeeded(any(), anyBoolean())); + + mDebugReportApi.scheduleTriggerDebugReport( + source, + trigger, + /* limit =*/ null, + mMeasurementDao, + DebugReportApi.Type.TRIGGER_EVENT_REPORT_WINDOW_NOT_STARTED); + verify(mMeasurementDao, never()).insertDebugReport(any()); + } + + @Test + public void testScheduleTriggerEventWindowNotStartedDebugReport_success() throws Exception { + Source source = + SourceFixture.getValidSourceBuilder() + .setEventId(SOURCE_EVENT_ID) + .setIsDebugReporting(true) + .setAdIdPermission(true) + .build(); + Trigger trigger = + TriggerFixture.getValidTriggerBuilder() + .setIsDebugReporting(true) + .setAdIdPermission(true) + .build(); + ExtendedMockito.doNothing() + .when(() -> VerboseDebugReportingJobService.scheduleIfNeeded(any(), anyBoolean())); + + mDebugReportApi.scheduleTriggerDebugReport( + source, + trigger, + /* limit =*/ null, + mMeasurementDao, + DebugReportApi.Type.TRIGGER_EVENT_REPORT_WINDOW_NOT_STARTED); + verify(mMeasurementDao, times(1)).insertDebugReport(any()); + } + + @Test + public void testScheduleTriggerEventWindowNotStartedDebugReport_debugFlagDisabled_dontSchedule() + throws Exception { + when(mFlags.getMeasurementEnableDebugReport()).thenReturn(false); + Source source = + SourceFixture.getValidSourceBuilder() + .setEventId(SOURCE_EVENT_ID) + .setIsDebugReporting(true) + .setAdIdPermission(true) + .build(); + Trigger trigger = + TriggerFixture.getValidTriggerBuilder() + .setIsDebugReporting(true) + .setAdIdPermission(true) + .build(); + ExtendedMockito.doNothing() + .when(() -> VerboseDebugReportingJobService.scheduleIfNeeded(any(), anyBoolean())); + + mDebugReportApi.scheduleTriggerDebugReport( + source, + trigger, + /* limit =*/ null, + mMeasurementDao, + DebugReportApi.Type.TRIGGER_EVENT_REPORT_WINDOW_NOT_STARTED); + verify(mMeasurementDao, never()).insertDebugReport(any()); + } + + @Test + public void + testScheduleTriggerEventWindowNotStartedDebugReport_triggerFlagDisabled_dontSchedule() + throws Exception { + when(mFlags.getMeasurementEnableTriggerDebugReport()).thenReturn(false); + Source source = + SourceFixture.getValidSourceBuilder() + .setEventId(SOURCE_EVENT_ID) + .setIsDebugReporting(true) + .setAdIdPermission(true) + .build(); + Trigger trigger = + TriggerFixture.getValidTriggerBuilder() + .setIsDebugReporting(true) + .setAdIdPermission(true) + .build(); + ExtendedMockito.doNothing() + .when(() -> VerboseDebugReportingJobService.scheduleIfNeeded(any(), anyBoolean())); + + mDebugReportApi.scheduleTriggerDebugReport( + source, + trigger, + /* limit =*/ null, + mMeasurementDao, + DebugReportApi.Type.TRIGGER_EVENT_REPORT_WINDOW_NOT_STARTED); + verify(mMeasurementDao, never()).insertDebugReport(any()); + } + + @Test + public void + testScheduleTriggerEventWindowNotStartedDebugReport_noTriggerPermission_dontSchedule() + throws Exception { + Source source = + SourceFixture.getMinimalValidSourceBuilder() + .setEventId(SOURCE_EVENT_ID) + .setIsDebugReporting(true) + .setAdIdPermission(true) + .build(); + Trigger trigger = TriggerFixture.getValidTriggerBuilder().setIsDebugReporting(true).build(); + ExtendedMockito.doNothing() + .when(() -> VerboseDebugReportingJobService.scheduleIfNeeded(any(), anyBoolean())); + + mDebugReportApi.scheduleTriggerDebugReport( + source, + trigger, + /* limit =*/ null, + mMeasurementDao, + DebugReportApi.Type.TRIGGER_EVENT_REPORT_WINDOW_NOT_STARTED); + verify(mMeasurementDao, never()).insertDebugReport(any()); + } + private static void assertSourceDebugReportParameters( DebugReport actualReport, String expectedReportType, diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/reporting/EventReportWindowCalcDelegateTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/reporting/EventReportWindowCalcDelegateTest.java index 713ce8e98..756c5434c 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/reporting/EventReportWindowCalcDelegateTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/measurement/reporting/EventReportWindowCalcDelegateTest.java @@ -61,6 +61,9 @@ public class EventReportWindowCalcDelegateTest { private static final String EVENT_REPORT_WINDOWS_2_WINDOWS_NO_START = "{'end_times': [172800000, 432000000]}"; + private static final String EVENT_REPORT_WINDOWS_2_WINDOWS_WITH_START = + "{ 'start_time': 3600000, 'end_times': [86400000, 1728000000]}"; + private static final String EVENT_REPORT_WINDOWS_5_WINDOWS_WITH_START = "{'start_time': 86400000, 'end_times': [172800000, 432000000, 604800000, 864000000," + " 1728000000]}"; @@ -71,9 +74,14 @@ public class EventReportWindowCalcDelegateTest { @Before public void setup() { - doReturn(false).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn(MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS) .when(mFlags).getMeasurementMinEventReportDelayMillis(); + doReturn(Flags.DEFAULT_MEASUREMENT_VTC_CONFIGURABLE_MAX_EVENT_REPORTS_COUNT) + .when(mFlags).getMeasurementVtcConfigurableMaxEventReportsCount(); + doReturn(Flags.MEASUREMENT_EVENT_REPORTS_VTC_EARLY_REPORTING_WINDOWS) + .when(mFlags).getMeasurementEventReportsVtcEarlyReportingWindows(); + doReturn(Flags.MEASUREMENT_EVENT_REPORTS_CTC_EARLY_REPORTING_WINDOWS) + .when(mFlags).getMeasurementEventReportsCtcEarlyReportingWindows(); mEventReportWindowCalcDelegate = new EventReportWindowCalcDelegate(mFlags); } @@ -85,6 +93,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) + .setExpiryTime(expiryTime) .setEventReportWindow(expiryTime) .setEventTime(sourceEventTime) .build(); @@ -104,6 +113,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) + .setExpiryTime(expiryTime) .setEventReportWindow(expiryTime) .setEventTime(sourceEventTime) .build(); @@ -121,6 +131,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) + .setExpiryTime(expiryTime) .setEventReportWindow(expiryTime) .setEventTime(sourceEventTime) .setInstallAttributed(true) @@ -141,6 +152,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) + .setExpiryTime(expiryTime) .setEventReportWindow(expiryTime) .setEventTime(sourceEventTime) .setInstallAttributed(true) @@ -159,6 +171,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) + .setExpiryTime(expiryTime) .setEventReportWindow(expiryTime) .setEventTime(sourceEventTime) .setInstallAttributed(true) @@ -177,6 +190,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) + .setExpiryTime(expiryTime) .setEventReportWindow(expiryTime) .setEventTime(sourceEventTime) .setInstallAttributed(true) @@ -195,6 +209,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) + .setExpiryTime(expiryTime) .setEventReportWindow(expiryTime) .setEventTime(sourceEventTime) .build(); @@ -212,6 +227,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.NAVIGATION) + .setExpiryTime(sourceExpiryTime) .setEventReportWindow(sourceExpiryTime) .setEventTime(sourceEventTime) .build(); @@ -231,6 +247,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.NAVIGATION) + .setExpiryTime(sourceExpiryTime) .setEventReportWindow(sourceExpiryTime) .setEventTime(sourceEventTime) .build(); @@ -250,6 +267,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.NAVIGATION) + .setExpiryTime(sourceExpiryTime) .setEventReportWindow(sourceExpiryTime) .setEventTime(sourceEventTime) .build(); @@ -267,6 +285,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.NAVIGATION) + .setExpiryTime(sourceExpiryTime) .setEventReportWindow(sourceExpiryTime) .setEventTime(sourceEventTime) .build(); @@ -278,6 +297,9 @@ public class EventReportWindowCalcDelegateTest { @Test public void testMaxReportCount() { + doReturn(Flags.DEFAULT_MEASUREMENT_VTC_CONFIGURABLE_MAX_EVENT_REPORTS_COUNT) + .when(mFlags).getMeasurementVtcConfigurableMaxEventReportsCount(); + Source eventSourceInstallNotAttributed = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) @@ -285,12 +307,7 @@ public class EventReportWindowCalcDelegateTest { .build(); assertEquals( PrivacyParams.EVENT_SOURCE_MAX_REPORTS, - mEventReportWindowCalcDelegate.getMaxReportCount( - eventSourceInstallNotAttributed, false)); - assertEquals( - PrivacyParams.EVENT_SOURCE_MAX_REPORTS, - mEventReportWindowCalcDelegate.getMaxReportCount( - eventSourceInstallNotAttributed, false)); + mEventReportWindowCalcDelegate.getMaxReportCount(eventSourceInstallNotAttributed)); Source navigationSourceInstallNotAttributed = SourceFixture.getMinimalValidSourceBuilder() @@ -300,46 +317,33 @@ public class EventReportWindowCalcDelegateTest { assertEquals( PrivacyParams.NAVIGATION_SOURCE_MAX_REPORTS, mEventReportWindowCalcDelegate.getMaxReportCount( - navigationSourceInstallNotAttributed, false)); - assertEquals( - PrivacyParams.NAVIGATION_SOURCE_MAX_REPORTS, - mEventReportWindowCalcDelegate.getMaxReportCount( - navigationSourceInstallNotAttributed, false)); + navigationSourceInstallNotAttributed)); Source eventSourceInstallAttributed = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) .setInstallAttributed(true) + .setInstallCooldownWindow(1L) .build(); assertEquals( PrivacyParams.INSTALL_ATTR_EVENT_SOURCE_MAX_REPORTS, - mEventReportWindowCalcDelegate.getMaxReportCount( - eventSourceInstallAttributed, true)); - // Install attribution state does not matter for web destination - assertEquals( - PrivacyParams.EVENT_SOURCE_MAX_REPORTS, - mEventReportWindowCalcDelegate.getMaxReportCount( - eventSourceInstallAttributed, false)); + mEventReportWindowCalcDelegate.getMaxReportCount(eventSourceInstallAttributed)); Source navigationSourceInstallAttributed = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.NAVIGATION) .setInstallAttributed(true) + .setInstallCooldownWindow(1L) .build(); assertEquals( PrivacyParams.NAVIGATION_SOURCE_MAX_REPORTS, mEventReportWindowCalcDelegate.getMaxReportCount( - navigationSourceInstallAttributed, true)); - assertEquals( - PrivacyParams.NAVIGATION_SOURCE_MAX_REPORTS, - mEventReportWindowCalcDelegate.getMaxReportCount( - navigationSourceInstallAttributed, true)); + navigationSourceInstallAttributed)); } @Test public void getMaxReportCount_configuredConversionsNonInstall_returnsConfiguredCount() { // Setup - doReturn(true).when(mFlags).getMeasurementEnableVtcConfigurableMaxEventReports(); doReturn(3).when(mFlags).getMeasurementVtcConfigurableMaxEventReportsCount(); Source nonInstallEventSource = SourceFixture.getMinimalValidSourceBuilder() @@ -349,13 +353,12 @@ public class EventReportWindowCalcDelegateTest { // Execution & assertion Assert.assertEquals( - 3, mEventReportWindowCalcDelegate.getMaxReportCount(nonInstallEventSource, false)); + 3, mEventReportWindowCalcDelegate.getMaxReportCount(nonInstallEventSource)); } @Test public void getMaxReportCount_configuredConversionsInstallCase_returnsConfiguredCount() { // Setup - doReturn(true).when(mFlags).getMeasurementEnableVtcConfigurableMaxEventReports(); doReturn(2).when(mFlags).getMeasurementVtcConfigurableMaxEventReportsCount(); Source installEventSource = SourceFixture.getMinimalValidSourceBuilder() @@ -365,29 +368,28 @@ public class EventReportWindowCalcDelegateTest { // Execution & assertion Assert.assertEquals( - 2, mEventReportWindowCalcDelegate.getMaxReportCount(installEventSource, true)); + 2, mEventReportWindowCalcDelegate.getMaxReportCount(installEventSource)); } @Test public void getMaxReportCount_configuredConversionsToOneInstallCase_incrementConfiguredCount() { // Setup - doReturn(true).when(mFlags).getMeasurementEnableVtcConfigurableMaxEventReports(); doReturn(1).when(mFlags).getMeasurementVtcConfigurableMaxEventReportsCount(); Source installEventSource = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) .setInstallAttributed(true) + .setInstallCooldownWindow(1L) .build(); // Execution & assertion Assert.assertEquals( - 2, mEventReportWindowCalcDelegate.getMaxReportCount(installEventSource, true)); + 2, mEventReportWindowCalcDelegate.getMaxReportCount(installEventSource)); } @Test public void getMaxReportCount_configuredConversionsToOneInstallCase_noEffectOnCtc() { // Setup - doReturn(true).when(mFlags).getMeasurementEnableVtcConfigurableMaxEventReports(); doReturn(2).when(mFlags).getMeasurementVtcConfigurableMaxEventReportsCount(); Source navigationSource = SourceFixture.getMinimalValidSourceBuilder() @@ -397,7 +399,7 @@ public class EventReportWindowCalcDelegateTest { // Execution & assertion Assert.assertEquals( - 3, mEventReportWindowCalcDelegate.getMaxReportCount(navigationSource, false)); + 3, mEventReportWindowCalcDelegate.getMaxReportCount(navigationSource)); } @Test @@ -409,48 +411,51 @@ public class EventReportWindowCalcDelegateTest { SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) .setEventTime(eventTime) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(30)) .setEventReportWindow(eventTime + TimeUnit.DAYS.toMillis(10)) .build(); assertEquals( eventTime + TimeUnit.DAYS.toMillis(10) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - eventSource10d, /* windowIndex= */ 0, /* isInstallCase */ false)); + eventSource10d, /* windowIndex= */ 0)); assertEquals( eventTime + TimeUnit.DAYS.toMillis(10) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - eventSource10d, /* windowIndex= */ 1, /* isInstallCase */ false)); + eventSource10d, /* windowIndex= */ 1)); // Expected: 1 window at expiry Source eventSource7d = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) .setEventTime(eventTime) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(30)) .setEventReportWindow(eventTime + TimeUnit.DAYS.toMillis(7)) .build(); assertEquals( eventTime + TimeUnit.DAYS.toMillis(7) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - eventSource7d, /* windowIndex= */ 0, /* isInstallCase */ false)); + eventSource7d, /* windowIndex= */ 0)); assertEquals( eventTime + TimeUnit.DAYS.toMillis(7) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - eventSource7d, /* windowIndex= */ 1, /* isInstallCase */ false)); + eventSource7d, /* windowIndex= */ 1)); // Expected: 1 window at expiry Source eventSource2d = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) .setEventTime(eventTime) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(30)) .setEventReportWindow(eventTime + TimeUnit.DAYS.toMillis(2)) .build(); assertEquals( eventTime + TimeUnit.DAYS.toMillis(2) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - eventSource2d, /* windowIndex= */ 0, /* isInstallCase */ false)); + eventSource2d, /* windowIndex= */ 0)); assertEquals( eventTime + TimeUnit.DAYS.toMillis(2) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - eventSource2d, /* windowIndex= */ 1, /* isInstallCase */ false)); + eventSource2d, /* windowIndex= */ 1)); } @Test @@ -465,21 +470,21 @@ public class EventReportWindowCalcDelegateTest { SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) .setEventTime(eventTime) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(30)) .setEventReportWindow(eventTime + TimeUnit.DAYS.toMillis(10)) .build(); assertEquals( eventTime + TimeUnit.DAYS.toMillis(10) + minDelayMillis, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - eventSource10d, /* windowIndex= */ 0, /* isInstallCase */ false)); + eventSource10d, /* windowIndex= */ 0)); assertEquals( eventTime + TimeUnit.DAYS.toMillis(10) + minDelayMillis, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - eventSource10d, /* windowIndex= */ 1, /* isInstallCase */ false)); + eventSource10d, /* windowIndex= */ 1)); } @Test public void getReportingTimeForNoising_eventSrcWithConfiguredReportingWindows() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn(VALID_1H_1D_WINDOW_CONFIG) .when(mFlags) .getMeasurementEventReportsVtcEarlyReportingWindows(); @@ -493,27 +498,27 @@ public class EventReportWindowCalcDelegateTest { SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) .setEventTime(eventTime) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(30)) .setEventReportWindow(eventTime + TimeUnit.DAYS.toMillis(10)) .build(); assertEquals( eventTime + TimeUnit.HOURS.toMillis(1) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - eventSource10d, /* windowIndex= */ 0, /* isInstallCase */ false)); + eventSource10d, /* windowIndex= */ 0)); assertEquals( eventTime + TimeUnit.DAYS.toMillis(1) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - eventSource10d, /* windowIndex= */ 1, /* isInstallCase */ false)); + eventSource10d, /* windowIndex= */ 1)); assertEquals( eventTime + TimeUnit.DAYS.toMillis(10) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - eventSource10d, /* windowIndex= */ 2, /* isInstallCase */ false)); + eventSource10d, /* windowIndex= */ 2)); } @Test public void getReportingTimeForNoising_eventSrcInstallAttWithConfiguredReportingWindows() { // Addition another window for install attribution is ignored when configurable windows // are applied. - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn(VALID_1H_1D_WINDOW_CONFIG) .when(mFlags) .getMeasurementEventReportsVtcEarlyReportingWindows(); @@ -528,28 +533,28 @@ public class EventReportWindowCalcDelegateTest { SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) .setEventTime(eventTime) + .setExpiryTime(expiry) .setEventReportWindow(expiry) .setInstallCooldownWindow(expiry) .build(); assertEquals( eventTime + TimeUnit.HOURS.toMillis(1) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - eventSource10d, /* windowIndex= */ 0, /* isInstallCase */ true)); + eventSource10d, /* windowIndex= */ 0)); assertEquals( eventTime + TimeUnit.DAYS.toMillis(1) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - eventSource10d, /* windowIndex= */ 1, /* isInstallCase */ true)); + eventSource10d, /* windowIndex= */ 1)); assertEquals( eventTime + TimeUnit.DAYS.toMillis(10) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - eventSource10d, /* windowIndex= */ 2, /* isInstallCase */ true)); + eventSource10d, /* windowIndex= */ 2)); } @Test public void getReportingTimeForNoising_navigationSrcWithConfiguredReportingWindows() { // Addition another window for install attribution is ignored when configurable windows // are applied. - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn(VALID_1H_1D_WINDOW_CONFIG) .when(mFlags) .getMeasurementEventReportsVtcEarlyReportingWindows(); @@ -563,27 +568,27 @@ public class EventReportWindowCalcDelegateTest { SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.NAVIGATION) .setEventTime(eventTime) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(30)) .setEventReportWindow(eventTime + TimeUnit.DAYS.toMillis(10)) .build(); assertEquals( eventTime + TimeUnit.HOURS.toMillis(2) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - eventSource10d, /* windowIndex= */ 0, /* isInstallCase */ false)); + eventSource10d, /* windowIndex= */ 0)); assertEquals( eventTime + TimeUnit.DAYS.toMillis(2) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - eventSource10d, /* windowIndex= */ 1, /* isInstallCase */ false)); + eventSource10d, /* windowIndex= */ 1)); assertEquals( eventTime + TimeUnit.DAYS.toMillis(10) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - eventSource10d, /* windowIndex= */ 2, /* isInstallCase */ false)); + eventSource10d, /* windowIndex= */ 2)); } @Test public void getReportingTimeForNoising_navigationSrcInstallAttWithConfiguredReportingWindows() { // Addition another window for install attribution is ignored when configurable windows // are applied. - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn(VALID_1H_1D_WINDOW_CONFIG) .when(mFlags) .getMeasurementEventReportsVtcEarlyReportingWindows(); @@ -598,21 +603,22 @@ public class EventReportWindowCalcDelegateTest { SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.NAVIGATION) .setEventTime(eventTime) + .setExpiryTime(expiry) .setEventReportWindow(expiry) .setInstallCooldownWindow(expiry) .build(); assertEquals( eventTime + TimeUnit.HOURS.toMillis(2) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - eventSource10d, /* windowIndex= */ 0, /* isInstallCase */ true)); + eventSource10d, /* windowIndex= */ 0)); assertEquals( eventTime + TimeUnit.DAYS.toMillis(2) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - eventSource10d, /* windowIndex= */ 1, /* isInstallCase */ true)); + eventSource10d, /* windowIndex= */ 1)); assertEquals( eventTime + TimeUnit.DAYS.toMillis(10) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - eventSource10d, /* windowIndex= */ 2, /* isInstallCase */ true)); + eventSource10d, /* windowIndex= */ 2)); } @Test @@ -625,36 +631,38 @@ public class EventReportWindowCalcDelegateTest { .setSourceType(Source.SourceType.EVENT) .setInstallCooldownWindow(TimeUnit.DAYS.toMillis(1)) .setEventTime(eventTime) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(30)) .setEventReportWindow(eventTime + TimeUnit.DAYS.toMillis(10)) .build(); assertEquals( eventTime + TimeUnit.DAYS.toMillis(2) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - eventSource10d, /* windowIndex= */ 0, /* isInstallCase */ true)); + eventSource10d, /* windowIndex= */ 0)); assertEquals( eventTime + TimeUnit.DAYS.toMillis(10) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - eventSource10d, /* windowIndex= */ 1, /* isInstallCase */ true)); + eventSource10d, /* windowIndex= */ 1)); assertEquals( eventTime + TimeUnit.DAYS.toMillis(10) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - eventSource10d, /* windowIndex= */ 2, /* isInstallCase */ true)); + eventSource10d, /* windowIndex= */ 2)); // Expected: 1 window at 2d(expiry) Source eventSource2d = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) .setEventTime(eventTime) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(30)) .setEventReportWindow(eventTime + TimeUnit.DAYS.toMillis(2)) .build(); assertEquals( eventTime + TimeUnit.DAYS.toMillis(2) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - eventSource2d, /* windowIndex= */ 0, /* isInstallCase */ true)); + eventSource2d, /* windowIndex= */ 0)); assertEquals( eventTime + TimeUnit.DAYS.toMillis(2) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - eventSource2d, /* windowIndex= */ 1, /* isInstallCase */ true)); + eventSource2d, /* windowIndex= */ 1)); } @Test @@ -666,56 +674,59 @@ public class EventReportWindowCalcDelegateTest { SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.NAVIGATION) .setEventTime(eventTime) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(30)) .setEventReportWindow(eventTime + TimeUnit.DAYS.toMillis(20)) .build(); assertEquals( eventTime + TimeUnit.DAYS.toMillis(2) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - navigationSource20d, /* windowIndex= */ 0, /* isInstallCase */ false)); + navigationSource20d, /* windowIndex= */ 0)); assertEquals( eventTime + TimeUnit.DAYS.toMillis(7) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - navigationSource20d, /* windowIndex= */ 1, /* isInstallCase */ false)); + navigationSource20d, /* windowIndex= */ 1)); assertEquals( eventTime + TimeUnit.DAYS.toMillis(20) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - navigationSource20d, /* windowIndex= */ 2, /* isInstallCase */ false)); + navigationSource20d, /* windowIndex= */ 2)); // Expected: 2 windows at 2d & expiry(7d) Source navigationSource7d = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.NAVIGATION) .setEventTime(eventTime) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(30)) .setEventReportWindow(eventTime + TimeUnit.DAYS.toMillis(7)) .build(); assertEquals( eventTime + TimeUnit.DAYS.toMillis(2) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - navigationSource7d, /* windowIndex= */ 0, /* isInstallCase */ false)); + navigationSource7d, /* windowIndex= */ 0)); assertEquals( eventTime + TimeUnit.DAYS.toMillis(7) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - navigationSource7d, /* windowIndex= */ 1, /* isInstallCase */ false)); + navigationSource7d, /* windowIndex= */ 1)); assertEquals( eventTime + TimeUnit.DAYS.toMillis(7) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - navigationSource7d, /* windowIndex= */ 2, /* isInstallCase */ false)); + navigationSource7d, /* windowIndex= */ 2)); // Expected: 1 window at 2d(expiry) Source navigationSource2d = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.NAVIGATION) .setEventTime(eventTime) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(30)) .setEventReportWindow(eventTime + TimeUnit.DAYS.toMillis(2)) .build(); assertEquals( eventTime + TimeUnit.DAYS.toMillis(2) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - navigationSource2d, /* windowIndex= */ 0, /* isInstallCase */ false)); + navigationSource2d, /* windowIndex= */ 0)); assertEquals( eventTime + TimeUnit.DAYS.toMillis(2) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - navigationSource2d, /* windowIndex= */ 1, /* isInstallCase */ false)); + navigationSource2d, /* windowIndex= */ 1)); } @Test @@ -728,20 +739,21 @@ public class EventReportWindowCalcDelegateTest { .setSourceType(Source.SourceType.NAVIGATION) .setInstallCooldownWindow(TimeUnit.DAYS.toMillis(1)) .setEventTime(eventTime) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(30)) .setEventReportWindow(eventTime + TimeUnit.DAYS.toMillis(20)) .build(); assertEquals( eventTime + TimeUnit.DAYS.toMillis(2) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - navigationSource20d, /* windowIndex= */ 0, /* isInstallCase */ false)); + navigationSource20d, /* windowIndex= */ 0)); assertEquals( eventTime + TimeUnit.DAYS.toMillis(7) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - navigationSource20d, /* windowIndex= */ 1, /* isInstallCase */ false)); + navigationSource20d, /* windowIndex= */ 1)); assertEquals( eventTime + TimeUnit.DAYS.toMillis(20) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - navigationSource20d, /* windowIndex= */ 2, /* isInstallCase */ false)); + navigationSource20d, /* windowIndex= */ 2)); // Expected: 2 windows at 2d & expiry(7d) Source navigationSource7d = @@ -749,20 +761,21 @@ public class EventReportWindowCalcDelegateTest { .setSourceType(Source.SourceType.NAVIGATION) .setInstallCooldownWindow(TimeUnit.DAYS.toMillis(1)) .setEventTime(eventTime) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(30)) .setEventReportWindow(eventTime + TimeUnit.DAYS.toMillis(7)) .build(); assertEquals( eventTime + TimeUnit.DAYS.toMillis(2) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - navigationSource7d, /* windowIndex= */ 0, /* isInstallCase */ false)); + navigationSource7d, /* windowIndex= */ 0)); assertEquals( eventTime + TimeUnit.DAYS.toMillis(7) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - navigationSource7d, /* windowIndex= */ 1, /* isInstallCase */ false)); + navigationSource7d, /* windowIndex= */ 1)); assertEquals( eventTime + TimeUnit.DAYS.toMillis(7) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - navigationSource7d, /* windowIndex= */ 2, /* isInstallCase */ false)); + navigationSource7d, /* windowIndex= */ 2)); // Expected: 1 window at 2d(expiry) Source navigationSource2d = @@ -770,169 +783,21 @@ public class EventReportWindowCalcDelegateTest { .setSourceType(Source.SourceType.NAVIGATION) .setInstallCooldownWindow(TimeUnit.DAYS.toMillis(1)) .setEventTime(eventTime) + .setExpiryTime(eventTime + TimeUnit.DAYS.toMillis(30)) .setEventReportWindow(eventTime + TimeUnit.DAYS.toMillis(2)) .build(); assertEquals( eventTime + TimeUnit.DAYS.toMillis(2) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - navigationSource2d, /* windowIndex= */ 0, /* isInstallCase */ false)); + navigationSource2d, /* windowIndex= */ 0)); assertEquals( eventTime + TimeUnit.DAYS.toMillis(2) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - navigationSource2d, /* windowIndex= */ 1, /* isInstallCase */ false)); - } - - @Test - public void getReportingTime_nullWindowConfigEventSrcAppDest_fallbackToDefault() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); - doReturn(null).when(mFlags).getMeasurementEventReportsVtcEarlyReportingWindows(); - doReturn(null).when(mFlags).getMeasurementEventReportsCtcEarlyReportingWindows(); - long triggerTime = System.currentTimeMillis(); - long expiryTime = triggerTime + TimeUnit.DAYS.toMillis(30); - long sourceEventTime = triggerTime - TimeUnit.DAYS.toMillis(1); - Source source = - SourceFixture.getMinimalValidSourceBuilder() - .setSourceType(Source.SourceType.EVENT) - .setEventReportWindow(expiryTime) - .setEventTime(sourceEventTime) - .build(); - assertEquals( - expiryTime + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, - mEventReportWindowCalcDelegate.getReportingTime( - source, triggerTime, EventSurfaceType.APP)); - } - - @Test - public void getReportingTime_nullWindowConfigEventSrcInstallAttAppDestTrigger1stWindow() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); - doReturn(null).when(mFlags).getMeasurementEventReportsVtcEarlyReportingWindows(); - doReturn(null).when(mFlags).getMeasurementEventReportsCtcEarlyReportingWindows(); - long triggerTime = System.currentTimeMillis(); - long expiryTime = triggerTime + TimeUnit.DAYS.toMillis(30); - long sourceEventTime = triggerTime - TimeUnit.DAYS.toMillis(1); - Source source = - SourceFixture.getMinimalValidSourceBuilder() - .setSourceType(Source.SourceType.EVENT) - .setEventReportWindow(expiryTime) - .setEventTime(sourceEventTime) - .setInstallAttributed(true) - .build(); - assertEquals( - sourceEventTime - + PrivacyParams.INSTALL_ATTR_EVENT_EARLY_REPORTING_WINDOW_MILLISECONDS[0] - + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, - mEventReportWindowCalcDelegate.getReportingTime( - source, triggerTime, EventSurfaceType.APP)); - } - - @Test - public void getReportingTime_nullWindowConfigEventSrcInstallAttAppDestTrigger2ndWindow() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); - doReturn(null).when(mFlags).getMeasurementEventReportsVtcEarlyReportingWindows(); - doReturn(null).when(mFlags).getMeasurementEventReportsCtcEarlyReportingWindows(); - long triggerTime = System.currentTimeMillis(); - long expiryTime = triggerTime + TimeUnit.DAYS.toMillis(30); - long sourceEventTime = triggerTime - TimeUnit.DAYS.toMillis(3); - Source source = - SourceFixture.getMinimalValidSourceBuilder() - .setSourceType(Source.SourceType.EVENT) - .setEventReportWindow(expiryTime) - .setEventTime(sourceEventTime) - .setInstallAttributed(true) - .build(); - assertEquals( - expiryTime + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, - mEventReportWindowCalcDelegate.getReportingTime( - source, triggerTime, EventSurfaceType.APP)); - } - - @Test - public void getReportingTime_nullWindowConfigEventSrcInstallAttWebDestTrigger1stWindow() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); - doReturn(null).when(mFlags).getMeasurementEventReportsVtcEarlyReportingWindows(); - doReturn(null).when(mFlags).getMeasurementEventReportsCtcEarlyReportingWindows(); - long triggerTime = System.currentTimeMillis(); - long expiryTime = triggerTime + TimeUnit.DAYS.toMillis(30); - long sourceEventTime = triggerTime - TimeUnit.DAYS.toMillis(1); - Source source = - SourceFixture.getMinimalValidSourceBuilder() - .setSourceType(Source.SourceType.EVENT) - .setEventReportWindow(expiryTime) - .setEventTime(sourceEventTime) - .setInstallAttributed(true) - .build(); - assertEquals( - expiryTime + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, - mEventReportWindowCalcDelegate.getReportingTime( - source, triggerTime, EventSurfaceType.WEB)); - } - - @Test - public void getReportingTime_nullWindowConfigEventSrcInstallAttWebDestTrigger2ndWindow() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); - doReturn(null).when(mFlags).getMeasurementEventReportsVtcEarlyReportingWindows(); - doReturn(null).when(mFlags).getMeasurementEventReportsCtcEarlyReportingWindows(); - long triggerTime = System.currentTimeMillis(); - long expiryTime = triggerTime + TimeUnit.DAYS.toMillis(30); - long sourceEventTime = triggerTime - TimeUnit.DAYS.toMillis(3); - Source source = - SourceFixture.getMinimalValidSourceBuilder() - .setSourceType(Source.SourceType.EVENT) - .setEventReportWindow(expiryTime) - .setEventTime(sourceEventTime) - .setInstallAttributed(true) - .build(); - assertEquals( - expiryTime + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, - mEventReportWindowCalcDelegate.getReportingTime( - source, triggerTime, EventSurfaceType.WEB)); - } - - @Test - public void getReportingTime_nullWindowConfigEventSourceWebDestination_fallbackToDefault() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); - doReturn(null).when(mFlags).getMeasurementEventReportsVtcEarlyReportingWindows(); - doReturn(null).when(mFlags).getMeasurementEventReportsCtcEarlyReportingWindows(); - long triggerTime = System.currentTimeMillis(); - long expiryTime = triggerTime + TimeUnit.DAYS.toMillis(30); - long sourceEventTime = triggerTime - TimeUnit.DAYS.toMillis(1); - Source source = - SourceFixture.getMinimalValidSourceBuilder() - .setSourceType(Source.SourceType.EVENT) - .setEventReportWindow(expiryTime) - .setEventTime(sourceEventTime) - .build(); - assertEquals( - expiryTime + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, - mEventReportWindowCalcDelegate.getReportingTime( - source, triggerTime, EventSurfaceType.WEB)); - } - - @Test - public void getReportingTime_nullWindowConfigNavigationSourceTriggerInFirstWindow() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); - doReturn(null).when(mFlags).getMeasurementEventReportsVtcEarlyReportingWindows(); - doReturn(null).when(mFlags).getMeasurementEventReportsCtcEarlyReportingWindows(); - long triggerTime = System.currentTimeMillis(); - long sourceExpiryTime = triggerTime + TimeUnit.DAYS.toMillis(25); - long sourceEventTime = triggerTime - TimeUnit.DAYS.toMillis(1); - Source source = - SourceFixture.getMinimalValidSourceBuilder() - .setSourceType(Source.SourceType.NAVIGATION) - .setEventReportWindow(sourceExpiryTime) - .setEventTime(sourceEventTime) - .build(); - assertEquals( - sourceEventTime - + PrivacyParams.NAVIGATION_EARLY_REPORTING_WINDOW_MILLISECONDS[0] - + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, - mEventReportWindowCalcDelegate.getReportingTime( - source, triggerTime, EventSurfaceType.APP)); + navigationSource2d, /* windowIndex= */ 1)); } @Test public void getReportingTime_emptyWindowConfigNavigationSourceTriggerNextHour() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn("").when(mFlags).getMeasurementEventReportsVtcEarlyReportingWindows(); doReturn("").when(mFlags).getMeasurementEventReportsCtcEarlyReportingWindows(); long triggerTime = System.currentTimeMillis(); @@ -941,6 +806,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.NAVIGATION) + .setExpiryTime(sourceExpiryTime) .setEventReportWindow(sourceExpiryTime) .setEventTime(sourceEventTime) .build(); @@ -952,7 +818,6 @@ public class EventReportWindowCalcDelegateTest { @Test public void getReportingTime_emptyWindowConfigEventSrcTriggerNextHour() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn("").when(mFlags).getMeasurementEventReportsVtcEarlyReportingWindows(); doReturn("").when(mFlags).getMeasurementEventReportsCtcEarlyReportingWindows(); long triggerTime = System.currentTimeMillis(); @@ -963,6 +828,7 @@ public class EventReportWindowCalcDelegateTest { .setAppDestinations( SourceFixture.ValidSourceParams.ATTRIBUTION_DESTINATIONS) .setSourceType(Source.SourceType.EVENT) + .setExpiryTime(sourceExpiryTime) .setEventReportWindow(sourceExpiryTime) .setEventTime(sourceEventTime) .build(); @@ -974,7 +840,6 @@ public class EventReportWindowCalcDelegateTest { @Test public void getReportingTime_emptyWindowConfigEventSrcInstallAttTriggerNextHour() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn("").when(mFlags).getMeasurementEventReportsVtcEarlyReportingWindows(); doReturn("").when(mFlags).getMeasurementEventReportsCtcEarlyReportingWindows(); long triggerTime = System.currentTimeMillis(); @@ -985,6 +850,7 @@ public class EventReportWindowCalcDelegateTest { .setAppDestinations( SourceFixture.ValidSourceParams.ATTRIBUTION_DESTINATIONS) .setSourceType(Source.SourceType.EVENT) + .setExpiryTime(sourceExpiryTime) .setEventReportWindow(sourceExpiryTime) .setEventTime(sourceEventTime) .setInstallAttributed(true) @@ -999,7 +865,6 @@ public class EventReportWindowCalcDelegateTest { @Test public void getReportingTime_emptyWindowConfigNavigationSrcInstallAttTriggerNextHour() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn("").when(mFlags).getMeasurementEventReportsVtcEarlyReportingWindows(); doReturn("").when(mFlags).getMeasurementEventReportsCtcEarlyReportingWindows(); long triggerTime = System.currentTimeMillis(); @@ -1010,6 +875,7 @@ public class EventReportWindowCalcDelegateTest { .setAppDestinations( SourceFixture.ValidSourceParams.ATTRIBUTION_DESTINATIONS) .setSourceType(Source.SourceType.NAVIGATION) + .setExpiryTime(sourceExpiryTime) .setEventReportWindow(sourceExpiryTime) .setEventTime(sourceEventTime) .setInstallAttributed(true) @@ -1021,70 +887,7 @@ public class EventReportWindowCalcDelegateTest { } @Test - public void getReportingTime_nullWindowConfigNavigationSourceTriggerInSecondWindow() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); - doReturn(null).when(mFlags).getMeasurementEventReportsVtcEarlyReportingWindows(); - doReturn(null).when(mFlags).getMeasurementEventReportsCtcEarlyReportingWindows(); - long triggerTime = System.currentTimeMillis(); - long sourceExpiryTime = triggerTime + TimeUnit.DAYS.toMillis(25); - long sourceEventTime = triggerTime - TimeUnit.DAYS.toMillis(3); - Source source = - SourceFixture.getMinimalValidSourceBuilder() - .setSourceType(Source.SourceType.NAVIGATION) - .setEventReportWindow(sourceExpiryTime) - .setEventTime(sourceEventTime) - .build(); - assertEquals( - sourceEventTime - + PrivacyParams.NAVIGATION_EARLY_REPORTING_WINDOW_MILLISECONDS[1] - + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, - mEventReportWindowCalcDelegate.getReportingTime( - source, triggerTime, EventSurfaceType.APP)); - } - - @Test - public void getReportingTime_nullWindowConfigNavigationSecondExpiry() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); - doReturn(null).when(mFlags).getMeasurementEventReportsVtcEarlyReportingWindows(); - doReturn(null).when(mFlags).getMeasurementEventReportsCtcEarlyReportingWindows(); - long triggerTime = System.currentTimeMillis(); - long sourceExpiryTime = triggerTime + TimeUnit.DAYS.toMillis(2); - long sourceEventTime = triggerTime - TimeUnit.DAYS.toMillis(3); - Source source = - SourceFixture.getMinimalValidSourceBuilder() - .setSourceType(Source.SourceType.NAVIGATION) - .setEventReportWindow(sourceExpiryTime) - .setEventTime(sourceEventTime) - .build(); - assertEquals( - sourceExpiryTime + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, - mEventReportWindowCalcDelegate.getReportingTime( - source, triggerTime, EventSurfaceType.APP)); - } - - @Test - public void getReportingTime_nullWindowConfigNavigationLast_fallbackToDefault() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); - doReturn(null).when(mFlags).getMeasurementEventReportsVtcEarlyReportingWindows(); - doReturn(null).when(mFlags).getMeasurementEventReportsCtcEarlyReportingWindows(); - long triggerTime = System.currentTimeMillis(); - long sourceExpiryTime = triggerTime + TimeUnit.DAYS.toMillis(1); - long sourceEventTime = triggerTime - TimeUnit.DAYS.toMillis(20); - Source source = - SourceFixture.getMinimalValidSourceBuilder() - .setSourceType(Source.SourceType.NAVIGATION) - .setEventReportWindow(sourceExpiryTime) - .setEventTime(sourceEventTime) - .build(); - assertEquals( - sourceExpiryTime + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, - mEventReportWindowCalcDelegate.getReportingTime( - source, triggerTime, EventSurfaceType.APP)); - } - - @Test public void getReportingTime_MalformedWindowConfigEventSourceAppDestination_returnsDefault() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn(MALFORMED_WINDOW_CONFIG) .when(mFlags) .getMeasurementEventReportsVtcEarlyReportingWindows(); @@ -1097,6 +900,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) + .setExpiryTime(expiryTime) .setEventReportWindow(expiryTime) .setEventTime(sourceEventTime) .build(); @@ -1108,7 +912,6 @@ public class EventReportWindowCalcDelegateTest { @Test public void getReportingTime_MalformedWindowConfigEventSrcInstallAttAppDestTrigger1stWindow() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn(MALFORMED_WINDOW_CONFIG) .when(mFlags) .getMeasurementEventReportsVtcEarlyReportingWindows(); @@ -1121,6 +924,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) + .setExpiryTime(expiryTime) .setEventReportWindow(expiryTime) .setEventTime(sourceEventTime) .setInstallAttributed(true) @@ -1135,7 +939,6 @@ public class EventReportWindowCalcDelegateTest { @Test public void getReportingTime_MalformedWindowConfigEventSrcInstallAttAppDestTrigger2ndWindow() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn(MALFORMED_WINDOW_CONFIG) .when(mFlags) .getMeasurementEventReportsVtcEarlyReportingWindows(); @@ -1148,6 +951,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) + .setExpiryTime(expiryTime) .setEventReportWindow(expiryTime) .setEventTime(sourceEventTime) .setInstallAttributed(true) @@ -1160,7 +964,6 @@ public class EventReportWindowCalcDelegateTest { @Test public void getReportingTime_MalformedWindowConfigEventSrcInstallAttWebDestTrigger1stWindow() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn(MALFORMED_WINDOW_CONFIG) .when(mFlags) .getMeasurementEventReportsVtcEarlyReportingWindows(); @@ -1173,6 +976,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) + .setExpiryTime(expiryTime) .setEventReportWindow(expiryTime) .setEventTime(sourceEventTime) .setInstallAttributed(true) @@ -1185,7 +989,6 @@ public class EventReportWindowCalcDelegateTest { @Test public void getReportingTime_MalformedWindowConfigEventSrcInstallAttWebDestTrigger2ndWindow() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn(MALFORMED_WINDOW_CONFIG) .when(mFlags) .getMeasurementEventReportsVtcEarlyReportingWindows(); @@ -1198,6 +1001,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) + .setExpiryTime(expiryTime) .setEventReportWindow(expiryTime) .setEventTime(sourceEventTime) .setInstallAttributed(true) @@ -1210,7 +1014,6 @@ public class EventReportWindowCalcDelegateTest { @Test public void getReportingTime_MalformedWindowConfigEventSourceWebDestination() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn(MALFORMED_WINDOW_CONFIG) .when(mFlags) .getMeasurementEventReportsVtcEarlyReportingWindows(); @@ -1223,6 +1026,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) + .setExpiryTime(expiryTime) .setEventReportWindow(expiryTime) .setEventTime(sourceEventTime) .build(); @@ -1234,7 +1038,6 @@ public class EventReportWindowCalcDelegateTest { @Test public void getReportingTime_MalformedWindowConfigNavigationSourceTriggerInFirstWindow() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn(VALID_1H_1D_WINDOW_CONFIG) .when(mFlags) .getMeasurementEventReportsVtcEarlyReportingWindows(); @@ -1247,6 +1050,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.NAVIGATION) + .setExpiryTime(sourceExpiryTime) .setEventReportWindow(sourceExpiryTime) .setEventTime(sourceEventTime) .build(); @@ -1260,7 +1064,6 @@ public class EventReportWindowCalcDelegateTest { @Test public void getReportingTime_MalformedWindowConfigNavigationSourceTriggerInSecondWindow() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn(VALID_1H_1D_WINDOW_CONFIG) .when(mFlags) .getMeasurementEventReportsVtcEarlyReportingWindows(); @@ -1273,6 +1076,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.NAVIGATION) + .setExpiryTime(sourceExpiryTime) .setEventReportWindow(sourceExpiryTime) .setEventTime(sourceEventTime) .build(); @@ -1286,7 +1090,6 @@ public class EventReportWindowCalcDelegateTest { @Test public void getReportingTime_MalformedWindowConfigNavigationSecondExpiry_fallbackToDefault() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn(VALID_1H_1D_WINDOW_CONFIG) .when(mFlags) .getMeasurementEventReportsVtcEarlyReportingWindows(); @@ -1299,6 +1102,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.NAVIGATION) + .setExpiryTime(sourceExpiryTime) .setEventReportWindow(sourceExpiryTime) .setEventTime(sourceEventTime) .build(); @@ -1310,7 +1114,6 @@ public class EventReportWindowCalcDelegateTest { @Test public void getReportingTime_MalformedWindowConfigNavigationLast_fallbackToDefault() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn(VALID_1H_1D_WINDOW_CONFIG) .when(mFlags) .getMeasurementEventReportsVtcEarlyReportingWindows(); @@ -1323,6 +1126,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.NAVIGATION) + .setExpiryTime(sourceExpiryTime) .setEventReportWindow(sourceExpiryTime) .setEventTime(sourceEventTime) .build(); @@ -1334,7 +1138,6 @@ public class EventReportWindowCalcDelegateTest { @Test public void getReportingTime_validWindowConfigEventSourceTriggerIn1stWindow() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn(VALID_1H_1D_WINDOW_CONFIG) .when(mFlags) .getMeasurementEventReportsVtcEarlyReportingWindows(); @@ -1347,6 +1150,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) + .setExpiryTime(expiryTime) .setEventReportWindow(expiryTime) .setEventTime(sourceEventTime) .build(); @@ -1358,7 +1162,6 @@ public class EventReportWindowCalcDelegateTest { @Test public void getReportingTime_validWindowConfigEventSourceTriggerIn2ndWindow() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn(VALID_1H_1D_WINDOW_CONFIG) .when(mFlags) .getMeasurementEventReportsVtcEarlyReportingWindows(); @@ -1371,6 +1174,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) + .setExpiryTime(expiryTime) .setEventReportWindow(expiryTime) .setEventTime(sourceEventTime) .build(); @@ -1383,7 +1187,6 @@ public class EventReportWindowCalcDelegateTest { @Test public void getReportingTime_validWindowConfigEventSourceTriggerInLastWindow() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn(VALID_1H_1D_WINDOW_CONFIG) .when(mFlags) .getMeasurementEventReportsVtcEarlyReportingWindows(); @@ -1396,6 +1199,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) + .setExpiryTime(expiryTime) .setEventReportWindow(expiryTime) .setEventTime(sourceEventTime) .setInstallAttributed(true) @@ -1408,7 +1212,6 @@ public class EventReportWindowCalcDelegateTest { @Test public void getReportingTime_validWindowConfigNavigationSourceTriggerIn1stWindow() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn(VALID_1H_1D_WINDOW_CONFIG) .when(mFlags) .getMeasurementEventReportsVtcEarlyReportingWindows(); @@ -1421,6 +1224,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.NAVIGATION) + .setExpiryTime(expiryTime) .setEventReportWindow(expiryTime) .setEventTime(sourceEventTime) .build(); @@ -1432,7 +1236,6 @@ public class EventReportWindowCalcDelegateTest { @Test public void getReportingTime_validWindowConfigNavigationSourceTriggerIn2ndWindow() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn(VALID_1H_1D_WINDOW_CONFIG) .when(mFlags) .getMeasurementEventReportsVtcEarlyReportingWindows(); @@ -1445,6 +1248,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.NAVIGATION) + .setExpiryTime(expiryTime) .setEventReportWindow(expiryTime) .setEventTime(sourceEventTime) .build(); @@ -1457,7 +1261,6 @@ public class EventReportWindowCalcDelegateTest { @Test public void getReportingTime_validWindowConfigNavigationSourceTriggerInLastWindow() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn(MALFORMED_WINDOW_CONFIG) .when(mFlags) .getMeasurementEventReportsVtcEarlyReportingWindows(); @@ -1470,6 +1273,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.NAVIGATION) + .setExpiryTime(expiryTime) .setEventReportWindow(expiryTime) .setEventTime(sourceEventTime) .setInstallAttributed(true) @@ -1482,7 +1286,6 @@ public class EventReportWindowCalcDelegateTest { @Test public void getReportingTime_invalidWindowConfigEventSourceTriggerIn1stWindow() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn(INVALID_1H_1D_2D_WINDOW_CONFIG) .when(mFlags) .getMeasurementEventReportsVtcEarlyReportingWindows(); @@ -1495,6 +1298,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.EVENT) + .setExpiryTime(expiryTime) .setEventReportWindow(expiryTime) .setEventTime(sourceEventTime) .build(); @@ -1506,7 +1310,6 @@ public class EventReportWindowCalcDelegateTest { @Test public void getReportingTime_invalidWindowConfigNavigationSourceTriggerIn1stWindow() { - doReturn(true).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); doReturn(VALID_1H_1D_WINDOW_CONFIG) .when(mFlags) .getMeasurementEventReportsVtcEarlyReportingWindows(); @@ -1519,6 +1322,7 @@ public class EventReportWindowCalcDelegateTest { Source source = SourceFixture.getMinimalValidSourceBuilder() .setSourceType(Source.SourceType.NAVIGATION) + .setExpiryTime(expiryTime) .setEventReportWindow(expiryTime) .setEventTime(sourceEventTime) .build(); @@ -1713,49 +1517,49 @@ public class EventReportWindowCalcDelegateTest { assertEquals( sourceTime + TimeUnit.DAYS.toMillis(2) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - oneWindowNoStart, 0, false)); + oneWindowNoStart, 0)); // InstallCase doesn't affect the report time assertEquals( sourceTime + TimeUnit.DAYS.toMillis(2) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - oneWindowNoStart, 0, true)); + oneWindowNoStart, 0)); assertEquals( sourceTime + TimeUnit.DAYS.toMillis(2) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - oneWindowWithStart, 0, false)); + oneWindowWithStart, 0)); assertEquals( sourceTime + TimeUnit.DAYS.toMillis(2) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - twoWindowsNoStart, 0, false)); + twoWindowsNoStart, 0)); assertEquals( sourceTime + TimeUnit.DAYS.toMillis(5) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - twoWindowsNoStart, 1, false)); + twoWindowsNoStart, 1)); assertEquals( sourceTime + TimeUnit.DAYS.toMillis(2) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - fiveWindowsWithStart, 0, false)); + fiveWindowsWithStart, 0)); assertEquals( sourceTime + TimeUnit.DAYS.toMillis(5) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - fiveWindowsWithStart, 1, false)); + fiveWindowsWithStart, 1)); assertEquals( sourceTime + TimeUnit.DAYS.toMillis(7) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - fiveWindowsWithStart, 2, false)); + fiveWindowsWithStart, 2)); assertEquals( sourceTime + TimeUnit.DAYS.toMillis(10) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - fiveWindowsWithStart, 3, false)); + fiveWindowsWithStart, 3)); assertEquals( sourceTime + TimeUnit.DAYS.toMillis(20) + MEASUREMENT_MIN_EVENT_REPORT_DELAY_MILLIS, mEventReportWindowCalcDelegate.getReportingTimeForNoising( - fiveWindowsWithStart, 4, false)); + fiveWindowsWithStart, 4)); } @Test @@ -1863,7 +1667,8 @@ public class EventReportWindowCalcDelegateTest { @Test public void getMaxReportCount_flexLiteApi() { doReturn(true).when(mFlags).getMeasurementFlexLiteApiEnabled(); - doReturn(false).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); + doReturn(Flags.DEFAULT_MEASUREMENT_VTC_CONFIGURABLE_MAX_EVENT_REPORTS_COUNT) + .when(mFlags).getMeasurementVtcConfigurableMaxEventReportsCount(); long sourceTime = System.currentTimeMillis(); Source source10Reports = SourceFixture.getMinimalValidSourceBuilder() @@ -1871,10 +1676,20 @@ public class EventReportWindowCalcDelegateTest { .setExpiryTime(sourceTime + TimeUnit.DAYS.toMillis(30)) .setEventTime(sourceTime) .build(); - assertEquals(10, mEventReportWindowCalcDelegate.getMaxReportCount(source10Reports, true)); - assertEquals(10, mEventReportWindowCalcDelegate.getMaxReportCount(source10Reports, false)); + assertEquals(10, mEventReportWindowCalcDelegate.getMaxReportCount(source10Reports)); + assertEquals(10, mEventReportWindowCalcDelegate.getMaxReportCount(source10Reports)); - Source sourceDefaultEvent = + Source eventSourceInstallAttributed = + SourceFixture.getMinimalValidSourceBuilder() + .setEventReportWindows(EVENT_REPORT_WINDOWS_5_WINDOWS_WITH_START) + .setExpiryTime(sourceTime + TimeUnit.DAYS.toMillis(30)) + .setEventTime(sourceTime) + .setSourceType(Source.SourceType.EVENT) + .setInstallAttributed(true) + .setInstallCooldownWindow(1L) + .build(); + + Source eventSource = SourceFixture.getMinimalValidSourceBuilder() .setEventReportWindows(EVENT_REPORT_WINDOWS_5_WINDOWS_WITH_START) .setExpiryTime(sourceTime + TimeUnit.DAYS.toMillis(30)) @@ -1884,12 +1699,23 @@ public class EventReportWindowCalcDelegateTest { assertEquals( PrivacyParams.INSTALL_ATTR_EVENT_SOURCE_MAX_REPORTS, - mEventReportWindowCalcDelegate.getMaxReportCount(sourceDefaultEvent, true)); + mEventReportWindowCalcDelegate.getMaxReportCount( + eventSourceInstallAttributed)); assertEquals( PrivacyParams.EVENT_SOURCE_MAX_REPORTS, - mEventReportWindowCalcDelegate.getMaxReportCount(sourceDefaultEvent, false)); + mEventReportWindowCalcDelegate.getMaxReportCount(eventSource)); - Source sourceDefaultNavigation = + Source navigationSourceInstallAttributed = + SourceFixture.getMinimalValidSourceBuilder() + .setEventReportWindows(EVENT_REPORT_WINDOWS_5_WINDOWS_WITH_START) + .setExpiryTime(sourceTime + TimeUnit.DAYS.toMillis(30)) + .setEventTime(sourceTime) + .setSourceType(Source.SourceType.NAVIGATION) + .setInstallAttributed(true) + .setInstallCooldownWindow(1L) + .build(); + + Source navigationSource = SourceFixture.getMinimalValidSourceBuilder() .setEventReportWindows(EVENT_REPORT_WINDOWS_5_WINDOWS_WITH_START) .setExpiryTime(sourceTime + TimeUnit.DAYS.toMillis(30)) @@ -1898,18 +1724,27 @@ public class EventReportWindowCalcDelegateTest { .build(); assertEquals( - PrivacyParams.INSTALL_ATTR_NAVIGATION_SOURCE_MAX_REPORTS, - mEventReportWindowCalcDelegate.getMaxReportCount(sourceDefaultNavigation, true)); + PrivacyParams.NAVIGATION_SOURCE_MAX_REPORTS, + mEventReportWindowCalcDelegate.getMaxReportCount( + navigationSourceInstallAttributed)); assertEquals( PrivacyParams.NAVIGATION_SOURCE_MAX_REPORTS, - mEventReportWindowCalcDelegate.getMaxReportCount(sourceDefaultNavigation, false)); + mEventReportWindowCalcDelegate.getMaxReportCount(navigationSource)); } @Test public void getReportingWindowCountForNoising_flexLiteApi() { doReturn(true).when(mFlags).getMeasurementFlexLiteApiEnabled(); - doReturn(false).when(mFlags).getMeasurementEnableConfigurableEventReportingWindows(); long sourceTime = System.currentTimeMillis(); + Source defaultSourceEventInstallAttributed = + SourceFixture.getMinimalValidSourceBuilder() + .setExpiryTime(sourceTime + TimeUnit.DAYS.toMillis(30)) + .setEventTime(sourceTime) + .setSourceType(Source.SourceType.EVENT) + .setInstallAttributed(true) + .setInstallCooldownWindow(1L) + .build(); + Source defaultSourceEvent = SourceFixture.getMinimalValidSourceBuilder() .setExpiryTime(sourceTime + TimeUnit.DAYS.toMillis(30)) @@ -1952,56 +1787,110 @@ public class EventReportWindowCalcDelegateTest { assertEquals( 2, mEventReportWindowCalcDelegate.getReportingWindowCountForNoising( - defaultSourceEvent, true)); + defaultSourceEventInstallAttributed)); assertEquals( 1, mEventReportWindowCalcDelegate.getReportingWindowCountForNoising( - defaultSourceEvent, false)); + defaultSourceEvent)); assertEquals( 3, mEventReportWindowCalcDelegate.getReportingWindowCountForNoising( - defaultSourceNavigation, true)); + defaultSourceNavigation)); assertEquals( 3, mEventReportWindowCalcDelegate.getReportingWindowCountForNoising( - defaultSourceNavigation, false)); + defaultSourceNavigation)); // InstallCase doesn't affect the report count assertEquals( 1, mEventReportWindowCalcDelegate.getReportingWindowCountForNoising( - oneWindowNoStart, true)); + oneWindowNoStart)); assertEquals( 1, mEventReportWindowCalcDelegate.getReportingWindowCountForNoising( - oneWindowNoStart, false)); + oneWindowNoStart)); assertEquals( 1, mEventReportWindowCalcDelegate.getReportingWindowCountForNoising( - oneWindowWithStart, true)); + oneWindowWithStart)); assertEquals( 1, mEventReportWindowCalcDelegate.getReportingWindowCountForNoising( - oneWindowWithStart, false)); + oneWindowWithStart)); assertEquals( 2, mEventReportWindowCalcDelegate.getReportingWindowCountForNoising( - twoWindowsNoStart, true)); + twoWindowsNoStart)); assertEquals( 2, mEventReportWindowCalcDelegate.getReportingWindowCountForNoising( - twoWindowsNoStart, false)); + twoWindowsNoStart)); assertEquals( 5, mEventReportWindowCalcDelegate.getReportingWindowCountForNoising( - fiveWindowsWithStart, true)); + fiveWindowsWithStart)); assertEquals( 5, mEventReportWindowCalcDelegate.getReportingWindowCountForNoising( - fiveWindowsWithStart, false)); + fiveWindowsWithStart)); + } + + @Test + public void fallsWithinWindow_windowNotStarted() { + doReturn(true).when(mFlags).getMeasurementFlexLiteApiEnabled(); + long sourceTime = System.currentTimeMillis(); + long triggerTime = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(1) - 1; + Source source = + SourceFixture.getMinimalValidSourceBuilder() + .setSourceType(Source.SourceType.EVENT) + .setEventTime(sourceTime) + .setEventReportWindows(EVENT_REPORT_WINDOWS_2_WINDOWS_WITH_START) + .setExpiryTime(sourceTime + TimeUnit.DAYS.toMillis(25)) + .build(); + assertEquals( + EventReportWindowCalcDelegate.MomentPlacement.BEFORE, + mEventReportWindowCalcDelegate.fallsWithinWindow( + source, triggerTime, EventSurfaceType.APP)); + } + + @Test + public void fallsWithinWindow_windowWithin() { + doReturn(true).when(mFlags).getMeasurementFlexLiteApiEnabled(); + long sourceTime = System.currentTimeMillis(); + long triggerTime = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(1); + Source source = + SourceFixture.getMinimalValidSourceBuilder() + .setSourceType(Source.SourceType.EVENT) + .setEventTime(sourceTime) + .setEventReportWindows(EVENT_REPORT_WINDOWS_2_WINDOWS_WITH_START) + .setExpiryTime(sourceTime + TimeUnit.DAYS.toMillis(25)) + .build(); + assertEquals( + EventReportWindowCalcDelegate.MomentPlacement.WITHIN, + mEventReportWindowCalcDelegate.fallsWithinWindow( + source, triggerTime, EventSurfaceType.APP)); + } + + @Test + public void fallsWithinWindow_windowPassed() { + doReturn(true).when(mFlags).getMeasurementFlexLiteApiEnabled(); + long sourceTime = System.currentTimeMillis(); + long triggerTime = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(20); + Source source = + SourceFixture.getMinimalValidSourceBuilder() + .setSourceType(Source.SourceType.EVENT) + .setEventTime(sourceTime) + .setEventReportWindows(EVENT_REPORT_WINDOWS_2_WINDOWS_WITH_START) + .setExpiryTime(sourceTime + TimeUnit.DAYS.toMillis(25)) + .build(); + assertEquals( + EventReportWindowCalcDelegate.MomentPlacement.AFTER, + mEventReportWindowCalcDelegate.fallsWithinWindow( + source, triggerTime, EventSurfaceType.APP)); } } diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/shell/AdServicesShellCommandHandlerTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/shell/AdServicesShellCommandHandlerTest.java index 7341b1d33..0d2c08844 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/shell/AdServicesShellCommandHandlerTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/shell/AdServicesShellCommandHandlerTest.java @@ -70,14 +70,14 @@ public final class AdServicesShellCommandHandlerTest extends AdServicesExtendedM @Test public void testRunHelp() throws Exception { - String result = mCmd.runInvalid(CMD_HELP); + String result = mCmd.runValid(CMD_HELP); assertHelpContents(result); } @Test public void testRunHelpShort() throws Exception { - String result = mCmd.runInvalid(CMD_SHORT_HELP); + String result = mCmd.runValid(CMD_SHORT_HELP); assertHelpContents(result); } @@ -283,7 +283,10 @@ public final class AdServicesShellCommandHandlerTest extends AdServicesExtendedM private final StringWriter mOutStringWriter = new StringWriter(); private final PrintWriter mOut = new PrintWriter(mOutStringWriter); - public final AdServicesShellCommandHandler cmd = new AdServicesShellCommandHandler(mOut); + private final StringWriter mErrStringWriter = new StringWriter(); + private final PrintWriter mErr = new PrintWriter(mErrStringWriter); + public final AdServicesShellCommandHandler cmd = + new AdServicesShellCommandHandler(mOut, mErr); private boolean mOutCalled; @@ -302,7 +305,7 @@ public final class AdServicesShellCommandHandlerTest extends AdServicesExtendedM .that(result) .isAtLeast(0); - return getOut(); + return getResult(mOut, mOutStringWriter); } /** @@ -316,7 +319,7 @@ public final class AdServicesShellCommandHandlerTest extends AdServicesExtendedM .that(result) .isLessThan(0); - return getOut(); + return getResult(mErr, mErrStringWriter); } /** @@ -334,13 +337,13 @@ public final class AdServicesShellCommandHandlerTest extends AdServicesExtendedM * <p>Can only be called once per test, as there is no way to reset it, which could cause * confusion for the test developer. */ - String getOut() throws IOException { + String getResult(PrintWriter pw, StringWriter sw) throws IOException { if (mOutCalled) { throw new IllegalStateException("getOut() already called"); } - mOut.flush(); - String out = mOutStringWriter.toString(); - mOut.close(); + pw.flush(); + String out = sw.toString(); + pw.close(); mOutCalled = true; return out; } diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/shell/ShellCommandServiceImplTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/shell/ShellCommandServiceImplTest.java new file mode 100644 index 000000000..a07a5f650 --- /dev/null +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/shell/ShellCommandServiceImplTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2023 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.shell; + +import android.adservices.shell.IShellCommandCallback; +import android.adservices.shell.ShellCommandParam; +import android.adservices.shell.ShellCommandResult; +import android.os.IBinder; + +import com.android.adservices.common.AdServicesUnitTestCase; +import com.android.adservices.common.NoFailureSyncCallback; + +import org.junit.Test; + +public final class ShellCommandServiceImplTest extends AdServicesUnitTestCase { + + @Test + public void testRunShellCommand() throws Exception { + ShellCommandServiceImpl service = new ShellCommandServiceImpl(); + SyncIShellCommandCallback callback = new SyncIShellCommandCallback(); + + service.runShellCommand(new ShellCommandParam("echo", "xxx"), callback); + + ShellCommandResult response = callback.assertResultReceived(); + expect.withMessage("result").that(response.getResultCode()).isEqualTo(0); + expect.withMessage("out").that(response.getOut()).contains("xxx"); + expect.withMessage("err").that(response.getErr()).isEmpty(); + } + + @Test + public void testRunShellCommand_invalidCommand() throws Exception { + ShellCommandServiceImpl service = new ShellCommandServiceImpl(); + SyncIShellCommandCallback callback = new SyncIShellCommandCallback(); + + service.runShellCommand(new ShellCommandParam("invalid-cmd"), callback); + + ShellCommandResult response = callback.assertResultReceived(); + expect.withMessage("result").that(response.getResultCode()).isEqualTo(-1); + expect.withMessage("out").that(response.getOut()).isEmpty(); + expect.withMessage("err").that(response.getErr()).contains("Unknown command"); + } + + private static final class SyncIShellCommandCallback + extends NoFailureSyncCallback<ShellCommandResult> implements IShellCommandCallback { + + @Override + public void onResult(ShellCommandResult response) { + injectResult(response); + } + + @Override + public IBinder asBinder() { + return null; + } + } +} diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/PeriodicEncodingJobWorkerTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/PeriodicEncodingJobWorkerTest.java index 3be31f28b..1bf8fb9b9 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/PeriodicEncodingJobWorkerTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/PeriodicEncodingJobWorkerTest.java @@ -37,10 +37,12 @@ import android.adservices.common.CommonFixture; import com.android.adservices.concurrency.AdServicesExecutors; import com.android.adservices.data.signals.DBEncodedPayload; import com.android.adservices.data.signals.DBEncoderLogicMetadata; +import com.android.adservices.data.signals.DBSignalsUpdateMetadata; import com.android.adservices.data.signals.EncodedPayloadDao; import com.android.adservices.data.signals.EncoderLogicHandler; import com.android.adservices.data.signals.EncoderLogicMetadataDao; import com.android.adservices.data.signals.EncoderPersistenceDao; +import com.android.adservices.data.signals.ProtectedSignalsDao; import com.android.adservices.service.Flags; import com.android.adservices.service.adselection.AdSelectionScriptEngine; import com.android.adservices.service.devapi.DevContextFilter; @@ -113,6 +115,7 @@ public class PeriodicEncodingJobWorkerTest { @Mock private EncoderLogicMetadataDao mEncoderLogicMetadataDao; @Mock private EncoderPersistenceDao mEncoderPersistenceDao; @Mock private EncodedPayloadDao mEncodedPayloadDao; + @Mock private ProtectedSignalsDao mProtectedSignalsDao; @Mock private SignalsProviderImpl mSignalStorageManager; @Mock private AdSelectionScriptEngine mScriptEngine; @Mock private DevContextFilter mDevContextFilter; @@ -138,9 +141,9 @@ public class PeriodicEncodingJobWorkerTest { new PeriodicEncodingJobWorker( mEncoderLogicHandler, mEncoderLogicMetadataDao, - mEncoderPersistenceDao, mEncodedPayloadDao, mSignalStorageManager, + mProtectedSignalsDao, mScriptEngine, mBackgroundExecutor, mLightWeightExecutor, @@ -172,9 +175,9 @@ public class PeriodicEncodingJobWorkerTest { new PeriodicEncodingJobWorker( mEncoderLogicHandler, mEncoderLogicMetadataDao, - mEncoderPersistenceDao, mEncodedPayloadDao, mSignalStorageManager, + mProtectedSignalsDao, mScriptEngine, mBackgroundExecutor, mLightWeightExecutor, @@ -243,7 +246,8 @@ public class PeriodicEncodingJobWorkerTest { .get(5, TimeUnit.SECONDS)); assertEquals(IllegalStateException.class, e.getCause().getClass()); assertEquals(PAYLOAD_PERSISTENCE_ERROR_MSG, e.getCause().getMessage()); - Mockito.verifyZeroInteractions(mEncodedPayloadDao); + verify(mEncodedPayloadDao).getEncodedPayload(BUYER); + verifyNoMoreInteractions(mEncodedPayloadDao); } @Test @@ -267,7 +271,8 @@ public class PeriodicEncodingJobWorkerTest { }); assertEquals(IllegalStateException.class, e.getCause().getClass()); assertEquals(PAYLOAD_PERSISTENCE_ERROR_MSG, e.getCause().getMessage()); - Mockito.verifyZeroInteractions(mEncodedPayloadDao); + verify(mEncodedPayloadDao).getEncodedPayload(BUYER); + verifyNoMoreInteractions(mEncodedPayloadDao); } @Test @@ -328,7 +333,8 @@ public class PeriodicEncodingJobWorkerTest { stallEncodingLatch.getCount()); // e is TimeoutFuture$TimeoutFutureException which extends TimeoutException assertTrue(TimeoutException.class.isAssignableFrom(e.getCause().getClass())); - Mockito.verifyZeroInteractions(mEncodedPayloadDao); + verify(mEncodedPayloadDao).getEncodedPayload(BUYER); + verifyNoMoreInteractions(mEncodedPayloadDao); } @Test @@ -361,6 +367,8 @@ public class PeriodicEncodingJobWorkerTest { verify(mEncoderLogicHandler).getAllRegisteredEncoders(); verify(mEncoderLogicHandler).getEncoder(BUYER); verify(mSignalStorageManager).getSignals(BUYER); + verify(mEncodedPayloadDao, times(1)).getEncodedPayload(BUYER); + verify(mEncodedPayloadDao, times(1)).getEncodedPayload(BUYER_2); verify(mEncodedPayloadDao, times(1)).persistEncodedPayload(mEncodedPayloadCaptor.capture()); verify(mEncoderLogicHandler).updateEncoderFailedCount(BUYER_2, 1); assertEquals(BUYER, mEncodedPayloadCaptor.getValue().getBuyer()); @@ -368,7 +376,6 @@ public class PeriodicEncodingJobWorkerTest { assertEquals( getSetFromBytes(validResponse), getSetFromBytes(mEncodedPayloadCaptor.getValue().getEncodedPayload())); - verify(mEncoderLogicHandler).updateEncoderFailedCount(BUYER_2, 1); } @Test @@ -417,6 +424,39 @@ public class PeriodicEncodingJobWorkerTest { } @Test + public void testEncodeSignals_noUpdateToBuyer_skipEncoding() + throws ExecutionException, InterruptedException, TimeoutException { + when(mSignalStorageManager.getSignals(BUYER)).thenReturn(FAKE_SIGNALS); + when(mProtectedSignalsDao.getSignalsUpdateMetadata(BUYER)) + .thenReturn( + DBSignalsUpdateMetadata.builder() + .setBuyer(BUYER) + .setLastSignalsUpdatedTime(CommonFixture.FIXED_EARLIER_ONE_DAY) + .build()); + when(mEncodedPayloadDao.getEncodedPayload(BUYER)) + .thenReturn( + DBEncodedPayload.create( + BUYER, 1, CommonFixture.FIXED_NOW, new byte[] {0x22, 0x33})); + + mJobWorker + .runEncodingPerBuyer( + DBEncoderLogicMetadata.builder() + .setBuyer(BUYER) + .setCreationTime(CommonFixture.FIXED_EARLIER_ONE_DAY) + .setVersion(1) + .setFailedEncodingCount(1) + .build(), + TIMEOUT_SECONDS) + .get(TIMEOUT_SECONDS, TimeUnit.SECONDS); + + verify(mSignalStorageManager).getSignals(BUYER); + verify(mProtectedSignalsDao).getSignalsUpdateMetadata(BUYER); + verify(mEncodedPayloadDao).getEncodedPayload(BUYER); + verifyNoMoreInteractions(mSignalStorageManager, mProtectedSignalsDao, mEncodedPayloadDao); + verifyZeroInteractions(mEncoderLogicHandler, mScriptEngine); + } + + @Test public void testUpdatesEncodersAllUpdatedEncodersDoNotDownloadAgain() { when(mEncoderLogicMetadataDao.getBuyersWithEncodersBeforeTime(any())) .thenReturn(Collections.emptyList()); diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/ProtectedSignalsServiceImplTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/ProtectedSignalsServiceImplTest.java index 07ca5a5b4..079d58612 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/ProtectedSignalsServiceImplTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/ProtectedSignalsServiceImplTest.java @@ -170,8 +170,7 @@ public class ProtectedSignalsServiceImplTest { mProtectedSignalsService.updateSignals(mInput, mUpdateSignalsCallbackMock); verify(mFledgeAuthorizationFilterMock) - .assertAppDeclaredProtectedSignalsPermission( - eq(CONTEXT), eq(PACKAGE), eq(API_NAME)); + .assertAppDeclaredPermission(eq(CONTEXT), eq(PACKAGE), eq(API_NAME)); verify(mCallingAppUidSupplierMock).getCallingAppUid(); verify(mDevContextFilterMock).createDevContext(); verify(mFlagsMock).getDisableFledgeEnrollmentCheck(); diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/SignalsEncodingE2ETest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/SignalsEncodingE2ETest.java index 3c8e73dfc..f877873f9 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/SignalsEncodingE2ETest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/SignalsEncodingE2ETest.java @@ -25,7 +25,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; @@ -199,6 +198,7 @@ public class SignalsEncodingE2ETest { mEncoderPersistenceDao, mEncoderEndpointsDao, mEncoderLogicMetadataDao, + mSignalsDao, mAdServicesHttpsClient, mBackgroundExecutorService); mUpdateEncoderEventHandler = @@ -244,7 +244,8 @@ public class SignalsEncodingE2ETest { mBackgroundExecutorService, mUpdatesDownloader, mUpdateProcessingOrchestrator, - mAdtechUriValidator); + mAdtechUriValidator, + CommonFixture.FIXED_CLOCK_TRUNCATED_TO_MILLI); mService = new ProtectedSignalsServiceImpl( mContextSpy, @@ -277,9 +278,9 @@ public class SignalsEncodingE2ETest { new PeriodicEncodingJobWorker( mEncoderLogicHandler, mEncoderLogicMetadataDao, - mEncoderPersistenceDao, mEncodedPayloadDao, mSignalStorageManager, + mSignalsDao, mAdSelectionScriptEngine, mBackgroundExecutorService, mLightweightExecutorService, @@ -650,9 +651,9 @@ public class SignalsEncodingE2ETest { new PeriodicEncodingJobWorker( mEncoderLogicHandler, mEncoderLogicMetadataDao, - mEncoderPersistenceDao, mEncodedPayloadDao, mSignalStorageManager, + mSignalsDao, mAdSelectionScriptEngine, mBackgroundExecutorService, mLightweightExecutorService, @@ -686,9 +687,9 @@ public class SignalsEncodingE2ETest { new PeriodicEncodingJobWorker( mEncoderLogicHandler, mEncoderLogicMetadataDao, - mEncoderPersistenceDao, mEncodedPayloadDao, mSignalStorageManager, + mSignalsDao, mAdSelectionScriptEngine, mBackgroundExecutorService, mLightweightExecutorService, @@ -705,7 +706,9 @@ public class SignalsEncodingE2ETest { "Encoding JS should have returned size of signals as result", new byte[] {(byte) expected.size()}, payload2); - assertTrue(secondEncodingRun.getCreationTime().isAfter(firstEncodingRun.getCreationTime())); + // The second run should skip based on the logic that we will skip encoding for unchanged + // buyer. + assertEquals(secondEncodingRun.getCreationTime(), firstEncodingRun.getCreationTime()); encoderLogicDownloadedLatch.await(5, TimeUnit.SECONDS); assertEquals( diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/SignalsIntakeE2ETest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/SignalsIntakeE2ETest.java index d57f6adf1..c158543e1 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/SignalsIntakeE2ETest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/SignalsIntakeE2ETest.java @@ -172,6 +172,7 @@ public class SignalsIntakeE2ETest { mEncoderPersistenceDao, mEncoderEndpointsDao, mEncoderLogicMetadataDao, + mSignalsDao, mAdServicesHttpsClientMock, mBackgroundExecutorService); mUpdateEncoderEventHandler = @@ -235,7 +236,8 @@ public class SignalsIntakeE2ETest { mBackgroundExecutorService, mUpdatesDownloader, mUpdateProcessingOrchestrator, - mAdtechUriValidator); + mAdtechUriValidator, + CommonFixture.FIXED_CLOCK_TRUNCATED_TO_MILLI); mService = new ProtectedSignalsServiceImpl( mContextSpy, diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/SignalsMaintenanceTasksWorkerTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/SignalsMaintenanceTasksWorkerTest.java index ab251d56a..60d8859ac 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/SignalsMaintenanceTasksWorkerTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/SignalsMaintenanceTasksWorkerTest.java @@ -17,6 +17,7 @@ package com.android.adservices.service.signals; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; @@ -55,6 +56,7 @@ public class SignalsMaintenanceTasksWorkerTest { @Mock private PackageManager mPackageManagerMock; SignalsMaintenanceTasksWorker mSignalsMaintenanceTasksWorker; + Instant mNow; Instant mExpirationTime; @Before @@ -69,6 +71,7 @@ public class SignalsMaintenanceTasksWorkerTest { mClockMock, mPackageManagerMock); when(mClockMock.instant()).thenReturn(CommonFixture.FIXED_NOW); + mNow = CommonFixture.FIXED_NOW; mExpirationTime = CommonFixture.FIXED_NOW.minusSeconds(ProtectedSignal.EXPIRATION_SECONDS); } @@ -76,21 +79,25 @@ public class SignalsMaintenanceTasksWorkerTest { public void testClearInvalidSignalsEnrollmentEnabled() throws Exception { when(mFlagsMock.getDisableFledgeEnrollmentCheck()).thenReturn(false); - mSignalsMaintenanceTasksWorker.clearInvalidSignals(mExpirationTime); + mSignalsMaintenanceTasksWorker.clearInvalidSignals(mExpirationTime, mNow); - verify(mProtectedSignalsDaoMock).deleteSignalsBeforeTime(mExpirationTime); + verify(mProtectedSignalsDaoMock) + .deleteExpiredSignalsAndUpdateSignalsUpdateMetadata(mExpirationTime, mNow); verify(mFlagsMock).getDisableFledgeEnrollmentCheck(); verify(mProtectedSignalsDaoMock).deleteDisallowedBuyerSignals(any()); - verify(mProtectedSignalsDaoMock).deleteAllDisallowedPackageSignals(any(), any()); + verify(mProtectedSignalsDaoMock) + .deleteAllDisallowedPackageSignalsAndUpdateSignalUpdateMetadata( + any(), any(), eq(mNow)); } @Test public void testClearInvalidSignalsEnrollmentDisabled() throws Exception { when(mFlagsMock.getDisableFledgeEnrollmentCheck()).thenReturn(true); - mSignalsMaintenanceTasksWorker.clearInvalidSignals(mExpirationTime); + mSignalsMaintenanceTasksWorker.clearInvalidSignals(mExpirationTime, mNow); - verify(mProtectedSignalsDaoMock).deleteSignalsBeforeTime(mExpirationTime); + verify(mProtectedSignalsDaoMock) + .deleteExpiredSignalsAndUpdateSignalsUpdateMetadata(mExpirationTime, mNow); verify(mFlagsMock).getDisableFledgeEnrollmentCheck(); } diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/UpdateProcessingOrchestratorTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/UpdateProcessingOrchestratorTest.java index 4b6b57b39..346f969a0 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/UpdateProcessingOrchestratorTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/UpdateProcessingOrchestratorTest.java @@ -25,6 +25,7 @@ import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; @@ -97,7 +98,7 @@ public class UpdateProcessingOrchestratorTest { ADTECH, PACKAGE, NOW, new JSONObject(), DEV_CONTEXT); verify(mProtectedSignalsDaoMock).getSignalsByBuyer(eq(ADTECH)); verify(mProtectedSignalsDaoMock) - .insertAndDelete(Collections.emptyList(), Collections.emptyList()); + .insertAndDelete(ADTECH, NOW, Collections.emptyList(), Collections.emptyList()); verifyZeroInteractions(mUpdateProcessorSelectorMock); verify(mSignalEvictionControllerMock) .evict( @@ -136,7 +137,9 @@ public class UpdateProcessingOrchestratorTest { mUpdateProcessingOrchestrator.processUpdates( ADTECH, PACKAGE, NOW, commandToNumber, DEV_CONTEXT)); assertEquals(exception, t.getCause()); + verify(mProtectedSignalsDaoMock).getSignalsByBuyer(ADTECH); verifyZeroInteractions(mSignalEvictionControllerMock); + verifyNoMoreInteractions(mProtectedSignalsDaoMock); } @Test @@ -156,8 +159,9 @@ public class UpdateProcessingOrchestratorTest { mUpdateProcessingOrchestrator.processUpdates(ADTECH, PACKAGE, NOW, json, DEV_CONTEXT); - List<DBProtectedSignal> expected = Arrays.asList(createSignal(KEY_1, VALUE)); - verify(mProtectedSignalsDaoMock).insertAndDelete(eq(expected), eq(Collections.emptyList())); + List<DBProtectedSignal> expected = List.of(createSignal(KEY_1, VALUE)); + verify(mProtectedSignalsDaoMock) + .insertAndDelete(ADTECH, NOW, expected, Collections.emptyList()); verify(mUpdateProcessorSelectorMock).getUpdateProcessor(eq(TEST_PROCESSOR)); verify(mSignalEvictionControllerMock) .evict( @@ -185,7 +189,8 @@ public class UpdateProcessingOrchestratorTest { mUpdateProcessingOrchestrator.processUpdates(ADTECH, PACKAGE, NOW, json, DEV_CONTEXT); List<DBProtectedSignal> expected = Arrays.asList(createSignal(KEY_1, VALUE)); - verify(mProtectedSignalsDaoMock).insertAndDelete(eq(expected), eq(Collections.emptyList())); + verify(mProtectedSignalsDaoMock) + .insertAndDelete(ADTECH, NOW, expected, Collections.emptyList()); verify(mUpdateProcessorSelectorMock).getUpdateProcessor(eq(TEST_PROCESSOR)); verify(mSignalEvictionControllerMock) .evict( @@ -214,7 +219,7 @@ public class UpdateProcessingOrchestratorTest { verify(mProtectedSignalsDaoMock).getSignalsByBuyer(eq(ADTECH)); verify(mProtectedSignalsDaoMock) - .insertAndDelete(eq(Collections.emptyList()), eq(Arrays.asList(toRemove))); + .insertAndDelete(ADTECH, NOW, Collections.emptyList(), Arrays.asList(toRemove)); verify(mUpdateProcessorSelectorMock).getUpdateProcessor(eq(TEST_PROCESSOR)); verify(mSignalEvictionControllerMock) .evict(eq(ADTECH), eq(List.of(toKeep)), mUpdateOutputArgumentCaptor.capture()); @@ -250,7 +255,8 @@ public class UpdateProcessingOrchestratorTest { DBProtectedSignal expected1 = createSignal(KEY_1, VALUE); DBProtectedSignal expected2 = createSignal(KEY_2, VALUE); verify(mProtectedSignalsDaoMock) - .insertAndDelete(mInsertCaptor.capture(), eq(Collections.emptyList())); + .insertAndDelete( + eq(ADTECH), eq(NOW), mInsertCaptor.capture(), eq(Collections.emptyList())); assertThat(mInsertCaptor.getValue()) .containsExactlyElementsIn(Arrays.asList(expected1, expected2)); verify(mUpdateProcessorSelectorMock).getUpdateProcessor(eq(TEST_PROCESSOR + 1)); @@ -317,7 +323,7 @@ public class UpdateProcessingOrchestratorTest { verify(mProtectedSignalsDaoMock).getSignalsByBuyer(eq(ADTECH)); verify(mProtectedSignalsDaoMock) .insertAndDelete( - eq(Collections.emptyList()), eq(Arrays.asList(toRemove1, toRemove2))); + ADTECH, NOW, Collections.emptyList(), Arrays.asList(toRemove1, toRemove2)); verify(mUpdateProcessorSelectorMock).getUpdateProcessor(eq(TEST_PROCESSOR)); verify(mSignalEvictionControllerMock) .evict(eq(ADTECH), eq(List.of()), mUpdateOutputArgumentCaptor.capture()); @@ -441,7 +447,7 @@ public class UpdateProcessingOrchestratorTest { mUpdateProcessingOrchestrator.processUpdates(ADTECH, PACKAGE, NOW, json, DEV_CONTEXT); List<DBProtectedSignal> expected = Arrays.asList(createSignal(KEY_1, VALUE)); - verify(mProtectedSignalsDaoMock).insertAndDelete(eq(expected), eq(expected)); + verify(mProtectedSignalsDaoMock).insertAndDelete(ADTECH, NOW, expected, expected); verify(mUpdateProcessorSelectorMock).getUpdateProcessor(eq(TEST_PROCESSOR)); } } diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/UpdateSignalsOrchestratorTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/UpdateSignalsOrchestratorTest.java index 0e693ffd3..9fea32c03 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/UpdateSignalsOrchestratorTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/signals/UpdateSignalsOrchestratorTest.java @@ -66,7 +66,8 @@ public class UpdateSignalsOrchestratorTest { AdServicesExecutors.getBackgroundExecutor(), mUpdatesDownloader, mUpdateProcessingOrchestrator, - mAdTechUriValidator); + mAdTechUriValidator, + CommonFixture.FIXED_CLOCK_TRUNCATED_TO_MILLI); } @Test diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/stats/AdServicesLoggerImplTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/stats/AdServicesLoggerImplTest.java index 7e63a2ced..4fbe03c82 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/stats/AdServicesLoggerImplTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/stats/AdServicesLoggerImplTest.java @@ -88,48 +88,37 @@ import static com.android.adservices.service.stats.RunAdSelectionProcessReported import static com.android.adservices.service.stats.UpdateCustomAudienceProcessReportedStatsTest.DATA_SIZE_OF_ADS_IN_BYTES; import static com.android.adservices.service.stats.UpdateCustomAudienceProcessReportedStatsTest.NUM_OF_ADS; -import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import android.adservices.adselection.ReportEventRequest; +import com.android.adservices.common.AdServicesMockitoTestCase; import com.android.adservices.service.enrollment.EnrollmentStatus; import com.android.adservices.service.measurement.Source; import com.android.adservices.service.measurement.WipeoutStatus; import com.android.adservices.service.measurement.attribution.AttributionStatus; -import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** Unit tests for {@link AdServicesLoggerImpl}. */ -public class AdServicesLoggerImplTest { - @Mock StatsdAdServicesLogger mStatsdLoggerMock; - private static final String SOURCE_REGISTRANT = "android-app://com.registrant"; +public final class AdServicesLoggerImplTest extends AdServicesMockitoTestCase { - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - } + @Mock private StatsdAdServicesLogger mStatsdLoggerMock; @Test public void testLogFledgeApiCallStats() { - final int latencyMs = 10; + int latencyMs = 10; AdServicesLoggerImpl adServicesLogger = new AdServicesLoggerImpl(mStatsdLoggerMock); adServicesLogger.logFledgeApiCallStats( AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS, STATUS_SUCCESS, latencyMs); verify(mStatsdLoggerMock) .logFledgeApiCallStats( - eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS), - eq(STATUS_SUCCESS), - eq(latencyMs)); + AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS, STATUS_SUCCESS, latencyMs); } @Test @@ -149,22 +138,18 @@ public class AdServicesLoggerImplTest { ArgumentCaptor<RunAdSelectionProcessReportedStats> argumentCaptor = ArgumentCaptor.forClass(RunAdSelectionProcessReportedStats.class); verify(mStatsdLoggerMock).logRunAdSelectionProcessReportedStats(argumentCaptor.capture()); - assertEquals(argumentCaptor.getValue().getIsRemarketingAdsWon(), IS_RMKT_ADS_WON); - assertEquals( - argumentCaptor.getValue().getDBAdSelectionSizeInBytes(), - DB_AD_SELECTION_SIZE_IN_BYTES); - assertEquals( - argumentCaptor.getValue().getPersistAdSelectionLatencyInMillis(), - PERSIST_AD_SELECTION_LATENCY_IN_MILLIS); - assertEquals( - argumentCaptor.getValue().getRunAdSelectionResultCode(), - PERSIST_AD_SELECTION_RESULT_CODE); - assertEquals( - argumentCaptor.getValue().getRunAdSelectionLatencyInMillis(), - RUN_AD_SELECTION_LATENCY_IN_MILLIS); - assertEquals( - argumentCaptor.getValue().getRunAdSelectionResultCode(), - RUN_AD_SELECTION_RESULT_CODE); + RunAdSelectionProcessReportedStats loggedStats = argumentCaptor.getValue(); + expect.that(loggedStats.getIsRemarketingAdsWon()).isEqualTo(IS_RMKT_ADS_WON); + expect.that(loggedStats.getDBAdSelectionSizeInBytes()) + .isEqualTo(DB_AD_SELECTION_SIZE_IN_BYTES); + expect.that(loggedStats.getPersistAdSelectionLatencyInMillis()) + .isEqualTo(PERSIST_AD_SELECTION_LATENCY_IN_MILLIS); + expect.that(loggedStats.getRunAdSelectionResultCode()) + .isEqualTo(PERSIST_AD_SELECTION_RESULT_CODE); + expect.that(loggedStats.getRunAdSelectionLatencyInMillis()) + .isEqualTo(RUN_AD_SELECTION_LATENCY_IN_MILLIS); + expect.that(loggedStats.getRunAdSelectionResultCode()) + .isEqualTo(RUN_AD_SELECTION_RESULT_CODE); } @Test @@ -200,49 +185,35 @@ public class AdServicesLoggerImplTest { ArgumentCaptor<RunAdScoringProcessReportedStats> argumentCaptor = ArgumentCaptor.forClass(RunAdScoringProcessReportedStats.class); verify(mStatsdLoggerMock).logRunAdScoringProcessReportedStats(argumentCaptor.capture()); - assertEquals( - argumentCaptor.getValue().getGetAdSelectionLogicLatencyInMillis(), - GET_AD_SELECTION_LOGIC_LATENCY_IN_MILLIS); - assertEquals( - argumentCaptor.getValue().getGetAdSelectionLogicResultCode(), - GET_AD_SELECTION_LOGIC_RESULT_CODE); - assertEquals( - argumentCaptor.getValue().getGetAdSelectionLogicScriptType(), - GET_AD_SELECTION_LOGIC_SCRIPT_TYPE); - assertEquals( - argumentCaptor.getValue().getFetchedAdSelectionLogicScriptSizeInBytes(), - FETCHED_AD_SELECTION_LOGIC_SCRIPT_SIZE_IN_BYTES); - assertEquals( - argumentCaptor.getValue().getGetTrustedScoringSignalsLatencyInMillis(), - GET_TRUSTED_SCORING_SIGNALS_LATENCY_IN_MILLIS); - assertEquals( - argumentCaptor.getValue().getGetTrustedScoringSignalsResultCode(), - GET_TRUSTED_SCORING_SIGNALS_RESULT_CODE); - assertEquals( - argumentCaptor.getValue().getFetchedTrustedScoringSignalsDataSizeInBytes(), - FETCHED_TRUSTED_SCORING_SIGNALS_DATA_SIZE_IN_BYTES); - assertEquals( - argumentCaptor.getValue().getScoreAdsLatencyInMillis(), - SCORE_ADS_LATENCY_IN_MILLIS); - assertEquals( - argumentCaptor.getValue().getGetAdScoresLatencyInMillis(), - GET_AD_SCORES_LATENCY_IN_MILLIS); - assertEquals( - argumentCaptor.getValue().getGetAdScoresResultCode(), GET_AD_SCORES_RESULT_CODE); - assertEquals( - argumentCaptor.getValue().getNumOfCasEnteringScoring(), - NUM_OF_CAS_ENTERING_SCORING); - assertEquals( - argumentCaptor.getValue().getNumOfRemarketingAdsEnteringScoring(), - NUM_OF_REMARKETING_ADS_ENTERING_SCORING); - assertEquals( - argumentCaptor.getValue().getNumOfContextualAdsEnteringScoring(), - NUM_OF_CONTEXTUAL_ADS_ENTERING_SCORING); - assertEquals( - argumentCaptor.getValue().getRunAdScoringLatencyInMillis(), - RUN_AD_SCORING_LATENCY_IN_MILLIS); - assertEquals( - argumentCaptor.getValue().getRunAdScoringResultCode(), RUN_AD_SCORING_RESULT_CODE); + RunAdScoringProcessReportedStats loggedStats = argumentCaptor.getValue(); + expect.that(loggedStats.getGetAdSelectionLogicLatencyInMillis()) + .isEqualTo(GET_AD_SELECTION_LOGIC_LATENCY_IN_MILLIS); + expect.that(loggedStats.getGetAdSelectionLogicResultCode()) + .isEqualTo(GET_AD_SELECTION_LOGIC_RESULT_CODE); + expect.that(loggedStats.getGetAdSelectionLogicScriptType()) + .isEqualTo(GET_AD_SELECTION_LOGIC_SCRIPT_TYPE); + expect.that(loggedStats.getFetchedAdSelectionLogicScriptSizeInBytes()) + .isEqualTo(FETCHED_AD_SELECTION_LOGIC_SCRIPT_SIZE_IN_BYTES); + expect.that(loggedStats.getGetTrustedScoringSignalsLatencyInMillis()) + .isEqualTo(GET_TRUSTED_SCORING_SIGNALS_LATENCY_IN_MILLIS); + expect.that(loggedStats.getGetTrustedScoringSignalsResultCode()) + .isEqualTo(GET_TRUSTED_SCORING_SIGNALS_RESULT_CODE); + expect.that(loggedStats.getFetchedTrustedScoringSignalsDataSizeInBytes()) + .isEqualTo(FETCHED_TRUSTED_SCORING_SIGNALS_DATA_SIZE_IN_BYTES); + expect.that(loggedStats.getScoreAdsLatencyInMillis()) + .isEqualTo(SCORE_ADS_LATENCY_IN_MILLIS); + expect.that(loggedStats.getGetAdScoresLatencyInMillis()) + .isEqualTo(GET_AD_SCORES_LATENCY_IN_MILLIS); + expect.that(loggedStats.getGetAdScoresResultCode()).isEqualTo(GET_AD_SCORES_RESULT_CODE); + expect.that(loggedStats.getNumOfCasEnteringScoring()) + .isEqualTo(NUM_OF_CAS_ENTERING_SCORING); + expect.that(loggedStats.getNumOfRemarketingAdsEnteringScoring()) + .isEqualTo(NUM_OF_REMARKETING_ADS_ENTERING_SCORING); + expect.that(loggedStats.getNumOfContextualAdsEnteringScoring()) + .isEqualTo(NUM_OF_CONTEXTUAL_ADS_ENTERING_SCORING); + expect.that(loggedStats.getRunAdScoringLatencyInMillis()) + .isEqualTo(RUN_AD_SCORING_LATENCY_IN_MILLIS); + expect.that(loggedStats.getRunAdScoringResultCode()).isEqualTo(RUN_AD_SCORING_RESULT_CODE); } @Test @@ -270,34 +241,26 @@ public class AdServicesLoggerImplTest { ArgumentCaptor<RunAdBiddingProcessReportedStats> argumentCaptor = ArgumentCaptor.forClass(RunAdBiddingProcessReportedStats.class); verify(mStatsdLoggerMock).logRunAdBiddingProcessReportedStats(argumentCaptor.capture()); - assertEquals( - argumentCaptor.getValue().getGetBuyersCustomAudienceLatencyInMills(), - GET_BUYERS_CUSTOM_AUDIENCE_LATENCY_IN_MILLIS); - assertEquals( - argumentCaptor.getValue().getGetBuyersCustomAudienceResultCode(), - GET_BUYERS_CUSTOM_AUDIENCE_RESULT_CODE); - assertEquals(argumentCaptor.getValue().getNumBuyersRequested(), NUM_BUYERS_REQUESTED); - assertEquals(argumentCaptor.getValue().getNumBuyersFetched(), NUM_BUYERS_FETCHED); - assertEquals( - argumentCaptor.getValue().getNumOfAdsEnteringBidding(), - NUM_OF_ADS_ENTERING_BIDDING); - assertEquals( - argumentCaptor.getValue().getNumOfCasEnteringBidding(), - NUM_OF_CAS_ENTERING_BIDDING); - assertEquals( - argumentCaptor.getValue().getNumOfCasPostBidding(), NUM_OF_CAS_POSTING_BIDDING); - assertEquals( - argumentCaptor.getValue().getRatioOfCasSelectingRmktAds(), - RATIO_OF_CAS_SELECTING_RMKT_ADS, - 0.0f); - assertEquals( - argumentCaptor.getValue().getRunAdBiddingLatencyInMillis(), - RUN_AD_BIDDING_LATENCY_IN_MILLIS); - assertEquals( - argumentCaptor.getValue().getRunAdBiddingResultCode(), RUN_AD_BIDDING_RESULT_CODE); - assertEquals( - argumentCaptor.getValue().getTotalAdBiddingStageLatencyInMillis(), - TOTAL_AD_BIDDING_STAGE_LATENCY_IN_MILLIS); + RunAdBiddingProcessReportedStats loggedStats = argumentCaptor.getValue(); + expect.that(loggedStats.getGetBuyersCustomAudienceLatencyInMills()) + .isEqualTo(GET_BUYERS_CUSTOM_AUDIENCE_LATENCY_IN_MILLIS); + expect.that(loggedStats.getGetBuyersCustomAudienceResultCode()) + .isEqualTo(GET_BUYERS_CUSTOM_AUDIENCE_RESULT_CODE); + expect.that(loggedStats.getNumBuyersRequested()).isEqualTo(NUM_BUYERS_REQUESTED); + expect.that(loggedStats.getNumBuyersFetched()).isEqualTo(NUM_BUYERS_FETCHED); + expect.that(loggedStats.getNumOfAdsEnteringBidding()) + .isEqualTo(NUM_OF_ADS_ENTERING_BIDDING); + expect.that(loggedStats.getNumOfCasEnteringBidding()) + .isEqualTo(NUM_OF_CAS_ENTERING_BIDDING); + expect.that(loggedStats.getNumOfCasPostBidding()).isEqualTo(NUM_OF_CAS_POSTING_BIDDING); + expect.that(loggedStats.getRatioOfCasSelectingRmktAds()) + .isWithin(0.0f) + .of(RATIO_OF_CAS_SELECTING_RMKT_ADS); + expect.that(loggedStats.getRunAdBiddingLatencyInMillis()) + .isEqualTo(RUN_AD_BIDDING_LATENCY_IN_MILLIS); + expect.that(loggedStats.getRunAdBiddingResultCode()).isEqualTo(RUN_AD_BIDDING_RESULT_CODE); + expect.that(loggedStats.getTotalAdBiddingStageLatencyInMillis()) + .isEqualTo(TOTAL_AD_BIDDING_STAGE_LATENCY_IN_MILLIS); } @Test @@ -332,44 +295,33 @@ public class AdServicesLoggerImplTest { ArgumentCaptor.forClass(RunAdBiddingPerCAProcessReportedStats.class); verify(mStatsdLoggerMock) .logRunAdBiddingPerCAProcessReportedStats(argumentCaptor.capture()); - assertEquals(argumentCaptor.getValue().getNumOfAdsForBidding(), NUM_OF_ADS_FOR_BIDDING); - assertEquals( - argumentCaptor.getValue().getRunAdBiddingPerCaLatencyInMillis(), - RUN_AD_BIDDING_PER_CA_LATENCY_IN_MILLIS); - assertEquals( - argumentCaptor.getValue().getRunAdBiddingPerCaResultCode(), - RUN_AD_BIDDING_PER_CA_RESULT_CODE); - assertEquals( - argumentCaptor.getValue().getGetBuyerDecisionLogicLatencyInMillis(), - GET_BUYER_DECISION_LOGIC_LATENCY_IN_MILLIS); - assertEquals( - argumentCaptor.getValue().getGetBuyerDecisionLogicResultCode(), - GET_BUYER_DECISION_LOGIC_RESULT_CODE); - assertEquals( - argumentCaptor.getValue().getBuyerDecisionLogicScriptType(), - BUYER_DECISION_LOGIC_SCRIPT_TYPE); - assertEquals( - argumentCaptor.getValue().getFetchedBuyerDecisionLogicScriptSizeInBytes(), - FETCHED_BUYER_DECISION_LOGIC_SCRIPT_SIZE_IN_BYTES); - assertEquals( - argumentCaptor.getValue().getNumOfKeysOfTrustedBiddingSignals(), - NUM_OF_KEYS_OF_TRUSTED_BIDDING_SIGNALS); - assertEquals( - argumentCaptor.getValue().getFetchedTrustedBiddingSignalsDataSizeInBytes(), - FETCHED_TRUSTED_BIDDING_SIGNALS_DATA_SIZE_IN_BYTES); - assertEquals( - argumentCaptor.getValue().getGetTrustedBiddingSignalsLatencyInMillis(), - GET_TRUSTED_BIDDING_SIGNALS_LATENCY_IN_MILLIS); - assertEquals( - argumentCaptor.getValue().getGetTrustedBiddingSignalsResultCode(), - GET_TRUSTED_BIDDING_SIGNALS_RESULT_CODE); - assertEquals( - argumentCaptor.getValue().getGenerateBidsLatencyInMillis(), - GENERATE_BIDS_LATENCY_IN_MILLIS); - assertEquals( - argumentCaptor.getValue().getRunBiddingLatencyInMillis(), - RUN_BIDDING_LATENCY_IN_MILLIS); - assertEquals(argumentCaptor.getValue().getRunBiddingResultCode(), RUN_BIDDING_RESULT_CODE); + RunAdBiddingPerCAProcessReportedStats loggedStats = argumentCaptor.getValue(); + expect.that(loggedStats.getNumOfAdsForBidding()).isEqualTo(NUM_OF_ADS_FOR_BIDDING); + expect.that(loggedStats.getRunAdBiddingPerCaLatencyInMillis()) + .isEqualTo(RUN_AD_BIDDING_PER_CA_LATENCY_IN_MILLIS); + expect.that(loggedStats.getRunAdBiddingPerCaResultCode()) + .isEqualTo(RUN_AD_BIDDING_PER_CA_RESULT_CODE); + expect.that(loggedStats.getGetBuyerDecisionLogicLatencyInMillis()) + .isEqualTo(GET_BUYER_DECISION_LOGIC_LATENCY_IN_MILLIS); + expect.that(loggedStats.getGetBuyerDecisionLogicResultCode()) + .isEqualTo(GET_BUYER_DECISION_LOGIC_RESULT_CODE); + expect.that(loggedStats.getBuyerDecisionLogicScriptType()) + .isEqualTo(BUYER_DECISION_LOGIC_SCRIPT_TYPE); + expect.that(loggedStats.getFetchedBuyerDecisionLogicScriptSizeInBytes()) + .isEqualTo(FETCHED_BUYER_DECISION_LOGIC_SCRIPT_SIZE_IN_BYTES); + expect.that(loggedStats.getNumOfKeysOfTrustedBiddingSignals()) + .isEqualTo(NUM_OF_KEYS_OF_TRUSTED_BIDDING_SIGNALS); + expect.that(loggedStats.getFetchedTrustedBiddingSignalsDataSizeInBytes()) + .isEqualTo(FETCHED_TRUSTED_BIDDING_SIGNALS_DATA_SIZE_IN_BYTES); + expect.that(loggedStats.getGetTrustedBiddingSignalsLatencyInMillis()) + .isEqualTo(GET_TRUSTED_BIDDING_SIGNALS_LATENCY_IN_MILLIS); + expect.that(loggedStats.getGetTrustedBiddingSignalsResultCode()) + .isEqualTo(GET_TRUSTED_BIDDING_SIGNALS_RESULT_CODE); + expect.that(loggedStats.getGenerateBidsLatencyInMillis()) + .isEqualTo(GENERATE_BIDS_LATENCY_IN_MILLIS); + expect.that(loggedStats.getRunBiddingLatencyInMillis()) + .isEqualTo(RUN_BIDDING_LATENCY_IN_MILLIS); + expect.that(loggedStats.getRunBiddingResultCode()).isEqualTo(RUN_BIDDING_RESULT_CODE); } @Test @@ -386,11 +338,11 @@ public class AdServicesLoggerImplTest { ArgumentCaptor<BackgroundFetchProcessReportedStats> argumentCaptor = ArgumentCaptor.forClass(BackgroundFetchProcessReportedStats.class); verify(mStatsdLoggerMock).logBackgroundFetchProcessReportedStats(argumentCaptor.capture()); - assertEquals(argumentCaptor.getValue().getLatencyInMillis(), LATENCY_IN_MILLIS); - assertEquals( - argumentCaptor.getValue().getNumOfEligibleToUpdateCas(), - NUM_OF_ELIGIBLE_TO_UPDATE_CAS); - assertEquals(argumentCaptor.getValue().getResultCode(), RESULT_CODE); + BackgroundFetchProcessReportedStats loggedStats = argumentCaptor.getValue(); + expect.that(loggedStats.getLatencyInMillis()).isEqualTo(LATENCY_IN_MILLIS); + expect.that(loggedStats.getNumOfEligibleToUpdateCas()) + .isEqualTo(NUM_OF_ELIGIBLE_TO_UPDATE_CAS); + expect.that(loggedStats.getResultCode()).isEqualTo(RESULT_CODE); } @Test @@ -408,11 +360,11 @@ public class AdServicesLoggerImplTest { ArgumentCaptor.forClass(UpdateCustomAudienceProcessReportedStats.class); verify(mStatsdLoggerMock) .logUpdateCustomAudienceProcessReportedStats(argumentCaptor.capture()); - assertEquals(argumentCaptor.getValue().getLatencyInMills(), LATENCY_IN_MILLIS); - assertEquals(argumentCaptor.getValue().getResultCode(), RESULT_CODE); - assertEquals( - argumentCaptor.getValue().getDataSizeOfAdsInBytes(), DATA_SIZE_OF_ADS_IN_BYTES); - assertEquals(argumentCaptor.getValue().getNumOfAds(), NUM_OF_ADS); + UpdateCustomAudienceProcessReportedStats loggedStats = argumentCaptor.getValue(); + expect.that(loggedStats.getLatencyInMills()).isEqualTo(LATENCY_IN_MILLIS); + expect.that(loggedStats.getResultCode()).isEqualTo(RESULT_CODE); + expect.that(loggedStats.getDataSizeOfAdsInBytes()).isEqualTo(DATA_SIZE_OF_ADS_IN_BYTES); + expect.that(loggedStats.getNumOfAds()).isEqualTo(NUM_OF_ADS); } @Test @@ -428,18 +380,18 @@ public class AdServicesLoggerImplTest { ArgumentCaptor<MeasurementReportsStats> argumentCaptor = ArgumentCaptor.forClass(MeasurementReportsStats.class); verify(mStatsdLoggerMock).logMeasurementReports(argumentCaptor.capture()); - assertEquals(argumentCaptor.getValue().getCode(), AD_SERVICES_MESUREMENT_REPORTS_UPLOADED); - assertEquals( - argumentCaptor.getValue().getType(), - AD_SERVICES_MEASUREMENT_REPORTS_UPLOADED__TYPE__EVENT); - assertEquals(argumentCaptor.getValue().getResultCode(), STATUS_SUCCESS); + MeasurementReportsStats loggedStats = argumentCaptor.getValue(); + expect.that(loggedStats.getCode()).isEqualTo(AD_SERVICES_MESUREMENT_REPORTS_UPLOADED); + expect.that(loggedStats.getType()) + .isEqualTo(AD_SERVICES_MEASUREMENT_REPORTS_UPLOADED__TYPE__EVENT); + expect.that(loggedStats.getResultCode()).isEqualTo(STATUS_SUCCESS); } @Test public void testLogApiCallStats() { - final String packageName = "com.android.test"; - final String sdkName = "com.android.container"; - final int latency = 100; + String packageName = "com.android.test"; + String sdkName = "com.android.container"; + int latency = 100; ApiCallStats stats = new ApiCallStats.Builder() .setCode(AD_SERVICES_API_CALLED) @@ -454,17 +406,16 @@ public class AdServicesLoggerImplTest { adServicesLogger.logApiCallStats(stats); ArgumentCaptor<ApiCallStats> argumentCaptor = ArgumentCaptor.forClass(ApiCallStats.class); verify(mStatsdLoggerMock).logApiCallStats(argumentCaptor.capture()); - assertEquals(argumentCaptor.getValue().getCode(), AD_SERVICES_API_CALLED); - assertEquals( - argumentCaptor.getValue().getApiClass(), - AD_SERVICES_API_CALLED__API_CLASS__TARGETING); - assertEquals( - argumentCaptor.getValue().getApiName(), - AD_SERVICES_API_CALLED__API_NAME__GET_TOPICS); - assertEquals(argumentCaptor.getValue().getAppPackageName(), packageName); - assertEquals(argumentCaptor.getValue().getSdkPackageName(), sdkName); - assertEquals(argumentCaptor.getValue().getLatencyMillisecond(), latency); - assertEquals(argumentCaptor.getValue().getResultCode(), STATUS_SUCCESS); + ApiCallStats loggedStats = argumentCaptor.getValue(); + expect.that(loggedStats.getCode()).isEqualTo(AD_SERVICES_API_CALLED); + expect.that(loggedStats.getApiClass()) + .isEqualTo(AD_SERVICES_API_CALLED__API_CLASS__TARGETING); + expect.that(loggedStats.getApiName()) + .isEqualTo(AD_SERVICES_API_CALLED__API_NAME__GET_TOPICS); + expect.that(loggedStats.getAppPackageName()).isEqualTo(packageName); + expect.that(loggedStats.getSdkPackageName()).isEqualTo(sdkName); + expect.that(loggedStats.getLatencyMillisecond()).isEqualTo(latency); + expect.that(loggedStats.getResultCode()).isEqualTo(STATUS_SUCCESS); } @Test @@ -479,18 +430,18 @@ public class AdServicesLoggerImplTest { adServicesLogger.logUIStats(stats); ArgumentCaptor<UIStats> argumentCaptor = ArgumentCaptor.forClass(UIStats.class); verify(mStatsdLoggerMock).logUIStats(argumentCaptor.capture()); - assertEquals(argumentCaptor.getValue().getCode(), AD_SERVICES_SETTINGS_USAGE_REPORTED); - assertEquals( - argumentCaptor.getValue().getRegion(), - AD_SERVICES_SETTINGS_USAGE_REPORTED__REGION__ROW); - assertEquals( - argumentCaptor.getValue().getAction(), - AD_SERVICES_SETTINGS_USAGE_REPORTED__ACTION__OPT_OUT_SELECTED); + UIStats loggedStats = argumentCaptor.getValue(); + expect.that(loggedStats.getCode()).isEqualTo(AD_SERVICES_SETTINGS_USAGE_REPORTED); + expect.that(loggedStats.getRegion()) + .isEqualTo(AD_SERVICES_SETTINGS_USAGE_REPORTED__REGION__ROW); + expect.that(loggedStats.getAction()) + .isEqualTo(AD_SERVICES_SETTINGS_USAGE_REPORTED__ACTION__OPT_OUT_SELECTED); } @Test public void testLogMsmtDebugKeyMatchStats() { - final String enrollmentId = "EnrollmentId"; + String sourceRegistrant = "android-app://com.registrant"; + String enrollmentId = "EnrollmentId"; long hashedValue = 5000L; long hashLimit = 10000L; MsmtDebugKeysMatchStats stats = @@ -501,19 +452,20 @@ public class AdServicesLoggerImplTest { AD_SERVICES_MEASUREMENT_DEBUG_KEYS__ATTRIBUTION_TYPE__APP_WEB) .setDebugJoinKeyHashedValue(hashedValue) .setDebugJoinKeyHashLimit(hashLimit) - .setSourceRegistrant(SOURCE_REGISTRANT) + .setSourceRegistrant(sourceRegistrant) .build(); AdServicesLoggerImpl adServicesLogger = new AdServicesLoggerImpl(mStatsdLoggerMock); adServicesLogger.logMeasurementDebugKeysMatch(stats); ArgumentCaptor<MsmtDebugKeysMatchStats> argumentCaptor = ArgumentCaptor.forClass(MsmtDebugKeysMatchStats.class); verify(mStatsdLoggerMock).logMeasurementDebugKeysMatch(argumentCaptor.capture()); - assertEquals(stats, argumentCaptor.getValue()); + expect.that(stats).isEqualTo(argumentCaptor.getValue()); } @Test public void testLogMsmtAdIdMatchForDebugKeysStats() { - final String enrollmentId = "enrollmentId"; + String sourceRegistrant = "android-app://com.registrant"; + String enrollmentId = "enrollmentId"; long uniqueAdIds = 2L; long uniqueAdIdLimit = 5L; MsmtAdIdMatchForDebugKeysStats stats = @@ -524,7 +476,7 @@ public class AdServicesLoggerImplTest { AD_SERVICES_MEASUREMENT_DEBUG_KEYS__ATTRIBUTION_TYPE__APP_WEB) .setNumUniqueAdIds(uniqueAdIds) .setNumUniqueAdIdsLimit(uniqueAdIdLimit) - .setSourceRegistrant(SOURCE_REGISTRANT) + .setSourceRegistrant(sourceRegistrant) .build(); AdServicesLoggerImpl adServicesLogger = new AdServicesLoggerImpl(mStatsdLoggerMock); @@ -533,7 +485,7 @@ public class AdServicesLoggerImplTest { ArgumentCaptor.forClass(MsmtAdIdMatchForDebugKeysStats.class); verify(mStatsdLoggerMock) .logMeasurementAdIdMatchForDebugKeysStats(argumentCaptor.capture()); - assertEquals(stats, argumentCaptor.getValue()); + expect.that(stats).isEqualTo(argumentCaptor.getValue()); } @Test @@ -554,22 +506,19 @@ public class AdServicesLoggerImplTest { ArgumentCaptor<MeasurementAttributionStats> argumentCaptor = ArgumentCaptor.forClass(MeasurementAttributionStats.class); verify(mStatsdLoggerMock).logMeasurementAttributionStats(argumentCaptor.capture()); - assertEquals(argumentCaptor.getValue().getCode(), AD_SERVICES_MEASUREMENT_ATTRIBUTION); - assertEquals( - argumentCaptor.getValue().getSourceType(), - AttributionStatus.SourceType.VIEW.getValue()); - assertEquals( - argumentCaptor.getValue().getSurfaceType(), - AttributionStatus.AttributionSurface.APP_WEB.getValue()); - assertEquals( - argumentCaptor.getValue().getResult(), - AttributionStatus.AttributionResult.SUCCESS.getValue()); - assertEquals( - argumentCaptor.getValue().getFailureType(), - AttributionStatus.FailureType.UNKNOWN.getValue()); - assertEquals(argumentCaptor.getValue().isSourceDerived(), false); - assertEquals(argumentCaptor.getValue().isInstallAttribution(), true); - assertEquals(argumentCaptor.getValue().getAttributionDelay(), 100L); + MeasurementAttributionStats loggedStats = argumentCaptor.getValue(); + expect.that(loggedStats.getCode()).isEqualTo(AD_SERVICES_MEASUREMENT_ATTRIBUTION); + expect.that(loggedStats.getSourceType()) + .isEqualTo(AttributionStatus.SourceType.VIEW.getValue()); + expect.that(loggedStats.getSurfaceType()) + .isEqualTo(AttributionStatus.AttributionSurface.APP_WEB.getValue()); + expect.that(loggedStats.getResult()) + .isEqualTo(AttributionStatus.AttributionResult.SUCCESS.getValue()); + expect.that(loggedStats.getFailureType()) + .isEqualTo(AttributionStatus.FailureType.UNKNOWN.getValue()); + expect.that(loggedStats.isSourceDerived()).isFalse(); + expect.that(loggedStats.isInstallAttribution()).isTrue(); + expect.that(loggedStats.getAttributionDelay()).isEqualTo(100L); } @Test @@ -585,10 +534,10 @@ public class AdServicesLoggerImplTest { ArgumentCaptor<MeasurementWipeoutStats> argumentCaptor = ArgumentCaptor.forClass(MeasurementWipeoutStats.class); verify(mStatsdLoggerMock).logMeasurementWipeoutStats(argumentCaptor.capture()); - assertEquals(argumentCaptor.getValue().getCode(), AD_SERVICES_MEASUREMENT_WIPEOUT); - assertEquals( - argumentCaptor.getValue().getWipeoutType(), - WipeoutStatus.WipeoutType.CONSENT_FLIP.ordinal()); + MeasurementWipeoutStats loggedStats = argumentCaptor.getValue(); + expect.that(loggedStats.getCode()).isEqualTo(AD_SERVICES_MEASUREMENT_WIPEOUT); + expect.that(loggedStats.getWipeoutType()) + .isEqualTo(WipeoutStatus.WipeoutType.CONSENT_FLIP.ordinal()); } @Test @@ -608,11 +557,11 @@ public class AdServicesLoggerImplTest { ArgumentCaptor.forClass(MeasurementDelayedSourceRegistrationStats.class); verify(mStatsdLoggerMock) .logMeasurementDelayedSourceRegistrationStats(argumentCaptor.capture()); - assertEquals( - argumentCaptor.getValue().getCode(), - AD_SERVICES_MEASUREMENT_DELAYED_SOURCE_REGISTRATION); - assertEquals(argumentCaptor.getValue().getRegistrationStatus(), UnknownEnumValue); - assertEquals(argumentCaptor.getValue().getRegistrationDelay(), registrationDelay); + MeasurementDelayedSourceRegistrationStats loggedStats = argumentCaptor.getValue(); + expect.that(loggedStats.getCode()) + .isEqualTo(AD_SERVICES_MEASUREMENT_DELAYED_SOURCE_REGISTRATION); + expect.that(loggedStats.getRegistrationStatus()).isEqualTo(UnknownEnumValue); + expect.that(loggedStats.getRegistrationDelay()).isEqualTo(registrationDelay); } @Test @@ -621,22 +570,21 @@ public class AdServicesLoggerImplTest { EnrollmentStatus.TransactionType.READ_TRANSACTION_TYPE.ordinal(); AdServicesLoggerImpl adServicesLogger = new AdServicesLoggerImpl(mStatsdLoggerMock); adServicesLogger.logEnrollmentDataStats(transactionTypeEnumValue, true, 100); - verify(mStatsdLoggerMock) - .logEnrollmentDataStats(eq(transactionTypeEnumValue), eq(true), eq(100)); + verify(mStatsdLoggerMock).logEnrollmentDataStats(transactionTypeEnumValue, true, 100); } @Test public void testLogEnrollmentMatchStats() { AdServicesLoggerImpl adServicesLogger = new AdServicesLoggerImpl(mStatsdLoggerMock); adServicesLogger.logEnrollmentMatchStats(true, 100); - verify(mStatsdLoggerMock).logEnrollmentMatchStats(eq(true), eq(100)); + verify(mStatsdLoggerMock).logEnrollmentMatchStats(true, 100); } @Test public void testLogEnrollmentFileDownloadStats() { AdServicesLoggerImpl adServicesLogger = new AdServicesLoggerImpl(mStatsdLoggerMock); adServicesLogger.logEnrollmentFileDownloadStats(true, 100); - verify(mStatsdLoggerMock).logEnrollmentFileDownloadStats(eq(true), eq(100)); + verify(mStatsdLoggerMock).logEnrollmentFileDownloadStats(true, 100); } @Test @@ -650,11 +598,7 @@ public class AdServicesLoggerImplTest { 100, dataFileGroupStatusEnumValue, 10, "SomeSdkName", errorCauseEnumValue); verify(mStatsdLoggerMock) .logEnrollmentFailedStats( - eq(100), - eq(dataFileGroupStatusEnumValue), - eq(10), - eq("SomeSdkName"), - eq(errorCauseEnumValue)); + 100, dataFileGroupStatusEnumValue, 10, "SomeSdkName", errorCauseEnumValue); } @Test @@ -683,14 +627,14 @@ public class AdServicesLoggerImplTest { ArgumentCaptor<MeasurementClickVerificationStats> argumentCaptor = ArgumentCaptor.forClass(MeasurementClickVerificationStats.class); verify(mStatsdLoggerMock).logMeasurementClickVerificationStats(argumentCaptor.capture()); - assertEquals(stats, argumentCaptor.getValue()); + expect.that(stats).isEqualTo(argumentCaptor.getValue()); } @Test public void testLogEncryptionKeyFetchedStats() { - final String enrollmentId = "enrollmentId"; - final String companyId = "companyId"; - final String encryptionKeyUrl = "https://www.adtech1.com/.well-known/encryption-keys"; + String enrollmentId = "enrollmentId"; + String companyId = "companyId"; + String encryptionKeyUrl = "https://www.adtech1.com/.well-known/encryption-keys"; AdServicesEncryptionKeyFetchedStats stats = AdServicesEncryptionKeyFetchedStats.builder() @@ -708,7 +652,7 @@ public class AdServicesLoggerImplTest { ArgumentCaptor<AdServicesEncryptionKeyFetchedStats> argumentCaptor = ArgumentCaptor.forClass(AdServicesEncryptionKeyFetchedStats.class); verify(mStatsdLoggerMock).logEncryptionKeyFetchedStats(argumentCaptor.capture()); - assertEquals(stats, argumentCaptor.getValue()); + expect.that(stats).isEqualTo(argumentCaptor.getValue()); } @Test @@ -726,7 +670,7 @@ public class AdServicesLoggerImplTest { ArgumentCaptor<AdServicesEncryptionKeyDbTransactionEndedStats> argumentCaptor = ArgumentCaptor.forClass(AdServicesEncryptionKeyDbTransactionEndedStats.class); verify(mStatsdLoggerMock).logEncryptionKeyDbTransactionEndedStats(argumentCaptor.capture()); - assertEquals(stats, argumentCaptor.getValue()); + expect.that(stats).isEqualTo(argumentCaptor.getValue()); } @Test @@ -760,7 +704,7 @@ public class AdServicesLoggerImplTest { ArgumentCaptor.forClass(DestinationRegisteredBeaconsReportedStats.class); verify(mStatsdLoggerMock).logDestinationRegisteredBeaconsReportedStats( argumentCaptor.capture()); - assertEquals(stats, argumentCaptor.getValue()); + expect.that(stats).isEqualTo(argumentCaptor.getValue()); } @Test @@ -778,7 +722,7 @@ public class AdServicesLoggerImplTest { ArgumentCaptor<ReportInteractionApiCalledStats> argumentCaptor = ArgumentCaptor.forClass(ReportInteractionApiCalledStats.class); verify(mStatsdLoggerMock).logReportInteractionApiCalledStats(argumentCaptor.capture()); - assertEquals(stats, argumentCaptor.getValue()); + expect.that(stats).isEqualTo(argumentCaptor.getValue()); } @Test @@ -796,6 +740,6 @@ public class AdServicesLoggerImplTest { ArgumentCaptor.forClass(InteractionReportingTableClearedStats.class); verify(mStatsdLoggerMock).logInteractionReportingTableClearedStats( argumentCaptor.capture()); - assertEquals(stats, argumentCaptor.getValue()); + expect.that(stats).isEqualTo(argumentCaptor.getValue()); } } diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/stats/StatsdAdServicesLoggerTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/stats/StatsdAdServicesLoggerTest.java index 50dfe87be..8ef66086b 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/stats/StatsdAdServicesLoggerTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/stats/StatsdAdServicesLoggerTest.java @@ -16,6 +16,7 @@ package com.android.adservices.service.stats; +import static com.android.adservices.mockito.ExtendedMockitoExpectations.mockIsAtLeastT; import static com.android.adservices.service.stats.AdServicesEncryptionKeyDbTransactionEndedStats.DbTransactionStatus.INSERT_EXCEPTION; import static com.android.adservices.service.stats.AdServicesEncryptionKeyDbTransactionEndedStats.DbTransactionType.WRITE_TRANSACTION_TYPE; import static com.android.adservices.service.stats.AdServicesEncryptionKeyDbTransactionEndedStats.MethodName.INSERT_KEY; @@ -36,18 +37,18 @@ import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICE import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_MEASUREMENT_WIPEOUT; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_MEASUREMENT_CLICK_VERIFICATION; import static com.android.adservices.service.stats.AdServicesStatsLog.DESTINATION_REGISTERED_BEACONS; -import static com.android.adservices.service.stats.AdServicesStatsLog.DESTINATION_REGISTERED_BEACONS__DESTINATION__SELLER_DESTINATION; import static com.android.adservices.service.stats.AdServicesStatsLog.INTERACTION_REPORTING_TABLE_CLEARED; import static com.android.adservices.service.stats.AdServicesStatsLog.REPORT_INTERACTION_API_CALLED; import static com.android.adservices.service.stats.EpochComputationClassifierStats.ClassifierType; import static com.android.adservices.service.stats.EpochComputationClassifierStats.OnDeviceClassifierStatus; import static com.android.adservices.service.stats.EpochComputationClassifierStats.PrecomputedClassifierStatus; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyIterable; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -57,28 +58,29 @@ import static org.mockito.Mockito.when; import android.adservices.adselection.ReportEventRequest; +import com.android.adservices.common.AdServicesExtendedMockitoTestCase; import com.android.adservices.service.Flags; import com.android.adservices.service.enrollment.EnrollmentStatus; import com.android.adservices.service.measurement.Source; import com.android.adservices.service.measurement.WipeoutStatus; import com.android.adservices.service.measurement.attribution.AttributionStatus; -import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.dx.mockito.inline.extended.MockedVoidMethod; import com.android.modules.utils.build.SdkLevel; +import com.android.modules.utils.testing.ExtendedMockitoRule.SpyStatic; import com.google.common.collect.ImmutableList; -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; -import org.mockito.MockitoSession; -import org.mockito.quality.Strictness; import java.util.Arrays; import java.util.List; -public class StatsdAdServicesLoggerTest { +@SpyStatic(SdkLevel.class) +@SpyStatic(AdServicesStatsLog.class) +public final class StatsdAdServicesLoggerTest extends AdServicesExtendedMockitoTestCase { + // Atom IDs private static final int TOPICS_REPORTED_ATOM_ID = 535; private static final int EPOCH_COMPUTATION_CLASSIFIER_ATOM_ID = 537; @@ -119,39 +121,25 @@ public class StatsdAdServicesLoggerTest { private static final int SELLER_DESTINATION = ReportEventRequest.FLAG_REPORTING_DESTINATION_SELLER; - private MockitoSession mMockitoSession; private StatsdAdServicesLogger mLogger; @Mock private Flags mFlags; @Before public void setUp() { - mMockitoSession = - ExtendedMockito.mockitoSession() - .mockStatic(SdkLevel.class) - .mockStatic(AdServicesStatsLog.class) - .strictness(Strictness.LENIENT) - .initMocks(this) - .startMocking(); - mLogger = new StatsdAdServicesLogger(mFlags); } - @After - public void tearDown() { - mMockitoSession.finishMocking(); - } - @Test public void testLogGetTopicsReportedStats_tPlus() { // Mocks when(mFlags.getCompatLoggingKillSwitch()).thenReturn(false); - ExtendedMockito.doReturn(true).when(SdkLevel::isAtLeastT); - ExtendedMockito.doNothing() + mockIsAtLeastT(true); + doNothing() .when( () -> AdServicesStatsLog.write( anyInt(), anyInt(), anyInt(), anyInt(), any(byte[].class))); - ExtendedMockito.doNothing() + doNothing() .when( () -> AdServicesStatsLog.write( @@ -161,7 +149,7 @@ public class StatsdAdServicesLoggerTest { mLogger.logGetTopicsReportedStats(TOPICS_REPORTED_STATS_DATA); // Verify compat logging - ExtendedMockito.verify( + verify( () -> AdServicesStatsLog.write( eq(TOPICS_REPORTED_COMPAT_ATOM_ID), @@ -170,7 +158,7 @@ public class StatsdAdServicesLoggerTest { eq(TOPIC_IDS_COUNT), any(byte[].class))); // Verify T+ logging - ExtendedMockito.verify( + verify( () -> AdServicesStatsLog.write( TOPICS_REPORTED_ATOM_ID, @@ -186,8 +174,8 @@ public class StatsdAdServicesLoggerTest { public void testLogGetTopicsReportedStats_tPlus_noCompatLoggingDueToKillSwitch() { // Mocks when(mFlags.getCompatLoggingKillSwitch()).thenReturn(true); - ExtendedMockito.doReturn(true).when(SdkLevel::isAtLeastT); - ExtendedMockito.doNothing() + mockIsAtLeastT(true); + doNothing() .when( () -> AdServicesStatsLog.write( @@ -197,7 +185,7 @@ public class StatsdAdServicesLoggerTest { mLogger.logGetTopicsReportedStats(TOPICS_REPORTED_STATS_DATA); // Verify T+ logging only - ExtendedMockito.verify( + verify( () -> AdServicesStatsLog.write( TOPICS_REPORTED_ATOM_ID, @@ -213,8 +201,8 @@ public class StatsdAdServicesLoggerTest { public void testLogGetTopicsReportedStats_sMinus() { // Mocks when(mFlags.getCompatLoggingKillSwitch()).thenReturn(false); - ExtendedMockito.doReturn(false).when(SdkLevel::isAtLeastT); - ExtendedMockito.doNothing() + mockIsAtLeastT(false); + doNothing() .when( () -> AdServicesStatsLog.write( @@ -224,7 +212,7 @@ public class StatsdAdServicesLoggerTest { mLogger.logGetTopicsReportedStats(TOPICS_REPORTED_STATS_DATA); // Verify only compat logging took place - ExtendedMockito.verify( + verify( () -> AdServicesStatsLog.write( eq(TOPICS_REPORTED_COMPAT_ATOM_ID), @@ -240,7 +228,7 @@ public class StatsdAdServicesLoggerTest { public void testLogGetTopicsReportedStats_sMinus_noLoggingDueToKillSwitch() { // Mocks when(mFlags.getCompatLoggingKillSwitch()).thenReturn(true); - ExtendedMockito.doReturn(false).when(SdkLevel::isAtLeastT); + mockIsAtLeastT(false); // Invoke logging call mLogger.logGetTopicsReportedStats(TOPICS_REPORTED_STATS_DATA); @@ -253,8 +241,8 @@ public class StatsdAdServicesLoggerTest { public void testLogEpochComputationClassifierStats_tPlus() { // Mocks when(mFlags.getCompatLoggingKillSwitch()).thenReturn(false); - ExtendedMockito.doReturn(true).when(SdkLevel::isAtLeastT); - ExtendedMockito.doNothing() + mockIsAtLeastT(true); + doNothing() .when( () -> AdServicesStatsLog.write( @@ -265,7 +253,7 @@ public class StatsdAdServicesLoggerTest { anyInt(), anyInt(), anyInt())); - ExtendedMockito.doNothing() + doNothing() .when( () -> AdServicesStatsLog.write( @@ -281,7 +269,7 @@ public class StatsdAdServicesLoggerTest { mLogger.logEpochComputationClassifierStats(EPOCH_COMPUTATION_CLASSIFIER_STATS_DATA); // Verify compat logging - ExtendedMockito.verify( + verify( () -> AdServicesStatsLog.write( eq(EPOCH_COMPUTATION_CLASSIFIER_COMPAT_ATOM_ID), @@ -297,7 +285,7 @@ public class StatsdAdServicesLoggerTest { .PRECOMPUTED_CLASSIFIER_STATUS_NOT_INVOKED .getCompatLoggingValue()))); // Verify T+ logging - ExtendedMockito.verify( + verify( () -> AdServicesStatsLog.write( EPOCH_COMPUTATION_CLASSIFIER_ATOM_ID, @@ -318,8 +306,8 @@ public class StatsdAdServicesLoggerTest { public void testLogEpochComputationClassifierStats_tPlus_noCompatLoggingDueToKillSwitch() { // Mocks when(mFlags.getCompatLoggingKillSwitch()).thenReturn(true); - ExtendedMockito.doReturn(true).when(SdkLevel::isAtLeastT); - ExtendedMockito.doNothing() + mockIsAtLeastT(true); + doNothing() .when( () -> AdServicesStatsLog.write( @@ -335,7 +323,7 @@ public class StatsdAdServicesLoggerTest { mLogger.logEpochComputationClassifierStats(EPOCH_COMPUTATION_CLASSIFIER_STATS_DATA); // Verify T+ logging - ExtendedMockito.verify( + verify( () -> AdServicesStatsLog.write( EPOCH_COMPUTATION_CLASSIFIER_ATOM_ID, @@ -356,8 +344,8 @@ public class StatsdAdServicesLoggerTest { public void testLogEpochComputationClassifierStats_sMinus() { // Mocks when(mFlags.getCompatLoggingKillSwitch()).thenReturn(false); - ExtendedMockito.doReturn(false).when(SdkLevel::isAtLeastT); - ExtendedMockito.doNothing() + mockIsAtLeastT(false); + doNothing() .when( () -> AdServicesStatsLog.write( @@ -373,7 +361,7 @@ public class StatsdAdServicesLoggerTest { mLogger.logEpochComputationClassifierStats(EPOCH_COMPUTATION_CLASSIFIER_STATS_DATA); // Verify only compat logging took place - ExtendedMockito.verify( + verify( () -> AdServicesStatsLog.write( eq(EPOCH_COMPUTATION_CLASSIFIER_COMPAT_ATOM_ID), @@ -396,7 +384,7 @@ public class StatsdAdServicesLoggerTest { public void testLogEpochComputationClassifierStats_sMinus_noLoggingDueToKillSwitch() { // Mocks when(mFlags.getCompatLoggingKillSwitch()).thenReturn(true); - ExtendedMockito.doReturn(false).when(SdkLevel::isAtLeastT); + mockIsAtLeastT(false); // Invoke logging call mLogger.logEpochComputationClassifierStats(EPOCH_COMPUTATION_CLASSIFIER_STATS_DATA); @@ -409,7 +397,7 @@ public class StatsdAdServicesLoggerTest { public void logMeasurementDebugKeysMatch_success() { when(mFlags.getMeasurementEnableAppPackageNameLogging()).thenReturn(true); when(mFlags.getMeasurementAppPackageNameLoggingAllowlist()).thenReturn(SOURCE_REGISTRANT); - final String enrollmentId = "EnrollmentId"; + String enrollmentId = "EnrollmentId"; long hashedValue = 5000L; long hashLimit = 10000L; int attributionType = AD_SERVICES_MEASUREMENT_DEBUG_KEYS__ATTRIBUTION_TYPE__APP_WEB; @@ -422,7 +410,7 @@ public class StatsdAdServicesLoggerTest { .setDebugJoinKeyHashLimit(hashLimit) .setSourceRegistrant(SOURCE_REGISTRANT) .build(); - ExtendedMockito.doNothing() + doNothing() .when( () -> AdServicesStatsLog.write( @@ -449,7 +437,7 @@ public class StatsdAdServicesLoggerTest { eq(hashedValue), eq(hashLimit), eq(SOURCE_REGISTRANT)); - ExtendedMockito.verify(writeInvocation); + verify(writeInvocation); verifyNoMoreInteractions(staticMockMarker(AdServicesStatsLog.class)); } @@ -474,7 +462,7 @@ public class StatsdAdServicesLoggerTest { .setEventReportCount(3) .setEventDebugReportCount(1) .build(); - ExtendedMockito.doNothing() + doNothing() .when( () -> AdServicesStatsLog.write( @@ -515,7 +503,7 @@ public class StatsdAdServicesLoggerTest { eq(1), eq(0)); - ExtendedMockito.verify(writeInvocation); + verify(writeInvocation); verifyNoMoreInteractions(staticMockMarker(AdServicesStatsLog.class)); } @@ -530,8 +518,7 @@ public class StatsdAdServicesLoggerTest { .setWipeoutType(WipeoutStatus.WipeoutType.CONSENT_FLIP.ordinal()) .setSourceRegistrant(SOURCE_REGISTRANT) .build(); - ExtendedMockito.doNothing() - .when(() -> AdServicesStatsLog.write(anyInt(), anyInt(), anyString())); + doNothing().when(() -> AdServicesStatsLog.write(anyInt(), anyInt(), anyString())); // Invoke logging call mLogger.logMeasurementWipeoutStats(stats); @@ -544,7 +531,7 @@ public class StatsdAdServicesLoggerTest { eq(WipeoutStatus.WipeoutType.CONSENT_FLIP.ordinal()), eq(SOURCE_REGISTRANT)); - ExtendedMockito.verify(writeInvocation); + verify(writeInvocation); verifyNoMoreInteractions(staticMockMarker(AdServicesStatsLog.class)); } @@ -562,7 +549,7 @@ public class StatsdAdServicesLoggerTest { .setRegistrationDelay(registrationDelay) .setRegistrant(SOURCE_REGISTRANT) .build(); - ExtendedMockito.doNothing() + doNothing() .when(() -> AdServicesStatsLog.write(anyInt(), anyInt(), anyLong(), anyString())); // Invoke logging call @@ -577,7 +564,7 @@ public class StatsdAdServicesLoggerTest { eq(registrationDelay), eq(SOURCE_REGISTRANT)); - ExtendedMockito.verify(writeInvocation); + verify(writeInvocation); verifyNoMoreInteractions(staticMockMarker(AdServicesStatsLog.class)); } @@ -585,7 +572,7 @@ public class StatsdAdServicesLoggerTest { @Test public void logConsentMigrationStats_success() { when(mFlags.getAdservicesConsentMigrationLoggingEnabled()).thenReturn(true); - ExtendedMockito.doNothing() + doNothing() .when( () -> AdServicesStatsLog.write( @@ -627,7 +614,7 @@ public class StatsdAdServicesLoggerTest { eq(2), eq(2), eq(2)); - ExtendedMockito.verify(writeInvocation); + verify(writeInvocation); verifyNoMoreInteractions(staticMockMarker(AdServicesStatsLog.class)); } @@ -660,7 +647,7 @@ public class StatsdAdServicesLoggerTest { public void logMeasurementAdIdMatchForDebugKeys_success() { when(mFlags.getMeasurementEnableAppPackageNameLogging()).thenReturn(true); when(mFlags.getMeasurementAppPackageNameLoggingAllowlist()).thenReturn(SOURCE_REGISTRANT); - final String enrollmentId = "EnrollmentId"; + String enrollmentId = "EnrollmentId"; long uniqueAdIdValue = 1L; long uniqueAdIdLimit = 5L; int attributionType = AD_SERVICES_MEASUREMENT_DEBUG_KEYS__ATTRIBUTION_TYPE__APP_WEB; @@ -673,7 +660,7 @@ public class StatsdAdServicesLoggerTest { .setNumUniqueAdIdsLimit(uniqueAdIdLimit) .setSourceRegistrant(SOURCE_REGISTRANT) .build(); - ExtendedMockito.doNothing() + doNothing() .when( () -> AdServicesStatsLog.write( @@ -700,7 +687,7 @@ public class StatsdAdServicesLoggerTest { eq(uniqueAdIdLimit), eq(SOURCE_REGISTRANT)); - ExtendedMockito.verify(writeInvocation); + verify(writeInvocation); verifyNoMoreInteractions(staticMockMarker(AdServicesStatsLog.class)); } @@ -708,7 +695,7 @@ public class StatsdAdServicesLoggerTest { @Test public void logMeasurementAdIdMatchForDebugKeys_appLoggingDisabled_emptyString() { when(mFlags.getMeasurementEnableAppPackageNameLogging()).thenReturn(false); - final String enrollmentId = "EnrollmentId"; + String enrollmentId = "EnrollmentId"; long uniqueAdIdValue = 1L; long uniqueAdIdLimit = 5L; int attributionType = AD_SERVICES_MEASUREMENT_DEBUG_KEYS__ATTRIBUTION_TYPE__APP_WEB; @@ -721,7 +708,7 @@ public class StatsdAdServicesLoggerTest { .setNumUniqueAdIdsLimit(uniqueAdIdLimit) .setSourceRegistrant(SOURCE_REGISTRANT) .build(); - ExtendedMockito.doNothing() + doNothing() .when( () -> AdServicesStatsLog.write( @@ -748,7 +735,7 @@ public class StatsdAdServicesLoggerTest { eq(uniqueAdIdLimit), eq("")); - ExtendedMockito.verify(writeInvocation); + verify(writeInvocation); verifyNoMoreInteractions(staticMockMarker(AdServicesStatsLog.class)); } @@ -757,7 +744,7 @@ public class StatsdAdServicesLoggerTest { public void logMeasurementAdIdMatchForDebugKeys_appNotAllowlisted_emptyString() { when(mFlags.getMeasurementEnableAppPackageNameLogging()).thenReturn(true); when(mFlags.getMeasurementAppPackageNameLoggingAllowlist()).thenReturn(""); - final String enrollmentId = "EnrollmentId"; + String enrollmentId = "EnrollmentId"; long uniqueAdIdValue = 1L; long uniqueAdIdLimit = 5L; int attributionType = AD_SERVICES_MEASUREMENT_DEBUG_KEYS__ATTRIBUTION_TYPE__APP_WEB; @@ -770,7 +757,7 @@ public class StatsdAdServicesLoggerTest { .setNumUniqueAdIdsLimit(uniqueAdIdLimit) .setSourceRegistrant(SOURCE_REGISTRANT) .build(); - ExtendedMockito.doNothing() + doNothing() .when( () -> AdServicesStatsLog.write( @@ -797,7 +784,7 @@ public class StatsdAdServicesLoggerTest { eq(uniqueAdIdLimit), eq("")); - ExtendedMockito.verify(writeInvocation); + verify(writeInvocation); verifyNoMoreInteractions(staticMockMarker(AdServicesStatsLog.class)); } @@ -806,7 +793,7 @@ public class StatsdAdServicesLoggerTest { public void logEnrollmentData_success() { int transactionTypeEnumValue = EnrollmentStatus.TransactionType.WRITE_TRANSACTION_TYPE.ordinal(); - ExtendedMockito.doNothing() + doNothing() .when(() -> AdServicesStatsLog.write(anyInt(), anyInt(), anyBoolean(), anyInt())); // Invoke logging call @@ -821,15 +808,14 @@ public class StatsdAdServicesLoggerTest { eq(true), eq(100)); - ExtendedMockito.verify(writeInvocation); + verify(writeInvocation); verifyNoMoreInteractions(staticMockMarker(AdServicesStatsLog.class)); } @Test public void logEnrollmentMatch_success() { - ExtendedMockito.doNothing() - .when(() -> AdServicesStatsLog.write(anyInt(), anyBoolean(), anyInt())); + doNothing().when(() -> AdServicesStatsLog.write(anyInt(), anyBoolean(), anyInt())); // Invoke logging call mLogger.logEnrollmentMatchStats(true, 100); @@ -840,15 +826,14 @@ public class StatsdAdServicesLoggerTest { AdServicesStatsLog.write( eq(AD_SERVICES_ENROLLMENT_MATCHED), eq(true), eq(100)); - ExtendedMockito.verify(writeInvocation); + verify(writeInvocation); verifyNoMoreInteractions(staticMockMarker(AdServicesStatsLog.class)); } @Test public void logEnrollmentFileDownload_success() { - ExtendedMockito.doNothing() - .when(() -> AdServicesStatsLog.write(anyInt(), anyBoolean(), anyInt())); + doNothing().when(() -> AdServicesStatsLog.write(anyInt(), anyBoolean(), anyInt())); // Invoke logging call mLogger.logEnrollmentFileDownloadStats(true, 100); @@ -859,7 +844,7 @@ public class StatsdAdServicesLoggerTest { AdServicesStatsLog.write( eq(AD_SERVICES_ENROLLMENT_FILE_DOWNLOADED), eq(true), eq(100)); - ExtendedMockito.verify(writeInvocation); + verify(writeInvocation); verifyNoMoreInteractions(staticMockMarker(AdServicesStatsLog.class)); } @@ -870,7 +855,7 @@ public class StatsdAdServicesLoggerTest { EnrollmentStatus.DataFileGroupStatus.PENDING_CUSTOM_VALIDATION.ordinal(); int errorCauseEnumValue = EnrollmentStatus.ErrorCause.ENROLLMENT_BLOCKLISTED_ERROR_CAUSE.ordinal(); - ExtendedMockito.doNothing() + doNothing() .when( () -> AdServicesStatsLog.write( @@ -896,7 +881,7 @@ public class StatsdAdServicesLoggerTest { eq("SomeSdkName"), eq(errorCauseEnumValue)); - ExtendedMockito.verify(writeInvocation); + verify(writeInvocation); verifyNoMoreInteractions(staticMockMarker(AdServicesStatsLog.class)); } @@ -922,7 +907,7 @@ public class StatsdAdServicesLoggerTest { .setSourceRegistrant(sourceRegistrant) .build(); - ExtendedMockito.doNothing() + doNothing() .when( () -> AdServicesStatsLog.write( @@ -951,16 +936,16 @@ public class StatsdAdServicesLoggerTest { eq(validDelayWindowMs), eq("")); // App package name not in allow list. - ExtendedMockito.verify(writeInvocation); + verify(writeInvocation); verifyNoMoreInteractions(staticMockMarker(AdServicesStatsLog.class)); } @Test public void logEncryptionKeyFetchedStats_success() { - final String enrollmentId = "enrollmentId"; - final String companyId = "companyId"; - final String encryptionKeyUrl = "https://www.adtech1.com/.well-known/encryption-keys"; + String enrollmentId = "enrollmentId"; + String companyId = "companyId"; + String encryptionKeyUrl = "https://www.adtech1.com/.well-known/encryption-keys"; AdServicesEncryptionKeyFetchedStats stats = AdServicesEncryptionKeyFetchedStats.builder() @@ -972,7 +957,7 @@ public class StatsdAdServicesLoggerTest { .setEncryptionKeyUrl(encryptionKeyUrl) .build(); - ExtendedMockito.doNothing() + doNothing() .when( () -> AdServicesStatsLog.write( @@ -999,7 +984,7 @@ public class StatsdAdServicesLoggerTest { eq(companyId), eq(encryptionKeyUrl)); - ExtendedMockito.verify(writeInvocation); + verify(writeInvocation); verifyNoMoreInteractions(staticMockMarker(AdServicesStatsLog.class)); } @@ -1013,8 +998,7 @@ public class StatsdAdServicesLoggerTest { .setMethodName(INSERT_KEY) .build(); - ExtendedMockito.doNothing() - .when(() -> AdServicesStatsLog.write(anyInt(), anyInt(), anyInt(), anyInt())); + doNothing().when(() -> AdServicesStatsLog.write(anyInt(), anyInt(), anyInt(), anyInt())); // Invoke logging call. mLogger.logEncryptionKeyDbTransactionEndedStats(stats); @@ -1028,7 +1012,7 @@ public class StatsdAdServicesLoggerTest { eq(INSERT_EXCEPTION.getValue()), eq(INSERT_KEY.getValue())); - ExtendedMockito.verify(writeInvocation); + verify(writeInvocation); verifyNoMoreInteractions(staticMockMarker(AdServicesStatsLog.class)); } @@ -1060,9 +1044,11 @@ public class StatsdAdServicesLoggerTest { .setAdServicesStatusCode(0) .build(); - ExtendedMockito.doNothing() - .when(() -> AdServicesStatsLog.write( - anyInt(), anyInt(), anyInt(), any(), anyInt(), anyInt())); + doNothing() + .when( + () -> + AdServicesStatsLog.write( + anyInt(), anyInt(), anyInt(), any(), anyInt(), anyInt())); // Invoke logging call. mLogger.logDestinationRegisteredBeaconsReportedStats(stats); @@ -1079,7 +1065,7 @@ public class StatsdAdServicesLoggerTest { eq(/* adServicesStatusCode */ 0) ); - ExtendedMockito.verify(writeInvocation); + verify(writeInvocation); verifyNoMoreInteractions(staticMockMarker(AdServicesStatsLog.class)); } @@ -1092,9 +1078,7 @@ public class StatsdAdServicesLoggerTest { .setNumMatchingUris(5) .build(); - ExtendedMockito.doNothing() - .when(() -> AdServicesStatsLog.write( - anyInt(), anyInt(), anyInt())); + doNothing().when(() -> AdServicesStatsLog.write(anyInt(), anyInt(), anyInt())); // Invoke logging call. mLogger.logReportInteractionApiCalledStats(stats); @@ -1108,7 +1092,7 @@ public class StatsdAdServicesLoggerTest { eq(/* numMatchingUris */ 5) ); - ExtendedMockito.verify(writeInvocation); + verify(writeInvocation); verifyNoMoreInteractions(staticMockMarker(AdServicesStatsLog.class)); } @@ -1121,9 +1105,7 @@ public class StatsdAdServicesLoggerTest { .setNumUnreportedUris(5) .build(); - ExtendedMockito.doNothing() - .when(() -> AdServicesStatsLog.write( - anyInt(), anyInt(), anyInt())); + doNothing().when(() -> AdServicesStatsLog.write(anyInt(), anyInt(), anyInt())); // Invoke logging call. mLogger.logInteractionReportingTableClearedStats(stats); @@ -1137,7 +1119,7 @@ public class StatsdAdServicesLoggerTest { eq(/* numUnreportedUris */ 5) ); - ExtendedMockito.verify(writeInvocation); + verify(writeInvocation); verifyNoMoreInteractions(staticMockMarker(AdServicesStatsLog.class)); } diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/EncryptionManagerTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/EncryptionManagerTest.java index 23b349405..afc827a75 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/EncryptionManagerTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/EncryptionManagerTest.java @@ -127,6 +127,7 @@ public final class EncryptionManagerTest { when(mFlags.getEnableDatabaseSchemaVersion9()).thenReturn(true); when(mFlags.getTopicsEncryptionEnabled()).thenReturn(true); + when(mFlags.getTopicsTestEncryptionPublicKey()).thenReturn(""); } @Test @@ -184,6 +185,23 @@ public final class EncryptionManagerTest { } @Test + public void testEncryption_useTestingKeys() { + String overrideTestKey = "YVfr8K7rpuv45LtaCv9L1eIGxBv/UK22WugJBjg53fo"; + when(mFlags.getTopicsTestEncryptionPublicKey()).thenReturn(overrideTestKey); + Topic topic = Topic.create(/* topic */ 5, /* taxonomyVersion */ 6L, /* modelVersion */ 7L); + + Optional<EncryptedTopic> optionalEncryptedTopic = + mEncryptionManager.encryptTopic(topic, SDK_NAME); + + // Verify EncryptedTopic is not empty. + assertThat(optionalEncryptedTopic.isPresent()).isTrue(); + assertThat(optionalEncryptedTopic.get().getEncryptedTopic()).isNotEmpty(); + // Verify test key used to override has been used. + assertThat(optionalEncryptedTopic.get().getKeyIdentifier()).isEqualTo(overrideTestKey); + assertThat(optionalEncryptedTopic.get().getEncapsulatedKey()).isNotEmpty(); + } + + @Test public void testEncryption_missingKeys() { doNothingOnErrorLogUtilError(); when(mEnrollmentDao.getEnrollmentDataFromSdkName(SDK_NAME)).thenReturn(ENROLLMENT_DATA); diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/TopicsServiceImplTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/TopicsServiceImplTest.java index fbd5fb26d..16f20a54e 100644 --- a/adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/TopicsServiceImplTest.java +++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/topics/TopicsServiceImplTest.java @@ -32,6 +32,7 @@ import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICE import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__GET_TOPICS; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__GET_TOPICS_PREVIEW_API; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__PACKAGE_NAME_NOT_FOUND_EXCEPTION; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__TOPICS_REQUEST_EMPTY_SDK_NAME; import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__TOPICS; import static com.google.common.truth.Truth.assertThat; @@ -287,6 +288,7 @@ public final class TopicsServiceImplTest extends AdServicesExtendedMockitoTestCa } @Test public void checkEmptySdkNameRequests() throws Exception { + ExtendedMockito.doNothing().when(() -> ErrorLogUtil.e(anyInt(), anyInt())); mockGetTopicsDisableDirectAppCalls(true); GetTopicsParam request = @@ -297,6 +299,12 @@ public final class TopicsServiceImplTest extends AdServicesExtendedMockitoTestCa .build(); invokeGetTopicsAndVerifyError(mSpyContext, STATUS_INVALID_ARGUMENT, request, false); + ExtendedMockito.verify( + () -> + ErrorLogUtil.e( + eq( + AD_SERVICES_ERROR_REPORTED__ERROR_CODE__TOPICS_REQUEST_EMPTY_SDK_NAME), + eq(AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__TOPICS))); } @Test diff --git a/adservices/tests/unittest/system-service/src/com/android/server/adservices/AdServicesShellCommandTest.java b/adservices/tests/unittest/system-service/src/com/android/server/adservices/AdServicesShellCommandTest.java index 779358be3..ca52afb01 100644 --- a/adservices/tests/unittest/system-service/src/com/android/server/adservices/AdServicesShellCommandTest.java +++ b/adservices/tests/unittest/system-service/src/com/android/server/adservices/AdServicesShellCommandTest.java @@ -18,10 +18,19 @@ package com.android.server.adservices; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.when; +import android.adservices.shell.IShellCommand; +import android.adservices.shell.IShellCommandCallback; +import android.adservices.shell.ShellCommandResult; +import android.content.Context; import android.os.Process; +import android.os.RemoteException; +import com.android.adservices.common.AdServicesMockitoTestCase; import com.android.server.adservices.AdServicesShellCommand.Injector; import com.google.common.truth.Expect; @@ -31,18 +40,15 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; import java.io.FileDescriptor; import java.io.PrintWriter; import java.io.StringWriter; -public final class AdServicesShellCommandTest { +public final class AdServicesShellCommandTest extends AdServicesMockitoTestCase { private static final String[] ALL_COMMANDS = new String[] {"help", "is-system-service-enabled"}; - @Rule public final MockitoRule rule = MockitoJUnit.rule(); @Rule public final Expect expect = Expect.create(); private final StringWriter mOutStringWriter = new StringWriter(); @@ -52,20 +58,30 @@ public final class AdServicesShellCommandTest { private final PrintWriter mErr = new PrintWriter(mErrStringWriter); @Mock private Flags mFlags; + @Mock private Context mContext; + + @Mock private IShellCommand mIShellCommand; private AdServicesShellCommand mShellCmd; + private Injector mInjector; + @Before public void setFixtures() { + mInjector = + new Injector() { + @Override + int getCallingUid() { + return Process.SHELL_UID; + } + + @Override + IShellCommand getShellCommandService(Context context) { + return mIShellCommand; + } + }; mShellCmd = - new AdServicesShellCommand( - new Injector() { - @Override - int getCallingUid() { - return Process.SHELL_UID; - } - }, - mFlags) { + new AdServicesShellCommand(mInjector, mFlags, mContext) { @Override public PrintWriter getOutPrintWriter() { return mOut; @@ -108,7 +124,8 @@ public final class AdServicesShellCommandTest { return 42; } }, - mFlags) + mFlags, + mContext) .onCommand("D'OH")); assertThat(e) .hasMessageThat() @@ -152,14 +169,60 @@ public final class AdServicesShellCommandTest { } @Test - public void testExec_invalidCommand() { - int result = runCmd("D'OH!"); - expect.withMessage("result").that(result).isEqualTo(-1); + public void testExec_invalidCommand() throws Exception { + String cmd = "D'OH!"; + ShellCommandResult responseInvalidShellCommand = + new ShellCommandResult.Builder() + .setErr(String.format("Unsupported command: %s", cmd)) + .setResultCode(-1) + .build(); + mockRunShellCommand(responseInvalidShellCommand); + int result = runCmd(cmd); + + expect.withMessage("result").that(result).isEqualTo(-1); expect.withMessage("out").that(getOut()).isEmpty(); String err = getErr(); expectHelpOutputHasAllCommands(err); - expectHelpOutputHasMessages(err, "D'OH!"); + expectHelpOutputHasMessages(err, cmd); + } + + @Test + public void testExec_validAdServicesShellCommand() throws Exception { + String cmd = "echo"; + ShellCommandResult response = + new ShellCommandResult.Builder().setOut(cmd).setResultCode(0).build(); + mockRunShellCommand(response); + + int result = runCmd(cmd); + + expect.withMessage("result").that(result).isEqualTo(0); + expect.withMessage("out").that(getOut()).contains(cmd); + expect.withMessage("err").that(getErr()).isEmpty(); + } + + @Test + public void testExec_adServicesCommand_throwsRemoteException() throws Exception { + String cmd = "echo"; + + doThrow(new RemoteException()).when(mIShellCommand).runShellCommand(any(), any()); + + int result = runCmd(cmd); + + expect.withMessage("result").that(result).isEqualTo(-1); + expect.withMessage("out").that(getOut()).isEmpty(); + expect.withMessage("err").that(getErr()).contains("Remote exception occurred"); + } + + @Test + public void testExec_adServicesCommand_timeoutHappens() throws Exception { + String cmd = "xxx"; + + int result = runCmd(cmd); + + expect.withMessage("result").that(result).isEqualTo(-1); + expect.withMessage("out").that(getOut()).isEmpty(); + expect.withMessage("err").that(getErr()).contains("Timeout occurred"); } @Test @@ -307,4 +370,14 @@ public final class AdServicesShellCommandTest { private void mockAdServicesSystemServiceEnabled(boolean value) { when(mFlags.getAdServicesSystemServiceEnabled()).thenReturn(value); } + + private void mockRunShellCommand(ShellCommandResult response) throws Exception { + doAnswer( + invocation -> { + ((IShellCommandCallback) invocation.getArgument(1)).onResult(response); + return null; + }) + .when(mIShellCommand) + .runShellCommand(any(), any()); + } } diff --git a/adservices/tests/unittest/ui/src/com/android/adservices/service/consent/AppConsentForRStorageManagerTest.java b/adservices/tests/unittest/ui/src/com/android/adservices/service/consent/AppConsentForRStorageManagerTest.java new file mode 100644 index 000000000..2569bc5c0 --- /dev/null +++ b/adservices/tests/unittest/ui/src/com/android/adservices/service/consent/AppConsentForRStorageManagerTest.java @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2023 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.consent; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertThrows; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; + +import com.android.adservices.common.AdServicesExtendedMockitoTestCase; +import com.android.adservices.data.common.BooleanFileDatastore; +import com.android.adservices.data.consent.AppConsentDao; +import com.android.adservices.service.FlagsFactory; +import com.android.adservices.service.common.compat.PackageManagerCompatUtils; +import com.android.adservices.service.extdata.AdServicesExtDataStorageServiceManager; +import com.android.adservices.service.ui.data.UxStatesDao; +import com.android.modules.utils.build.SdkLevel; +import com.android.modules.utils.testing.ExtendedMockitoRule.SpyStatic; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; + +import java.io.IOException; + +@SpyStatic(PackageManagerCompatUtils.class) +@SpyStatic(SdkLevel.class) +@SpyStatic(FlagsFactory.class) +public final class AppConsentForRStorageManagerTest extends AdServicesExtendedMockitoTestCase { + + private Context mContextSpy; + private BooleanFileDatastore mAppDaoDatastore; + private BooleanFileDatastore mConsentDatastore; + private AppConsentDao mAppConsentDaoSpy; + private AppConsentForRStorageManager mAppConsentForRStorageManager; + @Mock private UxStatesDao mUxStatesDaoMock; + + @Mock private AdServicesExtDataStorageServiceManager mAdExtDataManager; + + @Before + public void setup() { + mContextSpy = Mockito.spy(appContext.get()); + mConsentDatastore = + new BooleanFileDatastore( + mContextSpy, + ConsentConstants.STORAGE_XML_IDENTIFIER, + ConsentConstants.STORAGE_VERSION); + + mAppDaoDatastore = + new BooleanFileDatastore( + mContextSpy, AppConsentDao.DATASTORE_NAME, AppConsentDao.DATASTORE_VERSION); + + mAppConsentDaoSpy = + spy(new AppConsentDao(mAppDaoDatastore, mContextSpy.getPackageManager())); + mAppConsentForRStorageManager = + spy( + new AppConsentForRStorageManager( + mConsentDatastore, + mAppConsentDaoSpy, + mUxStatesDaoMock, + mAdExtDataManager)); + } + + @After + public void teardown() throws IOException { + mAppDaoDatastore.clear(); + mConsentDatastore.clear(); + } + + @Test + public void testNotSupportMethodException() { + assertThrows( + IllegalStateException.class, + () -> mAppConsentForRStorageManager.setConsentForApp("", false)); + + assertThrows( + IllegalStateException.class, + () -> mAppConsentForRStorageManager.clearAllAppConsentData()); + + assertThrows( + IllegalStateException.class, + () -> mAppConsentForRStorageManager.clearConsentForUninstalledApp("", 0)); + + assertThrows( + IllegalStateException.class, + () -> mAppConsentForRStorageManager.clearConsentForUninstalledApp("")); + + assertThrows( + IllegalStateException.class, + () -> mAppConsentForRStorageManager.clearKnownAppsWithConsent()); + + assertThrows( + IllegalStateException.class, + () -> mAppConsentForRStorageManager.recordGaUxNotificationDisplayed(true)); + + assertThrows( + IllegalStateException.class, + () -> mAppConsentForRStorageManager.recordNotificationDisplayed(true)); + + assertThrows( + IllegalStateException.class, + () -> mAppConsentForRStorageManager.getAppsWithRevokedConsent()); + + assertThrows( + IllegalStateException.class, + () -> mAppConsentForRStorageManager.getKnownAppsWithConsent()); + + assertThrows( + IllegalStateException.class, + () -> mAppConsentForRStorageManager.isConsentRevokedForApp("")); + + assertThrows( + IllegalStateException.class, + () -> mAppConsentForRStorageManager.setConsentForAppIfNew("", true)); + } + + @Test + public void testGetMeasurementConsent() { + when(mAdExtDataManager.getMsmtConsent()).thenReturn(true); + for (AdServicesApiType apiType : AdServicesApiType.values()) { + AdServicesApiConsent apiConsent = mAppConsentForRStorageManager.getConsent(apiType); + if (apiType == AdServicesApiType.MEASUREMENTS) { + + assertThat(apiConsent.isGiven()).isTrue(); + } else { + assertThat(apiConsent.isGiven()).isFalse(); + } + } + } + + @Test + public void testSetMeasurementConsent() throws IOException { + mAppConsentForRStorageManager.setConsent(AdServicesApiType.MEASUREMENTS, true); + verify(mAdExtDataManager).setMsmtConsent(eq(true)); + } + + @Test + public void testGetUserManualInteractionWithConsent() { + mAppConsentForRStorageManager.getUserManualInteractionWithConsent(); + verify(mAdExtDataManager).getManualInteractionWithConsentStatus(); + } + + @Test + public void testSetUserManualInteractionWithConsent() { + int userInteraction = 1; + mAppConsentForRStorageManager.recordUserManualInteractionWithConsent(userInteraction); + verify(mAdExtDataManager).setManualInteractionWithConsentStatus(eq(userInteraction)); + } + + @Test + public void testIsAdultAccount() { + mAppConsentForRStorageManager.isAdultAccount(); + verify(mAdExtDataManager).getIsAdultAccount(); + } + + @Test + public void testSetIsAdultAccount() { + mAppConsentForRStorageManager.setAdultAccount(true); + verify(mAdExtDataManager).setIsAdultAccount(eq(true)); + } + + @Test + public void testIsU18Account() { + mAppConsentForRStorageManager.isU18Account(); + verify(mAdExtDataManager).getIsU18Account(); + } + + @Test + public void testGetU18Notification() { + mAppConsentForRStorageManager.wasU18NotificationDisplayed(); + verify(mAdExtDataManager).getNotificationDisplayed(); + } + + @Test + public void testSetU18Notification() { + mAppConsentForRStorageManager.setU18NotificationDisplayed(true); + verify(mAdExtDataManager).setNotificationDisplayed(eq(true)); + } + + @Test + public void testSetIsU18Account() { + mAppConsentForRStorageManager.setU18Account(true); + verify(mAdExtDataManager).setIsU18Account(eq(true)); + } + + @Test + public void testGAAndBetaNotificationFlag() { + boolean gaDisplayedFlag = mAppConsentForRStorageManager.wasGaUxNotificationDisplayed(); + + boolean betaDisplayedFlag = mAppConsentForRStorageManager.wasNotificationDisplayed(); + assertThat(gaDisplayedFlag).isFalse(); + assertThat(betaDisplayedFlag).isFalse(); + } + + @Test + public void testSetMeasurementConsentException() { + assertThrows( + IllegalStateException.class, + () -> mAppConsentForRStorageManager.setConsent(AdServicesApiType.FLEDGE, true)); + } +} diff --git a/adservices/tests/unittest/ui/src/com/android/adservices/service/consent/ConsentCompositeStorageTest.java b/adservices/tests/unittest/ui/src/com/android/adservices/service/consent/ConsentCompositeStorageTest.java index baca04901..e9bca8c57 100644 --- a/adservices/tests/unittest/ui/src/com/android/adservices/service/consent/ConsentCompositeStorageTest.java +++ b/adservices/tests/unittest/ui/src/com/android/adservices/service/consent/ConsentCompositeStorageTest.java @@ -50,14 +50,14 @@ public class ConsentCompositeStorageTest { } @Test - public void testAllReadMethodsAppStorage() { + public void testAllReadMethodsAppStorage() throws IOException { ConsentCompositeStorage consentCompositeStorage = new ConsentCompositeStorage(ImmutableList.of(mAppConsentStorageManager)); testAllReadMethods(consentCompositeStorage); } @Test - public void testAllReadMethodsPPAPIAndSystem() { + public void testAllReadMethodsPPAPIAndSystem() throws IOException { ConsentCompositeStorage consentCompositeStorage = new ConsentCompositeStorage( ImmutableList.of(mAppConsentStorageManager, mAdServicesStorageManager)); @@ -79,14 +79,15 @@ public class ConsentCompositeStorageTest { testAllWriteMethods(consentCompositeStorage); } - private void testAllReadMethods(ConsentCompositeStorage consentCompositeStorage) { + private void testAllReadMethods(ConsentCompositeStorage consentCompositeStorage) + throws IOException { consentCompositeStorage.isAdIdEnabled(); consentCompositeStorage.isAdultAccount(); consentCompositeStorage.isConsentRevokedForApp(MOCK_PACKAGE_NAME); consentCompositeStorage.isEntryPointEnabled(); consentCompositeStorage.isU18Account(); - verifyReadMethods(consentCompositeStorage.getPrimaryStorage()); + verifyReadMethods(consentCompositeStorage.getConsentStorageList().get(0)); } private void testAllWriteMethods(ConsentCompositeStorage consentCompositeStorage) @@ -122,7 +123,7 @@ public class ConsentCompositeStorageTest { } } - private void verifyReadMethods(IConsentStorage consentStorage) { + private void verifyReadMethods(IConsentStorage consentStorage) throws IOException { Mockito.verify(consentStorage).isAdIdEnabled(); Mockito.verify(consentStorage).isAdultAccount(); Mockito.verify(consentStorage).isConsentRevokedForApp(eq(MOCK_PACKAGE_NAME)); diff --git a/adservices/tests/unittest/ui/src/com/android/adservices/service/consent/ConsentManagerV2Test.java b/adservices/tests/unittest/ui/src/com/android/adservices/service/consent/ConsentManagerV2Test.java new file mode 100644 index 000000000..5a6cbdbcc --- /dev/null +++ b/adservices/tests/unittest/ui/src/com/android/adservices/service/consent/ConsentManagerV2Test.java @@ -0,0 +1,4635 @@ +/* + * 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.consent; + +import static com.android.adservices.service.consent.ConsentConstants.CONSENT_KEY; +import static com.android.adservices.service.consent.ConsentConstants.DEFAULT_CONSENT; +import static com.android.adservices.service.consent.ConsentConstants.GA_UX_NOTIFICATION_DISPLAYED_ONCE; +import static com.android.adservices.service.consent.ConsentConstants.MANUAL_INTERACTION_WITH_CONSENT_RECORDED; +import static com.android.adservices.service.consent.ConsentConstants.NOTIFICATION_DISPLAYED_ONCE; +import static com.android.adservices.service.consent.ConsentConstants.SHARED_PREFS_CONSENT; +import static com.android.adservices.service.consent.ConsentConstants.SHARED_PREFS_KEY_APPSEARCH_HAS_MIGRATED; +import static com.android.adservices.service.consent.ConsentConstants.SHARED_PREFS_KEY_HAS_MIGRATED; +import static com.android.adservices.service.consent.ConsentConstants.SHARED_PREFS_KEY_PPAPI_HAS_CLEARED; +import static com.android.adservices.service.consent.ConsentManagerV2.MANUAL_INTERACTIONS_RECORDED; +import static com.android.adservices.service.consent.ConsentManagerV2.UNKNOWN; +import static com.android.adservices.service.consent.ConsentManagerV2.resetSharedPreference; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ERROR_WHILE_GET_CONSENT; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__PRIVACY_SANDBOX_SAVE_FAILURE; +import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX; +import static com.android.adservices.spe.AdservicesJobInfo.COBALT_LOGGING_JOB; +import static com.android.adservices.spe.AdservicesJobInfo.CONSENT_NOTIFICATION_JOB; +import static com.android.adservices.spe.AdservicesJobInfo.ENCRYPTION_KEY_PERIODIC_JOB; +import static com.android.adservices.spe.AdservicesJobInfo.FLEDGE_AD_SELECTION_DEBUG_REPORT_SENDER_JOB; +import static com.android.adservices.spe.AdservicesJobInfo.FLEDGE_BACKGROUND_FETCH_JOB; +import static com.android.adservices.spe.AdservicesJobInfo.MAINTENANCE_JOB; +import static com.android.adservices.spe.AdservicesJobInfo.MDD_CELLULAR_CHARGING_PERIODIC_TASK_JOB; +import static com.android.adservices.spe.AdservicesJobInfo.MDD_CHARGING_PERIODIC_TASK_JOB; +import static com.android.adservices.spe.AdservicesJobInfo.MDD_MAINTENANCE_PERIODIC_TASK_JOB; +import static com.android.adservices.spe.AdservicesJobInfo.MDD_WIFI_CHARGING_PERIODIC_TASK_JOB; +import static com.android.adservices.spe.AdservicesJobInfo.MEASUREMENT_AGGREGATE_FALLBACK_REPORTING_JOB; +import static com.android.adservices.spe.AdservicesJobInfo.MEASUREMENT_AGGREGATE_MAIN_REPORTING_JOB; +import static com.android.adservices.spe.AdservicesJobInfo.MEASUREMENT_ASYNC_REGISTRATION_FALLBACK_JOB; +import static com.android.adservices.spe.AdservicesJobInfo.MEASUREMENT_ASYNC_REGISTRATION_JOB; +import static com.android.adservices.spe.AdservicesJobInfo.MEASUREMENT_ATTRIBUTION_FALLBACK_JOB; +import static com.android.adservices.spe.AdservicesJobInfo.MEASUREMENT_ATTRIBUTION_JOB; +import static com.android.adservices.spe.AdservicesJobInfo.MEASUREMENT_DEBUG_REPORTING_FALLBACK_JOB; +import static com.android.adservices.spe.AdservicesJobInfo.MEASUREMENT_DELETE_EXPIRED_JOB; +import static com.android.adservices.spe.AdservicesJobInfo.MEASUREMENT_DELETE_UNINSTALLED_JOB; +import static com.android.adservices.spe.AdservicesJobInfo.MEASUREMENT_EVENT_FALLBACK_REPORTING_JOB; +import static com.android.adservices.spe.AdservicesJobInfo.MEASUREMENT_EVENT_MAIN_REPORTING_JOB; +import static com.android.adservices.spe.AdservicesJobInfo.MEASUREMENT_VERBOSE_DEBUG_REPORTING_FALLBACK_JOB; +import static com.android.adservices.spe.AdservicesJobInfo.PERIODIC_SIGNALS_ENCODING_JOB; +import static com.android.adservices.spe.AdservicesJobInfo.TOPICS_EPOCH_JOB; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.argThat; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeastOnce; +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.eq; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions; +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.assertNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.adservices.AdServicesManager; +import android.app.adservices.IAdServicesManager; +import android.app.adservices.consent.ConsentParcel; +import android.app.job.JobScheduler; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.os.RemoteException; +import android.os.SystemClock; + +import androidx.test.core.content.pm.ApplicationInfoBuilder; +import androidx.test.filters.SmallTest; + +import com.android.adservices.AdServicesCommon; +import com.android.adservices.cobalt.CobaltJobService; +import com.android.adservices.common.AdServicesExtendedMockitoTestCase; +import com.android.adservices.data.DbTestUtil; +import com.android.adservices.data.adselection.AppInstallDao; +import com.android.adservices.data.adselection.FrequencyCapDao; +import com.android.adservices.data.common.BooleanFileDatastore; +import com.android.adservices.data.consent.AppConsentDao; +import com.android.adservices.data.consent.AppConsentDaoFixture; +import com.android.adservices.data.customaudience.CustomAudienceDao; +import com.android.adservices.data.enrollment.EnrollmentDao; +import com.android.adservices.data.topics.Topic; +import com.android.adservices.data.topics.TopicsTables; +import com.android.adservices.download.MddJobService; +import com.android.adservices.errorlogging.ErrorLogUtil; +import com.android.adservices.service.Flags; +import com.android.adservices.service.FlagsFactory; +import com.android.adservices.service.MaintenanceJobService; +import com.android.adservices.service.appsearch.AppSearchConsentStorageManager; +import com.android.adservices.service.common.BackgroundJobsManager; +import com.android.adservices.service.common.UserProfileIdManager; +import com.android.adservices.service.common.compat.FileCompatUtils; +import com.android.adservices.service.common.compat.PackageManagerCompatUtils; +import com.android.adservices.service.common.feature.PrivacySandboxFeatureType; +import com.android.adservices.service.encryptionkey.EncryptionKeyJobService; +import com.android.adservices.service.extdata.AdServicesExtDataStorageServiceManager; +import com.android.adservices.service.measurement.DeleteExpiredJobService; +import com.android.adservices.service.measurement.DeleteUninstalledJobService; +import com.android.adservices.service.measurement.MeasurementImpl; +import com.android.adservices.service.measurement.attribution.AttributionFallbackJobService; +import com.android.adservices.service.measurement.attribution.AttributionJobService; +import com.android.adservices.service.measurement.registration.AsyncRegistrationFallbackJobService; +import com.android.adservices.service.measurement.registration.AsyncRegistrationQueueJobService; +import com.android.adservices.service.measurement.reporting.AggregateFallbackReportingJobService; +import com.android.adservices.service.measurement.reporting.AggregateReportingJobService; +import com.android.adservices.service.measurement.reporting.DebugReportingFallbackJobService; +import com.android.adservices.service.measurement.reporting.EventFallbackReportingJobService; +import com.android.adservices.service.measurement.reporting.EventReportingJobService; +import com.android.adservices.service.measurement.reporting.VerboseDebugReportingFallbackJobService; +import com.android.adservices.service.stats.AdServicesLoggerImpl; +import com.android.adservices.service.stats.ConsentMigrationStats; +import com.android.adservices.service.stats.StatsdAdServicesLogger; +import com.android.adservices.service.stats.UiStatsLogger; +import com.android.adservices.service.topics.AppUpdateManager; +import com.android.adservices.service.topics.BlockedTopicsManager; +import com.android.adservices.service.topics.CacheManager; +import com.android.adservices.service.topics.EpochJobService; +import com.android.adservices.service.topics.EpochManager; +import com.android.adservices.service.topics.TopicsWorker; +import com.android.adservices.service.ui.data.UxStatesDao; +import com.android.adservices.service.ui.enrollment.collection.PrivacySandboxEnrollmentChannelCollection; +import com.android.adservices.service.ui.ux.collection.PrivacySandboxUxCollection; +import com.android.dx.mockito.inline.extended.ExtendedMockito; +import com.android.dx.mockito.inline.extended.MockedVoidMethod; +import com.android.modules.utils.build.SdkLevel; +import com.android.modules.utils.testing.ExtendedMockitoRule.MockStatic; +import com.android.modules.utils.testing.ExtendedMockitoRule.SpyStatic; + +import com.google.common.collect.ImmutableList; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentMatcher; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.verification.VerificationMode; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@SpyStatic(AdServicesLoggerImpl.class) +@SpyStatic(AggregateFallbackReportingJobService.class) +@SpyStatic(AggregateReportingJobService.class) +@SpyStatic(AsyncRegistrationQueueJobService.class) +@SpyStatic(AsyncRegistrationFallbackJobService.class) +@SpyStatic(AttributionJobService.class) +@SpyStatic(AttributionFallbackJobService.class) +@SpyStatic(BackgroundJobsManager.class) +@SpyStatic(ConsentManagerV2.class) +@SpyStatic(DeleteExpiredJobService.class) +@SpyStatic(DeleteUninstalledJobService.class) +@SpyStatic(DeviceRegionProvider.class) +@SpyStatic(EpochJobService.class) +@SpyStatic(ErrorLogUtil.class) +@SpyStatic(EventFallbackReportingJobService.class) +@SpyStatic(EventReportingJobService.class) +@SpyStatic(DebugReportingFallbackJobService.class) +@SpyStatic(VerboseDebugReportingFallbackJobService.class) +@SpyStatic(FlagsFactory.class) +@SpyStatic(MaintenanceJobService.class) +@SpyStatic(MddJobService.class) +@SpyStatic(EncryptionKeyJobService.class) +@SpyStatic(CobaltJobService.class) +@SpyStatic(UiStatsLogger.class) +@SpyStatic(StatsdAdServicesLogger.class) +@MockStatic(PackageManagerCompatUtils.class) +@MockStatic(SdkLevel.class) +@SmallTest +public final class ConsentManagerV2Test extends AdServicesExtendedMockitoTestCase { + + @Spy private Context mContextSpy; + private BooleanFileDatastore mDatastore; + private BooleanFileDatastore mConsentDatastore; + private ConsentManagerV2 mConsentManager; + private AppConsentDao mAppConsentDaoSpy; + private EnrollmentDao mEnrollmentDaoSpy; + private AdServicesStorageManager mAdServicesStorageManager; + + private AdServicesManager mAdServicesManager; + + private AppConsentStorageManager mAppConsentStorageManager; + private AppConsentForRStorageManager mAppConsentForRStorageManager; + + @Mock private AdServicesExtDataStorageServiceManager mAdServicesExtDataManager; + + @Mock private AdServicesLoggerImpl mAdServicesLoggerImplMock; + + @Mock private TopicsWorker mTopicsWorkerMock; + @Mock private MeasurementImpl mMeasurementImplMock; + @Mock private CustomAudienceDao mCustomAudienceDaoMock; + @Mock private AppInstallDao mAppInstallDaoMock; + @Mock private FrequencyCapDao mFrequencyCapDaoMock; + @Mock private UiStatsLogger mUiStatsLoggerMock; + + @Mock private AppUpdateManager mAppUpdateManagerMock; + @Mock private CacheManager mCacheManagerMock; + @Mock private BlockedTopicsManager mBlockedTopicsManagerMock; + @Mock private EpochManager mMockEpochManager; + + @Mock private PackageManager mPackageManager; + @Mock private Flags mMockFlags; + @Mock private JobScheduler mJobSchedulerMock; + @Mock private IAdServicesManager mMockIAdServicesManager; + @Mock private AppSearchConsentStorageManager mAppSearchConsentManagerMock; + + @Mock private UserProfileIdManager mUserProfileIdManagerMock; + @Mock private UxStatesDao mUxStatesDaoMock; + @Mock private StatsdAdServicesLogger mStatsdAdServicesLoggerMock; + + @Before + public void setup() throws IOException { + mContextSpy = spy(appContext.get()); + doReturn(mStatsdAdServicesLoggerMock).when(StatsdAdServicesLogger::getInstance); + mDatastore = + spy( + new BooleanFileDatastore( + mContextSpy, + AppConsentDao.DATASTORE_NAME, + AppConsentDao.DATASTORE_VERSION)); + // For each file, we should ensure there is only one instance of datastore that is able to + // access it. (Refer to BooleanFileDatastore.class) + mConsentDatastore = spy(ConsentManagerV2.createAndInitializeDataStore(mContextSpy)); + mAppConsentDaoSpy = spy(new AppConsentDao(mDatastore, mContextSpy.getPackageManager())); + mEnrollmentDaoSpy = + spy( + new EnrollmentDao( + mContextSpy, DbTestUtil.getSharedDbHelperForTest(), mMockFlags)); + mAppConsentStorageManager = + spy( + new AppConsentStorageManager( + mConsentDatastore, mAppConsentDaoSpy, mUxStatesDaoMock)); + + mAppConsentForRStorageManager = + spy( + new AppConsentForRStorageManager( + mConsentDatastore, + mAppConsentDaoSpy, + mUxStatesDaoMock, + mAdServicesExtDataManager)); + mAdServicesManager = new AdServicesManager(mMockIAdServicesManager); + doReturn(mAdServicesManager).when(mContextSpy).getSystemService(AdServicesManager.class); + + doReturn(mAdServicesLoggerImplMock).when(AdServicesLoggerImpl::getInstance); + mAdServicesStorageManager = + spy(new AdServicesStorageManager(mAdServicesManager, mPackageManager)); + // Default to use PPAPI consent to test migration-irrelevant logic. + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.PPAPI_ONLY); + doReturn(mMockFlags).when(FlagsFactory::getFlags); + doReturn(true).when(mMockFlags).getFledgeAdSelectionFilteringEnabled(); + doReturn(true).when(mMockFlags).getAdservicesConsentMigrationLoggingEnabled(); + doReturn(true).when(mMockFlags).getEnrollmentEnableLimitedLogging(); + doReturn(mAdServicesLoggerImplMock).when(AdServicesLoggerImpl::getInstance); + doReturn(true).when(() -> EpochJobService.scheduleIfNeeded(any(Context.class), eq(false))); + doReturn(true) + .when(() -> MaintenanceJobService.scheduleIfNeeded(any(Context.class), eq(false))); + doReturn(true).when(() -> MddJobService.scheduleIfNeeded(any(Context.class), eq(false))); + doReturn(true) + .when( + () -> + EncryptionKeyJobService.scheduleIfNeeded( + any(Context.class), eq(false))); + doNothing().when(() -> AggregateReportingJobService.scheduleIfNeeded(any(), anyBoolean())); + doNothing() + .when( + () -> + AggregateFallbackReportingJobService.scheduleIfNeeded( + any(), anyBoolean())); + doNothing().when(() -> AttributionFallbackJobService.scheduleIfNeeded(any(), anyBoolean())); + doNothing() + .when( + () -> + AsyncRegistrationFallbackJobService.scheduleIfNeeded( + any(), anyBoolean())); + doNothing() + .when( + () -> + VerboseDebugReportingFallbackJobService.scheduleIfNeeded( + any(), anyBoolean())); + doNothing() + .when(() -> DebugReportingFallbackJobService.scheduleIfNeeded(any(), anyBoolean())); + doNothing().when(() -> AttributionJobService.scheduleIfNeeded(any(), anyBoolean())); + doReturn(true).when(() -> EpochJobService.scheduleIfNeeded(any(), anyBoolean())); + doReturn(true).when(() -> MddJobService.scheduleIfNeeded(any(), anyBoolean())); + doNothing().when(() -> EventReportingJobService.scheduleIfNeeded(any(), anyBoolean())); + doNothing() + .when(() -> EventFallbackReportingJobService.scheduleIfNeeded(any(), anyBoolean())); + doNothing().when(() -> DeleteExpiredJobService.scheduleIfNeeded(any(), anyBoolean())); + doNothing().when(() -> DeleteUninstalledJobService.scheduleIfNeeded(any(), anyBoolean())); + doReturn(true).when(() -> MaintenanceJobService.scheduleIfNeeded(any(), anyBoolean())); + doNothing() + .when(() -> AsyncRegistrationQueueJobService.scheduleIfNeeded(any(), anyBoolean())); + doReturn(true).when(() -> CobaltJobService.scheduleIfNeeded(any(), anyBoolean())); + doNothing().when(() -> UiStatsLogger.logOptInSelected()); + doNothing().when(() -> UiStatsLogger.logOptOutSelected()); + doNothing().when(() -> UiStatsLogger.logOptInSelected(any())); + doNothing().when(() -> UiStatsLogger.logOptOutSelected(any())); + // The consent_source_of_truth=APPSEARCH_ONLY value is overridden on T+, so ignore level. + doReturn(false).when(() -> SdkLevel.isAtLeastT()); + } + + @After + public void teardown() throws IOException { + mDatastore.clear(); + mConsentDatastore.clear(); + } + + @Test + public void testConsentIsGivenAfterEnabling_PpApiOnly() throws RemoteException, IOException { + boolean isGiven = true; + int consentSourceOfTruth = Flags.PPAPI_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(isGiven, consentSourceOfTruth); + + spyConsentManager.enable(mContextSpy); + + assertThat(spyConsentManager.getConsent().isGiven()).isTrue(); + + verifyConsentMigration( + /* isGiven */ isGiven, + /* hasWrittenToPpApi */ true, + /* hasWrittenToSystemServer */ false, + /* hasReadFromSystemServer */ false); + verifyDataCleanup(spyConsentManager); + } + + @Test + public void testConsentIsGivenAfterEnabling_SystemServerOnly() + throws RemoteException, IOException { + boolean isGiven = true; + int consentSourceOfTruth = Flags.SYSTEM_SERVER_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(isGiven, consentSourceOfTruth); + + spyConsentManager.enable(mContextSpy); + + assertThat(spyConsentManager.getConsent().isGiven()).isTrue(); + + verifyConsentMigration( + /* isGiven */ isGiven, + /* hasWrittenToPpApi */ false, + /* hasWrittenToSystemServer */ true, + /* hasReadFromSystemServer */ true); + verifyDataCleanup(spyConsentManager); + } + + @Test + public void testConsentIsGivenAfterEnabling_PPAPIAndSystemServer() + throws RemoteException, IOException { + boolean isGiven = true; + int consentSourceOfTruth = Flags.PPAPI_AND_SYSTEM_SERVER; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(isGiven, consentSourceOfTruth); + + spyConsentManager.enable(mContextSpy); + + assertThat(spyConsentManager.getConsent().isGiven()).isTrue(); + + verifyConsentMigration( + /* isGiven */ isGiven, + /* hasWrittenToPpApi */ true, + /* hasWrittenToSystemServer */ true, + /* hasReadFromSystemServer */ true); + verifyDataCleanup(spyConsentManager); + } + + @Test + public void testConsentIsGivenAfterEnabling_AppSearchOnly() throws Exception { + boolean isGiven = true; + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + doReturn(true).when(mMockFlags).getEnableAppsearchConsentData(); + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(isGiven, consentSourceOfTruth); + + spyConsentManager.enable(mContextSpy); + + assertThat(spyConsentManager.getConsent().isGiven()).isTrue(); + + verifyConsentMigration( + /* isGiven */ isGiven, + /* hasWrittenToPpApi */ false, + /* hasWrittenToSystemServer */ false, + /* hasReadFromSystemServer */ false); + verify(mAppSearchConsentManagerMock, atLeastOnce()).getConsent(AdServicesApiType.ALL_API); + verifyDataCleanup(spyConsentManager); + } + + @Test + public void testConsentManager_LazyEnable() throws Exception { + boolean isGiven = true; + int consentSourceOfTruth = Flags.PPAPI_AND_SYSTEM_SERVER; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(isGiven, consentSourceOfTruth); + doReturn(ConsentParcel.createGivenConsent(ConsentParcel.ALL_API)) + .when(mMockIAdServicesManager) + .getConsent(ConsentParcel.ALL_API); + + doReturn(true).when(mMockFlags).getConsentManagerLazyEnableMode(); + spyConsentManager.enable(mContextSpy); + spyConsentManager.enable(mContextSpy); + assertThat(spyConsentManager.getConsent().isGiven()).isTrue(); + verify(spyConsentManager, times(0)).setConsentToSourceOfTruth(isGiven); + verifyResetApiCalled(spyConsentManager, 0); + } + + @Test + public void testConsentManager_LazyDisabled() throws Exception { + boolean isGiven = true; + int consentSourceOfTruth = Flags.PPAPI_AND_SYSTEM_SERVER; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(isGiven, consentSourceOfTruth); + doReturn(ConsentParcel.createGivenConsent(ConsentParcel.ALL_API)) + .when(mMockIAdServicesManager) + .getConsent(ConsentParcel.ALL_API); + doReturn(false).when(mMockFlags).getConsentManagerLazyEnableMode(); + spyConsentManager.enable(mContextSpy); + + assertThat(spyConsentManager.getConsent().isGiven()).isTrue(); + verify(spyConsentManager).setConsentToSourceOfTruth(isGiven); + verifyResetApiCalled(spyConsentManager, 1); + } + + @Test + public void testConsentManagerPreApi_LazyEnable() throws Exception { + boolean isGiven = true; + int consentSourceOfTruth = Flags.SYSTEM_SERVER_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(isGiven, consentSourceOfTruth); + doReturn(ConsentParcel.createGivenConsent(ConsentParcel.MEASUREMENT)) + .when(mMockIAdServicesManager) + .getConsent(ConsentParcel.MEASUREMENT); + + doReturn(true).when(mMockFlags).getConsentManagerLazyEnableMode(); + doReturn(true).when(mMockFlags).getGaUxFeatureEnabled(); + spyConsentManager.setPerApiConsentToSourceOfTruth(true, AdServicesApiType.ALL_API); + spyConsentManager.enable(mContextSpy, AdServicesApiType.MEASUREMENTS); + assertThat(spyConsentManager.getConsent(AdServicesApiType.MEASUREMENTS).isGiven()).isTrue(); + verify(spyConsentManager, never()).resetByApi(eq(AdServicesApiType.MEASUREMENTS)); + } + + @Test + public void testConsentManagerPreApi_LazyDisabled() throws Exception { + boolean isGiven = true; + int consentSourceOfTruth = Flags.SYSTEM_SERVER_ONLY; + doReturn(PrivacySandboxUxCollection.UNSUPPORTED_UX).when(mAdServicesStorageManager).getUx(); + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(isGiven, consentSourceOfTruth); + doReturn(ConsentParcel.createGivenConsent(ConsentParcel.MEASUREMENT)) + .when(mMockIAdServicesManager) + .getConsent(ConsentParcel.MEASUREMENT); + doReturn(false).when(mMockFlags).getConsentManagerLazyEnableMode(); + doReturn(true).when(mMockFlags).getGaUxFeatureEnabled(); + spyConsentManager.enable(mContextSpy, AdServicesApiType.MEASUREMENTS); + assertThat(spyConsentManager.getConsent(AdServicesApiType.MEASUREMENTS).isGiven()).isTrue(); + + verify(spyConsentManager).resetByApi(eq(AdServicesApiType.MEASUREMENTS)); + } + + private static void verifyResetApiCalled( + ConsentManagerV2 spyConsentManager, int wantedNumOfInvocations) throws IOException { + verify(spyConsentManager, times(wantedNumOfInvocations)).resetTopicsAndBlockedTopics(); + verify(spyConsentManager, times(wantedNumOfInvocations)).clearAllAppConsentData(); + verify(spyConsentManager, times(wantedNumOfInvocations)).resetMeasurement(); + } + + @Test + public void testConsentIsRevokedAfterDisabling_PpApiOnly() throws RemoteException, IOException { + boolean isGiven = false; + int consentSourceOfTruth = Flags.PPAPI_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(isGiven, consentSourceOfTruth); + + spyConsentManager.disable(mContextSpy); + + assertThat(spyConsentManager.getConsent().isGiven()).isFalse(); + + verifyConsentMigration( + /* isGiven */ isGiven, + /* hasWrittenToPpApi */ true, + /* hasWrittenToSystemServer */ false, + /* hasReadFromSystemServer */ false); + verifyDataCleanup(spyConsentManager); + } + + @Test + public void testConsentIsRevokedAfterDisabling_SystemServerOnly() + throws RemoteException, IOException { + boolean isGiven = false; + int consentSourceOfTruth = Flags.SYSTEM_SERVER_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(isGiven, consentSourceOfTruth); + + spyConsentManager.disable(mContextSpy); + + assertThat(spyConsentManager.getConsent().isGiven()).isFalse(); + + verifyConsentMigration( + /* isGiven */ isGiven, + /* hasWrittenToPpApi */ false, + /* hasWrittenToSystemServer */ true, + /* hasReadFromSystemServer */ true); + verifyDataCleanup(spyConsentManager); + } + + @Test + public void testConsentIsRevokedAfterDisabling_PpApiAndSystemServer() + throws RemoteException, IOException { + boolean isGiven = false; + int consentSourceOfTruth = Flags.PPAPI_AND_SYSTEM_SERVER; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(isGiven, consentSourceOfTruth); + + spyConsentManager.disable(mContextSpy); + + assertThat(spyConsentManager.getConsent().isGiven()).isFalse(); + + verifyConsentMigration( + /* isGiven */ isGiven, + /* hasWrittenToPpApi */ true, + /* hasWrittenToSystemServer */ true, + /* hasReadFromSystemServer */ true); + verifyDataCleanup(spyConsentManager); + } + + @Test + public void testConsentIsRevokedAfterDisabling_AppSearchOnly() + throws RemoteException, IOException { + boolean isGiven = false; + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + doReturn(true).when(mMockFlags).getEnableAppsearchConsentData(); + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(isGiven, consentSourceOfTruth); + assertThat(spyConsentManager.getConsent().isGiven()).isFalse(); + spyConsentManager.disable(mContextSpy); + + assertThat(spyConsentManager.getConsent().isGiven()).isFalse(); + + verifyConsentMigration( + /* isGiven */ isGiven, + /* hasWrittenToPpApi */ false, + /* hasWrittenToSystemServer */ false, + /* hasReadFromSystemServer */ false); + verify(mAppSearchConsentManagerMock, atLeastOnce()).getConsent(AdServicesApiType.ALL_API); + verifyDataCleanup(spyConsentManager); + } + + @Test + public void testConsentIsRevokedAfterDisabling_notSupportedFlag() throws RemoteException { + boolean isGiven = true; + int invalidConsentSourceOfTruth = 5; + assertThrows( + IllegalArgumentException.class, + () -> + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ isGiven, invalidConsentSourceOfTruth)); + } + + @Test + public void testJobsAreScheduledAfterEnablingKillSwitchOff() { + doReturn(false).when(mMockFlags).getTopicsKillSwitch(); + doReturn(false).when(mMockFlags).getFledgeSelectAdsKillSwitch(); + doReturn(false).when(mMockFlags).getMeasurementKillSwitch(); + doReturn(false).when(mMockFlags).getMddBackgroundTaskKillSwitch(); + doReturn(true).when(mMockFlags).getCobaltLoggingEnabled(); + + mConsentManager.enable(mContextSpy); + + verify(() -> BackgroundJobsManager.scheduleAllBackgroundJobs(any(Context.class))); + verify(() -> EpochJobService.scheduleIfNeeded(any(Context.class), eq(false))); + verify(() -> MddJobService.scheduleIfNeeded(any(Context.class), eq(false)), times(3)); + verify( + () -> EncryptionKeyJobService.scheduleIfNeeded(any(Context.class), eq(false)), + times(2)); + verify( + () -> MaintenanceJobService.scheduleIfNeeded(any(Context.class), eq(false)), + times(2)); + verify(() -> AggregateReportingJobService.scheduleIfNeeded(any(Context.class), eq(false))); + verify( + () -> + AggregateFallbackReportingJobService.scheduleIfNeeded( + any(Context.class), eq(false))); + verify(() -> AttributionFallbackJobService.scheduleIfNeeded(any(Context.class), eq(false))); + verify( + () -> + AsyncRegistrationFallbackJobService.scheduleIfNeeded( + any(Context.class), eq(false))); + verify( + () -> + VerboseDebugReportingFallbackJobService.scheduleIfNeeded( + any(Context.class), eq(false))); + verify( + () -> + DebugReportingFallbackJobService.scheduleIfNeeded( + any(Context.class), eq(false))); + verify(() -> AttributionJobService.scheduleIfNeeded(any(Context.class), eq(false))); + verify(() -> EventReportingJobService.scheduleIfNeeded(any(Context.class), eq(false))); + verify( + () -> + EventFallbackReportingJobService.scheduleIfNeeded( + any(Context.class), eq(false))); + verify(() -> DeleteExpiredJobService.scheduleIfNeeded(any(Context.class), eq(false))); + verify(() -> DeleteUninstalledJobService.scheduleIfNeeded(any(Context.class), eq(false))); + verify( + () -> + AsyncRegistrationQueueJobService.scheduleIfNeeded( + any(Context.class), eq(false))); + verify(() -> CobaltJobService.scheduleIfNeeded(any(Context.class), eq(false))); + } + + @Test + public void testJobsAreNotScheduledAfterEnablingKillSwitchOn() { + doReturn(true).when(mMockFlags).getTopicsKillSwitch(); + doReturn(true).when(mMockFlags).getFledgeSelectAdsKillSwitch(); + doReturn(true).when(mMockFlags).getMeasurementKillSwitch(); + doReturn(true).when(mMockFlags).getMddBackgroundTaskKillSwitch(); + doReturn(false).when(mMockFlags).getCobaltLoggingEnabled(); + + mConsentManager.enable(mContextSpy); + + verify(() -> BackgroundJobsManager.scheduleAllBackgroundJobs(any(Context.class))); + verify(() -> EpochJobService.scheduleIfNeeded(any(Context.class), eq(false)), never()); + verify( + () -> MaintenanceJobService.scheduleIfNeeded(any(Context.class), eq(false)), + never()); + verify(() -> MddJobService.scheduleIfNeeded(any(Context.class), eq(false)), never()); + verify( + () -> EncryptionKeyJobService.scheduleIfNeeded(any(Context.class), eq(false)), + never()); + verify( + () -> AggregateReportingJobService.scheduleIfNeeded(any(Context.class), eq(false)), + never()); + verify( + () -> + AggregateFallbackReportingJobService.scheduleIfNeeded( + any(Context.class), eq(false)), + never()); + verify( + () -> AttributionFallbackJobService.scheduleIfNeeded(any(Context.class), eq(false)), + never()); + verify( + () -> + AsyncRegistrationFallbackJobService.scheduleIfNeeded( + any(Context.class), eq(false)), + never()); + verify( + () -> + VerboseDebugReportingFallbackJobService.scheduleIfNeeded( + any(Context.class), eq(false)), + never()); + verify( + () -> + DebugReportingFallbackJobService.scheduleIfNeeded( + any(Context.class), eq(false)), + never()); + verify( + () -> AttributionJobService.scheduleIfNeeded(any(Context.class), eq(false)), + never()); + verify( + () -> EventReportingJobService.scheduleIfNeeded(any(Context.class), eq(false)), + never()); + verify( + () -> + EventFallbackReportingJobService.scheduleIfNeeded( + any(Context.class), eq(false)), + never()); + verify( + () -> DeleteExpiredJobService.scheduleIfNeeded(any(Context.class), eq(false)), + never()); + verify( + () -> DeleteUninstalledJobService.scheduleIfNeeded(any(Context.class), eq(false)), + never()); + verify( + () -> + AsyncRegistrationQueueJobService.scheduleIfNeeded( + any(Context.class), eq(false)), + never()); + verify(() -> CobaltJobService.scheduleIfNeeded(any(Context.class), eq(false)), never()); + } + + @Test + public void testJobsAreUnscheduledAfterDisabling() { + doReturn(mJobSchedulerMock).when(mContextSpy).getSystemService(JobScheduler.class); + mConsentManager.disable(mContextSpy); + + verify(() -> UiStatsLogger.logOptOutSelected()); + + verify(mJobSchedulerMock).cancel(MAINTENANCE_JOB.getJobId()); + verify(mJobSchedulerMock).cancel(TOPICS_EPOCH_JOB.getJobId()); + verify(mJobSchedulerMock).cancel(MEASUREMENT_EVENT_MAIN_REPORTING_JOB.getJobId()); + verify(mJobSchedulerMock).cancel(MEASUREMENT_DELETE_EXPIRED_JOB.getJobId()); + verify(mJobSchedulerMock).cancel(MEASUREMENT_DELETE_UNINSTALLED_JOB.getJobId()); + verify(mJobSchedulerMock).cancel(MEASUREMENT_ATTRIBUTION_JOB.getJobId()); + verify(mJobSchedulerMock).cancel(MEASUREMENT_EVENT_FALLBACK_REPORTING_JOB.getJobId()); + verify(mJobSchedulerMock).cancel(MEASUREMENT_AGGREGATE_MAIN_REPORTING_JOB.getJobId()); + verify(mJobSchedulerMock).cancel(MEASUREMENT_AGGREGATE_FALLBACK_REPORTING_JOB.getJobId()); + verify(mJobSchedulerMock).cancel(MEASUREMENT_ASYNC_REGISTRATION_JOB.getJobId()); + verify(mJobSchedulerMock).cancel(MEASUREMENT_ATTRIBUTION_FALLBACK_JOB.getJobId()); + verify(mJobSchedulerMock).cancel(MEASUREMENT_ASYNC_REGISTRATION_FALLBACK_JOB.getJobId()); + verify(mJobSchedulerMock) + .cancel(MEASUREMENT_VERBOSE_DEBUG_REPORTING_FALLBACK_JOB.getJobId()); + verify(mJobSchedulerMock).cancel(MEASUREMENT_DEBUG_REPORTING_FALLBACK_JOB.getJobId()); + verify(mJobSchedulerMock).cancel(FLEDGE_BACKGROUND_FETCH_JOB.getJobId()); + verify(mJobSchedulerMock).cancel(FLEDGE_AD_SELECTION_DEBUG_REPORT_SENDER_JOB.getJobId()); + verify(mJobSchedulerMock).cancel(PERIODIC_SIGNALS_ENCODING_JOB.getJobId()); + verify(mJobSchedulerMock).cancel(CONSENT_NOTIFICATION_JOB.getJobId()); + verify(mJobSchedulerMock).cancel(MDD_MAINTENANCE_PERIODIC_TASK_JOB.getJobId()); + verify(mJobSchedulerMock).cancel(MDD_CHARGING_PERIODIC_TASK_JOB.getJobId()); + verify(mJobSchedulerMock).cancel(MDD_CELLULAR_CHARGING_PERIODIC_TASK_JOB.getJobId()); + verify(mJobSchedulerMock).cancel(MDD_WIFI_CHARGING_PERIODIC_TASK_JOB.getJobId()); + verify(mJobSchedulerMock).cancel(ENCRYPTION_KEY_PERIODIC_JOB.getJobId()); + verify(mJobSchedulerMock).cancel(COBALT_LOGGING_JOB.getJobId()); + + verifyNoMoreInteractions(mJobSchedulerMock); + } + + @Test + public void testDataIsResetAfterConsentIsRevoked() throws IOException { + mConsentManager.disable(mContextSpy); + + verify(() -> UiStatsLogger.logOptOutSelected()); + + SystemClock.sleep(1000); + verify(mTopicsWorkerMock).clearAllTopicsData(any()); + // TODO(b/240988406): change to test for correct method call + verify(mAppConsentDaoSpy).clearAllConsentData(); + verify(mEnrollmentDaoSpy).deleteAll(); + verify(mMeasurementImplMock).deleteAllMeasurementData(any()); + verify(mCustomAudienceDaoMock).deleteAllCustomAudienceData(); + verify(mAppInstallDaoMock).deleteAllAppInstallData(); + verify(mFrequencyCapDaoMock).deleteAllHistogramData(); + verify(mUserProfileIdManagerMock).deleteId(); + } + + @Test + public void testDataIsResetAfterConsentIsRevokedFilteringDisabled() throws IOException { + doReturn(false).when(mMockFlags).getFledgeAdSelectionFilteringEnabled(); + mConsentManager.disable(mContextSpy); + + verify(() -> UiStatsLogger.logOptOutSelected()); + + SystemClock.sleep(1000); + verify(mTopicsWorkerMock).clearAllTopicsData(any()); + // TODO(b/240988406): change to test for correct method call + verify(mAppConsentDaoSpy).clearAllConsentData(); + verify(mEnrollmentDaoSpy).deleteAll(); + verify(mMeasurementImplMock).deleteAllMeasurementData(any()); + verify(mCustomAudienceDaoMock).deleteAllCustomAudienceData(); + verifyZeroInteractions(mAppInstallDaoMock, mFrequencyCapDaoMock); + verify(mUserProfileIdManagerMock).deleteId(); + } + + @Test + public void testDataIsResetAfterConsentIsGiven() throws IOException { + mConsentManager.enable(mContextSpy); + + verify(() -> UiStatsLogger.logOptInSelected()); + + SystemClock.sleep(1000); + verify(mTopicsWorkerMock).clearAllTopicsData(any()); + // TODO(b/240988406): change to test for correct method call + verify(mAppConsentDaoSpy).clearAllConsentData(); + verify(mMeasurementImplMock).deleteAllMeasurementData(any()); + verify(mCustomAudienceDaoMock).deleteAllCustomAudienceData(); + verify(mAppInstallDaoMock).deleteAllAppInstallData(); + verify(mFrequencyCapDaoMock).deleteAllHistogramData(); + verify(mUserProfileIdManagerMock).deleteId(); + verify(mUserProfileIdManagerMock).getOrCreateId(); + } + + @Test + public void testDataIsResetAfterConsentIsGivenFilteringDisabled() throws IOException { + doReturn(false).when(mMockFlags).getFledgeAdSelectionFilteringEnabled(); + mConsentManager.enable(mContextSpy); + + verify(() -> UiStatsLogger.logOptInSelected()); + + SystemClock.sleep(1000); + verify(mTopicsWorkerMock).clearAllTopicsData(any()); + // TODO(b/240988406): change to test for correct method call + verify(mAppConsentDaoSpy).clearAllConsentData(); + verify(mMeasurementImplMock).deleteAllMeasurementData(any()); + verify(mCustomAudienceDaoMock).deleteAllCustomAudienceData(); + verifyZeroInteractions(mAppInstallDaoMock, mFrequencyCapDaoMock); + verify(mUserProfileIdManagerMock).deleteId(); + verify(mUserProfileIdManagerMock).getOrCreateId(); + } + + @Test + public void testIsFledgeConsentRevokedForAppWithFullApiConsentGaUxEnabled_ppApiOnly() + throws IOException, PackageManager.NameNotFoundException { + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(true); + mConsentManager.enable(mContextSpy, AdServicesApiType.FLEDGE); + assertTrue(mConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()); + + verify(() -> UiStatsLogger.logOptInSelected(AdServicesApiType.FLEDGE)); + + mockGetPackageUid(AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + mockGetPackageUid(AppConsentDaoFixture.APP20_PACKAGE_NAME, AppConsentDaoFixture.APP20_UID); + mockGetPackageUid(AppConsentDaoFixture.APP30_PACKAGE_NAME, AppConsentDaoFixture.APP30_UID); + + mDatastore.put(AppConsentDaoFixture.APP10_DATASTORE_KEY, false); + mDatastore.put(AppConsentDaoFixture.APP20_DATASTORE_KEY, true); + + assertFalse( + mConsentManager.isFledgeConsentRevokedForApp( + AppConsentDaoFixture.APP10_PACKAGE_NAME)); + assertTrue( + mConsentManager.isFledgeConsentRevokedForApp( + AppConsentDaoFixture.APP20_PACKAGE_NAME)); + assertFalse( + mConsentManager.isFledgeConsentRevokedForApp( + AppConsentDaoFixture.APP30_PACKAGE_NAME)); + } + + @Test + public void testIsFledgeConsentRevokedForAppWithFullApiConsentGaUxEnabled_systemServerOnly() + throws PackageManager.NameNotFoundException, RemoteException { + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(true); + int consentSourceOfTruth = Flags.SYSTEM_SERVER_ONLY; + mConsentManager = getConsentManagerByConsentSourceOfTruth(consentSourceOfTruth); + doReturn(ConsentParcel.createGivenConsent(ConsentParcel.FLEDGE)) + .when(mMockIAdServicesManager) + .getConsent(ConsentParcel.FLEDGE); + + assertTrue(mConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()); + mockGetPackageUid(AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + mockGetPackageUid(AppConsentDaoFixture.APP20_PACKAGE_NAME, AppConsentDaoFixture.APP20_UID); + mockGetPackageUid(AppConsentDaoFixture.APP30_PACKAGE_NAME, AppConsentDaoFixture.APP30_UID); + + doReturn(false) + .when(mMockIAdServicesManager) + .isConsentRevokedForApp( + AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + doReturn(true) + .when(mMockIAdServicesManager) + .isConsentRevokedForApp( + AppConsentDaoFixture.APP20_PACKAGE_NAME, AppConsentDaoFixture.APP20_UID); + doReturn(false) + .when(mMockIAdServicesManager) + .isConsentRevokedForApp( + AppConsentDaoFixture.APP30_PACKAGE_NAME, AppConsentDaoFixture.APP30_UID); + + assertFalse( + mConsentManager.isFledgeConsentRevokedForApp( + AppConsentDaoFixture.APP10_PACKAGE_NAME)); + assertTrue( + mConsentManager.isFledgeConsentRevokedForApp( + AppConsentDaoFixture.APP20_PACKAGE_NAME)); + assertFalse( + mConsentManager.isFledgeConsentRevokedForApp( + AppConsentDaoFixture.APP30_PACKAGE_NAME)); + } + + @Test + public void testIsFledgeConsentRevokedForAppWithFullApiConsentGaUxEnabled_appSearchOnly() + throws Exception { + runTestIsFledgeConsentRevokedForAppWithFullApiConsentAppSearchOnly(true); + } + + @Test + public void testIsFledgeConsentRevokedForAppWithFullApiConsentGaUxDisabled_appSearchOnly() + throws Exception { + runTestIsFledgeConsentRevokedForAppWithFullApiConsentAppSearchOnly(false); + } + + private void runTestIsFledgeConsentRevokedForAppWithFullApiConsentAppSearchOnly( + boolean isGaUxEnabled) throws Exception { + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(isGaUxEnabled); + when(mMockFlags.getEnableAppsearchConsentData()).thenReturn(true); + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + mConsentManager = getConsentManagerByConsentSourceOfTruth(consentSourceOfTruth); + when(mAppSearchConsentManagerMock.getConsent(any())).thenReturn(AdServicesApiConsent.GIVEN); + + mConsentManager.enable(mContextSpy, AdServicesApiType.FLEDGE); + verify(() -> UiStatsLogger.logOptInSelected(AdServicesApiType.FLEDGE)); + + String app1 = AppConsentDaoFixture.APP10_PACKAGE_NAME; + String app2 = AppConsentDaoFixture.APP20_PACKAGE_NAME; + String app3 = AppConsentDaoFixture.APP30_PACKAGE_NAME; + mockGetPackageUid(app1, AppConsentDaoFixture.APP10_UID); + mockGetPackageUid(app2, AppConsentDaoFixture.APP20_UID); + mockGetPackageUid(app3, AppConsentDaoFixture.APP30_UID); + + when(mAppSearchConsentManagerMock.isConsentRevokedForApp(app1)).thenReturn(false); + when(mAppSearchConsentManagerMock.isConsentRevokedForApp(app2)).thenReturn(true); + when(mAppSearchConsentManagerMock.isConsentRevokedForApp(app3)).thenReturn(false); + + assertFalse(mConsentManager.isFledgeConsentRevokedForApp(app1)); + assertTrue(mConsentManager.isFledgeConsentRevokedForApp(app2)); + assertFalse(mConsentManager.isFledgeConsentRevokedForApp(app3)); + } + + @Test + public void testIsFledgeConsentRevokedForAppWithFullApiConsentGaUxEnabled_ppApiAndSystemServer() + throws PackageManager.NameNotFoundException, RemoteException { + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(true); + int consentSourceOfTruth = Flags.PPAPI_AND_SYSTEM_SERVER; + mConsentManager = getConsentManagerByConsentSourceOfTruth(consentSourceOfTruth); + doReturn(ConsentParcel.createGivenConsent(ConsentParcel.FLEDGE)) + .when(mMockIAdServicesManager) + .getConsent(ConsentParcel.FLEDGE); + + assertTrue(mConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()); + mockGetPackageUid(AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + mockGetPackageUid(AppConsentDaoFixture.APP20_PACKAGE_NAME, AppConsentDaoFixture.APP20_UID); + mockGetPackageUid(AppConsentDaoFixture.APP30_PACKAGE_NAME, AppConsentDaoFixture.APP30_UID); + + doReturn(false) + .when(mMockIAdServicesManager) + .isConsentRevokedForApp( + AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + doReturn(true) + .when(mMockIAdServicesManager) + .isConsentRevokedForApp( + AppConsentDaoFixture.APP20_PACKAGE_NAME, AppConsentDaoFixture.APP20_UID); + doReturn(false) + .when(mMockIAdServicesManager) + .isConsentRevokedForApp( + AppConsentDaoFixture.APP30_PACKAGE_NAME, AppConsentDaoFixture.APP30_UID); + + assertFalse( + mConsentManager.isFledgeConsentRevokedForApp( + AppConsentDaoFixture.APP10_PACKAGE_NAME)); + assertTrue( + mConsentManager.isFledgeConsentRevokedForApp( + AppConsentDaoFixture.APP20_PACKAGE_NAME)); + assertFalse( + mConsentManager.isFledgeConsentRevokedForApp( + AppConsentDaoFixture.APP30_PACKAGE_NAME)); + } + + @Test + public void testIsFledgeConsentRevokedForAppWithoutPrivacySandboxConsentGaUxEnabled_ppApiOnly() + throws PackageManager.NameNotFoundException { + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(true); + mConsentManager.disable(mContextSpy, AdServicesApiType.FLEDGE); + assertFalse(mConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()); + + verify(() -> UiStatsLogger.logOptOutSelected(AdServicesApiType.FLEDGE)); + + mockGetPackageUid(AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + mockGetPackageUid(AppConsentDaoFixture.APP20_PACKAGE_NAME, AppConsentDaoFixture.APP20_UID); + + assertTrue( + mConsentManager.isFledgeConsentRevokedForApp( + AppConsentDaoFixture.APP10_PACKAGE_NAME)); + assertTrue( + mConsentManager.isFledgeConsentRevokedForApp( + AppConsentDaoFixture.APP20_PACKAGE_NAME)); + } + + @Test + public void testIsFledgeConsentRevokedForAppWithoutPrivacySandboxConsentGaUxEnabled_sysServer() + throws RemoteException { + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(true); + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.SYSTEM_SERVER_ONLY); + doReturn(ConsentParcel.createRevokedConsent(ConsentParcel.FLEDGE)) + .when(mMockIAdServicesManager) + .getConsent(ConsentParcel.FLEDGE); + assertFalse(mConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()); + + assertTrue( + mConsentManager.isFledgeConsentRevokedForApp( + AppConsentDaoFixture.APP10_PACKAGE_NAME)); + assertTrue( + mConsentManager.isFledgeConsentRevokedForApp( + AppConsentDaoFixture.APP20_PACKAGE_NAME)); + } + + @Test + public void testIsFledgeConsentRevokedForAppWithoutPrivacySandboxConsentGaUxEnabled_bothSrc() + throws RemoteException { + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(true); + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.PPAPI_AND_SYSTEM_SERVER); + doReturn(ConsentParcel.createRevokedConsent(ConsentParcel.FLEDGE)) + .when(mMockIAdServicesManager) + .getConsent(ConsentParcel.FLEDGE); + assertFalse(mConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()); + + assertTrue( + mConsentManager.isFledgeConsentRevokedForApp( + AppConsentDaoFixture.APP10_PACKAGE_NAME)); + assertTrue( + mConsentManager.isFledgeConsentRevokedForApp( + AppConsentDaoFixture.APP20_PACKAGE_NAME)); + } + + @Test + public void testIsFledgeConsentRevokedForNotFoundAppGaUxEnabledThrows_ppApiOnly() + throws PackageManager.NameNotFoundException { + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(true); + mConsentManager.enable(mContextSpy, AdServicesApiType.FLEDGE); + assertTrue(mConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()); + + verify(() -> UiStatsLogger.logOptInSelected(AdServicesApiType.FLEDGE)); + + mockThrowExceptionOnGetPackageUid(AppConsentDaoFixture.APP_NOT_FOUND_PACKAGE_NAME); + assertThrows( + IllegalArgumentException.class, + () -> + mConsentManager.isFledgeConsentRevokedForApp( + AppConsentDaoFixture.APP_NOT_FOUND_PACKAGE_NAME)); + } + + @Test + public void testIsFledgeConsentRevokedForNotFoundAppGaUxEnabledThrows_systemServerOnly() + throws PackageManager.NameNotFoundException, RemoteException { + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(true); + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.SYSTEM_SERVER_ONLY); + doReturn(ConsentParcel.createGivenConsent(ConsentParcel.FLEDGE)) + .when(mMockIAdServicesManager) + .getConsent(ConsentParcel.FLEDGE); + assertTrue(mConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()); + + mockThrowExceptionOnGetPackageUid(AppConsentDaoFixture.APP_NOT_FOUND_PACKAGE_NAME); + assertThrows( + IllegalArgumentException.class, + () -> + mConsentManager.isFledgeConsentRevokedForApp( + AppConsentDaoFixture.APP_NOT_FOUND_PACKAGE_NAME)); + } + + @Test + public void testIsFledgeConsentRevokedForNotFoundAppGaUxEnabledThrows_ppApiAndSystemServer() + throws PackageManager.NameNotFoundException, RemoteException { + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(true); + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.PPAPI_AND_SYSTEM_SERVER); + doReturn(ConsentParcel.createGivenConsent(ConsentParcel.FLEDGE)) + .when(mMockIAdServicesManager) + .getConsent(ConsentParcel.FLEDGE); + assertTrue(mConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()); + + mockThrowExceptionOnGetPackageUid(AppConsentDaoFixture.APP_NOT_FOUND_PACKAGE_NAME); + assertThrows( + IllegalArgumentException.class, + () -> + mConsentManager.isFledgeConsentRevokedForApp( + AppConsentDaoFixture.APP_NOT_FOUND_PACKAGE_NAME)); + } + + // AppSearch test for isFledgeConsentRevokedForAppAfterSettingFledgeUse with GA UX enabled. + @Test + public void testIsFledgeConsentRevokedForAppAfterSetFledgeUseWithFullApiConsentGaUxEnabled_as() + throws Exception { + runTestIsFledgeConsentRevokedForAppAfterSetFledgeUseWithFullApiConsentAppSearch(true); + } + + private void runTestIsFledgeConsentRevokedForAppAfterSetFledgeUseWithFullApiConsentAppSearch( + boolean isGaUxEnabled) throws Exception { + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.APPSEARCH_ONLY); + when(mMockFlags.getEnableAppsearchConsentData()).thenReturn(true); + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(isGaUxEnabled); + when(mAppSearchConsentManagerMock.getConsent(any())).thenReturn(AdServicesApiConsent.GIVEN); + mConsentManager.enable(mContextSpy); + verify(() -> UiStatsLogger.logOptInSelected()); + + String app1 = AppConsentDaoFixture.APP10_PACKAGE_NAME; + String app2 = AppConsentDaoFixture.APP20_PACKAGE_NAME; + String app3 = AppConsentDaoFixture.APP30_PACKAGE_NAME; + mockGetPackageUid(app1, AppConsentDaoFixture.APP10_UID); + mockGetPackageUid(app2, AppConsentDaoFixture.APP20_UID); + mockGetPackageUid(app3, AppConsentDaoFixture.APP30_UID); + + when(mAppSearchConsentManagerMock.setConsentForAppIfNew(app1, false)).thenReturn(false); + when(mAppSearchConsentManagerMock.setConsentForAppIfNew(app2, false)).thenReturn(true); + when(mAppSearchConsentManagerMock.setConsentForAppIfNew(app3, false)).thenReturn(false); + + assertFalse(mConsentManager.isFledgeConsentRevokedForAppAfterSettingFledgeUse(app1)); + assertTrue(mConsentManager.isFledgeConsentRevokedForAppAfterSettingFledgeUse(app2)); + assertFalse(mConsentManager.isFledgeConsentRevokedForAppAfterSettingFledgeUse(app3)); + } + + @Test + public void + testIsFledgeConsentRevokedForAppAfterSetFledgeUseWithFullApiConsentGaUxEnabled_ppApi() + throws IOException, PackageManager.NameNotFoundException { + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(true); + mConsentManager.enable(mContextSpy, AdServicesApiType.FLEDGE); + assertTrue(mConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()); + + verify(() -> UiStatsLogger.logOptInSelected(AdServicesApiType.FLEDGE)); + + mockGetPackageUid(AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + mockGetPackageUid(AppConsentDaoFixture.APP20_PACKAGE_NAME, AppConsentDaoFixture.APP20_UID); + mockGetPackageUid(AppConsentDaoFixture.APP30_PACKAGE_NAME, AppConsentDaoFixture.APP30_UID); + + mDatastore.put(AppConsentDaoFixture.APP10_DATASTORE_KEY, false); + mDatastore.put(AppConsentDaoFixture.APP20_DATASTORE_KEY, true); + + assertFalse( + mConsentManager.isFledgeConsentRevokedForAppAfterSettingFledgeUse( + AppConsentDaoFixture.APP10_PACKAGE_NAME)); + assertTrue( + mConsentManager.isFledgeConsentRevokedForAppAfterSettingFledgeUse( + AppConsentDaoFixture.APP20_PACKAGE_NAME)); + assertFalse( + mConsentManager.isFledgeConsentRevokedForAppAfterSettingFledgeUse( + AppConsentDaoFixture.APP30_PACKAGE_NAME)); + } + + @Test + public void + testIsFledgeConsentRevokedForAppAfterSetFledgeUseWithFullApiConsentGaUxEnabled_sysSer() + throws PackageManager.NameNotFoundException, RemoteException { + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(true); + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.SYSTEM_SERVER_ONLY); + doReturn(ConsentParcel.createGivenConsent(ConsentParcel.FLEDGE)) + .when(mMockIAdServicesManager) + .getConsent(ConsentParcel.FLEDGE); + assertTrue(mConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()); + + mockGetPackageUid(AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + mockGetPackageUid(AppConsentDaoFixture.APP20_PACKAGE_NAME, AppConsentDaoFixture.APP20_UID); + mockGetPackageUid(AppConsentDaoFixture.APP30_PACKAGE_NAME, AppConsentDaoFixture.APP30_UID); + + doReturn(false) + .when(mMockIAdServicesManager) + .setConsentForAppIfNew( + AppConsentDaoFixture.APP10_PACKAGE_NAME, + AppConsentDaoFixture.APP10_UID, + false); + doReturn(true) + .when(mMockIAdServicesManager) + .setConsentForAppIfNew( + AppConsentDaoFixture.APP20_PACKAGE_NAME, + AppConsentDaoFixture.APP20_UID, + false); + doReturn(false) + .when(mMockIAdServicesManager) + .setConsentForAppIfNew( + AppConsentDaoFixture.APP30_PACKAGE_NAME, + AppConsentDaoFixture.APP30_UID, + false); + + assertFalse( + mConsentManager.isFledgeConsentRevokedForAppAfterSettingFledgeUse( + AppConsentDaoFixture.APP10_PACKAGE_NAME)); + assertTrue( + mConsentManager.isFledgeConsentRevokedForAppAfterSettingFledgeUse( + AppConsentDaoFixture.APP20_PACKAGE_NAME)); + assertFalse( + mConsentManager.isFledgeConsentRevokedForAppAfterSettingFledgeUse( + AppConsentDaoFixture.APP30_PACKAGE_NAME)); + } + + @Test + public void + testIsFledgeConsentRevokedForAppAfterSetFledgeUseWithFullApiConsentGaUxEnabled_both() + throws PackageManager.NameNotFoundException, RemoteException { + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(true); + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.PPAPI_AND_SYSTEM_SERVER); + doReturn(ConsentParcel.createGivenConsent(ConsentParcel.FLEDGE)) + .when(mMockIAdServicesManager) + .getConsent(ConsentParcel.FLEDGE); + assertTrue(mConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()); + + mockGetPackageUid(AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + mockGetPackageUid(AppConsentDaoFixture.APP20_PACKAGE_NAME, AppConsentDaoFixture.APP20_UID); + mockGetPackageUid(AppConsentDaoFixture.APP30_PACKAGE_NAME, AppConsentDaoFixture.APP30_UID); + + doReturn(false) + .when(mMockIAdServicesManager) + .setConsentForAppIfNew( + AppConsentDaoFixture.APP10_PACKAGE_NAME, + AppConsentDaoFixture.APP10_UID, + false); + doReturn(true) + .when(mMockIAdServicesManager) + .setConsentForAppIfNew( + AppConsentDaoFixture.APP20_PACKAGE_NAME, + AppConsentDaoFixture.APP20_UID, + false); + doReturn(false) + .when(mMockIAdServicesManager) + .setConsentForAppIfNew( + AppConsentDaoFixture.APP30_PACKAGE_NAME, + AppConsentDaoFixture.APP30_UID, + false); + + assertFalse( + mConsentManager.isFledgeConsentRevokedForAppAfterSettingFledgeUse( + AppConsentDaoFixture.APP10_PACKAGE_NAME)); + assertTrue( + mConsentManager.isFledgeConsentRevokedForAppAfterSettingFledgeUse( + AppConsentDaoFixture.APP20_PACKAGE_NAME)); + assertFalse( + mConsentManager.isFledgeConsentRevokedForAppAfterSettingFledgeUse( + AppConsentDaoFixture.APP30_PACKAGE_NAME)); + } + + @Test + public void + testIsFledgeConsentRevokedForAppSetFledgeUseNoPrivacySandboxConsentGaUxEnabled_ppApi() + throws PackageManager.NameNotFoundException { + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(true); + + mConsentManager.disable(mContextSpy, AdServicesApiType.FLEDGE); + assertFalse(mConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()); + + mockGetPackageUid(AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + mockGetPackageUid(AppConsentDaoFixture.APP20_PACKAGE_NAME, AppConsentDaoFixture.APP20_UID); + + assertTrue( + mConsentManager.isFledgeConsentRevokedForAppAfterSettingFledgeUse( + AppConsentDaoFixture.APP10_PACKAGE_NAME)); + assertTrue( + mConsentManager.isFledgeConsentRevokedForAppAfterSettingFledgeUse( + AppConsentDaoFixture.APP20_PACKAGE_NAME)); + } + + @Test + public void + testIsFledgeConsentRevokedForAppSetFledgeUseNoPrivacySandboxConsentGaUxEnabled_sysSer() + throws RemoteException { + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(true); + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.SYSTEM_SERVER_ONLY); + doReturn(ConsentParcel.createRevokedConsent(ConsentParcel.FLEDGE)) + .when(mMockIAdServicesManager) + .getConsent(ConsentParcel.FLEDGE); + assertFalse(mConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()); + + assertTrue( + mConsentManager.isFledgeConsentRevokedForAppAfterSettingFledgeUse( + AppConsentDaoFixture.APP10_PACKAGE_NAME)); + assertTrue( + mConsentManager.isFledgeConsentRevokedForAppAfterSettingFledgeUse( + AppConsentDaoFixture.APP20_PACKAGE_NAME)); + } + + @Test + public void + testIsFledgeConsentRevokedForAppSetFledgeUseNoPrivacySandboxConsentGaUxEnabled_both() + throws RemoteException { + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(true); + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.PPAPI_AND_SYSTEM_SERVER); + doReturn(ConsentParcel.createRevokedConsent(ConsentParcel.FLEDGE)) + .when(mMockIAdServicesManager) + .getConsent(ConsentParcel.FLEDGE); + assertFalse(mConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()); + + assertTrue( + mConsentManager.isFledgeConsentRevokedForAppAfterSettingFledgeUse( + AppConsentDaoFixture.APP10_PACKAGE_NAME)); + assertTrue( + mConsentManager.isFledgeConsentRevokedForAppAfterSettingFledgeUse( + AppConsentDaoFixture.APP20_PACKAGE_NAME)); + } + + @Test + public void testIsFledgeConsentRevokedForAppAfterSettingFledgeUseThrows_ppApiOnly() + throws PackageManager.NameNotFoundException { + mConsentManager.enable(mContextSpy, AdServicesApiType.FLEDGE); + assertTrue(mConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()); + + verify(() -> UiStatsLogger.logOptInSelected(AdServicesApiType.FLEDGE)); + + mockThrowExceptionOnGetPackageUid(AppConsentDaoFixture.APP_NOT_FOUND_PACKAGE_NAME); + assertThrows( + IllegalArgumentException.class, + () -> + mConsentManager.isFledgeConsentRevokedForAppAfterSettingFledgeUse( + AppConsentDaoFixture.APP_NOT_FOUND_PACKAGE_NAME)); + } + + @Test + public void testIsFledgeConsentRevokedForAppAfterSettingFledgeUseThrows_systemServerOnly() + throws PackageManager.NameNotFoundException, RemoteException { + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.SYSTEM_SERVER_ONLY); + doReturn(ConsentParcel.createGivenConsent(ConsentParcel.FLEDGE)) + .when(mMockIAdServicesManager) + .getConsent(ConsentParcel.FLEDGE); + assertTrue(mConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()); + + mockThrowExceptionOnGetPackageUid(AppConsentDaoFixture.APP_NOT_FOUND_PACKAGE_NAME); + assertThrows( + IllegalArgumentException.class, + () -> + mConsentManager.isFledgeConsentRevokedForAppAfterSettingFledgeUse( + AppConsentDaoFixture.APP_NOT_FOUND_PACKAGE_NAME)); + } + + @Test + public void testIsFledgeConsentRevokedForAppAfterSettingFledgeUseThrows_ppApiAndSystemServer() + throws PackageManager.NameNotFoundException, RemoteException { + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.PPAPI_AND_SYSTEM_SERVER); + doReturn(ConsentParcel.createGivenConsent(ConsentParcel.FLEDGE)) + .when(mMockIAdServicesManager) + .getConsent(ConsentParcel.FLEDGE); + assertTrue(mConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()); + + mockThrowExceptionOnGetPackageUid(AppConsentDaoFixture.APP_NOT_FOUND_PACKAGE_NAME); + assertThrows( + IllegalArgumentException.class, + () -> + mConsentManager.isFledgeConsentRevokedForAppAfterSettingFledgeUse( + AppConsentDaoFixture.APP_NOT_FOUND_PACKAGE_NAME)); + } + + @Test + public void testGetKnownAppsWithConsent_ppApiOnly() + throws IOException, PackageManager.NameNotFoundException { + mConsentManager.enable(mContextSpy); + assertTrue(mConsentManager.getConsent().isGiven()); + + verify(() -> UiStatsLogger.logOptInSelected()); + + mockGetPackageUid(AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + mockGetPackageUid(AppConsentDaoFixture.APP20_PACKAGE_NAME, AppConsentDaoFixture.APP20_UID); + mockGetPackageUid(AppConsentDaoFixture.APP30_PACKAGE_NAME, AppConsentDaoFixture.APP30_UID); + + mDatastore.put(AppConsentDaoFixture.APP10_DATASTORE_KEY, false); + mDatastore.put(AppConsentDaoFixture.APP20_DATASTORE_KEY, false); + mDatastore.put(AppConsentDaoFixture.APP30_DATASTORE_KEY, false); + List<ApplicationInfo> applicationsInstalled = + createApplicationInfos( + AppConsentDaoFixture.APP10_PACKAGE_NAME, + AppConsentDaoFixture.APP20_PACKAGE_NAME, + AppConsentDaoFixture.APP30_PACKAGE_NAME); + mockInstalledApplications(applicationsInstalled); + + ImmutableList<App> knownAppsWithConsent = mConsentManager.getKnownAppsWithConsent(); + ImmutableList<App> appsWithRevokedConsent = mConsentManager.getAppsWithRevokedConsent(); + + // all apps have received a consent + assertThat(knownAppsWithConsent).hasSize(3); + assertThat(appsWithRevokedConsent).isEmpty(); + } + + @Test + public void testGetKnownAppsWithConsent_systemServerOnly() throws RemoteException { + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.SYSTEM_SERVER_ONLY); + doReturn(ConsentParcel.createGivenConsent(ConsentParcel.ALL_API)) + .when(mMockIAdServicesManager) + .getConsent(ConsentParcel.ALL_API); + assertTrue(mConsentManager.getConsent().isGiven()); + + List<ApplicationInfo> applicationsInstalled = + createApplicationInfos( + AppConsentDaoFixture.APP10_PACKAGE_NAME, + AppConsentDaoFixture.APP20_PACKAGE_NAME, + AppConsentDaoFixture.APP30_PACKAGE_NAME); + List<String> applicationsInstalledNames = + applicationsInstalled.stream() + .map(applicationInfo -> applicationInfo.packageName) + .collect(Collectors.toList()); + mockInstalledApplications(applicationsInstalled); + + doReturn(applicationsInstalledNames) + .when(mMockIAdServicesManager) + .getKnownAppsWithConsent( + argThat(new ListMatcherIgnoreOrder(applicationsInstalledNames))); + doReturn(List.of()) + .when(mMockIAdServicesManager) + .getAppsWithRevokedConsent( + argThat(new ListMatcherIgnoreOrder(applicationsInstalledNames))); + + ImmutableList<App> knownAppsWithConsent = mConsentManager.getKnownAppsWithConsent(); + ImmutableList<App> appsWithRevokedConsent = mConsentManager.getAppsWithRevokedConsent(); + + verify(mMockIAdServicesManager) + .getKnownAppsWithConsent( + argThat(new ListMatcherIgnoreOrder(applicationsInstalledNames))); + verify(mMockIAdServicesManager) + .getAppsWithRevokedConsent( + argThat(new ListMatcherIgnoreOrder(applicationsInstalledNames))); + + verify(mAdServicesStorageManager, times(2)).getInstalledPackages(); + verifyNoMoreInteractions(mAppConsentDaoSpy); + + // all apps have received a consent + assertThat(knownAppsWithConsent).hasSize(3); + assertThat(appsWithRevokedConsent).isEmpty(); + } + + @Test + public void testGetKnownAppsWithConsent_ppApiAndSystemServer() throws RemoteException { + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.PPAPI_AND_SYSTEM_SERVER); + doReturn(ConsentParcel.createGivenConsent(ConsentParcel.ALL_API)) + .when(mMockIAdServicesManager) + .getConsent(ConsentParcel.ALL_API); + assertTrue(mConsentManager.getConsent().isGiven()); + + List<ApplicationInfo> applicationsInstalled = + createApplicationInfos( + AppConsentDaoFixture.APP10_PACKAGE_NAME, + AppConsentDaoFixture.APP20_PACKAGE_NAME, + AppConsentDaoFixture.APP30_PACKAGE_NAME); + List<String> applicationsInstalledNames = + applicationsInstalled.stream() + .map(applicationInfo -> applicationInfo.packageName) + .collect(Collectors.toList()); + mockInstalledApplications(applicationsInstalled); + + doReturn(applicationsInstalledNames) + .when(mMockIAdServicesManager) + .getKnownAppsWithConsent( + argThat(new ListMatcherIgnoreOrder(applicationsInstalledNames))); + doReturn(List.of()) + .when(mMockIAdServicesManager) + .getAppsWithRevokedConsent( + argThat(new ListMatcherIgnoreOrder(applicationsInstalledNames))); + + ImmutableList<App> knownAppsWithConsent = mConsentManager.getKnownAppsWithConsent(); + ImmutableList<App> appsWithRevokedConsent = mConsentManager.getAppsWithRevokedConsent(); + + verify(mMockIAdServicesManager) + .getKnownAppsWithConsent( + argThat(new ListMatcherIgnoreOrder(applicationsInstalledNames))); + verify(mMockIAdServicesManager) + .getAppsWithRevokedConsent( + argThat(new ListMatcherIgnoreOrder(applicationsInstalledNames))); + + verify(mAdServicesStorageManager, times(2)).getInstalledPackages(); + verifyNoMoreInteractions(mAppConsentDaoSpy); + + // all apps have received a consent + assertThat(knownAppsWithConsent).hasSize(3); + assertThat(appsWithRevokedConsent).isEmpty(); + } + + @Test + public void testGetKnownAppsWithConsent_appSearchOnly() { + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.APPSEARCH_ONLY); + when(mMockFlags.getEnableAppsearchConsentData()).thenReturn(true); + + ImmutableList<String> consentedAppsList = + ImmutableList.of(AppConsentDaoFixture.APP10_PACKAGE_NAME); + ImmutableList<String> revokedAppsList = + ImmutableList.of( + AppConsentDaoFixture.APP20_PACKAGE_NAME, + AppConsentDaoFixture.APP30_PACKAGE_NAME); + + doReturn(consentedAppsList).when(mAppSearchConsentManagerMock).getKnownAppsWithConsent(); + doReturn(revokedAppsList).when(mAppSearchConsentManagerMock).getAppsWithRevokedConsent(); + + ImmutableList<App> knownAppsWithConsent = mConsentManager.getKnownAppsWithConsent(); + ImmutableList<App> appsWithRevokedConsent = mConsentManager.getAppsWithRevokedConsent(); + + verify(mAppSearchConsentManagerMock).getKnownAppsWithConsent(); + verify(mAppSearchConsentManagerMock).getAppsWithRevokedConsent(); + + // Correct apps have received consent. + assertThat(knownAppsWithConsent).hasSize(1); + assertThat(knownAppsWithConsent.get(0).getPackageName()) + .isEqualTo(AppConsentDaoFixture.APP10_PACKAGE_NAME); + assertThat(appsWithRevokedConsent).hasSize(2); + assertThat( + appsWithRevokedConsent.stream() + .map(app -> app.getPackageName()) + .collect(Collectors.toList())) + .containsAtLeast( + AppConsentDaoFixture.APP20_PACKAGE_NAME, + AppConsentDaoFixture.APP30_PACKAGE_NAME); + } + + @Test + public void testGetKnownAppsWithConsent_ppapiAndAdExtDataServiceOnly() throws IOException { + when(mMockFlags.getEnableAdExtServiceConsentData()).thenReturn(true); + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.PPAPI_AND_ADEXT_SERVICE); + assertThat(mConsentManager.getKnownAppsWithConsent()).isEqualTo(ImmutableList.of()); + } + + @Test + public void testGetKnownAppsWithConsentAfterConsentForOneOfThemWasRevoked_ppApiOnly() + throws IOException, PackageManager.NameNotFoundException { + // mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.PPAPI_ONLY); + doNothing().when(mCustomAudienceDaoMock).deleteCustomAudienceDataByOwner(any()); + + mConsentManager.enable(mContextSpy); + mConsentManager.setConsentToSourceOfTruth(true); + assertThat(mConsentManager.getConsent().isGiven()).isTrue(); + + verify(() -> UiStatsLogger.logOptInSelected()); + + mockGetPackageUid(AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + mockGetPackageUid(AppConsentDaoFixture.APP20_PACKAGE_NAME, AppConsentDaoFixture.APP20_UID); + mockGetPackageUid(AppConsentDaoFixture.APP30_PACKAGE_NAME, AppConsentDaoFixture.APP30_UID); + + mDatastore.put(AppConsentDaoFixture.APP10_DATASTORE_KEY, false); + mDatastore.put(AppConsentDaoFixture.APP20_DATASTORE_KEY, false); + mDatastore.put(AppConsentDaoFixture.APP30_DATASTORE_KEY, false); + List<ApplicationInfo> applicationsInstalled = + createApplicationInfos( + AppConsentDaoFixture.APP10_PACKAGE_NAME, + AppConsentDaoFixture.APP20_PACKAGE_NAME, + AppConsentDaoFixture.APP30_PACKAGE_NAME); + mockInstalledApplications(applicationsInstalled); + + App app = App.create(AppConsentDaoFixture.APP10_PACKAGE_NAME); + + // revoke consent for first app + mConsentManager.revokeConsentForApp(app); + ImmutableList<App> knownAppsWithConsent = mConsentManager.getKnownAppsWithConsent(); + ImmutableList<App> appsWithRevokedConsent = mConsentManager.getAppsWithRevokedConsent(); + + // all apps have received a consent + assertThat(knownAppsWithConsent).hasSize(2); + assertThat(appsWithRevokedConsent).hasSize(1); + App appWithRevokedConsent = appsWithRevokedConsent.get(0); + assertThat(appWithRevokedConsent.getPackageName()).isEqualTo(app.getPackageName()); + + SystemClock.sleep(1000); + verify(mCustomAudienceDaoMock).deleteCustomAudienceDataByOwner(app.getPackageName()); + verify(mAppInstallDaoMock).deleteByPackageName(app.getPackageName()); + verify(mFrequencyCapDaoMock).deleteHistogramDataBySourceApp(app.getPackageName()); + } + + @Test + public void testGetKnownAppsWithConsentAfterConsentForOneOfThemWasRevokedAndRestored_ppApiOnly() + throws IOException, PackageManager.NameNotFoundException { + doNothing().when(mCustomAudienceDaoMock).deleteCustomAudienceDataByOwner(any()); + + mConsentManager.enable(mContextSpy); + assertTrue(mConsentManager.getConsent().isGiven()); + + verify(() -> UiStatsLogger.logOptInSelected()); + + mockGetPackageUid(AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + mockGetPackageUid(AppConsentDaoFixture.APP20_PACKAGE_NAME, AppConsentDaoFixture.APP20_UID); + mockGetPackageUid(AppConsentDaoFixture.APP30_PACKAGE_NAME, AppConsentDaoFixture.APP30_UID); + + mDatastore.put(AppConsentDaoFixture.APP10_DATASTORE_KEY, false); + mDatastore.put(AppConsentDaoFixture.APP20_DATASTORE_KEY, false); + mDatastore.put(AppConsentDaoFixture.APP30_DATASTORE_KEY, false); + App app = App.create(AppConsentDaoFixture.APP10_PACKAGE_NAME); + List<ApplicationInfo> applicationsInstalled = + createApplicationInfos( + AppConsentDaoFixture.APP10_PACKAGE_NAME, + AppConsentDaoFixture.APP20_PACKAGE_NAME, + AppConsentDaoFixture.APP30_PACKAGE_NAME); + mockInstalledApplications(applicationsInstalled); + + // revoke consent for first app + mConsentManager.revokeConsentForApp(app); + ImmutableList<App> knownAppsWithConsent = mConsentManager.getKnownAppsWithConsent(); + ImmutableList<App> appsWithRevokedConsent = mConsentManager.getAppsWithRevokedConsent(); + + // all apps have received a consent + assertThat(knownAppsWithConsent).hasSize(2); + assertThat(appsWithRevokedConsent).hasSize(1); + App appWithRevokedConsent = appsWithRevokedConsent.get(0); + assertThat(appWithRevokedConsent.getPackageName()).isEqualTo(app.getPackageName()); + + SystemClock.sleep(1000); + verify(mCustomAudienceDaoMock).deleteCustomAudienceDataByOwner(app.getPackageName()); + verify(mAppInstallDaoMock).deleteByPackageName(app.getPackageName()); + verify(mFrequencyCapDaoMock).deleteHistogramDataBySourceApp(app.getPackageName()); + + // restore consent for first app + mConsentManager.restoreConsentForApp(app); + knownAppsWithConsent = mConsentManager.getKnownAppsWithConsent(); + appsWithRevokedConsent = mConsentManager.getAppsWithRevokedConsent(); + + // all apps have received a consent + assertThat(knownAppsWithConsent).hasSize(3); + assertThat(appsWithRevokedConsent).isEmpty(); + } + + @Test + public void testSetConsentForApp_ppApiOnly() throws Exception { + mConsentManager.enable(mContextSpy, AdServicesApiType.FLEDGE); + assertTrue(mConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()); + + verify(() -> UiStatsLogger.logOptInSelected(AdServicesApiType.FLEDGE)); + + mockGetPackageUid(AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + + mConsentManager.revokeConsentForApp(App.create(AppConsentDaoFixture.APP10_PACKAGE_NAME)); + assertTrue( + mConsentManager.isFledgeConsentRevokedForApp( + AppConsentDaoFixture.APP10_PACKAGE_NAME)); + + mConsentManager.restoreConsentForApp(App.create(AppConsentDaoFixture.APP10_PACKAGE_NAME)); + assertFalse( + mConsentManager.isFledgeConsentRevokedForApp( + AppConsentDaoFixture.APP10_PACKAGE_NAME)); + } + + @Test + public void testSetConsentForApp_systemServerOnly() throws Exception { + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.SYSTEM_SERVER_ONLY); + doReturn(ConsentParcel.createGivenConsent(ConsentParcel.FLEDGE)) + .when(mMockIAdServicesManager) + .getConsent(ConsentParcel.FLEDGE); + assertTrue(mConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()); + + mockGetPackageUid(AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + + mConsentManager.revokeConsentForApp(App.create(AppConsentDaoFixture.APP10_PACKAGE_NAME)); + verify(mMockIAdServicesManager) + .setConsentForApp( + AppConsentDaoFixture.APP10_PACKAGE_NAME, + AppConsentDaoFixture.APP10_UID, + true); + + mConsentManager.restoreConsentForApp(App.create(AppConsentDaoFixture.APP10_PACKAGE_NAME)); + verify(mMockIAdServicesManager) + .setConsentForApp( + AppConsentDaoFixture.APP10_PACKAGE_NAME, + AppConsentDaoFixture.APP10_UID, + false); + } + + @Test + public void testSetConsentForApp_ppApiAndSystemServer() throws Exception { + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.PPAPI_AND_SYSTEM_SERVER); + doReturn(ConsentParcel.createGivenConsent(ConsentParcel.FLEDGE)) + .when(mMockIAdServicesManager) + .getConsent(ConsentParcel.FLEDGE); + assertTrue(mConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()); + + mockGetPackageUid(AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + + mConsentManager.revokeConsentForApp(App.create(AppConsentDaoFixture.APP10_PACKAGE_NAME)); + verify(mMockIAdServicesManager) + .setConsentForApp( + AppConsentDaoFixture.APP10_PACKAGE_NAME, + AppConsentDaoFixture.APP10_UID, + true); + assertEquals(Boolean.TRUE, mDatastore.get(AppConsentDaoFixture.APP10_DATASTORE_KEY)); + + mConsentManager.restoreConsentForApp(App.create(AppConsentDaoFixture.APP10_PACKAGE_NAME)); + verify(mMockIAdServicesManager) + .setConsentForApp( + AppConsentDaoFixture.APP10_PACKAGE_NAME, + AppConsentDaoFixture.APP10_UID, + false); + assertEquals(Boolean.FALSE, mDatastore.get(AppConsentDaoFixture.APP10_DATASTORE_KEY)); + } + + @Test + public void testSetConsentForApp_appSearchOnly() throws Exception { + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.APPSEARCH_ONLY); + when(mMockFlags.getEnableAppsearchConsentData()).thenReturn(true); + mockGetPackageUid(AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + + App app = App.create(AppConsentDaoFixture.APP10_PACKAGE_NAME); + mConsentManager.revokeConsentForApp(app); + verify(mAppSearchConsentManagerMock).setConsentForApp(app.getPackageName(), true); + + mConsentManager.restoreConsentForApp(app); + verify(mAppSearchConsentManagerMock).setConsentForApp(app.getPackageName(), false); + } + + @Test + public void testRevokeConsentForApp_ppapiAndAdExtDataServiceOnly() { + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.PPAPI_AND_ADEXT_SERVICE); + when(mMockFlags.getEnableAdExtServiceConsentData()).thenReturn(true); + App app = App.create(AppConsentDaoFixture.APP10_PACKAGE_NAME); + + assertThrows(RuntimeException.class, () -> mConsentManager.revokeConsentForApp(app)); + } + + @Test + public void testRestoreConsentForApp_ppapiAndAdExtDataServiceOnly() { + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.PPAPI_AND_ADEXT_SERVICE); + when(mMockFlags.getEnableAdExtServiceConsentData()).thenReturn(true); + App app = App.create(AppConsentDaoFixture.APP10_PACKAGE_NAME); + + assertThrows(RuntimeException.class, () -> mConsentManager.restoreConsentForApp(app)); + } + + @Test + public void clearConsentForUninstalledApp_ppApiOnly() + throws PackageManager.NameNotFoundException, IOException { + mockGetPackageUid(AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + + mConsentManager.restoreConsentForApp(App.create(AppConsentDaoFixture.APP10_PACKAGE_NAME)); + assertEquals(Boolean.FALSE, mDatastore.get(AppConsentDaoFixture.APP10_DATASTORE_KEY)); + mConsentManager.clearConsentForUninstalledApp( + AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + assertNull(mDatastore.get(AppConsentDaoFixture.APP10_DATASTORE_KEY)); + } + + @Test + public void clearConsentForUninstalledApp_systemServerOnly() throws RemoteException { + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.SYSTEM_SERVER_ONLY); + mConsentManager.clearConsentForUninstalledApp( + AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + verify(mMockIAdServicesManager) + .clearConsentForUninstalledApp( + AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + } + + @Test + public void clearConsentForUninstalledApp_ppApiAndSystemServer() + throws PackageManager.NameNotFoundException, IOException, RemoteException { + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.PPAPI_AND_SYSTEM_SERVER); + mockGetPackageUid(AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + + mConsentManager.restoreConsentForApp(App.create(AppConsentDaoFixture.APP10_PACKAGE_NAME)); + assertEquals(Boolean.FALSE, mDatastore.get(AppConsentDaoFixture.APP10_DATASTORE_KEY)); + verify(mMockIAdServicesManager) + .setConsentForApp( + AppConsentDaoFixture.APP10_PACKAGE_NAME, + AppConsentDaoFixture.APP10_UID, + false); + mConsentManager.clearConsentForUninstalledApp( + AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + assertNull(mDatastore.get(AppConsentDaoFixture.APP10_DATASTORE_KEY)); + verify(mMockIAdServicesManager) + .clearConsentForUninstalledApp( + AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + } + + @Test + public void clearConsentForUninstalledApp_appSearchOnly() throws Exception { + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.APPSEARCH_ONLY); + when(mMockFlags.getEnableAppsearchConsentData()).thenReturn(true); + String packageName = AppConsentDaoFixture.APP10_PACKAGE_NAME; + mockGetPackageUid(packageName, AppConsentDaoFixture.APP10_UID); + + mConsentManager.clearConsentForUninstalledApp(packageName, AppConsentDaoFixture.APP10_UID); + verify(mAppSearchConsentManagerMock) + .clearConsentForUninstalledApp(packageName, AppConsentDaoFixture.APP10_UID); + } + + @Test + public void clearConsentForUninstalledApp_ppapiAndAdExtDataServiceOnly() { + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.PPAPI_AND_ADEXT_SERVICE); + when(mMockFlags.getEnableAdExtServiceConsentData()).thenReturn(true); + String packageName = AppConsentDaoFixture.APP10_PACKAGE_NAME; + + assertThrows( + RuntimeException.class, + () -> + mConsentManager.clearConsentForUninstalledApp( + packageName, AppConsentDaoFixture.APP10_UID)); + } + + @Test + public void clearConsentForUninstalledAppWithoutUid_ppApiOnly() throws IOException { + mDatastore.put(AppConsentDaoFixture.APP10_DATASTORE_KEY, true); + mDatastore.put(AppConsentDaoFixture.APP20_DATASTORE_KEY, true); + mDatastore.put(AppConsentDaoFixture.APP30_DATASTORE_KEY, false); + + mConsentManager.clearConsentForUninstalledApp(AppConsentDaoFixture.APP20_PACKAGE_NAME); + + assertEquals(true, mDatastore.get(AppConsentDaoFixture.APP10_DATASTORE_KEY)); + assertNull(mDatastore.get(AppConsentDaoFixture.APP20_DATASTORE_KEY)); + assertEquals(false, mDatastore.get(AppConsentDaoFixture.APP30_DATASTORE_KEY)); + + verify(mAppConsentDaoSpy).clearConsentForUninstalledApp(anyString()); + } + + @Test + public void clearConsentForUninstalledAppWithoutUid_ppApiOnly_validatesInput() { + assertThrows( + NullPointerException.class, + () -> { + mConsentManager.clearConsentForUninstalledApp(null); + }); + assertThrows( + IllegalArgumentException.class, + () -> { + mConsentManager.clearConsentForUninstalledApp(""); + }); + } + + @Test + public void testGetKnownTopicsWithConsent() { + long taxonomyVersion = 1L; + long modelVersion = 1L; + Topic topic1 = Topic.create(1, taxonomyVersion, modelVersion); + Topic topic2 = Topic.create(2, taxonomyVersion, modelVersion); + ImmutableList<Topic> expectedKnownTopicsWithConsent = ImmutableList.of(topic1, topic2); + doReturn(expectedKnownTopicsWithConsent) + .when(mTopicsWorkerMock) + .getKnownTopicsWithConsent(); + + ImmutableList<Topic> knownTopicsWithConsent = mConsentManager.getKnownTopicsWithConsent(); + + assertThat(knownTopicsWithConsent) + .containsExactlyElementsIn(expectedKnownTopicsWithConsent); + } + + @Test + public void testGetTopicsWithRevokedConsent() { + long taxonomyVersion = 1L; + long modelVersion = 1L; + Topic topic1 = Topic.create(1, taxonomyVersion, modelVersion); + Topic topic2 = Topic.create(2, taxonomyVersion, modelVersion); + ImmutableList<Topic> expectedTopicsWithRevokedConsent = ImmutableList.of(topic1, topic2); + doReturn(expectedTopicsWithRevokedConsent) + .when(mTopicsWorkerMock) + .getTopicsWithRevokedConsent(); + + ImmutableList<Topic> topicsWithRevokedConsent = + mConsentManager.getTopicsWithRevokedConsent(); + + assertThat(topicsWithRevokedConsent) + .containsExactlyElementsIn(expectedTopicsWithRevokedConsent); + } + + @Test + public void testResetAllAppConsentAndAppData_ppApiOnly() + throws IOException, PackageManager.NameNotFoundException { + doNothing().when(mCustomAudienceDaoMock).deleteAllCustomAudienceData(); + + // Prepopulate with consent data for some apps + mConsentManager.enable(mContextSpy); + assertTrue(mConsentManager.getConsent().isGiven()); + + verify(() -> UiStatsLogger.logOptInSelected()); + + mockGetPackageUid(AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + mockGetPackageUid(AppConsentDaoFixture.APP20_PACKAGE_NAME, AppConsentDaoFixture.APP20_UID); + mockGetPackageUid(AppConsentDaoFixture.APP30_PACKAGE_NAME, AppConsentDaoFixture.APP30_UID); + + mDatastore.put(AppConsentDaoFixture.APP10_DATASTORE_KEY, false); + mDatastore.put(AppConsentDaoFixture.APP20_DATASTORE_KEY, true); + mDatastore.put(AppConsentDaoFixture.APP30_DATASTORE_KEY, false); + List<ApplicationInfo> applicationsInstalled = + createApplicationInfos( + AppConsentDaoFixture.APP10_PACKAGE_NAME, + AppConsentDaoFixture.APP20_PACKAGE_NAME, + AppConsentDaoFixture.APP30_PACKAGE_NAME); + mockInstalledApplications(applicationsInstalled); + + // Verify population was successful + ImmutableList<App> knownAppsWithConsent = mConsentManager.getKnownAppsWithConsent(); + ImmutableList<App> appsWithRevokedConsent = mConsentManager.getAppsWithRevokedConsent(); + assertThat(knownAppsWithConsent).hasSize(2); + assertThat(appsWithRevokedConsent).hasSize(1); + + mConsentManager.clearAllAppConsentData(); + + // All app consent data was deleted + knownAppsWithConsent = mConsentManager.getKnownAppsWithConsent(); + appsWithRevokedConsent = mConsentManager.getAppsWithRevokedConsent(); + assertThat(knownAppsWithConsent).isEmpty(); + assertThat(appsWithRevokedConsent).isEmpty(); + + SystemClock.sleep(1000); + verify(mCustomAudienceDaoMock, times(2)).deleteAllCustomAudienceData(); + verify(mAppInstallDaoMock, times(2)).deleteAllAppInstallData(); + verify(mFrequencyCapDaoMock, times(2)).deleteAllHistogramData(); + } + + @Test + public void testResetAllAppConsentAndAppData_systemServerOnly() + throws IOException, RemoteException { + doNothing().when(mCustomAudienceDaoMock).deleteAllCustomAudienceData(); + + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.SYSTEM_SERVER_ONLY); + + mConsentManager.clearAllAppConsentData(); + + verify(mMockIAdServicesManager).clearAllAppConsentData(); + + SystemClock.sleep(1000); + verify(mCustomAudienceDaoMock).deleteAllCustomAudienceData(); + verify(mAppInstallDaoMock).deleteAllAppInstallData(); + verify(mFrequencyCapDaoMock).deleteAllHistogramData(); + } + + @Test + public void testResetAllAppConsentAndAppData_ppApiAndSystemServer() + throws IOException, PackageManager.NameNotFoundException, RemoteException { + doNothing().when(mCustomAudienceDaoMock).deleteAllCustomAudienceData(); + + // Prepopulate with consent data for some apps + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.PPAPI_AND_SYSTEM_SERVER); + mConsentManager.enable(mContextSpy); + + verify(() -> UiStatsLogger.logOptInSelected()); + + doReturn(ConsentParcel.createGivenConsent(ConsentParcel.ALL_API)) + .when(mMockIAdServicesManager) + .getConsent(ConsentParcel.ALL_API); + assertTrue(mConsentManager.getConsent().isGiven()); + + mockGetPackageUid(AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + mockGetPackageUid(AppConsentDaoFixture.APP20_PACKAGE_NAME, AppConsentDaoFixture.APP20_UID); + mockGetPackageUid(AppConsentDaoFixture.APP30_PACKAGE_NAME, AppConsentDaoFixture.APP30_UID); + + mDatastore.put(AppConsentDaoFixture.APP10_DATASTORE_KEY, false); + mDatastore.put(AppConsentDaoFixture.APP20_DATASTORE_KEY, true); + mDatastore.put(AppConsentDaoFixture.APP30_DATASTORE_KEY, false); + List<ApplicationInfo> applicationsInstalled = + createApplicationInfos( + AppConsentDaoFixture.APP10_PACKAGE_NAME, + AppConsentDaoFixture.APP20_PACKAGE_NAME, + AppConsentDaoFixture.APP30_PACKAGE_NAME); + mockInstalledApplications(applicationsInstalled); + + // Verify population was successful + List<App> knownAppsWithConsent = + mDatastore.keySetFalse().stream().map(App::create).collect(Collectors.toList()); + List<App> appsWithRevokedConsent = + mDatastore.keySetTrue().stream().map(App::create).collect(Collectors.toList()); + assertThat(knownAppsWithConsent).hasSize(2); + assertThat(appsWithRevokedConsent).hasSize(1); + + mConsentManager.clearAllAppConsentData(); + + // All app consent data was deleted + knownAppsWithConsent = + mDatastore.keySetFalse().stream().map(App::create).collect(Collectors.toList()); + appsWithRevokedConsent = + mDatastore.keySetTrue().stream().map(App::create).collect(Collectors.toList()); + assertThat(knownAppsWithConsent).isEmpty(); + assertThat(appsWithRevokedConsent).isEmpty(); + + verify(mMockIAdServicesManager, times(2)).clearAllAppConsentData(); + + SystemClock.sleep(1000); + verify(mCustomAudienceDaoMock, times(2)).deleteAllCustomAudienceData(); + verify(mAppInstallDaoMock, times(2)).deleteAllAppInstallData(); + verify(mFrequencyCapDaoMock, times(2)).deleteAllHistogramData(); + } + + @Test + public void testResetAllAppConsentAndAppData_appSearchOnly() throws Exception { + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.APPSEARCH_ONLY); + when(mMockFlags.getEnableAppsearchConsentData()).thenReturn(true); + + mConsentManager.clearKnownAppsWithConsent(); + verify(mAppSearchConsentManagerMock).clearKnownAppsWithConsent(); + } + + @Test + public void testResetAllAppConsentAndAppData_ppapiAndAdExtDataServiceOnly() { + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.PPAPI_AND_ADEXT_SERVICE); + when(mMockFlags.getEnableAdExtServiceConsentData()).thenReturn(true); + + assertThrows(IllegalStateException.class, () -> mConsentManager.clearAllAppConsentData()); + } + + @Test + public void testResetAllowedAppConsentAndAppData_ppApiOnly() + throws IOException, PackageManager.NameNotFoundException { + doNothing().when(mCustomAudienceDaoMock).deleteAllCustomAudienceData(); + + // Prepopulate with consent data for some apps + mConsentManager.enable(mContextSpy); + assertTrue(mConsentManager.getConsent().isGiven()); + + verify(() -> UiStatsLogger.logOptInSelected()); + + mockGetPackageUid(AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + mockGetPackageUid(AppConsentDaoFixture.APP20_PACKAGE_NAME, AppConsentDaoFixture.APP20_UID); + mockGetPackageUid(AppConsentDaoFixture.APP30_PACKAGE_NAME, AppConsentDaoFixture.APP30_UID); + + mDatastore.put(AppConsentDaoFixture.APP10_DATASTORE_KEY, false); + mDatastore.put(AppConsentDaoFixture.APP20_DATASTORE_KEY, true); + mDatastore.put(AppConsentDaoFixture.APP30_DATASTORE_KEY, false); + List<ApplicationInfo> applicationsInstalled = + createApplicationInfos( + AppConsentDaoFixture.APP10_PACKAGE_NAME, + AppConsentDaoFixture.APP20_PACKAGE_NAME, + AppConsentDaoFixture.APP30_PACKAGE_NAME); + mockInstalledApplications(applicationsInstalled); + + // Verify population was successful + ImmutableList<App> knownAppsWithConsentBeforeReset = + mConsentManager.getKnownAppsWithConsent(); + ImmutableList<App> appsWithRevokedConsentBeforeReset = + mConsentManager.getAppsWithRevokedConsent(); + assertThat(knownAppsWithConsentBeforeReset).hasSize(2); + assertThat(appsWithRevokedConsentBeforeReset).hasSize(1); + mConsentManager.clearKnownAppsWithConsent(); + + // Known apps with consent were cleared; revoked apps were not deleted + ImmutableList<App> knownAppsWithConsentAfterReset = + mConsentManager.getKnownAppsWithConsent(); + ImmutableList<App> appsWithRevokedConsentAfterReset = + mConsentManager.getAppsWithRevokedConsent(); + assertThat(knownAppsWithConsentAfterReset).isEmpty(); + assertThat(appsWithRevokedConsentAfterReset).hasSize(1); + assertThat( + appsWithRevokedConsentAfterReset.stream() + .map(App::getPackageName) + .collect(Collectors.toList())) + .containsExactlyElementsIn( + appsWithRevokedConsentBeforeReset.stream() + .map(App::getPackageName) + .collect(Collectors.toList())); + + SystemClock.sleep(1000); + verify(mCustomAudienceDaoMock, times(2)).deleteAllCustomAudienceData(); + verify(mAppInstallDaoMock, times(2)).deleteAllAppInstallData(); + verify(mFrequencyCapDaoMock, times(2)).deleteAllHistogramData(); + } + + @Test + public void testResetAllowedAppConsentAndAppData_systemServerOnly() + throws IOException, RemoteException { + doNothing().when(mCustomAudienceDaoMock).deleteAllCustomAudienceData(); + + // Prepopulate with consent data for some apps + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.SYSTEM_SERVER_ONLY); + mConsentManager.clearKnownAppsWithConsent(); + + verify(mMockIAdServicesManager).clearKnownAppsWithConsent(); + + SystemClock.sleep(1000); + verify(mCustomAudienceDaoMock).deleteAllCustomAudienceData(); + verify(mAppInstallDaoMock).deleteAllAppInstallData(); + verify(mFrequencyCapDaoMock).deleteAllHistogramData(); + } + + @Test + public void testResetAllowedAppConsentAndAppData_ppApiAndSystemServer() + throws IOException, PackageManager.NameNotFoundException, RemoteException { + doNothing().when(mCustomAudienceDaoMock).deleteAllCustomAudienceData(); + + // Prepopulate with consent data for some apps + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.PPAPI_AND_SYSTEM_SERVER); + doReturn(ConsentParcel.createGivenConsent(ConsentParcel.ALL_API)) + .when(mMockIAdServicesManager) + .getConsent(ConsentParcel.ALL_API); + assertTrue(mConsentManager.getConsent().isGiven()); + mockGetPackageUid(AppConsentDaoFixture.APP10_PACKAGE_NAME, AppConsentDaoFixture.APP10_UID); + mockGetPackageUid(AppConsentDaoFixture.APP20_PACKAGE_NAME, AppConsentDaoFixture.APP20_UID); + mockGetPackageUid(AppConsentDaoFixture.APP30_PACKAGE_NAME, AppConsentDaoFixture.APP30_UID); + + mDatastore.put(AppConsentDaoFixture.APP10_DATASTORE_KEY, false); + mDatastore.put(AppConsentDaoFixture.APP20_DATASTORE_KEY, true); + mDatastore.put(AppConsentDaoFixture.APP30_DATASTORE_KEY, false); + List<ApplicationInfo> applicationsInstalled = + createApplicationInfos( + AppConsentDaoFixture.APP10_PACKAGE_NAME, + AppConsentDaoFixture.APP20_PACKAGE_NAME, + AppConsentDaoFixture.APP30_PACKAGE_NAME); + mockInstalledApplications(applicationsInstalled); + + // Verify population was successful + List<App> knownAppsWithConsentBeforeReset = + mDatastore.keySetFalse().stream().map(App::create).collect(Collectors.toList()); + List<App> appsWithRevokedConsentBeforeReset = + mDatastore.keySetTrue().stream().map(App::create).collect(Collectors.toList()); + assertThat(knownAppsWithConsentBeforeReset).hasSize(2); + assertThat(appsWithRevokedConsentBeforeReset).hasSize(1); + mConsentManager.clearKnownAppsWithConsent(); + + // Known apps with consent were cleared; revoked apps were not deleted + List<App> knownAppsWithConsentAfterReset = + mDatastore.keySetFalse().stream().map(App::create).collect(Collectors.toList()); + List<App> appsWithRevokedConsentAfterReset = + mDatastore.keySetTrue().stream().map(App::create).collect(Collectors.toList()); + assertThat(knownAppsWithConsentAfterReset).isEmpty(); + assertThat(appsWithRevokedConsentAfterReset).hasSize(1); + assertThat( + appsWithRevokedConsentAfterReset.stream() + .map(App::getPackageName) + .collect(Collectors.toList())) + .containsExactlyElementsIn( + appsWithRevokedConsentBeforeReset.stream() + .map(App::getPackageName) + .collect(Collectors.toList())); + + verify(mMockIAdServicesManager).clearKnownAppsWithConsent(); + + SystemClock.sleep(1000); + verify(mCustomAudienceDaoMock).deleteAllCustomAudienceData(); + verify(mAppInstallDaoMock).deleteAllAppInstallData(); + verify(mFrequencyCapDaoMock).deleteAllHistogramData(); + } + + @Test + public void testResetAllowedAppConsentAndAppData_appSearchOnly() throws Exception { + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.APPSEARCH_ONLY); + when(mMockFlags.getEnableAppsearchConsentData()).thenReturn(true); + + mConsentManager.clearKnownAppsWithConsent(); + verify(mAppSearchConsentManagerMock).clearKnownAppsWithConsent(); + } + + @Test + public void testResetAllowedAppConsentAndAppData_ppapiAndAdExtDataServiceOnly() { + mConsentManager = getConsentManagerByConsentSourceOfTruth(Flags.PPAPI_AND_ADEXT_SERVICE); + when(mMockFlags.getEnableAdExtServiceConsentData()).thenReturn(true); + + assertThrows( + IllegalStateException.class, () -> mConsentManager.clearKnownAppsWithConsent()); + } + + @Test + public void testNotificationDisplayedRecorded_PpApiOnly() throws RemoteException { + int consentSourceOfTruth = Flags.PPAPI_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + assertThat(spyConsentManager.wasNotificationDisplayed()).isFalse(); + + verify(mMockIAdServicesManager, never()).wasNotificationDisplayed(); + + spyConsentManager.recordNotificationDisplayed(true); + + assertThat(spyConsentManager.wasNotificationDisplayed()).isTrue(); + + verify(mMockIAdServicesManager, never()).wasNotificationDisplayed(); + verify(mMockIAdServicesManager, never()).recordNotificationDisplayed(true); + } + + @Test + public void testNotificationDisplayedRecorded_SystemServerOnly() throws RemoteException { + int consentSourceOfTruth = Flags.SYSTEM_SERVER_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + assertThat(spyConsentManager.wasNotificationDisplayed()).isFalse(); + + verify(mMockIAdServicesManager).wasNotificationDisplayed(); + + doReturn(true).when(mMockIAdServicesManager).wasNotificationDisplayed(); + spyConsentManager.recordNotificationDisplayed(true); + + assertThat(spyConsentManager.wasNotificationDisplayed()).isTrue(); + + verify(mMockIAdServicesManager, times(2)).wasNotificationDisplayed(); + verify(mMockIAdServicesManager).recordNotificationDisplayed(true); + + // Verify notificationDisplayed is not set in PPAPI + assertThat(mConsentDatastore.get(NOTIFICATION_DISPLAYED_ONCE)).isFalse(); + } + + @Test + public void testNotificationDisplayedRecorded_PpApiAndSystemServer() throws RemoteException { + int consentSourceOfTruth = Flags.PPAPI_AND_SYSTEM_SERVER; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + Boolean wasNotificationDisplayed = spyConsentManager.wasNotificationDisplayed(); + + assertThat(wasNotificationDisplayed).isFalse(); + + verify(mMockIAdServicesManager).wasNotificationDisplayed(); + + doReturn(true).when(mMockIAdServicesManager).wasNotificationDisplayed(); + spyConsentManager.recordNotificationDisplayed(true); + + assertThat(spyConsentManager.wasNotificationDisplayed()).isTrue(); + + verify(mMockIAdServicesManager, times(2)).wasNotificationDisplayed(); + verify(mMockIAdServicesManager).recordNotificationDisplayed(true); + + // Verify notificationDisplayed is also set in PPAPI + assertThat(mConsentDatastore.get(NOTIFICATION_DISPLAYED_ONCE)).isTrue(); + } + + @Test + public void testNotificationDisplayedRecorded_appSearchOnly() throws RemoteException { + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + when(mMockFlags.getEnableAppsearchConsentData()).thenReturn(true); + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + doReturn(false).when(mAppSearchConsentManagerMock).wasNotificationDisplayed(); + assertThat(spyConsentManager.wasNotificationDisplayed()).isFalse(); + verify(mAppSearchConsentManagerMock).wasNotificationDisplayed(); + + doReturn(true).when(mAppSearchConsentManagerMock).wasNotificationDisplayed(); + spyConsentManager.recordNotificationDisplayed(true); + + assertThat(spyConsentManager.wasNotificationDisplayed()).isTrue(); + + verify(mAppSearchConsentManagerMock, times(2)).wasNotificationDisplayed(); + verify(mAppSearchConsentManagerMock).recordNotificationDisplayed(true); + } + + @Test + public void testNotificationDisplayedRecorded_ppapiAndAdExtDataServiceOnly() + throws RemoteException { + int consentSourceOfTruth = Flags.PPAPI_AND_ADEXT_SERVICE; + when(mMockFlags.getEnableAdExtServiceConsentData()).thenReturn(true); + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + assertThat(spyConsentManager.wasNotificationDisplayed()).isFalse(); + } + + @Test + public void testGaUxNotificationDisplayedRecorded_PpApiOnly() throws RemoteException { + int consentSourceOfTruth = Flags.PPAPI_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + assertThat(spyConsentManager.wasGaUxNotificationDisplayed()).isFalse(); + + verify(mMockIAdServicesManager, never()).wasGaUxNotificationDisplayed(); + + spyConsentManager.recordGaUxNotificationDisplayed(true); + + assertThat(spyConsentManager.wasGaUxNotificationDisplayed()).isTrue(); + + verify(mMockIAdServicesManager, never()).wasGaUxNotificationDisplayed(); + verify(mMockIAdServicesManager, never()).recordGaUxNotificationDisplayed(true); + } + + @Test + public void testGaUxNotificationDisplayedRecorded_SystemServerOnly() throws RemoteException { + int consentSourceOfTruth = Flags.SYSTEM_SERVER_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + assertThat(spyConsentManager.wasGaUxNotificationDisplayed()).isFalse(); + + verify(mMockIAdServicesManager).wasGaUxNotificationDisplayed(); + + doReturn(true).when(mMockIAdServicesManager).wasGaUxNotificationDisplayed(); + spyConsentManager.recordGaUxNotificationDisplayed(true); + + assertThat(spyConsentManager.wasGaUxNotificationDisplayed()).isTrue(); + + verify(mMockIAdServicesManager, times(2)).wasGaUxNotificationDisplayed(); + verify(mMockIAdServicesManager).recordGaUxNotificationDisplayed(true); + + // Verify notificationDisplayed is not set in PPAPI + assertThat(mConsentDatastore.get(GA_UX_NOTIFICATION_DISPLAYED_ONCE)).isFalse(); + } + + @Test + public void testGaUxNotificationDisplayedRecorded_PpApiAndSystemServer() + throws RemoteException { + int consentSourceOfTruth = Flags.PPAPI_AND_SYSTEM_SERVER; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + Boolean wasGaUxNotificationDisplayed = spyConsentManager.wasGaUxNotificationDisplayed(); + + assertThat(wasGaUxNotificationDisplayed).isFalse(); + + verify(mMockIAdServicesManager).wasGaUxNotificationDisplayed(); + + doReturn(true).when(mMockIAdServicesManager).wasGaUxNotificationDisplayed(); + spyConsentManager.recordGaUxNotificationDisplayed(true); + + assertThat(spyConsentManager.wasGaUxNotificationDisplayed()).isTrue(); + + verify(mMockIAdServicesManager, times(2)).wasGaUxNotificationDisplayed(); + verify(mMockIAdServicesManager).recordGaUxNotificationDisplayed(true); + + // Verify notificationDisplayed is also set in PPAPI + assertThat(mConsentDatastore.get(GA_UX_NOTIFICATION_DISPLAYED_ONCE)).isTrue(); + } + + @Test + public void testGaUxNotificationDisplayedRecorded_appSearchOnly() throws RemoteException { + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + when(mMockFlags.getEnableAppsearchConsentData()).thenReturn(true); + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + when(mAppSearchConsentManagerMock.wasGaUxNotificationDisplayed()).thenReturn(false); + assertThat(spyConsentManager.wasGaUxNotificationDisplayed()).isFalse(); + verify(mAppSearchConsentManagerMock).wasGaUxNotificationDisplayed(); + + when(mAppSearchConsentManagerMock.wasGaUxNotificationDisplayed()).thenReturn(true); + spyConsentManager.recordGaUxNotificationDisplayed(true); + assertThat(spyConsentManager.wasGaUxNotificationDisplayed()).isTrue(); + + verify(mAppSearchConsentManagerMock, times(2)).wasGaUxNotificationDisplayed(); + verify(mAppSearchConsentManagerMock).recordGaUxNotificationDisplayed(true); + } + + @Test + public void testGaUxNotificationDisplayedRecorded_ppapiAndAdExtDataServiceOnly() + throws RemoteException { + int consentSourceOfTruth = Flags.PPAPI_AND_ADEXT_SERVICE; + when(mMockFlags.getEnableAdExtServiceConsentData()).thenReturn(true); + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + assertThat(spyConsentManager.wasGaUxNotificationDisplayed()).isFalse(); + } + + @Test + public void testNotificationDisplayedRecorded_notSupportedFlag() throws RemoteException { + int invalidConsentSourceOfTruth = 5; + assertThrows( + IllegalArgumentException.class, + () -> + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, invalidConsentSourceOfTruth)); + } + + @Test + public void testClearPpApiConsent() throws IOException { + mConsentDatastore.put(CONSENT_KEY, true); + mConsentDatastore.put(NOTIFICATION_DISPLAYED_ONCE, true); + assertThat(mConsentDatastore.get(CONSENT_KEY)).isTrue(); + assertThat(mConsentDatastore.get(NOTIFICATION_DISPLAYED_ONCE)).isTrue(); + + ConsentManagerV2.clearPpApiConsent(mContextSpy, mConsentDatastore); + assertThat(mConsentDatastore.get(CONSENT_KEY)).isNull(); + assertThat(mConsentDatastore.get(NOTIFICATION_DISPLAYED_ONCE)).isNull(); + + // Verify this should only happen once + mConsentDatastore.put(CONSENT_KEY, true); + mConsentDatastore.put(NOTIFICATION_DISPLAYED_ONCE, true); + assertThat(mConsentDatastore.get(CONSENT_KEY)).isTrue(); + assertThat(mConsentDatastore.get(NOTIFICATION_DISPLAYED_ONCE)).isTrue(); + // Consent is not cleared again + ConsentManagerV2.clearPpApiConsent(mContextSpy, mConsentDatastore); + assertThat(mConsentDatastore.get(CONSENT_KEY)).isTrue(); + assertThat(mConsentDatastore.get(NOTIFICATION_DISPLAYED_ONCE)).isTrue(); + + // Clear shared preference + ConsentManagerV2.resetSharedPreference(mContextSpy, SHARED_PREFS_KEY_PPAPI_HAS_CLEARED); + } + + @Test + public void testMigratePpApiConsentToSystemService() throws RemoteException, IOException { + // Disable IPC calls + doNothing().when(mMockIAdServicesManager).setConsent(any()); + doNothing().when(mMockIAdServicesManager).recordNotificationDisplayed(true); + + mConsentDatastore.put(CONSENT_KEY, true); + mConsentDatastore.put(NOTIFICATION_DISPLAYED_ONCE, true); + assertThat(mConsentDatastore.get(CONSENT_KEY)).isTrue(); + assertThat(mConsentDatastore.get(NOTIFICATION_DISPLAYED_ONCE)).isTrue(); + + ConsentManagerV2.migratePpApiConsentToSystemService( + mContextSpy, + mConsentDatastore, + mAdServicesStorageManager, + mStatsdAdServicesLoggerMock); + + verify(mMockIAdServicesManager).setConsent(any()); + verify(mMockIAdServicesManager).recordNotificationDisplayed(true); + + // Verify this should only happen once + ConsentManagerV2.migratePpApiConsentToSystemService( + mContextSpy, + mConsentDatastore, + mAdServicesStorageManager, + mStatsdAdServicesLoggerMock); + verify(mMockIAdServicesManager).setConsent(any()); + verify(mMockIAdServicesManager).recordNotificationDisplayed(true); + + // Clear shared preference + ConsentManagerV2.resetSharedPreference(mContextSpy, SHARED_PREFS_KEY_HAS_MIGRATED); + } + + @Test + public void testMigratePpApiConsentToSystemServiceWithSuccessfulConsentMigrationLogging() + throws RemoteException, IOException { + // Disable IPC calls + doNothing().when(mMockIAdServicesManager).setConsent(any()); + doNothing().when(mMockIAdServicesManager).recordNotificationDisplayed(true); + mConsentDatastore.put(CONSENT_KEY, true); + mConsentDatastore.put(NOTIFICATION_DISPLAYED_ONCE, true); + assertThat(mConsentDatastore.get(CONSENT_KEY)).isTrue(); + assertThat(mConsentDatastore.get(NOTIFICATION_DISPLAYED_ONCE)).isTrue(); + + SharedPreferences sharedPreferences = + mContextSpy.getSharedPreferences(SHARED_PREFS_CONSENT, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPreferences.edit(); + + editor.putBoolean(SHARED_PREFS_KEY_HAS_MIGRATED, false); + editor.putBoolean(SHARED_PREFS_KEY_APPSEARCH_HAS_MIGRATED, false); + editor.commit(); + ExtendedMockito.doReturn(false).when(() -> DeviceRegionProvider.isEuDevice(any())); + + ConsentManagerV2.migratePpApiConsentToSystemService( + mContextSpy, + mConsentDatastore, + mAdServicesStorageManager, + mStatsdAdServicesLoggerMock); + + ConsentMigrationStats consentMigrationStats = + ConsentMigrationStats.builder() + .setTopicsConsent(true) + .setFledgeConsent(true) + .setMsmtConsent(true) + .setDefaultConsent(true) + .setMigrationStatus( + ConsentMigrationStats.MigrationStatus + .SUCCESS_WITH_SHARED_PREF_UPDATED) + .setMigrationType( + ConsentMigrationStats.MigrationType.PPAPI_TO_SYSTEM_SERVICE) + .setRegion(2) + .build(); + + verify(mStatsdAdServicesLoggerMock).logConsentMigrationStats(consentMigrationStats); + + // Clear shared preference + ConsentManagerV2.resetSharedPreference(mContextSpy, SHARED_PREFS_KEY_HAS_MIGRATED); + ConsentManagerV2.resetSharedPreference( + mContextSpy, SHARED_PREFS_KEY_APPSEARCH_HAS_MIGRATED); + } + + @Test + public void testMigratePpApiConsentToSystemServiceWithUnSuccessfulConsentMigrationLogging() + throws RemoteException, IOException { + // Disable IPC calls + doNothing().when(mMockIAdServicesManager).setConsent(any()); + doNothing().when(mMockIAdServicesManager).recordNotificationDisplayed(true); + mConsentDatastore.put(CONSENT_KEY, true); + mConsentDatastore.put(NOTIFICATION_DISPLAYED_ONCE, true); + + SharedPreferences sharedPreferences = mock(SharedPreferences.class); + SharedPreferences.Editor editor = mock(SharedPreferences.Editor.class); + doReturn(editor).when(sharedPreferences).edit(); + doReturn(false).when(editor).commit(); + doReturn(sharedPreferences).when(mContextSpy).getSharedPreferences(anyString(), anyInt()); + + doNothing().when(() -> ErrorLogUtil.e(anyInt(), anyInt())); + doNothing().when(mStatsdAdServicesLoggerMock).logConsentMigrationStats(any()); + ExtendedMockito.doReturn(false).when(() -> DeviceRegionProvider.isEuDevice(any())); + + ConsentManagerV2.migratePpApiConsentToSystemService( + mContextSpy, + mConsentDatastore, + mAdServicesStorageManager, + mStatsdAdServicesLoggerMock); + + ConsentMigrationStats consentMigrationStats = + ConsentMigrationStats.builder() + .setTopicsConsent(true) + .setFledgeConsent(true) + .setMsmtConsent(true) + .setDefaultConsent(true) + .setMigrationStatus( + ConsentMigrationStats.MigrationStatus + .SUCCESS_WITH_SHARED_PREF_NOT_UPDATED) + .setMigrationType( + ConsentMigrationStats.MigrationType.PPAPI_TO_SYSTEM_SERVICE) + .setRegion(2) + .build(); + + verify(mStatsdAdServicesLoggerMock).logConsentMigrationStats(consentMigrationStats); + + doReturn(true).when(editor).commit(); + // Clear shared preference + ConsentManagerV2.resetSharedPreference(mContextSpy, SHARED_PREFS_KEY_HAS_MIGRATED); + } + + @Test + public void testMigratePpApiConsentToSystemServiceThrowsException() + throws RemoteException, IOException { + mConsentDatastore.put(NOTIFICATION_DISPLAYED_ONCE, true); + doThrow(RemoteException.class) + .when(mMockIAdServicesManager) + .recordNotificationDisplayed(true); + + doNothing().when(() -> ErrorLogUtil.e(anyInt(), anyInt())); + doNothing().when(mStatsdAdServicesLoggerMock).logConsentMigrationStats(any()); + ExtendedMockito.doReturn(false).when(() -> DeviceRegionProvider.isEuDevice(any())); + + ConsentManagerV2.migratePpApiConsentToSystemService( + mContextSpy, + mConsentDatastore, + mAdServicesStorageManager, + mStatsdAdServicesLoggerMock); + + ConsentMigrationStats consentMigrationStats = + ConsentMigrationStats.builder() + .setTopicsConsent(true) + .setFledgeConsent(true) + .setMsmtConsent(true) + .setDefaultConsent(true) + .setMigrationStatus(ConsentMigrationStats.MigrationStatus.FAILURE) + .setMigrationType( + ConsentMigrationStats.MigrationType.PPAPI_TO_SYSTEM_SERVICE) + .setRegion(2) + .build(); + + verify(mStatsdAdServicesLoggerMock).logConsentMigrationStats(consentMigrationStats); + + // Clear shared preference + ConsentManagerV2.resetSharedPreference(mContextSpy, SHARED_PREFS_KEY_HAS_MIGRATED); + } + + @Test + public void testHandleConsentMigrationIfNeeded_ExtServices() throws RemoteException { + doReturn("com." + AdServicesCommon.ADEXTSERVICES_PACKAGE_NAME_SUFFIX) + .when(mContextSpy) + .getPackageName(); + + ConsentManagerV2.handleConsentMigrationIfNeeded( + mContextSpy, + mConsentDatastore, + mAdServicesStorageManager, + mStatsdAdServicesLoggerMock, + Flags.PPAPI_AND_SYSTEM_SERVER); + + verify(mContextSpy, never()).getSharedPreferences(anyString(), anyInt()); + verify(mMockIAdServicesManager, never()).setConsent(any()); + } + + @Test + public void testHandleConsentMigrationFromAppSearchIfNeeded_ExtServices() throws Exception { + doReturn("com." + AdServicesCommon.ADEXTSERVICES_PACKAGE_NAME_SUFFIX) + .when(mContextSpy) + .getPackageName(); + SharedPreferences mockSharedPrefs = mock(SharedPreferences.class); + SharedPreferences.Editor mockEditor = mock(SharedPreferences.Editor.class); + when(mockSharedPrefs.edit()).thenReturn(mockEditor); + when(mContextSpy.getSharedPreferences(any(String.class), anyInt())) + .thenReturn(mockSharedPrefs); + ConsentManagerV2.handleConsentMigrationFromAppSearchIfNeeded( + mContextSpy, + mDatastore, + mAppConsentDaoSpy, + mAppSearchConsentManagerMock, + mAdServicesStorageManager, + mStatsdAdServicesLoggerMock); + + verify(mContextSpy, never()).getSharedPreferences(anyString(), anyInt()); + verify(mAppSearchConsentManagerMock, never()) + .migrateConsentDataIfNeeded(any(), any(), any(), any()); + verify(mMockIAdServicesManager, never()).setConsent(any()); + verify(mMockIAdServicesManager, never()).recordNotificationDisplayed(true); + verify(mMockIAdServicesManager, never()).recordGaUxNotificationDisplayed(true); + verify(mMockIAdServicesManager, never()).recordDefaultConsent(anyBoolean()); + verify(mMockIAdServicesManager, never()).recordAdServicesDeletionOccurred(anyInt()); + verify(mMockIAdServicesManager, never()).recordDefaultAdIdState(anyBoolean()); + verify(mMockIAdServicesManager, never()).recordFledgeDefaultConsent(anyBoolean()); + verify(mMockIAdServicesManager, never()).recordMeasurementDefaultConsent(anyBoolean()); + verify(mMockIAdServicesManager, never()).recordTopicsDefaultConsent(anyBoolean()); + verify(mMockIAdServicesManager, never()).recordUserManualInteractionWithConsent(anyInt()); + verify(mockEditor, never()).putBoolean(any(), anyBoolean()); + } + + @Test + public void testResetSharedPreference() { + SharedPreferences sharedPreferences = + FileCompatUtils.getSharedPreferencesHelper( + mContextSpy, SHARED_PREFS_CONSENT, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPreferences.edit(); + + editor.putBoolean(SHARED_PREFS_KEY_PPAPI_HAS_CLEARED, true); + editor.putBoolean(SHARED_PREFS_KEY_HAS_MIGRATED, true); + editor.commit(); + + assertThat( + sharedPreferences.getBoolean( + SHARED_PREFS_KEY_PPAPI_HAS_CLEARED, /* defValue */ false)) + .isTrue(); + assertThat( + sharedPreferences.getBoolean( + SHARED_PREFS_KEY_HAS_MIGRATED, /* defValue */ false)) + .isTrue(); + + resetSharedPreference(mContextSpy, SHARED_PREFS_KEY_PPAPI_HAS_CLEARED); + resetSharedPreference(mContextSpy, SHARED_PREFS_KEY_HAS_MIGRATED); + + assertThat( + sharedPreferences.getBoolean( + SHARED_PREFS_KEY_PPAPI_HAS_CLEARED, /* defValue */ true)) + .isFalse(); + assertThat(sharedPreferences.getBoolean(SHARED_PREFS_KEY_HAS_MIGRATED, /* defValue */ true)) + .isFalse(); + } + + @Test + public void testHandleConsentMigrationIfNeeded_PpApiOnly() { + // Disable actual execution of internal methods + doNothing() + .when( + () -> + ConsentManagerV2.resetSharedPreference( + mContextSpy, SHARED_PREFS_KEY_HAS_MIGRATED)); + doNothing() + .when( + () -> + ConsentManagerV2.migratePpApiConsentToSystemService( + mContextSpy, + mConsentDatastore, + mAdServicesStorageManager, + mStatsdAdServicesLoggerMock)); + doNothing().when(() -> ConsentManagerV2.clearPpApiConsent(mContextSpy, mConsentDatastore)); + + int consentSourceOfTruth = Flags.PPAPI_ONLY; + ConsentManagerV2.handleConsentMigrationIfNeeded( + mContextSpy, + mConsentDatastore, + mAdServicesStorageManager, + mStatsdAdServicesLoggerMock, + consentSourceOfTruth); + + verify( + () -> + ConsentManagerV2.resetSharedPreference( + mContextSpy, SHARED_PREFS_KEY_HAS_MIGRATED)); + verify( + () -> + ConsentManagerV2.migratePpApiConsentToSystemService( + mContextSpy, + mConsentDatastore, + mAdServicesStorageManager, + mStatsdAdServicesLoggerMock), + never()); + verify(() -> ConsentManagerV2.clearPpApiConsent(mContextSpy, mConsentDatastore), never()); + } + + @Test + public void testHandleConsentMigrationIfNeeded_SystemServerOnly() { + // Disable actual execution of internal methods + doNothing() + .when( + () -> + ConsentManagerV2.resetSharedPreference( + mContextSpy, SHARED_PREFS_KEY_HAS_MIGRATED)); + doNothing() + .when( + () -> + ConsentManagerV2.migratePpApiConsentToSystemService( + mContextSpy, + mConsentDatastore, + mAdServicesStorageManager, + mStatsdAdServicesLoggerMock)); + doNothing().when(() -> ConsentManagerV2.clearPpApiConsent(mContextSpy, mConsentDatastore)); + + int consentSourceOfTruth = Flags.SYSTEM_SERVER_ONLY; + ConsentManagerV2.handleConsentMigrationIfNeeded( + mContextSpy, + mConsentDatastore, + mAdServicesStorageManager, + mStatsdAdServicesLoggerMock, + consentSourceOfTruth); + + verify( + () -> + ConsentManagerV2.resetSharedPreference( + mContextSpy, SHARED_PREFS_KEY_HAS_MIGRATED), + never()); + verify( + () -> + ConsentManagerV2.migratePpApiConsentToSystemService( + mContextSpy, + mConsentDatastore, + mAdServicesStorageManager, + mStatsdAdServicesLoggerMock)); + verify(() -> ConsentManagerV2.clearPpApiConsent(mContextSpy, mConsentDatastore)); + } + + @Test + public void testHandleConsentMigrationIfNeeded_PpApiAndSystemServer() { + // Disable actual execution of internal methods + doNothing() + .when( + () -> + ConsentManagerV2.resetSharedPreference( + mContextSpy, SHARED_PREFS_KEY_HAS_MIGRATED)); + doNothing() + .when( + () -> + ConsentManagerV2.migratePpApiConsentToSystemService( + mContextSpy, + mConsentDatastore, + mAdServicesStorageManager, + mStatsdAdServicesLoggerMock)); + doNothing().when(() -> ConsentManagerV2.clearPpApiConsent(mContextSpy, mConsentDatastore)); + + int consentSourceOfTruth = Flags.PPAPI_AND_SYSTEM_SERVER; + ConsentManagerV2.handleConsentMigrationIfNeeded( + mContextSpy, + mConsentDatastore, + mAdServicesStorageManager, + mStatsdAdServicesLoggerMock, + consentSourceOfTruth); + + verify( + () -> + ConsentManagerV2.resetSharedPreference( + mContextSpy, SHARED_PREFS_KEY_HAS_MIGRATED), + never()); + verify( + () -> + ConsentManagerV2.migratePpApiConsentToSystemService( + mContextSpy, + mConsentDatastore, + mAdServicesStorageManager, + mStatsdAdServicesLoggerMock)); + verify(() -> ConsentManagerV2.clearPpApiConsent(mContextSpy, mConsentDatastore), never()); + } + + @Test + public void testHandleConsentMigrationIfNeeded_AppSearchOnly() { + // Disable actual execution of internal methods + doNothing() + .when( + () -> + ConsentManagerV2.resetSharedPreference( + mContextSpy, SHARED_PREFS_KEY_HAS_MIGRATED)); + doNothing() + .when( + () -> + ConsentManagerV2.migratePpApiConsentToSystemService( + mContextSpy, + mConsentDatastore, + mAdServicesStorageManager, + mStatsdAdServicesLoggerMock)); + doNothing().when(() -> ConsentManagerV2.clearPpApiConsent(mContextSpy, mConsentDatastore)); + + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + ConsentManagerV2.handleConsentMigrationIfNeeded( + mContextSpy, + mConsentDatastore, + mAdServicesStorageManager, + mStatsdAdServicesLoggerMock, + consentSourceOfTruth); + + verify( + () -> + ConsentManagerV2.resetSharedPreference( + mContextSpy, SHARED_PREFS_KEY_HAS_MIGRATED), + never()); + verify( + () -> + ConsentManagerV2.migratePpApiConsentToSystemService( + mContextSpy, + mConsentDatastore, + mAdServicesStorageManager, + mStatsdAdServicesLoggerMock), + never()); + verify(() -> ConsentManagerV2.clearPpApiConsent(mContextSpy, mConsentDatastore), never()); + } + + @Test + public void testHandleConsentMigrationFromAppSearchIfNeeded_notMigrated() throws Exception { + when(mAppSearchConsentManagerMock.migrateConsentDataIfNeeded(any(), any(), any(), any())) + .thenReturn(false); + BooleanFileDatastore mockDatastore = mock(BooleanFileDatastore.class); + + SharedPreferences mockSharedPrefs = mock(SharedPreferences.class); + SharedPreferences.Editor mockEditor = mock(SharedPreferences.Editor.class); + when(mockSharedPrefs.edit()).thenReturn(mockEditor); + when(mContextSpy.getSharedPreferences(any(String.class), anyInt())) + .thenReturn(mockSharedPrefs); + + ConsentManagerV2.handleConsentMigrationFromAppSearchIfNeeded( + mContextSpy, + mockDatastore, + mAppConsentDaoSpy, + mAppSearchConsentManagerMock, + mAdServicesStorageManager, + mStatsdAdServicesLoggerMock); + verify(mockEditor, never()).putBoolean(any(), anyBoolean()); + verify(mAppSearchConsentManagerMock).migrateConsentDataIfNeeded(any(), any(), any(), any()); + verify(mAppSearchConsentManagerMock, never()).recordNotificationDisplayed(true); + verify(mAppSearchConsentManagerMock, never()).recordGaUxNotificationDisplayed(true); + verify(mAppSearchConsentManagerMock, never()).recordDefaultConsent(any(), anyBoolean()); + + verify(mAppSearchConsentManagerMock, never()).recordDefaultAdIdState(anyBoolean()); + verify(mAppSearchConsentManagerMock, never()).recordDefaultConsent(any(), anyBoolean()); + + verify(mAppSearchConsentManagerMock, never()) + .recordUserManualInteractionWithConsent(anyInt()); + } + + @Test + public void testHandleConsentMigrationFromAppSearchIfNeeded() throws Exception { + when(mAppSearchConsentManagerMock.migrateConsentDataIfNeeded(any(), any(), any(), any())) + .thenReturn(true); + when(mAppSearchConsentManagerMock.getConsent(any())).thenReturn(AdServicesApiConsent.GIVEN); + when(mAppSearchConsentManagerMock.getDefaultConsent(any())) + .thenReturn(AdServicesApiConsent.GIVEN); + mConsentDatastore.put(CONSENT_KEY, true); + mConsentDatastore.put(NOTIFICATION_DISPLAYED_ONCE, true); + + SharedPreferences mockSharedPrefs = mock(SharedPreferences.class); + SharedPreferences.Editor mockEditor = mock(SharedPreferences.Editor.class); + when(mockSharedPrefs.edit()).thenReturn(mockEditor); + when(mContextSpy.getSharedPreferences(any(String.class), anyInt())) + .thenReturn(mockSharedPrefs); + when(mAppSearchConsentManagerMock.getUserManualInteractionWithConsent()) + .thenReturn(MANUAL_INTERACTIONS_RECORDED); + when(mockEditor.commit()).thenReturn(true); + ExtendedMockito.doReturn(false).when(() -> DeviceRegionProvider.isEuDevice(any())); + + ConsentManagerV2.handleConsentMigrationFromAppSearchIfNeeded( + mContextSpy, + mConsentDatastore, + mAppConsentDaoSpy, + mAppSearchConsentManagerMock, + mAdServicesStorageManager, + mStatsdAdServicesLoggerMock); + verify(mAppSearchConsentManagerMock).migrateConsentDataIfNeeded(any(), any(), any(), any()); + + // Verify interactions data is migrated. + assertThat(mConsentDatastore.get(ConsentConstants.MANUAL_INTERACTION_WITH_CONSENT_RECORDED)) + .isTrue(); + verify(mAdServicesStorageManager).recordUserManualInteractionWithConsent(anyInt()); + + // Verify migration is recorded. + verify(mockEditor) + .putBoolean(eq(ConsentConstants.SHARED_PREFS_KEY_APPSEARCH_HAS_MIGRATED), eq(true)); + + // Verify default consents data is migrated. + assertThat(mConsentDatastore.get(ConsentConstants.TOPICS_DEFAULT_CONSENT)).isTrue(); + assertThat(mConsentDatastore.get(ConsentConstants.FLEDGE_DEFAULT_CONSENT)).isTrue(); + assertThat(mConsentDatastore.get(ConsentConstants.MEASUREMENT_DEFAULT_CONSENT)).isTrue(); + assertThat(mConsentDatastore.get(ConsentConstants.CONSENT_KEY)).isTrue(); + assertThat(mConsentDatastore.get(ConsentConstants.DEFAULT_CONSENT)).isTrue(); + verify(mAdServicesStorageManager) + .recordDefaultConsent(eq(AdServicesApiType.ALL_API), eq(true)); + verify(mAdServicesStorageManager) + .recordDefaultConsent(eq(AdServicesApiType.TOPICS), eq(true)); + verify(mAdServicesStorageManager) + .recordDefaultConsent(eq(AdServicesApiType.MEASUREMENTS), eq(true)); + verify(mAdServicesStorageManager) + .recordDefaultConsent(eq(AdServicesApiType.FLEDGE), eq(true)); + + // Verify per API consents data is migrated. + assertThat(mConsentDatastore.get(AdServicesApiType.TOPICS.toPpApiDatastoreKey())).isTrue(); + assertThat(mConsentDatastore.get(AdServicesApiType.FLEDGE.toPpApiDatastoreKey())).isTrue(); + assertThat(mConsentDatastore.get(AdServicesApiType.MEASUREMENTS.toPpApiDatastoreKey())) + .isTrue(); + verify(mAdServicesStorageManager, atLeast(4)).recordDefaultConsent(any(), anyBoolean()); + ExtendedMockito.doReturn(false).when(() -> DeviceRegionProvider.isEuDevice(any())); + + ConsentMigrationStats consentMigrationStats = + ConsentMigrationStats.builder() + .setTopicsConsent(true) + .setFledgeConsent(true) + .setMsmtConsent(true) + .setDefaultConsent(true) + .setMigrationStatus( + ConsentMigrationStats.MigrationStatus + .SUCCESS_WITH_SHARED_PREF_UPDATED) + .setMigrationType( + ConsentMigrationStats.MigrationType.APPSEARCH_TO_SYSTEM_SERVICE) + .setRegion(2) + .build(); + + verify(mStatsdAdServicesLoggerMock).logConsentMigrationStats(consentMigrationStats); + } + + @Test + public void testHandleConsentMigrationFromAppSearchIfNeededSharedPrefsEditorUnsuccessful() + throws Exception { + when(mAppSearchConsentManagerMock.migrateConsentDataIfNeeded(any(), any(), any(), any())) + .thenReturn(true); + when(mAppSearchConsentManagerMock.getConsent(any())).thenReturn(AdServicesApiConsent.GIVEN); + when(mAppSearchConsentManagerMock.getDefaultConsent(any())) + .thenReturn(AdServicesApiConsent.GIVEN); + mConsentDatastore.put(CONSENT_KEY, true); + mConsentDatastore.put(NOTIFICATION_DISPLAYED_ONCE, true); + + SharedPreferences mockSharedPrefs = mock(SharedPreferences.class); + SharedPreferences.Editor mockEditor = mock(SharedPreferences.Editor.class); + when(mockSharedPrefs.edit()).thenReturn(mockEditor); + when(mContextSpy.getSharedPreferences(any(String.class), anyInt())) + .thenReturn(mockSharedPrefs); + when(mAppSearchConsentManagerMock.getUserManualInteractionWithConsent()) + .thenReturn(MANUAL_INTERACTIONS_RECORDED); + when(mockEditor.commit()).thenReturn(false); + doNothing().when(() -> ErrorLogUtil.e(any(), anyInt(), anyInt())); + ExtendedMockito.doReturn(false).when(() -> DeviceRegionProvider.isEuDevice(any())); + + ConsentManagerV2.handleConsentMigrationFromAppSearchIfNeeded( + mContextSpy, + mConsentDatastore, + mAppConsentDaoSpy, + mAppSearchConsentManagerMock, + mAdServicesStorageManager, + mStatsdAdServicesLoggerMock); + + ConsentMigrationStats consentMigrationStats = + ConsentMigrationStats.builder() + .setTopicsConsent(true) + .setFledgeConsent(true) + .setMsmtConsent(true) + .setDefaultConsent(true) + .setMigrationStatus( + ConsentMigrationStats.MigrationStatus + .SUCCESS_WITH_SHARED_PREF_NOT_UPDATED) + .setMigrationType( + ConsentMigrationStats.MigrationType.APPSEARCH_TO_SYSTEM_SERVICE) + .setRegion(2) + .build(); + + verify(mStatsdAdServicesLoggerMock).logConsentMigrationStats(consentMigrationStats); + } + + @Test + public void testHandleConsentMigrationFromAppSearchIfNeededThrowsException() throws Exception { + when(mAppSearchConsentManagerMock.migrateConsentDataIfNeeded(any(), any(), any(), any())) + .thenThrow(IOException.class); + + doNothing().when(() -> ErrorLogUtil.e(any(), anyInt(), anyInt())); + doNothing().when(mStatsdAdServicesLoggerMock).logConsentMigrationStats(any()); + + doReturn(false).when(() -> DeviceRegionProvider.isEuDevice(any())); + + ConsentManagerV2.handleConsentMigrationFromAppSearchIfNeeded( + mContextSpy, + mConsentDatastore, + mAppConsentDaoSpy, + mAppSearchConsentManagerMock, + mAdServicesStorageManager, + mStatsdAdServicesLoggerMock); + + ConsentMigrationStats consentMigrationStats = + ConsentMigrationStats.builder() + .setTopicsConsent(true) + .setFledgeConsent(true) + .setMsmtConsent(true) + .setDefaultConsent(true) + .setMigrationStatus(ConsentMigrationStats.MigrationStatus.FAILURE) + .setMigrationType( + ConsentMigrationStats.MigrationType.APPSEARCH_TO_SYSTEM_SERVICE) + .setRegion(2) + .build(); + Mockito.verify(mStatsdAdServicesLoggerMock).logConsentMigrationStats(consentMigrationStats); + } + + @Test + public void testTopicsProxyCalls() { + Topic topic = Topic.create(1, 1, 1); + ArrayList<String> tablesToBlock = new ArrayList<>(); + tablesToBlock.add(TopicsTables.BlockedTopicsContract.TABLE); + + TopicsWorker topicsWorker = + spy( + new TopicsWorker( + mMockEpochManager, + mCacheManagerMock, + mBlockedTopicsManagerMock, + mAppUpdateManagerMock, + mMockFlags)); + + ConsentManagerV2 consentManager = + new ConsentManagerV2( + topicsWorker, + mAppConsentDaoSpy, + mEnrollmentDaoSpy, + mMeasurementImplMock, + mCustomAudienceDaoMock, + mAppConsentStorageManager, + mAppInstallDaoMock, + mFrequencyCapDaoMock, + mAdServicesStorageManager, + mConsentDatastore, + mAppSearchConsentManagerMock, + mUserProfileIdManagerMock, + mAppConsentForRStorageManager, + mMockFlags, + Flags.PPAPI_ONLY, + true, + true); + + doNothing().when(mBlockedTopicsManagerMock).blockTopic(any()); + doNothing().when(mBlockedTopicsManagerMock).unblockTopic(any()); + // The actual usage is to invoke clearAllTopicsData() from TopicsWorker + doNothing().when(topicsWorker).clearAllTopicsData(any()); + + consentManager.revokeConsentForTopic(topic); + consentManager.restoreConsentForTopic(topic); + consentManager.resetTopics(); + + verify(mBlockedTopicsManagerMock).blockTopic(topic); + verify(mBlockedTopicsManagerMock).unblockTopic(topic); + verify(topicsWorker).clearAllTopicsData(tablesToBlock); + } + + @Test + public void testLoggingSettingsUsageReportedOptInSelectedRow() { + doReturn(false).when(() -> DeviceRegionProvider.isEuDevice(any(Context.class))); + ConsentManagerV2 temporalConsentManagerV2 = + getConsentManagerByConsentSourceOfTruth(Flags.PPAPI_ONLY); + + temporalConsentManagerV2.enable(mContextSpy); + + verify(mUiStatsLoggerMock).logOptInSelected(); + } + + @Test + public void testLoggingSettingsUsageReportedOptInSelectedEu() { + doReturn(true).when(() -> DeviceRegionProvider.isEuDevice(any(Context.class))); + ConsentManagerV2 temporalConsentManagerV2 = + getConsentManagerByConsentSourceOfTruth(Flags.PPAPI_ONLY); + + temporalConsentManagerV2.enable(mContextSpy); + + verify(mUiStatsLoggerMock).logOptInSelected(); + } + + @Test + public void testConsentPerApiIsGivenAfterEnabling_PpApiOnly() + throws RemoteException, IOException { + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(true); + boolean isGiven = true; + int consentSourceOfTruth = Flags.PPAPI_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(isGiven, consentSourceOfTruth); + + spyConsentManager.enable(mContextSpy, AdServicesApiType.TOPICS); + + assertThat(spyConsentManager.getConsent(AdServicesApiType.TOPICS).isGiven()).isTrue(); + verify(spyConsentManager) + .setPerApiConsentToSourceOfTruth( + eq(/* isGiven */ true), eq(AdServicesApiType.TOPICS)); + + verify(spyConsentManager).resetTopicsAndBlockedTopics(); + } + + @Test + public void testConsentPerApiIsGivenAfterEnabling_SystemServerOnly() throws RemoteException { + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(true); + doReturn(PrivacySandboxUxCollection.UNSUPPORTED_UX).when(mAdServicesStorageManager).getUx(); + boolean isGiven = true; + int consentSourceOfTruth = Flags.SYSTEM_SERVER_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForConsentPerApiTesting( + isGiven, consentSourceOfTruth, AdServicesApiType.TOPICS.toConsentApiType()); + + spyConsentManager.enable(mContextSpy, AdServicesApiType.TOPICS); + + assertThat(spyConsentManager.getConsent(AdServicesApiType.TOPICS).isGiven()).isTrue(); + verify(mAdServicesStorageManager).setConsent(eq(AdServicesApiType.TOPICS), eq(isGiven)); + verify(mMockIAdServicesManager, times(2)).getConsent(ConsentParcel.TOPICS); + verify(spyConsentManager).resetTopicsAndBlockedTopics(); + } + + @Test + public void testConsentPerApiIsGivenAfterEnabling_PpApiAndSystemServer() + throws RemoteException, IOException { + doNothing().when(() -> ErrorLogUtil.e(any(), anyInt(), anyInt())); + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(true); + doReturn(PrivacySandboxUxCollection.UNSUPPORTED_UX).when(mAdServicesStorageManager).getUx(); + boolean isGiven = true; + int consentSourceOfTruth = Flags.PPAPI_AND_SYSTEM_SERVER; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForConsentPerApiTesting( + isGiven, consentSourceOfTruth, AdServicesApiType.TOPICS.toConsentApiType()); + doThrow(RuntimeException.class) + .when(mAdServicesStorageManager) + .getConsent(eq(AdServicesApiType.TOPICS)); + spyConsentManager.enable(mContextSpy, AdServicesApiType.TOPICS); + doReturn(AdServicesApiConsent.GIVEN) + .when(mAdServicesStorageManager) + .getConsent(eq(AdServicesApiType.TOPICS)); + assertThat(spyConsentManager.getConsent(AdServicesApiType.TOPICS).isGiven()).isTrue(); + verify(mAdServicesStorageManager).setConsent(eq(AdServicesApiType.TOPICS), eq(isGiven)); + + verify(mAdServicesStorageManager, times(2)).getConsent(eq(AdServicesApiType.TOPICS)); + + verify(spyConsentManager) + .setPerApiConsentToSourceOfTruth( + eq(/* isGiven */ true), eq(AdServicesApiType.TOPICS)); + verify(spyConsentManager).resetTopicsAndBlockedTopics(); + verify( + () -> + ErrorLogUtil.e( + any(Throwable.class), + eq(AD_SERVICES_ERROR_REPORTED__ERROR_CODE__ERROR_WHILE_GET_CONSENT), + eq(AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX))); + } + + @Test + public void testConsentPerApiIsGivenAfterEnabling_AppSearchOnly() throws RemoteException { + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(true); + doReturn(true).when(mMockFlags).getEnableAppsearchConsentData(); + when(mAppSearchConsentManagerMock.getConsent(any())) + .thenReturn(AdServicesApiConsent.REVOKED); + boolean isGiven = true; + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + AdServicesApiType apiType = AdServicesApiType.TOPICS; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(isGiven, consentSourceOfTruth); + + spyConsentManager.enable(mContextSpy, AdServicesApiType.TOPICS); + verify(spyConsentManager) + .setPerApiConsentToSourceOfTruth(eq(/* isGiven */ true), eq(apiType)); + verify(mAppSearchConsentManagerMock).setConsent(eq(apiType), eq(isGiven)); + when(mAppSearchConsentManagerMock.getConsent(AdServicesApiType.TOPICS)) + .thenReturn(AdServicesApiConsent.GIVEN); + assertThat(spyConsentManager.getConsent(AdServicesApiType.TOPICS).isGiven()).isTrue(); + } + + @Test + public void testMeasurementConsentIsGivenAfterEnabling_ppapiAndAdExtDataServiceOnly() + throws RemoteException { + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(true); + doReturn(true).when(mMockFlags).getEnableAdExtServiceConsentData(); + boolean isGiven = true; + int consentSourceOfTruth = Flags.PPAPI_AND_ADEXT_SERVICE; + AdServicesApiType apiType = AdServicesApiType.MEASUREMENTS; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(isGiven, consentSourceOfTruth); + + spyConsentManager.enable(mContextSpy, AdServicesApiType.MEASUREMENTS); + verify(spyConsentManager) + .setPerApiConsentToSourceOfTruth(eq(/* isGiven */ true), eq(apiType)); + verify(mAdServicesExtDataManager).setMsmtConsent(eq(true)); + when(mAdServicesExtDataManager.getMsmtConsent()).thenReturn(true); + assertThat(spyConsentManager.getConsent(AdServicesApiType.MEASUREMENTS).isGiven()).isTrue(); + } + + @Test + public void testMeasurementConsentIsRevokedAfterDisabling_ppapiAndAdExtDataServiceOnly() + throws RemoteException, IOException { + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(true); + doReturn(true).when(mMockFlags).getEnableAdExtServiceConsentData(); + boolean isGiven = false; + int consentSourceOfTruth = Flags.PPAPI_AND_ADEXT_SERVICE; + AdServicesApiType apiType = AdServicesApiType.MEASUREMENTS; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(isGiven, consentSourceOfTruth); + when(mAppConsentForRStorageManager.getConsent(any())) + .thenReturn(AdServicesApiConsent.REVOKED); + spyConsentManager.disable(mContextSpy, AdServicesApiType.MEASUREMENTS); + verify(spyConsentManager) + .setPerApiConsentToSourceOfTruth(eq(/* isGiven */ false), eq(apiType)); + verify(mAppConsentForRStorageManager) + .setConsent(eq(AdServicesApiType.MEASUREMENTS), eq(false)); + when(mAppConsentForRStorageManager.getConsent(eq(AdServicesApiType.MEASUREMENTS))) + .thenReturn(AdServicesApiConsent.REVOKED); + assertThat(spyConsentManager.getConsent(AdServicesApiType.MEASUREMENTS).isGiven()) + .isFalse(); + } + + @Test + public void testFledgeConsentIsEnabled_userProfileIdIsClearedThanRecreated() + throws RemoteException { + doNothing().when(() -> ErrorLogUtil.e(any(), anyInt(), anyInt())); + doReturn(PrivacySandboxUxCollection.UNSUPPORTED_UX).when(mAdServicesStorageManager).getUx(); + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(true); + boolean isGiven = true; + int consentSourceOfTruth = Flags.PPAPI_AND_SYSTEM_SERVER; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForConsentPerApiTesting( + isGiven, consentSourceOfTruth, AdServicesApiType.FLEDGE.toConsentApiType()); + + spyConsentManager.enable(mContextSpy, AdServicesApiType.FLEDGE); + + assertThat(spyConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()).isTrue(); + verify(mUserProfileIdManagerMock).deleteId(); + verify(mUserProfileIdManagerMock).getOrCreateId(); + } + + @Test + public void testFledgeConsentIsDisabled_userProfileIdIsCleared() throws RemoteException { + doNothing().when(() -> ErrorLogUtil.e(any(), anyInt(), anyInt())); + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(true); + doReturn(PrivacySandboxUxCollection.UNSUPPORTED_UX).when(mAdServicesStorageManager).getUx(); + boolean isGiven = false; + int consentSourceOfTruth = Flags.PPAPI_AND_SYSTEM_SERVER; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForConsentPerApiTesting( + isGiven, consentSourceOfTruth, AdServicesApiType.FLEDGE.toConsentApiType()); + + spyConsentManager.disable(mContextSpy, AdServicesApiType.FLEDGE); + + assertThat(spyConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()).isFalse(); + verify(mUserProfileIdManagerMock).deleteId(); + } + + @Test + public void testGetDefaultConsent_AppSearchOnly() throws RemoteException { + doReturn(true).when(mMockFlags).getEnableAppsearchConsentData(); + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(false, consentSourceOfTruth); + + when(mAppSearchConsentManagerMock.getDefaultConsent(eq(AdServicesApiType.ALL_API))) + .thenReturn(AdServicesApiConsent.REVOKED); + assertThat(spyConsentManager.getDefaultConsent()).isFalse(); + + verify(mAppSearchConsentManagerMock).getDefaultConsent(eq(AdServicesApiType.ALL_API)); + } + + @Test + public void testGetDefaultConsent_ppapiAndAdExtDataServiceOnly() + throws RemoteException, IOException { + doReturn(true).when(mMockFlags).getEnableAdExtServiceConsentData(); + int consentSourceOfTruth = Flags.PPAPI_AND_ADEXT_SERVICE; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(false, consentSourceOfTruth); + assertThat(spyConsentManager.getDefaultConsent()).isFalse(); + } + + @Test + public void testGetTopicsDefaultConsent_AppSearchOnly() throws RemoteException { + doReturn(true).when(mMockFlags).getEnableAppsearchConsentData(); + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(false, consentSourceOfTruth); + + when(mAppSearchConsentManagerMock.getDefaultConsent(eq(AdServicesApiType.TOPICS))) + .thenReturn(AdServicesApiConsent.REVOKED); + assertThat(spyConsentManager.getTopicsDefaultConsent()).isFalse(); + verify(mAppSearchConsentManagerMock).getDefaultConsent(eq(AdServicesApiType.TOPICS)); + } + + @Test + public void testGetTopicsDefaultConsent_ppapiAndAdExtDataServiceOnly() + throws RemoteException, IOException { + doReturn(true).when(mMockFlags).getEnableAdExtServiceConsentData(); + int consentSourceOfTruth = Flags.PPAPI_AND_ADEXT_SERVICE; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(false, consentSourceOfTruth); + + assertThat(spyConsentManager.getTopicsDefaultConsent()).isFalse(); + } + + @Test + public void testGetFledgeDefaultConsent_AppSearchOnly() throws RemoteException { + doReturn(true).when(mMockFlags).getEnableAppsearchConsentData(); + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(false, consentSourceOfTruth); + + when(mAppSearchConsentManagerMock.getDefaultConsent(eq(AdServicesApiType.FLEDGE))) + .thenReturn(AdServicesApiConsent.REVOKED); + assertThat(spyConsentManager.getFledgeDefaultConsent()).isFalse(); + verify(mAppSearchConsentManagerMock).getDefaultConsent(eq(AdServicesApiType.FLEDGE)); + } + + @Test + public void testGetFledgeDefaultConsent_ppapiAndAdExtDataServiceOnly() throws RemoteException { + doReturn(true).when(mMockFlags).getEnableAdExtServiceConsentData(); + int consentSourceOfTruth = Flags.PPAPI_AND_ADEXT_SERVICE; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(false, consentSourceOfTruth); + + assertThat(spyConsentManager.getFledgeDefaultConsent()).isFalse(); + } + + @Test + public void testGetMeasurementDefaultConsent_AppSearchOnly() throws RemoteException { + doReturn(true).when(mMockFlags).getEnableAppsearchConsentData(); + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(false, consentSourceOfTruth); + + when(mAppSearchConsentManagerMock.getDefaultConsent(eq(AdServicesApiType.MEASUREMENTS))) + .thenReturn(AdServicesApiConsent.REVOKED); + assertThat(spyConsentManager.getMeasurementDefaultConsent()).isFalse(); + verify(mAppSearchConsentManagerMock).getDefaultConsent(eq(AdServicesApiType.MEASUREMENTS)); + } + + @Test + public void testGetMeasurementDefaultConsent_ppapiAndAdExtDataServiceOnly() + throws RemoteException { + doReturn(true).when(mMockFlags).getEnableAdExtServiceConsentData(); + int consentSourceOfTruth = Flags.PPAPI_AND_ADEXT_SERVICE; + + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(false, consentSourceOfTruth); + spyConsentManager.recordMeasurementDefaultConsent(true); + // assertThat(spyConsentManager.getMeasurementDefaultConsent()).isTrue(); + // spyConsentManager.recordMeasurementDefaultConsent(false); + // assertThat(spyConsentManager.getMeasurementDefaultConsent()).isFalse(); + } + + @Test + public void testGetDefaultAdIdState_AppSearchOnly() throws RemoteException { + doReturn(true).when(mMockFlags).getEnableAppsearchConsentData(); + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(false, consentSourceOfTruth); + + when(mAppSearchConsentManagerMock.getDefaultAdIdState()).thenReturn(false); + assertThat(spyConsentManager.getDefaultAdIdState()).isFalse(); + verify(mAppSearchConsentManagerMock).getDefaultAdIdState(); + } + + @Test + public void testGetDefaultAdIdState_ppapiAndAdExtDataServiceOnly() throws RemoteException { + doReturn(true).when(mMockFlags).getEnableAdExtServiceConsentData(); + int consentSourceOfTruth = Flags.PPAPI_AND_ADEXT_SERVICE; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(false, consentSourceOfTruth); + + spyConsentManager.recordDefaultAdIdState(true); + assertThat(spyConsentManager.getDefaultAdIdState()).isTrue(); + spyConsentManager.recordDefaultAdIdState(false); + assertThat(spyConsentManager.getDefaultAdIdState()).isFalse(); + } + + @Test + public void testRecordDefaultConsent_AppSearchOnly() throws RemoteException, IOException { + doReturn(true).when(mMockFlags).getEnableAppsearchConsentData(); + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(false, consentSourceOfTruth); + + spyConsentManager.recordDefaultConsent(true); + verify(mAppSearchConsentManagerMock) + .recordDefaultConsent(eq(AdServicesApiType.ALL_API), eq(true)); + } + + @Test + public void testRecordDefaultConsent_ppapiAndAdExtDataServiceOnly() + throws RemoteException, IOException { + doReturn(true).when(mMockFlags).getEnableAdExtServiceConsentData(); + int consentSourceOfTruth = Flags.PPAPI_AND_ADEXT_SERVICE; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(false, consentSourceOfTruth); + assertThrows(RuntimeException.class, () -> spyConsentManager.recordDefaultConsent(false)); + } + + @Test + public void testRecordTopicsDefaultConsent_AppSearchOnly() throws RemoteException, IOException { + doReturn(true).when(mMockFlags).getEnableAppsearchConsentData(); + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(false, consentSourceOfTruth); + + spyConsentManager.recordTopicsDefaultConsent(true); + verify(mAppSearchConsentManagerMock) + .recordDefaultConsent(eq(AdServicesApiType.TOPICS), eq(true)); + } + + @Test + public void testRecordTopicsDefaultConsent_ppapiAndAdExtDataServiceOnly() + throws RemoteException { + doReturn(true).when(mMockFlags).getEnableAdExtServiceConsentData(); + int consentSourceOfTruth = Flags.PPAPI_AND_ADEXT_SERVICE; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(false, consentSourceOfTruth); + assertThrows( + IllegalStateException.class, + () -> spyConsentManager.recordTopicsDefaultConsent(true)); + } + + @Test + public void testRecordFledgeDefaultConsent_AppSearchOnly() throws RemoteException, IOException { + doReturn(true).when(mMockFlags).getEnableAppsearchConsentData(); + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(false, consentSourceOfTruth); + + spyConsentManager.recordFledgeDefaultConsent(true); + verify(mAppSearchConsentManagerMock) + .recordDefaultConsent(eq(AdServicesApiType.FLEDGE), eq(true)); + } + + @Test + public void testRecordFledgeDefaultConsent_ppapiAndAdExtDataServiceOnly() + throws RemoteException, IOException { + doReturn(true).when(mMockFlags).getEnableAdExtServiceConsentData(); + int consentSourceOfTruth = Flags.PPAPI_AND_ADEXT_SERVICE; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(false, consentSourceOfTruth); + assertThrows( + IllegalStateException.class, + () -> spyConsentManager.recordFledgeDefaultConsent(true)); + } + + @Test + public void testRecordMeasurementDefaultConsent_AppSearchOnly() throws RemoteException { + doReturn(true).when(mMockFlags).getEnableAppsearchConsentData(); + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(false, consentSourceOfTruth); + + spyConsentManager.recordMeasurementDefaultConsent(true); + verify(mAppSearchConsentManagerMock) + .recordDefaultConsent(eq(AdServicesApiType.MEASUREMENTS), eq(true)); + } + + @Test + public void testRecordDefaultAdIdState_AppSearchOnly() throws RemoteException { + doReturn(true).when(mMockFlags).getEnableAppsearchConsentData(); + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(false, consentSourceOfTruth); + + spyConsentManager.recordDefaultAdIdState(true); + verify(mAppSearchConsentManagerMock).recordDefaultAdIdState(eq(true)); + } + + @Test + public void testAllThreeConsentsPerApiAreGivenAggregatedConsentIsSet_PpApiOnly() + throws RemoteException, IOException { + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(true); + boolean isGiven = true; + int consentSourceOfTruth = Flags.PPAPI_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(isGiven, consentSourceOfTruth); + + spyConsentManager.enable(mContextSpy, AdServicesApiType.TOPICS); + spyConsentManager.enable(mContextSpy, AdServicesApiType.FLEDGE); + spyConsentManager.enable(mContextSpy, AdServicesApiType.MEASUREMENTS); + + assertThat(spyConsentManager.getConsent(AdServicesApiType.TOPICS).isGiven()).isTrue(); + assertThat(spyConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()).isTrue(); + assertThat(spyConsentManager.getConsent(AdServicesApiType.MEASUREMENTS).isGiven()).isTrue(); + assertThat(spyConsentManager.getConsent().isGiven()).isTrue(); + verify(spyConsentManager) + .setPerApiConsentToSourceOfTruth( + eq(/* isGiven */ true), eq(AdServicesApiType.TOPICS)); + verify(spyConsentManager) + .setPerApiConsentToSourceOfTruth( + eq(/* isGiven */ true), eq(AdServicesApiType.FLEDGE)); + verify(spyConsentManager) + .setPerApiConsentToSourceOfTruth( + eq(/* isGiven */ true), eq(AdServicesApiType.MEASUREMENTS)); + verify(mConsentDatastore, times(1)) + .put(eq(AdServicesApiType.ALL_API.toPpApiDatastoreKey()), eq(true)); + + verifyDataCleanup(spyConsentManager); + } + + @Test + public void testAllConsentAreRevokedClenaupIsExecuted() throws IOException, RemoteException { + when(mMockFlags.getGaUxFeatureEnabled()).thenReturn(true); + boolean isGiven = true; + int consentSourceOfTruth = Flags.PPAPI_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting(isGiven, consentSourceOfTruth); + + // set up the initial state + spyConsentManager.enable(mContextSpy, AdServicesApiType.TOPICS); + spyConsentManager.enable(mContextSpy, AdServicesApiType.FLEDGE); + spyConsentManager.enable(mContextSpy, AdServicesApiType.MEASUREMENTS); + + assertThat(spyConsentManager.getConsent(AdServicesApiType.TOPICS).isGiven()).isTrue(); + assertThat(spyConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()).isTrue(); + assertThat(spyConsentManager.getConsent(AdServicesApiType.MEASUREMENTS).isGiven()).isTrue(); + assertThat(spyConsentManager.getConsent().isGiven()).isTrue(); + verify(spyConsentManager) + .setPerApiConsentToSourceOfTruth( + eq(/* isGiven */ true), eq(AdServicesApiType.TOPICS)); + verify(spyConsentManager) + .setPerApiConsentToSourceOfTruth( + eq(/* isGiven */ true), eq(AdServicesApiType.FLEDGE)); + verify(spyConsentManager) + .setPerApiConsentToSourceOfTruth( + eq(/* isGiven */ true), eq(AdServicesApiType.MEASUREMENTS)); + + // disable all the consent one by one + spyConsentManager.disable(mContextSpy, AdServicesApiType.TOPICS); + spyConsentManager.disable(mContextSpy, AdServicesApiType.FLEDGE); + spyConsentManager.disable(mContextSpy, AdServicesApiType.MEASUREMENTS); + + assertThat(spyConsentManager.getConsent(AdServicesApiType.TOPICS).isGiven()).isFalse(); + assertThat(spyConsentManager.getConsent(AdServicesApiType.FLEDGE).isGiven()).isFalse(); + assertThat(spyConsentManager.getConsent(AdServicesApiType.MEASUREMENTS).isGiven()) + .isFalse(); + assertThat(spyConsentManager.getConsent().isGiven()).isFalse(); + + verify( + () -> + BackgroundJobsManager.unscheduleJobsPerApi( + any(JobScheduler.class), eq(AdServicesApiType.TOPICS))); + verify( + () -> + BackgroundJobsManager.unscheduleJobsPerApi( + any(JobScheduler.class), eq(AdServicesApiType.FLEDGE))); + verify( + () -> + BackgroundJobsManager.unscheduleJobsPerApi( + any(JobScheduler.class), eq(AdServicesApiType.MEASUREMENTS))); + verify(() -> BackgroundJobsManager.unscheduleAllBackgroundJobs(any(JobScheduler.class))); + + verify(spyConsentManager, times(2)).resetTopicsAndBlockedTopics(); + verify(spyConsentManager, times(2)).clearAllAppConsentData(); + verify(spyConsentManager, times(2)).resetMeasurement(); + } + + @Test + public void testManualInteractionWithConsentRecorded_PpApiOnly() throws RemoteException { + int consentSourceOfTruth = Flags.PPAPI_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + assertThat(spyConsentManager.getUserManualInteractionWithConsent()).isEqualTo(UNKNOWN); + + verify(mMockIAdServicesManager, never()).getUserManualInteractionWithConsent(); + + spyConsentManager.recordUserManualInteractionWithConsent(MANUAL_INTERACTIONS_RECORDED); + + assertThat(spyConsentManager.getUserManualInteractionWithConsent()) + .isEqualTo(MANUAL_INTERACTIONS_RECORDED); + + verify(mMockIAdServicesManager, never()).getUserManualInteractionWithConsent(); + verify(mMockIAdServicesManager, never()).recordUserManualInteractionWithConsent(anyInt()); + } + + @Test + public void testManualInteractionWithConsentRecorded_SystemServerOnly() throws RemoteException { + int consentSourceOfTruth = Flags.SYSTEM_SERVER_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + assertThat(spyConsentManager.getUserManualInteractionWithConsent()).isEqualTo(UNKNOWN); + + verify(mMockIAdServicesManager).getUserManualInteractionWithConsent(); + + doReturn(MANUAL_INTERACTIONS_RECORDED) + .when(mMockIAdServicesManager) + .getUserManualInteractionWithConsent(); + spyConsentManager.recordUserManualInteractionWithConsent(MANUAL_INTERACTIONS_RECORDED); + + assertThat(spyConsentManager.getUserManualInteractionWithConsent()) + .isEqualTo(MANUAL_INTERACTIONS_RECORDED); + + verify(mMockIAdServicesManager, times(2)).getUserManualInteractionWithConsent(); + verify(mMockIAdServicesManager).recordUserManualInteractionWithConsent(anyInt()); + + // Verify the bit is not set in PPAPI + assertThat(mConsentDatastore.get(MANUAL_INTERACTION_WITH_CONSENT_RECORDED)).isNull(); + } + + @Test + public void testManualInteractionWithConsentRecorded_PpApiAndSystemServer() + throws RemoteException { + int consentSourceOfTruth = Flags.PPAPI_AND_SYSTEM_SERVER; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + @ConsentManagerV2.UserManualInteraction + int userManualInteractionWithConsent = + spyConsentManager.getUserManualInteractionWithConsent(); + + assertThat(userManualInteractionWithConsent).isEqualTo(UNKNOWN); + + verify(mMockIAdServicesManager).getUserManualInteractionWithConsent(); + + doReturn(MANUAL_INTERACTIONS_RECORDED) + .when(mMockIAdServicesManager) + .getUserManualInteractionWithConsent(); + spyConsentManager.recordUserManualInteractionWithConsent(MANUAL_INTERACTIONS_RECORDED); + + assertThat(spyConsentManager.getUserManualInteractionWithConsent()) + .isEqualTo(MANUAL_INTERACTIONS_RECORDED); + + verify(mMockIAdServicesManager, times(2)).getUserManualInteractionWithConsent(); + verify(mMockIAdServicesManager).recordUserManualInteractionWithConsent(anyInt()); + + // Verify the bit is also set in PPAPI + assertThat(mConsentDatastore.get(MANUAL_INTERACTION_WITH_CONSENT_RECORDED)).isTrue(); + } + + @Test + public void testManualInteractionWithConsentRecorded_appSearchOnly() throws RemoteException { + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + when(mMockFlags.getEnableAppsearchConsentData()).thenReturn(true); + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + when(mAppSearchConsentManagerMock.getUserManualInteractionWithConsent()) + .thenReturn(UNKNOWN); + assertThat(spyConsentManager.getUserManualInteractionWithConsent()).isEqualTo(UNKNOWN); + verify(mAppSearchConsentManagerMock).getUserManualInteractionWithConsent(); + verify(mMockIAdServicesManager, never()).getUserManualInteractionWithConsent(); + + spyConsentManager.recordUserManualInteractionWithConsent(MANUAL_INTERACTIONS_RECORDED); + verify(mAppSearchConsentManagerMock) + .recordUserManualInteractionWithConsent(MANUAL_INTERACTIONS_RECORDED); + when(mAppSearchConsentManagerMock.getUserManualInteractionWithConsent()) + .thenReturn(MANUAL_INTERACTIONS_RECORDED); + assertThat(spyConsentManager.getUserManualInteractionWithConsent()) + .isEqualTo(MANUAL_INTERACTIONS_RECORDED); + + verify(mMockIAdServicesManager, never()).getUserManualInteractionWithConsent(); + verify(mMockIAdServicesManager, never()).recordUserManualInteractionWithConsent(anyInt()); + } + + @Test + public void testManualInteractionWithConsentRecorded_ppapiAndAdExtDataServiceOnly() + throws RemoteException, IOException { + int consentSourceOfTruth = Flags.PPAPI_AND_ADEXT_SERVICE; + when(mMockFlags.getEnableAdExtServiceConsentData()).thenReturn(true); + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + when(mAdServicesExtDataManager.getManualInteractionWithConsentStatus()).thenReturn(UNKNOWN); + assertThat(spyConsentManager.getUserManualInteractionWithConsent()).isEqualTo(UNKNOWN); + verify(mAdServicesExtDataManager).getManualInteractionWithConsentStatus(); + + spyConsentManager.recordUserManualInteractionWithConsent(MANUAL_INTERACTIONS_RECORDED); + verify(mAppConsentForRStorageManager) + .recordUserManualInteractionWithConsent(MANUAL_INTERACTIONS_RECORDED); + when(mAdServicesExtDataManager.getManualInteractionWithConsentStatus()) + .thenReturn(MANUAL_INTERACTIONS_RECORDED); + assertThat(spyConsentManager.getUserManualInteractionWithConsent()) + .isEqualTo(MANUAL_INTERACTIONS_RECORDED); + } + + // Note this method needs to be invoked after other private variables are initialized. + private ConsentManagerV2 getConsentManagerByConsentSourceOfTruth(int consentSourceOfTruth) { + return new ConsentManagerV2( + mTopicsWorkerMock, + mAppConsentDaoSpy, + mEnrollmentDaoSpy, + mMeasurementImplMock, + mCustomAudienceDaoMock, + mAppConsentStorageManager, + mAppInstallDaoMock, + mFrequencyCapDaoMock, + mAdServicesStorageManager, + mConsentDatastore, + mAppSearchConsentManagerMock, + mUserProfileIdManagerMock, + mAppConsentForRStorageManager, + mMockFlags, + consentSourceOfTruth, + true, + true); + } + + private ConsentManagerV2 getSpiedConsentManagerForMigrationTesting( + boolean isGiven, int consentSourceOfTruth) throws RemoteException { + ConsentManagerV2 consentManager = + spy(getConsentManagerByConsentSourceOfTruth(consentSourceOfTruth)); + + // Disable IPC calls + ConsentParcel consentParcel = + isGiven + ? ConsentParcel.createGivenConsent(ConsentParcel.ALL_API) + : ConsentParcel.createRevokedConsent(ConsentParcel.ALL_API); + doReturn(consentParcel).when(mMockIAdServicesManager).getConsent(ConsentParcel.ALL_API); + doReturn(isGiven).when(mMockIAdServicesManager).wasNotificationDisplayed(); + doNothing().when(mMockIAdServicesManager).recordNotificationDisplayed(true); + doReturn(isGiven).when(mMockIAdServicesManager).wasGaUxNotificationDisplayed(); + doNothing().when(mMockIAdServicesManager).recordGaUxNotificationDisplayed(true); + doReturn(UNKNOWN).when(mMockIAdServicesManager).getUserManualInteractionWithConsent(); + doNothing().when(mMockIAdServicesManager).recordUserManualInteractionWithConsent(anyInt()); + doReturn(AdServicesApiConsent.getConsent(isGiven)) + .when(mAppSearchConsentManagerMock) + .getConsent(AdServicesApiType.ALL_API); + return consentManager; + } + + private ConsentManagerV2 getSpiedConsentManagerForConsentPerApiTesting( + boolean isGiven, + int consentSourceOfTruth, + @ConsentParcel.ConsentApiType int consentApiType) + throws RemoteException { + ConsentManagerV2 consentManager = + spy(getConsentManagerByConsentSourceOfTruth(consentSourceOfTruth)); + + // Disable IPC calls + doNothing().when(mAdServicesStorageManager).setConsent(any(), anyBoolean()); + ConsentParcel consentParcel = + isGiven + ? ConsentParcel.createGivenConsent(consentApiType) + : ConsentParcel.createRevokedConsent(consentApiType); + doReturn(consentParcel).when(mMockIAdServicesManager).getConsent(consentApiType); + doReturn(isGiven).when(mMockIAdServicesManager).wasNotificationDisplayed(); + doNothing().when(mMockIAdServicesManager).recordNotificationDisplayed(true); + doReturn(isGiven).when(mMockIAdServicesManager).wasGaUxNotificationDisplayed(); + doNothing().when(mMockIAdServicesManager).recordGaUxNotificationDisplayed(true); + doReturn(UNKNOWN).when(mMockIAdServicesManager).getUserManualInteractionWithConsent(); + doNothing().when(mMockIAdServicesManager).recordUserManualInteractionWithConsent(anyInt()); + doReturn(AdServicesApiConsent.getConsent(isGiven)) + .when(mAppSearchConsentManagerMock) + .getConsent(any()); + return consentManager; + } + + private void verifyConsentMigration( + boolean isGiven, + boolean hasWrittenToPpApi, + boolean hasWrittenToSystemServer, + boolean hasReadFromSystemServer) + throws RemoteException, IOException { + verify(mAppConsentStorageManager, verificationMode(hasWrittenToPpApi)) + .setConsent(any(), eq(isGiven)); + verify(mAdServicesStorageManager, verificationMode(hasWrittenToSystemServer)) + .setConsent(any(), eq(isGiven)); + + verify(mMockIAdServicesManager, verificationMode(hasReadFromSystemServer)) + .getConsent(ConsentParcel.ALL_API); + } + + private void verifyDataCleanup(ConsentManagerV2 consentManager) throws IOException { + verify(consentManager).resetTopicsAndBlockedTopics(); + verify(consentManager).clearAllAppConsentData(); + verify(consentManager).resetMeasurement(); + } + + private VerificationMode verificationMode(boolean hasHappened) { + return hasHappened ? atLeastOnce() : never(); + } + + private void mockGetPackageUid(@NonNull String packageName, int uid) + throws PackageManager.NameNotFoundException { + doReturn(uid) + .when( + () -> + PackageManagerCompatUtils.getPackageUid( + any(), eq(packageName), anyInt())); + } + + private void mockInstalledApplications(List<ApplicationInfo> applicationsInstalled) { + doReturn(applicationsInstalled) + .when(() -> PackageManagerCompatUtils.getInstalledApplications(any(), anyInt())); + } + + private void mockThrowExceptionOnGetPackageUid(@NonNull String packageName) { + doThrow(PackageManager.NameNotFoundException.class) + .when( + () -> + PackageManagerCompatUtils.getPackageUid( + any(), eq(packageName), anyInt())); + } + + private List<ApplicationInfo> createApplicationInfos(String... packageNames) { + return Arrays.stream(packageNames) + .map(s -> ApplicationInfoBuilder.newBuilder().setPackageName(s).build()) + .collect(Collectors.toList()); + } + + private class ListMatcherIgnoreOrder implements ArgumentMatcher<List<String>> { + @NonNull private final List<String> mStrings; + + private ListMatcherIgnoreOrder(@NonNull List<String> strings) { + Objects.requireNonNull(strings); + mStrings = strings; + } + + @Override + public boolean matches(@Nullable List<String> argument) { + if (argument == null) { + return false; + } + if (argument.size() != mStrings.size()) { + return false; + } + if (!argument.containsAll(mStrings)) { + return false; + } + if (!mStrings.containsAll(argument)) { + return false; + } + return true; + } + } + + @Test + public void testCurrentPrivacySandboxFeature_ppapiOnly() throws RemoteException { + getCurrentPrivacySandboxFeatureWithPpApiOnly(Flags.PPAPI_ONLY); + } + + @Test + public void testCurrentPrivacySandboxFeature_ppapiAndAdExtDataServiceOnly() + throws RemoteException { + when(mMockFlags.getEnableAdExtServiceConsentData()).thenReturn(true); + getCurrentPrivacySandboxFeatureWithPpApiOnly(Flags.PPAPI_AND_ADEXT_SERVICE); + } + + private void getCurrentPrivacySandboxFeatureWithPpApiOnly(int consentSourceOfTruth) + throws RemoteException { + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + assertThat(spyConsentManager.getCurrentPrivacySandboxFeature()) + .isEqualTo(PrivacySandboxFeatureType.PRIVACY_SANDBOX_UNSUPPORTED); + verify(mMockIAdServicesManager, never()).getCurrentPrivacySandboxFeature(); + + spyConsentManager.setCurrentPrivacySandboxFeature( + PrivacySandboxFeatureType.PRIVACY_SANDBOX_FIRST_CONSENT); + assertThat(spyConsentManager.getCurrentPrivacySandboxFeature()) + .isEqualTo(PrivacySandboxFeatureType.PRIVACY_SANDBOX_FIRST_CONSENT); + + verify(mMockIAdServicesManager, never()).getCurrentPrivacySandboxFeature(); + verify(mMockIAdServicesManager, never()).setCurrentPrivacySandboxFeature(anyString()); + } + + @Test + public void testCurrentPrivacySandboxFeature_SystemServerOnly() + throws RemoteException, IOException { + int consentSourceOfTruth = Flags.SYSTEM_SERVER_ONLY; + doThrow(RuntimeException.class) + .when(mAdServicesStorageManager) + .getCurrentPrivacySandboxFeature(); + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + doNothing().when(() -> ErrorLogUtil.e(any(), anyInt(), anyInt())); + assertThat(spyConsentManager.getCurrentPrivacySandboxFeature()) + .isEqualTo(PrivacySandboxFeatureType.PRIVACY_SANDBOX_UNSUPPORTED); + verify(mAdServicesStorageManager).getCurrentPrivacySandboxFeature(); + + int errorCode = AD_SERVICES_ERROR_REPORTED__ERROR_CODE__PRIVACY_SANDBOX_SAVE_FAILURE; + MockedVoidMethod mockedVoidMethod = + () -> + ErrorLogUtil.e( + any(), + eq(errorCode), + eq(AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX)); + verify(mockedVoidMethod); + doReturn(PrivacySandboxFeatureType.PRIVACY_SANDBOX_FIRST_CONSENT) + .when(mAdServicesStorageManager) + .getCurrentPrivacySandboxFeature(); + spyConsentManager.setCurrentPrivacySandboxFeature( + PrivacySandboxFeatureType.PRIVACY_SANDBOX_FIRST_CONSENT); + assertThat(spyConsentManager.getCurrentPrivacySandboxFeature()) + .isEqualTo(PrivacySandboxFeatureType.PRIVACY_SANDBOX_FIRST_CONSENT); + + doReturn(PrivacySandboxFeatureType.PRIVACY_SANDBOX_RECONSENT) + .when(mAdServicesStorageManager) + .getCurrentPrivacySandboxFeature(); + spyConsentManager.setCurrentPrivacySandboxFeature( + PrivacySandboxFeatureType.PRIVACY_SANDBOX_RECONSENT); + assertThat(spyConsentManager.getCurrentPrivacySandboxFeature()) + .isEqualTo(PrivacySandboxFeatureType.PRIVACY_SANDBOX_RECONSENT); + + doReturn(PrivacySandboxFeatureType.PRIVACY_SANDBOX_UNSUPPORTED) + .when(mAdServicesStorageManager) + .getCurrentPrivacySandboxFeature(); + spyConsentManager.setCurrentPrivacySandboxFeature( + PrivacySandboxFeatureType.PRIVACY_SANDBOX_UNSUPPORTED); + assertThat(spyConsentManager.getCurrentPrivacySandboxFeature()) + .isEqualTo(PrivacySandboxFeatureType.PRIVACY_SANDBOX_UNSUPPORTED); + + assertThat( + mConsentDatastore.get( + PrivacySandboxFeatureType.PRIVACY_SANDBOX_UNSUPPORTED.name())) + .isNull(); + assertThat( + mConsentDatastore.get( + PrivacySandboxFeatureType.PRIVACY_SANDBOX_FIRST_CONSENT.name())) + .isNull(); + assertThat( + mConsentDatastore.get( + PrivacySandboxFeatureType.PRIVACY_SANDBOX_RECONSENT.name())) + .isNull(); + } + + @Test + public void testCurrentPrivacySandboxFeature_PpApiAndSystemServer() + throws RemoteException, IOException { + doNothing().when(() -> ErrorLogUtil.e(any(), anyInt(), anyInt())); + int consentSourceOfTruth = Flags.PPAPI_AND_SYSTEM_SERVER; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + doThrow(RuntimeException.class) + .when(mAdServicesStorageManager) + .getCurrentPrivacySandboxFeature(); + assertThat(spyConsentManager.getCurrentPrivacySandboxFeature()) + .isEqualTo(PrivacySandboxFeatureType.PRIVACY_SANDBOX_UNSUPPORTED); + verify(mAdServicesStorageManager).getCurrentPrivacySandboxFeature(); + doThrow(RuntimeException.class) + .when(mAdServicesStorageManager) + .getCurrentPrivacySandboxFeature(); + int saveErrorCode = AD_SERVICES_ERROR_REPORTED__ERROR_CODE__PRIVACY_SANDBOX_SAVE_FAILURE; + MockedVoidMethod mockedVoidMethod = + () -> + ErrorLogUtil.e( + any(), + eq(saveErrorCode), + eq(AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__UX)); + verify(mockedVoidMethod); + doReturn(PrivacySandboxFeatureType.PRIVACY_SANDBOX_FIRST_CONSENT) + .when(mAdServicesStorageManager) + .getCurrentPrivacySandboxFeature(); + spyConsentManager.setCurrentPrivacySandboxFeature( + PrivacySandboxFeatureType.PRIVACY_SANDBOX_FIRST_CONSENT); + assertThat(spyConsentManager.getCurrentPrivacySandboxFeature()) + .isEqualTo(PrivacySandboxFeatureType.PRIVACY_SANDBOX_FIRST_CONSENT); + + doReturn(PrivacySandboxFeatureType.PRIVACY_SANDBOX_UNSUPPORTED) + .when(mAdServicesStorageManager) + .getCurrentPrivacySandboxFeature(); + spyConsentManager.setCurrentPrivacySandboxFeature( + PrivacySandboxFeatureType.PRIVACY_SANDBOX_UNSUPPORTED); + assertThat(spyConsentManager.getCurrentPrivacySandboxFeature()) + .isEqualTo(PrivacySandboxFeatureType.PRIVACY_SANDBOX_UNSUPPORTED); + + doReturn(PrivacySandboxFeatureType.PRIVACY_SANDBOX_RECONSENT) + .when(mAdServicesStorageManager) + .getCurrentPrivacySandboxFeature(); + spyConsentManager.setCurrentPrivacySandboxFeature( + PrivacySandboxFeatureType.PRIVACY_SANDBOX_RECONSENT); + assertThat(spyConsentManager.getCurrentPrivacySandboxFeature()) + .isEqualTo(PrivacySandboxFeatureType.PRIVACY_SANDBOX_RECONSENT); + + // Only the last set bit is true. + assertThat( + mConsentDatastore.get( + PrivacySandboxFeatureType.PRIVACY_SANDBOX_UNSUPPORTED.name())) + .isFalse(); + assertThat( + mConsentDatastore.get( + PrivacySandboxFeatureType.PRIVACY_SANDBOX_FIRST_CONSENT.name())) + .isFalse(); + assertThat( + mConsentDatastore.get( + PrivacySandboxFeatureType.PRIVACY_SANDBOX_RECONSENT.name())) + .isTrue(); + } + + @Test + public void testCurrentPrivacySandboxFeature_appSearchOnly() throws RemoteException { + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + when(mMockFlags.getEnableAppsearchConsentData()).thenReturn(true); + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + when(mAppSearchConsentManagerMock.getCurrentPrivacySandboxFeature()) + .thenReturn(PrivacySandboxFeatureType.PRIVACY_SANDBOX_UNSUPPORTED); + assertThat(spyConsentManager.getCurrentPrivacySandboxFeature()) + .isEqualTo(PrivacySandboxFeatureType.PRIVACY_SANDBOX_UNSUPPORTED); + + spyConsentManager.setCurrentPrivacySandboxFeature( + PrivacySandboxFeatureType.PRIVACY_SANDBOX_FIRST_CONSENT); + verify(mAppSearchConsentManagerMock) + .setCurrentPrivacySandboxFeature( + eq(PrivacySandboxFeatureType.PRIVACY_SANDBOX_FIRST_CONSENT)); + when(mAppSearchConsentManagerMock.getCurrentPrivacySandboxFeature()) + .thenReturn(PrivacySandboxFeatureType.PRIVACY_SANDBOX_FIRST_CONSENT); + assertThat(spyConsentManager.getCurrentPrivacySandboxFeature()) + .isEqualTo(PrivacySandboxFeatureType.PRIVACY_SANDBOX_FIRST_CONSENT); + + verify(mMockIAdServicesManager, never()).getCurrentPrivacySandboxFeature(); + verify(mMockIAdServicesManager, never()).setCurrentPrivacySandboxFeature(anyString()); + } + + @Test + public void isAdIdEnabledTest_SystemServerOnly() throws RemoteException { + int consentSourceOfTruth = Flags.SYSTEM_SERVER_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + assertThat(spyConsentManager.isAdIdEnabled()).isFalse(); + + verify(mMockIAdServicesManager).isAdIdEnabled(); + + doReturn(true).when(mMockIAdServicesManager).isAdIdEnabled(); + spyConsentManager.setAdIdEnabled(true); + + assertThat(spyConsentManager.isAdIdEnabled()).isTrue(); + + verify(mMockIAdServicesManager, times(2)).isAdIdEnabled(); + verify(mMockIAdServicesManager).setAdIdEnabled(anyBoolean()); + } + + @Test + public void isAdIdEnabledTest_PpApiAndSystemServer() throws RemoteException { + int consentSourceOfTruth = Flags.PPAPI_AND_SYSTEM_SERVER; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + Boolean isAdIdEnabled = spyConsentManager.isAdIdEnabled(); + + assertThat(isAdIdEnabled).isFalse(); + + verify(mMockIAdServicesManager).isAdIdEnabled(); + + doReturn(true).when(mMockIAdServicesManager).isAdIdEnabled(); + spyConsentManager.setAdIdEnabled(true); + + assertThat(spyConsentManager.isAdIdEnabled()).isTrue(); + + verify(mMockIAdServicesManager, times(2)).isAdIdEnabled(); + verify(mMockIAdServicesManager).setAdIdEnabled(anyBoolean()); + } + + @Test + public void isAdIdEnabledTest_appSearchOnly() throws RemoteException { + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + when(mMockFlags.getEnableAppsearchConsentData()).thenReturn(true); + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + doReturn(false).when(mAppSearchConsentManagerMock).isAdIdEnabled(); + assertThat(spyConsentManager.isAdIdEnabled()).isFalse(); + verify(mAppSearchConsentManagerMock).isAdIdEnabled(); + + doReturn(true).when(mAppSearchConsentManagerMock).isAdIdEnabled(); + spyConsentManager.setAdIdEnabled(true); + + assertThat(spyConsentManager.isAdIdEnabled()).isTrue(); + + verify(mAppSearchConsentManagerMock, times(2)).isAdIdEnabled(); + verify(mAppSearchConsentManagerMock).setAdIdEnabled(anyBoolean()); + } + + @Test + public void isU18AccountTest_SystemServerOnly() throws RemoteException { + int consentSourceOfTruth = Flags.SYSTEM_SERVER_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + assertThat(spyConsentManager.isU18Account()).isFalse(); + + verify(mMockIAdServicesManager).isU18Account(); + + doReturn(true).when(mMockIAdServicesManager).isU18Account(); + spyConsentManager.setU18Account(true); + + assertThat(spyConsentManager.isU18Account()).isTrue(); + + verify(mMockIAdServicesManager, times(2)).isU18Account(); + verify(mMockIAdServicesManager).setU18Account(anyBoolean()); + } + + @Test + public void isU18AccountTest_PpApiAndSystemServer() throws RemoteException, IOException { + int consentSourceOfTruth = Flags.PPAPI_AND_SYSTEM_SERVER; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + Boolean isU18Account = spyConsentManager.isU18Account(); + + assertThat(isU18Account).isFalse(); + + verify(mMockIAdServicesManager).isU18Account(); + + doReturn(true).when(mMockIAdServicesManager).isU18Account(); + spyConsentManager.setU18Account(true); + + assertThat(spyConsentManager.isU18Account()).isTrue(); + + verify(mMockIAdServicesManager, times(2)).isU18Account(); + verify(mMockIAdServicesManager).setU18Account(anyBoolean()); + } + + @Test + public void isU18AccountTest_appSearchOnly() throws RemoteException { + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + when(mMockFlags.getEnableAppsearchConsentData()).thenReturn(true); + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + doReturn(false).when(mAppSearchConsentManagerMock).isU18Account(); + assertThat(spyConsentManager.isU18Account()).isFalse(); + verify(mAppSearchConsentManagerMock).isU18Account(); + + doReturn(true).when(mAppSearchConsentManagerMock).isU18Account(); + spyConsentManager.setU18Account(true); + + assertThat(spyConsentManager.isU18Account()).isTrue(); + + verify(mAppSearchConsentManagerMock, times(2)).isU18Account(); + verify(mAppSearchConsentManagerMock).setU18Account(anyBoolean()); + } + + @Test + public void isU18AccountTest_ppapiAndAdExtDataServiceOnly() + throws RemoteException, IOException { + int consentSourceOfTruth = Flags.PPAPI_AND_ADEXT_SERVICE; + when(mMockFlags.getEnableAdExtServiceConsentData()).thenReturn(true); + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + doReturn(false).when(mAppConsentForRStorageManager).isU18Account(); + assertThat(spyConsentManager.isU18Account()).isFalse(); + verify(mAppConsentForRStorageManager).isU18Account(); + + doReturn(true).when(mAppConsentForRStorageManager).isU18Account(); + spyConsentManager.setU18Account(true); + + assertThat(spyConsentManager.isU18Account()).isTrue(); + + verify(mAppConsentForRStorageManager, times(2)).isU18Account(); + verify(mAppConsentForRStorageManager).setU18Account(anyBoolean()); + } + + @Test + public void isEntryPointEnabledTest_ppapiAndAdExtDataServiceOnly() + throws RemoteException, IOException { + int consentSourceOfTruth = Flags.PPAPI_AND_ADEXT_SERVICE; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + spyConsentManager.setEntryPointEnabled(true); + assertThat(spyConsentManager.isEntryPointEnabled()).isTrue(); + spyConsentManager.setEntryPointEnabled(false); + assertThat(spyConsentManager.isEntryPointEnabled()).isFalse(); + } + + @Test + public void isEntryPointEnabledTest_SystemServerOnly() throws RemoteException { + int consentSourceOfTruth = Flags.SYSTEM_SERVER_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + assertThat(spyConsentManager.isEntryPointEnabled()).isFalse(); + + verify(mMockIAdServicesManager).isEntryPointEnabled(); + + doReturn(true).when(mMockIAdServicesManager).isEntryPointEnabled(); + spyConsentManager.setEntryPointEnabled(true); + + assertThat(spyConsentManager.isEntryPointEnabled()).isTrue(); + + verify(mMockIAdServicesManager, times(2)).isEntryPointEnabled(); + verify(mMockIAdServicesManager).setEntryPointEnabled(anyBoolean()); + } + + @Test + public void isEntryPointEnabledTest_PpApiAndSystemServer() throws RemoteException { + int consentSourceOfTruth = Flags.PPAPI_AND_SYSTEM_SERVER; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + Boolean isEntryPointEnabled = spyConsentManager.isEntryPointEnabled(); + + assertThat(isEntryPointEnabled).isFalse(); + + verify(mMockIAdServicesManager).isEntryPointEnabled(); + + doReturn(true).when(mMockIAdServicesManager).isEntryPointEnabled(); + spyConsentManager.setEntryPointEnabled(true); + + assertThat(spyConsentManager.isEntryPointEnabled()).isTrue(); + + verify(mMockIAdServicesManager, times(2)).isEntryPointEnabled(); + verify(mMockIAdServicesManager).setEntryPointEnabled(anyBoolean()); + } + + @Test + public void isEntryPointEnabledTest_appSearchOnly() throws RemoteException { + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + when(mMockFlags.getEnableAppsearchConsentData()).thenReturn(true); + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + doReturn(false).when(mAppSearchConsentManagerMock).isEntryPointEnabled(); + assertThat(spyConsentManager.isEntryPointEnabled()).isFalse(); + verify(mAppSearchConsentManagerMock).isEntryPointEnabled(); + + doReturn(true).when(mAppSearchConsentManagerMock).isEntryPointEnabled(); + spyConsentManager.setEntryPointEnabled(true); + + assertThat(spyConsentManager.isEntryPointEnabled()).isTrue(); + + verify(mAppSearchConsentManagerMock, times(2)).isEntryPointEnabled(); + verify(mAppSearchConsentManagerMock).setEntryPointEnabled(anyBoolean()); + } + + @Test + public void isAdultAccountTest_SystemServerOnly() throws RemoteException { + int consentSourceOfTruth = Flags.SYSTEM_SERVER_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + assertThat(spyConsentManager.isAdultAccount()).isFalse(); + + verify(mMockIAdServicesManager).isAdultAccount(); + + doReturn(true).when(mMockIAdServicesManager).isAdultAccount(); + spyConsentManager.setAdultAccount(true); + + assertThat(spyConsentManager.isAdultAccount()).isTrue(); + + verify(mMockIAdServicesManager, times(2)).isAdultAccount(); + verify(mMockIAdServicesManager).setAdultAccount(anyBoolean()); + } + + @Test + public void isAdultAccountTest_PpApiAndSystemServer() throws RemoteException { + int consentSourceOfTruth = Flags.PPAPI_AND_SYSTEM_SERVER; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + Boolean isAdultAccount = spyConsentManager.isAdultAccount(); + + assertThat(isAdultAccount).isFalse(); + + verify(mMockIAdServicesManager).isAdultAccount(); + + doReturn(true).when(mMockIAdServicesManager).isAdultAccount(); + spyConsentManager.setAdultAccount(true); + + assertThat(spyConsentManager.isAdultAccount()).isTrue(); + + verify(mMockIAdServicesManager, times(2)).isAdultAccount(); + verify(mMockIAdServicesManager).setAdultAccount(anyBoolean()); + } + + @Test + public void isAdultAccountTest_ppapiAndAdExtDataServiceOnly() + throws RemoteException, IOException { + int consentSourceOfTruth = Flags.PPAPI_AND_ADEXT_SERVICE; + when(mMockFlags.getEnableAdExtServiceConsentData()).thenReturn(true); + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + doReturn(false).when(mAppConsentForRStorageManager).isAdultAccount(); + assertThat(spyConsentManager.isAdultAccount()).isFalse(); + verify(mAppConsentForRStorageManager).isAdultAccount(); + + doReturn(true).when(mAppConsentForRStorageManager).isAdultAccount(); + spyConsentManager.setAdultAccount(true); + + assertThat(spyConsentManager.isAdultAccount()).isTrue(); + + verify(mAppConsentForRStorageManager, times(2)).isAdultAccount(); + verify(mAppConsentForRStorageManager).setAdultAccount(anyBoolean()); + } + + @Test + public void isAdultAccountTest_appSearchOnly() throws RemoteException { + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + when(mMockFlags.getEnableAppsearchConsentData()).thenReturn(true); + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + doReturn(false).when(mAppSearchConsentManagerMock).isAdultAccount(); + assertThat(spyConsentManager.isAdultAccount()).isFalse(); + verify(mAppSearchConsentManagerMock).isAdultAccount(); + + doReturn(true).when(mAppSearchConsentManagerMock).isAdultAccount(); + spyConsentManager.setAdultAccount(true); + + assertThat(spyConsentManager.isAdultAccount()).isTrue(); + + verify(mAppSearchConsentManagerMock, times(2)).isAdultAccount(); + verify(mAppSearchConsentManagerMock).setAdultAccount(anyBoolean()); + } + + @Test + public void testDefaultConsentRecorded_PpApiOnly() throws RemoteException { + int consentSourceOfTruth = Flags.PPAPI_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + assertThat(spyConsentManager.getDefaultConsent()).isFalse(); + + verify(mMockIAdServicesManager, never()).getDefaultConsent(); + + spyConsentManager.recordDefaultConsent(true); + + assertThat(spyConsentManager.getDefaultConsent()).isTrue(); + + verify(mMockIAdServicesManager, never()).getDefaultConsent(); + verify(mMockIAdServicesManager, never()).recordDefaultConsent(anyBoolean()); + } + + @Test + public void testDefaultConsentRecorded_SystemServerOnly() throws RemoteException { + int consentSourceOfTruth = Flags.SYSTEM_SERVER_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + assertThat(spyConsentManager.getDefaultConsent()).isFalse(); + + verify(mMockIAdServicesManager).getDefaultConsent(); + + doReturn(true).when(mMockIAdServicesManager).getDefaultConsent(); + spyConsentManager.recordDefaultConsent(true); + + assertThat(spyConsentManager.getDefaultConsent()).isTrue(); + + verify(mMockIAdServicesManager, times(2)).getDefaultConsent(); + verify(mMockIAdServicesManager).recordDefaultConsent(eq(true)); + + // Verify default consent is not set in PPAPI + assertThat(mConsentDatastore.get(DEFAULT_CONSENT)).isNull(); + } + + @Test + public void testDefaultConsentRecorded_PpApiAndSystemServer() throws RemoteException { + int consentSourceOfTruth = Flags.PPAPI_AND_SYSTEM_SERVER; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + Boolean getDefaultConsent = spyConsentManager.getDefaultConsent(); + + assertThat(getDefaultConsent).isFalse(); + + verify(mMockIAdServicesManager).getDefaultConsent(); + + doReturn(true).when(mMockIAdServicesManager).getDefaultConsent(); + spyConsentManager.recordDefaultConsent(true); + + assertThat(spyConsentManager.getDefaultConsent()).isTrue(); + + verify(mMockIAdServicesManager, times(2)).getDefaultConsent(); + verify(mMockIAdServicesManager).recordDefaultConsent(eq(true)); + + // Verify default consent is also set in PPAPI + assertThat(mConsentDatastore.get(DEFAULT_CONSENT)).isTrue(); + } + + @Test + public void testDefaultConsentRecorded_ppapiAndAdExtDataServiceOnly() throws RemoteException { + int consentSourceOfTruth = Flags.PPAPI_AND_ADEXT_SERVICE; + when(mMockFlags.getEnableAdExtServiceConsentData()).thenReturn(true); + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + assertThat(spyConsentManager.getDefaultConsent()).isFalse(); + } + + @Test + public void wasU18NotificationDisplayedTest_SystemServerOnly() throws RemoteException { + int consentSourceOfTruth = Flags.SYSTEM_SERVER_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + assertThat(spyConsentManager.wasU18NotificationDisplayed()).isFalse(); + + verify(mMockIAdServicesManager).wasU18NotificationDisplayed(); + + doReturn(true).when(mMockIAdServicesManager).wasU18NotificationDisplayed(); + spyConsentManager.setU18NotificationDisplayed(true); + + assertThat(spyConsentManager.wasU18NotificationDisplayed()).isTrue(); + + verify(mMockIAdServicesManager, times(2)).wasU18NotificationDisplayed(); + verify(mMockIAdServicesManager).setU18NotificationDisplayed(anyBoolean()); + } + + @Test + public void wasU18NotificationDisplayedTest_PpApiAndSystemServer() throws RemoteException { + int consentSourceOfTruth = Flags.PPAPI_AND_SYSTEM_SERVER; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + Boolean wasU18NotificationDisplayed = spyConsentManager.wasU18NotificationDisplayed(); + + assertThat(wasU18NotificationDisplayed).isFalse(); + + verify(mMockIAdServicesManager).wasU18NotificationDisplayed(); + + doReturn(true).when(mMockIAdServicesManager).wasU18NotificationDisplayed(); + spyConsentManager.setU18NotificationDisplayed(true); + + assertThat(spyConsentManager.wasU18NotificationDisplayed()).isTrue(); + + verify(mMockIAdServicesManager, times(2)).wasU18NotificationDisplayed(); + verify(mMockIAdServicesManager).setU18NotificationDisplayed(anyBoolean()); + } + + @Test + public void wasU18NotificationDisplayedTest_appSearchOnly() throws RemoteException { + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + when(mMockFlags.getEnableAppsearchConsentData()).thenReturn(true); + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + doReturn(false).when(mAppSearchConsentManagerMock).wasU18NotificationDisplayed(); + assertThat(spyConsentManager.wasU18NotificationDisplayed()).isFalse(); + verify(mAppSearchConsentManagerMock).wasU18NotificationDisplayed(); + + doReturn(true).when(mAppSearchConsentManagerMock).wasU18NotificationDisplayed(); + spyConsentManager.setU18NotificationDisplayed(true); + + assertThat(spyConsentManager.wasU18NotificationDisplayed()).isTrue(); + + verify(mAppSearchConsentManagerMock, times(2)).wasU18NotificationDisplayed(); + verify(mAppSearchConsentManagerMock).setU18NotificationDisplayed(anyBoolean()); + } + + @Test + public void testWasU18NotificationDisplayed_ppapiAndAdExtDataServiceOnly() + throws RemoteException, IOException { + int consentSourceOfTruth = Flags.PPAPI_AND_ADEXT_SERVICE; + when(mMockFlags.getEnableAdExtServiceConsentData()).thenReturn(true); + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + doReturn(false).when(mAppConsentForRStorageManager).wasU18NotificationDisplayed(); + assertThat(spyConsentManager.wasU18NotificationDisplayed()).isFalse(); + verify(mAppConsentForRStorageManager).wasU18NotificationDisplayed(); + + doReturn(true).when(mAppConsentForRStorageManager).wasU18NotificationDisplayed(); + spyConsentManager.setU18NotificationDisplayed(true); + + assertThat(spyConsentManager.wasU18NotificationDisplayed()).isTrue(); + + verify(mAppConsentForRStorageManager, times(2)).wasU18NotificationDisplayed(); + verify(mAppConsentForRStorageManager).setU18NotificationDisplayed(anyBoolean()); + } + + @Test + public void testIsRvcAdultUser_ageCheckFlagOn() throws RemoteException { + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + when(mMockFlags.getEnableAdExtServiceConsentData()).thenReturn(true); + when(mMockFlags.getRvcPostOtaNotifAgeCheck()).thenReturn(true); + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + doReturn(true).when(mAppConsentForRStorageManager).wasU18NotificationDisplayed(); + doReturn(false).when(mAppConsentForRStorageManager).isU18Account(); + doReturn(true).when(mAppConsentForRStorageManager).isAdultAccount(); + assertThat(spyConsentManager.isOtaAdultUserFromRvc()).isTrue(); + verify(mAppConsentForRStorageManager).wasU18NotificationDisplayed(); + verify(mAppConsentForRStorageManager).isAdultAccount(); + verify(mAppConsentForRStorageManager).isU18Account(); + } + + @Test + public void testIsRvcAdultUser_ageCheckFlagOff() throws RemoteException { + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + when(mMockFlags.getEnableAdExtServiceConsentData()).thenReturn(true); + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + doReturn(true).when(mAppConsentForRStorageManager).wasU18NotificationDisplayed(); + assertThat(spyConsentManager.isOtaAdultUserFromRvc()).isTrue(); + verify(mAppConsentForRStorageManager).wasU18NotificationDisplayed(); + } + + @Test + public void testGetUx_PpApiOnly() throws RemoteException { + getUxWithPpApiOnly(Flags.PPAPI_ONLY); + } + + @Test + public void testGetUx_ppapiAndAdExtDataServiceOnly() throws RemoteException { + when(mMockFlags.getEnableAdExtServiceConsentData()).thenReturn(true); + getUxWithPpApiOnly(Flags.PPAPI_AND_ADEXT_SERVICE); + } + + private void getUxWithPpApiOnly(int consentSourceOfTruth) throws RemoteException { + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + for (PrivacySandboxUxCollection ux : PrivacySandboxUxCollection.values()) { + doReturn(ux).when(mUxStatesDaoMock).getUx(); + assertThat(spyConsentManager.getUx()).isEqualTo(ux); + + spyConsentManager.setUx(ux); + } + + verify(mUxStatesDaoMock, times(5)).getUx(); + verify(mUxStatesDaoMock, times(5)).setUx(any()); + } + + @Test + public void getUxTest_SystemServerOnly() throws RemoteException { + int consentSourceOfTruth = Flags.SYSTEM_SERVER_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + for (PrivacySandboxUxCollection ux : PrivacySandboxUxCollection.values()) { + doReturn(ux.toString()).when(mMockIAdServicesManager).getUx(); + assertThat(spyConsentManager.getUx()).isEqualTo(ux); + + spyConsentManager.setUx(ux); + } + + verify(mMockIAdServicesManager, times(5)).getUx(); + verify(mMockIAdServicesManager, times(5)).setUx(any()); + } + + @Test + public void getUxTest_PpApiAndSystemServer() throws RemoteException { + int consentSourceOfTruth = Flags.PPAPI_AND_SYSTEM_SERVER; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + for (PrivacySandboxUxCollection ux : PrivacySandboxUxCollection.values()) { + doReturn(ux.toString()).when(mMockIAdServicesManager).getUx(); + assertThat(spyConsentManager.getUx()).isEqualTo(ux); + + spyConsentManager.setUx(ux); + } + + verify(mMockIAdServicesManager, times(5)).getUx(); + verify(mMockIAdServicesManager, times(5)).setUx(any()); + } + + @Test + public void getUxTest_appSearchOnly() throws RemoteException { + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + when(mMockFlags.getEnableAppsearchConsentData()).thenReturn(true); + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + for (PrivacySandboxUxCollection ux : PrivacySandboxUxCollection.values()) { + doReturn(ux).when(mAppSearchConsentManagerMock).getUx(); + assertThat(spyConsentManager.getUx()).isEqualTo(ux); + + spyConsentManager.setUx(ux); + } + + verify(mAppSearchConsentManagerMock, times(5)).getUx(); + verify(mAppSearchConsentManagerMock, times(5)).setUx(any()); + } + + @Test + public void testGetEnrollmentChannel_ppapiOnly() throws RemoteException, IOException { + getEnrollmentChannelWithPpApiOnly(Flags.PPAPI_ONLY); + } + + @Test + public void testGetEnrollmentChannel_ppapiAndAdExtDataServiceOnly() + throws RemoteException, IOException { + when(mMockFlags.getEnableAdExtServiceConsentData()).thenReturn(true); + getEnrollmentChannelWithPpApiOnly(Flags.PPAPI_AND_ADEXT_SERVICE); + } + + private void getEnrollmentChannelWithPpApiOnly(int consentSourceOfTruth) + throws RemoteException { + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + for (PrivacySandboxUxCollection ux : PrivacySandboxUxCollection.values()) { + for (PrivacySandboxEnrollmentChannelCollection channel : + ux.getEnrollmentChannelCollection()) { + doReturn(channel).when(mUxStatesDaoMock).getEnrollmentChannel(ux); + assertThat(spyConsentManager.getEnrollmentChannel(ux)).isEqualTo(channel); + + spyConsentManager.setEnrollmentChannel(ux, channel); + } + } + + verify(mUxStatesDaoMock, times(20)).getEnrollmentChannel(any()); + verify(mUxStatesDaoMock, times(20)).setEnrollmentChannel(any(), any()); + } + + @Test + public void getEnrollmentChannel_SystemServerOnly() throws RemoteException { + int consentSourceOfTruth = Flags.SYSTEM_SERVER_ONLY; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + for (PrivacySandboxUxCollection ux : PrivacySandboxUxCollection.values()) { + for (PrivacySandboxEnrollmentChannelCollection channel : + ux.getEnrollmentChannelCollection()) { + doReturn(channel.toString()).when(mMockIAdServicesManager).getEnrollmentChannel(); + assertThat(spyConsentManager.getEnrollmentChannel(ux)).isEqualTo(channel); + + spyConsentManager.setEnrollmentChannel(ux, channel); + } + } + + verify(mMockIAdServicesManager, times(20)).getEnrollmentChannel(); + verify(mMockIAdServicesManager, times(20)).setEnrollmentChannel(anyString()); + } + + @Test + public void getEnrollmentChannel_PpApiAndSystemServer() throws RemoteException { + int consentSourceOfTruth = Flags.PPAPI_AND_SYSTEM_SERVER; + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + for (PrivacySandboxUxCollection ux : PrivacySandboxUxCollection.values()) { + for (PrivacySandboxEnrollmentChannelCollection channel : + ux.getEnrollmentChannelCollection()) { + doReturn(channel.toString()).when(mMockIAdServicesManager).getEnrollmentChannel(); + assertThat(spyConsentManager.getEnrollmentChannel(ux)).isEqualTo(channel); + + spyConsentManager.setEnrollmentChannel(ux, channel); + } + } + + verify(mMockIAdServicesManager, times(20)).getEnrollmentChannel(); + verify(mMockIAdServicesManager, times(20)).setEnrollmentChannel(anyString()); + } + + @Test + public void getEnrollmentChannel_appSearchOnly() throws RemoteException { + int consentSourceOfTruth = Flags.APPSEARCH_ONLY; + when(mMockFlags.getEnableAppsearchConsentData()).thenReturn(true); + ConsentManagerV2 spyConsentManager = + getSpiedConsentManagerForMigrationTesting( + /* isGiven */ false, consentSourceOfTruth); + + for (PrivacySandboxUxCollection ux : PrivacySandboxUxCollection.values()) { + for (PrivacySandboxEnrollmentChannelCollection channel : + ux.getEnrollmentChannelCollection()) { + doReturn(channel).when(mAppSearchConsentManagerMock).getEnrollmentChannel(ux); + assertThat(spyConsentManager.getEnrollmentChannel(ux)).isEqualTo(channel); + + spyConsentManager.setEnrollmentChannel(ux, channel); + } + } + + verify(mAppSearchConsentManagerMock, times(20)).getEnrollmentChannel(any()); + verify(mAppSearchConsentManagerMock, times(20)).setEnrollmentChannel(any(), any()); + } +} diff --git a/adservices/tests/unittest/ui/src/com/android/adservices/service/ui/UxEngineTest.java b/adservices/tests/unittest/ui/src/com/android/adservices/service/ui/UxEngineTest.java index f52fe2d82..0cdbf6701 100644 --- a/adservices/tests/unittest/ui/src/com/android/adservices/service/ui/UxEngineTest.java +++ b/adservices/tests/unittest/ui/src/com/android/adservices/service/ui/UxEngineTest.java @@ -19,7 +19,7 @@ package com.android.adservices.service.ui; import static com.android.adservices.service.FlagsConstants.KEY_ADSERVICES_ENABLED; import static com.android.adservices.service.FlagsConstants.KEY_GA_UX_FEATURE_ENABLED; import static com.android.adservices.service.FlagsConstants.KEY_IS_U18_UX_DETENTION_CHANNEL_ENABLED; -import static com.android.adservices.service.FlagsConstants.KEY_RVC_NOTIFICATION_ENABLED; +import static com.android.adservices.service.FlagsConstants.KEY_RVC_POST_OTA_NOTIFICATION_ENABLED; import static com.android.adservices.service.FlagsConstants.KEY_RVC_UX_ENABLED; import static com.android.adservices.service.FlagsConstants.KEY_U18_UX_ENABLED; import static com.android.adservices.service.ui.ux.collection.PrivacySandboxUxCollection.BETA_UX; @@ -748,7 +748,7 @@ public class UxEngineTest { verify(mUxStatesManager).getFlag(KEY_RVC_UX_ENABLED); // RVC UX logic where flag is checked once in RvcPostOTAChannel. - verify(mUxStatesManager).getFlag(KEY_RVC_NOTIFICATION_ENABLED); + verify(mUxStatesManager).getFlag(KEY_RVC_POST_OTA_NOTIFICATION_ENABLED); // The UX can not be updated due to the fact that graduation channel is currently disabled. verify(mConsentManager, never()).setUx(any()); diff --git a/adservices/tests/unittest/ui/src/com/android/adservices/service/ui/enrollment/AlreadyEnrolledChannelTest.java b/adservices/tests/unittest/ui/src/com/android/adservices/service/ui/enrollment/AlreadyEnrolledChannelTest.java index ff676ef37..aafd575ad 100644 --- a/adservices/tests/unittest/ui/src/com/android/adservices/service/ui/enrollment/AlreadyEnrolledChannelTest.java +++ b/adservices/tests/unittest/ui/src/com/android/adservices/service/ui/enrollment/AlreadyEnrolledChannelTest.java @@ -151,15 +151,4 @@ public final class AlreadyEnrolledChannelTest extends AdServicesExtendedMockitoT mUxStatesManager)) .isFalse(); } - - // @Test - // public void enrollTest_enrollDoesNotTriggerNotification() { - // mAlreadyEnrolledChannel.enroll(appContext.get(), mConsentManager); - // verify( - // () -> - // ConsentNotificationJobService.schedule( - // any(Context.class), anyBoolean(), anyBoolean()), - // never()); - // } - } diff --git a/adservices/tests/unittest/ui/src/com/android/adservices/service/ui/enrollment/RvcPostOTAChannelTest.java b/adservices/tests/unittest/ui/src/com/android/adservices/service/ui/enrollment/RvcPostOTAChannelTest.java index d8a222b08..1d0b4f932 100644 --- a/adservices/tests/unittest/ui/src/com/android/adservices/service/ui/enrollment/RvcPostOTAChannelTest.java +++ b/adservices/tests/unittest/ui/src/com/android/adservices/service/ui/enrollment/RvcPostOTAChannelTest.java @@ -16,7 +16,7 @@ package com.android.adservices.service.ui.enrollment; -import static com.android.adservices.service.FlagsConstants.KEY_RVC_NOTIFICATION_ENABLED; +import static com.android.adservices.service.FlagsConstants.KEY_RVC_POST_OTA_NOTIFICATION_ENABLED; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; @@ -87,7 +87,7 @@ public class RvcPostOTAChannelTest { @Test public void isEligibleTest_isRvcAdultUser() { - doReturn(true).when(mUxStatesManager).getFlag(KEY_RVC_NOTIFICATION_ENABLED); + doReturn(true).when(mUxStatesManager).getFlag(KEY_RVC_POST_OTA_NOTIFICATION_ENABLED); doReturn(true).when(mConsentManager).isOtaAdultUserFromRvc(); assertThat( @@ -100,7 +100,7 @@ public class RvcPostOTAChannelTest { @Test public void isEligibleTest_notRvcAdultUser() { - doReturn(true).when(mUxStatesManager).getFlag(KEY_RVC_NOTIFICATION_ENABLED); + doReturn(true).when(mUxStatesManager).getFlag(KEY_RVC_POST_OTA_NOTIFICATION_ENABLED); doReturn(false).when(mConsentManager).isOtaAdultUserFromRvc(); assertThat( diff --git a/apex/Android.bp b/apex/Android.bp index 8305223e3..3036699db 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -115,6 +115,7 @@ bootclasspath_fragment { "android.adservices.exceptions", "android.adservices.extdata", "android.adservices.measurement", + "android.adservices.shell", "android.adservices.signals", "android.adservices.topics", "android.app.adservices", diff --git a/sdksandbox/service/java/com/android/server/sdksandbox/SdkSandboxManagerService.java b/sdksandbox/service/java/com/android/server/sdksandbox/SdkSandboxManagerService.java index 43e141987..6714cd178 100644 --- a/sdksandbox/service/java/com/android/server/sdksandbox/SdkSandboxManagerService.java +++ b/sdksandbox/service/java/com/android/server/sdksandbox/SdkSandboxManagerService.java @@ -217,20 +217,11 @@ public class SdkSandboxManagerService extends ISdkSandboxManager.Stub { private static final String WEBVIEW_SAFE_MODE_CONTENT_PROVIDER = "SafeModeContentProvider"; - // On UDC, AdServicesManagerService.Lifecycle implements dumpable so it's dumped as part of - // SystemServer. // If AdServices register itself as binder service, dump() will ignore the --AdServices option @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) static final String DUMP_AD_SERVICES_MESSAGE_HANDLED_BY_AD_SERVICES_ITSELF = "Don't need to dump AdServices as it's available as " + AD_SERVICES_SYSTEM_SERVICE; - // On UDC, if AdServices register itself as binder service, dump() will ignore the --AdServices - // option because AdServices could be dumped as part of SystemService - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) - static final String DUMP_AD_SERVICES_MESSAGE_HANDLED_BY_SYSTEM_SERVICE = - "Don't need to dump AdServices on UDC+ - use " - + "'dumpsys system_server_dumper --name AdServices instead'"; - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) static final ArraySet<String> DEFAULT_ACTIVITY_ALLOWED_ACTIONS = new ArraySet<>( @@ -1164,17 +1155,6 @@ public class SdkSandboxManagerService extends ISdkSandboxManager.Stub { } } - if (SdkLevel.isAtLeastU()) { - // AdServices didn't register itself as binder service, but - // AdServicesManagerService.Lifecycle implements Dumpable so it's dumped as - // part of SystemServer - if (quiet) { - Log.d(TAG, DUMP_AD_SERVICES_MESSAGE_HANDLED_BY_SYSTEM_SERVICE); - } else { - writer.println(DUMP_AD_SERVICES_MESSAGE_HANDLED_BY_SYSTEM_SERVICE); - } - return; - } writer.print("AdServices:"); IBinder adServicesManager = getAdServicesManager(); if (adServicesManager == null) { diff --git a/sdksandbox/tests/cts/endtoendtests/Android.bp b/sdksandbox/tests/cts/endtoendtests/Android.bp index 3684cbee7..c477725f9 100644 --- a/sdksandbox/tests/cts/endtoendtests/Android.bp +++ b/sdksandbox/tests/cts/endtoendtests/Android.bp @@ -48,6 +48,7 @@ android_test { "cts", "mts-adservices", "general-tests", + "mcts-adservices", ], } @@ -78,5 +79,6 @@ android_test { "cts", "mts-adservices", "general-tests", + "mcts-adservices", ], } diff --git a/sdksandbox/tests/cts/endtoendtests/AndroidTest.xml b/sdksandbox/tests/cts/endtoendtests/AndroidTest.xml index 04237b6c1..8d58b9c56 100644 --- a/sdksandbox/tests/cts/endtoendtests/AndroidTest.xml +++ b/sdksandbox/tests/cts/endtoendtests/AndroidTest.xml @@ -20,6 +20,24 @@ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app"/> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi"/> <option name="config-descriptor:metadata" key="parameter" value="secondary_user"/> + <option name="test-user-token" value="%TEST_USER%"/> + + <!-- IMPORTANT: This needs to come before SuiteApkInstaller + + SuiteApkInstaller is not able to uninstall the Sdk-Provider.apk because the test + app that requires it is still installed at that point. This means the apk would + stay on the test device after the test ends. + + This preparer, during tear down, runs after SuiteApkInstaller and will finally + uninstall the provider, leaving the device in a clean state. + + See b/314294893 for more details. + --> + <target_preparer + class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="teardown-command" + value="pm --user %TEST_USER% uninstall com.android.ctssdkprovider"/> + </target_preparer> <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true"/> @@ -41,7 +59,6 @@ <option name="teardown-command" value="cmd sdk_sandbox set-state --reset" /> </target_preparer> - <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController" > <option name="mainline-module-package-name" value="com.google.android.adservices" /> diff --git a/sdksandbox/tests/cts/endtoendtests/DisabledAndroidTest.xml b/sdksandbox/tests/cts/endtoendtests/DisabledAndroidTest.xml index 98f3e93e3..9c266f849 100644 --- a/sdksandbox/tests/cts/endtoendtests/DisabledAndroidTest.xml +++ b/sdksandbox/tests/cts/endtoendtests/DisabledAndroidTest.xml @@ -20,6 +20,26 @@ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app"/> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi"/> <option name="config-descriptor:metadata" key="parameter" value="secondary_user"/> + + <option name="test-user-token" value="%TEST_USER%"/> + + <!-- IMPORTANT: This needs to come before SuiteApkInstaller + + SuiteApkInstaller is not able to uninstall the Sdk-Provider.apk because the test + app that requires it is still installed at that point. This means the apk would + stay on the test device after the test ends. + + This preparer, during tear down, runs after SuiteApkInstaller and will finally + uninstall the provider, leaving the device in a clean state. + + See b/314294893 for more details. + --> + <target_preparer + class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="teardown-command" + value="pm --user %TEST_USER% uninstall com.android.ctssdkprovider"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true"/> <option name="test-file-name" value="SdkSandboxManagerCtsProvider.apk"/> @@ -37,7 +57,6 @@ <option name="teardown-command" value="cmd sdk_sandbox set-state --reset" /> </target_preparer> - <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController" > <option name="mainline-module-package-name" value="com.google.android.adservices" /> diff --git a/sdksandbox/tests/cts/endtoendtests/providers/MediationProvider/Android.bp b/sdksandbox/tests/cts/endtoendtests/providers/MediationProvider/Android.bp index 25723690b..869d57534 100644 --- a/sdksandbox/tests/cts/endtoendtests/providers/MediationProvider/Android.bp +++ b/sdksandbox/tests/cts/endtoendtests/providers/MediationProvider/Android.bp @@ -25,6 +25,7 @@ android_test_helper_app { ], static_libs: [ "CtsMediationTestSdkApi", + "CtsSdkProviderApi", "compatibility-device-util-axt", "SdkSandboxTestUtils", ], diff --git a/sdksandbox/tests/cts/endtoendtests/providers/MediationProvider/src/com/android/sdksandbox/cts/provider/mediationtest/IMediationTestSdkApi.aidl b/sdksandbox/tests/cts/endtoendtests/providers/MediationProvider/src/com/android/sdksandbox/cts/provider/mediationtest/IMediationTestSdkApi.aidl index 702f115f5..dd00bc0f2 100644 --- a/sdksandbox/tests/cts/endtoendtests/providers/MediationProvider/src/com/android/sdksandbox/cts/provider/mediationtest/IMediationTestSdkApi.aidl +++ b/sdksandbox/tests/cts/endtoendtests/providers/MediationProvider/src/com/android/sdksandbox/cts/provider/mediationtest/IMediationTestSdkApi.aidl @@ -21,4 +21,5 @@ interface IMediationTestSdkApi { List<AppOwnedSdkSandboxInterface> getAppOwnedSdkSandboxInterfaces(); List<SandboxedSdk> getSandboxedSdks(); void loadSdkBySdk(String sdkName); + void checkCanCallMediateeInterface(boolean inSandbox, String expectedValue); } diff --git a/sdksandbox/tests/cts/endtoendtests/providers/MediationProvider/src/com/android/sdksandbox/cts/provider/mediationtest/MediationTestSdkApiImpl.java b/sdksandbox/tests/cts/endtoendtests/providers/MediationProvider/src/com/android/sdksandbox/cts/provider/mediationtest/MediationTestSdkApiImpl.java index 68e1c1d34..e521e0282 100644 --- a/sdksandbox/tests/cts/endtoendtests/providers/MediationProvider/src/com/android/sdksandbox/cts/provider/mediationtest/MediationTestSdkApiImpl.java +++ b/sdksandbox/tests/cts/endtoendtests/providers/MediationProvider/src/com/android/sdksandbox/cts/provider/mediationtest/MediationTestSdkApiImpl.java @@ -23,6 +23,10 @@ import android.app.sdksandbox.sdkprovider.SdkSandboxController; import android.app.sdksandbox.testutils.FakeLoadSdkCallback; import android.content.Context; import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; + +import com.android.ctssdkprovider.ICtsSdkProviderApi; import java.util.List; @@ -60,4 +64,48 @@ public class MediationTestSdkApiImpl extends IMediationTestSdkApi.Stub { throw new IllegalStateException(e); } } + + /** + * Checks that the mediator can invoke an interface defined by a mediatee, either inside or + * outside the sandbox. + */ + @Override + public void checkCanCallMediateeInterface(boolean inSandbox, String expectedValue) { + ICtsSdkProviderApi sdk = getMediateeInterface(inSandbox); + String clientPackageName; + try { + clientPackageName = sdk.getClientPackageName(); + } catch (RemoteException e) { + throw new IllegalStateException("Cannot invoke interface: " + e.getMessage()); + } + if (!clientPackageName.equals(expectedValue)) { + throw new IllegalStateException( + "String " + + clientPackageName + + " did not match expected value " + + expectedValue); + } + } + + private ICtsSdkProviderApi getMediateeInterface(boolean inSandbox) { + if (inSandbox) { + List<SandboxedSdk> sandboxedSdks = getSandboxedSdks(); + for (SandboxedSdk sandboxedSdk : sandboxedSdks) { + IBinder iBinder = sandboxedSdk.getInterface(); + if (iBinder.queryLocalInterface(ICtsSdkProviderApi.DESCRIPTOR) != null) { + return ICtsSdkProviderApi.Stub.asInterface(iBinder); + } + } + } else { + List<AppOwnedSdkSandboxInterface> appOwnedInterfaces = + getAppOwnedSdkSandboxInterfaces(); + for (AppOwnedSdkSandboxInterface appOwnedInterface : appOwnedInterfaces) { + IBinder iBinder = appOwnedInterface.getInterface(); + if (iBinder.queryLocalInterface(ICtsSdkProviderApi.DESCRIPTOR) != null) { + return ICtsSdkProviderApi.Stub.asInterface(iBinder); + } + } + } + throw new IllegalStateException("No mediatee interface found."); + } } diff --git a/sdksandbox/tests/cts/endtoendtests/src/com/android/tests/sdksandbox/endtoend/SdkSandboxMediationTest.java b/sdksandbox/tests/cts/endtoendtests/src/com/android/tests/sdksandbox/endtoend/SdkSandboxMediationTest.java index e1678fd80..b3a6f18bf 100644 --- a/sdksandbox/tests/cts/endtoendtests/src/com/android/tests/sdksandbox/endtoend/SdkSandboxMediationTest.java +++ b/sdksandbox/tests/cts/endtoendtests/src/com/android/tests/sdksandbox/endtoend/SdkSandboxMediationTest.java @@ -34,6 +34,7 @@ import android.os.RemoteException; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.rules.ActivityScenarioRule; +import com.android.ctssdkprovider.ICtsSdkProviderApi; import com.android.sdksandbox.cts.provider.mediationtest.IMediationTestSdkApi; import org.junit.After; @@ -69,7 +70,6 @@ public class SdkSandboxMediationTest extends SandboxKillerBeforeTest { private Context mContext; private SdkSandboxManager mSdkSandboxManager; - private IMediationTestSdkApi mSdk; @Before public void setup() { @@ -102,7 +102,7 @@ public class SdkSandboxMediationTest extends SandboxKillerBeforeTest { @Test public void testGetAppOwnedSdkSandboxInterfaces() throws Exception { - loadMediatorSdkAndPopulateInterface(); + IMediationTestSdkApi mediatorSdk = loadMediatorSdk(); IBinder iBinder = new Binder(); mSdkSandboxManager.registerAppOwnedSdkSandboxInterface( new AppOwnedSdkSandboxInterface( @@ -111,7 +111,7 @@ public class SdkSandboxMediationTest extends SandboxKillerBeforeTest { /*interfaceIBinder=*/ iBinder)); final List<AppOwnedSdkSandboxInterface> appOwnedSdkSandboxInterfaceList = - mSdk.getAppOwnedSdkSandboxInterfaces(); + mediatorSdk.getAppOwnedSdkSandboxInterfaces(); assertThat(appOwnedSdkSandboxInterfaceList).hasSize(1); @@ -123,13 +123,13 @@ public class SdkSandboxMediationTest extends SandboxKillerBeforeTest { @Test public void testGetAppOwnedSdkSandboxInterfaces_NoInterface() throws Exception { - loadMediatorSdkAndPopulateInterface(); - assertThat(mSdk.getAppOwnedSdkSandboxInterfaces()).hasSize(0); + IMediationTestSdkApi mediatorSdk = loadMediatorSdk(); + assertThat(mediatorSdk.getAppOwnedSdkSandboxInterfaces()).hasSize(0); } @Test public void testGetAppOwnedSdkSandboxInterfaces_MultipleInterfaces() throws Exception { - loadMediatorSdkAndPopulateInterface(); + IMediationTestSdkApi mediatorSdk = loadMediatorSdk(); IBinder iBinder = new Binder(); mSdkSandboxManager.registerAppOwnedSdkSandboxInterface( new AppOwnedSdkSandboxInterface( @@ -144,7 +144,7 @@ public class SdkSandboxMediationTest extends SandboxKillerBeforeTest { /*version=*/ 1, /*interfaceIBinder=*/ iBinder2)); final List<AppOwnedSdkSandboxInterface> appOwnedSdkSandboxInterfaceList = - mSdk.getAppOwnedSdkSandboxInterfaces(); + mediatorSdk.getAppOwnedSdkSandboxInterfaces(); assertThat(appOwnedSdkSandboxInterfaceList).hasSize(2); @@ -169,9 +169,9 @@ public class SdkSandboxMediationTest extends SandboxKillerBeforeTest { @Test public void testGetSandboxedSdk_GetsAllSdksLoadedInTheSandbox() throws Exception { - loadMediatorSdkAndPopulateInterface(); + IMediationTestSdkApi mediatorSdk = loadMediatorSdk(); - final List<SandboxedSdk> sandboxedSdks = mSdk.getSandboxedSdks(); + final List<SandboxedSdk> sandboxedSdks = mediatorSdk.getSandboxedSdks(); assertThat(sandboxedSdks).hasSize(1); assertThat(sandboxedSdks.get(0).getInterface().getInterfaceDescriptor()) .isEqualTo( @@ -181,10 +181,10 @@ public class SdkSandboxMediationTest extends SandboxKillerBeforeTest { @Test public void testGetSandboxedSdk_MultipleSdks() throws Exception { - loadMediatorSdkAndPopulateInterface(); + IMediationTestSdkApi mediatorSdk = loadMediatorSdk(); loadMediateeSdk(); - final List<SandboxedSdk> sandboxedSdks = mSdk.getSandboxedSdks(); + final List<SandboxedSdk> sandboxedSdks = mediatorSdk.getSandboxedSdks(); assertThat(sandboxedSdks).hasSize(2); Set<String> interfaceDescriptors = sandboxedSdks.stream() @@ -207,10 +207,10 @@ public class SdkSandboxMediationTest extends SandboxKillerBeforeTest { @Test public void testLoadSdkByOtherSdk() throws Exception { - loadMediatorSdkAndPopulateInterface(); - mSdk.loadSdkBySdk(MEDIATEE_SDK_NAME); + IMediationTestSdkApi mediatorSdk = loadMediatorSdk(); + mediatorSdk.loadSdkBySdk(MEDIATEE_SDK_NAME); - final List<SandboxedSdk> sandboxedSdks = mSdk.getSandboxedSdks(); + final List<SandboxedSdk> sandboxedSdks = mediatorSdk.getSandboxedSdks(); assertThat(sandboxedSdks).hasSize(2); Set<String> loadedSdks = sandboxedSdks.stream() @@ -225,10 +225,11 @@ public class SdkSandboxMediationTest extends SandboxKillerBeforeTest { @Test public void testLoadSdkBySameSdk() throws Exception { - loadMediatorSdkAndPopulateInterface(); + IMediationTestSdkApi mediatorSdk = loadMediatorSdk(); IllegalStateException exception = assertThrows( - IllegalStateException.class, () -> mSdk.loadSdkBySdk(MEDIATOR_SDK_NAME)); + IllegalStateException.class, + () -> mediatorSdk.loadSdkBySdk(MEDIATOR_SDK_NAME)); assertThat(exception.getMessage()) .isEqualTo( "java.lang.AssertionError: Load SDK was not successful. errorCode: 101," @@ -239,10 +240,12 @@ public class SdkSandboxMediationTest extends SandboxKillerBeforeTest { @Test public void testLoadSdkThatDoesNotExist() throws Exception { - loadMediatorSdkAndPopulateInterface(); + IMediationTestSdkApi mediatorSdk = loadMediatorSdk(); final String nonExistingSdk = "non-existing-sdk"; IllegalStateException exception = - assertThrows(IllegalStateException.class, () -> mSdk.loadSdkBySdk(nonExistingSdk)); + assertThrows( + IllegalStateException.class, + () -> mediatorSdk.loadSdkBySdk(nonExistingSdk)); assertThat(exception.getMessage()) .isEqualTo( "java.lang.AssertionError: " @@ -253,18 +256,36 @@ public class SdkSandboxMediationTest extends SandboxKillerBeforeTest { + " not found for loading"); } - private void loadMediatorSdkAndPopulateInterface() { + @Test + public void testSandboxedSdkCanCallAppSdk() throws Exception { + IMediationTestSdkApi mediatorSdk = loadMediatorSdk(); + // we load the mediatee SDK to get a handle to its IBinder, which will be registered as an + // app owned interface. This is done to avoid re-implementing an ICtsSdkProviderApi. + ICtsSdkProviderApi mediateeSdk = loadMediateeSdk(); + mSdkSandboxManager.registerAppOwnedSdkSandboxInterface( + new AppOwnedSdkSandboxInterface("mediatee", 1, mediateeSdk.asBinder())); + mediatorSdk.checkCanCallMediateeInterface(/*inSandbox=*/ false, mContext.getPackageName()); + } + + @Test + public void testSandboxedSdkCanCallSandboxedSdk() throws Exception { + IMediationTestSdkApi mediatorSdk = loadMediatorSdk(); + loadMediateeSdk(); + mediatorSdk.checkCanCallMediateeInterface(/*inSandbox=*/ true, mContext.getPackageName()); + } + + private IMediationTestSdkApi loadMediatorSdk() { FakeLoadSdkCallback callback = new FakeLoadSdkCallback(); mSdkSandboxManager.loadSdk(MEDIATOR_SDK_NAME, new Bundle(), Runnable::run, callback); callback.assertLoadSdkIsSuccessful(); - // Store the returned SDK interface so that we can interact with it later. - mSdk = IMediationTestSdkApi.Stub.asInterface(callback.getSandboxedSdk().getInterface()); + return IMediationTestSdkApi.Stub.asInterface(callback.getSandboxedSdk().getInterface()); } - private void loadMediateeSdk() { + private ICtsSdkProviderApi loadMediateeSdk() { FakeLoadSdkCallback callback = new FakeLoadSdkCallback(); mSdkSandboxManager.loadSdk(MEDIATEE_SDK_NAME, new Bundle(), Runnable::run, callback); callback.assertLoadSdkIsSuccessful(); + return ICtsSdkProviderApi.Stub.asInterface(callback.getSandboxedSdk().getInterface()); } } diff --git a/sdksandbox/tests/cts/hostside/Android.bp b/sdksandbox/tests/cts/hostside/Android.bp index 39ad735a1..d48ec7bec 100644 --- a/sdksandbox/tests/cts/hostside/Android.bp +++ b/sdksandbox/tests/cts/hostside/Android.bp @@ -31,6 +31,7 @@ java_test_host { "cts", "mts-adservices", "general-tests", + "mcts-adservices", ], data: [ ":CtsSdkSandboxHostTestApp", diff --git a/sdksandbox/tests/cts/inprocess/Android.bp b/sdksandbox/tests/cts/inprocess/Android.bp index 9a367342e..94090c2c3 100644 --- a/sdksandbox/tests/cts/inprocess/Android.bp +++ b/sdksandbox/tests/cts/inprocess/Android.bp @@ -43,9 +43,10 @@ android_test { test_suites: [ "cts", "mts-adservices", - "general-tests" + "general-tests", + "mcts-adservices", ], test_options: { extra_test_configs: ["AndroidTest_MultiUser.xml"] }, -} +}
\ No newline at end of file diff --git a/sdksandbox/tests/cts/inprocess/src/com/android/sdksandbox/tests/cts/inprocess/SdkSandboxConfigurationTest.java b/sdksandbox/tests/cts/inprocess/src/com/android/sdksandbox/tests/cts/inprocess/SdkSandboxConfigurationTest.java index 4a356ca0d..618a1eeb8 100644 --- a/sdksandbox/tests/cts/inprocess/src/com/android/sdksandbox/tests/cts/inprocess/SdkSandboxConfigurationTest.java +++ b/sdksandbox/tests/cts/inprocess/src/com/android/sdksandbox/tests/cts/inprocess/SdkSandboxConfigurationTest.java @@ -36,6 +36,9 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.PackageInfoFlags; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; +import android.hardware.Sensor; +import android.hardware.SensorManager; +import android.media.AudioManager; import android.os.IBinder; import android.os.Process; import android.os.SELinux; @@ -68,6 +71,9 @@ public class SdkSandboxConfigurationTest { private static final String TEST_PKG = "com.android.sdksandbox.tests.cts.inprocesstests"; private static final String CURRENT_USER_ID = String.valueOf(Process.myUserHandle().getUserId(Process.myUid())); + private final Context mContext = + InstrumentationRegistry.getInstrumentation().getTargetContext(); + private final PackageManager mPackageManager = mContext.getPackageManager(); @Before public void setUp() { @@ -76,11 +82,9 @@ public class SdkSandboxConfigurationTest { InstrumentationRegistry.getInstrumentation().getContext())); } - /** - * Tests that uid belongs to the sdk sandbox processes uid range. - */ + /** Tests that uid belongs to the sdk sandbox processes uid range. */ @Test - public void testUidBelongsToSdkSandboxRange() throws Exception { + public void testUidBelongsToSdkSandboxRange() { int myUid = Process.myUid(); assertWithMessage(myUid + " is not a SdkSandbox uid").that(Process.isSdkSandbox()).isTrue(); } @@ -89,17 +93,17 @@ public class SdkSandboxConfigurationTest { * Tests that sdk sandbox processes are running under the {@code sdk_sandbox} selinux domain. */ @Test - public void testCorrectSelinuxDomain() throws Exception { - final String ctx = SELinux.getContext(); - assertThat(ctx).contains("u:r:sdk_sandbox"); + public void testCorrectSelinuxDomain() { + final String selinuxContext = SELinux.getContext(); + assertThat(selinuxContext).contains("u:r:sdk_sandbox"); } /** Tests that sdk sandbox SDK minimum and target versions are correct. */ @Test public void testCorrectSdkVersion() throws Exception { - final Context ctx = InstrumentationRegistry.getInstrumentation().getTargetContext(); - final PackageManager pm = ctx.getPackageManager(); - final PackageInfo info = pm.getPackageInfo(ctx.getPackageName(), PackageInfoFlags.of(0)); + final PackageManager pm = mContext.getPackageManager(); + final PackageInfo info = + pm.getPackageInfo(mContext.getPackageName(), PackageInfoFlags.of(0)); int minSdkVersion = info.applicationInfo.minSdkVersion; assertThat(minSdkVersion).isEqualTo(33); @@ -113,21 +117,18 @@ public class SdkSandboxConfigurationTest { */ @Test public void testClientAppIsVisibleToSdkSandbox() throws Exception { - final Context ctx = InstrumentationRegistry.getInstrumentation().getTargetContext(); - final PackageManager pm = ctx.getPackageManager(); - final PackageInfo info = pm.getPackageInfo(TEST_PKG, PackageInfoFlags.of(0)); + final PackageInfo info = mPackageManager.getPackageInfo(TEST_PKG, PackageInfoFlags.of(0)); assertThat(info.applicationInfo.uid).isEqualTo( Process.getAppUidForSdkSandboxUid(Process.myUid())); } /** - * Tests that {@link Context#getDataDir()} returns correct value for the CE storage of the - * sak sandbox. + * Tests that {@link Context#getDataDir()} returns correct value for the CE storage of the sak + * sandbox. */ @Test - public void testGetDataDir_CE() throws Exception { - final Context ctx = InstrumentationRegistry.getInstrumentation().getTargetContext(); - final File dir = ctx.getDataDir(); + public void testGetDataDir_CE() { + final File dir = mContext.getDataDir(); assertThat(dir.getAbsolutePath()) .isEqualTo( "/data/misc_ce/" + CURRENT_USER_ID + "/sdksandbox/" + TEST_PKG + "/shared"); @@ -139,10 +140,7 @@ public class SdkSandboxConfigurationTest { */ @Test public void testGetDataDir_DE() throws Exception { - final Context ctx = - InstrumentationRegistry.getInstrumentation() - .getTargetContext() - .createDeviceProtectedStorageContext(); + final Context ctx = mContext.createDeviceProtectedStorageContext(); final File dir = ctx.getDataDir(); assertThat(dir.getAbsolutePath()) .isEqualTo( @@ -152,13 +150,13 @@ public class SdkSandboxConfigurationTest { /** Tests that sdk sandbox process can write to it's CE storage. */ @Test public void testCanWriteToDataDir_CE() throws Exception { - final Context ctx = InstrumentationRegistry.getInstrumentation().getTargetContext(); - try (OutputStreamWriter writer = new OutputStreamWriter( - ctx.openFileOutput("random_ce_file", MODE_PRIVATE))) { + try (OutputStreamWriter writer = + new OutputStreamWriter(mContext.openFileOutput("random_ce_file", MODE_PRIVATE))) { writer.write("I am an sdk sandbox"); } - try (BufferedReader reader = new BufferedReader( - new InputStreamReader(ctx.openFileInput("random_ce_file")))) { + try (BufferedReader reader = + new BufferedReader( + new InputStreamReader(mContext.openFileInput("random_ce_file")))) { String line = reader.readLine(); assertThat(line).isEqualTo("I am an sdk sandbox"); } @@ -167,10 +165,7 @@ public class SdkSandboxConfigurationTest { /** Tests that sdk sandbox process can write to it's DE storage. */ @Test public void testCanWriteToDataDir_DE() throws Exception { - final Context ctx = - InstrumentationRegistry.getInstrumentation() - .getTargetContext() - .createDeviceProtectedStorageContext(); + final Context ctx = mContext.createDeviceProtectedStorageContext(); try (OutputStreamWriter writer = new OutputStreamWriter( ctx.openFileOutput("random_de_file", MODE_PRIVATE))) { writer.write("I am also an sdk sandbox"); @@ -190,13 +185,11 @@ public class SdkSandboxConfigurationTest { assumeThat( SdkSandboxManager.getSdkSandboxState(), equalTo(SdkSandboxManager.SDK_SANDBOX_STATE_ENABLED_PROCESS_ISOLATION)); - final Context ctx = InstrumentationRegistry.getInstrumentation().getTargetContext(); // First check that we can resolve the adservices apk - final PackageManager pm = ctx.getPackageManager(); final Intent resolveIntent = new Intent(AdServicesCommon.ACTION_TOPICS_SERVICE); final List<ResolveInfo> services = - pm.queryIntentServices( + mPackageManager.queryIntentServices( resolveIntent, PackageManager.ResolveInfoFlags.of( PackageManager.GET_SERVICES @@ -221,13 +214,13 @@ public class SdkSandboxConfigurationTest { @Override public void onServiceDisconnected(ComponentName name) {} }; - final boolean ret = ctx.bindService(serviceIntent, conn, Context.BIND_AUTO_CREATE); + final boolean ret = mContext.bindService(serviceIntent, conn, Context.BIND_AUTO_CREATE); try { assertThat(ret).isTrue(); assertThat(latch.await(3, TimeUnit.SECONDS)).isTrue(); } finally { - ctx.unbindService(conn); + mContext.unbindService(conn); } } @@ -244,9 +237,30 @@ public class SdkSandboxConfigurationTest { // Now time to query the current WebView provider through PackageManager, this is used to // check if this sdk sandbox process can see the WebView. - final Context ctx = InstrumentationRegistry.getInstrumentation().getTargetContext(); final PackageInfo webViewProviderInfo = - ctx.getPackageManager().getPackageInfo(info.packageName, PackageInfoFlags.of(0)); + mPackageManager.getPackageInfo(info.packageName, PackageInfoFlags.of(0)); assertThat(webViewProviderInfo).isNotNull(); } + + @Test + public void testCanAccessGyroscope() { + assumeTrue(mPackageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_GYROSCOPE)); + + SensorManager sensorManager = mContext.getSystemService(SensorManager.class); + assertThat(sensorManager).isNotNull(); + Sensor gyroscope = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE); + assertThat(gyroscope).isNotNull(); + } + + @Test + public void testCanAccessVolume() { + assumeTrue(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)); + + AudioManager audioManager = mContext.getSystemService(AudioManager.class); + int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_SYSTEM); + int minVolume = audioManager.getStreamMinVolume(AudioManager.STREAM_SYSTEM); + int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_SYSTEM); + assertThat(currentVolume).isAtMost(maxVolume); + assertThat(currentVolume).isAtLeast(minVolume); + } } diff --git a/sdksandbox/tests/hostsidetests/BroadcastRestrictionsHostTest/app/src/com/android/tests/sdksandbox/BroadcastRestrictionsTestApp.java b/sdksandbox/tests/hostsidetests/BroadcastRestrictionsHostTest/app/src/com/android/tests/sdksandbox/BroadcastRestrictionsTestApp.java index 61a59810b..9b16bf2e3 100644 --- a/sdksandbox/tests/hostsidetests/BroadcastRestrictionsHostTest/app/src/com/android/tests/sdksandbox/BroadcastRestrictionsTestApp.java +++ b/sdksandbox/tests/hostsidetests/BroadcastRestrictionsHostTest/app/src/com/android/tests/sdksandbox/BroadcastRestrictionsTestApp.java @@ -27,11 +27,14 @@ import android.app.sdksandbox.testutils.ConfigListener; import android.app.sdksandbox.testutils.DeviceConfigUtils; import android.app.sdksandbox.testutils.EmptyActivity; import android.app.sdksandbox.testutils.FakeLoadSdkCallback; +import android.app.sdksandbox.testutils.ProtoUtil; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.IBinder; import android.provider.DeviceConfig; +import android.util.ArrayMap; +import android.util.ArraySet; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.rules.ActivityScenarioRule; @@ -211,9 +214,10 @@ public class BroadcastRestrictionsTestApp { public void testRegisterBroadcastReceiver_DeviceConfigEmptyAllowlistApplied() throws Exception { mDeviceConfigUtils.setProperty(PROPERTY_ENFORCE_RESTRICTIONS, "true"); - // Set an empty allowlist for effectiveTargetSdkVersion U. This should block all - // BroadcastReceivers. - mDeviceConfigUtils.setProperty(PROPERTY_BROADCASTRECEIVER_ALLOWLIST, "CgQIIhIA"); + ArrayMap<Integer, List<String>> allowedIntentActions = new ArrayMap<>(); + allowedIntentActions.put(34, new ArrayList<>()); + String encodedAllowlist = ProtoUtil.encodeBroadcastReceiverAllowlist(allowedIntentActions); + mDeviceConfigUtils.setProperty(PROPERTY_BROADCASTRECEIVER_ALLOWLIST, encodedAllowlist); loadSdk(); assertThrows( @@ -232,12 +236,10 @@ public class BroadcastRestrictionsTestApp { public void testRegisterBroadcastReceiver_DeviceConfigAllowlistApplied() throws Exception { mDeviceConfigUtils.setProperty(PROPERTY_ENFORCE_RESTRICTIONS, "true"); - // Set an allowlist mapping from U to {android.intent.action.VIEW, - // android.intent.action.SCREEN_OFF} - final String encodedAllowlist = - "CkIIIhI+ChphbmRyb2lkLmludGVudC5hY3Rpb24uVklFVwogYW5kcm9pZC5pbnRlbnQuYWN0aW9uLlNDUk" - + "VFTl9PRkY="; - + ArrayMap<Integer, List<String>> allowedIntentActions = new ArrayMap<>(); + allowedIntentActions.put( + 34, new ArrayList<>(Arrays.asList(Intent.ACTION_VIEW, Intent.ACTION_SCREEN_OFF))); + String encodedAllowlist = ProtoUtil.encodeBroadcastReceiverAllowlist(allowedIntentActions); mDeviceConfigUtils.setProperty(PROPERTY_BROADCASTRECEIVER_ALLOWLIST, encodedAllowlist); loadSdk(); @@ -268,13 +270,10 @@ public class BroadcastRestrictionsTestApp { mDeviceConfigUtils.setProperty(PROPERTY_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS, "true"); - // Base64 encoded proto AllowedBroadcastReceivers containing the strings Intent.ACTION_VIEW - // and Intent.ACTION_SEND. - String encodedNextAllowlist = - "ChphbmRyb2lkLmludGVudC5hY3Rpb24uVklFVwoaYW5kcm9pZC5pbnRlbnQuYWN0aW9uLlNFTkQ="; - // Set the canary set. - mDeviceConfigUtils.setProperty( - PROPERTY_NEXT_BROADCASTRECEIVER_ALLOWLIST, encodedNextAllowlist); + ArraySet<String> allowedIntentActions = + new ArraySet<>(Arrays.asList(Intent.ACTION_VIEW, Intent.ACTION_SEND)); + String encodedAllowlist = ProtoUtil.encodeBroadcastReceiverAllowlist(allowedIntentActions); + mDeviceConfigUtils.setProperty(PROPERTY_NEXT_BROADCASTRECEIVER_ALLOWLIST, encodedAllowlist); loadSdk(); // No exception should be thrown when registering a BroadcastReceiver with diff --git a/sdksandbox/tests/hostsidetests/ContentProviderRestrictionsHostTest/app/src/com/android/tests/sdksandbox/ContentProviderRestrictionsTestApp.java b/sdksandbox/tests/hostsidetests/ContentProviderRestrictionsHostTest/app/src/com/android/tests/sdksandbox/ContentProviderRestrictionsTestApp.java index 1f142edce..95528a90e 100644 --- a/sdksandbox/tests/hostsidetests/ContentProviderRestrictionsHostTest/app/src/com/android/tests/sdksandbox/ContentProviderRestrictionsTestApp.java +++ b/sdksandbox/tests/hostsidetests/ContentProviderRestrictionsHostTest/app/src/com/android/tests/sdksandbox/ContentProviderRestrictionsTestApp.java @@ -27,10 +27,12 @@ import android.app.sdksandbox.testutils.ConfigListener; import android.app.sdksandbox.testutils.DeviceConfigUtils; import android.app.sdksandbox.testutils.EmptyActivity; import android.app.sdksandbox.testutils.FakeLoadSdkCallback; +import android.app.sdksandbox.testutils.ProtoUtil; import android.content.Context; import android.os.Bundle; import android.os.IBinder; import android.provider.DeviceConfig; +import android.util.ArrayMap; import android.util.ArraySet; import android.webkit.WebViewUpdateService; @@ -47,7 +49,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; @RunWith(JUnit4.class) public class ContentProviderRestrictionsTestApp { @@ -169,17 +173,16 @@ public class ContentProviderRestrictionsTestApp { public void testGetContentProvider_DeviceConfigAllowlistApplied() throws Exception { mDeviceConfigUtils.setProperty(ENFORCE_RESTRICTIONS, "true"); - /** - * Base64 encoded proto ContentProviderAllowlists containing allowlist_per_target_sdk { key: - * 34 value { authorities: "com.android.textclassifier.icons" authorities: "user_dictionary" - * } } - * - * <p>allowlist_per_target_sdk { key: 35 value { authorities: - * "com.android.textclassifier.icons" authorities: "user_dictionary" } } - */ - String encodedAllowlist = - "CjcIIhIzCiBjb20uYW5kcm9pZC50ZXh0Y2xhc3NpZmllci5pY29ucwoPdXNlcl9kaWN0aW9uYXJ5CjcII" - + "xIzCiBjb20uYW5kcm9pZC50ZXh0Y2xhc3NpZmllci5pY29ucwoPdXNlcl9kaWN0aW9uYXJ5"; + ArrayMap<Integer, List<String>> allowedAuthorities = new ArrayMap<>(); + allowedAuthorities.put( + 34, + new ArrayList<>( + Arrays.asList("com.android.textclassifier.icons", "user_dictionary"))); + allowedAuthorities.put( + 35, + new ArrayList<>( + Arrays.asList("com.android.textclassifier.icons", "user_dictionary"))); + String encodedAllowlist = ProtoUtil.encodeContentProviderAllowlist(allowedAuthorities); mDeviceConfigUtils.setProperty(PROPERTY_CONTENTPROVIDER_ALLOWLIST, encodedAllowlist); loadSdk(); @@ -198,19 +201,19 @@ public class ContentProviderRestrictionsTestApp { mDeviceConfigUtils.setProperty(ENFORCE_RESTRICTIONS, "true"); mDeviceConfigUtils.setProperty(PROPERTY_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS, "true"); - // Base64 encoded proto AllowedContentProviders containing the string - // 'com.android.textclassifier.icons' - String encodedNextAllowlist = "CiBjb20uYW5kcm9pZC50ZXh0Y2xhc3NpZmllci5pY29ucw=="; - // Set the canary set. + ArraySet<String> nextAllowedAuthorities = + new ArraySet<>(Arrays.asList("com.android.textclassifier.icons")); + String nextEncodedAllowlist = + ProtoUtil.encodeContentProviderAllowlist(nextAllowedAuthorities); mDeviceConfigUtils.setProperty( - PROPERTY_NEXT_CONTENTPROVIDER_ALLOWLIST, encodedNextAllowlist); - - // Base64 encoded proto ContentProviderAllowlists containing mappings to the string - // 'com.android.textclassifier.icons' and 'user_dictionary'. - String encodedAllowlist = - "CjcIIhIzCiBjb20uYW5kcm9pZC50ZXh0Y2xhc3NpZmllci5pY29ucwoPdXNlcl9kaWN0aW9uYXJ5"; - // Also set the non-canary allowlist to verify that this allowlist is not applied when the - // canary flag is set. + PROPERTY_NEXT_CONTENTPROVIDER_ALLOWLIST, nextEncodedAllowlist); + + ArrayMap<Integer, List<String>> allowedAuthorities = new ArrayMap<>(); + allowedAuthorities.put( + 34, + new ArrayList<>( + Arrays.asList("com.android.textclassifier.icons", "user_dictionary"))); + String encodedAllowlist = ProtoUtil.encodeContentProviderAllowlist(allowedAuthorities); mDeviceConfigUtils.setProperty(PROPERTY_CONTENTPROVIDER_ALLOWLIST, encodedAllowlist); loadSdk(); @@ -227,16 +230,9 @@ public class ContentProviderRestrictionsTestApp { public void testGetContentProvider_DeviceConfigWildcardAllowlistApplied() throws Exception { mDeviceConfigUtils.setProperty(ENFORCE_RESTRICTIONS, "true"); - /* - * Base64 encoded proto ContentProviderAllowlists in the following form: - * allowlist_per_target_sdk { - * key: 34 - * value { - * authorities: "*" - * } - * } - */ - String encodedAllowlist = "CgcIIhIDCgEq"; + ArrayMap<Integer, List<String>> allowedAuthorities = new ArrayMap<>(); + allowedAuthorities.put(34, new ArrayList<>(Arrays.asList("*"))); + String encodedAllowlist = ProtoUtil.encodeContentProviderAllowlist(allowedAuthorities); mDeviceConfigUtils.setProperty(PROPERTY_CONTENTPROVIDER_ALLOWLIST, encodedAllowlist); loadSdk(); @@ -251,16 +247,9 @@ public class ContentProviderRestrictionsTestApp { public void testGetContentProvider_DeviceConfigAllowlistWithWildcardApplied() throws Exception { mDeviceConfigUtils.setProperty(ENFORCE_RESTRICTIONS, "true"); - /* - * Base64 encoded proto ContentProviderAllowlists in the following form: - * allowlist_per_target_sdk { - * key: 34 - * value { - * authorities: "com.android.contacts.*" - * } - * } - */ - String encodedAllowlist = "ChwIIhIYChZjb20uYW5kcm9pZC5jb250YWN0cy4q"; + ArrayMap<Integer, List<String>> allowedAuthorities = new ArrayMap<>(); + allowedAuthorities.put(34, new ArrayList<>(Arrays.asList("com.android.contacts.*"))); + String encodedAllowlist = ProtoUtil.encodeContentProviderAllowlist(allowedAuthorities); mDeviceConfigUtils.setProperty(PROPERTY_CONTENTPROVIDER_ALLOWLIST, encodedAllowlist); loadSdk(); mContentProvidersSdkApi.getContentProviderByAuthority( @@ -335,16 +324,9 @@ public class ContentProviderRestrictionsTestApp { throws Exception { mDeviceConfigUtils.setProperty(PROPERTY_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS, "true"); - /* - * Base64 encoded proto ContentProviderAllowlists in the following form: - * allowlist_per_target_sdk { - * key: 34 - * value { - * authorities: "com.android.contacts.*" - * } - * } - */ - String encodedAllowlist = "ChwIIhIYChZjb20uYW5kcm9pZC5jb250YWN0cy4q"; + ArrayMap<Integer, List<String>> allowedAuthorities = new ArrayMap<>(); + allowedAuthorities.put(34, new ArrayList<>(Arrays.asList("com.android.contacts.*"))); + String encodedAllowlist = ProtoUtil.encodeContentProviderAllowlist(allowedAuthorities); mDeviceConfigUtils.setProperty(PROPERTY_CONTENTPROVIDER_ALLOWLIST, encodedAllowlist); loadSdk(); mContentProvidersSdkApi.getContentProviderByAuthority( diff --git a/sdksandbox/tests/testutils/src/android/app/sdksandbox/testutils/ProtoUtil.java b/sdksandbox/tests/testutils/src/android/app/sdksandbox/testutils/ProtoUtil.java new file mode 100644 index 000000000..aedf8863c --- /dev/null +++ b/sdksandbox/tests/testutils/src/android/app/sdksandbox/testutils/ProtoUtil.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2024 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 android.app.sdksandbox.testutils; + +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Base64; + +import com.android.server.sdksandbox.proto.Activity.ActivityAllowlists; +import com.android.server.sdksandbox.proto.Activity.AllowedActivities; +import com.android.server.sdksandbox.proto.BroadcastReceiver.AllowedBroadcastReceivers; +import com.android.server.sdksandbox.proto.BroadcastReceiver.BroadcastReceiverAllowlists; +import com.android.server.sdksandbox.proto.ContentProvider.AllowedContentProviders; +import com.android.server.sdksandbox.proto.ContentProvider.ContentProviderAllowlists; +import com.android.server.sdksandbox.proto.Services.AllowedService; +import com.android.server.sdksandbox.proto.Services.AllowedServices; +import com.android.server.sdksandbox.proto.Services.ServiceAllowlists; + +import java.util.List; + +/** Utility class to get encoded string for various restrictions */ +public class ProtoUtil { + /** Encode authorities for ContentProvider Allowlist */ + public static String encodeContentProviderAllowlist( + ArrayMap<Integer, List<String>> authorities) { + ContentProviderAllowlists.Builder contentProviderAllowlistsBuilder = + ContentProviderAllowlists.newBuilder(); + + authorities.entrySet().stream() + .forEach( + x -> { + AllowedContentProviders allowedContentProvidersBuilder = + AllowedContentProviders.newBuilder() + .addAllAuthorities(x.getValue()) + .build(); + contentProviderAllowlistsBuilder.putAllowlistPerTargetSdk( + x.getKey(), allowedContentProvidersBuilder); + }); + ContentProviderAllowlists contentProviderAllowlists = + contentProviderAllowlistsBuilder.build(); + + return Base64.encodeToString( + contentProviderAllowlists.toByteArray(), Base64.NO_PADDING | Base64.NO_WRAP); + } + + /** Encode authorities for ContentProvider Allowlist */ + public static String encodeContentProviderAllowlist(ArraySet<String> authorities) { + AllowedContentProviders allowedContentProvidersBuilder = + AllowedContentProviders.newBuilder().addAllAuthorities(authorities).build(); + return Base64.encodeToString( + allowedContentProvidersBuilder.toByteArray(), Base64.NO_PADDING | Base64.NO_WRAP); + } + + /** Encode intent actions for startActivity Allowlist */ + public static String encodeActivityAllowlist(ArrayMap<Integer, List<String>> actions) { + ActivityAllowlists.Builder activityAllowlistsBuilder = ActivityAllowlists.newBuilder(); + + actions.entrySet().stream() + .forEach( + x -> { + AllowedActivities allowedActivitiesBuilder = + AllowedActivities.newBuilder() + .addAllActions(x.getValue()) + .build(); + activityAllowlistsBuilder.putAllowlistPerTargetSdk( + x.getKey(), allowedActivitiesBuilder); + }); + ActivityAllowlists activityAllowlists = activityAllowlistsBuilder.build(); + String res = + Base64.encodeToString( + activityAllowlists.toByteArray(), Base64.NO_PADDING | Base64.NO_WRAP); + + return res; + } + + /** Encode intent actions for broadcastReceivers Allowlist */ + public static String encodeBroadcastReceiverAllowlist( + ArrayMap<Integer, List<String>> intentActions) { + BroadcastReceiverAllowlists.Builder broadcastReceiverAllowlistBuilder = + BroadcastReceiverAllowlists.newBuilder(); + + intentActions.entrySet().stream() + .forEach( + x -> { + AllowedBroadcastReceivers allowedBroadcastReceivers = + AllowedBroadcastReceivers.newBuilder() + .addAllIntentActions(x.getValue()) + .build(); + + broadcastReceiverAllowlistBuilder.putAllowlistPerTargetSdk( + x.getKey(), allowedBroadcastReceivers); + }); + BroadcastReceiverAllowlists broadcastReceiverAllowlist = + broadcastReceiverAllowlistBuilder.build(); + return Base64.encodeToString( + broadcastReceiverAllowlist.toByteArray(), Base64.NO_PADDING | Base64.NO_WRAP); + } + + /** Encode intent actions for broadcastReceivers Allowlist */ + public static String encodeBroadcastReceiverAllowlist(ArraySet<String> actions) { + AllowedBroadcastReceivers allowedBroadcastReceivers = + AllowedBroadcastReceivers.newBuilder().addAllIntentActions(actions).build(); + return Base64.encodeToString( + allowedBroadcastReceivers.toByteArray(), Base64.NO_PADDING | Base64.NO_WRAP); + } + + /** + * Encode intent action, packageName, component className, component packageName for Service + * Allowlist + */ + public static String encodeServiceAllowlist( + ArrayMap<Integer, List<ArrayMap<String, String>>> services) { + ServiceAllowlists.Builder serviceAllowlistsBuilder = ServiceAllowlists.newBuilder(); + + services.entrySet().stream() + .forEach( + x -> { + AllowedServices.Builder allowedServicesBuilder = + AllowedServices.newBuilder(); + if (x.getValue().size() == 0) { + allowedServicesBuilder.addAllowedServices( + AllowedService.newBuilder().build()); + } else { + x.getValue() + .forEach( + service -> { + allowedServicesBuilder.addAllowedServices( + getAllowedService(service)); + }); + } + serviceAllowlistsBuilder.putAllowlistPerTargetSdk( + x.getKey(), allowedServicesBuilder.build()); + }); + ServiceAllowlists serviceAllowlists = serviceAllowlistsBuilder.build(); + return Base64.encodeToString( + serviceAllowlists.toByteArray(), Base64.NO_PADDING | Base64.NO_WRAP); + } + + private static AllowedService getAllowedService(ArrayMap<String, String> service) { + return AllowedService.newBuilder() + .setAction(service.get("action")) + .setPackageName(service.get("packageName")) + .setComponentClassName(service.get("componentClassName")) + .setComponentPackageName(service.get("componentPackageName")) + .build(); + } +} diff --git a/sdksandbox/tests/testutils/src/com/android/server/sdksandbox/testutils/FakeSdkSandboxProvider.java b/sdksandbox/tests/testutils/src/com/android/server/sdksandbox/testutils/FakeSdkSandboxProvider.java index f1cb19197..6510ec2a7 100644 --- a/sdksandbox/tests/testutils/src/com/android/server/sdksandbox/testutils/FakeSdkSandboxProvider.java +++ b/sdksandbox/tests/testutils/src/com/android/server/sdksandbox/testutils/FakeSdkSandboxProvider.java @@ -38,6 +38,8 @@ public class FakeSdkSandboxProvider implements SdkSandboxServiceProvider { private final ArrayMap<CallingInfo, ISdkSandboxService> mService = new ArrayMap<>(); private static final String TEST_PACKAGE = "com.android.server.sdksandbox.tests"; + public static final String FAKE_DUMP_OUTPUT = "FakeDump"; + // When set to true, this will fail the bindService call private boolean mFailBinding = false; @@ -127,7 +129,7 @@ public class FakeSdkSandboxProvider implements SdkSandboxServiceProvider { @Override public void dump(PrintWriter writer) { - writer.println("FakeDump"); + writer.println(FAKE_DUMP_OUTPUT); } @NonNull diff --git a/sdksandbox/tests/unittest/Android.bp b/sdksandbox/tests/unittest/Android.bp index 6344c438d..fe65d38d3 100644 --- a/sdksandbox/tests/unittest/Android.bp +++ b/sdksandbox/tests/unittest/Android.bp @@ -29,6 +29,7 @@ java_defaults { "compatibility-device-util-axt-minus-dexmaker", "mockito-target-extended-minus-junit4", "truth", + "adservices-test-utility", "SdkSandboxTestUtils", "SdkSandbox-java-lib", ], diff --git a/sdksandbox/tests/unittest/src/com/android/sdksandbox/SdkSandboxTest.java b/sdksandbox/tests/unittest/src/com/android/sdksandbox/SdkSandboxTest.java index bbd0f6501..18e817bbe 100644 --- a/sdksandbox/tests/unittest/src/com/android/sdksandbox/SdkSandboxTest.java +++ b/sdksandbox/tests/unittest/src/com/android/sdksandbox/SdkSandboxTest.java @@ -174,12 +174,20 @@ public class SdkSandboxTest { Context context = InstrumentationRegistry.getInstrumentation().getContext(); mContext = Mockito.spy(context); + mApplicationInfo = mContext.getPackageManager().getApplicationInfo(SDK_PACKAGE, 0); + mLoader = getClassLoader(mApplicationInfo); + if (sCustomizedSdkContextEnabled) { + Mockito.doReturn(mContext) + .when(mContext) + .createContextForSdkInSandbox(Mockito.any(), Mockito.anyInt()); + Mockito.doReturn(mLoader).when(mContext).getClassLoader(); + } + mSpyPackageManager = Mockito.spy(mContext.getPackageManager()); - mInjector = Mockito.spy(new InjectorForTest(mContext)); Mockito.doReturn(mSpyPackageManager).when(mContext).getPackageManager(); + + mInjector = Mockito.spy(new InjectorForTest(mContext)); mService = new SdkSandboxServiceImpl(mInjector); - mApplicationInfo = mContext.getPackageManager().getApplicationInfo(SDK_PACKAGE, 0); - mLoader = getClassLoader(mApplicationInfo); } @After diff --git a/sdksandbox/tests/unittest/src/com/android/server/sdksandbox/SdkSandboxManagerServiceUnitTest.java b/sdksandbox/tests/unittest/src/com/android/server/sdksandbox/SdkSandboxManagerServiceUnitTest.java index c89bf03a7..e9f80f3dd 100644 --- a/sdksandbox/tests/unittest/src/com/android/server/sdksandbox/SdkSandboxManagerServiceUnitTest.java +++ b/sdksandbox/tests/unittest/src/com/android/server/sdksandbox/SdkSandboxManagerServiceUnitTest.java @@ -22,6 +22,7 @@ import static android.app.sdksandbox.ISharedPreferencesSyncCallback.PREFERENCES_ import static android.app.sdksandbox.SdkSandboxManager.ACTION_START_SANDBOXED_ACTIVITY; import static android.app.sdksandbox.SdkSandboxManager.LOAD_SDK_INTERNAL_ERROR; +import static com.android.server.sdksandbox.testutils.FakeSdkSandboxProvider.FAKE_DUMP_OUTPUT; import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_SDK_SANDBOX_ORDER_ID; import static com.google.common.truth.Truth.assertThat; @@ -30,6 +31,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import android.Manifest; @@ -74,6 +76,7 @@ import android.util.Log; import androidx.annotation.Nullable; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.adservices.shared.testing.common.DumpHelper; import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder; import com.android.modules.utils.build.SdkLevel; @@ -271,7 +274,9 @@ public class SdkSandboxManagerServiceUnitTest { if (sSdkSandboxSettingsListener != null) { sSdkSandboxSettingsListener.unregisterPropertiesListener(); } - mStaticMockSession.finishMocking(); + if (mStaticMockSession != null) { + mStaticMockSession.finishMocking(); + } } /** Mock the ActivityManager::killUid to avoid SecurityException thrown in test. **/ @@ -2169,26 +2174,9 @@ public class SdkSandboxManagerServiceUnitTest { mockGrantedPermission(DUMP); mService.registerAdServicesManagerService(mAdServicesManager, /* published= */ false); - String dump; - try (StringWriter stringWriter = new StringWriter()) { - // Mock call to mAdServicesManager.dump(); - FileDescriptor fd = new FileDescriptor(); - PrintWriter writer = new PrintWriter(stringWriter); - String[] args = new String[0]; - Mockito.doAnswer( - (inv) -> { - writer.println("FakeAdServiceDump"); - return null; - }) - .when(mAdServicesManager) - .dump(fd, args); - - mService.dump(fd, writer, args); - - dump = stringWriter.toString(); - } + String dump = mockAdServicesDumpAndDump("FakeAdServiceDump"); - assertThat(dump).contains("FakeDump"); + assertThat(dump).contains(FAKE_DUMP_OUTPUT); assertThat(dump).contains("FakeAdServiceDump"); } @@ -2198,15 +2186,9 @@ public class SdkSandboxManagerServiceUnitTest { mockGrantedPermission(DUMP); mService.registerAdServicesManagerService(mAdServicesManager, /* published= */ true); - String dump; - try (StringWriter stringWriter = new StringWriter()) { - mService.dump(new FileDescriptor(), new PrintWriter(stringWriter), new String[0]); - - dump = stringWriter.toString(); - } - - assertThat(dump).contains("FakeDump"); + String dump = dump(); + assertThat(dump).contains(FAKE_DUMP_OUTPUT); Mockito.verify(mAdServicesManager, Mockito.never()) .dump(ArgumentMatchers.any(), ArgumentMatchers.any()); } @@ -2217,16 +2199,10 @@ public class SdkSandboxManagerServiceUnitTest { mockGrantedPermission(DUMP); mService.registerAdServicesManagerService(mAdServicesManager, /* published= */ false); - String dump; - try (StringWriter stringWriter = new StringWriter()) { - mService.dump(new FileDescriptor(), new PrintWriter(stringWriter), new String[0]); - dump = stringWriter.toString(); - } - - assertThat(dump).contains("FakeDump"); + String dump = mockAdServicesDumpAndDump("FakeAdServiceDump"); - Mockito.verify(mAdServicesManager, Mockito.never()) - .dump(ArgumentMatchers.any(), ArgumentMatchers.any()); + assertThat(dump).contains(FAKE_DUMP_OUTPUT); + assertThat(dump).contains("FakeAdServiceDump"); } @Test @@ -2235,14 +2211,9 @@ public class SdkSandboxManagerServiceUnitTest { mockGrantedPermission(DUMP); mService.registerAdServicesManagerService(mAdServicesManager, /* published= */ true); - String dump; - try (StringWriter stringWriter = new StringWriter()) { - mService.dump(new FileDescriptor(), new PrintWriter(stringWriter), new String[0]); - dump = stringWriter.toString(); - } - - assertThat(dump).contains("FakeDump"); + String dump = dump(); + assertThat(dump).contains(FAKE_DUMP_OUTPUT); Mockito.verify(mAdServicesManager, Mockito.never()) .dump(ArgumentMatchers.any(), ArgumentMatchers.any()); } @@ -2253,24 +2224,7 @@ public class SdkSandboxManagerServiceUnitTest { mockGrantedPermission(DUMP); mService.registerAdServicesManagerService(mAdServicesManager, /* published= */ false); - String dump; - try (StringWriter stringWriter = new StringWriter()) { - // Mock call to mAdServicesManager.dump(); - FileDescriptor fd = new FileDescriptor(); - PrintWriter writer = new PrintWriter(stringWriter); - String[] args = new String[] {"--AdServices"}; - Mockito.doAnswer( - (inv) -> { - writer.println("FakeAdServiceDump"); - return null; - }) - .when(mAdServicesManager) - .dump(fd, args); - - mService.dump(fd, writer, args); - - dump = stringWriter.toString(); - } + String dump = mockAdServicesDumpAndDump("FakeAdServiceDump", "--AdServices"); assertThat(dump).isEqualTo("AdServices:\n\nFakeAdServiceDump\n\n"); } @@ -2281,14 +2235,7 @@ public class SdkSandboxManagerServiceUnitTest { mockGrantedPermission(DUMP); mService.registerAdServicesManagerService(mAdServicesManager, /* published= */ true); - String dump; - try (StringWriter stringWriter = new StringWriter()) { - mService.dump( - new FileDescriptor(), - new PrintWriter(stringWriter), - new String[] {"--AdServices"}); - dump = stringWriter.toString(); - } + String dump = dump("--AdServices"); assertThat(dump) .isEqualTo( @@ -2305,22 +2252,9 @@ public class SdkSandboxManagerServiceUnitTest { mockGrantedPermission(DUMP); mService.registerAdServicesManagerService(mAdServicesManager, /* published= */ false); - String dump; - try (StringWriter stringWriter = new StringWriter()) { - mService.dump( - new FileDescriptor(), - new PrintWriter(stringWriter), - new String[] {"--AdServices"}); - dump = stringWriter.toString(); - } - - assertThat(dump) - .isEqualTo( - SdkSandboxManagerService.DUMP_AD_SERVICES_MESSAGE_HANDLED_BY_SYSTEM_SERVICE - + "\n"); + String dump = mockAdServicesDumpAndDump("FakeAdServiceDump", "--AdServices"); - Mockito.verify(mAdServicesManager, Mockito.never()) - .dump(ArgumentMatchers.any(), ArgumentMatchers.any()); + assertThat(dump).isEqualTo("AdServices:\n\nFakeAdServiceDump\n\n"); } @Test @@ -2329,23 +2263,13 @@ public class SdkSandboxManagerServiceUnitTest { mockGrantedPermission(DUMP); mService.registerAdServicesManagerService(mAdServicesManager, /* published= */ true); - String dump; - try (StringWriter stringWriter = new StringWriter()) { - mService.dump( - new FileDescriptor(), - new PrintWriter(stringWriter), - new String[] {"--AdServices"}); - dump = stringWriter.toString(); - } + String dump = mockAdServicesDumpAndDump("FakeAdServiceDump", "--AdServices"); assertThat(dump) .isEqualTo( SdkSandboxManagerService .DUMP_AD_SERVICES_MESSAGE_HANDLED_BY_AD_SERVICES_ITSELF + "\n"); - - Mockito.verify(mAdServicesManager, Mockito.never()) - .dump(ArgumentMatchers.any(), ArgumentMatchers.any()); } @Test(expected = SecurityException.class) @@ -2433,7 +2357,6 @@ public class SdkSandboxManagerServiceUnitTest { disableForegroundCheck(); sSdkSandboxSettingsListener.setKillSwitchState(true); - setDeviceConfigProperty(PROPERTY_DISABLE_SANDBOX, "true"); FakeLoadSdkCallbackBinder callback = new FakeLoadSdkCallbackBinder(); mService.loadSdk( TEST_PACKAGE, @@ -2815,6 +2738,34 @@ public class SdkSandboxManagerServiceUnitTest { } } + private String dump(String... args) throws Exception { + Log.d(TAG, "dump(): args=" + Arrays.toString(args)); + return DumpHelper.dump(pw -> mService.dump(new FileDescriptor(), pw, args)); + } + + private String mockAdServicesDumpAndDump(String adServicesDump, String... args) + throws Exception { + Log.d( + TAG, + "mockAdServicesDumpAndDump(): adServicesDump=" + + adServicesDump + + ", args=" + + Arrays.toString(args)); + return DumpHelper.dump( + pw -> { + Mockito.doAnswer( + inv -> { + Log.d(TAG, inv.toString()); + pw.println(adServicesDump); + return null; + }) + .when(mAdServicesManager) + .dump(any(), eq(args)); + + mService.dump(new FileDescriptor(), pw, args); + }); + } + private static Bundle getTestBundle() { final Bundle data = new Bundle(); data.putString(TEST_KEY, TEST_VALUE); diff --git a/shared/device-side/libraries/Android.bp b/shared/device-side/libraries/Android.bp index d243b1531..4880a29ec 100644 --- a/shared/device-side/libraries/Android.bp +++ b/shared/device-side/libraries/Android.bp @@ -70,7 +70,7 @@ java_library { // NOTE: currently there's just one library for everything (which for // now is just ApplicationContext), but it might be better to split -// into multiple libraries in the feature (if it gets bigger and/or +// into multiple libraries in the future (if it gets bigger and/or // if new classes adds external dependencies) java_library { name: "adservices-shared-common", @@ -79,6 +79,9 @@ java_library { srcs: [ "java/com/android/adservices/shared/common/*.java", ], + static_libs: [ + "modules-utils-preconditions", + ], libs: [ "framework-annotations-lib", "jsr305", // for @ThreadSafe", @@ -106,3 +109,23 @@ java_library { ], apex_available: ["com.android.adservices", "com.android.extservices", "com.android.ondevicepersonalization"], } + +// NOTE: this util library only have one Preconditions class now. If we add more classes, we should +// consider splitting this into a specific preconditions library. (if it gets bigger and/or if new +// classes adds external dependencies) +java_library { + name: "adservices-shared-util", + min_sdk_version: "30", + sdk_version: "module_current", + srcs: [ + "java/com/android/adservices/shared/util/*.java", + ], + libs: [ + "framework-annotations-lib", + ], + static_libs: [ + "modules-utils-build", + "error_prone_annotations", // For @FormatMethod + ], + apex_available: ["com.android.adservices", "com.android.extservices", "com.android.ondevicepersonalization"], +}
\ No newline at end of file diff --git a/shared/device-side/libraries/java/com/android/adservices/shared/common/ApplicationContextSingleton.java b/shared/device-side/libraries/java/com/android/adservices/shared/common/ApplicationContextSingleton.java index 77e20507b..c8066570b 100644 --- a/shared/device-side/libraries/java/com/android/adservices/shared/common/ApplicationContextSingleton.java +++ b/shared/device-side/libraries/java/com/android/adservices/shared/common/ApplicationContextSingleton.java @@ -19,6 +19,7 @@ import android.content.Context; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; @@ -49,11 +50,7 @@ public final class ApplicationContextSingleton { */ public static Context get() { Context context = sContext.get(); - // TODO(b/303886367): use Preconditions.checkState() - if (context == null) { - // TODO(b/309169907): log to CEL - throw new IllegalStateException(ERROR_MESSAGE_SET_NOT_CALLED); - } + Preconditions.checkState(context != null, ERROR_MESSAGE_SET_NOT_CALLED); return context; } @@ -68,11 +65,9 @@ public final class ApplicationContextSingleton { public static void set(Context context) { Context appContext = Objects.requireNonNull(context, "context cannot be null").getApplicationContext(); - // TODO(b/303886367): use Preconditions.checkIllegalArgument() - if (appContext == null) { - throw new IllegalArgumentException( - "Context (" + context + ") does not have an application context"); - } + + Preconditions.checkArgument( + appContext != null, "Context (%s) does not have an application context", context); // Set if it's not set yet if (sContext.compareAndSet(null, appContext)) { diff --git a/shared/device-side/libraries/java/com/android/adservices/shared/testing/common/DumpHelper.java b/shared/device-side/libraries/java/com/android/adservices/shared/testing/common/DumpHelper.java index da3466c6c..cde3e3fd9 100644 --- a/shared/device-side/libraries/java/com/android/adservices/shared/testing/common/DumpHelper.java +++ b/shared/device-side/libraries/java/com/android/adservices/shared/testing/common/DumpHelper.java @@ -19,7 +19,6 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.fail; -import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; @@ -30,7 +29,7 @@ import java.util.Objects; public final class DumpHelper { /** Calls {@code dump()} in the given dumper, and return its output. */ - public static String dump(Dumper dumper) throws IOException { + public static String dump(Dumper dumper) throws Exception { Objects.requireNonNull(dumper); try (StringWriter sw = new StringWriter()) { PrintWriter pw = new PrintWriter(sw); @@ -67,7 +66,7 @@ public final class DumpHelper { /** Helper to dump an object. */ public interface Dumper { /** Ah, might as well dump (dump!) */ - void dump(PrintWriter printWriter); + void dump(PrintWriter printWriter) throws Exception; } private DumpHelper() { diff --git a/shared/device-side/libraries/java/com/android/adservices/shared/util/Preconditions.java b/shared/device-side/libraries/java/com/android/adservices/shared/util/Preconditions.java new file mode 100644 index 000000000..31e302565 --- /dev/null +++ b/shared/device-side/libraries/java/com/android/adservices/shared/util/Preconditions.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2023 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.shared.util; + +import com.google.errorprone.annotations.FormatMethod; +import com.google.errorprone.annotations.FormatString; + +/** + * Simple static methods to be called at the start of your own methods to verify correct arguments + * and state. + * + * <p>Note: This class contains method(s) added after S+ in modules-utils-preconditions library. Use + * this class only when accessing Preconditions methods not available in R. For example, {@link + * #checkState(boolean, String, Object...)}. + */ +public final class Preconditions { + + /** + * Ensures the truth of an expression involving the state of the calling instance, but not + * involving any parameters to the calling method. + * + * @param expression a boolean expression + * @param messageTemplate a printf-style message template to use if the check fails; will be + * converted to a string using {@link String#format(String, Object...)} + * @param messageArgs arguments for {@code messageTemplate} + * @throws IllegalStateException if {@code expression} is false + */ + @FormatMethod + public static void checkState( + boolean expression, @FormatString String messageTemplate, Object... messageArgs) { + if (!expression) { + throw new IllegalStateException(String.format(messageTemplate, messageArgs)); + } + } +} diff --git a/shared/device-side/tests/Android.bp b/shared/device-side/tests/Android.bp index 5fb9a0ae3..0d6b83780 100644 --- a/shared/device-side/tests/Android.bp +++ b/shared/device-side/tests/Android.bp @@ -27,6 +27,7 @@ android_test { "adservices-shared-error-logging", "adservices-shared-storage", "adservices-shared-testing", + "adservices-shared-util", "androidx.test.core", "androidx.test.runner", "auto_value_annotations", diff --git a/shared/device-side/tests/java/com/android/adservices/shared/storage/BooleanFileDatastoreTest.java b/shared/device-side/tests/java/com/android/adservices/shared/storage/BooleanFileDatastoreTest.java index b771355f0..f9c4c993a 100644 --- a/shared/device-side/tests/java/com/android/adservices/shared/storage/BooleanFileDatastoreTest.java +++ b/shared/device-side/tests/java/com/android/adservices/shared/storage/BooleanFileDatastoreTest.java @@ -236,7 +236,6 @@ public final class BooleanFileDatastoreTest { assertWithMessage("get(%s)", TEST_KEY).that(readValue).isNotNull(); assertWithMessage("get(%s)", TEST_KEY).that(readValue).isEqualTo(insertedValue); - Set<String> keys = mDatastore.keySet(); assertWithMessage("keys").that(mDatastore.keySet()).containsExactly(TEST_KEY); // Delete diff --git a/shared/device-side/tests/java/com/android/adservices/shared/util/PreconditionsTest.java b/shared/device-side/tests/java/com/android/adservices/shared/util/PreconditionsTest.java new file mode 100644 index 000000000..f4ef110af --- /dev/null +++ b/shared/device-side/tests/java/com/android/adservices/shared/util/PreconditionsTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2023 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.shared.util; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertThrows; + +import org.junit.Test; + +public final class PreconditionsTest { + private static final String MESSAGE = "A test message"; + private static final String MESSAGE_TEMPLATE = "Expect a message: %s"; + + @Test + public void testCheckState_success() { + Preconditions.checkState(true, MESSAGE_TEMPLATE, MESSAGE); + } + + @Test + public void testCheckState_failure() { + Exception e = + assertThrows( + IllegalStateException.class, + () -> Preconditions.checkState(false, MESSAGE)); + assertThat(e).hasMessageThat().isEqualTo(MESSAGE); + } + + @Test + public void testCheckState_formattedMessage_success() { + Preconditions.checkState(true, MESSAGE_TEMPLATE, MESSAGE); + } + + @Test + public void testCheckState_formattedMessage_failure() { + Exception e = + assertThrows( + IllegalStateException.class, + () -> Preconditions.checkState(false, MESSAGE_TEMPLATE, MESSAGE)); + assertThat(e).hasMessageThat().isEqualTo(String.format(MESSAGE_TEMPLATE, MESSAGE)); + } +} |