diff options
author | Guillaume Jacquart <guillaume.jacquart@hoodbrains.com> | 2023-11-06 08:14:27 +0000 |
---|---|---|
committer | Guillaume Jacquart <guillaume.jacquart@hoodbrains.com> | 2023-11-06 08:14:27 +0000 |
commit | 95d9421d4d982562f83db019e5c3f59c5acfcdf4 (patch) | |
tree | 56c69c0911e512aaaecd22cb02f2c1305f42d8e2 /trackersservicestandalone | |
parent | 50e213ce1db332b95af5018e553c0ee2cd810e39 (diff) | |
parent | 9d55978063947d5865bb3fa4e0c2ebef78f78812 (diff) |
Merge branch 'epic18-standalone_trackers_tor' into 'main'
epic18: Manage VPN services for Tor or Tracker control
See merge request e/os/advanced-privacy!149
Diffstat (limited to 'trackersservicestandalone')
-rw-r--r-- | trackersservicestandalone/build.gradle | 1 | ||||
-rw-r--r-- | trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt | 21 | ||||
-rw-r--r-- | trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersSupervisorStandalone.kt (renamed from trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt) | 30 | ||||
-rw-r--r-- | trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TunLooper.kt | 1 | ||||
-rw-r--r-- | trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/usecases/VpnSupervisorUseCaseStandalone.kt | 154 |
5 files changed, 181 insertions, 26 deletions
diff --git a/trackersservicestandalone/build.gradle b/trackersservicestandalone/build.gradle index ead9dbd..5b574cd 100644 --- a/trackersservicestandalone/build.gradle +++ b/trackersservicestandalone/build.gradle @@ -29,6 +29,7 @@ android { dependencies { implementation project(":core") implementation project(":trackers") + implementation project(":ipscrambling") implementation( libs.androidx.core.ktx, diff --git a/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt index 918977f..152a3e9 100644 --- a/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt +++ b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt @@ -16,16 +16,15 @@ */ package foundation.e.advancedprivacy.trackers.service -import android.content.Context import android.content.Intent import android.net.VpnService import android.os.Build import android.os.ParcelFileDescriptor import foundation.e.advancedprivacy.core.utils.notificationBuilder -import foundation.e.advancedprivacy.domain.entities.FeatureServiceState +import foundation.e.advancedprivacy.domain.entities.FeatureState import foundation.e.advancedprivacy.domain.entities.NOTIFICATION_TRACKER_FLAG import foundation.e.advancedprivacy.domain.entities.NotificationContent -import foundation.e.advancedprivacy.trackers.domain.externalinterfaces.TrackersServiceSupervisor +import foundation.e.advancedprivacy.trackers.domain.externalinterfaces.TrackersSupervisor import foundation.e.advancedprivacy.trackers.service.Config.DNS_SERVER_TO_CATCH_IPV4 import foundation.e.advancedprivacy.trackers.service.Config.DNS_SERVER_TO_CATCH_IPV6 import foundation.e.advancedprivacy.trackers.service.Config.SESSION_NAME @@ -39,18 +38,12 @@ import timber.log.Timber class TrackersService : VpnService() { companion object { var coroutineScope = CoroutineScope(Dispatchers.IO) - - fun start(context: Context) { - prepare(context) - val intent = Intent(context, TrackersService::class.java) - context.startService(intent) - } } private val networkDNSAddressRepository: NetworkDNSAddressRepository = get(NetworkDNSAddressRepository::class.java) - private val trackersServiceSupervisor: TrackersServiceSupervisorImpl = get( - TrackersServiceSupervisor::class.java - ) as TrackersServiceSupervisorImpl + private val trackersSupervisor: TrackersSupervisorStandalone = get( + TrackersSupervisor::class.java + ) as TrackersSupervisorStandalone private val notificationTrackerFlag: NotificationContent = get(NotificationContent::class.java, named("notificationTrackerFlag")) @@ -64,14 +57,14 @@ class TrackersService : VpnService() { content = notificationTrackerFlag ).build() ) - trackersServiceSupervisor.state.value = FeatureServiceState.ON + trackersSupervisor.mutableState.value = FeatureState.ON return START_STICKY } override fun onDestroy() { networkDNSAddressRepository.stop() - trackersServiceSupervisor.state.value = FeatureServiceState.OFF + trackersSupervisor.mutableState.value = FeatureState.OFF super.onDestroy() } diff --git a/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersSupervisorStandalone.kt index e2a6692..ac06ced 100644 --- a/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt +++ b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersSupervisorStandalone.kt @@ -18,37 +18,42 @@ package foundation.e.advancedprivacy.trackers.service import android.content.Context import android.content.Intent -import foundation.e.advancedprivacy.domain.entities.FeatureServiceState -import foundation.e.advancedprivacy.trackers.domain.externalinterfaces.TrackersServiceSupervisor +import foundation.e.advancedprivacy.domain.entities.FeatureState +import foundation.e.advancedprivacy.domain.usecases.VpnSupervisorUseCase +import foundation.e.advancedprivacy.trackers.domain.externalinterfaces.TrackersSupervisor import foundation.e.advancedprivacy.trackers.service.data.NetworkDNSAddressRepository import foundation.e.advancedprivacy.trackers.service.data.RequestDNSRepository import foundation.e.advancedprivacy.trackers.service.usecases.ResolveDNSUseCase +import foundation.e.advancedprivacy.trackers.service.usecases.VpnSupervisorUseCaseStandalone import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import org.koin.core.module.dsl.bind import org.koin.core.module.dsl.singleOf import org.koin.dsl.module import org.pcap4j.packet.DnsPacket import java.util.function.Function -class TrackersServiceSupervisorImpl( +class TrackersSupervisorStandalone( private val context: Context, private val resolveDNSUseCase: ResolveDNSUseCase -) : TrackersServiceSupervisor { - internal val state: MutableStateFlow<FeatureServiceState> = MutableStateFlow(FeatureServiceState.OFF) +) : TrackersSupervisor { + internal val mutableState: MutableStateFlow<FeatureState> = MutableStateFlow(FeatureState.OFF) + override val state: StateFlow<FeatureState> = mutableState override fun start(): Boolean { return if (!isRunning()) { - state.value = FeatureServiceState.STARTING - TrackersService.start(context) + mutableState.value = FeatureState.STARTING + val intent = Intent(context, TrackersService::class.java) + context.startService(intent) true } else false } override fun stop(): Boolean { - return when (state.value) { - FeatureServiceState.ON -> { - state.value = FeatureServiceState.STOPPING + return when (mutableState.value) { + FeatureState.ON -> { + mutableState.value = FeatureState.STOPPING kotlin.runCatching { TrackersService.coroutineScope.cancel() } context.stopService(Intent(context, TrackersService::class.java)) true @@ -58,7 +63,7 @@ class TrackersServiceSupervisorImpl( } override fun isRunning(): Boolean { - return state.value != FeatureServiceState.OFF + return state.value != FeatureState.OFF } override val dnsFilterForIpScrambling = Function<DnsPacket?, DnsPacket?> { dnsRequest -> resolveDNSUseCase.shouldBlock(dnsRequest) } @@ -69,5 +74,6 @@ val trackerServiceModule = module { singleOf(::RequestDNSRepository) singleOf(::ResolveDNSUseCase) singleOf(::TunLooper) - singleOf(::TrackersServiceSupervisorImpl) { bind<TrackersServiceSupervisor>() } + singleOf(::TrackersSupervisorStandalone) { bind<TrackersSupervisor>() } + singleOf(::VpnSupervisorUseCaseStandalone) { bind<VpnSupervisorUseCase>() } } diff --git a/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TunLooper.kt b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TunLooper.kt index 7813c67..bb349eb 100644 --- a/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TunLooper.kt +++ b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TunLooper.kt @@ -84,6 +84,7 @@ class TunLooper( } } } + closeStreams() } private suspend fun handleIpPacket(buffer: ByteArray, pLen: Int) { diff --git a/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/usecases/VpnSupervisorUseCaseStandalone.kt b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/usecases/VpnSupervisorUseCaseStandalone.kt new file mode 100644 index 0000000..683f886 --- /dev/null +++ b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/usecases/VpnSupervisorUseCaseStandalone.kt @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2023 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 <https://www.gnu.org/licenses/>. + */ +package foundation.e.advancedprivacy.trackers.service.usecases + +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.repositories.LocalStateRepository +import foundation.e.advancedprivacy.domain.usecases.VpnSupervisorUseCase +import foundation.e.advancedprivacy.externalinterfaces.servicesupervisors.FeatureSupervisor +import foundation.e.advancedprivacy.ipscrambler.OrbotSupervisor +import foundation.e.advancedprivacy.trackers.domain.externalinterfaces.TrackersSupervisor +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.launch + +class VpnSupervisorUseCaseStandalone( + private val localStateRepository: LocalStateRepository, + private val trackersSupervisor: TrackersSupervisor, + private val orbotSupervisor: OrbotSupervisor, + private val scope: CoroutineScope, +) : VpnSupervisorUseCase { + private var applySettingJob: Job? = null + + init { + listenSettings() + } + + override fun listenSettings() { + var previousBlockTrackers: Boolean? = null + + localStateRepository.blockTrackers.combine( + localStateRepository.ipScramblingSetting + ) { blockTrackers, hideIp -> + applySettingJob?.cancel() + applySettingJob = scope.launch { + when { + blockTrackers && !hideIp -> + launchVpnService(trackersSupervisor) + + !blockTrackers && !hideIp -> + stopAllServices() + + else -> { + if (blockTrackers && previousBlockTrackers != true) { + localStateRepository.emitStartVpnDisclaimer(IpScrambling()) + } + + launchVpnService(orbotSupervisor) + } + } + + previousBlockTrackers = blockTrackers + } + }.launchIn(scope) + } + + private fun stopAllServices() { + listOf(orbotSupervisor, trackersSupervisor).map { stopVpnService(it) } + } + + private fun stopVpnService(supervisor: FeatureSupervisor): FeatureSupervisor { + when (supervisor.state.value) { + FeatureState.ON, + FeatureState.STARTING -> + supervisor.stop() + + else -> {} + } + return supervisor + } + + private suspend fun launchVpnService(supervisor: FeatureSupervisor) { + stopVpnService(otherSupervisor(supervisor)).let { otherSupervisor -> + otherSupervisor.state.first { it == FeatureState.OFF } + } + + when (supervisor.state.value) { + FeatureState.STOPPING -> { + supervisor.state.first { it == FeatureState.OFF } + initiateStartVpnService(supervisor) + } + + FeatureState.OFF -> initiateStartVpnService(supervisor) + else -> {} + } + } + + private fun otherSupervisor(supervisor: FeatureSupervisor): FeatureSupervisor { + return when (supervisor) { + trackersSupervisor -> orbotSupervisor + else -> trackersSupervisor + } + } + + private fun getSupervisor(feature: MainFeatures): FeatureSupervisor { + return when (feature) { + is TrackersControl -> trackersSupervisor + else -> orbotSupervisor + } + } + + private suspend fun initiateStartVpnService(supervisor: FeatureSupervisor) { + val authorizeVpnIntent = orbotSupervisor.prepareAndroidVpn() + val feature = when (supervisor) { + trackersSupervisor -> TrackersControl(authorizeVpnIntent) + else -> IpScrambling(authorizeVpnIntent) + } + + if (authorizeVpnIntent == null) { + localStateRepository.emitStartVpnDisclaimer(feature) + startVpnService(feature) + } else { + localStateRepository.emitStartVpnDisclaimer(feature) + } + } + + override fun startVpnService(feature: MainFeatures) { + if (feature is IpScrambling) { + localStateRepository.internetPrivacyMode.value = FeatureState.STARTING + orbotSupervisor.setDNSFilter(trackersSupervisor.dnsFilterForIpScrambling) + } + + getSupervisor(feature).start() + } + + override fun cancelStartVpnService(feature: MainFeatures) { + when (feature) { + is IpScrambling -> + localStateRepository.setIpScramblingSetting(enabled = false) + is TrackersControl -> + trackersSupervisor.stop() + else -> {} + } + } +} |