diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-01-09 13:16:57 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-01-09 13:16:57 +0000 |
commit | 8814c89b401ee967edae9294ef5b3a67e72ebd19 (patch) | |
tree | 9b32c045aad303912763cd22950042171b46f04d | |
parent | 384856ee2643741d07f2423a0cfd5087fa96ac16 (diff) | |
parent | e937f25407ed9f2c9eef2130664239a9162b1a95 (diff) | |
download | AdServices-aml_mpr_341511070.tar.gz |
Snap for 11289794 from e937f25407ed9f2c9eef2130664239a9162b1a95 to mainline-mediaprovider-releaseaml_mpr_341511070
Change-Id: Ie75f9076eda86c593566237deff17cf3df147f33
37 files changed, 926 insertions, 269 deletions
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 69845db1a..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,23 +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; @@ -72,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 { @@ -84,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)); @@ -171,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()) { @@ -185,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(); } @@ -210,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(); @@ -248,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(); @@ -312,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(); @@ -353,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/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/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/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/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/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/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 77920bbfd..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 @@ -374,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/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/tests/cts/src/android/adservices/debuggablects/AdSelectionTest.java b/adservices/tests/cts/src/android/adservices/debuggablects/AdSelectionTest.java index cc70fe0bc..1fb43a294 100644 --- a/adservices/tests/cts/src/android/adservices/debuggablects/AdSelectionTest.java +++ b/adservices/tests/cts/src/android/adservices/debuggablects/AdSelectionTest.java @@ -229,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( 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 54a7715c8..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 @@ -628,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()) { 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 779c29b09..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; @@ -40,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 { @@ -75,19 +73,15 @@ public class NotificationLandingPage { @Test public void testNotificationLandingPage() throws Exception { - final long start = System.currentTimeMillis(); - - Trace.beginSection("NotificationTriggerEvent"); 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, 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 2f3cbffcc..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; @@ -39,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 { @@ -74,11 +73,12 @@ 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, @@ -86,7 +86,6 @@ public class UiSettingsMainPage { /* 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/test-util/side-less/com/android/adservices/common/AbstractFlagsSetterRule.java b/adservices/tests/test-util/side-less/com/android/adservices/common/AbstractFlagsSetterRule.java index 75342bc66..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 @@ -389,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) { @@ -420,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) { @@ -546,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/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/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(); + } +} |