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 }