Dan Ballard pushed to branch tor-browser-140.1.0esr-15.0-1 at The Tor Project / Applications / Tor Browser
Commits:
-
4fba4d54
by Dan Ballard at 2025-08-04T23:24:21-05:00
-
a04605ca
by Dan Ballard at 2025-08-04T23:24:21-05:00
-
c563c43e
by Dan Ballard at 2025-08-04T23:24:22-05:00
-
e9ac372c
by Dan Ballard at 2025-08-04T23:30:59-05:00
-
579ade4c
by Dan Ballard at 2025-08-04T23:31:02-05:00
13 changed files:
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/HomeActivity.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/IntentReceiverActivity.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/TorBrowserFeatures.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/TorBridgeConfigFragment.kt
- mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorBootstrapProgressViewModel.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/java/org/mozilla/fenix/tor/TorLogsViewModel.kt
- mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorAndroidIntegration.java
- + mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorConnectError.java
- mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorConnectStage.java
- toolkit/modules/TorAndroidIntegration.sys.mjs
Changes:
| ... | ... | @@ -101,7 +101,6 @@ import org.mozilla.fenix.browser.browsingmode.BrowsingMode |
| 101 | 101 | import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
|
| 102 | 102 | import org.mozilla.fenix.browser.browsingmode.DefaultBrowsingModeManager
|
| 103 | 103 | import org.mozilla.fenix.components.appstate.AppAction
|
| 104 | -import org.mozilla.fenix.components.appstate.AppAction.ShareAction
|
|
| 105 | 104 | import org.mozilla.fenix.components.appstate.OrientationMode
|
| 106 | 105 | import org.mozilla.fenix.components.metrics.BreadcrumbsRecorder
|
| 107 | 106 | import org.mozilla.fenix.components.metrics.GrowthDataWorker
|
| ... | ... | @@ -137,7 +136,6 @@ import org.mozilla.fenix.home.intent.OpenBrowserIntentProcessor |
| 137 | 136 | import org.mozilla.fenix.home.intent.OpenPasswordManagerIntentProcessor
|
| 138 | 137 | import org.mozilla.fenix.home.intent.OpenRecentlyClosedIntentProcessor
|
| 139 | 138 | import org.mozilla.fenix.home.intent.OpenSpecificTabIntentProcessor
|
| 140 | -import org.mozilla.fenix.home.intent.ReEngagementIntentProcessor
|
|
| 141 | 139 | import org.mozilla.fenix.home.intent.SpeechProcessingIntentProcessor
|
| 142 | 140 | import org.mozilla.fenix.home.intent.StartSearchIntentProcessor
|
| 143 | 141 | import org.mozilla.fenix.library.bookmarks.DesktopFolders
|
| ... | ... | @@ -167,19 +165,18 @@ import org.mozilla.fenix.theme.DefaultThemeManager |
| 167 | 165 | import org.mozilla.fenix.theme.StatusBarColorManager
|
| 168 | 166 | import org.mozilla.fenix.theme.ThemeManager
|
| 169 | 167 | import org.mozilla.fenix.tor.TorConnectionAssistFragmentDirections
|
| 170 | -import org.mozilla.fenix.tor.TorEvents
|
|
| 171 | 168 | import org.mozilla.fenix.utils.Settings
|
| 172 | 169 | import org.mozilla.fenix.utils.changeAppLauncherIcon
|
| 173 | 170 | import java.lang.ref.WeakReference
|
| 174 | 171 | import java.util.Locale
|
| 175 | 172 | |
| 176 | -import androidx.compose.material.SnackbarDuration
|
|
| 177 | 173 | import mozilla.components.browser.engine.gecko.GeckoEngine
|
| 178 | 174 | import org.mozilla.fenix.compose.core.Action
|
| 179 | 175 | import org.mozilla.fenix.compose.snackbar.SnackbarState
|
| 180 | 176 | import org.mozilla.fenix.compose.snackbar.Snackbar
|
| 181 | 177 | import org.mozilla.fenix.tor.UrlQuickLoadViewModel
|
| 182 | 178 | import org.mozilla.geckoview.TorAndroidIntegration
|
| 179 | +import org.mozilla.geckoview.TorAndroidIntegration.BootstrapStateChangeListener
|
|
| 183 | 180 | import org.mozilla.geckoview.TorConnectStage
|
| 184 | 181 | import kotlin.system.exitProcess
|
| 185 | 182 | |
| ... | ... | @@ -915,19 +912,25 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity, TorAn |
| 915 | 912 | */
|
| 916 | 913 | @SuppressLint("MissingSuperCall") // super.onNewIntent is called in [onNewIntentInternal(intent)]
|
| 917 | 914 | final override fun onNewIntent(intent: Intent) {
|
| 918 | - if (intent.action == ACTION_MAIN || components.torController.isConnected) {
|
|
| 915 | + if (intent.action == ACTION_MAIN || components.torController.isBootstrapped) {
|
|
| 919 | 916 | onNewIntentInternal(intent)
|
| 920 | 917 | } else {
|
| 921 | 918 | // Wait until Tor is connected to handle intents from external apps for links, search, etc.
|
| 922 | - components.torController.registerTorListener(object : TorEvents {
|
|
| 923 | - override fun onTorConnected() {
|
|
| 924 | - components.torController.unregisterTorListener(this)
|
|
| 925 | - onNewIntentInternal(intent)
|
|
| 919 | + val torIntegration = (components.core.engine as GeckoEngine).getTorIntegrationController()
|
|
| 920 | + torIntegration.registerBootstrapStateChangeListener(
|
|
| 921 | + object : BootstrapStateChangeListener {
|
|
| 922 | + |
|
| 923 | + override fun onBootstrapStageChange(stage: TorConnectStage) {
|
|
| 924 | + if (stage.isBootstrapped) {
|
|
| 925 | + torIntegration.unregisterBootstrapStateChangeListener(this)
|
|
| 926 | + onNewIntentInternal(intent)
|
|
| 927 | + }
|
|
| 928 | + }
|
|
| 929 | + |
|
| 930 | + override fun onBootstrapProgress(progress: Double, hasWarnings: Boolean) {}
|
|
| 926 | 931 | }
|
| 927 | - override fun onTorConnecting() { /* no-op */ }
|
|
| 928 | - override fun onTorStopped() { /* no-op */ }
|
|
| 929 | - override fun onTorStatusUpdate(entry: String?, status: String?, progress: Double?) { /* no-op */ }
|
|
| 930 | - })
|
|
| 932 | + )
|
|
| 933 | + |
|
| 931 | 934 | return
|
| 932 | 935 | }
|
| 933 | 936 | }
|
| ... | ... | @@ -1516,7 +1519,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity, TorAn |
| 1516 | 1519 | // we want to ignore other cases when the app gets open by users clicking on links,
|
| 1517 | 1520 | // unless Tor is not yet connected.
|
| 1518 | 1521 | getSettings().shouldStartOnHome() && (intent?.action == ACTION_MAIN ||
|
| 1519 | - !components.torController.isConnected)
|
|
| 1522 | + !components.torController.isBootstrapped)
|
|
| 1520 | 1523 | }
|
| 1521 | 1524 | }
|
| 1522 | 1525 | |
| ... | ... | @@ -1606,14 +1609,13 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity, TorAn |
| 1606 | 1609 | exitProcess(0)
|
| 1607 | 1610 | }
|
| 1608 | 1611 | |
| 1609 | - override fun onBootstrapStateChange(state: String) = Unit
|
|
| 1610 | - override fun onBootstrapStageChange(stage: TorConnectStage) = Unit
|
|
| 1611 | - override fun onBootstrapProgress(progress: Double, hasWarnings: Boolean) = Unit
|
|
| 1612 | - override fun onBootstrapComplete() {
|
|
| 1613 | - if (settings().useHtmlConnectionUi) {
|
|
| 1614 | - components.useCases.tabsUseCases.removeAllTabs()
|
|
| 1615 | - navHost.navController.navigate(NavGraphDirections.actionStartupHome())
|
|
| 1612 | + override fun onBootstrapStageChange(stage: TorConnectStage) {
|
|
| 1613 | + if (stage.isBootstrapped) {
|
|
| 1614 | + if (settings().useHtmlConnectionUi) {
|
|
| 1615 | + components.useCases.tabsUseCases.removeAllTabs()
|
|
| 1616 | + navHost.navController.navigate(NavGraphDirections.actionStartupHome())
|
|
| 1617 | + }
|
|
| 1616 | 1618 | }
|
| 1617 | 1619 | }
|
| 1618 | - override fun onBootstrapError(code: String?, message: String?, phase: String?, reason: String?) = Unit
|
|
| 1620 | + override fun onBootstrapProgress(progress: Double, hasWarnings: Boolean) = Unit
|
|
| 1619 | 1621 | } |
| ... | ... | @@ -13,6 +13,7 @@ import android.os.Build |
| 13 | 13 | import android.os.Bundle
|
| 14 | 14 | import android.os.StrictMode
|
| 15 | 15 | import androidx.annotation.VisibleForTesting
|
| 16 | +import mozilla.components.browser.engine.gecko.GeckoEngine
|
|
| 16 | 17 | import mozilla.components.feature.intent.ext.sanitize
|
| 17 | 18 | import mozilla.components.feature.intent.processing.IntentProcessor
|
| 18 | 19 | import mozilla.components.support.base.log.logger.Logger
|
| ... | ... | @@ -31,7 +32,8 @@ import org.mozilla.fenix.ext.settings |
| 31 | 32 | import org.mozilla.fenix.perf.MarkersActivityLifecycleCallbacks
|
| 32 | 33 | import org.mozilla.fenix.perf.StartupTimeline
|
| 33 | 34 | import org.mozilla.fenix.shortcut.NewTabShortcutIntentProcessor
|
| 34 | -import org.mozilla.fenix.tor.TorEvents
|
|
| 35 | +import org.mozilla.geckoview.TorAndroidIntegration.BootstrapStateChangeListener
|
|
| 36 | +import org.mozilla.geckoview.TorConnectStage
|
|
| 35 | 37 | |
| 36 | 38 | /**
|
| 37 | 39 | * Processes incoming intents and sends them to the corresponding activity.
|
| ... | ... | @@ -55,19 +57,24 @@ class IntentReceiverActivity : Activity() { |
| 55 | 57 | // the HomeActivity.
|
| 56 | 58 | val intent = intent?.let { Intent(it) } ?: Intent()
|
| 57 | 59 | intent.sanitize().stripUnwantedFlags()
|
| 58 | - if (intent.action == ACTION_MAIN || components.torController.isConnected) {
|
|
| 60 | + if (intent.action == ACTION_MAIN || components.torController.isBootstrapped) {
|
|
| 59 | 61 | processIntent(intent)
|
| 60 | 62 | } else {
|
| 61 | 63 | // Wait until Tor is connected to handle intents from external apps for links, search, etc.
|
| 62 | - components.torController.registerTorListener(object : TorEvents {
|
|
| 63 | - override fun onTorConnected() {
|
|
| 64 | - components.torController.unregisterTorListener(this)
|
|
| 65 | - processIntent(intent)
|
|
| 66 | - }
|
|
| 67 | - override fun onTorConnecting() { /* no-op */ }
|
|
| 68 | - override fun onTorStopped() { /* no-op */ }
|
|
| 69 | - override fun onTorStatusUpdate(entry: String?, status: String?, progress: Double?) { /* no-op */ }
|
|
| 70 | - })
|
|
| 64 | + val engine = components.core.engine as GeckoEngine
|
|
| 65 | + engine.getTorIntegrationController().registerBootstrapStateChangeListener(
|
|
| 66 | + object : BootstrapStateChangeListener {
|
|
| 67 | + |
|
| 68 | + override fun onBootstrapStageChange(stage: TorConnectStage) {
|
|
| 69 | + if (stage.isBootstrapped) {
|
|
| 70 | + engine.getTorIntegrationController().unregisterBootstrapStateChangeListener(this)
|
|
| 71 | + processIntent(intent)
|
|
| 72 | + }
|
|
| 73 | + }
|
|
| 74 | + |
|
| 75 | + override fun onBootstrapProgress(progress: Double, hasWarnings: Boolean) {}
|
|
| 76 | + })
|
|
| 77 | + |
|
| 71 | 78 | |
| 72 | 79 | // In the meantime, open the HomeActivity so the user can get connected.
|
| 73 | 80 | processIntent(Intent())
|
| ... | ... | @@ -20,7 +20,7 @@ import mozilla.components.support.webextensions.WebExtensionSupport |
| 20 | 20 | import mozilla.components.support.base.log.logger.Logger
|
| 21 | 21 | import org.mozilla.fenix.ext.components
|
| 22 | 22 | import org.mozilla.fenix.ext.settings
|
| 23 | -import org.mozilla.fenix.tor.TorEvents
|
|
| 23 | +import org.mozilla.fenix.tor.RunOnceBootstrapped
|
|
| 24 | 24 | |
| 25 | 25 | object TorBrowserFeatures {
|
| 26 | 26 | private val logger = Logger("torbrowser-features")
|
| ... | ... | @@ -142,9 +142,8 @@ object TorBrowserFeatures { |
| 142 | 142 | * causing automatic update checks failures (components.addonUpdater being a lazy prop).
|
| 143 | 143 | * The extension, from then on, should behave as if the user had installed it manually.
|
| 144 | 144 | */
|
| 145 | - context.components.torController.registerTorListener(object : TorEvents {
|
|
| 146 | - override fun onTorConnected() {
|
|
| 147 | - context.components.torController.unregisterTorListener(this)
|
|
| 145 | + context.components.torController.registerRunOnceBootstrapped(object : RunOnceBootstrapped {
|
|
| 146 | + override fun onBootstrapped() {
|
|
| 148 | 147 | // Enable automatic updates. This must be done on every startup (tor-browser#42353)
|
| 149 | 148 | context.components.addonUpdater.registerForFutureUpdates(NOSCRIPT_ID)
|
| 150 | 149 | // Force a one-time immediate update check for older installations
|
| ... | ... | @@ -153,18 +152,6 @@ object TorBrowserFeatures { |
| 153 | 152 | settings.noscriptUpdated = 2
|
| 154 | 153 | }
|
| 155 | 154 | }
|
| 156 | - |
|
| 157 | - @SuppressWarnings("EmptyFunctionBlock")
|
|
| 158 | - override fun onTorConnecting() {
|
|
| 159 | - }
|
|
| 160 | - |
|
| 161 | - @SuppressWarnings("EmptyFunctionBlock")
|
|
| 162 | - override fun onTorStopped() {
|
|
| 163 | - }
|
|
| 164 | - |
|
| 165 | - @SuppressWarnings("EmptyFunctionBlock")
|
|
| 166 | - override fun onTorStatusUpdate(entry: String?, status: String?, progress: Double?) {
|
|
| 167 | - }
|
|
| 168 | 155 | })
|
| 169 | 156 | }
|
| 170 | 157 |
| ... | ... | @@ -56,7 +56,6 @@ class TorBridgeConfigFragment : PreferenceFragmentCompat() { |
| 56 | 56 | setOnPreferenceChangeListener<Boolean> { preference, enabled ->
|
| 57 | 57 | preference.context.components.torController.bridgesEnabled = enabled
|
| 58 | 58 | updateCurrentConfiguredBridgePref(preference)
|
| 59 | - preference.context.components.torController.restartTor()
|
|
| 60 | 59 | true
|
| 61 | 60 | }
|
| 62 | 61 | }
|
| ... | ... | @@ -71,7 +70,6 @@ class TorBridgeConfigFragment : PreferenceFragmentCompat() { |
| 71 | 70 | preference.context.components.torController.bridgeTransport = TorBridgeTransportConfig.USER_PROVIDED
|
| 72 | 71 | preference.context.components.torController.userProvidedBridges = userProvidedBridge
|
| 73 | 72 | updateCurrentConfiguredBridgePref(preference)
|
| 74 | - preference.context.components.torController.restartTor()
|
|
| 75 | 73 | true
|
| 76 | 74 | }
|
| 77 | 75 | val userProvidedBridge: String? = context.components.torController.userProvidedBridges
|
| ... | ... | @@ -103,7 +101,6 @@ class TorBridgeConfigFragment : PreferenceFragmentCompat() { |
| 103 | 101 | preference.context.components.torController.bridgeTransport = bridge
|
| 104 | 102 | previousTransportConfig = bridge
|
| 105 | 103 | updateCurrentConfiguredBridgePref(preference)
|
| 106 | - preference.context.components.torController.restartTor()
|
|
| 107 | 104 | }
|
| 108 | 105 | true
|
| 109 | 106 | }
|
| ... | ... | @@ -27,20 +27,9 @@ class TorBootstrapProgressViewModel( |
| 27 | 27 | super.onCleared()
|
| 28 | 28 | }
|
| 29 | 29 | |
| 30 | - override fun onBootstrapStateChange(state: String?) {}
|
|
| 31 | 30 | override fun onBootstrapStageChange(stage: TorConnectStage) = Unit
|
| 32 | 31 | |
| 33 | 32 | override fun onBootstrapProgress(progress: Double, hasWarnings: Boolean) {
|
| 34 | 33 | this.progress.value = progress.toInt()
|
| 35 | 34 | }
|
| 36 | - |
|
| 37 | - override fun onBootstrapComplete() {}
|
|
| 38 | - |
|
| 39 | - override fun onBootstrapError(
|
|
| 40 | - code: String?,
|
|
| 41 | - message: String?,
|
|
| 42 | - phase: String?,
|
|
| 43 | - reason: String?,
|
|
| 44 | - ) {
|
|
| 45 | - }
|
|
| 46 | 35 | } |
| ... | ... | @@ -154,23 +154,12 @@ class TorConnectionAssistViewModel( |
| 154 | 154 | }
|
| 155 | 155 | }
|
| 156 | 156 | |
| 157 | - override fun onBootstrapStateChange(state: String?) {}
|
|
| 158 | - |
|
| 159 | - override fun onBootstrapStageChange(stage: TorConnectStage?) {
|
|
| 157 | + override fun onBootstrapStageChange(stage: TorConnectStage) {
|
|
| 160 | 158 | torConnectStage.value = stage
|
| 161 | 159 | }
|
| 162 | 160 | |
| 163 | 161 | override fun onBootstrapProgress(progress: Double, hasWarnings: Boolean) {}
|
| 164 | 162 | |
| 165 | - override fun onBootstrapComplete() {}
|
|
| 166 | - |
|
| 167 | - override fun onBootstrapError(
|
|
| 168 | - code: String?,
|
|
| 169 | - message: String?,
|
|
| 170 | - phase: String?,
|
|
| 171 | - reason: String?,
|
|
| 172 | - ) {}
|
|
| 173 | - |
|
| 174 | 163 | fun button1ShouldBeDisabled(screen: ConnectAssistUiState): Boolean {
|
| 175 | 164 | return selectedCountryCode.value == "automatic" && screen.regionDropDownDefaultItem == R.string.connection_assist_select_country_or_region
|
| 176 | 165 | }
|
| ... | ... | @@ -6,37 +6,14 @@ package org.mozilla.fenix.tor |
| 6 | 6 | |
| 7 | 7 | import androidx.lifecycle.LifecycleCoroutineScope
|
| 8 | 8 | |
| 9 | -interface TorEvents {
|
|
| 10 | - fun onTorConnecting()
|
|
| 11 | - fun onTorConnected()
|
|
| 12 | - fun onTorStatusUpdate(entry: String?, status: String?, progress: Double? = 0.0)
|
|
| 13 | - fun onTorStopped()
|
|
| 9 | +// Callback for function to be run one time when the system is bootstrapped and then disregarded
|
|
| 10 | +interface RunOnceBootstrapped {
|
|
| 11 | + fun onBootstrapped()
|
|
| 14 | 12 | }
|
| 15 | -class TorError(
|
|
| 16 | - var message: String,
|
|
| 17 | - var details: String,
|
|
| 18 | - var phase: String,
|
|
| 19 | - var reason: String,
|
|
| 20 | -) { }
|
|
| 21 | 13 | |
| 22 | -interface TorLogs {
|
|
| 23 | - fun onLog(type: String?, message: String?, timestamp: String?)
|
|
| 24 | -}
|
|
| 25 | - |
|
| 26 | -internal enum class TorStatus(val status: String) {
|
|
| 27 | - OFF("OFF"),
|
|
| 28 | - STARTING("STARTING"),
|
|
| 29 | - ON("ON"),
|
|
| 30 | - STOPPING("STOPPING"),
|
|
| 31 | - UNKNOWN("UNKNOWN");
|
|
| 32 | -}
|
|
| 33 | - |
|
| 34 | -interface TorController: TorEvents {
|
|
| 14 | +interface TorController {
|
|
| 35 | 15 | val logEntries: MutableList<TorLog>
|
| 36 | - val isStarting: Boolean
|
|
| 37 | - val isRestarting: Boolean
|
|
| 38 | 16 | val isBootstrapped: Boolean
|
| 39 | - val isConnected: Boolean
|
|
| 40 | 17 | var bridgesEnabled: Boolean
|
| 41 | 18 | var bridgeTransport: TorBridgeTransportConfig
|
| 42 | 19 | var userProvidedBridges: String?
|
| ... | ... | @@ -44,21 +21,21 @@ interface TorController: TorEvents { |
| 44 | 21 | fun start()
|
| 45 | 22 | fun stop()
|
| 46 | 23 | |
| 47 | - override fun onTorConnecting()
|
|
| 48 | - override fun onTorConnected()
|
|
| 49 | - override fun onTorStatusUpdate(entry: String?, status: String?, progress: Double?)
|
|
| 50 | - override fun onTorStopped()
|
|
| 51 | - |
|
| 52 | - fun getLastErrorState() : TorError?
|
|
| 53 | - |
|
| 54 | - fun registerTorListener(l: TorEvents)
|
|
| 55 | - fun unregisterTorListener(l: TorEvents)
|
|
| 56 | - |
|
| 57 | - fun registerTorLogListener(l: TorLogs)
|
|
| 58 | - fun unregisterTorLogListener(l: TorLogs)
|
|
| 24 | + // TorBrowserFeatures.install wants to register a callback for when tor bootstraps the first time
|
|
| 25 | + // so it can then check for noscript updates.
|
|
| 26 | + // Currently it needs to register it before TorAndroidIntegration is fully loaded, so this way
|
|
| 27 | + // they can register with TorController which will start streaming events from TAS when available
|
|
| 28 | + // and call them one time when the system is bootstrapped
|
|
| 29 | + // TODO: rewire the noscript update call in TorBrowserFeatures.install
|
|
| 30 | + // a) call TorBrowserFeatures.install from somewhere else (ex: move from Core.GeckoEngine.also
|
|
| 31 | + // to maybe FenixApplication.setupInMainProcessOnly
|
|
| 32 | + // dan: had trouble with this first time:
|
|
| 33 | + // https://gitlab.torproject.org/tpo/applications/tor-browser/-/merge_requests/1423#note_3191590
|
|
| 34 | + // b) just move the call to `context.components.addonUpdater.update(NOSCRIPT_ID)` somewhere else
|
|
| 35 | + // that can use TorAndroidIntegration.BootstrapListener
|
|
| 36 | + fun registerRunOnceBootstrapped(rob: RunOnceBootstrapped)
|
|
| 37 | + fun unregisterRunOnceBootstrapped(rob: RunOnceBootstrapped)
|
|
| 59 | 38 | |
| 60 | 39 | fun initiateTorBootstrap(lifecycleScope: LifecycleCoroutineScope? = null, withDebugLogging: Boolean = false)
|
| 61 | 40 | fun stopTor()
|
| 62 | - fun setTorStopped()
|
|
| 63 | - fun restartTor()
|
|
| 64 | 41 | } |
| ... | ... | @@ -4,75 +4,30 @@ package org.mozilla.fenix.tor |
| 4 | 4 | import android.content.Context
|
| 5 | 5 | import android.util.Log
|
| 6 | 6 | import androidx.lifecycle.LifecycleCoroutineScope
|
| 7 | -import kotlinx.coroutines.flow.MutableStateFlow
|
|
| 8 | -import kotlinx.coroutines.flow.StateFlow
|
|
| 9 | 7 | import mozilla.components.browser.engine.gecko.GeckoEngine
|
| 10 | 8 | import org.mozilla.fenix.ext.components
|
| 11 | 9 | import org.mozilla.geckoview.TorAndroidIntegration
|
| 12 | 10 | import org.mozilla.geckoview.TorAndroidIntegration.BootstrapStateChangeListener
|
| 13 | 11 | import org.mozilla.geckoview.TorAndroidIntegration.TorLogListener
|
| 14 | 12 | import org.mozilla.geckoview.TorConnectStage
|
| 13 | +import org.mozilla.geckoview.TorConnectStageName
|
|
| 15 | 14 | import org.mozilla.geckoview.TorSettings
|
| 16 | 15 | import org.mozilla.geckoview.TorSettings.BridgeBuiltinType
|
| 17 | 16 | import org.mozilla.geckoview.TorSettings.BridgeSource
|
| 18 | 17 | |
| 19 | -// Enum matching TorConnectState from TorConnect.sys.mjs that we get from onBootstrapStateChange
|
|
| 20 | -internal enum class TorConnectState(val state: String) {
|
|
| 21 | - Initial("Initial"),
|
|
| 22 | - Configuring("Configuring"),
|
|
| 23 | - AutoBootstrapping("AutoBootstrapping"),
|
|
| 24 | - Bootstrapping("Bootstrapping"),
|
|
| 25 | - Error("Error"),
|
|
| 26 | - Bootstrapped("Bootstrapped"),
|
|
| 27 | - Disabled("Disabled");
|
|
| 28 | - |
|
| 29 | - fun isStarting() = this == Bootstrapping || this == AutoBootstrapping
|
|
| 30 | - fun isError() = this == Error
|
|
| 31 | - |
|
| 32 | - fun isStarted() = this == Bootstrapped
|
|
| 33 | - |
|
| 34 | - fun isOff() = this == Initial || this == Configuring || this == Disabled || this == Error
|
|
| 35 | - |
|
| 36 | - |
|
| 37 | - // Convert to TorStatus that firefox-android uses based on tor-android-service
|
|
| 38 | - fun toTorStatus(): TorStatus {
|
|
| 39 | - return when (this) {
|
|
| 40 | - Initial -> TorStatus.OFF
|
|
| 41 | - Configuring -> TorStatus.OFF
|
|
| 42 | - AutoBootstrapping -> TorStatus.STARTING
|
|
| 43 | - Bootstrapping -> TorStatus.STARTING
|
|
| 44 | - Error -> TorStatus.UNKNOWN
|
|
| 45 | - Bootstrapped -> TorStatus.ON
|
|
| 46 | - Disabled -> TorStatus.OFF
|
|
| 47 | - }
|
|
| 48 | - }
|
|
| 49 | -}
|
|
| 50 | - |
|
| 51 | 18 | class TorControllerGV(
|
| 52 | 19 | private val context: Context,
|
| 53 | -) : TorController, TorEvents, BootstrapStateChangeListener, TorLogListener {
|
|
| 20 | +) : TorController, BootstrapStateChangeListener, TorLogListener {
|
|
| 54 | 21 | |
| 55 | 22 | private val TAG = "TorControllerGV"
|
| 56 | 23 | |
| 57 | - private var torListeners = mutableListOf<TorEvents>()
|
|
| 58 | - private var torLogListeners = mutableListOf<TorLogs>()
|
|
| 59 | - |
|
| 60 | - private val _lastKnownStatus = MutableStateFlow(TorConnectState.Initial)
|
|
| 61 | - internal val lastKnownStatus: StateFlow<TorConnectState> = _lastKnownStatus
|
|
| 24 | + private var runOnceBootstrappedHandlers = mutableListOf<RunOnceBootstrapped>()
|
|
| 62 | 25 | |
| 63 | - internal var lastKnownError: TorError? = null
|
|
| 64 | - private var wasTorBootstrapped = false
|
|
| 65 | - private var isTorRestarting = false
|
|
| 66 | - |
|
| 67 | - private var isTorBootstrapped = false
|
|
| 68 | - get() = ((_lastKnownStatus.value.isStarted()) && wasTorBootstrapped)
|
|
| 26 | + override val isBootstrapped get() =
|
|
| 27 | + getTorIntegration().lastKnowStage.value?.name?.isBootstrapped ?: false
|
|
| 69 | 28 | |
| 70 | 29 | private val entries = mutableListOf<TorLog>()
|
| 71 | 30 | override val logEntries get() = entries
|
| 72 | - override val isStarting get() = _lastKnownStatus.value.isStarting()
|
|
| 73 | - override val isRestarting get() = isTorRestarting
|
|
| 74 | - override val isBootstrapped get() = isTorBootstrapped
|
|
| 75 | - override val isConnected get() = (_lastKnownStatus.value.isStarted() && !isTorRestarting)
|
|
| 76 | 31 | |
| 77 | 32 | private fun getTorIntegration(): TorAndroidIntegration {
|
| 78 | 33 | return (context.components.core.engine as GeckoEngine).getTorIntegrationController()
|
| ... | ... | @@ -82,8 +37,7 @@ class TorControllerGV( |
| 82 | 37 | return getTorIntegration().getSettings()
|
| 83 | 38 | }
|
| 84 | 39 | |
| 85 | - |
|
| 86 | - // On a fresh install bridgeEnagled can be set to true without a valid bridgeSource
|
|
| 40 | + // On a fresh install bridgeEnabled can be set to true without a valid bridgeSource
|
|
| 87 | 41 | // having been selected. After first use this will not happen because last selected bridge
|
| 88 | 42 | // will be remembered and reused.
|
| 89 | 43 | // However, on first use, submitting this to TorSettings is an invalid state.
|
| ... | ... | @@ -105,7 +59,6 @@ class TorControllerGV( |
| 105 | 59 | }
|
| 106 | 60 | }
|
| 107 | 61 | |
| 108 | - |
|
| 109 | 62 | override var bridgeTransport: TorBridgeTransportConfig
|
| 110 | 63 | get() {
|
| 111 | 64 | return when (getTorSettings()?.bridgesSource) {
|
| ... | ... | @@ -144,7 +97,6 @@ class TorControllerGV( |
| 144 | 97 | }
|
| 145 | 98 | }
|
| 146 | 99 | |
| 147 | - |
|
| 148 | 100 | // Currently the UI takes a user provided string and sets this in one step so there is where we
|
| 149 | 101 | // actually set it.bridgesSource = BridgeSource.UserProvided, not above,
|
| 150 | 102 | // as TorSettings.sys.mjs #cleanupSettings could reject BridgeSource.UserProvided
|
| ... | ... | @@ -179,73 +131,37 @@ class TorControllerGV( |
| 179 | 131 | getTorIntegration().unregisterLogListener(this)
|
| 180 | 132 | }
|
| 181 | 133 | |
| 182 | - // TorEvents
|
|
| 183 | - override fun onTorConnecting() {
|
|
| 184 | - synchronized(torListeners) {
|
|
| 185 | - torListeners.toList().forEach { it.onTorConnecting() }
|
|
| 186 | - }
|
|
| 187 | - }
|
|
| 188 | - |
|
| 189 | - // TorEvents
|
|
| 190 | - override fun onTorConnected() {
|
|
| 191 | - synchronized(torListeners) {
|
|
| 192 | - torListeners.toList().forEach { it.onTorConnected() }
|
|
| 193 | - }
|
|
| 194 | - }
|
|
| 195 | - |
|
| 196 | - // TorEvents
|
|
| 197 | - override fun onTorStatusUpdate(entry: String?, status: String?, progress: Double?) {
|
|
| 198 | - synchronized(torListeners) {
|
|
| 199 | - torListeners.toList().forEach { it.onTorStatusUpdate(entry, status, progress) }
|
|
| 200 | - }
|
|
| 201 | - }
|
|
| 202 | - |
|
| 203 | - // TorEvents
|
|
| 204 | - override fun onTorStopped() {
|
|
| 205 | - synchronized(torListeners) {
|
|
| 206 | - torListeners.toList().forEach { it.onTorStopped() }
|
|
| 207 | - }
|
|
| 208 | - }
|
|
| 209 | - |
|
| 210 | 134 | override fun onLog(type: String?, message: String?, timestamp: String?) {
|
| 211 | - synchronized(torLogListeners) {
|
|
| 135 | + synchronized(entries) {
|
|
| 212 | 136 | entries.add(TorLog(type ?: "null", message ?: "null", timestamp ?: "null"))
|
| 213 | - torLogListeners.toList().forEach { it.onLog(type ?: "null", message ?: "null", timestamp) }
|
|
| 214 | - }
|
|
| 215 | - }
|
|
| 216 | - |
|
| 217 | - override fun registerTorListener(l: TorEvents) {
|
|
| 218 | - synchronized(torListeners) {
|
|
| 219 | - if (torListeners.contains(l)) {
|
|
| 220 | - return
|
|
| 221 | - }
|
|
| 222 | - torListeners.add(l)
|
|
| 223 | 137 | }
|
| 224 | 138 | }
|
| 225 | 139 | |
| 226 | - override fun unregisterTorListener(l: TorEvents) {
|
|
| 227 | - synchronized(torListeners) {
|
|
| 228 | - if (!torListeners.contains(l)) {
|
|
| 140 | + override fun registerRunOnceBootstrapped(rob: RunOnceBootstrapped) {
|
|
| 141 | + // TODO Remove need for this with tb-44002
|
|
| 142 | + // it would be nice to have a short circuit run and don't add if already bootstrapped
|
|
| 143 | + // however this calls context.components.core.engine which tries to lazy load engine
|
|
| 144 | + // which causes a recursive loop. instead we should do the work in tb-44002
|
|
| 145 | + // this is currently fine as there is a single use case for this called in
|
|
| 146 | + // TorBrowserFeatures that is at startup
|
|
| 147 | + //if (isBootstrapped) {
|
|
| 148 | + // rob.onBootstrapped()
|
|
| 149 | + // return
|
|
| 150 | + //}
|
|
| 151 | + synchronized(runOnceBootstrappedHandlers) {
|
|
| 152 | + if (runOnceBootstrappedHandlers.contains(rob)) {
|
|
| 229 | 153 | return
|
| 230 | 154 | }
|
| 231 | - torListeners.remove(l)
|
|
| 155 | + runOnceBootstrappedHandlers.add(rob)
|
|
| 232 | 156 | }
|
| 233 | 157 | }
|
| 234 | 158 | |
| 235 | - override fun registerTorLogListener(l: TorLogs) {
|
|
| 236 | - synchronized(torLogListeners) {
|
|
| 237 | - if (torLogListeners.contains(l)) {
|
|
| 238 | - return
|
|
| 239 | - }
|
|
| 240 | - torLogListeners.add(l)
|
|
| 241 | - }
|
|
| 242 | - }
|
|
| 243 | - override fun unregisterTorLogListener(l: TorLogs) {
|
|
| 244 | - synchronized(torLogListeners) {
|
|
| 245 | - if (!torLogListeners.contains(l)) {
|
|
| 159 | + override fun unregisterRunOnceBootstrapped(rob: RunOnceBootstrapped) {
|
|
| 160 | + synchronized(runOnceBootstrappedHandlers) {
|
|
| 161 | + if (!runOnceBootstrappedHandlers.contains(rob)) {
|
|
| 246 | 162 | return
|
| 247 | 163 | }
|
| 248 | - torLogListeners.remove(l)
|
|
| 164 | + runOnceBootstrappedHandlers.remove(rob)
|
|
| 249 | 165 | }
|
| 250 | 166 | }
|
| 251 | 167 | |
| ... | ... | @@ -260,82 +176,22 @@ class TorControllerGV( |
| 260 | 176 | getTorIntegration().cancelBootstrap()
|
| 261 | 177 | }
|
| 262 | 178 | |
| 263 | - override fun setTorStopped() {
|
|
| 264 | - _lastKnownStatus.value = TorConnectState.Configuring
|
|
| 265 | - onTorStatusUpdate(null, lastKnownStatus.toString(), 0.0)
|
|
| 266 | - onTorStopped()
|
|
| 267 | - }
|
|
| 268 | - |
|
| 269 | - override fun restartTor() {
|
|
| 270 | - if (!_lastKnownStatus.value.isStarted() && wasTorBootstrapped) {
|
|
| 271 | - // If we aren't started, but we were previously bootstrapped,
|
|
| 272 | - // then we handle a "restart" request as a "start" restart
|
|
| 273 | - initiateTorBootstrap()
|
|
| 274 | - } else {
|
|
| 275 | - // |isTorRestarting| tracks the state of restart. When we receive an |OFF| state
|
|
| 276 | - // from TorService in persistentBroadcastReceiver::onReceive we restart the Tor
|
|
| 277 | - // service.
|
|
| 278 | - isTorRestarting = true
|
|
| 279 | - stopTor()
|
|
| 280 | - }
|
|
| 281 | - }
|
|
| 282 | - |
|
| 283 | - override fun getLastErrorState() : TorError? {
|
|
| 284 | - return lastKnownError
|
|
| 285 | - }
|
|
| 286 | - |
|
| 287 | - // TorEventsBootstrapStateChangeListener -> (lastKnowStatus, TorEvents)
|
|
| 288 | - // Handle events from GeckoView TorAndroidIntegration and map to TorEvents based events
|
|
| 289 | - // and state for firefox-android (designed for tor-android-service)
|
|
| 290 | - // fun onTorConnecting()
|
|
| 291 | - // fun onTorConnected()
|
|
| 292 | - // fun onTorStatusUpdate(entry: String?, status: String?, progress: Double?)
|
|
| 293 | - // fun onTorStopped()
|
|
| 294 | - |
|
| 295 | 179 | // TorEventsBootstrapStateChangeListener
|
| 296 | - override fun onBootstrapStateChange(newStateVal: String?) {
|
|
| 297 | - Log.d(TAG, "onBootstrapStateChange(newStateVal = $newStateVal)")
|
|
| 298 | - val newState: TorConnectState = TorConnectState.valueOf(newStateVal ?: "Error")
|
|
| 299 | - |
|
| 300 | - if (newState.isError() && wasTorBootstrapped) {
|
|
| 301 | - stopTor()
|
|
| 302 | - }
|
|
| 303 | - |
|
| 304 | - if (newState.isStarted()) {
|
|
| 305 | - wasTorBootstrapped = true
|
|
| 306 | - onTorConnected()
|
|
| 307 | - }
|
|
| 308 | - |
|
| 309 | - if (wasTorBootstrapped && newState == TorConnectState.Configuring) {
|
|
| 310 | - wasTorBootstrapped = false
|
|
| 311 | - if (isTorRestarting) {
|
|
| 312 | - initiateTorBootstrap()
|
|
| 313 | - } else {
|
|
| 314 | - setTorStopped()
|
|
| 180 | + override fun onBootstrapStageChange(stage: TorConnectStage) {
|
|
| 181 | + Log.d(TAG, "onBootstrapStageChange(stage = $stage)")
|
|
| 182 | + |
|
| 183 | + if (stage.name == TorConnectStageName.Bootstrapped) {
|
|
| 184 | + synchronized(runOnceBootstrappedHandlers) {
|
|
| 185 | + runOnceBootstrappedHandlers.toList().forEach {
|
|
| 186 | + it.onBootstrapped()
|
|
| 187 | + runOnceBootstrappedHandlers.remove(it)
|
|
| 188 | + }
|
|
| 315 | 189 | }
|
| 316 | 190 | }
|
| 317 | - |
|
| 318 | - if (_lastKnownStatus.value.isOff() && newState.isStarting()) {
|
|
| 319 | - isTorRestarting = false
|
|
| 320 | - }
|
|
| 321 | - |
|
| 322 | - _lastKnownStatus.value = newState
|
|
| 323 | - onTorStatusUpdate(null, newStateVal, null)
|
|
| 324 | 191 | }
|
| 325 | 192 | |
| 326 | - override fun onBootstrapStageChange(stage: TorConnectStage) = Unit
|
|
| 327 | - |
|
| 328 | 193 | // TorEventsBootstrapStateChangeListener
|
| 329 | 194 | override fun onBootstrapProgress(progress: Double, hasWarnings: Boolean) {
|
| 330 | 195 | Log.d(TAG, "onBootstrapProgress(progress = $progress, hasWarnings = $hasWarnings)")
|
| 331 | - onTorStatusUpdate("", _lastKnownStatus.value.toTorStatus().status, progress)
|
|
| 332 | - }
|
|
| 333 | - |
|
| 334 | - // TorEventsBootstrapStateChangeListener
|
|
| 335 | - override fun onBootstrapComplete() = Unit
|
|
| 336 | - |
|
| 337 | - // TorEventsBootstrapStateChangeListener
|
|
| 338 | - override fun onBootstrapError(code: String?, message: String?, phase: String?, reason: String?) {
|
|
| 339 | - lastKnownError = TorError(code ?: "", message ?: "", phase ?: "", reason ?: "")
|
|
| 340 | 196 | }
|
| 341 | 197 | } |
| ... | ... | @@ -13,11 +13,15 @@ import android.widget.Toast |
| 13 | 13 | import androidx.lifecycle.AndroidViewModel
|
| 14 | 14 | import androidx.lifecycle.LiveData
|
| 15 | 15 | import androidx.lifecycle.MutableLiveData
|
| 16 | +import mozilla.components.browser.engine.gecko.GeckoEngine
|
|
| 16 | 17 | import org.mozilla.fenix.R
|
| 17 | 18 | import org.mozilla.fenix.ext.components
|
| 19 | +import org.mozilla.geckoview.TorAndroidIntegration.TorLogListener
|
|
| 18 | 20 | |
| 19 | -class TorLogsViewModel(application: Application) : AndroidViewModel(application), TorLogs {
|
|
| 21 | +class TorLogsViewModel(application: Application) : AndroidViewModel(application), TorLogListener {
|
|
| 20 | 22 | private val torController = application.components.torController
|
| 23 | + private val engine = application.components.core.engine as GeckoEngine
|
|
| 24 | + private val torAndroidIntegration = engine.getTorIntegrationController()
|
|
| 21 | 25 | private val clipboardManager =
|
| 22 | 26 | application.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
| 23 | 27 | |
| ... | ... | @@ -33,7 +37,7 @@ class TorLogsViewModel(application: Application) : AndroidViewModel(application) |
| 33 | 37 | |
| 34 | 38 | init {
|
| 35 | 39 | setupClipboardListener()
|
| 36 | - torController.registerTorLogListener(this)
|
|
| 40 | + torAndroidIntegration.registerLogListener(this)
|
|
| 37 | 41 | val currentEntries = torController.logEntries
|
| 38 | 42 | for (log in currentEntries) {
|
| 39 | 43 | addLog(log)
|
| ... | ... | @@ -46,7 +50,7 @@ class TorLogsViewModel(application: Application) : AndroidViewModel(application) |
| 46 | 50 | |
| 47 | 51 | override fun onCleared() {
|
| 48 | 52 | super.onCleared()
|
| 49 | - torController.unregisterTorLogListener(this)
|
|
| 53 | + torAndroidIntegration.unregisterLogListener(this)
|
|
| 50 | 54 | }
|
| 51 | 55 | |
| 52 | 56 | private fun setupClipboardListener() {
|
| ... | ... | @@ -39,11 +39,8 @@ public class TorAndroidIntegration implements BundleEventListener { |
| 39 | 39 | private static final String EVENT_TOR_STOP = "GeckoView:Tor:StopTor";
|
| 40 | 40 | private static final String EVENT_MEEK_START = "GeckoView:Tor:StartMeek";
|
| 41 | 41 | private static final String EVENT_MEEK_STOP = "GeckoView:Tor:StopMeek";
|
| 42 | - private static final String EVENT_CONNECT_STATE_CHANGED = "GeckoView:Tor:ConnectStateChanged"; // deprecation path
|
|
| 43 | - private static final String EVENT_CONNECT_STAGE_CHANGED = "GeckoView:Tor:ConnectStageChanged"; // replacement path
|
|
| 44 | - private static final String EVENT_CONNECT_ERROR = "GeckoView:Tor:ConnectError";
|
|
| 42 | + private static final String EVENT_CONNECT_STAGE_CHANGED = "GeckoView:Tor:ConnectStageChanged";
|
|
| 45 | 43 | private static final String EVENT_BOOTSTRAP_PROGRESS = "GeckoView:Tor:BootstrapProgress";
|
| 46 | - private static final String EVENT_BOOTSTRAP_COMPLETE = "GeckoView:Tor:BootstrapComplete";
|
|
| 47 | 44 | private static final String EVENT_TOR_LOGS = "GeckoView:Tor:Logs";
|
| 48 | 45 | private static final String EVENT_SETTINGS_READY = "GeckoView:Tor:SettingsReady";
|
| 49 | 46 | private static final String EVENT_SETTINGS_CHANGED = "GeckoView:Tor:SettingsChanged";
|
| ... | ... | @@ -62,6 +59,7 @@ public class TorAndroidIntegration implements BundleEventListener { |
| 62 | 59 | private static final String EVENT_QUICKSTART_GET = "GeckoView:Tor:QuickstartGet";
|
| 63 | 60 | private static final String EVENT_QUICKSTART_SET = "GeckoView:Tor:QuickstartSet";
|
| 64 | 61 | private static final String EVENT_REGION_NAMES_GET = "GeckoView:Tor:RegionNamesGet";
|
| 62 | + private static final String EVENT_SHOULD_SHOW_TOR_CONNECT = "GeckoView:Tor:ShouldShowTorConnect";
|
|
| 65 | 63 | |
| 66 | 64 | private static final String CONTROL_PORT_FILE = "/control-ipc";
|
| 67 | 65 | private static final String SOCKS_FILE = "/socks-ipc";
|
| ... | ... | @@ -124,11 +122,8 @@ public class TorAndroidIntegration implements BundleEventListener { |
| 124 | 122 | EVENT_MEEK_STOP,
|
| 125 | 123 | EVENT_SETTINGS_READY,
|
| 126 | 124 | EVENT_SETTINGS_CHANGED,
|
| 127 | - EVENT_CONNECT_STATE_CHANGED,
|
|
| 128 | 125 | EVENT_CONNECT_STAGE_CHANGED,
|
| 129 | - EVENT_CONNECT_ERROR,
|
|
| 130 | 126 | EVENT_BOOTSTRAP_PROGRESS,
|
| 131 | - EVENT_BOOTSTRAP_COMPLETE,
|
|
| 132 | 127 | EVENT_TOR_LOGS);
|
| 133 | 128 | }
|
| 134 | 129 | |
| ... | ... | @@ -157,35 +152,18 @@ public class TorAndroidIntegration implements BundleEventListener { |
| 157 | 152 | } else {
|
| 158 | 153 | Log.w(TAG, "Ignoring a settings changed event that did not have the new settings.");
|
| 159 | 154 | }
|
| 160 | - } else if (EVENT_CONNECT_STATE_CHANGED.equals(event)) {
|
|
| 161 | - String state = message.getString("state");
|
|
| 162 | - for (BootstrapStateChangeListener listener : mBootstrapStateListeners) {
|
|
| 163 | - listener.onBootstrapStateChange(state);
|
|
| 164 | - }
|
|
| 165 | 155 | } else if (EVENT_CONNECT_STAGE_CHANGED.equals(event)) {
|
| 166 | 156 | TorConnectStage stage = new TorConnectStage(message.getBundle("stage"));
|
| 167 | 157 | _lastKnownStage.setValue(stage);
|
| 168 | 158 | for (BootstrapStateChangeListener listener : mBootstrapStateListeners) {
|
| 169 | 159 | listener.onBootstrapStageChange(stage);
|
| 170 | 160 | }
|
| 171 | - } else if (EVENT_CONNECT_ERROR.equals(event)) {
|
|
| 172 | - String code = message.getString("code");
|
|
| 173 | - String msg = message.getString("message");
|
|
| 174 | - String phase = message.getString("phase");
|
|
| 175 | - String reason = message.getString("reason");
|
|
| 176 | - for (BootstrapStateChangeListener listener : mBootstrapStateListeners) {
|
|
| 177 | - listener.onBootstrapError(code, msg, phase, reason);
|
|
| 178 | - }
|
|
| 179 | 161 | } else if (EVENT_BOOTSTRAP_PROGRESS.equals(event)) {
|
| 180 | 162 | double progress = message.getDouble("progress");
|
| 181 | 163 | boolean hasWarnings = message.getBoolean("hasWarnings");
|
| 182 | 164 | for (BootstrapStateChangeListener listener : mBootstrapStateListeners) {
|
| 183 | 165 | listener.onBootstrapProgress(progress, hasWarnings);
|
| 184 | 166 | }
|
| 185 | - } else if (EVENT_BOOTSTRAP_COMPLETE.equals(event)) {
|
|
| 186 | - for (BootstrapStateChangeListener listener : mBootstrapStateListeners) {
|
|
| 187 | - listener.onBootstrapComplete();
|
|
| 188 | - }
|
|
| 189 | 167 | } else if (EVENT_TOR_LOGS.equals(event)) {
|
| 190 | 168 | String msg = message.getString("message");
|
| 191 | 169 | String type = message.getString("logType");
|
| ... | ... | @@ -647,15 +625,9 @@ public class TorAndroidIntegration implements BundleEventListener { |
| 647 | 625 | }
|
| 648 | 626 | |
| 649 | 627 | public interface BootstrapStateChangeListener {
|
| 650 | - void onBootstrapStateChange(String state); // depreaction path
|
|
| 651 | - |
|
| 652 | - void onBootstrapStageChange(TorConnectStage stage); // new upgrade
|
|
| 628 | + void onBootstrapStageChange(@NonNull TorConnectStage stage); // new upgrade
|
|
| 653 | 629 | |
| 654 | 630 | void onBootstrapProgress(double progress, boolean hasWarnings);
|
| 655 | - |
|
| 656 | - void onBootstrapComplete();
|
|
| 657 | - |
|
| 658 | - void onBootstrapError(String code, String message, String phase, String reason);
|
|
| 659 | 631 | }
|
| 660 | 632 | |
| 661 | 633 | public interface TorLogListener {
|
| ... | ... | @@ -736,6 +708,17 @@ public class TorAndroidIntegration implements BundleEventListener { |
| 736 | 708 | });
|
| 737 | 709 | }
|
| 738 | 710 | |
| 711 | + public interface ShouldShowTorConnectGetter {
|
|
| 712 | + void onValue(Boolean shouldShowTorConnect);
|
|
| 713 | + }
|
|
| 714 | + |
|
| 715 | + public void shouldShowTorConnectGet(ShouldShowTorConnectGetter shouldShowTorConnectGetter) {
|
|
| 716 | + EventDispatcher.getInstance().queryBoolean(EVENT_SHOULD_SHOW_TOR_CONNECT).then(shouldShowTorConnect -> {
|
|
| 717 | + shouldShowTorConnectGetter.onValue(shouldShowTorConnect);
|
|
| 718 | + return new GeckoResult<Void>();
|
|
| 719 | + });
|
|
| 720 | + }
|
|
| 721 | + |
|
| 739 | 722 | public @NonNull GeckoResult<Void> beginBootstrap() {
|
| 740 | 723 | return EventDispatcher.getInstance().queryVoid(EVENT_BOOTSTRAP_BEGIN);
|
| 741 | 724 | }
|
| ... | ... | @@ -754,21 +737,21 @@ public class TorAndroidIntegration implements BundleEventListener { |
| 754 | 737 | return EventDispatcher.getInstance().queryVoid(EVENT_BOOTSTRAP_CANCEL);
|
| 755 | 738 | }
|
| 756 | 739 | |
| 757 | - public void registerBootstrapStateChangeListener(BootstrapStateChangeListener listener) {
|
|
| 740 | + public synchronized void registerBootstrapStateChangeListener(BootstrapStateChangeListener listener) {
|
|
| 758 | 741 | mBootstrapStateListeners.add(listener);
|
| 759 | 742 | }
|
| 760 | 743 | |
| 761 | - public void unregisterBootstrapStateChangeListener(BootstrapStateChangeListener listener) {
|
|
| 744 | + public synchronized void unregisterBootstrapStateChangeListener(BootstrapStateChangeListener listener) {
|
|
| 762 | 745 | mBootstrapStateListeners.remove(listener);
|
| 763 | 746 | }
|
| 764 | 747 | |
| 765 | 748 | private final HashSet<BootstrapStateChangeListener> mBootstrapStateListeners = new HashSet<>();
|
| 766 | 749 | |
| 767 | - public void registerLogListener(TorLogListener listener) {
|
|
| 750 | + public synchronized void registerLogListener(TorLogListener listener) {
|
|
| 768 | 751 | mLogListeners.add(listener);
|
| 769 | 752 | }
|
| 770 | 753 | |
| 771 | - public void unregisterLogListener(TorLogListener listener) {
|
|
| 754 | + public synchronized void unregisterLogListener(TorLogListener listener) {
|
|
| 772 | 755 | mLogListeners.remove(listener);
|
| 773 | 756 | }
|
| 774 | 757 |
| 1 | +package org.mozilla.geckoview;
|
|
| 2 | + |
|
| 3 | +import org.mozilla.gecko.util.GeckoBundle;
|
|
| 4 | + |
|
| 5 | +public class TorConnectError {
|
|
| 6 | + public String code;
|
|
| 7 | + public String message;
|
|
| 8 | + public String phase;
|
|
| 9 | + public String reason;
|
|
| 10 | + |
|
| 11 | + public TorConnectError(GeckoBundle bundle) {
|
|
| 12 | + code = bundle.getString("code");
|
|
| 13 | + message = bundle.getString("message");
|
|
| 14 | + phase = bundle.getString("phase");
|
|
| 15 | + reason = bundle.getString("reason");
|
|
| 16 | + }
|
|
| 17 | + |
|
| 18 | + public TorConnectError(String code, String message, String phase, String reason) {
|
|
| 19 | + this.code = code;
|
|
| 20 | + this.message = message;
|
|
| 21 | + this.phase = phase;
|
|
| 22 | + this.reason = reason;
|
|
| 23 | + }
|
|
| 24 | +} |
| ... | ... | @@ -5,24 +5,10 @@ import org.mozilla.gecko.util.GeckoBundle; |
| 5 | 5 | // Class to receive ConnectStage object from TorConnect.sys.mjs ~ln677
|
| 6 | 6 | public class TorConnectStage {
|
| 7 | 7 | |
| 8 | - public class Error {
|
|
| 9 | - public String code;
|
|
| 10 | - public String message;
|
|
| 11 | - public String phase;
|
|
| 12 | - public String reason;
|
|
| 13 | - |
|
| 14 | - public Error(GeckoBundle bundle) {
|
|
| 15 | - code = bundle.getString("code");
|
|
| 16 | - message = bundle.getString("message");
|
|
| 17 | - phase = bundle.getString("phase");
|
|
| 18 | - reason = bundle.getString("reason");
|
|
| 19 | - }
|
|
| 20 | - }
|
|
| 21 | - |
|
| 22 | 8 | public TorConnectStageName name;
|
| 23 | 9 | // The TorConnectStage prior to this bootstrap attempt. Only set during the "Bootstrapping" stage.
|
| 24 | 10 | public TorConnectStageName bootstrapTrigger;
|
| 25 | - public Error error;
|
|
| 11 | + public TorConnectError error;
|
|
| 26 | 12 | public String defaultRegion;
|
| 27 | 13 | public Boolean potentiallyBlocked;
|
| 28 | 14 | public Boolean tryAgain;
|
| ... | ... | @@ -37,7 +23,7 @@ public class TorConnectStage { |
| 37 | 23 | potentiallyBlocked = bundle.getBoolean("potentiallyBlocked");
|
| 38 | 24 | tryAgain = bundle.getBoolean("tryAgain");
|
| 39 | 25 | if (bundle.getBundle("error") != null) {
|
| 40 | - error = new Error(bundle.getBundle("error"));
|
|
| 26 | + error = new TorConnectError(bundle.getBundle("error"));
|
|
| 41 | 27 | }
|
| 42 | 28 | bootstrappingStatus = new TorBootstrappingStatus(bundle.getBundle("bootstrappingStatus"));
|
| 43 | 29 | }
|
| ... | ... | @@ -28,7 +28,6 @@ const EmittedEvents = Object.freeze({ |
| 28 | 28 | settingsChanged: "GeckoView:Tor:SettingsChanged",
|
| 29 | 29 | connectStateChanged: "GeckoView:Tor:ConnectStateChanged", // deprecation path
|
| 30 | 30 | connectStageChanged: "GeckoView:Tor:ConnectStageChanged", // new replacement path
|
| 31 | - connectError: "GeckoView:Tor:ConnectError",
|
|
| 32 | 31 | bootstrapProgress: "GeckoView:Tor:BootstrapProgress",
|
| 33 | 32 | bootstrapComplete: "GeckoView:Tor:BootstrapComplete",
|
| 34 | 33 | torLogs: "GeckoView:Tor:Logs",
|
| ... | ... | @@ -49,6 +48,7 @@ const ListenedEvents = Object.freeze({ |
| 49 | 48 | quickstartGet: "GeckoView:Tor:QuickstartGet",
|
| 50 | 49 | quickstartSet: "GeckoView:Tor:QuickstartSet",
|
| 51 | 50 | regionNamesGet: "GeckoView:Tor:RegionNamesGet",
|
| 51 | + shouldShowTorConnectGet: "GeckoView:Tor:ShouldShowTorConnect",
|
|
| 52 | 52 | });
|
| 53 | 53 | |
| 54 | 54 | class TorAndroidIntegrationImpl {
|
| ... | ... | @@ -134,16 +134,6 @@ class TorAndroidIntegrationImpl { |
| 134 | 134 | type: EmittedEvents.bootstrapComplete,
|
| 135 | 135 | });
|
| 136 | 136 | break;
|
| 137 | - // TODO: Replace with StageChange stage.error.
|
|
| 138 | - case lazy.TorConnectTopics.Error:
|
|
| 139 | - lazy.EventDispatcher.instance.sendRequest({
|
|
| 140 | - type: EmittedEvents.connectError,
|
|
| 141 | - code: subj.wrappedJSObject.code ?? "",
|
|
| 142 | - message: subj.wrappedJSObject.message ?? "",
|
|
| 143 | - phase: subj.wrappedJSObject.cause?.phase ?? "",
|
|
| 144 | - reason: subj.wrappedJSObject.cause?.reason ?? "",
|
|
| 145 | - });
|
|
| 146 | - break;
|
|
| 147 | 137 | case lazy.TorProviderTopics.TorLog:
|
| 148 | 138 | lazy.EventDispatcher.instance.sendRequest({
|
| 149 | 139 | type: EmittedEvents.torLogs,
|
| ... | ... | @@ -225,6 +215,9 @@ class TorAndroidIntegrationImpl { |
| 225 | 215 | case ListenedEvents.regionNamesGet:
|
| 226 | 216 | callback?.onSuccess(lazy.TorConnect.getRegionNames());
|
| 227 | 217 | return;
|
| 218 | + case ListenedEvents.shouldShowTorConnectGet:
|
|
| 219 | + callback?.onSuccess(lazy.TorConnect.shouldShowTorConnect());
|
|
| 220 | + return;
|
|
| 228 | 221 | }
|
| 229 | 222 | callback?.onSuccess();
|
| 230 | 223 | } catch (e) {
|