tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 522d51622f0924e75b17dfdc82380d842dcf72ed
parent b6bd3b692663cf067f0c8f40b813036c374e3452
Author: Segun Famisa <sfamisa@mozilla.com>
Date:   Fri, 21 Nov 2025 14:22:32 +0000

Bug 1989004 - Use the correct granular prefs & API for LNA blocking in Fenix r=android-reviewers,boek

This change separates the Local Network Access (LNA) blocking feature into three distinct settings for more granular control:
- `lnaFeatureEnabled`: An overall toggle for the entire LNA blocking feature.
- `lnaBlockingEnabled`: Controls the request blocking aspect.
- `lnaTrackerBlockingEnabled`: Controls the tracker blocking aspect.

The implementation updates Nimbus, secret settings, and various site permission screens to use `lnaFeatureEnabled` as the primary check for UI visibility, while wiring up all three new settings from `android-components` through to the Gecko engine. This provides finer control for experimentation and development of the LNA feature.

Differential Revision: https://phabricator.services.mozilla.com/D272455

Diffstat:
Mmobile/android/android-components/components/browser/engine-gecko/src/main/java/mozilla/components/browser/engine/gecko/GeckoEngine.kt | 14++++++++++++--
Mmobile/android/android-components/components/concept/engine/src/main/java/mozilla/components/concept/engine/Settings.kt | 15++++++++++++++-
Mmobile/android/fenix/app/nimbus.fml.yaml | 10++++++++++
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/Core.kt | 2++
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SecretSettingsFragment.kt | 24++++++++++++++++++++++++
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentStore.kt | 2+-
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsDetailsExceptionsFragment.kt | 4++--
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SiteSettingsFragment.kt | 2+-
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/trustpanel/store/TrustPanelStore.kt | 2+-
Mmobile/android/fenix/app/src/main/java/org/mozilla/fenix/utils/Settings.kt | 27+++++++++++++++++++++++----
Mmobile/android/fenix/app/src/main/res/values/preference_keys.xml | 2++
Mmobile/android/fenix/app/src/main/res/values/static_strings.xml | 4+++-
Mmobile/android/fenix/app/src/main/res/xml/secret_settings_preferences.xml | 8++++++++
Mmobile/android/fenix/app/src/test/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsDetailsExceptionsFragmentTest.kt | 7+++----
Mmobile/android/fenix/app/src/test/java/org/mozilla/fenix/settings/trustpanel/TrustPanelStoreTest.kt | 4++--
15 files changed, 108 insertions(+), 19 deletions(-)

