Dan Ballard pushed to branch tor-browser-153.0esr-16.0-1 at The Tor Project / Applications / Tor Browser Commits: fe45b5e3 by Henry Wilkes at 2026-08-05T12:12:42-06:00 fixup! TB 40597: Implement TorSettings module TB 43571: Allow android to enter ProviderStopped. - - - - - 80e2704f by Henry Wilkes at 2026-08-05T12:12:43-06:00 fixup! TB 42247: Android helpers for the TorProvider TB 43571: Drop `TorProviderBuilder.firstWindowLoaded`, which is covered by `TorConnect.init` now. - - - - - 48c8f5a9 by clairehurst at 2026-08-05T12:12:43-06:00 fixup! [android] Implement Android-native Connection Assist UI Bug 43570: Add tor exit prompt - - - - - ec776e7f by clairehurst at 2026-08-05T12:12:43-06:00 fixup! TB 42247: Android helpers for the TorProvider Bug 43570: Add tor exit prompt - - - - - 45aace81 by clairehurst at 2026-08-05T12:12:44-06:00 fixup! [android] TBA strings Bug 43570: Add tor exit prompt - - - - - 1efdc3dc by clairehurst at 2026-08-05T12:12:44-06:00 fixup! [android] Implement Android-native Connection Assist UI Bug 43570: Add tor exit prompt # Conflicts: # mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/ProviderStoppedViewModel.kt - - - - - 5b194faf by clairehurst at 2026-08-05T12:13:48-06:00 fixup! TB 40933: Add tor-launcher functionality Bug 43571: [Android] Add tor exit prompt Specifically, this commit is a patch provided from https://gitlab.torproject.org/tpo/applications/tor-browser/-/merge_requests/... - - - - - 21 changed files: - mobile/android/fenix/app/src/main/java/org/mozilla/fenix/HomeActivity.kt - mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SecretSettingsFragment.kt - mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/ConnectAssistUiState.kt - + mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/ProviderStoppedViewModel.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/res/drawable/bullet_point.xml - + mobile/android/fenix/app/src/main/res/drawable/loading_wheel.xml - + mobile/android/fenix/app/src/main/res/drawable/onion_broken.xml - mobile/android/fenix/app/src/main/res/layout/fragment_tor_connection_assist.xml - mobile/android/fenix/app/src/main/res/navigation/nav_graph.xml - mobile/android/fenix/app/src/main/res/values/preference_keys.xml - mobile/android/fenix/app/src/main/res/values/torbrowser_strings.xml - mobile/android/fenix/app/src/main/res/xml/secret_settings_preferences.xml - + mobile/android/geckoview/src/main/java/org/mozilla/geckoview/ProviderStatus.java - mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorAndroidIntegration.java - mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorConnectStage.java - mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorConnectStageName.java - toolkit/components/tor-launcher/TorProcessAndroid.sys.mjs - toolkit/modules/TorAndroidIntegration.sys.mjs - toolkit/modules/TorConnect.sys.mjs Changes: ===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/HomeActivity.kt ===================================== @@ -7,6 +7,7 @@ package org.mozilla.fenix import android.annotation.SuppressLint import android.app.assist.AssistContent import android.app.PendingIntent +import android.content.ActivityNotFoundException import android.content.ComponentName import android.content.Context import android.content.Intent @@ -19,6 +20,7 @@ import android.os.Bundle import android.os.StrictMode import android.text.format.DateUtils import android.util.AttributeSet +import android.util.Log import android.view.ActionMode import android.view.KeyEvent import android.view.LayoutInflater @@ -40,7 +42,9 @@ import androidx.core.net.toUri import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.text.layoutDirection import androidx.core.view.doOnLayout +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.NavController import androidx.navigation.NavDirections import androidx.navigation.fragment.NavHostFragment @@ -203,6 +207,7 @@ import org.mozilla.fenix.compose.snackbar.SnackbarState import org.mozilla.fenix.compose.snackbar.Snackbar import org.mozilla.fenix.tor.CustomSecurityLevelViewModel import org.mozilla.fenix.tor.TorController +import org.mozilla.fenix.tor.ProviderStoppedViewModel import org.mozilla.fenix.tor.UrlQuickLoadViewModel import org.mozilla.geckoview.TorAndroidIntegration.BootstrapStateChangeListener import org.mozilla.geckoview.TorConnectStage @@ -436,6 +441,8 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity, Crash private var dialog: RedirectDialogFragment? = null + private val providerStoppedViewModel: ProviderStoppedViewModel by viewModels() + private val urlQuickLoadViewModel: UrlQuickLoadViewModel by viewModels() private val customSecurityLevelViewModel: CustomSecurityLevelViewModel by viewModels() @@ -445,6 +452,18 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity, Crash // DO NOT MOVE ANYTHING ABOVE THIS getProfilerTime CALL. val startTimeProfiler = components.core.engine.profiler?.getProfilerTime() + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + providerStoppedViewModel.providerStoppedStateFlow.collect { isStopped -> + Log.d("providerStoppedViewModel", "isStopped = $isStopped") + if (isStopped) { + navHost.navController.navigate(NavGraphDirections.actionNavigateToConnectionAssistFromAnywhere()) + providerStoppedViewModel.providerStoppedStateFlow.value = false + } + } + } + } + // Setup nimbus-cli tooling. This is a NOOP when launching normally. components.nimbus.sdk.initializeTooling(applicationContext, intent) components.strictMode.attachListenerToDisablePenaltyDeath(supportFragmentManager) @@ -1788,4 +1807,14 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity, Crash components.torController.shutdown() exitProcess(0) } + + fun openBatterySaverSettings() { + try { + startActivity( + Intent(android.provider.Settings.ACTION_BATTERY_SAVER_SETTINGS) + ) + } catch (e: ActivityNotFoundException) { + e.printStackTrace() + } + } } ===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SecretSettingsFragment.kt ===================================== @@ -265,6 +265,13 @@ class SecretSettingsFragment : PreferenceFragmentCompat(), SystemInsetsPaddedFra } } + requirePreference<Preference>(R.string.pref_key_test_kill_tor).apply { + setOnPreferenceClickListener { + requireContext().components.core.geckoRuntime.torIntegrationController.shutdown() + true + } + } + requirePreference<SwitchPreferenceCompat>(R.string.pref_key_enable_fxsuggest).apply { isVisible = FeatureFlags.FX_SUGGEST isChecked = settings.enableFxSuggest ===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/ConnectAssistUiState.kt ===================================== @@ -3,8 +3,8 @@ package org.mozilla.fenix.tor import androidx.annotation.ColorRes import androidx.annotation.DrawableRes import androidx.annotation.StringRes -import mozilla.components.lib.crash.R as crashR import org.mozilla.fenix.R +import mozilla.components.lib.crash.R as crashR enum class ConnectAssistUiState( val progressBarVisible: Boolean, @@ -33,6 +33,7 @@ enum class ConnectAssistUiState( val torBootstrapButton2ShouldOpenSettings: Boolean = true, val wordmarkLogoVisible: Boolean = false, val torBootstrapButton2ShouldRestartApp: Boolean = false, + val torBootstrapButton1ShouldRestartTor: Boolean = false, ) { Loading( progressBarVisible = false, @@ -298,5 +299,30 @@ enum class ConnectAssistUiState( torBootstrapButton2TextStringResource = crashR.string.mozac_lib_crash_dialog_button_restart, torBootstrapButton2ShouldOpenSettings = false, torBootstrapButton2ShouldRestartApp = true, - ) + ), + ProviderStopped( + progressBarVisible = false, + backButtonVisible = false, + settingsButtonVisible = true, + torConnectImageVisible = true, + torConnectImageResource = R.drawable.onion_broken, + titleLargeTextViewVisible = true, + titleLargeTextViewTextStringResource = R.string.connection_assist_provider_stopped_title, + titleDescriptionVisible = true, + learnMoreStringResource = R.string.connection_assist_final_error_learn_more_link, + internetErrorDescription = R.string.connection_assist_provider_stopped_description1, + internetErrorDescription1 = R.string.connection_assist_provider_stopped_description2, + internetErrorDescription2 = R.string.connection_assist_provider_stopped_description3, + titleDescriptionTextStringResource = null, + quickstartSwitchVisible = false, + regionDropDownVisible = false, + torBootstrapButton1Visible = true, + torBootstrapButton1TextStringResource = R.string.connection_assist_restart_connection_button, + torBootstrapButton1ShouldOpenSettings = false, + torBootstrapButton1ShouldRestartTor = true, + torBootstrapButton2Visible = false, + torBootstrapButton2TextStringResource = null, + torBootstrapButton2ShouldOpenSettings = false, + torBootstrapButton2ShouldRestartApp = false, + ), } ===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/ProviderStoppedViewModel.kt ===================================== @@ -0,0 +1,49 @@ +package org.mozilla.fenix.tor + +import android.app.Application +import android.util.Log +import androidx.lifecycle.AndroidViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import org.mozilla.fenix.ext.components +import org.mozilla.geckoview.TorAndroidIntegration +import org.mozilla.geckoview.TorConnectStage +import org.mozilla.geckoview.TorConnectStageName + +class ProviderStoppedViewModel( + application: Application, +) : AndroidViewModel(application), TorAndroidIntegration.BootstrapStateChangeListener { + + private val TAG = "ProviderStoppedViewModel" + + internal val providerStoppedStateFlow: MutableStateFlow<Boolean> by lazy { MutableStateFlow(false) } + + private val _maybeConfigIssue = MutableStateFlow(false) + internal val maybeConfigIssue: StateFlow<Boolean> = _maybeConfigIssue + + override fun onBootstrapStageChange(stage: TorConnectStage) { + when (stage.name) { + TorConnectStageName.ProviderStopped -> { + Log.d(TAG, "ProviderStopped detected") + providerStoppedStateFlow.value = true + } + + else -> { + providerStoppedStateFlow.value = false + } + } + + _maybeConfigIssue.value = stage.providerStatus.maybeConfigIssue + } + + init { + getApplication<Application>().components.core.geckoRuntime.torIntegrationController.registerBootstrapStateChangeListener(this) + } + + override fun onCleared() { + getApplication<Application>().components.core.geckoRuntime.torIntegrationController.unregisterBootstrapStateChangeListener(this) + } + + override fun onBootstrapProgress(progress: Double, hasWarnings: Boolean) {} + +} ===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorConnectionAssistFragment.kt ===================================== @@ -8,6 +8,7 @@ import android.graphics.Color import android.graphics.Typeface import android.os.Build import android.os.Bundle +import android.os.PowerManager import android.text.SpannableString import android.text.Spanned import android.text.TextPaint @@ -17,9 +18,11 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.annotation.StringRes import androidx.appcompat.content.res.AppCompatResources import androidx.compose.foundation.Image import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -43,14 +46,22 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.LinkAnnotation +import androidx.compose.ui.text.TextLinkStyles import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.core.content.getSystemService import androidx.core.view.isEmpty import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels @@ -68,6 +79,7 @@ import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.databinding.FragmentTorConnectionAssistBinding import org.mozilla.fenix.e2e.SystemInsetsPaddedFragment +import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.hideToolbar class TorConnectionAssistFragment : Fragment(), UserInteractionHandler, SystemInsetsPaddedFragment { @@ -76,6 +88,7 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler, SystemIn private val progressViewModel: TorBootstrapProgressViewModel by viewModels() private val quickstartViewModel: QuickstartViewModel by activityViewModels() private val torConnectionAssistViewModel : TorConnectionAssistViewModel by viewModels() + private val providerStoppedViewModel : ProviderStoppedViewModel by activityViewModels() private var _binding: FragmentTorConnectionAssistBinding? = null private val binding get() = _binding!! @@ -103,6 +116,7 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler, SystemIn Log.d(TAG, "shouldOpenHome = $it") if (it) { openHome() + torConnectionAssistViewModel.shouldOpenHome.value = false } } @@ -200,13 +214,142 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler, SystemIn binding.torConnectImage.setImageResource(screen.torConnectImageResource) } + @Composable + fun TextWithClickable( + @StringRes mainTextRes: Int, + @StringRes clickableTextRes: Int, + onClick: () -> Unit, + style: TextStyle, + tag: String, + ) { + Text( + text = buildAnnotatedString { + val clickableText = stringResource(clickableTextRes) + val plainText = stringResource(mainTextRes, clickableText) + append(plainText) + addLink( + clickable = LinkAnnotation.Clickable( + tag = tag, + styles = TextLinkStyles( + style = style.toSpanStyle().copy(textDecoration = TextDecoration.Underline), + pressedStyle = style.toSpanStyle(), + ), + linkInteractionListener = { onClick() } + ), + start = plainText.indexOf(clickableText), + end = plainText.indexOf(clickableText) + clickableText.length, + ) + }, + style = style, + ) + } + + @Preview + @Composable + fun DaemonFailedScreen( + isDeviceInPowerSaveMode: Boolean = false, + maybeConfigIssue: Boolean = false, + style: TextStyle = TextStyle( + fontSize = 16.sp, + lineHeight = 24.sp, + fontWeight = FontWeight(400), + color = PhotonColors.LightGrey05, + letterSpacing = 0.5.sp, + ) + ) { + Column( + verticalArrangement = Arrangement.spacedBy(12.dp, Alignment.CenterVertically), + ) { + Text( + text = stringResource(R.string.connection_assist_provider_stopped_description1), + style = style, + ) + Text( + text = stringResource(R.string.connection_assist_provider_stopped_description2), + style = style.copy(fontWeight = FontWeight.Bold), + ) + if (isDeviceInPowerSaveMode && !maybeConfigIssue) { + Row { + Image( + painter = painterResource(R.drawable.bullet_point), + contentDescription = null, + modifier = Modifier.padding(6.dp) + ) + Text( + style = style, + text = stringResource(R.string.connection_assist_provider_stopped_description_battery1), + ) + } + } + Row { + Image( + painter = painterResource(R.drawable.bullet_point), + contentDescription = null, + modifier = Modifier.padding(6.dp) + ) + Text( + style = style, + text = stringResource(R.string.connection_assist_provider_stopped_description3), + ) + } + Text( + text = stringResource(R.string.connection_assist_provider_stopped_description4), + style = style.copy(fontWeight = FontWeight.Bold), + ) + if (isDeviceInPowerSaveMode && !maybeConfigIssue) { + Row { + Image( + painter = painterResource(R.drawable.bullet_point), + contentDescription = null, + modifier = Modifier.padding(6.dp), + ) + TextWithClickable( + mainTextRes = R.string.connection_assist_provider_stopped_description_turn_off_battery_saver2, + clickableTextRes = R.string.connection_assist_provider_stopped_description_turn_off_battery_saver_clickable, + onClick = { (requireActivity() as HomeActivity).openBatterySaverSettings() }, + style = style, + tag = BATTERY_SETTINGS_TAG, + ) + } + } else { + Row { + Image( + painter = painterResource(R.drawable.bullet_point), + contentDescription = null, + modifier = Modifier.padding(6.dp), + ) + Text( + text = stringResource(R.string.connection_assist_provider_stopped_description5), + style = style, + ) + } + } + Text( + text = stringResource(R.string.connection_assist_provider_stopped_description6, + stringResource(R.string.connection_assist_provider_stopped_description7) + ), + style = style, + ) + } + } + private fun setTitle(screen: ConnectAssistUiState) { binding.titleLargeTextView.visibility = if (screen.titleLargeTextViewVisible) View.VISIBLE else View.GONE binding.titleLargeTextView.text = getString(screen.titleLargeTextViewTextStringResource) binding.titleDescription.visibility = if (screen.titleDescriptionVisible) View.VISIBLE else View.GONE - if (screen.learnMoreStringResource != null && screen.internetErrorDescription != null) { + binding.daemonFailedDescription.visibility = View.GONE + if (screen == ConnectAssistUiState.ProviderStopped) { + binding.titleDescription.visibility = View.GONE + binding.daemonFailedDescription.setContent { + DaemonFailedScreen( + isDeviceInPowerSaveMode = requireContext().getSystemService<PowerManager>()?.isPowerSaveMode ?: false, + maybeConfigIssue = providerStoppedViewModel.maybeConfigIssue.collectAsState().value, + ) + } + binding.daemonFailedDescription.visibility = View.VISIBLE + } else if (screen.learnMoreStringResource != null && screen.internetErrorDescription != null) { val learnMore: String = "" // getString(screen.learnMoreStringResource) tor-browser#43198 uncomment and add back once we have the "Learn more" screens for relevant pages val internetErrorDescription: String = if (screen.internetErrorDescription1 == null) { @@ -434,6 +577,13 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler, SystemIn setOnClickListener { if (screen.torBootstrapButton1ShouldOpenSettings) { openTorConnectionSettings() + } else if (screen.torBootstrapButton1ShouldRestartTor) { + requireContext().components.core.geckoRuntime.torIntegrationController.restartProvider() + backgroundTintList = AppCompatResources.getColorStateList( + requireContext(), + R.color.disabled_connect_button_purple, + ) + text = getString(R.string.connection_assist_restarting_connection_button) } else { torConnectionAssistViewModel.handleConnect(screen) } @@ -561,8 +711,11 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler, SystemIn } override fun onBackPressed(): Boolean { - torConnectionAssistViewModel.handleBackButtonPressed(requireActivity() as HomeActivity) - return true + return torConnectionAssistViewModel.handleBackButtonPressed(requireActivity() as HomeActivity) + } + + companion object { + const val BATTERY_SETTINGS_TAG = "BATTERY_SETTINGS_TAG" } } ===================================== mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorConnectionAssistViewModel.kt ===================================== @@ -138,17 +138,18 @@ class TorConnectionAssistViewModel( torConnectStage.collect { Log.d(TAG, "torConnectStageName: ${it?.name}") when (it?.name) { - TorConnectStageName.Disabled -> shouldOpenHome.value = true // TODO use TorConnect.enabled instead to determine this - TorConnectStageName.Loading -> _torConnectScreen.value = ConnectAssistUiState.Loading - TorConnectStageName.Start -> _torConnectScreen.value = ConnectAssistUiState.Start - TorConnectStageName.Bootstrapping -> _torConnectScreen.value = handleBootstrapTrigger(it.bootstrapTrigger) - TorConnectStageName.Offline -> _torConnectScreen.value = ConnectAssistUiState.Offline - TorConnectStageName.ChooseRegion -> _torConnectScreen.value = ConnectAssistUiState.ChooseRegion - TorConnectStageName.RegionNotFound -> _torConnectScreen.value = ConnectAssistUiState.RegionNotFound - TorConnectStageName.ConfirmRegion -> _torConnectScreen.value = ConnectAssistUiState.ConfirmRegion - TorConnectStageName.FinalError -> _torConnectScreen.value = ConnectAssistUiState.FinalError - TorConnectStageName.Bootstrapped -> shouldOpenHome.value = true - null -> {} + TorConnectStageName.Disabled -> shouldOpenHome.value = true // TODO use TorConnect.enabled instead to determine this + TorConnectStageName.Loading -> _torConnectScreen.value = ConnectAssistUiState.Loading + TorConnectStageName.ProviderStopped -> _torConnectScreen.value = ConnectAssistUiState.ProviderStopped + TorConnectStageName.Start -> _torConnectScreen.value = ConnectAssistUiState.Start + TorConnectStageName.Bootstrapping -> _torConnectScreen.value = handleBootstrapTrigger(it.bootstrapTrigger) + TorConnectStageName.Offline -> _torConnectScreen.value = ConnectAssistUiState.Offline + TorConnectStageName.ChooseRegion -> _torConnectScreen.value = ConnectAssistUiState.ChooseRegion + TorConnectStageName.RegionNotFound -> _torConnectScreen.value = ConnectAssistUiState.RegionNotFound + TorConnectStageName.ConfirmRegion -> _torConnectScreen.value = ConnectAssistUiState.ConfirmRegion + TorConnectStageName.FinalError -> _torConnectScreen.value = ConnectAssistUiState.FinalError + TorConnectStageName.Bootstrapped -> shouldOpenHome.value = true + null -> {} } } } @@ -168,11 +169,12 @@ class TorConnectionAssistViewModel( } } - fun handleBackButtonPressed(homeActivity: HomeActivity) { - when (torConnectScreen.value) { - ConnectAssistUiState.Loading -> homeActivity.shutDown() - ConnectAssistUiState.Start -> homeActivity.shutDown() - else -> torAndroidIntegration.startAgain() + fun handleBackButtonPressed(homeActivity: HomeActivity): Boolean { + return when (torConnectScreen.value) { + ConnectAssistUiState.Loading -> homeActivity.shutDown() + ConnectAssistUiState.Start -> homeActivity.shutDown() + ConnectAssistUiState.ProviderStopped -> false + else -> torAndroidIntegration.startAgain().let { true } } } ===================================== mobile/android/fenix/app/src/main/res/drawable/bullet_point.xml ===================================== @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:autoMirrored="true" + android:height="6dp" + android:viewportHeight="6" + android:viewportWidth="6" + android:width="6dp"> + <path + android:fillColor="#FBFBFE" + android:pathData="M3,3m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0" /> +</vector> ===================================== mobile/android/fenix/app/src/main/res/drawable/loading_wheel.xml ===================================== @@ -0,0 +1,12 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:autoMirrored="true" + android:height="16dp" + android:viewportHeight="16" + android:viewportWidth="16" + android:width="16dp"> + <path + android:fillAlpha="0.4" + android:fillColor="#FBFBFE" + android:fillType="evenOdd" + android:pathData="M8,15C8,14.448 7.549,14.009 7.004,13.918C4.164,13.443 2,10.974 2,8C2,4.686 4.686,2 8,2C10.974,2 13.443,4.164 13.918,7.004C14.009,7.549 14.448,8 15,8C15.552,8 16.007,7.55 15.938,7.002C15.447,3.055 12.08,0 8,0C3.582,0 0,3.582 0,8C0,12.08 3.055,15.447 7.002,15.938C7.55,16.007 8,15.552 8,15Z" /> +</vector> ===================================== mobile/android/fenix/app/src/main/res/drawable/onion_broken.xml ===================================== @@ -0,0 +1,21 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="40dp" android:viewportHeight="40" android:viewportWidth="40" android:width="40dp"> + + <path android:fillColor="#FBFBFE" android:pathData="M33.259,33.258C29.865,36.651 25.178,38.75 20,38.75C9.645,38.75 1.25,30.355 1.25,20C1.25,14.823 3.348,10.135 6.741,6.742L8.813,8.814C5.95,11.677 4.18,15.632 4.18,20C4.18,28.737 11.263,35.82 20,35.82C24.369,35.82 28.324,34.049 31.187,31.186L33.259,33.258Z"/> + + <path android:fillColor="#FBFBFE" android:pathData="M35.284,30.863C37.466,27.798 38.75,24.049 38.75,20C38.75,9.645 30.355,1.25 20,1.25C15.951,1.25 12.202,2.533 9.137,4.716L11.243,6.822C13.751,5.153 16.762,4.18 20,4.18C28.737,4.18 35.82,11.263 35.82,20C35.82,23.238 34.847,26.249 33.178,28.757L35.284,30.863Z"/> + + <path android:fillColor="#FBFBFE" android:pathData="M31.226,26.805C32.431,24.82 33.125,22.491 33.125,20C33.125,12.751 27.249,6.875 20,6.875C17.509,6.875 15.18,7.569 13.195,8.774L15.347,10.926C16.742,10.209 18.324,9.805 20,9.805C25.631,9.805 30.195,14.369 30.195,20C30.195,21.676 29.791,23.258 29.074,24.653L31.226,26.805Z"/> + + <path android:fillColor="#FBFBFE" android:pathData="M29.281,29.281L27.209,27.209C25.364,29.054 22.815,30.195 20,30.195C14.369,30.195 9.805,25.631 9.805,20C9.805,17.185 10.946,14.636 12.79,12.791L10.719,10.72C8.344,13.095 6.875,16.376 6.875,20C6.875,27.249 12.751,33.125 20,33.125C23.625,33.125 26.906,31.656 29.281,29.281Z"/> + + <path android:fillColor="#FBFBFE" android:pathData="M14.696,14.697C13.339,16.054 12.5,17.929 12.5,20C12.5,24.142 15.858,27.5 20,27.5C22.071,27.5 23.946,26.66 25.303,25.303L23.232,23.232C22.405,24.059 21.262,24.57 20,24.57C17.476,24.57 15.43,22.524 15.43,20C15.43,18.738 15.941,17.596 16.768,16.769L14.696,14.697Z"/> + + <path android:fillColor="#FBFBFE" android:pathData="M24.568,20.147C24.569,20.098 24.57,20.049 24.57,20C24.57,17.476 22.524,15.43 20,15.43C19.951,15.43 19.902,15.431 19.853,15.432L17.388,12.967C18.202,12.665 19.081,12.5 20,12.5C24.142,12.5 27.5,15.858 27.5,20C27.5,20.919 27.335,21.799 27.033,22.612L24.568,20.147Z"/> + + <path android:fillColor="#FBFBFE" android:pathData="M20,38.75C9.645,38.75 1.25,30.355 1.25,20C1.25,14.823 3.348,10.135 6.741,6.742L20,20V38.75Z"/> + + <path android:fillColor="#FBFBFE" android:pathData="M20,15.579V1.25C15.951,1.25 12.202,2.533 9.137,4.716L20,15.579Z"/> + + <path android:fillColor="#FF9AA2" android:pathData="M35.291,39.061C35.085,39.062 34.882,39.021 34.692,38.943C34.503,38.864 34.33,38.749 34.186,38.604L2.31,6.731C2.037,6.434 1.889,6.043 1.898,5.64C1.906,5.236 2.07,4.851 2.355,4.566C2.64,4.28 3.025,4.116 3.428,4.107C3.832,4.099 4.223,4.246 4.52,4.519L36.396,36.394C36.688,36.688 36.851,37.085 36.851,37.5C36.851,37.915 36.688,38.312 36.396,38.606C36.25,38.751 36.078,38.866 35.888,38.944C35.699,39.022 35.496,39.062 35.291,39.061Z"/> + +</vector> ===================================== mobile/android/fenix/app/src/main/res/layout/fragment_tor_connection_assist.xml ===================================== @@ -99,6 +99,19 @@ android:textSize="14sp" app:layout_constraintTop_toBottomOf="@id/title_large_text_view" /> + <androidx.compose.ui.platform.ComposeView + android:id="@+id/daemon_failed_description" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:visibility="gone" + android:paddingHorizontal="24dp" + android:paddingVertical="16dp" + app:layout_constraintBottom_toTopOf="@id/tor_bootstrap_button_1" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/title_large_text_view" + app:layout_constraintVertical_bias="0" /> + <androidx.appcompat.widget.SwitchCompat android:id="@+id/quickstart_switch" android:layout_width="match_parent" ===================================== mobile/android/fenix/app/src/main/res/navigation/nav_graph.xml ===================================== @@ -27,6 +27,10 @@ app:popUpTo="@id/torConnectionAssistFragment" app:popUpToInclusive="true"/> + <action + android:id="@+id/action_navigate_to_connection_assist_from_anywhere" + app:destination="@id/torConnectionAssistFragment" /> + <action android:id="@+id/action_global_home" app:destination="@id/homeFragment" ===================================== mobile/android/fenix/app/src/main/res/values/preference_keys.xml ===================================== @@ -652,6 +652,7 @@ <string name="pref_key_tor_network_settings_bridge_config" translatable="false">pref_key_tor_network_settings_bridge_config</string> <string name="pref_key_tor_logs" translatable="false">pref_key_tor_logs</string> <string name="pref_key_about_config_shortcut" translatable="false">pref_key_about_config_shortcut</string> + <string name="pref_key_test_kill_tor" translatable="false">pref_key_test_kill_tor</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> ===================================== mobile/android/fenix/app/src/main/res/values/torbrowser_strings.xml ===================================== @@ -135,6 +135,32 @@ <string name="connection_assist_back_button_content_description_start_again">Start again</string> <!-- Connection assist. This message is shown briefly after the connection to the Tor network completes. --> <string name="connection_assist_bootstrap_succeeded_toast_message">Connected to Tor</string> + <!-- Connection assist. Here "process" is a noun, referring to the execution of a computer program. "Tor process" specifically refers to the execution of the tor program, which is the program that handles the connection to the "Tor network". --> + <string name="connection_assist_provider_stopped_title">The Tor process has stopped working</string> + <!-- Connection assist. Here "process" is a noun, referring to the execution of a computer program. --> + <string name="connection_assist_provider_stopped_description1">The underlying process that controls your connection to the Tor network has stopped working.</string> + <!-- Connection assist. --> + <string name="connection_assist_provider_stopped_description2">What could be causing this?</string> + <!-- Connection assist. Here "process" is a noun, referring to the execution of a computer program. --> + <string name="connection_assist_provider_stopped_description_battery1">Your device has quit the process to save battery.</string> + <!-- Connection assist. Here "process" is a noun, referring to the execution of a computer program. "Tor process" specifically refers to the execution of the tor program, which is the program that handles the connection to the "Tor network". --> + <string name="connection_assist_provider_stopped_description3">The Tor process has stopped working due to a technical issue.</string> + <!-- Connection assist. --> + <string name="connection_assist_provider_stopped_description4">What can you do about it?</string> + <!-- Connection assist. Here "process" is a noun, referring to the execution of a computer program. "Tor process" specifically refers to the execution of the tor program, which is the program that handles the connection to the "Tor network". --> + <string name="connection_assist_provider_stopped_description5">Try restarting the Tor process (this won’t close your browser tabs).</string> + <!-- Connection assist.--> + <string name="connection_assist_provider_stopped_description_turn_off_battery_saver_clickable">Turn off Battery Saver on your device</string> + <!-- Connection assist. "%1$s" will be replaced with connection_assist_provider_stopped_description_turn_off_battery_saver_clickable. Here "process" is a noun, referring to the execution of a computer program. "Tor process" specifically refers to the execution of the tor program, which is the program that handles the connection to the "Tor network". --> + <string name="connection_assist_provider_stopped_description_turn_off_battery_saver2">%1$s and try restarting the Tor process (this won’t close your browser tabs).</string> + <!-- Connection assist. "%1$s" will be replaced with connection_assist_provider_stopped_description7. --> + <string name="connection_assist_provider_stopped_description6">If the problem remains, get in contact with us through one of our %1$s.</string> + <!-- Connection assist. --> + <string name="connection_assist_provider_stopped_description7">support channels</string> + <!-- Connection assist. Here "process" is a noun, referring to the execution of a computer program. "Tor process" specifically refers to the execution of the tor program, which is the program that handles the connection to the "Tor network". --> + <string name="connection_assist_restart_connection_button">Restart Tor process</string> + <!-- Connection assist. Here "process" is a noun, referring to the execution of a computer program. "Tor process" specifically refers to the execution of the tor program, which is the program that handles the connection to the "Tor network". --> + <string name="connection_assist_restarting_connection_button">Restarting Tor process…</string> <!-- Notification title for closing browser tabs. "%s" will be replaced with the localised application name, such as "Tor Browser". --> <string name="notification_close_tor_browser_tabs">Close %s’s tabs?</string> ===================================== mobile/android/fenix/app/src/main/res/xml/secret_settings_preferences.xml ===================================== @@ -135,6 +135,10 @@ android:key="@string/pref_key_enable_lna_tracker_blocking_enabled" android:title="@string/preferences_debug_settings_enable_lna_tracker_blocking" app:iconSpaceReserved="false" /> + <Preference + android:key="@string/pref_key_test_kill_tor" + android:title="TEST kill tor daemon" + app:iconSpaceReserved="false" /> </PreferenceCategory> <PreferenceCategory ===================================== mobile/android/geckoview/src/main/java/org/mozilla/geckoview/ProviderStatus.java ===================================== @@ -0,0 +1,11 @@ +package org.mozilla.geckoview; + +import org.mozilla.gecko.util.GeckoBundle; + +public class ProviderStatus { + public Boolean maybeConfigIssue; + + public ProviderStatus(GeckoBundle bundle) { + maybeConfigIssue = bundle.getBoolean("maybeConfigIssue"); + } +} ===================================== mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorAndroidIntegration.java ===================================== @@ -57,6 +57,7 @@ public class TorAndroidIntegration 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_START_AGAIN = "GeckoView:Tor:StartAgain"; + private static final String EVENT_RESTART_PROVIDER = "GeckoView:Tor:RestartProvider"; private static final String EVENT_QUICKSTART_GET = "GeckoView:Tor:QuickstartGet"; private static final String EVENT_QUICKSTART_SET = "GeckoView:Tor:QuickstartSet"; private static final String EVENT_REGION_NAMES_GET = "GeckoView:Tor:RegionNamesGet"; @@ -706,6 +707,10 @@ public class TorAndroidIntegration implements BundleEventListener { return EventDispatcher.getInstance().queryVoid(EVENT_START_AGAIN); } + public @NonNull GeckoResult<Void> restartProvider() { + return EventDispatcher.getInstance().queryVoid(EVENT_RESTART_PROVIDER); + } + public interface QuickstartGetter { void onValue(boolean enabled); } ===================================== mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorConnectStage.java ===================================== @@ -13,6 +13,7 @@ public class TorConnectStage { public Boolean potentiallyBlocked; public Boolean tryAgain; public TorBootstrappingStatus bootstrappingStatus; + public ProviderStatus providerStatus; public TorConnectStage(GeckoBundle bundle) { name = TorConnectStageName.fromString(bundle.getString("name")); @@ -26,6 +27,7 @@ public class TorConnectStage { error = new TorConnectError(bundle.getBundle("error")); } bootstrappingStatus = new TorBootstrappingStatus(bundle.getBundle("bootstrappingStatus")); + providerStatus = new ProviderStatus(bundle.getBundle("providerStatus")); } public Boolean isBootstrapped() { ===================================== mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorConnectStageName.java ===================================== @@ -3,9 +3,10 @@ package org.mozilla.geckoview; import java.security.InvalidParameterException; public enum TorConnectStageName { - // These names should match entries from TorConnectStage in TorConnect.sys.mjs at ~ln163. + // These names should match entries from TorConnectStage in TorConnect.sys.mjs at ~ln674. Disabled("Disabled"), Loading("Loading"), + ProviderStopped("ProviderStopped"), Start("Start"), Bootstrapping("Bootstrapping"), Offline("Offline"), ===================================== toolkit/components/tor-launcher/TorProcessAndroid.sys.mjs ===================================== @@ -51,6 +51,11 @@ export class TorProcessAndroid { * it failed to start tor. */ #startReject = null; + /** + * Tells whether we ever registered ourself as the listener to the various + * process events. + */ + #registeredListeners = false; onExit = () => {}; @@ -74,6 +79,7 @@ export class TorProcessAndroid { this, Object.values(TorIncomingEvents) ); + this.#registeredListeners = true; let config; try { config = await lazy.EventDispatcher.instance.sendRequestForResult( @@ -103,10 +109,13 @@ export class TorProcessAndroid { }); logger.debug("Sent the stop event."); this.#processHandle = null; - lazy.EventDispatcher.instance.unregisterListener( - this, - Object.values(TorIncomingEvents) - ); + if (this.#registeredListeners) { + lazy.EventDispatcher.instance.unregisterListener( + this, + Object.values(TorIncomingEvents) + ); + this.#registeredListeners = false; + } } onEvent(event, data, _callback) { ===================================== toolkit/modules/TorAndroidIntegration.sys.mjs ===================================== @@ -48,6 +48,7 @@ const ListenedEvents = Object.freeze({ bootstrapBeginAuto: "GeckoView:Tor:BootstrapBeginAuto", bootstrapCancel: "GeckoView:Tor:BootstrapCancel", startAgain: "GeckoView:Tor:StartAgain", + restartProvider: "GeckoView:Tor:RestartProvider", quickstartGet: "GeckoView:Tor:QuickstartGet", quickstartSet: "GeckoView:Tor:QuickstartSet", regionNamesGet: "GeckoView:Tor:RegionNamesGet", @@ -98,11 +99,6 @@ class TorAndroidIntegrationImpl { lazy.TorConnect.init(); lazy.TorDomainIsolator.init(); - - // On Android immediately call firstWindowLoaded. This should be safe to - // call since it will await the initialisation of the TorProvider set up - // by TorProviderBuilder.init. - lazy.TorProviderBuilder.firstWindowLoaded(); } observe(subj, topic) { @@ -203,6 +199,9 @@ class TorAndroidIntegrationImpl { case ListenedEvents.startAgain: lazy.TorConnect.startAgain(); break; + case ListenedEvents.restartProvider: + lazy.TorConnect.restartProvider(); + break; case ListenedEvents.quickstartGet: callback?.onSuccess(lazy.TorConnect.quickstart); return; ===================================== toolkit/modules/TorConnect.sys.mjs ===================================== @@ -1109,12 +1109,9 @@ export const TorConnect = { throw new Error(`Trying to set the stage to ${name} during a bootstrap`); } if (!this._providerRunning && name !== TorConnectStage.ProviderStopped) { - if (!lazy.TorLauncherUtil.isAndroid) { - // TODO: Remove Android exception. - throw new Error( - `Trying to set the stage to ${name} when provider is not running` - ); - } + throw new Error( + `Trying to set the stage to ${name} when provider is not running` + ); } lazy.logger.info(`Entering stage ${name}`); @@ -1596,13 +1593,8 @@ export const TorConnect = { return; } if (!this._providerRunning && stage !== TorConnectStage.ProviderStopped) { - if (!lazy.TorLauncherUtil.isAndroid) { - // TODO: Remove Android exception. - lazy.logger.warn( - `Cannot move to ${stage} when provider is not running` - ); - return; - } + lazy.logger.warn(`Cannot move to ${stage} when provider is not running`); + return; } if (this._stageName === TorConnectStage.Loading) { if (stage === TorConnectStage.ProviderStopped) { @@ -1709,13 +1701,7 @@ export const TorConnect = { // But other methods should take into account that _providerRunning is now // `false` to early return and guarantee that we enter this // ProviderStopped stage. - if (lazy.TorLauncherUtil.isAndroid) { - // TODO: Remove this Android path when android supports the - // `ProviderStopped` stage. - this._makeStageRequest(TorConnectStage.Start, true); - } else { - this._makeStageRequest(TorConnectStage.ProviderStopped, true); - } + this._makeStageRequest(TorConnectStage.ProviderStopped, true); } } View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/14f24ee... -- View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/14f24ee... You're receiving this email because of your account on gitlab.torproject.org. Manage all notifications: https://gitlab.torproject.org/-/profile/notifications | Help: https://gitlab.torproject.org/help
participants (1)
-
Dan Ballard (@dan)