Commit f384aa73 authored by Ben Schwartz's avatar Ben Schwartz Committed by Commit Bot

Secure DNS UI for Android

This replicates the desktop UI, with platform-appropriate adjustments.

Bug: 1040146
Change-Id: I33cea7dd37086d87580e149404ed3619a828ee45
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2240456Reviewed-by: default avatarPeter Conn <peconn@chromium.org>
Reviewed-by: default avatarNatalie Chouinard <chouinard@chromium.org>
Commit-Queue: Ben Schwartz <bemasc@chromium.org>
Auto-Submit: Ben Schwartz <bemasc@chromium.org>
Cr-Commit-Position: refs/heads/master@{#782049}
parent 10d15b44
......@@ -1002,6 +1002,8 @@ chrome_java_resources = [
"java/res/layout/search_engine.xml",
"java/res/layout/search_engine_recent_title.xml",
"java/res/layout/search_widget_template.xml",
"java/res/layout/secure_dns_provider_preference.xml",
"java/res/layout/secure_dns_provider_spinner_item.xml",
"java/res/layout/send_tab_to_self_device_picker_item.xml",
"java/res/layout/send_tab_to_self_device_picker_list.xml",
"java/res/layout/send_tab_to_self_device_picker_toolbar.xml",
......@@ -1119,6 +1121,7 @@ chrome_java_resources = [
"java/res/xml/notifications_preferences.xml",
"java/res/xml/privacy_preferences.xml",
"java/res/xml/search_widget_info.xml",
"java/res/xml/secure_dns_settings.xml",
"java/res/xml/sync_and_services_preferences.xml",
"java/res/xml/theme_preferences.xml",
"java/res/xml/tracing_preferences.xml",
......
......@@ -1336,6 +1336,8 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/privacy/settings/DoNotTrackSettings.java",
"java/src/org/chromium/chrome/browser/privacy/settings/PrivacyPreferencesManager.java",
"java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java",
"java/src/org/chromium/chrome/browser/privacy/settings/SecureDnsProviderPreference.java",
"java/src/org/chromium/chrome/browser/privacy/settings/SecureDnsSettings.java",
"java/src/org/chromium/chrome/browser/provider/BaseColumns.java",
"java/src/org/chromium/chrome/browser/provider/BookmarkColumns.java",
"java/src/org/chromium/chrome/browser/provider/ChromeBrowserProvider.java",
......
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2020 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="vertical"
android:paddingBottom="8dp"
android:focusable="false">
<org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionLayout
android:id="@+id/mode_group"
android:layout_width="match_parent"
android:layout_height="match_parent">
<org.chromium.components.browser_ui.widget.RadioButtonWithDescription
android:id="@+id/automatic"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="@dimen/min_touch_target_size"
android:background="?attr/selectableItemBackground"
app:primaryText="@string/settings_automatic_mode_label"
app:descriptionText="@string/settings_automatic_mode_description"/>
<org.chromium.components.browser_ui.widget.RadioButtonWithDescription
android:id="@+id/secure"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="@dimen/min_touch_target_size"
android:background="?attr/selectableItemBackground"
app:primaryText="@string/settings_secure_dropdown_mode_description"/>
</org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionLayout>
<!-- This container is relocated into @id/secure as an accessory view. -->
<LinearLayout
android:id="@+id/selection_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:paddingStart="@dimen/radio_button_accessory_view_padding_start"
android:paddingEnd="@dimen/radio_button_accessory_view_padding_end"
android:gravity="start"
android:orientation="vertical"
android:focusable="false">
<Spinner
android:id="@+id/dropdown_spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="4dp"/>
<TextView
android:id="@+id/privacy_policy"
android:paddingStart="4dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.TextMedium.Secondary"
android:text="@string/settings_secure_dropdown_mode_privacy_policy"/>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/custom_server_layout"
android:labelFor="@id/custom_server"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
tools:ignore="LabelFor"
android:id="@+id/custom_server"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.TextLarge.Primary"
android:inputType="textUri"
android:imeOptions="actionDone"
android:hint="@string/settings_secure_dns_custom_placeholder"/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2020 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. -->
<!-- Copy of @layout/preference_spinner_single_line_item with padding removed. -->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
style="?android:attr/spinnerItemStyle"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.TextLarge.Primary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:textAlignment="inherit"
android:paddingStart="0dp"/>
......@@ -23,6 +23,10 @@
android:fragment="org.chromium.chrome.browser.privacy.settings.DoNotTrackSettings"
android:key="do_not_track"
android:title="@string/do_not_track_title" />
<org.chromium.components.browser_ui.settings.ChromeBasePreference
android:key="secure_dns"
android:title="@string/settings_secure_dns_title"
android:fragment="org.chromium.chrome.browser.privacy.settings.SecureDnsSettings" />
<Preference
android:key="clear_browsing_data"
android:title="@string/clear_browsing_data_title"
......
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2020 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. -->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<org.chromium.components.browser_ui.settings.ChromeSwitchPreference
android:key="secure_dns_switch"
android:persistent="false"
android:title="@string/settings_secure_dns_title"
android:summary="@string/settings_secure_dns_description" />
<org.chromium.chrome.browser.privacy.settings.SecureDnsProviderPreference
android:key="secure_dns_provider" />
</PreferenceScreen>
......@@ -9,6 +9,7 @@ import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import org.chromium.base.CommandLine;
......@@ -41,19 +42,19 @@ public class PrivacyPreferencesManager implements CrashReportingPermissionManage
* for display in the UI.
*/
static class DohEntry {
final String mName; // Display name
final String mTemplate; // URI template, or "" for the custom entry.
final String mPrivacy; // Privacy policy link
public final @NonNull String name; // Display name
public final @NonNull String template; // URI template, or "" for the custom entry.
public final @NonNull String privacy; // Privacy policy link
DohEntry(String name, String template, String privacy) {
mName = name;
mTemplate = template;
mPrivacy = privacy;
this.name = name;
this.template = template;
this.privacy = privacy;
}
@Override
public String toString() {
return mName;
return name;
}
}
......@@ -368,7 +369,7 @@ public class PrivacyPreferencesManager implements CrashReportingPermissionManage
*
* @param mode The desired new Secure DNS mode.
*/
public void setSecureDnsModeMode(@SecureDnsMode int mode) {
public void setSecureDnsMode(@SecureDnsMode int mode) {
PrivacyPreferencesManagerJni.get().setSecureDnsMode(mode);
}
......@@ -392,6 +393,10 @@ public class PrivacyPreferencesManager implements CrashReportingPermissionManage
}
/**
* Get the raw preference value, which can represent multiple templates separated
* by whitespace. The raw value is needed in order to allow direct editing while
* preserving whitespace.
*
* @return The templates (separated by spaces) of the DoH server
* currently selected for use in "secure" mode, or "" if there is none.
*/
......@@ -425,7 +430,7 @@ public class PrivacyPreferencesManager implements CrashReportingPermissionManage
*/
public void updateDohDropdownHistograms(DohEntry oldEntry, DohEntry newEntry) {
PrivacyPreferencesManagerJni.get().updateDohDropdownHistograms(
oldEntry.mTemplate, newEntry.mTemplate);
oldEntry.template, newEntry.template);
}
/**
......
......@@ -19,6 +19,7 @@ import org.chromium.chrome.R;
import org.chromium.chrome.browser.help.HelpAndFeedback;
import org.chromium.chrome.browser.preferences.Pref;
import org.chromium.chrome.browser.preferences.PrefServiceBridge;
import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManager.DohEntry;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate;
import org.chromium.chrome.browser.settings.SettingsLauncher;
......@@ -28,9 +29,12 @@ import org.chromium.chrome.browser.usage_stats.UsageStatsConsentDialog;
import org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference;
import org.chromium.components.browser_ui.settings.ManagedPreferenceDelegate;
import org.chromium.components.browser_ui.settings.SettingsUtils;
import org.chromium.net.SecureDnsMode;
import org.chromium.ui.text.NoUnderlineClickableSpan;
import org.chromium.ui.text.SpanApplier;
import java.util.List;
/**
* Fragment to keep track of the all the privacy related preferences.
*/
......@@ -38,6 +42,7 @@ public class PrivacySettings
extends PreferenceFragmentCompat implements Preference.OnPreferenceChangeListener {
private static final String PREF_CAN_MAKE_PAYMENT = "can_make_payment";
private static final String PREF_NETWORK_PREDICTIONS = "preload_pages";
private static final String PREF_SECURE_DNS = "secure_dns";
private static final String PREF_USAGE_STATS = "usage_stats_reporting";
private static final String PREF_DO_NOT_TRACK = "do_not_track";
private static final String PREF_SYNC_AND_SERVICES_LINK = "sync_and_services_link";
......@@ -65,6 +70,9 @@ public class PrivacySettings
networkPredictionPref.setOnPreferenceChangeListener(this);
networkPredictionPref.setManagedPreferenceDelegate(mManagedPreferenceDelegate);
Preference secureDnsPref = findPreference(PREF_SECURE_DNS);
secureDnsPref.setVisible(privacyPrefManager.isDnsOverHttpsUiEnabled());
Preference syncAndServicesLink = findPreference(PREF_SYNC_AND_SERVICES_LINK);
NoUnderlineClickableSpan linkSpan = new NoUnderlineClickableSpan(getResources(), view -> {
SettingsLauncher settingsLauncher = new SettingsLauncherImpl();
......@@ -117,6 +125,31 @@ public class PrivacySettings
: R.string.text_off);
}
Preference secureDnsPref = findPreference(PREF_SECURE_DNS);
if (secureDnsPref != null && secureDnsPref.isVisible()) {
PrivacyPreferencesManager manager = PrivacyPreferencesManager.getInstance();
@SecureDnsMode
int mode = manager.getSecureDnsMode();
if (mode == SecureDnsMode.OFF) {
secureDnsPref.setSummary(R.string.text_off);
} else if (mode == SecureDnsMode.AUTOMATIC) {
secureDnsPref.setSummary(R.string.settings_automatic_mode_summary);
} else {
String templateGroup = manager.getDnsOverHttpsTemplates();
List<DohEntry> providers = manager.getDohProviders();
String serverName = templateGroup;
for (int i = 0; i < providers.size(); i++) {
DohEntry entry = providers.get(i);
if (entry.template.equals(templateGroup)) {
serverName = entry.name;
break;
}
}
secureDnsPref.setSummary(
String.format("%s - %s", getString(R.string.text_on), serverName));
}
}
Preference usageStatsPref = findPreference(PREF_USAGE_STATS);
if (usageStatsPref != null) {
if (BuildInfo.isAtLeastQ() && prefServiceBridge.getBoolean(Pref.USAGE_STATS_ENABLED)) {
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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.privacy.settings;
import android.os.Bundle;
import androidx.preference.PreferenceFragmentCompat;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.net.SecureDnsManagementMode;
import org.chromium.chrome.browser.privacy.settings.SecureDnsProviderPreference.State;
import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate;
import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
import org.chromium.components.browser_ui.settings.SettingsUtils;
import org.chromium.net.SecureDnsMode;
/**
* Fragment to manage Secure DNS preference. It consists of a toggle switch and,
* if the switch is enabled, a SecureDnsControl.
*/
public class SecureDnsSettings extends PreferenceFragmentCompat {
// Must match keys in secure_dns_settings.xml.
private static final String PREF_SECURE_DNS_SWITCH = "secure_dns_switch";
private static final String PREF_SECURE_DNS_PROVIDER = "secure_dns_provider";
private PrivacyPreferencesManager mManager;
private ChromeSwitchPreference mSecureDnsSwitch;
private SecureDnsProviderPreference mSecureDnsProviderPreference;
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
getActivity().setTitle(R.string.settings_secure_dns_title);
SettingsUtils.addPreferencesFromResource(this, R.xml.secure_dns_settings);
mManager = PrivacyPreferencesManager.getInstance();
// Set up preferences inside the activity.
mSecureDnsSwitch = (ChromeSwitchPreference) findPreference(PREF_SECURE_DNS_SWITCH);
mSecureDnsSwitch.setManagedPreferenceDelegate(
(ChromeManagedPreferenceDelegate) preference -> mManager.isSecureDnsModeManaged());
mSecureDnsSwitch.setOnPreferenceChangeListener((preference, enabled) -> {
storePreferenceState((boolean) enabled, mSecureDnsProviderPreference.getState());
loadPreferenceState();
return true;
});
if (!mManager.isSecureDnsModeManaged()) {
// If the mode isn't managed directly, we still need to disable the controls
// if we detect a managed system configuration, or any parental control software.
// However, we don't want to show the managed setting icon in this case, because the
// setting is not directly controlled by a policy.
@SecureDnsManagementMode
int managementMode = mManager.getSecureDnsManagementMode();
if (managementMode != SecureDnsManagementMode.NO_OVERRIDE) {
mSecureDnsSwitch.setEnabled(false);
boolean parentalControls =
managementMode == SecureDnsManagementMode.DISABLED_PARENTAL_CONTROLS;
mSecureDnsSwitch.setSummaryOff(parentalControls
? R.string.settings_secure_dns_disabled_for_parental_control
: R.string.settings_secure_dns_disabled_for_managed_environment);
}
}
mSecureDnsProviderPreference =
(SecureDnsProviderPreference) findPreference(PREF_SECURE_DNS_PROVIDER);
mSecureDnsProviderPreference.setOnPreferenceChangeListener((preference, value) -> {
State controlState = (State) value;
boolean valid = storePreferenceState(mSecureDnsSwitch.isChecked(), controlState);
if (valid != controlState.valid) {
mSecureDnsProviderPreference.setState(controlState.withValid(valid));
// Cancel the change to controlState.
return false;
}
return true;
});
// Update preference views and state.
loadPreferenceState();
}
/**
* @param enabled Whether the toggle switch is enabled
* @param controlState The state from SecureDnsControl.
* @return True if the state was successfully stored.
*/
private boolean storePreferenceState(boolean enabled, State controlState) {
if (!enabled) {
mManager.setSecureDnsMode(SecureDnsMode.OFF);
mManager.setDnsOverHttpsTemplates("");
} else if (!controlState.secure) {
mManager.setSecureDnsMode(SecureDnsMode.AUTOMATIC);
mManager.setDnsOverHttpsTemplates("");
} else {
if (controlState.template.isEmpty()
|| !mManager.setDnsOverHttpsTemplates(controlState.template)) {
return false;
}
mManager.setSecureDnsMode(SecureDnsMode.SECURE);
}
return true;
}
private void loadPreferenceState() {
@SecureDnsMode
int mode = mManager.getSecureDnsMode();
boolean enabled = mode != SecureDnsMode.OFF;
boolean enforced = mManager.isSecureDnsModeManaged()
|| mManager.getSecureDnsManagementMode() != SecureDnsManagementMode.NO_OVERRIDE;
mSecureDnsSwitch.setChecked(enabled);
mSecureDnsProviderPreference.setEnabled(enabled && !enforced);
boolean secure = mode == SecureDnsMode.SECURE;
String template = mManager.getDnsOverHttpsTemplates();
boolean valid = true; // States loaded from storage are presumed valid.
mSecureDnsProviderPreference.setState(new State(secure, template, valid));
}
@Override
public void onResume() {
super.onResume();
loadPreferenceState();
}
}
......@@ -726,6 +726,45 @@ For example, some websites may respond to this request by showing you ads that a
<message name="IDS_CAN_MAKE_PAYMENT_TITLE" desc="Title for preference to allow websites to know whether you have payment methods available through PaymentRequest.CanMakePayment interface">
Access payment methods
</message>
<message name="IDS_SETTINGS_CUSTOM" desc="Label for a custom option in a dropdown menu.">
Custom
</message>
<message name="IDS_SETTINGS_SECURE_DNS_TITLE" desc="Title of the Secure DNS settings option">
Use secure DNS
</message>
<message name="IDS_SETTINGS_SECURE_DNS_DESCRIPTION" desc="Secondary, continued explanation of secure DNS in Privacy options">
Determines how to connect to websites over a secure connection
</message>
<message name="IDS_SETTINGS_AUTOMATIC_MODE_LABEL" desc="Text of the radio button that puts secure DNS in auto-upgrade mode">
Use your current service provider
</message>
<message name="IDS_SETTINGS_AUTOMATIC_MODE_DESCRIPTION" desc="Descriptive text that appears below IDS_SETTINGS_AUTOMATIC_MODE_LABEL.">
Secure DNS may not be available all the time
</message>
<message name="IDS_SETTINGS_AUTOMATIC_MODE_SUMMARY" desc="Short description of automatic mode">
Automatic
</message>
<message name="IDS_SETTINGS_SECURE_DROPDOWN_MODE_DESCRIPTION" desc="Text of the radio button that allows a secure resolver to be selected from a dropdown menu">
Choose another provider:
</message>
<message name="IDS_SETTINGS_SECURE_DROPDOWN_MODE_PRIVACY_POLICY" desc="Text that displays a link to the privacy policy of the resolver selected from a dropdown menu">
See this provider's <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1<ex>https://google.com/</ex>"&gt;</ph>privacy policy<ph name="END_LINK">&lt;/a&gt;</ph>
</message>
<message name="IDS_SETTINGS_SECURE_DNS_DISABLED_FOR_MANAGED_ENVIRONMENT" desc="Substring of the secure DNS setting when secure DNS is disabled due to detection of a managed environment">
This setting is disabled on managed browsers
</message>
<message name="IDS_SETTINGS_SECURE_DNS_DISABLED_FOR_PARENTAL_CONTROL" desc="Substring of the secure DNS setting when secure DNS is disabled due to detection of OS-level parental controls">
This setting is disabled because parental controls are on
</message>
<message name="IDS_SETTINGS_SECURE_DNS_CUSTOM_PLACEHOLDER" desc="Placeholder text for a textbox where users can enter a custom secure DNS provider">
Provider URL
</message>
<message name="IDS_SETTINGS_SECURE_DNS_CUSTOM_FORMAT_ERROR" desc="Error text for an incorrectly formatted entry for the custom secure DNS provider">
Enter a correctly formatted URL
</message>
<message name="IDS_SETTINGS_SECURE_DNS_CUSTOM_CONNECTION_ERROR" desc="Error text for a custom secure DNS provider entry to which a probe connection fails">
Please verify that this is a valid provider or try again later
</message>
<message name="IDS_CLEAR_BROWSING_DATA_TITLE" desc="Title of the Clear Browsing Data screen. [CHAR-LIMIT=32]">
Clear browsing data
</message>
......
07470847c57193b5abe3c19d409f40fe03c64ac4
\ No newline at end of file
864e7cae4749082cf375f59b5f80b7a1bbcc3544
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment