summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillaume Jacquart <guillaume.jacquart@hoodbrains.com>2023-10-23 15:55:13 +0000
committerGuillaume Jacquart <guillaume.jacquart@hoodbrains.com>2023-10-23 15:55:13 +0000
commit0312ce64f85b5530a00bdc72eb310ba9dc1de05b (patch)
tree030ccbed3f44a2a1f96413947f50ec0520fb064d
parentd0c2b36ec81cd2a102d4b0a5b0fbeb1c1aa52e70 (diff)
parent54892a227a77839ee81df90df904675f958831a3 (diff)
Merge branch 'epic18-standalone_trackers_tor_alone' into 'main'
epic18: tracker control while tor is activated. See merge request e/os/advanced-privacy!148
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt11
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/Notifications.kt4
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepository.kt4
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/entities/InternetPrivacyMode.kt29
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/entities/ShowFeaturesWarning.kt4
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/usecases/GetQuickPrivacyStateUseCase.kt11
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/usecases/IpScramblingStateUseCase.kt99
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardFragment.kt10
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardState.kt4
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyFragment.kt24
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyState.kt4
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyViewModel.kt16
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/widget/WidgetUI.kt6
-rw-r--r--core/src/main/java/foundation/e/advancedprivacy/domain/entities/FeatureServiceState.kt6
-rw-r--r--gradle/libs.versions.toml2
-rw-r--r--ipscrambling/README.md2
-rw-r--r--ipscrambling/build.gradle4
-rw-r--r--ipscrambling/dependencies.gradle61
-rw-r--r--ipscrambling/exportdependencies/publish.gradle6
-rw-r--r--ipscrambling/exportdependencies/update_dependencies.md4
-rw-r--r--ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/KoinModule.kt2
-rw-r--r--ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/OrbotServiceSupervisor.kt (renamed from ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/IpScramblerModule.kt)132
-rw-r--r--settings.gradle2
-rw-r--r--trackers/build.gradle1
-rw-r--r--trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/externalinterfaces/TrackersServiceSupervisor.kt5
-rw-r--r--trackersservicee/build.gradle9
-rw-r--r--trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt7
-rw-r--r--trackersservicestandalone/build.gradle9
-rw-r--r--trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt11
-rw-r--r--trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/usecases/ResolveDNSUseCase.kt33
30 files changed, 244 insertions, 278 deletions
diff --git a/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt b/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt
index 20cefd5..fbf1252 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt
@@ -42,8 +42,6 @@ import foundation.e.advancedprivacy.features.trackers.TrackersViewModel
import foundation.e.advancedprivacy.features.trackers.apptrackers.AppTrackersViewModel
import foundation.e.advancedprivacy.ipscrambler.ipScramblerModule
import foundation.e.advancedprivacy.permissions.externalinterfaces.PermissionsPrivacyModuleImpl
-import foundation.e.advancedprivacy.trackers.domain.externalinterfaces.TrackersServiceSupervisor
-import foundation.e.advancedprivacy.trackers.service.TrackersServiceSupervisorImpl
import foundation.e.advancedprivacy.trackers.service.trackerServiceModule
import foundation.e.advancedprivacy.trackers.trackersModule
import org.koin.android.ext.koin.androidContext
@@ -122,11 +120,12 @@ val appModule = module {
singleOf(::GetQuickPrivacyStateUseCase)
single {
IpScramblingStateUseCase(
- ipScramblerModule = get(),
+ orbotServiceSupervisor = get(),
permissionsPrivacyModule = get(),
appDesc = get(named("AdvancedPrivacy")),
localStateRepository = get(),
appListsRepository = get(),
+ trackersServiceSupervisor = get(),
coroutineScope = get()
)
}
@@ -138,12 +137,6 @@ val appModule = module {
PermissionsPrivacyModuleImpl(context = androidContext())
}
- single<TrackersServiceSupervisor> {
- TrackersServiceSupervisorImpl(
- context = androidContext(),
- )
- }
-
viewModel { parameters ->
val appListUseCase: AppListUseCase = get()
val app = appListUseCase.getApp(parameters.get())
diff --git a/app/src/main/java/foundation/e/advancedprivacy/Notifications.kt b/app/src/main/java/foundation/e/advancedprivacy/Notifications.kt
index 455b1a7..639ede4 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/Notifications.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/Notifications.kt
@@ -29,7 +29,7 @@ import foundation.e.advancedprivacy.domain.entities.CHANNEL_FAKE_LOCATION_FLAG
import foundation.e.advancedprivacy.domain.entities.CHANNEL_FIRST_BOOT
import foundation.e.advancedprivacy.domain.entities.CHANNEL_IPSCRAMBLING_FLAG
import foundation.e.advancedprivacy.domain.entities.CHANNEL_TRACKER_FLAG
-import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode
+import foundation.e.advancedprivacy.domain.entities.FeatureServiceState
import foundation.e.advancedprivacy.domain.entities.MainFeatures
import foundation.e.advancedprivacy.domain.entities.NOTIFICATION_FAKE_LOCATION_FLAG
import foundation.e.advancedprivacy.domain.entities.NOTIFICATION_FIRST_BOOT
@@ -105,7 +105,7 @@ object Notifications {
}.launchIn(appScope)
getQuickPrivacyStateUseCase.ipScramblingMode.map {
- it != InternetPrivacyMode.REAL_IP
+ it != FeatureServiceState.OFF
}.distinctUntilChanged().onEach {
if (it) {
showFlagNotification(appContext, MainFeatures.IP_SCRAMBLING)
diff --git a/app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepository.kt b/app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepository.kt
index abc4de0..c7d4a27 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepository.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepository.kt
@@ -21,7 +21,7 @@ package foundation.e.advancedprivacy.data.repositories
import android.content.Context
import android.content.Intent
import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
-import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode
+import foundation.e.advancedprivacy.domain.entities.FeatureServiceState
import foundation.e.advancedprivacy.domain.entities.LocationMode
import foundation.e.advancedprivacy.domain.entities.ShowFeaturesWarning
import kotlinx.coroutines.flow.MutableSharedFlow
@@ -89,7 +89,7 @@ class LocalStateRepository(context: Context) {
_ipScramblingSetting.update { enabled }
}
- val internetPrivacyMode: MutableStateFlow<InternetPrivacyMode> = MutableStateFlow(InternetPrivacyMode.REAL_IP)
+ val internetPrivacyMode: MutableStateFlow<FeatureServiceState> = MutableStateFlow(FeatureServiceState.OFF)
private val _startVpnDisclaimer = MutableSharedFlow<ShowFeaturesWarning.IpScrambling>()
suspend fun emitStartVpnDisclaimer(intent: Intent?) {
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/entities/InternetPrivacyMode.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/InternetPrivacyMode.kt
deleted file mode 100644
index 986e798..0000000
--- a/app/src/main/java/foundation/e/advancedprivacy/domain/entities/InternetPrivacyMode.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2021 E FOUNDATION
- *
- * 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.domain.entities
-
-enum class InternetPrivacyMode {
- REAL_IP,
- HIDE_IP,
- HIDE_IP_LOADING,
- REAL_IP_LOADING;
-
- val isChecked get() = this == HIDE_IP || this == HIDE_IP_LOADING
-
- val isLoading get() = this == HIDE_IP_LOADING || this == REAL_IP_LOADING
-}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/entities/ShowFeaturesWarning.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/ShowFeaturesWarning.kt
index 221f4e1..0d8e0e8 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/domain/entities/ShowFeaturesWarning.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/ShowFeaturesWarning.kt
@@ -21,9 +21,11 @@ import android.content.Intent
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
-@Parcelize
sealed class ShowFeaturesWarning : Parcelable {
+ @Parcelize
object TrackersControl : ShowFeaturesWarning()
+ @Parcelize
object FakeLocation : ShowFeaturesWarning()
+ @Parcelize
data class IpScrambling(val startVpnDisclaimer: Intent? = null) : ShowFeaturesWarning()
}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/GetQuickPrivacyStateUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/GetQuickPrivacyStateUseCase.kt
index bc4871a..1b8f62c 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/GetQuickPrivacyStateUseCase.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/GetQuickPrivacyStateUseCase.kt
@@ -19,7 +19,7 @@ package foundation.e.advancedprivacy.domain.usecases
import foundation.e.advancedprivacy.data.repositories.LocalStateRepository
import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
-import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode
+import foundation.e.advancedprivacy.domain.entities.FeatureServiceState
import foundation.e.advancedprivacy.domain.entities.LocationMode
import foundation.e.advancedprivacy.domain.entities.QuickPrivacyState
import foundation.e.advancedprivacy.domain.entities.TrackerMode
@@ -41,13 +41,13 @@ class GetQuickPrivacyStateUseCase(
when {
!isBlockTrackers &&
locationMode == LocationMode.REAL_LOCATION &&
- internetPrivacyMode == InternetPrivacyMode.REAL_IP -> QuickPrivacyState.DISABLED
+ internetPrivacyMode == FeatureServiceState.OFF -> QuickPrivacyState.DISABLED
isAllTrackersBlocked &&
locationMode != LocationMode.REAL_LOCATION &&
internetPrivacyMode in listOf(
- InternetPrivacyMode.HIDE_IP,
- InternetPrivacyMode.HIDE_IP_LOADING
+ FeatureServiceState.ON,
+ FeatureServiceState.STARTING
) -> QuickPrivacyState.FULL_ENABLED
else -> QuickPrivacyState.ENABLED
@@ -71,7 +71,8 @@ class GetQuickPrivacyStateUseCase(
val locationMode: StateFlow<LocationMode> = localStateRepository.locationMode
- val ipScramblingMode: Flow<InternetPrivacyMode> = localStateRepository.internetPrivacyMode
+ val ipScramblingMode: Flow<FeatureServiceState> =
+ localStateRepository.internetPrivacyMode
fun toggleTrackers(enabled: Boolean?) {
val value = enabled ?: !localStateRepository.blockTrackers.value
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/IpScramblingStateUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/IpScramblingStateUseCase.kt
index 9c89329..79c79f7 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/IpScramblingStateUseCase.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/IpScramblingStateUseCase.kt
@@ -23,74 +23,40 @@ import foundation.e.advancedprivacy.common.isStandaloneBuild
import foundation.e.advancedprivacy.data.repositories.AppListsRepository
import foundation.e.advancedprivacy.data.repositories.LocalStateRepository
import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
-import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode
-import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode.HIDE_IP
-import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode.HIDE_IP_LOADING
-import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode.REAL_IP
-import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode.REAL_IP_LOADING
+import foundation.e.advancedprivacy.domain.entities.FeatureServiceState
import foundation.e.advancedprivacy.externalinterfaces.permissions.IPermissionsPrivacyModule
-import foundation.e.advancedprivacy.ipscrambler.IpScramblerModule
+import foundation.e.advancedprivacy.ipscrambler.OrbotServiceSupervisor
+import foundation.e.advancedprivacy.trackers.domain.externalinterfaces.TrackersServiceSupervisor
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
class IpScramblingStateUseCase(
- private val ipScramblerModule: IpScramblerModule,
+ private val orbotServiceSupervisor: OrbotServiceSupervisor,
private val permissionsPrivacyModule: IPermissionsPrivacyModule,
private val appDesc: ApplicationDescription,
private val localStateRepository: LocalStateRepository,
private val appListsRepository: AppListsRepository,
+ private val trackersServiceSupervisor: TrackersServiceSupervisor,
private val coroutineScope: CoroutineScope
) {
- val internetPrivacyMode: StateFlow<InternetPrivacyMode> = callbackFlow {
- val listener = object : IpScramblerModule.Listener {
- override fun onStatusChanged(newStatus: IpScramblerModule.Status) {
- trySend(map(newStatus))
- }
-
- override fun log(message: String) {}
- override fun onTrafficUpdate(
- upload: Long,
- download: Long,
- read: Long,
- write: Long
- ) {
- }
- }
- ipScramblerModule.addListener(listener)
- ipScramblerModule.requestStatus()
- awaitClose { ipScramblerModule.removeListener(listener) }
- }.stateIn(
- scope = coroutineScope,
- started = SharingStarted.Eagerly,
- initialValue = REAL_IP
- )
+ val internetPrivacyMode: StateFlow<FeatureServiceState> = orbotServiceSupervisor.state
init {
+ orbotServiceSupervisor.requestStatus()
+
coroutineScope.launch(Dispatchers.Default) {
localStateRepository.ipScramblingSetting.collect {
applySettings(it)
}
}
- coroutineScope.launch(Dispatchers.IO) {
- internetPrivacyMode.collect {
- if (
- it == REAL_IP &&
- localStateRepository.internetPrivacyMode.value == REAL_IP_LOADING
- ) {
- // Wait for orbot to relax before allowing user to reactivate it.
- delay(1000)
- }
- localStateRepository.internetPrivacyMode.value = it
- }
- }
+ orbotServiceSupervisor.state.map {
+ localStateRepository.internetPrivacyMode.value = it
+ }.launchIn(coroutineScope)
}
fun toggle(hideIp: Boolean) {
@@ -102,7 +68,7 @@ class IpScramblingStateUseCase(
}
val bypassTorApps: Set<String> get() {
- var whitelist = ipScramblerModule.appList
+ var whitelist = orbotServiceSupervisor.appList
if (getHiddenPackageNames().any { it in whitelist }) {
val mutable = whitelist.toMutableSet()
mutable.removeAll(getHiddenPackageNames())
@@ -120,7 +86,7 @@ class IpScramblingStateUseCase(
fun toggleBypassTor(packageName: String) {
val visibleList = bypassTorApps.toMutableSet()
- val rawList = ipScramblerModule.appList.toMutableSet()
+ val rawList = orbotServiceSupervisor.appList.toMutableSet()
if (visibleList.contains(packageName)) {
if (packageName == appListsRepository.dummySystemApp.packageName) {
@@ -139,24 +105,34 @@ class IpScramblingStateUseCase(
rawList.add(packageName)
}
}
- ipScramblerModule.appList = rawList
+ orbotServiceSupervisor.appList = rawList
+ }
+
+ val availablesLocations: List<String> = orbotServiceSupervisor.getAvailablesLocations().sorted()
+
+ val exitCountry: String get() = orbotServiceSupervisor.getExitCountryCode()
+
+ suspend fun setExitCountry(locationId: String) {
+ if (locationId != exitCountry) {
+ orbotServiceSupervisor.setExitCountryCode(locationId)
+ }
}
private suspend fun applySettings(isIpScramblingEnabled: Boolean) {
val currentMode = localStateRepository.internetPrivacyMode.value
when {
- isIpScramblingEnabled && currentMode in setOf(REAL_IP, REAL_IP_LOADING) ->
+ isIpScramblingEnabled && currentMode in setOf(FeatureServiceState.OFF, FeatureServiceState.STOPPING) ->
applyStartIpScrambling()
- !isIpScramblingEnabled && currentMode in setOf(HIDE_IP, HIDE_IP_LOADING) ->
- ipScramblerModule.stop()
+ !isIpScramblingEnabled && currentMode in setOf(FeatureServiceState.ON, FeatureServiceState.STARTING) ->
+ orbotServiceSupervisor.stop()
else -> {}
}
}
private suspend fun applyStartIpScrambling() {
- val authorizeVpnIntent = ipScramblerModule.prepareAndroidVpn()
+ val authorizeVpnIntent = orbotServiceSupervisor.prepareAndroidVpn()
if (authorizeVpnIntent == null) {
localStateRepository.emitStartVpnDisclaimer(null)
@@ -190,17 +166,8 @@ class IpScramblingStateUseCase(
}
fun startIpScrambling() {
- localStateRepository.internetPrivacyMode.value = HIDE_IP_LOADING
- ipScramblerModule.start(enableNotification = isStandaloneBuild)
- }
-
- private fun map(status: IpScramblerModule.Status): InternetPrivacyMode {
- return when (status) {
- IpScramblerModule.Status.OFF -> REAL_IP
- IpScramblerModule.Status.ON -> HIDE_IP
- IpScramblerModule.Status.STARTING -> HIDE_IP_LOADING
- IpScramblerModule.Status.STOPPING,
- IpScramblerModule.Status.START_DISABLED -> REAL_IP_LOADING
- }
+ localStateRepository.internetPrivacyMode.value = FeatureServiceState.STARTING
+ orbotServiceSupervisor.setDNSFilter((trackersServiceSupervisor.dnsFilterForIpScrambling))
+ orbotServiceSupervisor.start(enableNotification = isStandaloneBuild)
}
}
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 999955e..0a53c6c 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
@@ -34,7 +34,7 @@ import foundation.e.advancedprivacy.R
import foundation.e.advancedprivacy.common.GraphHolder
import foundation.e.advancedprivacy.common.NavToolbarFragment
import foundation.e.advancedprivacy.databinding.FragmentDashboardBinding
-import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode
+import foundation.e.advancedprivacy.domain.entities.FeatureServiceState
import foundation.e.advancedprivacy.domain.entities.LocationMode
import foundation.e.advancedprivacy.domain.entities.QuickPrivacyState
import foundation.e.advancedprivacy.domain.entities.TrackerMode
@@ -186,11 +186,11 @@ class DashboardFragment : NavToolbarFragment(R.layout.fragment_dashboard) {
binding.toggleIpscrambling.isChecked = state.ipScramblingMode.isChecked
val isLoading = state.ipScramblingMode.isLoading
binding.toggleIpscrambling.isEnabled = (
- state.ipScramblingMode != InternetPrivacyMode.REAL_IP_LOADING
+ state.ipScramblingMode != FeatureServiceState.STOPPING
)
binding.stateIpAddress.text = getString(
- if (state.ipScramblingMode == InternetPrivacyMode.HIDE_IP) R.string.dashboard_state_ipaddress_on
+ if (state.ipScramblingMode == FeatureServiceState.ON) R.string.dashboard_state_ipaddress_on
else R.string.dashboard_state_ipaddress_off
)
@@ -200,7 +200,7 @@ class DashboardFragment : NavToolbarFragment(R.layout.fragment_dashboard) {
binding.stateIpAddress.setTextColor(
getColor(
requireContext(),
- if (state.ipScramblingMode == InternetPrivacyMode.HIDE_IP) R.color.green_valid
+ if (state.ipScramblingMode == FeatureServiceState.ON) R.color.green_valid
else R.color.red_off
)
)
@@ -250,7 +250,7 @@ class DashboardFragment : NavToolbarFragment(R.layout.fragment_dashboard) {
)
binding.internetActivityPrivacy.subTitle = getString(
- if (state.ipScramblingMode == InternetPrivacyMode.HIDE_IP) R.string.dashboard_internet_activity_privacy_subtitle_on
+ if (state.ipScramblingMode == FeatureServiceState.ON) R.string.dashboard_internet_activity_privacy_subtitle_on
else R.string.dashboard_internet_activity_privacy_subtitle_off
)
diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardState.kt b/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardState.kt
index 8fc8767..069ff04 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardState.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardState.kt
@@ -17,7 +17,7 @@
package foundation.e.advancedprivacy.features.dashboard
-import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode
+import foundation.e.advancedprivacy.domain.entities.FeatureServiceState
import foundation.e.advancedprivacy.domain.entities.LocationMode
import foundation.e.advancedprivacy.domain.entities.QuickPrivacyState
import foundation.e.advancedprivacy.domain.entities.TrackerMode
@@ -26,7 +26,7 @@ data class DashboardState(
val quickPrivacyState: QuickPrivacyState = QuickPrivacyState.DISABLED,
val trackerMode: TrackerMode = TrackerMode.VULNERABLE,
val isLocationHidden: Boolean = false,
- val ipScramblingMode: InternetPrivacyMode = InternetPrivacyMode.REAL_IP_LOADING,
+ val ipScramblingMode: FeatureServiceState = FeatureServiceState.STOPPING,
val locationMode: LocationMode = LocationMode.REAL_LOCATION,
val leakedTrackersCount: Int? = null,
val trackersCount: Int? = null,
diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyFragment.kt b/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyFragment.kt
index 1180af3..b4fc8a1 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyFragment.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyFragment.kt
@@ -32,7 +32,7 @@ import foundation.e.advancedprivacy.common.NavToolbarFragment
import foundation.e.advancedprivacy.common.ToggleAppsAdapter
import foundation.e.advancedprivacy.common.setToolTipForAsterisk
import foundation.e.advancedprivacy.databinding.FragmentInternetActivityPolicyBinding
-import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode
+import foundation.e.advancedprivacy.domain.entities.FeatureServiceState
import kotlinx.coroutines.launch
import org.koin.androidx.viewmodel.ext.android.viewModel
import java.util.Locale
@@ -136,19 +136,12 @@ class InternetPrivacyFragment : NavToolbarFragment(R.layout.fragment_internet_ac
private fun render(state: InternetPrivacyState) {
binding.radioUseHiddenIp.radiobutton.apply {
- isChecked = state.mode in listOf(
- InternetPrivacyMode.HIDE_IP,
- InternetPrivacyMode.HIDE_IP_LOADING
- )
- isEnabled = state.mode != InternetPrivacyMode.HIDE_IP_LOADING
+ isChecked = state.mode.isChecked
+ isEnabled = state.mode != FeatureServiceState.STARTING
}
binding.radioUseRealIp.radiobutton.apply {
- isChecked =
- state.mode in listOf(
- InternetPrivacyMode.REAL_IP,
- InternetPrivacyMode.REAL_IP_LOADING
- )
- isEnabled = state.mode != InternetPrivacyMode.REAL_IP_LOADING
+ isChecked = !state.mode.isChecked
+ isEnabled = state.mode != FeatureServiceState.STOPPING
}
binding.ipscramblingSelectLocation.setSelection(state.selectedLocationPosition)
@@ -157,7 +150,7 @@ class InternetPrivacyFragment : NavToolbarFragment(R.layout.fragment_internet_ac
binding.apps.post {
(binding.apps.adapter as ToggleAppsAdapter?)?.setData(
list = state.getApps(),
- isEnabled = state.mode == InternetPrivacyMode.HIDE_IP
+ isEnabled = state.mode == FeatureServiceState.ON
)
}
@@ -170,10 +163,7 @@ class InternetPrivacyFragment : NavToolbarFragment(R.layout.fragment_internet_ac
)
when {
- state.mode in listOf(
- InternetPrivacyMode.HIDE_IP_LOADING,
- InternetPrivacyMode.REAL_IP_LOADING
- )
+ state.mode.isLoading
|| state.availableApps.isEmpty() -> {
binding.loader.visibility = View.VISIBLE
viewIdsToHide.forEach { it.visibility = View.GONE }
diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyState.kt b/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyState.kt
index 4d0fb38..e607d6c 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyState.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyState.kt
@@ -18,10 +18,10 @@
package foundation.e.advancedprivacy.features.internetprivacy
import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
-import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode
+import foundation.e.advancedprivacy.domain.entities.FeatureServiceState
data class InternetPrivacyState(
- val mode: InternetPrivacyMode = InternetPrivacyMode.REAL_IP,
+ val mode: FeatureServiceState = FeatureServiceState.OFF,
val availableApps: List<ApplicationDescription> = emptyList(),
val bypassTorApps: Collection<String> = emptyList(),
val selectedLocation: String = "",
diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyViewModel.kt b/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyViewModel.kt
index 059e11d..10530e1 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyViewModel.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyViewModel.kt
@@ -22,11 +22,10 @@ import androidx.annotation.StringRes
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import foundation.e.advancedprivacy.R
-import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode
+import foundation.e.advancedprivacy.domain.entities.FeatureServiceState
import foundation.e.advancedprivacy.domain.usecases.AppListUseCase
import foundation.e.advancedprivacy.domain.usecases.GetQuickPrivacyStateUseCase
import foundation.e.advancedprivacy.domain.usecases.IpScramblingStateUseCase
-import foundation.e.advancedprivacy.ipscrambler.IpScramblerModule
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.MutableSharedFlow
@@ -41,7 +40,6 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class InternetPrivacyViewModel(
- private val ipScramblerModule: IpScramblerModule,
private val getQuickPrivacyStateUseCase: GetQuickPrivacyStateUseCase,
private val ipScramblingStateUseCase: IpScramblingStateUseCase,
private val appListUseCase: AppListUseCase
@@ -56,7 +54,7 @@ class InternetPrivacyViewModel(
private val _singleEvents = MutableSharedFlow<SingleEvent>()
val singleEvents = _singleEvents.asSharedFlow()
- val availablesLocationsIds = listOf("", *ipScramblerModule.getAvailablesLocations().sorted().toTypedArray())
+ val availablesLocationsIds = listOf("", *ipScramblingStateUseCase.availablesLocations.toTypedArray())
init {
viewModelScope.launch(Dispatchers.IO) {
@@ -64,7 +62,7 @@ class InternetPrivacyViewModel(
it.copy(
mode = ipScramblingStateUseCase.internetPrivacyMode.value,
availableLocationIds = availablesLocationsIds,
- selectedLocation = ipScramblerModule.exitCountry
+ selectedLocation = ipScramblingStateUseCase.exitCountry
)
}
}
@@ -90,7 +88,7 @@ class InternetPrivacyViewModel(
launch {
ipScramblingStateUseCase.internetPrivacyMode
- .map { it == InternetPrivacyMode.HIDE_IP_LOADING }
+ .map { it == FeatureServiceState.STARTING }
.debounce(WARNING_LOADING_LONG_DELAY)
.collect {
if (it) _singleEvents.emit(
@@ -136,10 +134,8 @@ class InternetPrivacyViewModel(
private suspend fun actionSelectLocation(action: Action.SelectLocationAction) = withContext(Dispatchers.IO) {
val locationId = _state.value.availableLocationIds[action.position]
- if (locationId != ipScramblerModule.exitCountry) {
- ipScramblerModule.exitCountry = locationId
- _state.update { it.copy(selectedLocation = locationId) }
- }
+ ipScramblingStateUseCase.setExitCountry(locationId)
+ _state.update { it.copy(selectedLocation = locationId) }
}
sealed class SingleEvent {
diff --git a/app/src/main/java/foundation/e/advancedprivacy/widget/WidgetUI.kt b/app/src/main/java/foundation/e/advancedprivacy/widget/WidgetUI.kt
index bfd7d1a..1bd8693 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/widget/WidgetUI.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/widget/WidgetUI.kt
@@ -31,7 +31,7 @@ import foundation.e.advancedprivacy.R
import foundation.e.advancedprivacy.Widget
import foundation.e.advancedprivacy.Widget.Companion.isDarkText
import foundation.e.advancedprivacy.common.extensions.dpToPxF
-import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode
+import foundation.e.advancedprivacy.domain.entities.FeatureServiceState
import foundation.e.advancedprivacy.domain.entities.QuickPrivacyState
import foundation.e.advancedprivacy.domain.entities.TrackerMode
import foundation.e.advancedprivacy.features.dashboard.DashboardFragmentArgs
@@ -45,7 +45,7 @@ data class State(
val quickPrivacyState: QuickPrivacyState = QuickPrivacyState.DISABLED,
val trackerMode: TrackerMode = TrackerMode.VULNERABLE,
val isLocationHidden: Boolean = false,
- val ipScramblingMode: InternetPrivacyMode = InternetPrivacyMode.REAL_IP_LOADING,
+ val ipScramblingMode: FeatureServiceState = FeatureServiceState.STOPPING,
val dayStatistics: List<Pair<Int, Int>> = emptyList(),
val activeTrackersCount: Int = 0,
)
@@ -157,7 +157,7 @@ fun render(
setTextViewText(
R.id.state_ip_address,
context.getString(
- if (state.ipScramblingMode == InternetPrivacyMode.HIDE_IP) R.string.widget_state_ipaddress_on
+ if (state.ipScramblingMode == FeatureServiceState.ON) R.string.widget_state_ipaddress_on
else R.string.widget_state_ipaddress_off
)
)
diff --git a/core/src/main/java/foundation/e/advancedprivacy/domain/entities/FeatureServiceState.kt b/core/src/main/java/foundation/e/advancedprivacy/domain/entities/FeatureServiceState.kt
index 6bfecbb..f079c56 100644
--- a/core/src/main/java/foundation/e/advancedprivacy/domain/entities/FeatureServiceState.kt
+++ b/core/src/main/java/foundation/e/advancedprivacy/domain/entities/FeatureServiceState.kt
@@ -17,5 +17,9 @@
package foundation.e.advancedprivacy.domain.entities
enum class FeatureServiceState {
- OFF, ON, STARTING, STOPPING
+ OFF, ON, STARTING, STOPPING;
+
+ val isChecked get() = this == ON || this == STARTING
+
+ val isLoading get() = this == STARTING || this == STOPPING
}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 00bf753..ec4122e 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -7,7 +7,7 @@ androidx-navigation = "2.5.3"
androidx-test = "1.3.0"
androidx-lifecycle = "2.5.0"
androidx-room = "2.3.0"
-orbotservice = "orbot-16.6.3-1"
+orbotservice = "orbot-16.6.3-2"
retrofit = "2.9.0"
pcap4j = "1.8.2"
diff --git a/ipscrambling/README.md b/ipscrambling/README.md
index 777b6a1..be51828 100644
--- a/ipscrambling/README.md
+++ b/ipscrambling/README.md
@@ -22,7 +22,7 @@ This will put compiled AAR and pom file exposing their dependencies in the local
repository (usually in ~/.m2/repository).
-To push release on gitlab
+To push release on gitlab (you will need a valid gitLabPrivateToken in ~/.gradle/gradle.properties)
./gradlew --console=verbose publish
diff --git a/ipscrambling/build.gradle b/ipscrambling/build.gradle
index 39efce7..29fed4f 100644
--- a/ipscrambling/build.gradle
+++ b/ipscrambling/build.gradle
@@ -50,7 +50,9 @@ dependencies {
libs.bundles.koin,
libs.bundles.kotlin.android.coroutines,
libs.androidx.localbroadcast,
+ libs.pcap4j,
+ libs.timber
)
implementation libs.e.orbotservice
- //implementation project(':ipscrambling:orbotservice')
+ implementation project(':core')
}
diff --git a/ipscrambling/dependencies.gradle b/ipscrambling/dependencies.gradle
index 412bf9a..31b6b78 100644
--- a/ipscrambling/dependencies.gradle
+++ b/ipscrambling/dependencies.gradle
@@ -1,5 +1,5 @@
ext {
- versions = [
+ orbotversions = [
android_material : "1.4.0",
android_shell : "1.0.0",
android_snowfall : "1.2.1",
@@ -26,36 +26,37 @@ ext {
tor_android : "0.4.7.10",
pcap_core : "1.8.2",
pcap_factory : "1.8.2",
- orbot_service : libs.versions.orbotservice,
+ orbot_service : "orbot-16.6.3-2", //libs.versions.orbotservice,
+ orbot_service_dependencies : "orbot-16.6.3-1",
]
- libs = [
- android_material : "com.google.android.material:material:$versions.android_material",
- android_shell : "com.jaredrummler:android-shell:$versions.android_shell",
- android_snowfall : "com.github.jetradarmobile:android-snowfall:$versions.android_snowfall",
- android_volley : "com.android.volley:volley:$versions.android_volley",
- androidx_appcompat : "androidx.appcompat:appcompat:$versions.androidx_appcompat",
- androidx_constraint : "androidx.constraintlayout:constraintlayout:$versions.androidx_constraint",
- androidx_coordinator : "androidx.coordinatorlayout:coordinatorlayout:$versions.androidx_coordinator",
- androidx_core : "androidx.core:core:$versions.androidx_core",
- androidx_leanback_core : "androidx.leanback:leanback:$versions.androidx_leanback",
- androidx_leanback_paging : "androidx.leanback:leanback-paging:$versions.androidx_leanback_paging",
- androidx_leanback_preferences : "androidx.leanback:leanback-preference:$versions.androidx_leanback",
- androidx_leanback_tab : "androidx.leanback:leanback-tab:$versions.androidx_leanback_tab",
- androidx_localbroadcast : "androidx.localbroadcastmanager:localbroadcastmanager:$versions.androidx_localbroadcast",
- androidx_multidex : "androidx.multidex:multidex:$versions.androidx_multidex",
- androidx_palette : "androidx.palette:palette:$versions.androidx_palette",
- androidx_recyclerview : "androidx.recyclerview:recyclerview:$versions.androidx_recyclerview",
- apl_appintro : "com.github.apl-devs:appintro:$versions.apl_appintro",
- bclogic_pulsator : "pl.bclogic:pulsator4droid:$versions.bclogic_pulsator",
- fastlane_screengrab : "tools.fastlane:screengrab:$versions.fastlane_screengrab",
- guardian_geoip : "foundation.e:geoip:$versions.orbot_service",
- guardian_jsocks : "com.gitlab.guardianproject:jsocksandroid:$versions.guardian_jsocks",
- guardian_jtorctl : "info.guardianproject:jtorctl:$versions.guardian_jtorctl",
- ipt_proxy : "foundation.e:OrbotIPtProxy:$versions.orbot_service",
- portmapper : "com.offbynull.portmapper:portmapper:$versions.portmapper",
- tor_android : "foundation.e:tor-android:$versions.orbot_service",
- pcap_core : "org.pcap4j:pcap4j-core:$versions.pcap_core",
- pcap_factory : "org.pcap4j:pcap4j-packetfactory-static:$versions.pcap_factory"
+ orbotlibs = [
+ android_material : "com.google.android.material:material:$orbotversions.android_material",
+ android_shell : "com.jaredrummler:android-shell:$orbotversions.android_shell",
+ android_snowfall : "com.github.jetradarmobile:android-snowfall:$orbotversions.android_snowfall",
+ android_volley : "com.android.volley:volley:$orbotversions.android_volley",
+ androidx_appcompat : "androidx.appcompat:appcompat:$orbotversions.androidx_appcompat",
+ androidx_constraint : "androidx.constraintlayout:constraintlayout:$orbotversions.androidx_constraint",
+ androidx_coordinator : "androidx.coordinatorlayout:coordinatorlayout:$orbotversions.androidx_coordinator",
+ androidx_core : "androidx.core:core:$orbotversions.androidx_core",
+ androidx_leanback_core : "androidx.leanback:leanback:$orbotversions.androidx_leanback",
+ androidx_leanback_paging : "androidx.leanback:leanback-paging:$orbotversions.androidx_leanback_paging",
+ androidx_leanback_preferences : "androidx.leanback:leanback-preference:$orbotversions.androidx_leanback",
+ androidx_leanback_tab : "androidx.leanback:leanback-tab:$orbotversions.androidx_leanback_tab",
+ androidx_localbroadcast : "androidx.localbroadcastmanager:localbroadcastmanager:$orbotversions.androidx_localbroadcast",
+ androidx_multidex : "androidx.multidex:multidex:$orbotversions.androidx_multidex",
+ androidx_palette : "androidx.palette:palette:$orbotversions.androidx_palette",
+ androidx_recyclerview : "androidx.recyclerview:recyclerview:$orbotversions.androidx_recyclerview",
+ apl_appintro : "com.github.apl-devs:appintro:$orbotversions.apl_appintro",
+ bclogic_pulsator : "pl.bclogic:pulsator4droid:$orbotversions.bclogic_pulsator",
+ fastlane_screengrab : "tools.fastlane:screengrab:$orbotversions.fastlane_screengrab",
+ guardian_geoip : "foundation.e:geoip:$orbotversions.orbot_service_dependencies",
+ guardian_jsocks : "com.gitlab.guardianproject:jsocksandroid:$orbotversions.guardian_jsocks",
+ guardian_jtorctl : "info.guardianproject:jtorctl:$orbotversions.guardian_jtorctl",
+ ipt_proxy : "foundation.e:OrbotIPtProxy:$orbotversions.orbot_service_dependencies",
+ portmapper : "com.offbynull.portmapper:portmapper:$orbotversions.portmapper",
+ tor_android : "foundation.e:tor-android:$orbotversions.orbot_service_dependencies",
+ pcap_core : "org.pcap4j:pcap4j-core:$orbotversions.pcap_core",
+ pcap_factory : "org.pcap4j:pcap4j-packetfactory-static:$orbotversions.pcap_factory"
]
}
diff --git a/ipscrambling/exportdependencies/publish.gradle b/ipscrambling/exportdependencies/publish.gradle
index 657ce22..3ff0f91 100644
--- a/ipscrambling/exportdependencies/publish.gradle
+++ b/ipscrambling/exportdependencies/publish.gradle
@@ -5,7 +5,7 @@ publishing {
maven(MavenPublication) {
groupId 'foundation.e'
artifactId 'orbotservice'
- version versions.orbot_service
+ version orbotversions.orbot_service
artifact "$buildDir/outputs/aar/orbotservice-release.aar"
pom.withXml {
@@ -25,7 +25,7 @@ publishing {
def ciApiV4Url = System.getenv("CI_API_V4_URL")
if (ciJobToken != null) {
maven {
- url "${ciApiV4Url}/projects/1063/packages/maven"
+ url "${ciApiV4Url}/projects/900/packages/maven"
credentials(HttpHeaderCredentials) {
name = 'Job-Token'
value = ciJobToken
@@ -36,7 +36,7 @@ publishing {
}
} else {
maven {
- url "https://gitlab.e.foundation/api/v4/projects/1063/packages/maven"
+ url "https://gitlab.e.foundation/api/v4/projects/900/packages/maven"
credentials(HttpHeaderCredentials) {
name = "Private-Token"
value = gitLabPrivateToken
diff --git a/ipscrambling/exportdependencies/update_dependencies.md b/ipscrambling/exportdependencies/update_dependencies.md
index 32ef58d..faf11f3 100644
--- a/ipscrambling/exportdependencies/update_dependencies.md
+++ b/ipscrambling/exportdependencies/update_dependencies.md
@@ -17,7 +17,7 @@ mvn install:install-file \
mvn deploy:deploy-file \
-DrepositoryId=gitlab-e-maven \
--Durl=https://gitlab.e.foundation/api/v4/projects/1063/packages/maven \
+-Durl=https://gitlab.e.foundation/api/v4/projects/900/packages/maven \
-DartifactId=[ARTIFACTID] \
-Dpackaging=aar \
-Dfile=[PATH]
@@ -37,7 +37,7 @@ step 4 :
mvn deploy:deploy-file \
-DrepositoryId=gitlab-e-maven \
--Durl=https://gitlab.e.foundation/api/v4/projects/1063/packages/maven \
+-Durl=https://gitlab.e.foundation/api/v4/projects/900/packages/maven \
-DartifactId=tor-android \
-Dpackaging=aar \
-Dfile="../../orbot/libs/tor-android-binary-release.aar
diff --git a/ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/KoinModule.kt b/ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/KoinModule.kt
index bfb9b32..79aeb05 100644
--- a/ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/KoinModule.kt
+++ b/ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/KoinModule.kt
@@ -21,5 +21,5 @@ import org.koin.core.module.dsl.singleOf
import org.koin.dsl.module
val ipScramblerModule = module {
- singleOf(::IpScramblerModule)
+ singleOf(::OrbotServiceSupervisor)
}
diff --git a/ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/IpScramblerModule.kt b/ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/OrbotServiceSupervisor.kt
index d1f01a0..8813948 100644
--- a/ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/IpScramblerModule.kt
+++ b/ipscrambling/src/main/java/foundation/e/advancedprivacy/ipscrambler/OrbotServiceSupervisor.kt
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2023 MURENA SAS
* Copyright (C) 2021 E FOUNDATION
*
* This program is free software: you can redistribute it and/or modify
@@ -27,28 +28,37 @@ import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Message
-import android.util.Log
import androidx.localbroadcastmanager.content.LocalBroadcastManager
+import foundation.e.advancedprivacy.domain.entities.FeatureServiceState
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import org.pcap4j.packet.DnsPacket
import org.torproject.android.service.OrbotConstants
import org.torproject.android.service.OrbotConstants.ACTION_STOP_FOREGROUND_TASK
import org.torproject.android.service.OrbotService
import org.torproject.android.service.util.Prefs
+import timber.log.Timber
import java.security.InvalidParameterException
+import java.util.function.Function
@SuppressLint("CommitPrefEdits")
-class IpScramblerModule(private val context: Context) {
- interface Listener {
- fun onStatusChanged(newStatus: Status)
- fun log(message: String)
- fun onTrafficUpdate(upload: Long, download: Long, read: Long, write: Long)
- }
+class OrbotServiceSupervisor(
+ private val context: Context,
+ private val coroutineScope: CoroutineScope,
+) {
+ private val _state = MutableStateFlow(FeatureServiceState.OFF)
+ val state: StateFlow<FeatureServiceState> = _state
enum class Status {
OFF, ON, STARTING, STOPPING, START_DISABLED
}
companion object {
- const val TAG = "IpScramblerModule"
-
private val EXIT_COUNTRY_CODES = setOf("DE", "AT", "SE", "CH", "IS", "CA", "US", "ES", "FR", "BG", "PL", "AU", "BR", "CZ", "DK", "FI", "GB", "HU", "NL", "JP", "RO", "RU", "SG", "SK")
// Key where exit country is stored by orbot service.
@@ -58,7 +68,6 @@ class IpScramblerModule(private val context: Context) {
}
private var currentStatus: Status? = null
- private val listeners = mutableSetOf<Listener>()
private val localBroadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
@@ -70,7 +79,7 @@ class IpScramblerModule(private val context: Context) {
currentStatus = newStatus
}
} catch (e: Exception) {
- Log.e(TAG, "Can't parse Orbot service status.")
+ Timber.e("Can't parse Orbot service status.")
}
return
}
@@ -87,18 +96,6 @@ class IpScramblerModule(private val context: Context) {
val action = msg.obj as? String ?: return
val data = msg.data
when (action) {
- OrbotConstants.LOCAL_ACTION_LOG ->
- data.getString(OrbotConstants.LOCAL_EXTRA_LOG)?.let { newLog(it) }
-
- OrbotConstants.LOCAL_ACTION_BANDWIDTH -> {
- trafficUpdate(
- data.getLong("up", 0),
- data.getLong("down", 0),
- data.getLong("written", 0),
- data.getLong("read", 0)
- )
- }
-
OrbotConstants.LOCAL_ACTION_PORTS -> {
httpProxyPort = data.getInt(OrbotService.EXTRA_HTTP_PROXY_PORT, -1)
socksProxyPort = data.getInt(OrbotService.EXTRA_SOCKS_PROXY_PORT, -1)
@@ -110,9 +107,11 @@ class IpScramblerModule(private val context: Context) {
val newStatus = Status.valueOf(it)
updateStatus(newStatus, force = true)
} catch (e: Exception) {
- Log.e(TAG, "Can't parse Orbot service status.")
+ Timber.e("Can't parse Orbot service status.")
}
}
+ OrbotConstants.LOCAL_ACTION_LOG,
+ OrbotConstants.LOCAL_ACTION_BANDWIDTH -> {} // Unused in Advanced Privacy
}
super.handleMessage(msg)
}
@@ -150,9 +149,24 @@ class IpScramblerModule(private val context: Context) {
private fun updateStatus(status: Status, force: Boolean = false) {
if (force || status != currentStatus) {
- currentStatus = status
- listeners.forEach {
- it.onStatusChanged(status)
+ val newState = when (status) {
+ Status.OFF -> FeatureServiceState.OFF
+ Status.ON -> FeatureServiceState.ON
+ Status.STARTING -> FeatureServiceState.STARTING
+ Status.STOPPING,
+ Status.START_DISABLED -> FeatureServiceState.STOPPING
+ }
+
+ coroutineScope.launch(Dispatchers.IO) {
+ _state.update { currentState ->
+ if (newState == FeatureServiceState.OFF &&
+ currentState == FeatureServiceState.STOPPING
+ ) {
+ // Wait for orbot to relax before allowing user to reactivate it.
+ delay(1000)
+ }
+ newState
+ }
}
}
}
@@ -165,14 +179,6 @@ class IpScramblerModule(private val context: Context) {
return currentStatus != Status.OFF
}
- private fun newLog(message: String) {
- listeners.forEach { it.log(message) }
- }
-
- private fun trafficUpdate(upload: Long, download: Long, read: Long, write: Long) {
- listeners.forEach { it.onTrafficUpdate(upload, download, read, write) }
- }
-
private fun sendIntentToService(action: String, extra: Bundle? = null) {
val intent = Intent(context, OrbotService::class.java)
intent.action = action
@@ -203,27 +209,29 @@ class IpScramblerModule(private val context: Context) {
}
@SuppressLint("ApplySharedPref")
- private fun setExitCountryCode(countryCode: String) {
- val countryParam = when {
- countryCode.isEmpty() -> ""
- countryCode in EXIT_COUNTRY_CODES -> "{$countryCode}"
- else -> throw InvalidParameterException(
- "Only these countries are available: ${EXIT_COUNTRY_CODES.joinToString { ", " } }"
- )
- }
+ suspend fun setExitCountryCode(countryCode: String) {
+ withContext(Dispatchers.IO) {
+ val countryParam = when {
+ countryCode.isEmpty() -> ""
+ countryCode in EXIT_COUNTRY_CODES -> "{$countryCode}"
+ else -> throw InvalidParameterException(
+ "Only these countries are available: ${EXIT_COUNTRY_CODES.joinToString { ", " }}"
+ )
+ }
- if (isServiceRunning()) {
- val extra = Bundle()
- extra.putString("exit", countryParam)
- sendIntentToService(OrbotConstants.CMD_SET_EXIT, extra)
- } else {
- Prefs.getSharedPrefs(context)
- .edit().putString(PREFS_KEY_EXIT_NODES, countryParam)
- .commit()
+ if (isServiceRunning()) {
+ val extra = Bundle()
+ extra.putString("exit", countryParam)
+ sendIntentToService(OrbotConstants.CMD_SET_EXIT, extra)
+ } else {
+ Prefs.getSharedPrefs(context)
+ .edit().putString(PREFS_KEY_EXIT_NODES, countryParam)
+ .commit()
+ }
}
}
- private fun getExitCountryCode(): String {
+ fun getExitCountryCode(): String {
val raw = Prefs.getExitNodes()
return if (raw.isEmpty()) raw else raw.slice(1..2)
}
@@ -232,6 +240,10 @@ class IpScramblerModule(private val context: Context) {
return VpnService.prepare(context)
}
+ fun setDNSFilter(shouldBlock: Function<DnsPacket?, DnsPacket?>?) {
+ OrbotService.shouldBlock = shouldBlock
+ }
+
fun start(enableNotification: Boolean) {
Prefs.enableNotification(enableNotification)
Prefs.putUseVpn(true)
@@ -242,6 +254,8 @@ class IpScramblerModule(private val context: Context) {
}
fun stop() {
+ if (!isServiceRunning()) return
+
updateStatus(Status.STOPPING)
Prefs.putUseVpn(false)
@@ -280,10 +294,6 @@ class IpScramblerModule(private val context: Context) {
get() = getTorifiedApps()
set(value) = saveTorifiedApps(value)
- var exitCountry: String
- get() = getExitCountryCode()
- set(value) = setExitCountryCode(value)
-
fun getAvailablesLocations(): Set<String> = EXIT_COUNTRY_CODES
var httpProxyPort: Int = -1
@@ -292,16 +302,6 @@ class IpScramblerModule(private val context: Context) {
var socksProxyPort: Int = -1
private set
- fun addListener(listener: Listener) {
- listeners.add(listener)
- }
- fun removeListener(listener: Listener) {
- listeners.remove(listener)
- }
- fun clearListeners() {
- listeners.clear()
- }
-
fun onCleared() {
LocalBroadcastManager.getInstance(context).unregisterReceiver(localBroadcastReceiver)
}
diff --git a/settings.gradle b/settings.gradle
index 39e58c8..a25c6a8 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -18,12 +18,14 @@ include ':trackers'
include ':permissionse'
include ':permissionse:libs:hidden-apis-stub'
include ':ipscrambling'
+include ':ipscrambling:orbotservice'
include ':trackersservicestandalone'
include ':trackersservicee'
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
+ mavenLocal()
google()
mavenCentral()
maven { url "https://jitpack.io" }
diff --git a/trackers/build.gradle b/trackers/build.gradle
index 737db5a..69faabd 100644
--- a/trackers/build.gradle
+++ b/trackers/build.gradle
@@ -49,6 +49,7 @@ dependencies {
libs.bundles.koin,
libs.bundles.kotlin.android.coroutines,
libs.google.gson,
+ libs.pcap4j,
libs.retrofit,
libs.retrofit.scalars,
diff --git a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/externalinterfaces/TrackersServiceSupervisor.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/externalinterfaces/TrackersServiceSupervisor.kt
index d9674fc..79f721b 100644
--- a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/externalinterfaces/TrackersServiceSupervisor.kt
+++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/externalinterfaces/TrackersServiceSupervisor.kt
@@ -16,8 +16,13 @@
*/
package foundation.e.advancedprivacy.trackers.domain.externalinterfaces
+import org.pcap4j.packet.DnsPacket
+import java.util.function.Function
+
interface TrackersServiceSupervisor {
fun start(): Boolean
fun stop(): Boolean
fun isRunning(): Boolean
+
+ val dnsFilterForIpScrambling: Function<DnsPacket?, DnsPacket?>?
}
diff --git a/trackersservicee/build.gradle b/trackersservicee/build.gradle
index f7725bf..e93d5d6 100644
--- a/trackersservicee/build.gradle
+++ b/trackersservicee/build.gradle
@@ -5,14 +5,11 @@ plugins {
android {
namespace 'foundation.e.advancedprivacy.trackers.service'
- compileSdk 33
+ compileSdkVersion buildConfig.compileSdk
defaultConfig {
- minSdk 24
- targetSdk 33
-
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- consumerProguardFiles "consumer-rules.pro"
+ minSdkVersion buildConfig.minSdk
+ targetSdkVersion buildConfig.targetSdk
}
buildTypes {
diff --git a/trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt b/trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt
index 3903db4..dcdf0d4 100644
--- a/trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt
+++ b/trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt
@@ -21,7 +21,9 @@ import android.content.Intent
import foundation.e.advancedprivacy.trackers.domain.externalinterfaces.TrackersServiceSupervisor
import foundation.e.advancedprivacy.trackers.service.TrackersService.Companion.ACTION_START
import kotlinx.coroutines.isActive
+import org.koin.core.module.dsl.bind
import org.koin.core.module.dsl.factoryOf
+import org.koin.core.module.dsl.singleOf
import org.koin.dsl.module
class TrackersServiceSupervisorImpl(private val context: Context) : TrackersServiceSupervisor {
@@ -39,8 +41,13 @@ class TrackersServiceSupervisorImpl(private val context: Context) : TrackersServ
override fun isRunning(): Boolean {
return TrackersService.coroutineScope.isActive
}
+
+ override val dnsFilterForIpScrambling = null
}
val trackerServiceModule = module {
factoryOf(::DNSBlocker)
+ singleOf(::TrackersServiceSupervisorImpl) {
+ bind<TrackersServiceSupervisor>()
+ }
}
diff --git a/trackersservicestandalone/build.gradle b/trackersservicestandalone/build.gradle
index d5df422..ead9dbd 100644
--- a/trackersservicestandalone/build.gradle
+++ b/trackersservicestandalone/build.gradle
@@ -4,14 +4,11 @@ plugins {
}
android {
- compileSdk 32
+ compileSdkVersion buildConfig.compileSdk
defaultConfig {
- minSdk 26
- targetSdk 32
-
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- consumerProguardFiles "consumer-rules.pro"
+ minSdkVersion buildConfig.minSdk
+ targetSdkVersion buildConfig.targetSdk
}
buildTypes {
diff --git a/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt
index 25d3e2d..e2a6692 100644
--- a/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt
+++ b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt
@@ -25,10 +25,16 @@ import foundation.e.advancedprivacy.trackers.service.data.RequestDNSRepository
import foundation.e.advancedprivacy.trackers.service.usecases.ResolveDNSUseCase
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableStateFlow
+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(private val context: Context) : TrackersServiceSupervisor {
+class TrackersServiceSupervisorImpl(
+ private val context: Context,
+ private val resolveDNSUseCase: ResolveDNSUseCase
+) : TrackersServiceSupervisor {
internal val state: MutableStateFlow<FeatureServiceState> = MutableStateFlow(FeatureServiceState.OFF)
override fun start(): Boolean {
@@ -54,6 +60,8 @@ class TrackersServiceSupervisorImpl(private val context: Context) : TrackersServ
override fun isRunning(): Boolean {
return state.value != FeatureServiceState.OFF
}
+
+ override val dnsFilterForIpScrambling = Function<DnsPacket?, DnsPacket?> { dnsRequest -> resolveDNSUseCase.shouldBlock(dnsRequest) }
}
val trackerServiceModule = module {
@@ -61,4 +69,5 @@ val trackerServiceModule = module {
singleOf(::RequestDNSRepository)
singleOf(::ResolveDNSUseCase)
singleOf(::TunLooper)
+ singleOf(::TrackersServiceSupervisorImpl) { bind<TrackersServiceSupervisor>() }
}
diff --git a/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/usecases/ResolveDNSUseCase.kt b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/usecases/ResolveDNSUseCase.kt
index ac8aee0..b74b9cb 100644
--- a/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/usecases/ResolveDNSUseCase.kt
+++ b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/usecases/ResolveDNSUseCase.kt
@@ -16,6 +16,7 @@
*/
package foundation.e.advancedprivacy.trackers.service.usecases
+import foundation.e.advancedprivacy.core.utils.runSuspendCatching
import foundation.e.advancedprivacy.trackers.domain.usecases.FilterHostnameUseCase
import foundation.e.advancedprivacy.trackers.service.data.NetworkDNSAddressRepository
import foundation.e.advancedprivacy.trackers.service.data.RequestDNSRepository
@@ -24,6 +25,7 @@ import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import org.pcap4j.packet.DnsPacket
import org.pcap4j.packet.namednumber.DnsRCode
+import timber.log.Timber
import java.net.DatagramPacket
@OptIn(DelicateCoroutinesApi::class)
@@ -39,16 +41,35 @@ class ResolveDNSUseCase(
filterHostnameUseCase.writeLogJob(scope)
}
+ fun shouldBlock(dnsRequest: DnsPacket?): DnsPacket? {
+ if (dnsRequest == null) return null
+
+ return runSuspendCatching {
+ val host = dnsRequest.header.questions[0].qName.name
+ if (filterHostnameUseCase.shouldBlock(host)) {
+ dnsRequest.builder
+ .rCode(DnsRCode.NX_DOMAIN)
+ .response(true).build()
+ } else null
+ }.onFailure {
+ Timber.e(it, "Can't find host")
+ }.getOrNull()
+ }
+
suspend fun processDNS(dnsRequest: DnsPacket): DnsPacket? {
- val host = dnsRequest.header.questions[0].qName.name
- if (filterHostnameUseCase.shouldBlock(host)) {
- return dnsRequest.builder
- .rCode(DnsRCode.NX_DOMAIN)
- .response(true).build()
+ val blockedDnsResponse = shouldBlock(dnsRequest)
+
+ if (blockedDnsResponse != null) {
+ return blockedDnsResponse
}
val payload = dnsRequest.rawData
- val packet = DatagramPacket(payload, payload.size, networkDNSAddressRepository.dnsAddress, DNS_PORT)
+ val packet = DatagramPacket(
+ payload,
+ payload.size,
+ networkDNSAddressRepository.dnsAddress,
+ DNS_PORT
+ )
return requestDNSRepository.processDNS(packet)
}
}