GitLab is used only for code review, issue tracking and project management. Canonical locations for source code are still https://gitweb.torproject.org/ https://git.torproject.org/ and git-rw.torproject.org.

Commit e30022b5 authored by Matthew Finkel's avatar Matthew Finkel

Bug 40041: Implement Tor Network Settings

parent f94ec7da
......@@ -221,6 +221,9 @@ class SettingsFragment : PreferenceFragmentCompat() {
resources.getString(R.string.pref_key_search_settings) -> {
SettingsFragmentDirections.actionSettingsFragmentToSearchEngineFragment()
}
resources.getString(R.string.pref_key_tor_network_settings) -> {
SettingsFragmentDirections.actionSettingsFragmentToTorNetworkSettingsFragment()
}
resources.getString(R.string.pref_key_tracking_protection_settings) -> {
requireContext().metrics.track(Event.TrackingProtectionSettings)
SettingsFragmentDirections.actionSettingsFragmentToTrackingProtectionFragment()
......
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.settings
import android.os.Bundle
import androidx.preference.EditTextPreference
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference
import org.mozilla.fenix.Config
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.ext.showToolbar
import org.mozilla.fenix.tor.TorBridgeTransportConfig
import org.mozilla.fenix.utils.view.addToRadioGroup
import org.mozilla.fenix.utils.view.GroupableRadioButton
import org.mozilla.fenix.utils.view.uncheckAll
/**
* Displays the toggle for enabling bridges, options for built-in pluggable transports, and an additional
* preference for configuring a user-provided bridge.
*/
@Suppress("SpreadOperator")
class TorBridgeConfigFragment : PreferenceFragmentCompat() {
private val builtinBridgeRadioGroups = mutableListOf<GroupableRadioButton>()
private var previousTransportConfig: TorBridgeTransportConfig? = null
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.tor_bridge_config_preferences, rootKey)
// Initialize radio button group for built-in bridge transport types
val radioObfs4 = bindBridgeTransportRadio(TorBridgeTransportConfig.BUILTIN_OBFS4)
val radioMeekAzure = bindBridgeTransportRadio(TorBridgeTransportConfig.BUILTIN_MEEK_AZURE)
val radioSnowflake = bindBridgeTransportRadio(TorBridgeTransportConfig.BUILTIN_SNOWFLAKE)
builtinBridgeRadioGroups.addAll(mutableListOf(radioObfs4, radioMeekAzure, radioSnowflake))
// `*` is Kotlin's "spread" operator, for expanding an Array as a vararg.
addToRadioGroup(*builtinBridgeRadioGroups.toTypedArray())
// Snowflake is not available on Release at this time.
if (Config.channel.isRelease) {
radioSnowflake.apply {
isVisible = false
}
}
}
override fun onResume() {
super.onResume()
showToolbar(getString(R.string.preferences_tor_network_settings_bridge_config))
val bridgesEnabled = requireContext().components.torController.bridgesEnabled
val prefBridgeConfig =
requirePreference<SwitchPreference>(R.string.pref_key_tor_network_settings_bridge_config_toggle)
prefBridgeConfig.apply {
isChecked = bridgesEnabled
setOnPreferenceChangeListener<Boolean> { preference, enabled ->
preference.context.components.torController.bridgesEnabled = enabled
updateCurrentConfiguredBridgePref(preference)
preference.context.components.torController.restartTor()
true
}
}
val userProvidedBridges = requirePreference<EditTextPreference>(
R.string.pref_key_tor_network_settings_bridge_config_user_provided_bridge
)
userProvidedBridges.apply {
setOnPreferenceChangeListener<String> { preference, userProvidedBridge ->
builtinBridgeRadioGroups.uncheckAll()
preference.context.components.torController.bridgeTransport = TorBridgeTransportConfig.USER_PROVIDED
preference.context.components.torController.userProvidedBridges = userProvidedBridge
updateCurrentConfiguredBridgePref(preference)
preference.context.components.torController.restartTor()
true
}
val userProvidedBridge: String? = context.components.torController.userProvidedBridges
if (userProvidedBridge != null) {
setText(userProvidedBridge)
}
}
val currentBridgeType = prefBridgeConfig.context.components.torController.bridgeTransport
// Cache the current configured transport type
previousTransportConfig = currentBridgeType
builtinBridgeRadioGroups.uncheckAll()
if (currentBridgeType != TorBridgeTransportConfig.USER_PROVIDED) {
val bridgeRadioButton = requirePreference<RadioButtonPreference>(currentBridgeType.preferenceKey)
bridgeRadioButton.setCheckedWithoutClickListener(true)
}
updateCurrentConfiguredBridgePref(prefBridgeConfig)
}
private fun bindBridgeTransportRadio(
bridge: TorBridgeTransportConfig
): RadioButtonPreference {
val radio = requirePreference<RadioButtonPreference>(bridge.preferenceKey)
radio.apply {
setOnPreferenceChangeListener<Boolean> { preference, isChecked ->
if (isChecked && (previousTransportConfig!! != bridge)) {
preference.context.components.torController.bridgeTransport = bridge
previousTransportConfig = bridge
updateCurrentConfiguredBridgePref(preference)
context.components.torController.restartTor()
}
true
}
}
return radio
}
private fun setCurrentBridgeLabel(currentBridgePref: Preference?, bridge: String) {
currentBridgePref?.apply {
title = getString(
R
.string
.preferences_tor_network_settings_bridge_config_current_bridge,
bridge
)
}
}
private fun updateCurrentConfiguredBridgePref(preference: Preference) {
val currentBridge: Preference? =
findPreference(
getString(
R.string.pref_key_tor_network_settings_bridge_config_current_bridge
)
)
val enabled = requireContext().components.torController.bridgesEnabled
if (enabled) {
val configuredBridge = preference.context.components.torController.bridgeTransport
var bridges = when (configuredBridge) {
TorBridgeTransportConfig.USER_PROVIDED ->
preference.context.components.torController.userProvidedBridges
else -> configuredBridge.transportName
}
if (bridges == null) {
bridges = "not known"
}
setCurrentBridgeLabel(currentBridge, bridges)
} else {
setCurrentBridgeLabel(
currentBridge,
getString(R.string.tor_network_settings_bridge_not_configured)
)
}
}
}
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.settings
import android.os.Bundle
import androidx.navigation.findNavController
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.ext.showToolbar
import org.mozilla.fenix.tor.TorBridgeTransportConfig
import org.mozilla.fenix.tor.TorEvents
/**
* Lets the user configure Tor network connection settings
*/
class TorNetworkSettingsFragment : PreferenceFragmentCompat(), TorEvents {
override fun onResume() {
super.onResume()
val torController = requireContext().components.torController
torController.registerTorListener(this)
showToolbar(getString(R.string.preferences_tor_network_settings))
val yesString = getString(R.string.preferences_tor_network_settings_yes)
val noString = getString(R.string.preferences_tor_network_settings_no)
requirePreference<Preference>(R.string.pref_key_tor_network_settings_bridge_config).apply {
setOnPreferenceClickListener {
val directions =
TorNetworkSettingsFragmentDirections
.actionTorNetworkSettingsFragmentToTorBridgeConfigFragment()
requireView().findNavController().navigate(directions)
true
}
if (torController.bridgesEnabled) {
if (torController.bridgeTransport == TorBridgeTransportConfig.USER_PROVIDED) {
summary =
getString(
R
.string
.preferences_tor_network_settings_bridge_config_description_user_provided_enabled
)
} else {
summary =
getString(
R
.string
.preferences_tor_network_settings_bridge_config_description_builtin_transport_enabled
)
}
} else {
summary =
getString(
R
.string
.preferences_tor_network_settings_bridge_config_description
)
}
}
requirePreference<Preference>(R.string.pref_key_tor_network_settings_bridges_enabled).apply {
val formatStringRes = R.string.preferences_tor_network_settings_bridges_enabled
title = if (torController.bridgesEnabled) {
getString(formatStringRes, yesString)
} else {
getString(formatStringRes, noString)
}
}
setStatus()
}
private fun setStatus() {
val torController = requireContext().components.torController
val yesString = getString(R.string.preferences_tor_network_settings_yes)
val noString = getString(R.string.preferences_tor_network_settings_no)
requirePreference<Preference>(R.string.pref_key_tor_network_settings_tor_ready).apply {
val formatStringRes = R.string.preferences_tor_network_settings_tor_ready
@SuppressWarnings("ComplexCondition")
title = if (!torController.isStarting &&
torController.isConnected &&
torController.isBootstrapped &&
!torController.isRestarting) {
getString(formatStringRes, yesString)
} else {
getString(formatStringRes, noString)
}
}
requirePreference<Preference>(R.string.pref_key_tor_network_settings_state).apply {
val formatStringRes = R.string.preferences_tor_network_settings_state
title = if (torController.isRestarting) {
getString(formatStringRes,
getString(
R
.string
.preferences_tor_network_settings_restarting
)
)
} else if (torController.isStarting) {
getString(formatStringRes,
getString(
R
.string
.preferences_tor_network_settings_connecting
)
)
} else if (torController.isConnected) {
getString(formatStringRes,
getString(
R
.string
.preferences_tor_network_settings_connected
)
)
} else {
getString(formatStringRes,
getString(
R
.string
.preferences_tor_network_settings_disconnected
)
)
}
}
}
override fun onStop() {
super.onStop()
requireContext().components.torController.unregisterTorListener(this)
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.tor_network_settings_preferences, rootKey)
}
@SuppressWarnings("EmptyFunctionBlock")
override fun onTorConnecting() {
}
@SuppressWarnings("EmptyFunctionBlock")
override fun onTorConnected() {
}
@SuppressWarnings("EmptyFunctionBlock")
override fun onTorStopped() {
}
override fun onTorStatusUpdate(entry: String?, status: String?) {
setStatus()
}
}
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.tor
import android.os.Parcelable
import androidx.annotation.StringRes
import kotlinx.android.parcel.Parcelize
import org.mozilla.fenix.R
@Parcelize
enum class TorBridgeTransportConfig(
@StringRes val preferenceKey: Int,
val transportName: String
) : Parcelable {
BUILTIN_OBFS4(
preferenceKey = R.string.pref_key_tor_network_settings_bridge_config_builtin_bridge_obfs4,
transportName = "obfs4"
),
BUILTIN_MEEK_AZURE(
preferenceKey = R.string.pref_key_tor_network_settings_bridge_config_builtin_bridge_meek_azure,
transportName = "meek"
),
BUILTIN_SNOWFLAKE(
preferenceKey = R.string.pref_key_tor_network_settings_bridge_config_builtin_bridge_snowflake,
transportName = "snowflake"
),
USER_PROVIDED(
preferenceKey = R.string.pref_key_tor_network_settings_bridge_config_user_provided_bridge,
transportName = "user_provided"
)
}
object TorBridgeTransportConfigUtil {
fun getStringToBridgeTransport(bridge: String): TorBridgeTransportConfig {
return when (bridge) {
TorBridgeTransportConfig.BUILTIN_OBFS4.transportName ->
TorBridgeTransportConfig.BUILTIN_OBFS4
TorBridgeTransportConfig.BUILTIN_MEEK_AZURE.transportName ->
TorBridgeTransportConfig.BUILTIN_MEEK_AZURE
TorBridgeTransportConfig.BUILTIN_SNOWFLAKE.transportName ->
TorBridgeTransportConfig.BUILTIN_SNOWFLAKE
else -> TorBridgeTransportConfig.USER_PROVIDED
}
}
}
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="18dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M22.7,19l-9.1,-9.1c0.9,-2.3 0.4,-5 -1.5,-6.9 -2,-2 -5,-2.4 -7.4,-1.3L9,6 6,9 1.6,4.7C0.4,7.1 0.9,10.1 2.9,12.1c1.9,1.9 4.6,2.4 6.9,1.5l9.1,9.1c0.4,0.4 1,0.4 1.4,0l2.3,-2.3c0.5,-0.4 0.5,-1.1 0.1,-1.4z"
android:fillColor="#ffffff"/>
</vector>
......@@ -247,4 +247,20 @@
<string name="pref_key_tor_security_level_standard_default" translatable="false">pref_key_tor_security_level_standard_default</string>
<string name="pref_key_tor_security_level_safer_option" translatable="false">pref_key_tor_security_level_safer_option</string>
<string name="pref_key_tor_security_level_safest_option" translatable="false">pref_key_tor_security_level_safest_option</string>
<string name="pref_key_tor_network_settings" translatable="false">pref_key_tor_network_settings</string>
<string name="pref_key_tor_network_settings_explanation" translatable="false">pref_key_tor_network_settings_explanation</string>
<string name="pref_key_tor_network_settings_bridge_config" translatable="false">pref_key_tor_network_settings_bridge_config</string>
<string name="pref_key_tor_network_settings_bridge_config_explanation" translatable="false">pref_key_tor_network_settings_bridge_config_explanation</string>
<string name="pref_key_tor_network_settings_bridge_config_toggle" translatable="false">pref_key_tor_network_settings_bridge_config_toggle</string>
<string name="pref_key_tor_network_settings_bridge_config_builtin_bridge_obfs4" translatable="false">pref_key_tor_network_settings_bridge_config_builtin_bridge_obfs4</string>
<string name="pref_key_tor_network_settings_bridge_config_builtin_bridge_meek_azure" translatable="false">pref_key_tor_network_settings_bridge_config_builtin_bridge_meek_azure</string>
<string name="pref_key_tor_network_settings_bridge_config_builtin_bridge_snowflake" translatable="false">pref_key_tor_network_settings_bridge_config_builtin_bridge_snowflake</string>
<string name="pref_key_tor_network_settings_bridge_config_user_provided_bridge" translatable="false">pref_key_tor_network_settings_bridge_config_user_provided_bridge</string>
<string name="pref_key_tor_network_settings_bridge_config_user_provided_bridge_preference" translatable="false">pref_key_tor_network_settings_bridge_config_user_provided_bridge_preference</string>
<string name="pref_key_tor_network_settings_bridge_config_current_bridge" translatable="false">pref_key_tor_network_settings_bridge_config_current_bridge</string>
<string name="pref_key_tor_network_settings_status">pref_key_tor_network_settings_status</string>
<string name="pref_key_tor_network_settings_tor_ready">pref_key_tor_network_settings_tor_ready</string>
<string name="pref_key_tor_network_settings_state">pref_key_tor_network_settings_state</string>
<string name="pref_key_tor_network_settings_bridges_enabled">pref_key_tor_network_settings_bridges_enabled</string>
</resources>
......@@ -27,6 +27,33 @@
<string name="tor_explore_privately">Explore. Privately.</string>
<string name="preferences_tor_network_settings">Tor Network</string>
<string name="preferences_tor_network_settings_explanation">Tor Browser routes your traffic over the Tor Network, run by thousands of volunteers around the world.</string>
<string name="preferences_tor_network_settings_bridge_config">Config Bridge</string>
<string name="preferences_tor_network_settings_bridge_config_description">Use a Bridge to connect to Tor</string>
<string name="preferences_tor_network_settings_bridge_config_description_builtin_transport_enabled">You’re using a built-in bridge to connect to Tor</string>
<string name="preferences_tor_network_settings_bridge_config_description_user_provided_enabled">You provided a bridge to connect to Tor</string>
<string name="preferences_tor_network_settings_bridge_config_explanation">Bridges are unlisted relays that make it more difficult to block connections to the Tor Network. Because of how certain countries try to block Tor, certain bridges work in certain countries but not others.</string>
<string name="preferences_tor_network_settings_bridge_config_toggle">Use a Bridge</string>
<string name="preferences_tor_network_settings_bridge_config_toggle_description">Config a bridge to connect to Tor</string>
<string name="preferences_tor_network_settings_bridge_config_builtin_bridge_obfs4">obfs4</string>
<string name="preferences_tor_network_settings_bridge_config_builtin_bridge_meek_azure">meek-azure</string>
<string name="preferences_tor_network_settings_bridge_config_builtin_bridge_snowflake">snowflake</string>
<string name="preferences_tor_network_settings_bridge_config_user_provided_bridge">Provide a Bridge I know</string>
<string name="preferences_tor_network_settings_bridge_config_user_provided_bridge_description">Enter bridge information from a trusted source</string>
<string name="preferences_tor_network_settings_bridge_config_current_bridge">Current Configured Bridge: %s</string>
<string name="tor_network_settings_bridge_not_configured">Not Configured</string>
<string name="preferences_tor_network_settings_status">Current Status</string>
<string name="preferences_tor_network_settings_tor_ready">Is Tor Ready: %s</string>
<string name="preferences_tor_network_settings_state">State: %s</string>
<string name="preferences_tor_network_settings_yes">yes</string>
<string name="preferences_tor_network_settings_no">no</string>
<string name="preferences_tor_network_settings_disconnected">Disconnected</string>
<string name="preferences_tor_network_settings_connecting">Connecting</string>
<string name="preferences_tor_network_settings_connected">Connected</string>
<string name="preferences_tor_network_settings_restarting">Restarting</string>
<string name="preferences_tor_network_settings_bridges_enabled">Bridges are enabled: %s</string>
<!-- Description of security levels -->
<string name="tor_security_level_standard_option">Standard</string>
<string name="tor_security_level_standard_description">All Tor Browser and website features are enabled.</string>
......
<?xml version="1.0" encoding="utf-8"?><!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.preference.Preference
android:key="@string/pref_key_tor_network_settings_bridge_config_explanation"
android:summary="@string/preferences_tor_network_settings_bridge_config_explanation"
app:allowDividerBelow="false" />
<SwitchPreference
android:defaultValue="false"
android:key="@string/pref_key_tor_network_settings_bridge_config_toggle"
android:title="@string/preferences_tor_network_settings_bridge_config_toggle"
android:summary="@string/preferences_tor_network_settings_bridge_config_toggle_description" />
<org.mozilla.fenix.settings.RadioButtonPreference
android:defaultValue="false"
android:dependency="@string/pref_key_tor_network_settings_bridge_config_toggle"
android:key="@string/pref_key_tor_network_settings_bridge_config_builtin_bridge_obfs4"
android:title="@string/preferences_tor_network_settings_bridge_config_builtin_bridge_obfs4" />
<org.mozilla.fenix.settings.RadioButtonPreference
android:defaultValue="false"
android:dependency="@string/pref_key_tor_network_settings_bridge_config_toggle"
android:key="@string/pref_key_tor_network_settings_bridge_config_builtin_bridge_meek_azure"
android:title="@string/preferences_tor_network_settings_bridge_config_builtin_bridge_meek_azure" />
<org.mozilla.fenix.settings.RadioButtonPreference
android:defaultValue="false"
android:dependency="@string/pref_key_tor_network_settings_bridge_config_toggle"
android:key="@string/pref_key_tor_network_settings_bridge_config_builtin_bridge_snowflake"
android:title="@string/preferences_tor_network_settings_bridge_config_builtin_bridge_snowflake" />
<EditTextPreference
android:dependency="@string/pref_key_tor_network_settings_bridge_config_toggle"
android:key="@string/pref_key_tor_network_settings_bridge_config_user_provided_bridge"
android:title="@string/preferences_tor_network_settings_bridge_config_user_provided_bridge"
android:summary="@string/preferences_tor_network_settings_bridge_config_user_provided_bridge_description" />
<Preference
android:dependency="@string/pref_key_tor_network_settings_bridge_config_toggle"
android:key="@string/pref_key_tor_network_settings_bridge_config_current_bridge"
android:title="@string/preferences_tor_network_settings_bridge_config_current_bridge" />
</androidx.preference.PreferenceScreen>
<?xml version="1.0" encoding="utf-8"?><!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<Preference
android:key="@string/pref_key_tor_network_settings_explanation"
android:title="@string/preferences_tor_network_settings_explanation" />
<Preference
android:icon="@drawable/ic_tor_config_bridge"
android:key="@string/pref_key_tor_network_settings_bridge_config"
android:title="@string/preferences_tor_network_settings_bridge_config"
android:summary="@string/preferences_tor_network_settings_bridge_config_description" />
<androidx.preference.PreferenceCategory
android:key="@string/pref_key_tor_network_settings_status"
android:title="@string/preferences_tor_network_settings_status"
app:iconSpaceReserved="false"
android:layout="@layout/preference_category_main_style" >
<Preference
android:key="@string/pref_key_tor_network_settings_tor_ready"
android:title="@string/preferences_tor_network_settings_tor_ready" />
<Preference
android:key="@string/pref_key_tor_network_settings_state"
android:title="@string/preferences_tor_network_settings_state" />
<Preference
android:key="@string/pref_key_tor_network_settings_bridges_enabled"
android:title="@string/preferences_tor_network_settings_bridges_enabled" />
</androidx.preference.PreferenceCategory>
</androidx.preference.PreferenceScreen>
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