diff --git a/app/src/main/java/foundation/e/advancedprivacy/common/extensions/NavControllerExtensions.kt b/app/src/main/java/foundation/e/advancedprivacy/common/extensions/NavControllerExtensions.kt
new file mode 100644
index 0000000000000000000000000000000000000000..4af519db1898aa1f57da7d7a9f7ba00a79d5230d
--- /dev/null
+++ b/app/src/main/java/foundation/e/advancedprivacy/common/extensions/NavControllerExtensions.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2025 MURENA SAS
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package foundation.e.advancedprivacy.common.extensions
+
+import android.os.Bundle
+import androidx.navigation.NavController
+import androidx.navigation.NavDirections
+
+fun NavController.safeNavigate(direction: NavDirections) {
+ currentDestination?.getAction(direction.actionId)?.run {
+ navigate(direction.actionId)
+ }
+}
+
+fun NavController.safeNavigateWithArgs(direction: NavDirections, bundle: Bundle?) {
+ currentDestination?.getAction(direction.actionId)?.run {
+ navigate(direction.actionId, bundle)
+ }
+}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardFragment.kt b/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardFragment.kt
index 099d8fbbac3d504cfc0e53504b557ec3500ffc61..dec1510452e4544c9dff04a2c3ad84cc99174677 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardFragment.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardFragment.kt
@@ -32,6 +32,7 @@ import com.google.android.material.tabs.TabLayoutMediator
import foundation.e.advancedprivacy.R
import foundation.e.advancedprivacy.common.BigNumberFormatter
import foundation.e.advancedprivacy.common.NavToolbarFragment
+import foundation.e.advancedprivacy.common.extensions.safeNavigate
import foundation.e.advancedprivacy.databinding.FragmentDashboardBinding
import foundation.e.advancedprivacy.domain.entities.FeatureMode
import foundation.e.advancedprivacy.domain.entities.FeatureState
@@ -171,7 +172,7 @@ class DashboardFragment : NavToolbarFragment(R.layout.fragment_dashboard) {
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
- viewModel.navigate.collect(findNavController()::navigate)
+ viewModel.navigate.collect(findNavController()::safeNavigate)
}
}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersFragment.kt b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersFragment.kt
index 5f55b1a83c60d1df99d1c1442df0ea7944be81df..ba65d8180a3312d1d62b00e1a3b7ff0b7810ed78 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersFragment.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersFragment.kt
@@ -39,6 +39,7 @@ import com.google.android.material.tabs.TabLayoutMediator
import foundation.e.advancedprivacy.R
import foundation.e.advancedprivacy.common.NavToolbarFragment
import foundation.e.advancedprivacy.common.extensions.findViewHolderForAdapterPosition
+import foundation.e.advancedprivacy.common.extensions.safeNavigate
import foundation.e.advancedprivacy.common.extensions.updatePagerHeightForChild
import foundation.e.advancedprivacy.databinding.FragmentTrackersBinding
import kotlinx.coroutines.flow.SharedFlow
@@ -100,7 +101,7 @@ class TrackersFragment : NavToolbarFragment(R.layout.fragment_trackers) {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
- viewModel.navigate.collect(findNavController()::navigate)
+ viewModel.navigate.collect(findNavController()::safeNavigate)
}
}
}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersPeriodFragment.kt b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersPeriodFragment.kt
index 91f533944304528e40e6833584c4aa7e70084f0c..771c31d6485ed5e5ef8cf8480870f05c378c8fe4 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersPeriodFragment.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersPeriodFragment.kt
@@ -33,6 +33,7 @@ import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.tabs.TabLayoutMediator
import foundation.e.advancedprivacy.R
import foundation.e.advancedprivacy.common.extensions.findViewHolderForAdapterPosition
+import foundation.e.advancedprivacy.common.extensions.safeNavigate
import foundation.e.advancedprivacy.common.extensions.updatePagerHeightForChild
import foundation.e.advancedprivacy.databinding.TrackersPeriodFragmentBinding
import foundation.e.advancedprivacy.features.trackers.TrackersPeriodViewModel.SingleEvent
@@ -147,7 +148,7 @@ class TrackersPeriodFragment : Fragment(R.layout.trackers_period_fragment) {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
- viewModel.navigate.collect(findNavController()::navigate)
+ viewModel.navigate.collect(findNavController()::safeNavigate)
}
}
diff --git a/core/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/IPermissionsPrivacyModule.kt b/core/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/IPermissionsPrivacyModule.kt
index 599cb376ab6746ca0dbfce9bec0f20e86e278262..731d3cc4b4728a103c0aff26913c5d14b43c5b94 100644
--- a/core/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/IPermissionsPrivacyModule.kt
+++ b/core/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/IPermissionsPrivacyModule.kt
@@ -122,6 +122,9 @@ interface IPermissionsPrivacyModule {
*/
fun getAlwaysOnVpnPackage(): String?
+ /** Returns the dummy package name used by Android to highlight legacy VPN */
+ fun getLegacyVpnDummyPackage(): String
+
/**
* Allows users to block notifications sent through this channel, if this channel belongs to
* a package that is signed with the system signature.
diff --git a/permissionseos/libs/hidden-apis-stub/src/main/java/android/internal/net/VpnConfig.java b/permissionseos/libs/hidden-apis-stub/src/main/java/android/internal/net/VpnConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..134b1e9090e3a72b16499629bf3313476da4c4df
--- /dev/null
+++ b/permissionseos/libs/hidden-apis-stub/src/main/java/android/internal/net/VpnConfig.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2025 MURENA SAS
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package android.internal.net;
+
+// Stub based on:
+// https://gitlab.e.foundation/e/os/android_frameworks_base/-/blob/[VERSION]/core/java/com/android/internal/net/VpnConfig.java
+public class VpnConfig {
+
+ // Availability checked from SDK29 to SDK 35
+ public static final String LEGACY_VPN = "[Legacy VPN]";
+}
diff --git a/permissionseos/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModuleImpl.kt b/permissionseos/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModuleImpl.kt
index 4c96a02a71713d8965c4bc0138939cb5998d2761..06ea40b318bdd9809c8c46a2909ad9d7d5dc5268 100644
--- a/permissionseos/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModuleImpl.kt
+++ b/permissionseos/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModuleImpl.kt
@@ -28,6 +28,7 @@ import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.UserInfo
import android.graphics.drawable.Drawable
+import android.internal.net.VpnConfig
import android.net.IConnectivityManager
import android.net.VpnManager
import android.net.VpnManager.TYPE_VPN_SERVICE
@@ -203,6 +204,10 @@ class PermissionsPrivacyModuleImpl(context: Context) : PermissionsPrivacyModuleB
}
}
+ override fun getLegacyVpnDummyPackage(): String {
+ return VpnConfig.LEGACY_VPN
+ }
+
@TargetApi(29)
private fun getAlwaysOnVpnPackageSDK29(): String? {
val service: IConnectivityManager = IConnectivityManager.Stub.asInterface(
diff --git a/permissionsstandalone/src/main/java/foundation/e/advancedprivacy/permissions/externalinterfaces/PermissionsPrivacyModuleImpl.kt b/permissionsstandalone/src/main/java/foundation/e/advancedprivacy/permissions/externalinterfaces/PermissionsPrivacyModuleImpl.kt
index 142e34eaa6f82c59a7dd60ee52716e68797bb1c8..7ae8e813de78dcb896276cbd0386fcb0e416b110 100644
--- a/permissionsstandalone/src/main/java/foundation/e/advancedprivacy/permissions/externalinterfaces/PermissionsPrivacyModuleImpl.kt
+++ b/permissionsstandalone/src/main/java/foundation/e/advancedprivacy/permissions/externalinterfaces/PermissionsPrivacyModuleImpl.kt
@@ -56,5 +56,9 @@ class PermissionsPrivacyModuleImpl(context: Context) : PermissionsPrivacyModuleB
return null
}
+ override fun getLegacyVpnDummyPackage(): String {
+ return "[Legacy VPN]"
+ }
+
override fun setBlockable(notificationChannel: NotificationChannel) {}
}
diff --git a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/UpdateTrackerListUseCase.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/UpdateTrackerListUseCase.kt
index 2645e60d730fcef5d1b45fb82756e0445f31e626..711e7a03448fbb368b9300d57baf9c8504d69df1 100644
--- a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/UpdateTrackerListUseCase.kt
+++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/UpdateTrackerListUseCase.kt
@@ -20,6 +20,7 @@ package foundation.e.advancedprivacy.trackers.domain.usecases
import foundation.e.advancedprivacy.data.repositories.ETrackersApi
import foundation.e.advancedprivacy.data.repositories.RemoteTrackersListRepository
import foundation.e.advancedprivacy.trackers.data.TrackersRepository
+import java.io.IOException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import timber.log.Timber
@@ -39,6 +40,8 @@ class UpdateTrackerListUseCase(
try {
remoteTrackersListRepository.saveData(trackersRepository.eTrackerFile, api.trackers())
trackersRepository.initTrackersFile()
+ } catch (e: IOException) {
+ Timber.d(e, "While updating trackers, is internet reachable ?")
} catch (e: Exception) {
Timber.e(e, "While updating trackers")
}
diff --git a/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/VpnSupervisorUseCaseEos.kt b/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/VpnSupervisorUseCaseEos.kt
index afbf90faabb2f9e6697319488d29b7de6ff9bb9a..510c0499447240ce2c3a9d2750c19592ebb346bd 100644
--- a/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/VpnSupervisorUseCaseEos.kt
+++ b/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/VpnSupervisorUseCaseEos.kt
@@ -22,6 +22,7 @@ import foundation.e.advancedprivacy.domain.entities.FeatureState
import foundation.e.advancedprivacy.domain.entities.MainFeatures
import foundation.e.advancedprivacy.domain.entities.MainFeatures.IpScrambling
import foundation.e.advancedprivacy.domain.entities.MainFeatures.TrackersControl
+import foundation.e.advancedprivacy.domain.entities.ProfileType
import foundation.e.advancedprivacy.domain.repositories.LocalStateRepository
import foundation.e.advancedprivacy.domain.usecases.VpnSupervisorUseCase
import foundation.e.advancedprivacy.externalinterfaces.permissions.IPermissionsPrivacyModule
@@ -32,6 +33,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
+import timber.log.Timber
class VpnSupervisorUseCaseEos(
private val localStateRepository: LocalStateRepository,
@@ -82,12 +84,35 @@ class VpnSupervisorUseCaseEos(
permissionsPrivacyModule.setVpnPackageAuthorization(appDesc.packageName)
val alwaysOnVpnPackage = permissionsPrivacyModule.getAlwaysOnVpnPackage()
if (alwaysOnVpnPackage != null) {
- localStateRepository.emitOtherVpnRunning(
- permissionsPrivacyModule.getApplicationDescription(
+ val vpnApp = if (alwaysOnVpnPackage == permissionsPrivacyModule.getLegacyVpnDummyPackage()) {
+ ApplicationDescription(
packageName = alwaysOnVpnPackage,
- withIcon = false
+ uid = -1,
+ profileId = -1,
+ profileType = ProfileType.MAIN,
+ label = "PTP, L2TP/IPSec or IPSec",
+ icon = null
)
- )
+ } else {
+ try {
+ permissionsPrivacyModule.getApplicationDescription(
+ packageName = alwaysOnVpnPackage,
+ withIcon = false
+ )
+ } catch (e: Exception) {
+ Timber.e(e, "Can't getApplicationDescription for package: $alwaysOnVpnPackage")
+ ApplicationDescription(
+ packageName = alwaysOnVpnPackage,
+ uid = -1,
+ profileId = -1,
+ profileType = ProfileType.MAIN,
+ label = null,
+ icon = null
+ )
+ }
+ }
+
+ localStateRepository.emitOtherVpnRunning(vpnApp)
localStateRepository.toggleIpScrambling(enabled = false)
return
}