Dan Ballard pushed to branch tor-browser-128.7.0esr-14.5-1 at The Tor Project / Applications / Tor Browser
Commits: 4f30e0a4 by clairehurst at 2025-02-11T11:37:13-07:00 fixup! TB 40041 [android]: Implement Tor Network Settings
tor-browser#43408
- - - - - 6877b274 by clairehurst at 2025-02-11T12:06:17-07:00 fixup! TB 42247: Android helpers for the TorProvider
tor-browser#43408
- - - - - d95f26e0 by clairehurst at 2025-02-11T12:06:18-07:00 fixup! [android] Implement Android-native Connection Assist UI
tor-browser#43408
- - - - - 881f522b by clairehurst at 2025-02-11T12:06:18-07:00 fixup! TB 41878: [android] Add standalone Tor Bootstrap
tor-browser#43408
- - - - -
12 changed files:
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt - − mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/QuickStartPreference.kt - + mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/QuickstartViewModel.kt - mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorConnectionAssistFragment.kt - mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorConnectionAssistViewModel.kt - mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorController.kt - mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorControllerGV.kt - mobile/android/fenix/app/src/main/res/xml/preferences.xml - mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorIntegrationAndroid.java - mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorSettings.java - mobile/android/geckoview/src/main/java/org/mozilla/geckoview/androidlegacysettings/TorLegacyAndroidSettings.java - toolkit/modules/TorAndroidIntegration.sys.mjs
Changes:
===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SettingsFragment.kt ===================================== @@ -13,7 +13,6 @@ import android.os.Build import android.os.Bundle import android.os.Handler import android.os.Looper -import android.util.Log import android.view.LayoutInflater import android.view.WindowManager import android.widget.Toast @@ -65,16 +64,12 @@ import org.mozilla.fenix.ext.openSetDefaultBrowserOption import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.settings import org.mozilla.fenix.ext.showToolbar -import org.mozilla.fenix.gecko.GeckoProvider import org.mozilla.fenix.nimbus.FxNimbus import org.mozilla.fenix.perf.ProfilerViewModel import org.mozilla.fenix.settings.account.AccountUiView -import org.mozilla.fenix.tor.QuickStartPreference import org.mozilla.fenix.tor.SecurityLevel -import org.mozilla.fenix.tor.TorBridgeTransportConfig -import org.mozilla.fenix.tor.TorEvents +import org.mozilla.fenix.tor.QuickstartViewModel import org.mozilla.fenix.utils.Settings -import org.mozilla.geckoview.BuildConfig import kotlin.system.exitProcess import org.mozilla.fenix.GleanMetrics.Settings as SettingsMetrics
@@ -86,6 +81,8 @@ class SettingsFragment : PreferenceFragmentCompat(), UserInteractionHandler { private lateinit var addonFilePicker: AddonFilePicker private val profilerViewModel: ProfilerViewModel by activityViewModels()
+ private val quickstartViewModel: QuickstartViewModel by activityViewModels() + @VisibleForTesting internal val accountObserver = object : AccountObserver { private fun updateAccountUi(profile: Profile? = null) { @@ -776,10 +773,12 @@ class SettingsFragment : PreferenceFragmentCompat(), UserInteractionHandler { } }
- requirePreference<QuickStartPreference>(R.string.pref_key_quick_start).apply { + requirePreference<SwitchPreference>(R.string.pref_key_quick_start).apply { + isChecked = quickstartViewModel.quickstart().value == true setOnPreferenceClickListener { - context.components.torController.quickstart = !context.components.torController.quickstart - updateSwitch() + quickstartViewModel.quickstartSet( + isChecked, + ) true } }
===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/QuickStartPreference.kt deleted ===================================== @@ -1,32 +0,0 @@ -package org.mozilla.fenix.tor - -import android.content.Context -import android.util.AttributeSet -import androidx.preference.PreferenceViewHolder -import androidx.preference.SwitchPreference -import com.google.android.material.switchmaterial.SwitchMaterial -import org.mozilla.fenix.R -import org.mozilla.fenix.ext.components - -class QuickStartPreference @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, -) : SwitchPreference(context, attrs) { - - private var switchView: SwitchMaterial? = null - - init { - widgetLayoutResource = R.layout.preference_quick_start - } - - override fun onBindViewHolder(holder: PreferenceViewHolder) { - super.onBindViewHolder(holder) - switchView = holder.findViewById(R.id.switch_widget) as SwitchMaterial - - updateSwitch() - } - - fun updateSwitch() { - switchView?.isChecked = context.components.torController.quickstart - } -}
===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/QuickstartViewModel.kt ===================================== @@ -0,0 +1,41 @@ +package org.mozilla.fenix.tor + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import mozilla.components.browser.engine.gecko.GeckoEngine +import org.mozilla.fenix.ext.components + +class QuickstartViewModel( + application: Application, +) : AndroidViewModel(application) { + + private val torIntegrationAndroid = + (getApplication<Application>().components.core.engine as GeckoEngine).getTorIntegrationController() + + /** + * NOTE: Whilst the initial value for _quickstart is fetched from + * TorIntegrationAndroid.quickstartGet (which is surfaced from TorConnect.quickstart), and we + * pass on any changes in value up to TorConnect.quickstart (via quickstartSet()), we do not + * listen for any changes to the TorConnect.quickstart value via "QuickstartChange" because we + * do not expect anything outside of TorConnectViewModel to change its value, so we expect its + * value to remain in sync with our local value. + */ + init { + torIntegrationAndroid.quickstartGet { + _quickstart.value = it + } + } + + private val _quickstart = MutableLiveData<Boolean>() + fun quickstart(): LiveData<Boolean> { + return _quickstart + } + + fun quickstartSet(value: Boolean) { + torIntegrationAndroid.quickstartSet(value) + _quickstart.value = value + } + +}
===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorConnectionAssistFragment.kt ===================================== @@ -20,6 +20,7 @@ import android.view.ViewGroup import androidx.appcompat.content.res.AppCompatResources import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels +import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle @@ -38,6 +39,8 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler { private var _binding: FragmentTorConnectionAssistBinding? = null private val binding get() = _binding!!
+ private val quickstartViewModel: QuickstartViewModel by activityViewModels() + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -78,10 +81,10 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler { setProgressBarCompat(progress) }
- viewModel.quickstartToggle().observe( + quickstartViewModel.quickstart().observe( viewLifecycleOwner, ) { - binding.quickstartSwitch.isChecked = it == true + binding.quickstartSwitch.isChecked = it }
viewModel.shouldOpenHome().observe( @@ -185,9 +188,8 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler { private fun setQuickStart(screen: ConnectAssistUiState) { binding.quickstartSwitch.visibility = if (screen.quickstartSwitchVisible) View.VISIBLE else View.GONE - binding.quickstartSwitch.isChecked = viewModel.quickstartToggle().value == true binding.quickstartSwitch.setOnCheckedChangeListener { _, isChecked -> - viewModel.handleQuickstartChecked(isChecked) + quickstartViewModel.quickstartSet(isChecked) } }
===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorConnectionAssistViewModel.kt ===================================== @@ -27,13 +27,6 @@ class TorConnectionAssistViewModel( private val _torConnectScreen = MutableStateFlow(ConnectAssistUiState.Splash) internal val torConnectScreen: StateFlow<ConnectAssistUiState> = _torConnectScreen
- private val _quickStartToggle = MutableLiveData<Boolean>() // don't initialize with quickstart off the bat - fun quickstartToggle(): LiveData<Boolean?> { - _quickStartToggle.value = _torController.quickstart // quickstart isn't ready until torSettings is ready - return _quickStartToggle - } - - private val _shouldOpenHome = MutableLiveData(false) fun shouldOpenHome(): LiveData<Boolean> { return _shouldOpenHome @@ -66,11 +59,6 @@ class TorConnectionAssistViewModel( } }
- fun handleQuickstartChecked(checked: Boolean) { - _torController.quickstart = checked - _quickStartToggle.value = checked - } - fun handleButton1Pressed( screen: ConnectAssistUiState, lifecycleScope: LifecycleCoroutineScope?,
===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorController.kt ===================================== @@ -40,7 +40,6 @@ interface TorController: TorEvents { var bridgesEnabled: Boolean var bridgeTransport: TorBridgeTransportConfig var userProvidedBridges: String? - var quickstart: Boolean
fun start() fun stop()
===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorControllerGV.kt ===================================== @@ -73,17 +73,6 @@ class TorControllerGV( override val isBootstrapped get() = isTorBootstrapped override val isConnected get() = (_lastKnownStatus.value.isStarted() && !isTorRestarting)
- override var quickstart: Boolean - get() { - return getTorSettings()?.quickstart ?: false - } - set(value) { - getTorSettings()?.let { - it.quickstart = value - getTorIntegration().setSettings(it) - } - } - private fun getTorIntegration(): TorIntegrationAndroid { return (context.components.core.engine as GeckoEngine).getTorIntegrationController() }
===================================== mobile/android/fenix/app/src/main/res/xml/preferences.xml ===================================== @@ -176,7 +176,7 @@ android:title="@string/preferences_tor_network_settings_bridge_config" android:summary="@string/preferences_tor_network_settings_bridge_config_description" />
- <org.mozilla.fenix.tor.QuickStartPreference + <SwitchPreference android:key="@string/pref_key_quick_start" android:summary="@string/connection_assist_always_connect_automatically_toggle_description" android:title="@string/tor_bootstrap_quick_start_label"
===================================== mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorIntegrationAndroid.java ===================================== @@ -52,6 +52,8 @@ public class TorIntegrationAndroid implements BundleEventListener { private static final String EVENT_BOOTSTRAP_BEGIN_AUTO = "GeckoView:Tor:BootstrapBeginAuto"; private static final String EVENT_BOOTSTRAP_CANCEL = "GeckoView:Tor:BootstrapCancel"; private static final String EVENT_BOOTSTRAP_GET_STATE = "GeckoView:Tor:BootstrapGetState"; + private static final String EVENT_QUICKSTART_GET = "GeckoView:Tor:QuickstartGet"; + private static final String EVENT_QUICKSTART_SET = "GeckoView:Tor:QuickstartSet";
private static final String CONTROL_PORT_FILE = "/control-ipc"; private static final String SOCKS_FILE = "/socks-ipc"; @@ -682,6 +684,23 @@ public class TorIntegrationAndroid implements BundleEventListener { return EventDispatcher.getInstance().queryVoid(EVENT_SETTINGS_SET, bundle); }
+ public interface QuickstartGetter { + void onValue(boolean enabled); + } + + public void quickstartGet(QuickstartGetter quickstartGetter) { + EventDispatcher.getInstance().queryBoolean(EVENT_QUICKSTART_GET).then(enabled -> { + quickstartGetter.onValue(Boolean.TRUE.equals(enabled)); + return new GeckoResult<Void>(); + }); + } + + public @NonNull GeckoResult<Void> quickstartSet(boolean enabled) { + final GeckoBundle bundle = new GeckoBundle(1); + bundle.putBoolean("enabled", enabled); + return EventDispatcher.getInstance().queryVoid(EVENT_QUICKSTART_SET, bundle); + } + public @NonNull GeckoResult<Void> beginBootstrap() { return EventDispatcher.getInstance().queryVoid(EVENT_BOOTSTRAP_BEGIN); }
===================================== mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorSettings.java ===================================== @@ -101,8 +101,6 @@ public class TorSettings {
public boolean enabled = true;
- public boolean quickstart = false; - // bridges section public boolean bridgesEnabled = false; public BridgeSource bridgesSource = BridgeSource.Invalid; @@ -125,7 +123,6 @@ public class TorSettings {
public TorSettings(GeckoBundle bundle) { try { - GeckoBundle qs = bundle.getBundle("quickstart"); GeckoBundle bridges = bundle.getBundle("bridges"); GeckoBundle proxy = bundle.getBundle("proxy"); GeckoBundle firewall = bundle.getBundle("firewall"); @@ -135,8 +132,6 @@ public class TorSettings { bridgesBuiltinType = BridgeBuiltinType.fromString(bridges.getString("builtin_type")); bridgeBridgeStrings = bridges.getStringArray("bridge_strings");
- quickstart = qs.getBoolean("enabled"); - firewallEnabled = firewall.getBoolean("enabled"); firewallAllowedPorts = firewall.getIntArray("allowed_ports");
@@ -156,7 +151,6 @@ public class TorSettings { public GeckoBundle asGeckoBundle() { GeckoBundle bundle = new GeckoBundle();
- GeckoBundle qs = new GeckoBundle(); GeckoBundle bridges = new GeckoBundle(); GeckoBundle proxy = new GeckoBundle(); GeckoBundle firewall = new GeckoBundle(); @@ -166,8 +160,6 @@ public class TorSettings { bridges.putString("builtin_type", bridgesBuiltinType.toString()); bridges.putStringArray("bridge_strings", bridgeBridgeStrings);
- qs.putBoolean("enabled", quickstart); - firewall.putBoolean("enabled", firewallEnabled); firewall.putIntArray("allowed_ports", firewallAllowedPorts);
@@ -178,7 +170,6 @@ public class TorSettings { proxy.putInt("port", proxyPort); proxy.putInt("type", proxyType.toInt());
- bundle.putBundle("quickstart", qs); bundle.putBundle("bridges", bridges); bundle.putBundle("proxy", proxy); bundle.putBundle("firewall", firewall);
===================================== mobile/android/geckoview/src/main/java/org/mozilla/geckoview/androidlegacysettings/TorLegacyAndroidSettings.java ===================================== @@ -24,9 +24,6 @@ public class TorLegacyAndroidSettings { // always true, tor is enabled in TB settings.enabled = true;
- // firefox-android disconnected quick start a while ago so it's untracked - settings.quickstart = false; - settings.bridgesEnabled = Prefs.bridgesEnabled();
// tor-android-service CustomTorInstaller.java
===================================== toolkit/modules/TorAndroidIntegration.sys.mjs ===================================== @@ -41,12 +41,19 @@ const ListenedEvents = Object.freeze({ bootstrapBeginAuto: "GeckoView:Tor:BootstrapBeginAuto", bootstrapCancel: "GeckoView:Tor:BootstrapCancel", bootstrapGetState: "GeckoView:Tor:BootstrapGetState", + quickstartGet: "GeckoView:Tor:QuickstartGet", + quickstartSet: "GeckoView:Tor:QuickstartSet", });
class TorAndroidIntegrationImpl { #initialized = false;
- async init() { + /** + * Register our listeners. + * We want this function to block GeckoView initialization, so it should not be + * async. Any async task should be moved to #deferredInit, instead. + */ + init() { if (this.#initialized) { logger.warn("Something tried to initilize us again."); return; @@ -74,6 +81,14 @@ class TorAndroidIntegrationImpl { // by TorProviderBuilder.init. lazy.TorProviderBuilder.firstWindowLoaded();
+ this.#deferredInit(); + } + + /** + * Perform our init tasks that should not block the initialization of + * GeckoView. This function will not be awaited, so errors can only be logged. + */ + async #deferredInit() { try { await lazy.TorSettings.init(); await lazy.TorConnect.init(); @@ -82,24 +97,6 @@ class TorAndroidIntegrationImpl { } }
- /** - * Combine the current TorSettings settings with the TorConnect settings. - * - * @returns {object} The TorSettings in an object, which also has a - * `quickstart.enabled` property. - */ - // This is added for backward compatibility with TorSettings.getSettings prior - // to tor-browser#41921, when it used to control the quickstart setting. - // TODO: Have android separate out the request for TorConnect.quickstart. In - // principle, this would allow android tor connect UI to be loaded before - // TorSettings has initialized (the SettingsReady signal), similar to desktop. - // See tor-browser#43408. - #getAllSettings() { - const settings = lazy.TorSettings.getSettings(); - settings.quickstart = { enabled: lazy.TorConnect.quickstart }; - return settings; - } - observe(subj, topic) { switch (topic) { // TODO: Replace with StageChange. @@ -142,7 +139,7 @@ class TorAndroidIntegrationImpl { case lazy.TorSettingsTopics.Ready: lazy.EventDispatcher.instance.sendRequest({ type: EmittedEvents.settingsReady, - settings: this.#getAllSettings(), + settings: lazy.TorSettings.getSettings(), }); break; case lazy.TorSettingsTopics.SettingsChanged: @@ -151,20 +148,7 @@ class TorAndroidIntegrationImpl { lazy.EventDispatcher.instance.sendRequest({ type: EmittedEvents.settingsChanged, changes: subj.wrappedJSObject.changes ?? [], - settings: this.#getAllSettings(), - }); - break; - case lazy.TorConnectTopics.QuickstartChange: - // We also include the TorSettings, and a `changes` Array similar to - // SettingsChanged signal. This is for backward compatibility with - // TorSettings.getSettings prior to tor-browser#41921, when it used to - // control the quickstart setting. - // TODO: Have android separate out the request for TorConnect.quickstart. - // See tor-browser#43408. - lazy.EventDispatcher.instance.sendRequest({ - type: EmittedEvents.settingsChanged, - changes: ["quickstart.enabled"], - settings: this.#getAllSettings(), + settings: lazy.TorSettings.getSettings(), }); break; } @@ -175,19 +159,9 @@ class TorAndroidIntegrationImpl { try { switch (event) { case ListenedEvents.settingsGet: - callback?.onSuccess(this.#getAllSettings()); + callback?.onSuccess(lazy.TorSettings.getSettings()); return; case ListenedEvents.settingsSet: - // TODO: Set quickstart via a separate event. See tor-browser#43408. - // NOTE: Currently this may trigger GeckoView:Tor:SettingsChanged - // twice: once for quickstart.enabled, and again for the other - // settings. - if ( - "quickstart" in data.settings && - "enabled" in data.settings.quickstart - ) { - lazy.TorConnect.quickstart = data.settings.quickstart.enabled; - } // TODO: Handle setting throw? This can throw if data.settings is // incorrectly formatted, but more like it can throw when the settings // fail to be passed onto the TorProvider. tor-browser#43405. @@ -211,6 +185,12 @@ class TorAndroidIntegrationImpl { return; // TODO: Expose TorConnect.startAgain() to allow users to begin // from the start again. + case ListenedEvents.quickstartGet: + callback?.onSuccess(lazy.TorConnect.quickstart); + return; + case ListenedEvents.quickstartSet: + lazy.TorConnect.quickstart = data.enabled; + break; } callback?.onSuccess(); } catch (e) {
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/86b24fa...
tbb-commits@lists.torproject.org