chromium/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.site_settings;

import static androidx.test.espresso.Espresso.onData;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.swipeUp;
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.PreferenceMatchers.withKey;
import static androidx.test.espresso.matcher.RootMatchers.withDecorView;
import static androidx.test.espresso.matcher.ViewMatchers.assertThat;
import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
import static androidx.test.espresso.matcher.ViewMatchers.hasSibling;
import static androidx.test.espresso.matcher.ViewMatchers.isChecked;
import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withChild;
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;

import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.emptyIterable;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.refEq;

import static org.chromium.components.browser_ui.site_settings.AutoDarkMetrics.AutoDarkSettingsChangeSource.SITE_SETTINGS_GLOBAL;
import static org.chromium.components.content_settings.PrefNames.COOKIE_CONTROLS_MODE;
import static org.chromium.components.content_settings.PrefNames.DESKTOP_SITE_WINDOW_SETTING_ENABLED;
import static org.chromium.ui.test.util.ViewUtils.VIEW_GONE;
import static org.chromium.ui.test.util.ViewUtils.VIEW_INVISIBLE;
import static org.chromium.ui.test.util.ViewUtils.VIEW_NULL;
import static org.chromium.ui.test.util.ViewUtils.onViewWaiting;
import static org.chromium.ui.test.util.ViewUtils.waitForViewCheckingState;

import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.view.View;

import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.espresso.ViewInteraction;
import androidx.test.espresso.contrib.RecyclerViewActions;
import androidx.test.espresso.matcher.ViewMatchers;
import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;

import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

import org.chromium.base.ContextUtils;
import org.chromium.base.ThreadUtils;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.DisableIf;
import org.chromium.base.test.util.DisabledTest;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.Features.DisableFeatures;
import org.chromium.base.test.util.Features.EnableFeatures;
import org.chromium.base.test.util.HistogramWatcher;
import org.chromium.base.test.util.JniMocker;
import org.chromium.base.test.util.Restriction;
import org.chromium.chrome.browser.FederatedIdentityTestUtils;
import org.chromium.chrome.browser.browsing_data.BrowsingDataBridge;
import org.chromium.chrome.browser.browsing_data.BrowsingDataType;
import org.chromium.chrome.browser.browsing_data.TimePeriod;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.incognito.IncognitoUtils;
import org.chromium.chrome.browser.notifications.channels.ChromeChannelDefinitions;
import org.chromium.chrome.browser.notifications.channels.SiteChannelsManager;
import org.chromium.chrome.browser.permissions.PermissionTestRule;
import org.chromium.chrome.browser.permissions.PermissionTestRule.PermissionUpdateWaiter;
import org.chromium.chrome.browser.preferences.Pref;
import org.chromium.chrome.browser.profiles.ProfileManager;
import org.chromium.chrome.browser.settings.SettingsActivity;
import org.chromium.chrome.browser.settings.SettingsLauncherFactory;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.R;
import org.chromium.chrome.test.batch.BlankCTATabInitialStateRule;
import org.chromium.chrome.test.pagecontroller.utils.UiAutomatorUtils;
import org.chromium.chrome.test.util.ChromeRenderTestRule;
import org.chromium.chrome.test.util.browser.LocationSettingsTestUtil;
import org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference;
import org.chromium.components.browser_ui.settings.ChromeImageViewPreference;
import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
import org.chromium.components.browser_ui.settings.ExpandablePreferenceGroup;
import org.chromium.components.browser_ui.settings.SettingsLauncher;
import org.chromium.components.browser_ui.site_settings.ContentSettingException;
import org.chromium.components.browser_ui.site_settings.ContentSettingsResources;
import org.chromium.components.browser_ui.site_settings.GroupedWebsitesSettings;
import org.chromium.components.browser_ui.site_settings.RWSCookieSettings;
import org.chromium.components.browser_ui.site_settings.SingleCategorySettings;
import org.chromium.components.browser_ui.site_settings.SingleCategorySettingsConstants;
import org.chromium.components.browser_ui.site_settings.SingleWebsiteSettings;
import org.chromium.components.browser_ui.site_settings.SiteSettings;
import org.chromium.components.browser_ui.site_settings.SiteSettingsCategory;
import org.chromium.components.browser_ui.site_settings.TriStateCookieSettingsPreference;
import org.chromium.components.browser_ui.site_settings.TriStateSiteSettingsPreference;
import org.chromium.components.browser_ui.site_settings.Website;
import org.chromium.components.browser_ui.site_settings.WebsiteAddress;
import org.chromium.components.browser_ui.site_settings.WebsiteGroup;
import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridge;
import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridgeJni;
import org.chromium.components.browsing_data.DeleteBrowsingDataAction;
import org.chromium.components.content_settings.ContentSettingValues;
import org.chromium.components.content_settings.ContentSettingsType;
import org.chromium.components.content_settings.CookieControlsMode;
import org.chromium.components.content_settings.ProviderType;
import org.chromium.components.embedder_support.util.Origin;
import org.chromium.components.location.LocationUtils;
import org.chromium.components.permissions.nfc.NfcSystemLevelSetting;
import org.chromium.components.policy.test.annotations.Policies;
import org.chromium.components.prefs.PrefService;
import org.chromium.components.user_prefs.UserPrefs;
import org.chromium.content_public.browser.BrowserContextHandle;
import org.chromium.content_public.common.ContentSwitches;
import org.chromium.device.geolocation.LocationProviderOverrider;
import org.chromium.device.geolocation.MockLocationProvider;
import org.chromium.ui.test.util.DeviceRestriction;
import org.chromium.ui.test.util.RenderTestRule;
import org.chromium.ui.test.util.RenderTestRule.Component;
import org.chromium.ui.test.util.UiDisableIf;
import org.chromium.url.GURL;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeoutException;

/** Tests for everything under Settings > Site Settings. */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({
    ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
    ContentSwitches.HOST_RESOLVER_RULES + "=MAP * 127.0.0.1",
    "ignore-certificate-errors"
})
// TODO(crbug.com/344672098): Failing when batched, batch this again.
public class SiteSettingsTest {
    public static final String SITE_SETTINGS_BATCH_NAME = "site_settings";

    @ClassRule public static PermissionTestRule mPermissionRule = new PermissionTestRule(true);

    @Rule
    public BlankCTATabInitialStateRule mBlankCTATabInitialStateRule =
            new BlankCTATabInitialStateRule(mPermissionRule, false);

    @Rule
    public RenderTestRule mRenderTestRule =
            RenderTestRule.Builder.withPublicCorpus()
                    .setRevision(3)
                    .setBugComponent(Component.UI_BROWSER_MOBILE_SETTINGS)
                    .build();

    @Mock private SettingsLauncher mSettingsLauncher;

    @Rule public JniMocker mocker = new JniMocker();

    private PermissionUpdateWaiter mPermissionUpdateWaiter;

    private static final String[] NULL_ARRAY = new String[0];
    private static final String[] BINARY_TOGGLE_AND_INFO_TEXT =
            new String[] {"info_text", "binary_toggle"};
    private static final String[] BINARY_TOGGLE = new String[] {"binary_toggle"};
    private static final String[] BINARY_TOGGLE_WITH_EXCEPTION_AND_INFO_TEXT =
            new String[] {"info_text", "binary_toggle", "add_exception"};
    private static final String[] BINARY_TOGGLE_WITH_EXCEPTION =
            new String[] {"binary_toggle", "add_exception"};
    private static final String[] BINARY_TOGGLE_WITH_OS_WARNING_EXTRA =
            new String[] {"binary_toggle", "os_permissions_warning_extra"};
    private static final String[] CLEAR_BROWSING_DATA_LINK =
            new String[] {"clear_browsing_data_link", "clear_browsing_divider"};
    private static final String[] ANTI_ABUSE_PREF_KEYS = {
        "anti_abuse_when_on_header",
        "anti_abuse_when_on_section_one",
        "anti_abuse_when_on_section_two",
        "anti_abuse_when_on_section_three",
        "anti_abuse_things_to_consider_header",
        "anti_abuse_things_to_consider_section_one"
    };
    private static final String[] BINARY_TOGGLE_WITH_ANTI_ABUSE_PREF_KEYS = {
        "binary_toggle",
        "anti_abuse_when_on_header",
        "anti_abuse_when_on_section_one",
        "anti_abuse_when_on_section_two",
        "anti_abuse_when_on_section_three",
        "anti_abuse_things_to_consider_header",
        "anti_abuse_things_to_consider_section_one"
    };

    @Before
    public void setUp() throws TimeoutException {
        // Clean up cookies and permissions to ensure tests run in a clean environment.
        cleanUpCookiesAndPermissions();
        MockitoAnnotations.initMocks(this);
    }