diff --git a/mobile/android/android-components/components/browser/engine-gecko/src/main/java/mozilla/components/browser/engine/gecko/GeckoEngine.kt b/mobile/android/android-components/components/browser/engine-gecko/src/main/java/mozilla/components/browser/engine/gecko/GeckoEngine.kt @@ -1665,8 +1665,16 @@ class GeckoEngine( set(value) { runtime.settings.setBannedPorts(value) } override var lnaBlockingEnabled: Boolean - get() = runtime.settings.lnaBlockingEnabled - set(value) { runtime.settings.setLnaBlockingEnabled(value) } + get() = runtime.settings.lnaBlocking ?: false + set(value) { runtime.settings.setLnaBlocking(value) } + + override var lnaTrackerBlockingEnabled: Boolean + get() = runtime.settings.lnaBlockTrackers ?: false + set(value) { runtime.settings.setLnaBlockTrackers(value) } + + override var lnaFeatureEnabled: Boolean + get() = runtime.settings.lnaEnabled ?: false + set(value) { runtime.settings.setLnaEnabled(value) } override var crliteChannel: String? get() = runtime.settings.crliteChannel @@ -1722,6 +1730,8 @@ class GeckoEngine( this.dohAutoselectEnabled = it.dohAutoselectEnabled this.bannedPorts = it.bannedPorts this.lnaBlockingEnabled = it.lnaBlockingEnabled + this.lnaFeatureEnabled = it.lnaFeatureEnabled + this.lnaTrackerBlockingEnabled = it.lnaTrackerBlockingEnabled this.crliteChannel = it.crliteChannel this.safeBrowsingV5Enabled = it.safeBrowsingV5Enabled } diff --git a/mobile/android/android-components/components/concept/engine/src/main/java/mozilla/components/concept/engine/Settings.kt b/mobile/android/android-components/components/concept/engine/src/main/java/mozilla/components/concept/engine/Settings.kt @@ -364,11 +364,22 @@ abstract class Settings { open var bannedPorts: String by UnsupportedSetting() /** - * Setting to control blocking of local network & local device (localhost) access + * Setting to control the request blocking feature of Local Network / Device Access blocking */ open var lnaBlockingEnabled: Boolean by UnsupportedSetting() /** + * Setting to control the tracker blocking feature of Local Network / Device Access blocking + */ + open var lnaTrackerBlockingEnabled: Boolean by UnsupportedSetting() + + /** + * Setting to control the overall Local Network / Device Access blocking feature. This is a + * superset of [lnaBlockingEnabled] & [lnaTrackerBlockingEnabled] + */ + open var lnaFeatureEnabled: Boolean by UnsupportedSetting() + + /** * Setting to control the CRLite certificate blocklist channel */ open var crliteChannel: String? by UnsupportedSetting() @@ -450,6 +461,8 @@ data class DefaultSettings( override var dohAutoselectEnabled: Boolean = false, override var bannedPorts: String = "", override var lnaBlockingEnabled: Boolean = false, + override var lnaTrackerBlockingEnabled: Boolean = false, + override var lnaFeatureEnabled: Boolean = false, override var crliteChannel: String? = null, override var safeBrowsingV5Enabled: Boolean? = null, ) : Settings() { diff --git a/mobile/android/fenix/app/nimbus.fml.yaml b/mobile/android/fenix/app/nimbus.fml.yaml @@ -961,6 +961,16 @@ features: lna-blocking: description: Feature that allows blocking local network or device access. variables: + blocking: + description: > + Whether or not to block LNA requests + type: Boolean + default: false + block-trackers: + description: > + Whether or not to block trackers + type: Boolean + default: false enabled: description: > Whether or not to turn on local network / device access blocking diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/Core.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/Core.kt @@ -194,6 +194,8 @@ class Core( dohAutoselectEnabled = FxNimbus.features.doh.value().autoselectEnabled, bannedPorts = FxNimbus.features.networkingBannedPorts.value().bannedPortList, lnaBlockingEnabled = context.settings().isLnaBlockingEnabled, + lnaFeatureEnabled = context.settings().isLnaFeatureEnabled, + lnaTrackerBlockingEnabled = context.settings().isLnaTrackerBlockingEnabled, crliteChannel = FxNimbus.features.pki.value().crliteChannel, ) diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SecretSettingsFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/SecretSettingsFragment.kt @@ -197,6 +197,18 @@ class SecretSettingsFragment : PreferenceFragmentCompat() { onPreferenceChangeListener = SharedPreferenceUpdater() } + requirePreference<SwitchPreference>(R.string.pref_key_enable_lna_feature_enabled).apply { + isVisible = Config.channel.isNightlyOrDebug + isChecked = context.settings().isLnaFeatureEnabled + onPreferenceChangeListener = object : SharedPreferenceUpdater() { + override fun onPreferenceChange(preference: Preference, newValue: Any?): Boolean { + context.components.core.engine.settings.lnaFeatureEnabled = + newValue as Boolean + return super.onPreferenceChange(preference, newValue) + } + } + } + requirePreference<SwitchPreference>(R.string.pref_key_enable_lna_blocking_enabled).apply { isVisible = Config.channel.isNightlyOrDebug isChecked = context.settings().isLnaBlockingEnabled @@ -209,6 +221,18 @@ class SecretSettingsFragment : PreferenceFragmentCompat() { } } + requirePreference<SwitchPreference>(R.string.pref_key_enable_lna_tracker_blocking_enabled).apply { + isVisible = Config.channel.isNightlyOrDebug + isChecked = context.settings().isLnaTrackerBlockingEnabled + onPreferenceChangeListener = object : SharedPreferenceUpdater() { + override fun onPreferenceChange(preference: Preference, newValue: Any?): Boolean { + context.components.core.engine.settings.lnaTrackerBlockingEnabled = + newValue as Boolean + return super.onPreferenceChange(preference, newValue) + } + } + } + requirePreference<SwitchPreference>(R.string.pref_key_allow_settings_search).apply { isVisible = Config.channel.isNightlyOrDebug isChecked = context.settings().isSettingsSearchEnabled diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentStore.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/quicksettings/QuickSettingsFragmentStore.kt @@ -192,7 +192,7 @@ class QuickSettingsFragmentStore( WebsitePermission.Toggleable( phoneFeature = this, status = getActionLabel(context, permissions, settings), - isVisible = settings.isLnaBlockingEnabled && shouldBeVisible(permissions, settings), + isVisible = settings.isLnaFeatureEnabled && shouldBeVisible(permissions, settings), isEnabled = shouldBeEnabled(context, permissions, settings), isBlockedByAndroid = !isAndroidPermissionGranted(context), ) diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsDetailsExceptionsFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsDetailsExceptionsFragment.kt @@ -86,8 +86,8 @@ class SitePermissionsDetailsExceptionsFragment : PreferenceFragmentCompat() { initPhoneFeature(CROSS_ORIGIN_STORAGE_ACCESS) initPhoneFeature(MEDIA_KEY_SYSTEM_ACCESS) initAutoplayFeature() - initPhoneFeature(LOCAL_DEVICE_ACCESS, visible = settings.isLnaBlockingEnabled) - initPhoneFeature(LOCAL_NETWORK_ACCESS, visible = settings.isLnaBlockingEnabled) + initPhoneFeature(LOCAL_DEVICE_ACCESS, visible = settings.isLnaFeatureEnabled) + initPhoneFeature(LOCAL_NETWORK_ACCESS, visible = settings.isLnaFeatureEnabled) bindClearPermissionsButton() } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SiteSettingsFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SiteSettingsFragment.kt @@ -91,7 +91,7 @@ class SiteSettingsFragment : PreferenceFragmentCompat() { // not need to be bound .filter { it != PhoneFeature.AUTOPLAY_INAUDIBLE } .excludeFeatures( - condition = { !requireContext().settings().isLnaBlockingEnabled }, + condition = { !requireContext().settings().isLnaFeatureEnabled }, features = setOf( PhoneFeature.LOCAL_DEVICE_ACCESS, PhoneFeature.LOCAL_NETWORK_ACCESS, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/trustpanel/store/TrustPanelStore.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/trustpanel/store/TrustPanelStore.kt @@ -89,7 +89,7 @@ class TrustPanelStore( WebsitePermission.Toggleable( isEnabled = status.isAllowed(), isBlockedByAndroid = false, - isVisible = settings.isLnaBlockingEnabled && sitePermissions != null && + isVisible = settings.isLnaFeatureEnabled && sitePermissions != null && status.doNotAskAgain(), deviceFeature = phoneFeature, ) diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/utils/Settings.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/utils/Settings.kt @@ -849,14 +849,33 @@ class Settings( ) /** - * Indicates if the LNA (Local Network Access / Local Device Access) blocking is enabled - * - * This refers to whether or not we are blocking or allowing requests that originate from - * remote origins targeting either localhost addresses or local network addresses. + * Indicates if the request blocking feature for Local Network / Local Device Access blocking is enabled. */ var isLnaBlockingEnabled by lazyFeatureFlagPreference( key = appContext.getPreferenceKey(R.string.pref_key_enable_lna_blocking_enabled), featureFlag = true, + default = { FxNimbus.features.lnaBlocking.value().blocking }, + ) + + /** + * Indicates if the Local Network / Local Device Access tracker blocking feature is enabled. + */ + var isLnaTrackerBlockingEnabled by lazyFeatureFlagPreference( + key = appContext.getPreferenceKey(R.string.pref_key_enable_lna_tracker_blocking_enabled), + featureFlag = true, + default = { FxNimbus.features.lnaBlocking.value().blockTrackers }, + ) + + /** + * Indicates if the overall Local Network / Local Device Access feature is enabled. + * + * Local Network / Local Device Access blocking refers to whether or not we are blocking or + * allowing requests that originate from remote origins targeting either localhost addresses or + * local network addresses. + */ + var isLnaFeatureEnabled by lazyFeatureFlagPreference( + key = appContext.getPreferenceKey(R.string.pref_key_enable_lna_feature_enabled), + featureFlag = true, default = { FxNimbus.features.lnaBlocking.value().enabled }, ) diff --git a/mobile/android/fenix/app/src/main/res/values/preference_keys.xml b/mobile/android/fenix/app/src/main/res/values/preference_keys.xml @@ -445,6 +445,8 @@ <string name="pref_key_enable_address_sync" translatable="false">pref_key_enable_address_sync</string>" <string name="pref_key_enable_toolbar_customization">pref_key_enable_toolbar_customization</string> <string name="pref_key_enable_lna_blocking_enabled" translatable="false">pref_key_enable_lna_blocking_enabled</string> + <string name="pref_key_enable_lna_tracker_blocking_enabled" translatable="false">pref_key_enable_lna_tracker_blocking_enabled</string> + <string name="pref_key_enable_lna_feature_enabled" translatable="false">pref_key_enable_lna_feature_enabled</string> <string name="pref_key_enable_isolated_process" translatable="false">pref_key_enable_isolated_process</string> <string name="pref_key_enable_app_zygote_process" translatable="false">pref_key_enable_app_zygote_process</string> diff --git a/mobile/android/fenix/app/src/main/res/values/static_strings.xml b/mobile/android/fenix/app/src/main/res/values/static_strings.xml @@ -127,7 +127,9 @@ <string name="preferences_terms_of_use_debug_timer" translatable="false">30s ToU timer enabled</string> <!-- Label for enabling LNA blocking --> - <string name="preferences_debug_settings_enable_lna_blocking" translatable="false">Enable LNA blocking</string> + <string name="preferences_debug_settings_enable_lna_blocking" translatable="false">Enable LNA request blocking</string> + <string name="preferences_debug_settings_enable_lna_tracker_blocking" translatable="false">Enable LNA tracker blocking</string> + <string name="preferences_debug_settings_enable_lna_feature" translatable="false">Enable LNA blocking feature overall (required for anything LNA)</string> <!-- Label for enabling isolated content processes in secret settings. --> <string name="preferences_debug_settings_isolated_process" translatable="false">Enable Isolated Content Process (requires restart)</string> diff --git a/mobile/android/fenix/app/src/main/res/xml/secret_settings_preferences.xml b/mobile/android/fenix/app/src/main/res/xml/secret_settings_preferences.xml @@ -111,10 +111,18 @@ android:title="@string/preferences_debug_settings_enable_doh_settings" app:iconSpaceReserved="false" /> <SwitchPreference + android:key="@string/pref_key_enable_lna_feature_enabled" + android:title="@string/preferences_debug_settings_enable_lna_feature" + app:iconSpaceReserved="false" /> + <SwitchPreference android:key="@string/pref_key_enable_lna_blocking_enabled" android:title="@string/preferences_debug_settings_enable_lna_blocking" app:iconSpaceReserved="false" /> <SwitchPreference + android:key="@string/pref_key_enable_lna_tracker_blocking_enabled" + android:title="@string/preferences_debug_settings_enable_lna_tracker_blocking" + app:iconSpaceReserved="false" /> + <SwitchPreference android:key="@string/pref_key_enable_isolated_process" android:title="@string/preferences_debug_settings_isolated_process" app:iconSpaceReserved="false" /> diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsDetailsExceptionsFragmentTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsDetailsExceptionsFragmentTest.kt @@ -22,7 +22,6 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mozilla.fenix.R -import org.mozilla.fenix.ext.settings import org.mozilla.fenix.settings.PhoneFeature import org.mozilla.fenix.settings.requirePreference import org.mozilla.fenix.utils.Settings @@ -58,7 +57,7 @@ class SitePermissionsDetailsExceptionsFragmentTest { fragment.sitePermissions = permissions every { permissions.origin } returns "mozilla.org" - every { settings.isLnaBlockingEnabled } returns false + every { settings.isLnaFeatureEnabled } returns false every { fragment.provideContext() } returns context every { fragment.provideSettings() } returns settings } @@ -86,7 +85,7 @@ class SitePermissionsDetailsExceptionsFragmentTest { @Test fun `WHEN bindCategoryPhoneFeatures is called AND LNA is not enabled THEN LNA preference is hidden`() { - every { settings.isLnaBlockingEnabled } returns false + every { settings.isLnaFeatureEnabled } returns false fragment.bindCategoryPhoneFeatures() @@ -106,7 +105,7 @@ class SitePermissionsDetailsExceptionsFragmentTest { @Test fun `WHEN bindCategoryPhoneFeatures is called AND LNA is enabled THEN LNA preference is shown`() { - every { settings.isLnaBlockingEnabled } returns true + every { settings.isLnaFeatureEnabled } returns true fragment.bindCategoryPhoneFeatures() diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/settings/trustpanel/TrustPanelStoreTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/settings/trustpanel/TrustPanelStoreTest.kt @@ -122,7 +122,7 @@ class TrustPanelStoreTest { whenever(sitePermissions.localNetworkAccess).thenReturn(ALLOWED) whenever(permissionHighlights.isAutoPlayBlocking).thenReturn(true) - whenever(settings.isLnaBlockingEnabled).thenReturn(false) + whenever(settings.isLnaFeatureEnabled).thenReturn(false) val state = TrustPanelStore.createWebsitePermissionState( settings = settings, @@ -156,7 +156,7 @@ class TrustPanelStoreTest { whenever(sitePermissions.localDeviceAccess).thenReturn(ALLOWED) whenever(sitePermissions.localNetworkAccess).thenReturn(ALLOWED) whenever(permissionHighlights.isAutoPlayBlocking).thenReturn(true) - whenever(settings.isLnaBlockingEnabled).thenReturn(true) + whenever(settings.isLnaFeatureEnabled).thenReturn(true) val state = TrustPanelStore.createWebsitePermissionState( settings = settings,