    @After
    public void tearDown() throws TimeoutException {
        if (mPermissionUpdateWaiter != null) {
            ThreadUtils.runOnUiThreadBlocking(
                    () -> {
                        mPermissionRule
                                .getActivity()
                                .getActivityTab()
                                .removeObserver(mPermissionUpdateWaiter);
                    });
        }

        // Clean up default content setting and system settings.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    for (int t = 0; t < SiteSettingsCategory.Type.NUM_ENTRIES; t++) {
                        if (SiteSettingsCategory.contentSettingsType(t) >= 0) {
                            WebsitePreferenceBridge.setDefaultContentSetting(
                                    getBrowserContextHandle(),
                                    SiteSettingsCategory.contentSettingsType(t),
                                    ContentSettingValues.DEFAULT);
                        }
                    }
                });
        LocationUtils.setFactory(null);
        LocationProviderOverrider.setLocationProviderImpl(null);
        ContextUtils.getAppSharedPreferences()
                .edit()
                .remove(
                        SingleCategorySettingsConstants
                                .USER_ENABLED_DESKTOP_SITE_GLOBAL_SETTING_PREFERENCE_KEY)
                .apply();
    }

    @AfterClass
    public static void tearDownAfterClass() throws TimeoutException {
        cleanUpCookiesAndPermissions();
    }

    private static BrowserContextHandle getBrowserContextHandle() {
        return ProfileManager.getLastUsedRegularProfile();
    }

    private void initializeUpdateWaiter(final boolean expectGranted) {
        if (mPermissionUpdateWaiter != null) {
            ThreadUtils.runOnUiThreadBlocking(
                    () -> {
                        mPermissionRule
                                .getActivity()
                                .getActivityTab()
                                .removeObserver(mPermissionUpdateWaiter);
                    });
        }
        Tab tab = mPermissionRule.getActivity().getActivityTab();

        mPermissionUpdateWaiter =
                new PermissionUpdateWaiter(
                        expectGranted ? "Granted" : "Denied", mPermissionRule.getActivity());
        ThreadUtils.runOnUiThreadBlocking(() -> tab.addObserver(mPermissionUpdateWaiter));
    }

    private void triggerEmbargoForOrigin(String url) throws TimeoutException {
        // Ignore notification request 4 times to enter embargo. 5th one ensures that notifications
        // are blocked by actually causing a deny-by-embargo.
        for (int i = 0; i < 5; i++) {
            mPermissionRule.loadUrl(url);
            mPermissionRule.runJavaScriptCodeInCurrentTab("requestPermissionAndRespond()");
        }
    }

    private int getTabCount() {
        return ThreadUtils.runOnUiThreadBlocking(
                () -> mPermissionRule.getActivity().getTabModelSelector().getTotalTabCount());
    }

    private static void cleanUpCookiesAndPermissions() throws TimeoutException {
        CallbackHelper helper = new CallbackHelper();
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    BrowsingDataBridge.getForProfile(ProfileManager.getLastUsedRegularProfile())
                            .clearBrowsingData(
                                    helper::notifyCalled,
                                    new int[] {
                                        BrowsingDataType.SITE_DATA, BrowsingDataType.SITE_SETTINGS
                                    },
                                    TimePeriod.ALL_TIME);
                });
        helper.waitForCallback(0);
    }

    /**
     * Returns a {@link Matcher} for a preference's managed disclaimer, depending on highlighting of
     * managed prefs being enabled. Use {@code activeView} as true for the view that's is supposed
     * to be shown when the preference is managed, or as false for the view that is always supposed
     * to be hidden because of the highlighting experiment.
     */
    private static Matcher<View> getManagedViewMatcher(boolean activeView) {
        return activeView
                ? allOf(
                        withId(R.id.managed_disclaimer_text),
                        hasSibling(withId(R.id.radio_button_layout)))
                : withId(R.id.managed_view_legacy);
    }

    private void createCookieExceptions() {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    WebsitePreferenceBridge.setContentSettingCustomScope(
                            getBrowserContextHandle(),
                            ContentSettingsType.COOKIES,
                            "*",
                            "secondary.com",
                            ContentSettingValues.ALLOW);
                    WebsitePreferenceBridge.setContentSettingCustomScope(
                            getBrowserContextHandle(),
                            ContentSettingsType.COOKIES,
                            "primary.com",
                            "*",
                            ContentSettingValues.ALLOW);
                });
    }

    private void createStorageAccessExceptions() {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    WebsitePreferenceBridge.setContentSettingCustomScope(
                            getBrowserContextHandle(),
                            ContentSettingsType.STORAGE_ACCESS,
                            "primary.com",
                            "secondary.com",
                            ContentSettingValues.ALLOW);
                    WebsitePreferenceBridge.setContentSettingCustomScope(
                            getBrowserContextHandle(),
                            ContentSettingsType.STORAGE_ACCESS,
                            "primary.com",
                            "secondary3.com",
                            ContentSettingValues.ALLOW);
                    WebsitePreferenceBridge.setContentSettingCustomScope(
                            getBrowserContextHandle(),
                            ContentSettingsType.STORAGE_ACCESS,
                            "primary2.com",
                            "secondary2.com",
                            ContentSettingValues.ALLOW);
                });
    }

    private Website getStorageAccessSite() {
        WebsiteAddress permissionOrigin = WebsiteAddress.create("primary.com");
        WebsiteAddress permissionEmbedder = WebsiteAddress.create("*");
        Website site = new Website(permissionOrigin, permissionEmbedder);
        site.addEmbeddedPermission(
                new ContentSettingException(
                        ContentSettingsType.STORAGE_ACCESS,
                        "primary.com",
                        "secondary1.com",
                        ContentSettingValues.ALLOW,
                        ProviderType.PREF_PROVIDER,
                        30,
                        false));
        site.addEmbeddedPermission(
                new ContentSettingException(
                        ContentSettingsType.STORAGE_ACCESS,
                        "primary.com",
                        "secondary3.com",
                        ContentSettingValues.ALLOW,
                        ProviderType.PREF_PROVIDER,
                        30,
                        false));

        return site;
    }

    /** Sets Allow Location Enabled to be true and make sure it is set correctly. */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    @DisableFeatures(ChromeFeatureList.PERMISSION_DEDICATED_CPSS_SETTING_ANDROID)
    public void testSetAllowLocationEnabled() throws Exception {
        LocationSettingsTestUtil.setSystemLocationSettingEnabled(true);
        LocationProviderOverrider.setLocationProviderImpl(new MockLocationProvider());
        new TwoStatePermissionTestCase(
                        "Location",
                        SiteSettingsCategory.Type.DEVICE_LOCATION,
                        ContentSettingsType.GEOLOCATION,
                        true)
                .run();
        ThreadUtils.runOnUiThreadBlocking(
                () ->
                        Assert.assertTrue(
                                "Location should be allowed.",
                                WebsitePreferenceBridge.areAllLocationSettingsEnabled(
                                        getBrowserContextHandle())));

        initializeUpdateWaiter(/* expectGranted= */ true);

        // Launch a page that uses geolocation and make sure a permission prompt shows up.
        mPermissionRule.runAllowTest(
                mPermissionUpdateWaiter,
                "/chrome/test/data/geolocation/geolocation_on_load.html",
                "",
                0,
                false,
                true);
    }

    /** Sets Allow Location Enabled to be false and make sure it is set correctly. */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testSetAllowLocationNotEnabled() throws Exception {
        LocationSettingsTestUtil.setSystemLocationSettingEnabled(true);
        LocationProviderOverrider.setLocationProviderImpl(new MockLocationProvider());
        new TwoStatePermissionTestCase(
                        "Location",
                        SiteSettingsCategory.Type.DEVICE_LOCATION,
                        ContentSettingsType.GEOLOCATION,
                        false)
                .run();
        ThreadUtils.runOnUiThreadBlocking(
                () ->
                        Assert.assertFalse(
                                "Location should be blocked.",
                                WebsitePreferenceBridge.areAllLocationSettingsEnabled(
                                        getBrowserContextHandle())));

        // Launch a page that uses geolocation. No permission prompt is expected.
        initializeUpdateWaiter(/* expectGranted= */ false);
        mPermissionRule.runNoPromptTest(
                mPermissionUpdateWaiter,
                "/chrome/test/data/geolocation/geolocation_on_load.html",
                "",
                0,
                false,
                true);
    }

    private void setCookiesEnabled(final SettingsActivity settingsActivity, final boolean enabled) {
        ThreadUtils.runOnUiThreadBlocking(
                new Runnable() {
                    @Override
                    public void run() {
                        final SingleCategorySettings websitePreferences =
                                (SingleCategorySettings) settingsActivity.getMainFragment();
                        final TriStateCookieSettingsPreference cookies =
                                websitePreferences.findPreference(
                                        SingleCategorySettings.TRI_STATE_COOKIE_TOGGLE);

                        websitePreferences.onPreferenceChange(
                                cookies,
                                enabled
                                        ? CookieControlsMode.OFF
                                        : CookieControlsMode.BLOCK_THIRD_PARTY);
                        Assert.assertEquals(
                                "Cookies should be " + (enabled ? "allowed" : "blocked"),
                                doesAcceptCookies(),
                                enabled);
                    }

                    private boolean doesAcceptCookies() {
                        return UserPrefs.get(ProfileManager.getLastUsedRegularProfile())
                                        .getInteger(COOKIE_CONTROLS_MODE)
                                == CookieControlsMode.OFF;
                    }
                });
    }

    private enum ToggleButtonState {
        EnabledUnchecked,
        EnabledChecked,
        Disabled
    }

    /** Checks if the button representing the given state matches the managed expectation. */
    private void checkTriStateCookieToggleButtonState(
            final SettingsActivity settingsActivity,
            final @CookieControlsMode int state,
            final ToggleButtonState toggleState) {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    SingleCategorySettings preferences =
                            (SingleCategorySettings) settingsActivity.getMainFragment();
                    TriStateCookieSettingsPreference triStateCookieToggle =
                            preferences.findPreference(
                                    SingleCategorySettings.TRI_STATE_COOKIE_TOGGLE);
                    boolean enabled = toggleState != ToggleButtonState.Disabled;
                    boolean checked = toggleState == ToggleButtonState.EnabledChecked;
                    Assert.assertEquals(
                            state + " button should be " + (enabled ? "enabled" : "disabled"),
                            enabled,
                            triStateCookieToggle.isButtonEnabledForTesting(state));
                    Assert.assertEquals(
                            state + " button should be " + (checked ? "checked" : "unchecked"),
                            checked,
                            triStateCookieToggle.isButtonCheckedForTesting(state));
                });
    }

    private void checkDefaultCookiesSettingManaged(boolean expected) {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    Assert.assertEquals(
                            "Default Cookie Setting should be "
                                    + (expected ? "managed" : "unmanaged"),
                            expected,
                            WebsitePreferenceBridge.isContentSettingManaged(
                                    getBrowserContextHandle(), ContentSettingsType.COOKIES));
                });
    }

    private void checkThirdPartyCookieBlockingManaged(boolean expected) {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    Assert.assertEquals(
                            "Third Party Cookie Blocking should be "
                                    + (expected ? "managed" : "unmanaged"),
                            expected,
                            UserPrefs.get(ProfileManager.getLastUsedRegularProfile())
                                    .isManagedPreference(COOKIE_CONTROLS_MODE));
                });
    }

    private void setGlobalToggleForCategory(
            final @SiteSettingsCategory.Type int type, final boolean enabled) {
        final SettingsActivity settingsActivity =
                SiteSettingsTestUtils.startSiteSettingsCategory(type);

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    SingleCategorySettings preferences =
                            (SingleCategorySettings) settingsActivity.getMainFragment();
                    if (type == SiteSettingsCategory.Type.THIRD_PARTY_COOKIES) {
                        TriStateCookieSettingsPreference preference =
                                preferences.findPreference(
                                        SingleCategorySettings.TRI_STATE_COOKIE_TOGGLE);
                        preferences.onPreferenceChange(
                                preference,
                                enabled
                                        ? CookieControlsMode.OFF
                                        : CookieControlsMode.BLOCK_THIRD_PARTY);
                    } else {
                        ChromeSwitchPreference toggle =
                                preferences.findPreference(
                                        SingleCategorySettings.BINARY_TOGGLE_KEY);
                        preferences.onPreferenceChange(toggle, enabled);
                    }
                });
        if (type == SiteSettingsCategory.Type.SITE_DATA && !enabled) {
            int id = R.string.website_settings_site_data_page_block_confirm_dialog_confirm_button;
            onViewWaiting(
                            withText(id),
                            // checkRootDialog=true ensures dialog is in focus, avoids flakiness.
                            true)
                    .perform(click());
        }
        settingsActivity.finish();
    }

    private void setGlobalTriStateToggleForCategory(
            final @SiteSettingsCategory.Type int type, final int newValue) {
        final SettingsActivity settingsActivity =
                SiteSettingsTestUtils.startSiteSettingsCategory(type);

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    SingleCategorySettings preferences =
                            (SingleCategorySettings) settingsActivity.getMainFragment();
                    TriStateSiteSettingsPreference triStateToggle =
                            preferences.findPreference(SingleCategorySettings.TRI_STATE_TOGGLE_KEY);
                    preferences.onPreferenceChange(triStateToggle, newValue);
                });
        settingsActivity.finish();
    }

    private void setTriStateCookieToggle(@CookieControlsMode int newState) {
        final SettingsActivity settingsActivity =
                SiteSettingsTestUtils.startSiteSettingsCategory(
                        SiteSettingsCategory.Type.THIRD_PARTY_COOKIES);

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    SingleCategorySettings preferences =
                            (SingleCategorySettings) settingsActivity.getMainFragment();
                    TriStateCookieSettingsPreference triStateCookieToggle =
                            preferences.findPreference(
                                    SingleCategorySettings.TRI_STATE_COOKIE_TOGGLE);
                    preferences.onPreferenceChange(triStateCookieToggle, newState);
                });
        settingsActivity.finish();
    }

    /**
     * Tests that the Preferences designated by keys in |expectedKeys|, and only these preferences,
     * will be shown for the category specified by |type|. The order of Preferences matters.
     */
    private void checkPreferencesForCategory(
            final @SiteSettingsCategory.Type int type, String[] expectedKeys) {
        final SettingsActivity settingsActivity;

        if (type == SiteSettingsCategory.Type.ALL_SITES
                || type == SiteSettingsCategory.Type.USE_STORAGE
                || type == SiteSettingsCategory.Type.ZOOM) {
            settingsActivity = SiteSettingsTestUtils.startAllSitesSettings(type);
        } else {
            settingsActivity = SiteSettingsTestUtils.startSiteSettingsCategory(type);
        }

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    PreferenceFragmentCompat preferenceFragment =
                            (PreferenceFragmentCompat) settingsActivity.getMainFragment();
                    PreferenceScreen preferenceScreen = preferenceFragment.getPreferenceScreen();
                    int preferenceCount = preferenceScreen.getPreferenceCount();

                    ArrayList<String> actualKeys = new ArrayList<>();
                    for (int index = 0; index < preferenceCount; index++) {
                        Preference preference = preferenceScreen.getPreference(index);
                        if (!preference.isVisible()) continue;
                        String key = preference.getKey();
                        // Not all Preferences have keys. For example, the list of websites below
                        // the toggles, which are dynamically added. Ignore those.
                        if (key != null) actualKeys.add(key);
                    }

                    assertThat(
                            actualKeys,
                            expectedKeys.length == 0 ? emptyIterable() : contains(expectedKeys));
                });
        settingsActivity.finish();
    }

    private void testExpectedPreferences(
            final @SiteSettingsCategory.Type int type,
            String[] disabledExpectedKeys,
            String[] enabledExpectedKeys) {
        // Disable the category and check for the right preferences.
        setGlobalToggleForCategory(type, false);
        checkPreferencesForCategory(type, disabledExpectedKeys);
        // Re-enable the category and check for the right preferences.
        setGlobalToggleForCategory(type, true);
        checkPreferencesForCategory(type, enabledExpectedKeys);
    }

    /** Allows cookies to be set and ensures that they are. */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testCookiesNotBlocked() throws Exception {
        SettingsActivity settingsActivity =
                SiteSettingsTestUtils.startSiteSettingsCategory(
                        SiteSettingsCategory.Type.THIRD_PARTY_COOKIES);
        setCookiesEnabled(settingsActivity, true);
        settingsActivity.finish();

        final String url = mPermissionRule.getURL("/chrome/test/data/android/cookie.html");

        // Load the page and clear any set cookies.
        mPermissionRule.loadUrl(url);
        mPermissionRule.runJavaScriptCodeInCurrentTab("clearCookie()");
        Assert.assertEquals("\"\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
        mPermissionRule.runJavaScriptCodeInCurrentTab("setCookie()");
        Assert.assertEquals(
                "\"Foo=Bar\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));

        // Load the page again and ensure the cookie still is set.
        mPermissionRule.loadUrl(url);
        Assert.assertEquals(
                "\"Foo=Bar\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
    }

    /** Clicks on cookies radio buttons and verify the right FPS subpage is launched. */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    @EnableFeatures(ChromeFeatureList.PRIVACY_SANDBOX_FPS_UI)
    public void testCookiesFPSSubpageIsLaunched() throws Exception {
        SettingsActivity settingsActivity =
                SiteSettingsTestUtils.startSiteSettingsCategory(
                        SiteSettingsCategory.Type.THIRD_PARTY_COOKIES);

        verifyFPSCookieSubpageIsLaunchedWithParams(
                settingsActivity, CookieControlsMode.BLOCK_THIRD_PARTY);
        verifyFPSCookieSubpageIsLaunchedWithParams(
                settingsActivity, CookieControlsMode.INCOGNITO_ONLY);
    }

    private void verifyFPSCookieSubpageIsLaunchedWithParams(
            final SettingsActivity settingsActivity,
            @CookieControlsMode int expectedCookieControlMode) {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    final SingleCategorySettings websitePreferences =
                            (SingleCategorySettings) settingsActivity.getMainFragment();
                    final TriStateCookieSettingsPreference cookies =
                            websitePreferences.findPreference(
                                    SingleCategorySettings.TRI_STATE_COOKIE_TOGGLE);

                    Mockito.clearInvocations(mSettingsLauncher);
                    websitePreferences.setSettingsLauncher(mSettingsLauncher);

                    SiteSettingsTestUtils.getCookieRadioButtonFrom(
                                    cookies, expectedCookieControlMode)
                            .getAuxButtonForTests()
                            .performClick();

                    Bundle fragmentArgs = new Bundle();
                    fragmentArgs.putInt(
                            RWSCookieSettings.EXTRA_COOKIE_PAGE_STATE, expectedCookieControlMode);

                    Mockito.verify(mSettingsLauncher)
                            .launchSettingsActivity(
                                    eq(websitePreferences.getContext()),
                                    eq(RWSCookieSettings.class),
                                    refEq(fragmentArgs));
                });
    }

    /** Blocks specific sites from setting cookies and ensures that no cookies can be set. */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    @DisabledTest(message = "https://crbug.com/1395173")
    public void testSiteExceptionSiteDataBlocked() throws Exception {
        setGlobalToggleForCategory(SiteSettingsCategory.Type.SITE_DATA, true);

        final String url = mPermissionRule.getURL("/chrome/test/data/android/cookie.html");

        // Load the page and clear any set cookies.
        mPermissionRule.loadUrl(url);
        mPermissionRule.runJavaScriptCodeInCurrentTab("clearCookie()");
        Assert.assertEquals("\"\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));

        // Check cookies can be set for this website when there is no rule.
        mPermissionRule.runJavaScriptCodeInCurrentTab("setCookie()");
        Assert.assertEquals(
                "\"Foo=Bar\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));

        // Set specific rule to block site and ensure it cannot set cookies.
        mPermissionRule.loadUrl(url);
        mPermissionRule.runJavaScriptCodeInCurrentTab("clearCookie()");

        setGlobalToggleForCategory(SiteSettingsCategory.Type.SITE_DATA, false);
        mPermissionRule.runJavaScriptCodeInCurrentTab("setCookie()");
        Assert.assertEquals("\"\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));

        // Load the page again and ensure the cookie remains unset.
        mPermissionRule.loadUrl(url);
        Assert.assertEquals("\"\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
    }

    /** Set a cookie and check that it is removed when a site is cleared. */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    @DisabledTest(message = "https://crbug.com/1112409")
    public void testClearCookies() throws Exception {
        final String url = mPermissionRule.getURL("/chrome/test/data/android/cookie.html");

        mPermissionRule.loadUrl(url);
        Assert.assertEquals("\"\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
        mPermissionRule.runJavaScriptCodeInCurrentTab("setCookie()");
        Assert.assertEquals(
                "\"Foo=Bar\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));

        HistogramWatcher histogramWatcher =
                HistogramWatcher.newSingleRecordWatcher(
                        "Privacy.DeleteBrowsingData.Action",
                        DeleteBrowsingDataAction.SITES_SETTINGS_PAGE);

        resetSite(WebsiteAddress.create(url));

        // Load the page again and ensure the cookie is gone.
        mPermissionRule.loadUrl(url);
        Assert.assertEquals("\"\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
        // Verify DeleteBrowsingDataAction metric is recorded.
        histogramWatcher.assertExpected();
    }

    /** Tests clearing cookies for a group of websites. */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testClearCookiesGroup() throws Exception {
        final String url1 =
                mPermissionRule.getURLWithHostName(
                        "one.example.com", "/chrome/test/data/android/cookie.html");
        final String url2 =
                mPermissionRule.getURLWithHostName(
                        "two.example.com", "/chrome/test/data/android/cookie.html");
        final String url3 =
                mPermissionRule.getURLWithHostName(
                        "foo.com", "/chrome/test/data/android/cookie.html");

        mPermissionRule.loadUrl(url1);
        Assert.assertEquals("\"\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
        mPermissionRule.runJavaScriptCodeInCurrentTab("setCookie(\".example.com\")");
        mPermissionRule.runJavaScriptCodeInCurrentTab("setCookie(\".one.example.com\")");
        Assert.assertEquals(
                "\"Foo=Bar; Foo=Bar\"",
                mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));

        mPermissionRule.loadUrl(url2);
        Assert.assertEquals(
                "\"Foo=Bar\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
        mPermissionRule.runJavaScriptCodeInCurrentTab("setCookie(\".two.example.com\")");
        Assert.assertEquals(
                "\"Foo=Bar; Foo=Bar\"",
                mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));

        mPermissionRule.loadUrl(url3);
        Assert.assertEquals("\"\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
        mPermissionRule.runJavaScriptCodeInCurrentTab("setCookie(\".foo.com\")");
        Assert.assertEquals(
                "\"Foo=Bar\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));

        HistogramWatcher histogramWatcher =
                HistogramWatcher.newSingleRecordWatcher(
                        "Privacy.DeleteBrowsingData.Action",
                        DeleteBrowsingDataAction.SITES_SETTINGS_PAGE);

        resetGroup(Arrays.asList(WebsiteAddress.create(url1), WebsiteAddress.create(url2)));

        // 1 and 2 got cleared; 3 stays intact.
        mPermissionRule.loadUrl(url1);
        Assert.assertEquals("\"\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
        mPermissionRule.loadUrl(url2);
        Assert.assertEquals("\"\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
        mPermissionRule.loadUrl(url3);
        Assert.assertEquals(
                "\"Foo=Bar\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));

        // Verify DeleteBrowsingDataAction metric is recorded.
        histogramWatcher.assertExpected();
    }

    /** Set cookies for domains and check that they are removed when a site is cleared. */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    @DisabledTest(message = "https://crbug.com/1329450")
    public void testClearDomainCookies() throws Exception {
        final String url =
                mPermissionRule.getURLWithHostName(
                        "test.example.com", "/chrome/test/data/android/cookie.html");

        mPermissionRule.loadUrl(url);
        Assert.assertEquals("\"\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
        mPermissionRule.runJavaScriptCodeInCurrentTab("setCookie(\".example.com\")");
        mPermissionRule.runJavaScriptCodeInCurrentTab("setCookie(\".test.example.com\")");
        Assert.assertEquals(
                "\"Foo=Bar; Foo=Bar\"",
                mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));

        resetSite(WebsiteAddress.create("test.example.com"));

        // Load the page again and ensure the cookie is gone.
        mPermissionRule.loadUrl(url);
        Assert.assertEquals("\"\"", mPermissionRule.runJavaScriptCodeInCurrentTab("getCookie()"));
    }

    /**
     * Set the cookie content setting to allow through policy and ensure the correct radio buttons
     * are enabled.
     */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    @Policies.Add({@Policies.Item(key = "DefaultCookiesSetting", string = "1")})
    public void testDefaultCookiesSettingManagedAllow() throws Exception {
        checkDefaultCookiesSettingManaged(true);
        checkThirdPartyCookieBlockingManaged(false);
        // The ContentSetting is managed (and set to ALLOW) while ThirdPartyCookieBlocking is not
        // managed. This means that every button other than BLOCK is enabled.
        SettingsActivity settingsActivity =
                SiteSettingsTestUtils.startSiteSettingsCategory(
                        SiteSettingsCategory.Type.THIRD_PARTY_COOKIES);
        checkTriStateCookieToggleButtonState(
                settingsActivity, CookieControlsMode.OFF, ToggleButtonState.EnabledUnchecked);
        checkTriStateCookieToggleButtonState(
                settingsActivity,
                CookieControlsMode.INCOGNITO_ONLY,
                ToggleButtonState.EnabledChecked);
        checkTriStateCookieToggleButtonState(
                settingsActivity,
                CookieControlsMode.BLOCK_THIRD_PARTY,
                ToggleButtonState.EnabledUnchecked);
        // TODO(crbug.com/40064993): fix this assertion.
        // onView(getManagedViewMatcher(/* activeView= */ true)).check(matches(isDisplayed()));
        onView(getManagedViewMatcher(/* activeView= */ false)).check(matches(not(isDisplayed())));
        settingsActivity.finish();
    }

    /**
     * Set the cookie content setting to allow through policy, disable incognito mode and ensure the
     * correct radio buttons are enabled.
     */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    @Policies.Add({@Policies.Item(key = "DefaultCookiesSetting", string = "1")})
    public void testDefaultCookiesSettingManagedAllowWithIncognitoDisabled() throws Exception {
        IncognitoUtils.setEnabledForTesting(false);
        setTriStateCookieToggle(CookieControlsMode.INCOGNITO_ONLY);

        checkDefaultCookiesSettingManaged(true);
        checkThirdPartyCookieBlockingManaged(false);
        // The ContentSetting is managed (and set to ALLOW) while ThirdPartyCookieBlocking managed.
        // Cookie toggle is set to block third party incognito but since
        // incognito is disabled the button should be disabled and the allow
        // toggle should be checked.
        SettingsActivity settingsActivity =
                SiteSettingsTestUtils.startSiteSettingsCategory(
                        SiteSettingsCategory.Type.THIRD_PARTY_COOKIES);
        checkTriStateCookieToggleButtonState(
                settingsActivity, CookieControlsMode.OFF, ToggleButtonState.EnabledChecked);
        checkTriStateCookieToggleButtonState(
                settingsActivity, CookieControlsMode.INCOGNITO_ONLY, ToggleButtonState.Disabled);
        checkTriStateCookieToggleButtonState(
                settingsActivity,
                CookieControlsMode.BLOCK_THIRD_PARTY,
                ToggleButtonState.EnabledUnchecked);
        // TODO(crbug.com/40064993): fix this assertion.
        // onView(getManagedViewMatcher(/* activeView= */ true)).check(matches(isDisplayed()));
        onView(getManagedViewMatcher(/* activeView= */ false)).check(matches(not(isDisplayed())));
        settingsActivity.finish();
    }

    /**
     * Enable third-party cookie blocking through policy and ensure the correct radio buttons are
     * enabled.
     */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    @Policies.Add({@Policies.Item(key = "BlockThirdPartyCookies", string = "true")})
    public void testBlockThirdPartyCookiesManagedTrue() throws Exception {
        checkDefaultCookiesSettingManaged(false);
        checkThirdPartyCookieBlockingManaged(true);
        // ThirdPartyCookieBlocking is managed (and set to true) while the ContentSetting is not
        // managed. This means a user can choose only between BLOCK_THIRD_PARTY and BLOCK, so only
        // these should be enabled.
        SettingsActivity settingsActivity =
                SiteSettingsTestUtils.startSiteSettingsCategory(
                        SiteSettingsCategory.Type.THIRD_PARTY_COOKIES);
        checkTriStateCookieToggleButtonState(
                settingsActivity, CookieControlsMode.OFF, ToggleButtonState.Disabled);
        checkTriStateCookieToggleButtonState(
                settingsActivity, CookieControlsMode.INCOGNITO_ONLY, ToggleButtonState.Disabled);
        checkTriStateCookieToggleButtonState(
                settingsActivity,
                CookieControlsMode.BLOCK_THIRD_PARTY,
                ToggleButtonState.EnabledChecked);
        onView(getManagedViewMatcher(/* activeView= */ true)).check(matches(isDisplayed()));
        onView(getManagedViewMatcher(/* activeView= */ false)).check(matches(not(isDisplayed())));
        settingsActivity.finish();
    }

    /**
     * Disable third-party cookie blocking through policy and ensure the correct radio buttons are
     * enabled.
     */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    @Policies.Add({@Policies.Item(key = "BlockThirdPartyCookies", string = "false")})
    public void testBlockThirdPartyCookiesManagedFalse() throws Exception {
        checkDefaultCookiesSettingManaged(false);
        checkThirdPartyCookieBlockingManaged(true);
        // ThirdPartyCookieBlocking is managed (and set to false) while the ContentSetting is not
        // managed. This means a user can only choose to ALLOW all cookies or BLOCK all cookies, so
        // only these should be enabled.
        SettingsActivity settingsActivity =
                SiteSettingsTestUtils.startSiteSettingsCategory(
                        SiteSettingsCategory.Type.THIRD_PARTY_COOKIES);
        checkTriStateCookieToggleButtonState(
                settingsActivity, CookieControlsMode.OFF, ToggleButtonState.EnabledChecked);
        checkTriStateCookieToggleButtonState(
                settingsActivity, CookieControlsMode.INCOGNITO_ONLY, ToggleButtonState.Disabled);
        checkTriStateCookieToggleButtonState(
                settingsActivity, CookieControlsMode.BLOCK_THIRD_PARTY, ToggleButtonState.Disabled);
        onView(getManagedViewMatcher(/* activeView= */ true)).check(matches(isDisplayed()));
        onView(getManagedViewMatcher(/* activeView= */ false)).check(matches(not(isDisplayed())));
        settingsActivity.finish();
    }

    /**
     * Set both the cookie content setting and third-party cookie blocking through policy and ensure
     * the correct radio buttons are enabled.
     */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    @Policies.Add({
        @Policies.Item(key = "DefaultCookiesSetting", string = "1"),
        @Policies.Item(key = "BlockThirdPartyCookies", string = "false")
    })
    public void testAllCookieSettingsManaged() throws Exception {
        checkDefaultCookiesSettingManaged(true);
        checkThirdPartyCookieBlockingManaged(true);
        // The ContentSetting and ThirdPartyCookieBlocking are managed. This means a user has a
        // fixed setting for cookies that they cannot change. Therefore, all buttons except the
        // selected one should be disabled.
        SettingsActivity settingsActivity =
                SiteSettingsTestUtils.startSiteSettingsCategory(
                        SiteSettingsCategory.Type.THIRD_PARTY_COOKIES);
        checkTriStateCookieToggleButtonState(
                settingsActivity, CookieControlsMode.OFF, ToggleButtonState.EnabledChecked);
        checkTriStateCookieToggleButtonState(
                settingsActivity, CookieControlsMode.INCOGNITO_ONLY, ToggleButtonState.Disabled);
        checkTriStateCookieToggleButtonState(
                settingsActivity, CookieControlsMode.BLOCK_THIRD_PARTY, ToggleButtonState.Disabled);
        onView(getManagedViewMatcher(/* activeView= */ true)).check(matches(isDisplayed()));
        onView(getManagedViewMatcher(/* activeView= */ false)).check(matches(not(isDisplayed())));
        settingsActivity.finish();
    }

    /** Ensure no radio buttons are enforced when cookie settings are unmanaged. */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testNoCookieSettingsManaged() throws Exception {
        checkDefaultCookiesSettingManaged(false);
        checkThirdPartyCookieBlockingManaged(false);
        // The ContentSetting and ThirdPartyCookieBlocking are unmanaged. This means all buttons
        // should be enabled.
        SettingsActivity settingsActivity =
                SiteSettingsTestUtils.startSiteSettingsCategory(
                        SiteSettingsCategory.Type.THIRD_PARTY_COOKIES);
        checkTriStateCookieToggleButtonState(
                settingsActivity, CookieControlsMode.OFF, ToggleButtonState.EnabledUnchecked);
        checkTriStateCookieToggleButtonState(
                settingsActivity,
                CookieControlsMode.INCOGNITO_ONLY,
                ToggleButtonState.EnabledChecked);
        checkTriStateCookieToggleButtonState(
                settingsActivity,
                CookieControlsMode.BLOCK_THIRD_PARTY,
                ToggleButtonState.EnabledUnchecked);
        onView(getManagedViewMatcher(/* activeView= */ true)).check(matches(not(isDisplayed())));
        onView(getManagedViewMatcher(/* activeView= */ false)).check(matches(not(isDisplayed())));
        settingsActivity.finish();
    }

    /** Ensure no radio buttons are enforced when cookie settings are unmanaged. */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testNoCookieSettingsManagedWithIncognitoDisabled() throws Exception {
        IncognitoUtils.setEnabledForTesting(false);
        checkDefaultCookiesSettingManaged(false);
        checkThirdPartyCookieBlockingManaged(false);
        // The ContentSetting and ThirdPartyCookieBlocking are unmanaged. This means all buttons
        // should be enabled.
        SettingsActivity settingsActivity =
                SiteSettingsTestUtils.startSiteSettingsCategory(
                        SiteSettingsCategory.Type.THIRD_PARTY_COOKIES);
        checkTriStateCookieToggleButtonState(
                settingsActivity, CookieControlsMode.OFF, ToggleButtonState.EnabledChecked);
        checkTriStateCookieToggleButtonState(
                settingsActivity, CookieControlsMode.INCOGNITO_ONLY, ToggleButtonState.Disabled);
        checkTriStateCookieToggleButtonState(
                settingsActivity,
                CookieControlsMode.BLOCK_THIRD_PARTY,
                ToggleButtonState.EnabledUnchecked);
        onView(getManagedViewMatcher(/* activeView= */ true)).check(matches(not(isDisplayed())));
        onView(getManagedViewMatcher(/* activeView= */ false)).check(matches(not(isDisplayed())));
        settingsActivity.finish();
    }

    private void resetSite(WebsiteAddress address) {
        Website website = new Website(address, address);
        final SettingsActivity settingsActivity =
                SiteSettingsTestUtils.startSingleWebsitePreferences(website);
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    SingleWebsiteSettings websitePreferences =
                            (SingleWebsiteSettings) settingsActivity.getMainFragment();
                    websitePreferences.resetSite();
                });
        settingsActivity.finish();
    }

    private void resetGroup(List<WebsiteAddress> addresses) {
        List<Website> sites = new ArrayList<>();
        for (WebsiteAddress address : addresses) {
            Website website = new Website(address, address);
            sites.add(website);
        }
        WebsiteGroup group = new WebsiteGroup(addresses.get(0).getDomainAndRegistry(), sites);
        final SettingsActivity settingsActivity =
                SiteSettingsTestUtils.startGroupedWebsitesPreferences(group);
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    GroupedWebsitesSettings websitePreferences =
                            (GroupedWebsitesSettings) settingsActivity.getMainFragment();
                    websitePreferences.resetGroup();
                });
        settingsActivity.finish();
    }

    /** Sets Allow Popups Enabled to be false and make sure it is set correctly. */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testPopupsBlocked() throws TimeoutException {
        new TwoStatePermissionTestCase(
                        "Popups",
                        SiteSettingsCategory.Type.POPUPS,
                        ContentSettingsType.POPUPS,
                        false)
                .run();

        // Test that the popup doesn't open.
        mPermissionRule.setUpUrl("/chrome/test/data/android/popup.html");
        mPermissionRule.runJavaScriptCodeInCurrentTab("openPopup();");
        InstrumentationRegistry.getInstrumentation().waitForIdleSync();

        Assert.assertEquals(1, getTabCount());
    }

    /** Sets Allow Popups Enabled to be true and make sure it is set correctly. */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testPopupsNotBlocked() throws TimeoutException {
        new TwoStatePermissionTestCase(
                        "Popups",
                        SiteSettingsCategory.Type.POPUPS,
                        ContentSettingsType.POPUPS,
                        true)
                .run();

        // Test that a popup opens.
        mPermissionRule.setUpUrl("/chrome/test/data/android/popup.html");
        mPermissionRule.runJavaScriptCodeInCurrentTab("openPopup();");
        InstrumentationRegistry.getInstrumentation().waitForIdleSync();

        Assert.assertEquals(2, getTabCount());
    }

    /** Test that showing the Site Settings menu doesn't crash (crbug.com/610576). */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testSiteSettingsMenu() {
        final SettingsActivity settingsActivity = SiteSettingsTestUtils.startSiteSettingsMenu("");
        settingsActivity.finish();
    }

    /**
     * Test that showing the Site Settings menu contains the "Third-party cookies" and "Site data"
     * rows.
     */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testSiteSettingsMenuWithPSS4Enabled() {
        final SettingsActivity settingsActivity = SiteSettingsTestUtils.startSiteSettingsMenu("");
        SiteSettings websitePreferences = (SiteSettings) settingsActivity.getMainFragment();
        assertNull(websitePreferences.findPreference("cookies"));
        assertNotNull(websitePreferences.findPreference("third_party_cookies"));
        assertNotNull(websitePreferences.findPreference("site_data"));
        settingsActivity.finish();
    }

    /** Test that showing the Site Settings menu contains the "Tracking protection" row. */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    @EnableFeatures(ChromeFeatureList.TRACKING_PROTECTION_3PCD)
    public void testSiteSettingsMenuWithTrackingProtectionEnabled() {
        final SettingsActivity settingsActivity = SiteSettingsTestUtils.startSiteSettingsMenu("");
        SiteSettings websitePreferences = (SiteSettings) settingsActivity.getMainFragment();
        assertNotNull(websitePreferences.findPreference("tracking_protection"));
        settingsActivity.finish();
    }

    /** Test that showing the Site Settings menu does not contain the "Anti-abuse" row. */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    @DisableFeatures(ChromeFeatureList.PRIVATE_STATE_TOKENS)
    public void testSiteSettingsMenuWithPrivateStateTokensDisabled() {
        final SettingsActivity settingsActivity = SiteSettingsTestUtils.startSiteSettingsMenu("");
        SiteSettings websitePreferences = (SiteSettings) settingsActivity.getMainFragment();
        assertNull(websitePreferences.findPreference("anti_abuse"));
        settingsActivity.finish();
    }

    /** Test that showing the Site Settings menu contains the "Anti-abuse" row. */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    @EnableFeatures(ChromeFeatureList.PRIVATE_STATE_TOKENS)
    public void testSiteSettingsMenuWithPrivateStateTokensEnabled() {
        final SettingsActivity settingsActivity = SiteSettingsTestUtils.startSiteSettingsMenu("");
        SiteSettings websitePreferences = (SiteSettings) settingsActivity.getMainFragment();
        assertNotNull(websitePreferences.findPreference("anti_abuse"));
        settingsActivity.finish();
    }

    /**
     * Tests that only expected Preferences are shown for a category. This santiy checks the number
     * of categories only. Each category has its own individual test below.
     */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesShown() {
        // If you add a category in the SiteSettings UI, please update this total AND add a test for
        // it below, named "testOnlyExpectedPreferences<Category>".
        Assert.assertEquals(32, SiteSettingsCategory.Type.NUM_ENTRIES);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesZoom() {
        checkPreferencesForCategory(SiteSettingsCategory.Type.ZOOM, NULL_ARRAY);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesAllSites() {
        checkPreferencesForCategory(SiteSettingsCategory.Type.ALL_SITES, CLEAR_BROWSING_DATA_LINK);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesADS() {
        testExpectedPreferences(SiteSettingsCategory.Type.ADS, BINARY_TOGGLE, BINARY_TOGGLE);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    @EnableFeatures(ChromeFeatureList.PRIVATE_STATE_TOKENS)
    public void testOnlyExpectedPreferencesAntiAbuse() {
        testExpectedPreferences(
                SiteSettingsCategory.Type.ANTI_ABUSE,
                BINARY_TOGGLE_WITH_ANTI_ABUSE_PREF_KEYS,
                BINARY_TOGGLE_WITH_ANTI_ABUSE_PREF_KEYS);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesAugmentedReality() {
        testExpectedPreferences(
                SiteSettingsCategory.Type.AUGMENTED_REALITY, BINARY_TOGGLE, BINARY_TOGGLE);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesAutoDarkWebContent() {
        testExpectedPreferences(
                SiteSettingsCategory.Type.AUTO_DARK_WEB_CONTENT, BINARY_TOGGLE, BINARY_TOGGLE);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesAutomaticDownloads() {
        testExpectedPreferences(
                SiteSettingsCategory.Type.AUTOMATIC_DOWNLOADS,
                BINARY_TOGGLE_WITH_EXCEPTION,
                BINARY_TOGGLE);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesBackgroundSync() {
        testExpectedPreferences(
                SiteSettingsCategory.Type.BACKGROUND_SYNC,
                BINARY_TOGGLE_WITH_EXCEPTION,
                BINARY_TOGGLE);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesBluetooth() {
        testExpectedPreferences(SiteSettingsCategory.Type.BLUETOOTH, BINARY_TOGGLE, BINARY_TOGGLE);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesBluetoothScanning() {
        testExpectedPreferences(
                SiteSettingsCategory.Type.BLUETOOTH_SCANNING, BINARY_TOGGLE, BINARY_TOGGLE);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesCamera() {
        testExpectedPreferences(SiteSettingsCategory.Type.CAMERA, BINARY_TOGGLE, BINARY_TOGGLE);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesClipboard() {
        testExpectedPreferences(SiteSettingsCategory.Type.CLIPBOARD, BINARY_TOGGLE, BINARY_TOGGLE);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesThirdPartyCookies() {
        testExpectedPreferences(
                SiteSettingsCategory.Type.THIRD_PARTY_COOKIES,
                new String[] {"info_text", "tri_state_cookie_toggle", "add_exception"},
                new String[] {"info_text", "tri_state_cookie_toggle"});
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesSiteData() {
        testExpectedPreferences(
                SiteSettingsCategory.Type.SITE_DATA,
                BINARY_TOGGLE_WITH_EXCEPTION_AND_INFO_TEXT,
                BINARY_TOGGLE_WITH_EXCEPTION_AND_INFO_TEXT);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedExceptionsSiteData() {
        createCookieExceptions();
        SiteSettingsTestUtils.startSiteSettingsCategory(SiteSettingsCategory.Type.SITE_DATA);

        onView(withText("primary.com")).check(matches(isDisplayed()));
        onView(withText("secondary.com")).check(doesNotExist());
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedExceptionsThirdPartyCookies() {
        createCookieExceptions();
        SiteSettingsTestUtils.startSiteSettingsCategory(
                SiteSettingsCategory.Type.THIRD_PARTY_COOKIES);

        onView(withText("primary.com")).check(doesNotExist());
        onView(withText("secondary.com")).check(matches(isDisplayed()));
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesStorageAccess() {
        testExpectedPreferences(
                SiteSettingsCategory.Type.STORAGE_ACCESS,
                BINARY_TOGGLE_AND_INFO_TEXT,
                BINARY_TOGGLE_AND_INFO_TEXT);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testExpectedExceptionsStorageAccess() {
        createStorageAccessExceptions();
        SiteSettingsTestUtils.startSiteSettingsCategory(SiteSettingsCategory.Type.STORAGE_ACCESS);

        onView(withText("primary.com")).check(matches(isDisplayed()));
        onView(withText("2 sites")).check(matches(isDisplayed()));
        onView(withText("primary2.com")).check(matches(isDisplayed()));
        onView(withText("1 site")).check(matches(isDisplayed()));

        getImageViewWidget("primary.com").check(matches(isDisplayed())).perform(click());

        // Check that the subpage is shown with the correct origins.
        onView(withText("primary.com")).check(matches(isDisplayed()));
        onViewWaiting(withText("secondary.com")).check(matches(isDisplayed()));
        onViewWaiting(withText("secondary3.com")).check(matches(isDisplayed()));
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testResetExceptionGroupStorageAccess() {
        createStorageAccessExceptions();
        SiteSettingsTestUtils.startSiteSettingsCategory(SiteSettingsCategory.Type.STORAGE_ACCESS);

        onView(withText("primary.com")).check(matches(isDisplayed()));
        onView(withText("2 sites")).check(matches(isDisplayed()));
        onView(withText("primary2.com")).check(matches(isDisplayed()));
        onView(withText("1 site")).check(matches(isDisplayed()));

        onView(withText("primary.com")).perform(click());
        onView(withText("Remove")).perform(click());

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    assertEquals(
                            ContentSettingValues.ASK,
                            WebsitePreferenceBridge.getContentSetting(
                                    getBrowserContextHandle(),
                                    ContentSettingsType.STORAGE_ACCESS,
                                    new GURL("https://primary.com"),
                                    new GURL("https://secondary.com")));
                    assertEquals(
                            ContentSettingValues.ASK,
                            WebsitePreferenceBridge.getContentSetting(
                                    getBrowserContextHandle(),
                                    ContentSettingsType.STORAGE_ACCESS,
                                    new GURL("https://primary.com"),
                                    new GURL("https://secondary3.com")));
                    assertEquals(
                            ContentSettingValues.ALLOW,
                            WebsitePreferenceBridge.getContentSetting(
                                    getBrowserContextHandle(),
                                    ContentSettingsType.STORAGE_ACCESS,
                                    new GURL("https://primary2.com"),
                                    new GURL("https://secondary2.com")));
                });

        onView(withText("primary.com")).check(doesNotExist());
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testBlockExceptionGroupStorageAccess() {
        createStorageAccessExceptions();
        SiteSettingsTestUtils.startSiteSettingsCategory(SiteSettingsCategory.Type.STORAGE_ACCESS);

        onView(withText("primary.com")).check(matches(isDisplayed()));
        onView(withText("2 sites")).check(matches(isDisplayed()));
        onView(withText("primary2.com")).check(matches(isDisplayed()));
        onView(withText("1 site")).check(matches(isDisplayed()));

        onView(withText("primary.com")).perform(click());
        onView(withText("Block")).perform(click());

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    assertEquals(
                            ContentSettingValues.BLOCK,
                            WebsitePreferenceBridge.getContentSetting(
                                    getBrowserContextHandle(),
                                    ContentSettingsType.STORAGE_ACCESS,
                                    new GURL("https://primary.com"),
                                    new GURL("https://secondary.com")));
                    assertEquals(
                            ContentSettingValues.BLOCK,
                            WebsitePreferenceBridge.getContentSetting(
                                    getBrowserContextHandle(),
                                    ContentSettingsType.STORAGE_ACCESS,
                                    new GURL("https://primary.com"),
                                    new GURL("https://secondary3.com")));
                    assertEquals(
                            ContentSettingValues.ALLOW,
                            WebsitePreferenceBridge.getContentSetting(
                                    getBrowserContextHandle(),
                                    ContentSettingsType.STORAGE_ACCESS,
                                    new GURL("https://primary2.com"),
                                    new GURL("https://secondary2.com")));
                });
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testStorageAccessSubpage() {
        createStorageAccessExceptions();
        final SettingsActivity settingsActivity =
                SiteSettingsTestUtils.startStorageAccessSettingsActivity(getStorageAccessSite());

        onViewWaiting(withText("secondary1.com")).check(matches(isDisplayed()));
        onViewWaiting(withText("secondary3.com")).check(matches(isDisplayed()));

        // Reset first permission.
        getImageViewWidget("secondary1.com").check(matches(isDisplayed())).perform(click());

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    assertEquals(
                            ContentSettingValues.ASK,
                            WebsitePreferenceBridge.getContentSetting(
                                    getBrowserContextHandle(),
                                    ContentSettingsType.STORAGE_ACCESS,
                                    new GURL("https://primary.com"),
                                    new GURL("https://secondary1.com")));
                    assertEquals(
                            ContentSettingValues.ALLOW,
                            WebsitePreferenceBridge.getContentSetting(
                                    getBrowserContextHandle(),
                                    ContentSettingsType.STORAGE_ACCESS,
                                    new GURL("https://primary.com"),
                                    new GURL("https://secondary3.com")));
                });

        waitForViewCheckingState(
                withText("secondary1.com"), VIEW_INVISIBLE | VIEW_NULL | VIEW_GONE);
        onView(withText("secondary1.com")).check(doesNotExist());
        onView(withText("secondary3.com")).check(matches(isDisplayed()));

        // Reset second permission.
        getImageViewWidget("secondary3.com").check(matches(isDisplayed())).perform(click());

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    assertEquals(
                            ContentSettingValues.ASK,
                            WebsitePreferenceBridge.getContentSetting(
                                    getBrowserContextHandle(),
                                    ContentSettingsType.STORAGE_ACCESS,
                                    new GURL("https://primary.com"),
                                    new GURL("https://secondary3.com")));
                });

        // Check that, because there aren't any permissions to show, the activity is closed.
        Assert.assertTrue(settingsActivity.isFinishing());
    }

    private ViewInteraction getImageViewWidget(String preferenceTitle) {
        return onView(
                allOf(
                        withId(R.id.image_view_widget),
                        isDescendantOfA(withChild(withChild(withText(preferenceTitle))))));
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    @EnableFeatures(ChromeFeatureList.PRIVACY_SANDBOX_FPS_UI)
    public void testExpectedCookieButtonsCheckedWhenFPSUiEnabled() {
        SettingsActivity settingsActivity =
                SiteSettingsTestUtils.startSiteSettingsCategory(
                        SiteSettingsCategory.Type.THIRD_PARTY_COOKIES);

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    SingleCategorySettings preferences =
                            (SingleCategorySettings) settingsActivity.getMainFragment();
                    TriStateCookieSettingsPreference cookieToggle =
                            preferences.findPreference(
                                    SingleCategorySettings.TRI_STATE_COOKIE_TOGGLE);

                    clickButtonAndVerifyItsChecked(cookieToggle, CookieControlsMode.OFF);
                    clickButtonAndVerifyItsChecked(cookieToggle, CookieControlsMode.INCOGNITO_ONLY);
                    clickButtonAndVerifyItsChecked(
                            cookieToggle, CookieControlsMode.BLOCK_THIRD_PARTY);
                });

        settingsActivity.finish();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    @EnableFeatures({ChromeFeatureList.PRIVACY_SANDBOX_FPS_UI})
    public void testExpectedCookieButtonsCheckedWhenFPSUiAndPSS4Enabled() {
        SettingsActivity settingsActivity =
                SiteSettingsTestUtils.startSiteSettingsCategory(
                        SiteSettingsCategory.Type.THIRD_PARTY_COOKIES);

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    SingleCategorySettings preferences =
                            (SingleCategorySettings) settingsActivity.getMainFragment();
                    TriStateCookieSettingsPreference threeStateCookieToggle =
                            preferences.findPreference(
                                    SingleCategorySettings.TRI_STATE_COOKIE_TOGGLE);

                    clickButtonAndVerifyItsChecked(threeStateCookieToggle, CookieControlsMode.OFF);
                    clickButtonAndVerifyItsChecked(
                            threeStateCookieToggle, CookieControlsMode.INCOGNITO_ONLY);
                    clickButtonAndVerifyItsChecked(
                            threeStateCookieToggle, CookieControlsMode.BLOCK_THIRD_PARTY);
                });

        settingsActivity.finish();
    }

    private void clickButtonAndVerifyItsChecked(
            TriStateCookieSettingsPreference threeStateCookieToggle,
            @CookieControlsMode int state) {
        threeStateCookieToggle.getButton(state).performClick();
        Assert.assertTrue(
                "Button should be checked.", threeStateCookieToggle.getButton(state).isChecked());
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    @DisableFeatures(ChromeFeatureList.PERMISSION_DEDICATED_CPSS_SETTING_ANDROID)
    public void testOnlyExpectedPreferencesDeviceLocation() {
        LocationSettingsTestUtil.setSystemLocationSettingEnabled(true);

        testExpectedPreferences(
                SiteSettingsCategory.Type.DEVICE_LOCATION, BINARY_TOGGLE, BINARY_TOGGLE);

        // Disable system location setting and check for the right preferences.
        LocationSettingsTestUtil.setSystemLocationSettingEnabled(false);
        checkPreferencesForCategory(
                SiteSettingsCategory.Type.DEVICE_LOCATION, BINARY_TOGGLE_WITH_OS_WARNING_EXTRA);
        LocationSettingsTestUtil.setSystemLocationSettingEnabled(true);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesFederatedIdentityAPI() {
        testExpectedPreferences(
                SiteSettingsCategory.Type.FEDERATED_IDENTITY_API,
                BINARY_TOGGLE_WITH_EXCEPTION,
                BINARY_TOGGLE_WITH_EXCEPTION);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesHandTracking() {
        testExpectedPreferences(
                SiteSettingsCategory.Type.HAND_TRACKING, BINARY_TOGGLE, BINARY_TOGGLE);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesIdleDetection() {
        testExpectedPreferences(
                SiteSettingsCategory.Type.IDLE_DETECTION, BINARY_TOGGLE, BINARY_TOGGLE);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesJavascript() {
        testExpectedPreferences(
                SiteSettingsCategory.Type.JAVASCRIPT,
                BINARY_TOGGLE_WITH_EXCEPTION,
                BINARY_TOGGLE_WITH_EXCEPTION);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesMicrophone() {
        testExpectedPreferences(SiteSettingsCategory.Type.MICROPHONE, BINARY_TOGGLE, BINARY_TOGGLE);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesNFC() {
        NfcSystemLevelSetting.setNfcSettingForTesting(true);

        testExpectedPreferences(SiteSettingsCategory.Type.NFC, BINARY_TOGGLE, BINARY_TOGGLE);

        // Disable system nfc setting and check for the right preferences.
        NfcSystemLevelSetting.setNfcSettingForTesting(false);
        checkPreferencesForCategory(
                SiteSettingsCategory.Type.NFC, BINARY_TOGGLE_WITH_OS_WARNING_EXTRA);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    @EnableFeatures("QuietNotificationPrompts")
    @DisableFeatures(ChromeFeatureList.PERMISSION_DEDICATED_CPSS_SETTING_ANDROID)
    public void testOnlyExpectedPreferencesNotifications() {
        String[] notifications_enabled;
        String[] notifications_disabled;
        // The "notifications_vibrate" option has been removed in Android O but is present in
        // earlier versions.
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
            notifications_enabled =
                    new String[] {
                        "binary_toggle", "notifications_quiet_ui", "notifications_vibrate"
                    };
            notifications_disabled = new String[] {"binary_toggle", "notifications_vibrate"};
        } else {
            notifications_enabled = new String[] {"binary_toggle", "notifications_quiet_ui"};
            notifications_disabled = BINARY_TOGGLE;
        }

        testExpectedPreferences(
                SiteSettingsCategory.Type.NOTIFICATIONS,
                notifications_disabled,
                notifications_enabled);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesPopups() {
        testExpectedPreferences(SiteSettingsCategory.Type.POPUPS, BINARY_TOGGLE, BINARY_TOGGLE);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesProtectedMedia() {
        String[] protectedMedia = new String[] {"tri_state_toggle", "protected_content_learn_more"};
        setGlobalTriStateToggleForCategory(
                SiteSettingsCategory.Type.PROTECTED_MEDIA, ContentSettingValues.ALLOW);
        checkPreferencesForCategory(SiteSettingsCategory.Type.PROTECTED_MEDIA, protectedMedia);
        setGlobalTriStateToggleForCategory(
                SiteSettingsCategory.Type.PROTECTED_MEDIA, ContentSettingValues.ASK);
        checkPreferencesForCategory(SiteSettingsCategory.Type.PROTECTED_MEDIA, protectedMedia);
        setGlobalTriStateToggleForCategory(
                SiteSettingsCategory.Type.PROTECTED_MEDIA, ContentSettingValues.BLOCK);
        checkPreferencesForCategory(SiteSettingsCategory.Type.PROTECTED_MEDIA, protectedMedia);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesRequestDesktopSite() {
        String[] rdsEnabled = {"binary_toggle", "desktop_site_window", "add_exception"};
        testExpectedPreferences(
                SiteSettingsCategory.Type.REQUEST_DESKTOP_SITE,
                BINARY_TOGGLE_WITH_EXCEPTION,
                rdsEnabled);
        Assert.assertTrue(
                "SharedPreference USER_ENABLED_DESKTOP_SITE_GLOBAL_SETTING_PREFERENCE_KEY should be"
                        + " updated.",
                ContextUtils.getAppSharedPreferences()
                        .contains(
                                SingleCategorySettingsConstants
                                        .USER_ENABLED_DESKTOP_SITE_GLOBAL_SETTING_PREFERENCE_KEY));
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesSensors() {
        testExpectedPreferences(SiteSettingsCategory.Type.SENSORS, BINARY_TOGGLE, BINARY_TOGGLE);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesSound() {
        testExpectedPreferences(
                SiteSettingsCategory.Type.SOUND,
                BINARY_TOGGLE_WITH_EXCEPTION,
                BINARY_TOGGLE_WITH_EXCEPTION);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesUSB() {
        testExpectedPreferences(SiteSettingsCategory.Type.USB, BINARY_TOGGLE, BINARY_TOGGLE);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesUseStorage() {
        checkPreferencesForCategory(SiteSettingsCategory.Type.USE_STORAGE, NULL_ARRAY);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testOnlyExpectedPreferencesVirtualReality() {
        testExpectedPreferences(
                SiteSettingsCategory.Type.VIRTUAL_REALITY, BINARY_TOGGLE, BINARY_TOGGLE);
    }

    /** Tests system NFC support in Preferences. */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testSystemNfcSupport() {
        // Disable system nfc support and check for the right preferences.
        NfcSystemLevelSetting.setNfcSupportForTesting(false);
        checkPreferencesForCategory(
                SiteSettingsCategory.Type.NFC, BINARY_TOGGLE_WITH_OS_WARNING_EXTRA);
    }

    /**
     * Tests that {@link SingleWebsiteSettings#resetSite} doesn't crash (see e.g. the crash on host
     * names in issue 600232).
     */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testResetDoesntCrash() {
        WebsiteAddress address = WebsiteAddress.create("example.com");
        resetSite(address);
    }

    /**
     * Sets Allow Camera Enabled to be false and make sure it is set correctly.
     *
     * @throws Exception
     */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    @CommandLineFlags.Add(ContentSwitches.USE_FAKE_DEVICE_FOR_MEDIA_STREAM)
    public void testCameraBlocked() throws Exception {
        new TwoStatePermissionTestCase(
                        "Camera",
                        SiteSettingsCategory.Type.CAMERA,
                        ContentSettingsType.MEDIASTREAM_CAMERA,
                        false)
                .run();

        // Test that the camera permission doesn't get requested.
        initializeUpdateWaiter(/* expectGranted= */ false);
        mPermissionRule.runNoPromptTest(
                mPermissionUpdateWaiter,
                "/content/test/data/media/getusermedia.html",
                "getUserMediaAndStopLegacy({video: true, audio: false});",
                0,
                /* withGesture= */ true,
                /* isDialog= */ true);
    }

    /**
     * Sets Allow Camera Enabled to be true and make sure it is set correctly.
     *
     * @throws Exception
     */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    @CommandLineFlags.Add({ContentSwitches.USE_FAKE_DEVICE_FOR_MEDIA_STREAM})
    public void testCameraNotBlocked() throws Exception {
        new TwoStatePermissionTestCase(
                        "Camera",
                        SiteSettingsCategory.Type.CAMERA,
                        ContentSettingsType.MEDIASTREAM_CAMERA,
                        true)
                .run();

        initializeUpdateWaiter(/* expectGranted= */ true);
        mPermissionRule.runAllowTest(
                mPermissionUpdateWaiter,
                "/content/test/data/media/getusermedia.html",
                "getUserMediaAndStopLegacy({video: true, audio: false});",
                0,
                /* withGesture= */ true,
                /* isDialog= */ true);
    }

    /**
     * Sets Allow Mic Enabled to be false and make sure it is set correctly.
     *
     * @throws Exception
     */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    @CommandLineFlags.Add({ContentSwitches.USE_FAKE_DEVICE_FOR_MEDIA_STREAM})
    public void testMicBlocked() throws Exception {
        new TwoStatePermissionTestCase(
                        "Mic",
                        SiteSettingsCategory.Type.MICROPHONE,
                        ContentSettingsType.MEDIASTREAM_MIC,
                        false)
                .run();

        // Test that the microphone permission doesn't get requested.
        initializeUpdateWaiter(/* expectGranted= */ false);
        mPermissionRule.runNoPromptTest(
                mPermissionUpdateWaiter,
                "/content/test/data/media/getusermedia.html",
                "getUserMediaAndStopLegacy({video: false, audio: true});",
                0,
                true,
                true);
    }

    /**
     * Sets Allow Mic Enabled to be true and make sure it is set correctly.
     *
     * @throws Exception
     */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    @CommandLineFlags.Add({ContentSwitches.USE_FAKE_DEVICE_FOR_MEDIA_STREAM})
    public void testMicNotBlocked() throws Exception {
        new TwoStatePermissionTestCase(
                        "Mic",
                        SiteSettingsCategory.Type.MICROPHONE,
                        ContentSettingsType.MEDIASTREAM_MIC,
                        true)
                .run();

        // Launch a page that uses the microphone and make sure a permission prompt shows up.
        initializeUpdateWaiter(/* expectGranted= */ true);
        mPermissionRule.runAllowTest(
                mPermissionUpdateWaiter,
                "/content/test/data/media/getusermedia.html",
                "getUserMediaAndStopLegacy({video: false, audio: true});",
                0,
                true,
                true);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testAllowBackgroundSync() {
        new TwoStatePermissionTestCase(
                        "BackgroundSync",
                        SiteSettingsCategory.Type.BACKGROUND_SYNC,
                        ContentSettingsType.BACKGROUND_SYNC,
                        true)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testBlockBackgroundSync() {
        new TwoStatePermissionTestCase(
                        "BackgroundSync",
                        SiteSettingsCategory.Type.BACKGROUND_SYNC,
                        ContentSettingsType.BACKGROUND_SYNC,
                        false)
                .withExpectedPrefKeys(SingleCategorySettings.ADD_EXCEPTION_KEY)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testAllowUsb() {
        new TwoStatePermissionTestCase(
                        "USB", SiteSettingsCategory.Type.USB, ContentSettingsType.USB_GUARD, true)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testBlockUsb() {
        new TwoStatePermissionTestCase(
                        "USB", SiteSettingsCategory.Type.USB, ContentSettingsType.USB_GUARD, false)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testAllowAutomaticDownloads() {
        new TwoStatePermissionTestCase(
                        "AutomaticDownloads",
                        SiteSettingsCategory.Type.AUTOMATIC_DOWNLOADS,
                        ContentSettingsType.AUTOMATIC_DOWNLOADS,
                        true)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testBlockAutomaticDownloads() {
        new TwoStatePermissionTestCase(
                        "AutomaticDownloads",
                        SiteSettingsCategory.Type.AUTOMATIC_DOWNLOADS,
                        ContentSettingsType.AUTOMATIC_DOWNLOADS,
                        false)
                .withExpectedPrefKeys(SingleCategorySettings.ADD_EXCEPTION_KEY)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testAllowBluetoothScanning() {
        new TwoStatePermissionTestCase(
                        "BluetoothScanning",
                        SiteSettingsCategory.Type.BLUETOOTH_SCANNING,
                        ContentSettingsType.BLUETOOTH_SCANNING,
                        true)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testBlockBluetoothScanning() {
        new TwoStatePermissionTestCase(
                        "BluetoothScanning",
                        SiteSettingsCategory.Type.BLUETOOTH_SCANNING,
                        ContentSettingsType.BLUETOOTH_SCANNING,
                        false)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testAllowBluetoothGuard() {
        new TwoStatePermissionTestCase(
                        "BluetoothGuard",
                        SiteSettingsCategory.Type.BLUETOOTH,
                        ContentSettingsType.BLUETOOTH_GUARD,
                        true)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testBlockBluetoothGuard() {
        new TwoStatePermissionTestCase(
                        "BluetoothGuard",
                        SiteSettingsCategory.Type.BLUETOOTH,
                        ContentSettingsType.BLUETOOTH_GUARD,
                        false)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testAllowNfc() {
        NfcSystemLevelSetting.setNfcSettingForTesting(true);
        new TwoStatePermissionTestCase(
                        "NFC", SiteSettingsCategory.Type.NFC, ContentSettingsType.NFC, true)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testBlockNfc() {
        NfcSystemLevelSetting.setNfcSettingForTesting(true);
        new TwoStatePermissionTestCase(
                        "NFC", SiteSettingsCategory.Type.NFC, ContentSettingsType.NFC, false)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testAllowAntiAbuse() {
        new TwoStatePermissionTestCase(
                        "AntiAbuse",
                        SiteSettingsCategory.Type.ANTI_ABUSE,
                        ContentSettingsType.ANTI_ABUSE,
                        true)
                .withExpectedPrefKeys(ANTI_ABUSE_PREF_KEYS)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testBlockAntiAbuse() {
        new TwoStatePermissionTestCase(
                        "AntiAbuse",
                        SiteSettingsCategory.Type.ANTI_ABUSE,
                        ContentSettingsType.ANTI_ABUSE,
                        false)
                .withExpectedPrefKeys(ANTI_ABUSE_PREF_KEYS)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testAllowAr() {
        new TwoStatePermissionTestCase(
                        "AR",
                        SiteSettingsCategory.Type.AUGMENTED_REALITY,
                        ContentSettingsType.AR,
                        true)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testBlockAr() {
        new TwoStatePermissionTestCase(
                        "AR",
                        SiteSettingsCategory.Type.AUGMENTED_REALITY,
                        ContentSettingsType.AR,
                        false)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testAllowVr() {
        new TwoStatePermissionTestCase(
                        "VR",
                        SiteSettingsCategory.Type.VIRTUAL_REALITY,
                        ContentSettingsType.VR,
                        true)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testBlockVr() {
        new TwoStatePermissionTestCase(
                        "VR",
                        SiteSettingsCategory.Type.VIRTUAL_REALITY,
                        ContentSettingsType.VR,
                        false)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testAllowHandTracking() {
        new TwoStatePermissionTestCase(
                        "HandTracking",
                        SiteSettingsCategory.Type.HAND_TRACKING,
                        ContentSettingsType.HAND_TRACKING,
                        true)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testBlockHandTracking() {
        new TwoStatePermissionTestCase(
                        "HandTracking",
                        SiteSettingsCategory.Type.HAND_TRACKING,
                        ContentSettingsType.HAND_TRACKING,
                        false)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testAllowIdleDetection() {
        new TwoStatePermissionTestCase(
                        "IdleDetection",
                        SiteSettingsCategory.Type.IDLE_DETECTION,
                        ContentSettingsType.IDLE_DETECTION,
                        true)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testBlockIdleDetection() {
        new TwoStatePermissionTestCase(
                        "IdleDetection",
                        SiteSettingsCategory.Type.IDLE_DETECTION,
                        ContentSettingsType.IDLE_DETECTION,
                        false)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testAllowAutoDark() {
        final String histogramName = "Android.DarkTheme.AutoDarkMode.SettingsChangeSource.Enabled";
        final int preTestCount =
                RecordHistogram.getHistogramValueCountForTesting(
                        histogramName, SITE_SETTINGS_GLOBAL);
        new TwoStatePermissionTestCase(
                        "AutoDarkWebContent",
                        SiteSettingsCategory.Type.AUTO_DARK_WEB_CONTENT,
                        ContentSettingsType.AUTO_DARK_WEB_CONTENT,
                        true)
                .run();
        Assert.assertEquals(
                "<" + histogramName + "> should be recorded for SITE_SETTINGS_GLOBAL.",
                preTestCount + 1,
                RecordHistogram.getHistogramValueCountForTesting(
                        histogramName, SITE_SETTINGS_GLOBAL));
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testBlockAutoDark() {
        final String histogramName = "Android.DarkTheme.AutoDarkMode.SettingsChangeSource.Disabled";
        final int preTestCount =
                RecordHistogram.getHistogramValueCountForTesting(
                        histogramName, SITE_SETTINGS_GLOBAL);
        new TwoStatePermissionTestCase(
                        "AutoDarkWebContent",
                        SiteSettingsCategory.Type.AUTO_DARK_WEB_CONTENT,
                        ContentSettingsType.AUTO_DARK_WEB_CONTENT,
                        false)
                .run();
        Assert.assertEquals(
                "<" + histogramName + "> should be recorded for SITE_SETTINGS_GLOBAL.",
                preTestCount + 1,
                RecordHistogram.getHistogramValueCountForTesting(
                        histogramName, SITE_SETTINGS_GLOBAL));
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testAllowRequestDesktopSite() {
        new TwoStatePermissionTestCase(
                        "RequestDesktopSite",
                        SiteSettingsCategory.Type.REQUEST_DESKTOP_SITE,
                        ContentSettingsType.REQUEST_DESKTOP_SITE,
                        true)
                .withExpectedPrefKeys(SingleCategorySettings.DESKTOP_SITE_WINDOW_TOGGLE_KEY)
                .withExpectedPrefKeys(SingleCategorySettings.ADD_EXCEPTION_KEY)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testBlockRequestDesktopSite() {
        new TwoStatePermissionTestCase(
                        "RequestDesktopSite",
                        SiteSettingsCategory.Type.REQUEST_DESKTOP_SITE,
                        ContentSettingsType.REQUEST_DESKTOP_SITE,
                        false)
                .withExpectedPrefKeys(SingleCategorySettings.ADD_EXCEPTION_KEY)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testAllowFederatedIdentityApi() {
        new TwoStatePermissionTestCase(
                        "FederatedIdentityApi",
                        SiteSettingsCategory.Type.FEDERATED_IDENTITY_API,
                        ContentSettingsType.FEDERATED_IDENTITY_API,
                        true)
                .withExpectedPrefKeys(SingleCategorySettings.ADD_EXCEPTION_KEY)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testBlockFederatedIdentityApi() {
        new TwoStatePermissionTestCase(
                        "FederatedIdentityApi",
                        SiteSettingsCategory.Type.FEDERATED_IDENTITY_API,
                        ContentSettingsType.FEDERATED_IDENTITY_API,
                        false)
                .withExpectedPrefKeys(SingleCategorySettings.ADD_EXCEPTION_KEY)
                .run();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    @DisableIf.Build(
            message = "Flaky, see crbug.com/1170671",
            sdk_is_less_than = Build.VERSION_CODES.Q)
    // Auto does not have actions to handle ACTION_CHANNEL_NOTIFICATION_SETTINGS
    @Restriction(DeviceRestriction.RESTRICTION_TYPE_NON_AUTO)
    public void testEmbargoedNotificationSiteSettings() throws Exception {
        final String url =
                mPermissionRule.getURLWithHostName(
                        "example.com", "/chrome/test/data/notifications/notification_tester.html");

        triggerEmbargoForOrigin(url);

        SettingsLauncher settingsLauncher = SettingsLauncherFactory.createSettingsLauncher();
        Context context = ApplicationProvider.getApplicationContext();
        Intent intent =
                settingsLauncher.createSettingsActivityIntent(
                        context,
                        SingleWebsiteSettings.class,
                        SingleWebsiteSettings.createFragmentArgsForSite(url));
        final SettingsActivity settingsActivity =
                (SettingsActivity)
                        InstrumentationRegistry.getInstrumentation().startActivitySync(intent);

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    final SingleWebsiteSettings websitePreferences =
                            (SingleWebsiteSettings) settingsActivity.getMainFragment();

                    final Preference notificationPreference =
                            websitePreferences.findPreference("push_notifications_list");

                    Assert.assertEquals(
                            context.getString(R.string.automatically_blocked),
                            notificationPreference.getSummary());

                    websitePreferences.launchOsChannelSettingsFromPreference(
                            notificationPreference);

                    // Ensure that a proper separate channel has indeed been created to allow the
                    // user to alter the setting.
                    Assert.assertNotEquals(
                            ChromeChannelDefinitions.ChannelId.SITES,
                            SiteChannelsManager.getInstance()
                                    .getChannelIdForOrigin(Origin.createOrThrow(url).toString()));
                });
        // Close the OS notification settings UI.
        UiAutomatorUtils.getInstance().pressBack();
        settingsActivity.finish();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    @DisabledTest(message = "https://crbug.com/1094934")
    public void testEmbargoedNotificationCategorySiteSettings() throws Exception {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
        final String urlToEmbargo =
                mPermissionRule.getURLWithHostName(
                        "example.com", "/chrome/test/data/notifications/notification_tester.html");

        triggerEmbargoForOrigin(urlToEmbargo);

        final String urlToBlock =
                mPermissionRule.getURLWithHostName(
                        "exampleToBlock.com",
                        "/chrome/test/data/notifications/notification_tester.html");

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    WebsitePreferenceBridgeJni.get()
                            .setPermissionSettingForOrigin(
                                    getBrowserContextHandle(),
                                    ContentSettingsType.NOTIFICATIONS,
                                    urlToBlock,
                                    urlToBlock,
                                    ContentSettingValues.BLOCK);
                });

        final SettingsActivity settingsActivity =
                SiteSettingsTestUtils.startSiteSettingsCategory(
                        SiteSettingsCategory.Type.NOTIFICATIONS);

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    boolean blockedByEmbargo =
                            WebsitePreferenceBridgeJni.get()
                                    .isNotificationEmbargoedForOrigin(
                                            getBrowserContextHandle(), urlToEmbargo);
                    Assert.assertTrue(blockedByEmbargo);

                    final String blockedGroupKey = "blocked_group";
                    // Click on Blocked group in Category Settings. By default Blocked is closed, to
                    // be able to find any origins inside, Blocked should be opened.
                    SingleCategorySettings websitePreferences =
                            (SingleCategorySettings) settingsActivity.getMainFragment();
                    websitePreferences.findPreference(blockedGroupKey).performClick();

                    // After triggering onClick on Blocked group, all UI will be discarded and
                    // reinitialized from scratch. Init all variables again, otherwise it will use
                    // stale information.
                    websitePreferences =
                            (SingleCategorySettings) settingsActivity.getMainFragment();
                    ExpandablePreferenceGroup blockedGroup =
                            (ExpandablePreferenceGroup)
                                    websitePreferences.findPreference(blockedGroupKey);

                    Assert.assertTrue(blockedGroup.isExpanded());
                    // Only |url| has been added under embargo.
                    Assert.assertEquals(2, blockedGroup.getPreferenceCount());

                    Assert.assertEquals(
                            ApplicationProvider.getApplicationContext()
                                    .getString(R.string.automatically_blocked),
                            blockedGroup.getPreference(0).getSummary());

                    // Blocked origin should has no summary.
                    assertNull(blockedGroup.getPreference(1).getSummary());
                });
        settingsActivity.finish();
    }

    /**
     * Test that embargoing federated identity permission displays "Automatically Blocked" message
     * in page info UI. Federated identity is a content setting. Content settings use a different
     * code path than permissions (like notifications).
     */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testEmbargoedFederatedIdentity() throws Exception {
        final String rpUrl =
                mPermissionRule.getURLWithHostName(
                        "example.com", "/chrome/test/data/android/simple.html");

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    FederatedIdentityTestUtils.embargoFedCmForRelyingParty(new GURL(rpUrl));
                });

        SettingsLauncher settingsLauncher = SettingsLauncherFactory.createSettingsLauncher();
        Context context = ApplicationProvider.getApplicationContext();
        Intent intent =
                settingsLauncher.createSettingsActivityIntent(
                        context,
                        SingleWebsiteSettings.class,
                        SingleWebsiteSettings.createFragmentArgsForSite(rpUrl));
        final SettingsActivity settingsActivity =
                (SettingsActivity)
                        InstrumentationRegistry.getInstrumentation().startActivitySync(intent);

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    final SingleWebsiteSettings websitePreferences =
                            (SingleWebsiteSettings) settingsActivity.getMainFragment();
                    final Preference fedCmPreference =
                            websitePreferences.findPreference("federated_identity_api_list");

                    Assert.assertEquals(
                            context.getString(R.string.automatically_blocked),
                            fedCmPreference.getSummary());
                });
        settingsActivity.finish();
    }

    @Test
    @MediumTest
    @Feature({"Preferences"})
    @DisableIf.Build(
            message = "https://crbug.com/1414569",
            sdk_is_greater_than = Build.VERSION_CODES.N_MR1,
            sdk_is_less_than = Build.VERSION_CODES.P)
    public void testProtectedContentDefaultOption() throws Exception {
        initializeUpdateWaiter(/* expectGranted= */ true);
        mPermissionRule.runNoPromptTest(
                mPermissionUpdateWaiter,
                "/content/test/data/android/eme_permissions.html",
                "requestEME()",
                0,
                true,
                true);
    }

    @Test
    @MediumTest
    @Feature({"Preferences"})
    @DisableIf.Build(
            message = "https://crbug.com/1414569",
            sdk_is_greater_than = Build.VERSION_CODES.N_MR1,
            sdk_is_less_than = Build.VERSION_CODES.P)
    public void testProtectedContentAskAllow() throws Exception {
        setGlobalTriStateToggleForCategory(
                SiteSettingsCategory.Type.PROTECTED_MEDIA, ContentSettingValues.ASK);

        initializeUpdateWaiter(/* expectGranted= */ true);
        mPermissionRule.runAllowTest(
                mPermissionUpdateWaiter,
                "/content/test/data/android/eme_permissions.html",
                "requestEME()",
                0,
                true,
                true);
    }

    @Test
    @MediumTest
    @Feature({"Preferences"})
    @DisableIf.Build(
            message = "https://crbug.com/1414569",
            sdk_is_greater_than = Build.VERSION_CODES.N_MR1,
            sdk_is_less_than = Build.VERSION_CODES.P)
    public void testProtectedContentAskBlocked() throws Exception {
        setGlobalTriStateToggleForCategory(
                SiteSettingsCategory.Type.PROTECTED_MEDIA, ContentSettingValues.ASK);

        initializeUpdateWaiter(/* expectGranted= */ false);
        mPermissionRule.runDenyTest(
                mPermissionUpdateWaiter,
                "/content/test/data/android/eme_permissions.html",
                "requestEME()",
                0,
                true,
                true);
    }

    @Test
    @MediumTest
    @Feature({"Preferences"})
    @DisableIf.Build(
            message = "https://crbug.com/1414569",
            sdk_is_greater_than = Build.VERSION_CODES.N_MR1,
            sdk_is_less_than = Build.VERSION_CODES.P)
    public void testProtectedContentBlocked() throws Exception {
        setGlobalTriStateToggleForCategory(
                SiteSettingsCategory.Type.PROTECTED_MEDIA, ContentSettingValues.BLOCK);

        initializeUpdateWaiter(/* expectGranted= */ false);
        mPermissionRule.runNoPromptTest(
                mPermissionUpdateWaiter,
                "/content/test/data/android/eme_permissions.html",
                "requestEME()",
                0,
                true,
                true);
    }

    @Test
    @MediumTest
    @Feature({"Preferences"})
    @DisableIf.Build(
            message = "https://crbug.com/1269556,https://crbug.com/1414569",
            sdk_is_greater_than = Build.VERSION_CODES.N_MR1)
    @DisableIf.Device(type = {UiDisableIf.TABLET}) // https://crbug.com/1234530
    public void testProtectedContentAllowThenBlock() throws Exception {
        initializeUpdateWaiter(/* expectGranted= */ true);
        mPermissionRule.runNoPromptTest(
                mPermissionUpdateWaiter,
                "/content/test/data/android/eme_permissions.html",
                "requestEME()",
                0,
                true,
                true);

        setGlobalTriStateToggleForCategory(
                SiteSettingsCategory.Type.PROTECTED_MEDIA, ContentSettingValues.BLOCK);

        initializeUpdateWaiter(/* expectGranted= */ false);
        mPermissionRule.runNoPromptTest(
                mPermissionUpdateWaiter,
                "/content/test/data/android/eme_permissions.html",
                "requestEME()",
                0,
                true,
                true);
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    public void testDesktopSiteWindowSettings() {
        final SettingsActivity settingsActivity =
                SiteSettingsTestUtils.startSiteSettingsCategory(
                        SiteSettingsCategory.Type.REQUEST_DESKTOP_SITE);

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    HistogramWatcher histogramExpectation =
                            HistogramWatcher.newSingleRecordWatcher(
                                    "Android.RequestDesktopSite.WindowSettingChanged", true);
                    SingleCategorySettings preferences =
                            (SingleCategorySettings) settingsActivity.getMainFragment();
                    // Window setting is only available when the Global Setting is ON.
                    ChromeSwitchPreference toggle =
                            preferences.findPreference(SingleCategorySettings.BINARY_TOGGLE_KEY);
                    preferences.onPreferenceChange(toggle, true);

                    ChromeBaseCheckBoxPreference windowSettingPref =
                            preferences.findPreference(
                                    SingleCategorySettings.DESKTOP_SITE_WINDOW_TOGGLE_KEY);
                    PrefService prefService = UserPrefs.get(getBrowserContextHandle());
                    preferences.onPreferenceChange(windowSettingPref, true);
                    Assert.assertTrue(
                            "Window setting should be ON.",
                            prefService.getBoolean(DESKTOP_SITE_WINDOW_SETTING_ENABLED));
                    histogramExpectation.assertExpected();

                    preferences.onPreferenceChange(windowSettingPref, false);
                    Assert.assertFalse(
                            "Window setting should be OFF.",
                            prefService.getBoolean(DESKTOP_SITE_WINDOW_SETTING_ENABLED));
                });
        settingsActivity.finish();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    @EnableFeatures(ChromeFeatureList.SAFETY_HUB)
    public void testAutorevokePermissionsSwitch() {
        HistogramWatcher histogramExpectation =
                HistogramWatcher.newSingleRecordWatcher(
                        SiteSettings.PERMISSION_AUTOREVOCATION_HISTOGRAM_NAME, true);
        // Set the initial toggle state.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    UserPrefs.get(getBrowserContextHandle())
                            .setBoolean(Pref.UNUSED_SITE_PERMISSIONS_REVOCATION_ENABLED, false);
                });

        final SettingsActivity settingsActivity = SiteSettingsTestUtils.startSiteSettingsMenu("");

        // Scroll to permission autorevocation preference and click it.
        onView(withId(R.id.recycler_view))
                .perform(
                        RecyclerViewActions.scrollTo(
                                hasDescendant(
                                        withText(
                                                R.string.safety_hub_autorevocation_toggle_title))));
        onView(withText(R.string.safety_hub_autorevocation_toggle_title))
                .check(matches(not(isChecked())))
                .perform(click());

        // Verify that the pref has been correctly set.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    Assert.assertTrue(
                            "Unused site permission revocation should be enabled.",
                            UserPrefs.get(getBrowserContextHandle())
                                    .getBoolean(Pref.UNUSED_SITE_PERMISSIONS_REVOCATION_ENABLED));
                });
        histogramExpectation.assertExpected();

        settingsActivity.finish();
    }

    @Test
    @SmallTest
    @Feature({"Preferences"})
    @DisableFeatures(ChromeFeatureList.SAFETY_HUB)
    public void testAutorevokePermissionsSwitchNotDisplayed() {
        final SettingsActivity settingsActivity = SiteSettingsTestUtils.startSiteSettingsMenu("");
        final SiteSettings siteSettingsFragment = (SiteSettings) settingsActivity.getMainFragment();
        // Verify that the preference does not exist.
        assertNull(
                siteSettingsFragment.findPreference(SiteSettings.PERMISSION_AUTOREVOCATION_PREF));
        settingsActivity.finish();
    }

    private void renderSettingsPage(SettingsActivity settingsActivity, String name)
            throws IOException {
        View view = settingsActivity.findViewById(android.R.id.content).getRootView();
        ChromeRenderTestRule.sanitize(view);
        mRenderTestRule.render(view, name);
        settingsActivity.finish();
    }

    private void renderCategoryPage(@SiteSettingsCategory.Type int category, String name)
            throws IOException {
        var settingsActivity = SiteSettingsTestUtils.startSiteSettingsCategory(category);
        renderSettingsPage(settingsActivity, name);
    }

    @Test
    @SmallTest
    @Feature({"RenderTest"})
    public void testRenderStorageAccessPage() throws Exception {
        createStorageAccessExceptions();
        renderCategoryPage(
                SiteSettingsCategory.Type.STORAGE_ACCESS, "site_settings_storage_access_page");
    }

    @Test
    @SmallTest
    @Feature({"RenderTest"})
    public void testRenderStorageAccessSubpage() throws Exception {
        createStorageAccessExceptions();
        final SettingsActivity settingsActivity =
                SiteSettingsTestUtils.startStorageAccessSettingsActivity(getStorageAccessSite());
        renderSettingsPage(settingsActivity, "site_settings_storage_access_subpage");
    }

    @Test
    @SmallTest
    @Feature({"RenderTest"})
    public void testRenderSiteDataPage() throws Exception {
        createCookieExceptions();
        renderCategoryPage(SiteSettingsCategory.Type.SITE_DATA, "site_settings_site_data_page");
    }

    @Test
    @SmallTest
    @Feature({"RenderTest"})
    @DisableFeatures(ChromeFeatureList.PRIVACY_SANDBOX_FPS_UI)
    public void testRenderThirdPartyCookiesPage() throws Exception {
        createCookieExceptions();
        renderCategoryPage(
                SiteSettingsCategory.Type.THIRD_PARTY_COOKIES,
                "site_settings_third_party_cookies_page");
    }

    @Test
    @SmallTest
    @Feature({"RenderTest"})
    @EnableFeatures({ChromeFeatureList.PRIVACY_SANDBOX_FPS_UI})
    public void testRenderThirdPartyCookiesPageWithFPS() throws Exception {
        createCookieExceptions();
        renderCategoryPage(
                SiteSettingsCategory.Type.THIRD_PARTY_COOKIES,
                "site_settings_third_party_cookies_page_fps");
    }

    @Test
    @SmallTest
    @Feature({"RenderTest"})
    @DisableFeatures({ChromeFeatureList.PRIVACY_SANDBOX_FPS_UI})
    public void testRenderCookiesPageThirdPartyCookiesPageWithoutFPS() throws Exception {
        createCookieExceptions();
        renderCategoryPage(
                SiteSettingsCategory.Type.THIRD_PARTY_COOKIES,
                "site_settings_third_party_cookies_page_without_fps");
    }

    @Test
    @SmallTest
    @Feature({"RenderTest"})
    @EnableFeatures(ChromeFeatureList.PRIVACY_SANDBOX_FPS_UI)
    public void testRenderCookiesPageWithFPS() throws Exception {
        createCookieExceptions();
        renderCategoryPage(
                SiteSettingsCategory.Type.THIRD_PARTY_COOKIES, "site_settings_cookies_page_fps");
    }

    @Test
    @SmallTest
    @Feature({"RenderTest"})
    @DisableFeatures(ChromeFeatureList.PERMISSION_DEDICATED_CPSS_SETTING_ANDROID)
    public void testRenderLocationPage() throws Exception {
        createCookieExceptions();
        renderCategoryPage(
                SiteSettingsCategory.Type.DEVICE_LOCATION, "site_settings_location_page");
    }

    @Test
    @SmallTest
    @Feature({"RenderTest"})
    public void testRenderProtectedMediaPage() throws Exception {
        createCookieExceptions();
        renderCategoryPage(
                SiteSettingsCategory.Type.PROTECTED_MEDIA, "site_settings_protected_media_page");
    }

    /** Test case for checking that settings with binary toggles are disabled by policy. */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    @Policies.Add({
        @Policies.Item(key = "DefaultJavaScriptSetting", string = "2"),
        @Policies.Item(key = "DefaultPopupsSetting", string = "2"),
        @Policies.Item(key = "DefaultGeolocationSetting", string = "2")
    })
    public void testAllTwoStateToggleDisabledByPolicy() {
        testTwoStateToggleDisabledByPolicy(SiteSettingsCategory.Type.JAVASCRIPT);
        testTwoStateToggleDisabledByPolicy(SiteSettingsCategory.Type.POPUPS);
        testTwoStateToggleDisabledByPolicy(SiteSettingsCategory.Type.DEVICE_LOCATION);
        // TODO(crbug.com/40879457): add a test for sensors once crash in the sensors settings page
        // is
        // resolved.
    }

    public void testTwoStateToggleDisabledByPolicy(@SiteSettingsCategory.Type int type) {
        final SettingsActivity settingsActivity =
                SiteSettingsTestUtils.startSiteSettingsCategory(type);
        SingleCategorySettings singleCategorySettings =
                (SingleCategorySettings) settingsActivity.getMainFragment();
        ChromeSwitchPreference binaryToggle =
                (ChromeSwitchPreference)
                        singleCategorySettings.findPreference(
                                SingleCategorySettings.BINARY_TOGGLE_KEY);

        Assert.assertFalse(binaryToggle.isEnabled());
    }

    /**
     * Allows third party cookies for a website, and tests that the UI shows a managed preference in
     * the allowed group. Checks that it shows the toast when the preference is clicked.
     */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    @Policies.Add({
        @Policies.Item(key = "CookiesAllowedForUrls", string = "[\"[*.]chromium.org\"]")
    })
    public void testAllowCookiesForURL() throws Exception {
        testCookiesSettingsManagedForURL(SingleCategorySettings.ALLOWED_GROUP);
    }

    /**
     * Blocks third party cookies for a website, and tests that the UI shows a managed preference in
     * the blocked group. Checks that it shows toast when the preference is clicked.
     */
    @Test
    @SmallTest
    @Feature({"Preferences"})
    @Policies.Add({
        @Policies.Item(key = "CookiesBlockedForUrls", string = "[\"[*.]chromium.org\"]")
    })
    public void testBlockCookiesForURL() throws Exception {
        testCookiesSettingsManagedForURL(SingleCategorySettings.BLOCKED_GROUP);
    }

    public void testCookiesSettingsManagedForURL(String setting) throws Exception {
        final SettingsActivity settingsActivity =
                SiteSettingsTestUtils.startSiteSettingsCategory(
                        SiteSettingsCategory.Type.SITE_DATA);

        SingleCategorySettings websitePreferences =
                (SingleCategorySettings) settingsActivity.getMainFragment();
        ExpandablePreferenceGroup managedGroup =
                (ExpandablePreferenceGroup) websitePreferences.findPreference(setting);
        Assert.assertTrue("The blocked group should be expanded.", managedGroup.isExpanded());
        Assert.assertEquals(
                "The blocked expandable group should have exactly one website listed.",
                1,
                managedGroup.getPreferenceCount());
        ChromeImageViewPreference websitePreference =
                (ChromeImageViewPreference) managedGroup.getPreference(0);

        /*
         * Swipes to the end of the screen to show the website preference for the blocked site
         * then checks that the content description and the summary text reflect the managed state.
         */
        onView(ViewMatchers.withId(android.R.id.content)).perform(swipeUp());
        onData(withKey(setting))
                .inAdapterView(
                        allOf(
                                withContentDescription(R.string.managed_by_your_organization),
                                withText(R.string.managed_by_your_organization),
                                isDisplayed()));

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    websitePreference.performClick();
                });
        onView(withText(R.string.managed_by_your_organization))
                .inRoot(withDecorView(allOf(withId(R.id.toast_text))))
                .check(matches(isDisplayed()));
    }

    static class PermissionTestCase {
        protected final String mTestName;
        protected final @SiteSettingsCategory.Type int mSiteSettingsType;
        protected final @ContentSettingsType.EnumType int mContentSettingsType;
        protected final boolean mIsCategoryEnabled;
        protected final List<String> mExpectedPreferenceKeys;

        protected SettingsActivity mSettingsActivity;

        PermissionTestCase(
                final String testName,
                @SiteSettingsCategory.Type final int siteSettingsType,
                @ContentSettingsType.EnumType final int contentSettingsType,
                final boolean enabled) {
            mTestName = testName;
            mSiteSettingsType = siteSettingsType;
            mContentSettingsType = contentSettingsType;
            mIsCategoryEnabled = enabled;

            mExpectedPreferenceKeys = new ArrayList<>();
        }

        /** Set extra expected pref keys for category settings screen. */
        PermissionTestCase withExpectedPrefKeys(String expectedPrefKeys) {
            mExpectedPreferenceKeys.add(expectedPrefKeys);
            return this;
        }

        PermissionTestCase withExpectedPrefKeys(String[] expectedPrefKeys) {
            mExpectedPreferenceKeys.addAll(Arrays.asList(expectedPrefKeys));
            return this;
        }

        public void run() {
            mSettingsActivity = SiteSettingsTestUtils.startSiteSettingsCategory(mSiteSettingsType);
            ThreadUtils.runOnUiThreadBlocking(
                    () -> {
                        SingleCategorySettings singleCategorySettings =
                                (SingleCategorySettings) mSettingsActivity.getMainFragment();

                        doTest(singleCategorySettings);
                    });
            mSettingsActivity.finish();
        }

        protected void doTest(SingleCategorySettings singleCategorySettings) {
            assertPreferenceOnScreen(singleCategorySettings, mExpectedPreferenceKeys);
        }

        protected void assertPreferenceOnScreen(
                SingleCategorySettings singleCategorySettings, List<String> expectedKeys) {
            PreferenceScreen preferenceScreen = singleCategorySettings.getPreferenceScreen();
            int preferenceCount = preferenceScreen.getPreferenceCount();

            ArrayList<String> actualKeys = new ArrayList<>();
            for (int index = 0; index < preferenceCount; index++) {
                Preference preference = preferenceScreen.getPreference(index);
                String key = preference.getKey();
                // Not all Preferences have keys. For example, the list of websites below the
                // toggles, which are dynamically added. Ignore those.
                if (key != null && preference.isVisible()) actualKeys.add(key);
            }

            Assert.assertEquals(
                    actualKeys.toString() + " should match " + expectedKeys.toString(),
                    expectedKeys,
                    actualKeys);
        }
    }

    /** Test case for site settings with a global toggle. */
    static class TwoStatePermissionTestCase extends PermissionTestCase {
        TwoStatePermissionTestCase(
                String testName, int siteSettingsType, int contentSettingsType, boolean enabled) {
            super(testName, siteSettingsType, contentSettingsType, enabled);

            mExpectedPreferenceKeys.add(SingleCategorySettings.BINARY_TOGGLE_KEY);
        }

        @Override
        public void doTest(SingleCategorySettings singleCategorySettings) {
            // Verify toggle related checks first as they may affect the preferences on the screen.
            assertToggleTitleAndSummary(singleCategorySettings);
            assertGlobalToggleForCategory(singleCategorySettings);

            super.doTest(singleCategorySettings);
        }

        /** Verify {@link SingleCategorySettings} is wired correctly. */
        private void assertGlobalToggleForCategory(SingleCategorySettings singleCategorySettings) {
            final String exceptionString =
                    "Test <"
                            + mTestName
                            + ">: Content setting category <"
                            + mContentSettingsType
                            + "> should be "
                            + (mIsCategoryEnabled ? "enabled" : "disabled")
                            + " with Site Settings <"
                            + mSiteSettingsType
                            + ">.";

            ChromeSwitchPreference toggle =
                    singleCategorySettings.findPreference(SingleCategorySettings.BINARY_TOGGLE_KEY);
            assertNotNull("Toggle should not be null.", toggle);

            singleCategorySettings.onPreferenceChange(toggle, mIsCategoryEnabled);
            Assert.assertEquals(
                    exceptionString,
                    mIsCategoryEnabled,
                    WebsitePreferenceBridge.isCategoryEnabled(
                            getBrowserContextHandle(), mContentSettingsType));
        }

        /** Verfiy {@link ContentSettingsResources} is set correctly. */
        private void assertToggleTitleAndSummary(SingleCategorySettings singleCategorySettings) {
            ChromeSwitchPreference toggle =
                    singleCategorySettings.findPreference(SingleCategorySettings.BINARY_TOGGLE_KEY);
            assert toggle != null;

            Assert.assertEquals(
                    "Preference title is not set correctly.",
                    singleCategorySettings
                            .getResources()
                            .getString(ContentSettingsResources.getTitle(mContentSettingsType)),
                    toggle.getTitle());
            assertNotNull("Enabled summary text should not be null.", toggle.getSummaryOn());
            assertNotNull("Disabled summary text should not be null.", toggle.getSummaryOff());

            String summary =
                    mIsCategoryEnabled
                            ? toggle.getSummaryOn().toString()
                            : toggle.getSummaryOff().toString();
            String expected =
                    singleCategorySettings
                            .getResources()
                            .getString(
                                    mIsCategoryEnabled
                                            ? ContentSettingsResources.getEnabledSummary(
                                                    mContentSettingsType)
                                            : ContentSettingsResources.getDisabledSummary(
                                                    mContentSettingsType));
            Assert.assertEquals(
                    "Summary text in state <" + mIsCategoryEnabled + "> does not match.",
                    expected,
                    summary);
        }
    }
}