summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillaume Jacquart <guillaume.jacquart@hoodbrains.com>2023-10-11 16:36:02 +0000
committerGuillaume Jacquart <guillaume.jacquart@hoodbrains.com>2023-10-11 16:36:02 +0000
commit95e68cbbe748f81af1113753c5b99929e3db9ea2 (patch)
tree776b2e1fb3d5c16a48beb1c947eef887b2475db8
parent41c0e48498979ec8310961e40c89aa6b3c0d8e0f (diff)
epic18: Trackers control on standalone app (without Ipscrambling).
-rw-r--r--.gitignore16
-rw-r--r--.idea/.gitignore3
-rw-r--r--.idea/.name1
-rw-r--r--.idea/codeStyles/Project.xml167
-rw-r--r--.idea/codeStyles/codeStyleConfig.xml5
-rw-r--r--.idea/copyright/MURENA.xml6
-rw-r--r--.idea/copyright/profiles_settings.xml3
-rw-r--r--.idea/encodings.xml6
-rw-r--r--.idea/inspectionProfiles/ktlint.xml7
-rw-r--r--.idea/inspectionProfiles/profiles_settings.xml6
-rw-r--r--.idea/runConfigurations/Build_system_app.xml13
-rw-r--r--.idea/vcs.xml6
-rw-r--r--app/build.gradle3
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/AdvancedPrivacyApplication.kt9
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/DependencyContainer.kt0
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt27
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/Notifications.kt48
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/common/BuildFlavor.kt22
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt2
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt2
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/usecases/IpScramblingStateUseCase.kt3
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStateUseCase.kt6
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStatisticsUseCase.kt4
-rw-r--r--app/src/main/res/values/strings.xml5
-rw-r--r--build.gradle1
-rw-r--r--core/src/main/java/foundation/e/advancedprivacy/core/utils/NotificationsHelper.kt35
-rw-r--r--core/src/main/java/foundation/e/advancedprivacy/domain/entities/FeatureServiceState.kt21
-rw-r--r--core/src/main/java/foundation/e/advancedprivacy/domain/entities/NotificationChannels.kt27
-rw-r--r--core/src/main/java/foundation/e/advancedprivacy/domain/entities/NotificationContent.kt27
-rw-r--r--core/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModuleBase.kt (renamed from core/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/APermissionsPrivacyModule.kt)2
-rw-r--r--fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt4
-rw-r--r--gradle/libs.versions.toml6
-rw-r--r--permissionse/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModuleImpl.kt (renamed from permissionse/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModule.kt)4
-rw-r--r--permissionsstandalone/src/main/java/foundation/e/advancedprivacy/permissions/externalinterfaces/PermissionsPrivacyModuleImpl.kt (renamed from permissionsstandalone/src/main/java/foundation/e/advancedprivacy/permissions/externalinterfaces/PermissionsPrivacyModule.kt)4
-rw-r--r--settings.gradle3
-rw-r--r--trackers/src/main/AndroidManifest.xml17
-rw-r--r--trackers/src/main/java/foundation/e/advancedprivacy/trackers/KoinModule.kt18
-rw-r--r--trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/RemoteTrackersListRepository.kt2
-rw-r--r--trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/StatsDatabase.kt12
-rw-r--r--trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/TrackersRepository.kt2
-rw-r--r--trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/externalinterfaces/TrackersServiceSupervisor.kt23
-rw-r--r--trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/FilterHostnameUseCase.kt108
-rw-r--r--trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/StatisticsUseCase.kt12
-rw-r--r--trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/TrackersLogger.kt60
-rw-r--r--trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/UpdateTrackerListUseCase.kt2
-rw-r--r--trackersservicee/.gitignore1
-rw-r--r--trackersservicee/build.gradle43
-rw-r--r--trackersservicee/consumer-rules.pro0
-rw-r--r--trackersservicee/proguard-rules.pro21
-rw-r--r--trackersservicee/src/main/AndroidManifest.xml35
-rw-r--r--trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/DNSBlocker.kt (renamed from trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/DNSBlocker.kt)51
-rw-r--r--trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt (renamed from trackers/src/main/java/foundation/e/advancedprivacy/trackers/services/DNSBlockerService.kt)20
-rw-r--r--trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt46
-rw-r--r--trackersservicestandalone/.gitignore1
-rw-r--r--trackersservicestandalone/build.gradle43
-rw-r--r--trackersservicestandalone/consumer-rules.pro0
-rw-r--r--trackersservicestandalone/proguard-rules.pro21
-rw-r--r--trackersservicestandalone/src/main/AndroidManifest.xml39
-rw-r--r--trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/Config.kt44
-rw-r--r--trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt127
-rw-r--r--trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt64
-rw-r--r--trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TunLooper.kt167
-rw-r--r--trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/data/NetworkDNSAddressRepository.kt59
-rw-r--r--trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/data/RequestDNSRepository.kt48
-rw-r--r--trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/usecases/ResolveDNSUseCase.kt54
65 files changed, 1187 insertions, 457 deletions
diff --git a/.gitignore b/.gitignore
index 09d358c..7d06206 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,21 +6,7 @@ build/
local.properties
# IntelliJ .idea folder
-/.idea/caches
-/.idea/libraries
-/.idea/misc.xml
-/.idea/modules.xml
-/.idea/workspace.xml
-/.idea/navEditor.xml
-.idea/tasks.xml
-.idea/compiler.xml
-/.idea/assetWizardSettings.xml
-/.idea/jarRepositories.xml
-/.idea/google-java-format.xml
-/.idea/runConfigurations.xml
-/.idea/dbnavigator.xml
-/.idea/deploymentTargetDropDown.xml
-/.idea/kotlinc.xml
+.idea/
gradle.xml
markdown-*.xml
diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index 26d3352..0000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
diff --git a/.idea/.name b/.idea/.name
deleted file mode 100644
index 5f54997..0000000
--- a/.idea/.name
+++ /dev/null
@@ -1 +0,0 @@
-AdvancedPrivacy
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
deleted file mode 100644
index 97c1063..0000000
--- a/.idea/codeStyles/Project.xml
+++ /dev/null
@@ -1,167 +0,0 @@
-<component name="ProjectCodeStyleConfiguration">
- <code_scheme name="Project" version="173">
- <option name="FORMATTER_TAGS_ENABLED" value="true" />
- <AndroidXmlCodeStyleSettings>
- <option name="LAYOUT_SETTINGS">
- <value>
- <option name="INSERT_LINE_BREAK_AFTER_LAST_ATTRIBUTE" value="true" />
- </value>
- </option>
- <option name="MANIFEST_SETTINGS">
- <value>
- <option name="INSERT_LINE_BREAK_AFTER_LAST_ATTRIBUTE" value="true" />
- </value>
- </option>
- <option name="OTHER_SETTINGS">
- <value>
- <option name="INSERT_LINE_BREAK_AFTER_LAST_ATTRIBUTE" value="true" />
- </value>
- </option>
- </AndroidXmlCodeStyleSettings>
- <JetCodeStyleSettings>
- <option name="PACKAGES_TO_USE_STAR_IMPORTS">
- <value>
- <package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
- </value>
- </option>
- <option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
- <option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
- <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
- </JetCodeStyleSettings>
- <MarkdownNavigatorCodeStyleSettings>
- <option name="RIGHT_MARGIN" value="72" />
- </MarkdownNavigatorCodeStyleSettings>
- <codeStyleSettings language="Groovy">
- <indentOptions>
- <option name="CONTINUATION_INDENT_SIZE" value="4" />
- </indentOptions>
- </codeStyleSettings>
- <codeStyleSettings language="XML">
- <indentOptions>
- <option name="CONTINUATION_INDENT_SIZE" value="4" />
- </indentOptions>
- <arrangement>
- <rules>
- <section>
- <rule>
- <match>
- <AND>
- <NAME>xmlns:android</NAME>
- <XML_ATTRIBUTE />
- <XML_NAMESPACE>^$</XML_NAMESPACE>
- </AND>
- </match>
- </rule>
- </section>
- <section>
- <rule>
- <match>
- <AND>
- <NAME>xmlns:.*</NAME>
- <XML_ATTRIBUTE />
- <XML_NAMESPACE>^$</XML_NAMESPACE>
- </AND>
- </match>
- <order>BY_NAME</order>
- </rule>
- </section>
- <section>
- <rule>
- <match>
- <AND>
- <NAME>.*:id</NAME>
- <XML_ATTRIBUTE />
- <XML_NAMESPACE>http://schemas.android.com/apk/res/android\n</XML_NAMESPACE>
- </AND>
- </match>
- </rule>
- </section>
- <section>
- <rule>
- <match>
- <AND>
- <NAME>.*:name</NAME>
- <XML_ATTRIBUTE />
- <XML_NAMESPACE>http://schemas.android.com/apk/res/android\n</XML_NAMESPACE>
- </AND>
- </match>
- </rule>
- </section>
- <section>
- <rule>
- <match>
- <AND>
- <NAME>name</NAME>
- <XML_ATTRIBUTE />
- <XML_NAMESPACE>^$</XML_NAMESPACE>
- </AND>
- </match>
- </rule>
- </section>
- <section>
- <rule>
- <match>
- <AND>
- <NAME>style</NAME>
- <XML_ATTRIBUTE />
- <XML_NAMESPACE>^$</XML_NAMESPACE>
- </AND>
- </match>
- </rule>
- </section>
- <section>
- <rule>
- <match>
- <AND>
- <NAME>.*</NAME>
- <XML_ATTRIBUTE />
- <XML_NAMESPACE>^$</XML_NAMESPACE>
- </AND>
- </match>
- <order>BY_NAME</order>
- </rule>
- </section>
- <section>
- <rule>
- <match>
- <AND>
- <NAME>.*</NAME>
- <XML_ATTRIBUTE />
- <XML_NAMESPACE>http://schemas.android.com/apk/res/android\n</XML_NAMESPACE>
- </AND>
- </match>
- <order>ANDROID_ATTRIBUTE_ORDER</order>
- </rule>
- </section>
- <section>
- <rule>
- <match>
- <AND>
- <NAME>.*</NAME>
- <XML_ATTRIBUTE />
- <XML_NAMESPACE>.*</XML_NAMESPACE>
- </AND>
- </match>
- <order>BY_NAME</order>
- </rule>
- </section>
- </rules>
- </arrangement>
- </codeStyleSettings>
- <codeStyleSettings language="kotlin">
- <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
- <option name="RIGHT_MARGIN" value="100" />
- <option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" />
- <option name="LINE_COMMENT_ADD_SPACE" value="true" />
- <option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
- <option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
- <option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="0" />
- <option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
- <option name="FIELD_ANNOTATION_WRAP" value="1" />
- <option name="VARIABLE_ANNOTATION_WRAP" value="1" />
- <indentOptions>
- <option name="CONTINUATION_INDENT_SIZE" value="4" />
- </indentOptions>
- </codeStyleSettings>
- </code_scheme>
-</component> \ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
deleted file mode 100644
index 0f7bc51..0000000
--- a/.idea/codeStyles/codeStyleConfig.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<component name="ProjectCodeStyleConfiguration">
- <state>
- <option name="USE_PER_PROJECT_SETTINGS" value="true" />
- </state>
-</component>
diff --git a/.idea/copyright/MURENA.xml b/.idea/copyright/MURENA.xml
deleted file mode 100644
index 0c0ee95..0000000
--- a/.idea/copyright/MURENA.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<component name="CopyrightManager">
- <copyright>
- <option name="notice" value="Copyright (C) &amp;#36;today.year MURENA SAS&#10;&#10;This program is free software: you can redistribute it and/or modify&#10;it under the terms of the GNU General Public License as published by&#10;the Free Software Foundation, either version 3 of the License, or&#10;(at your option) any later version.&#10;&#10;This program is distributed in the hope that it will be useful,&#10;but WITHOUT ANY WARRANTY; without even the implied warranty of&#10;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the&#10;GNU General Public License for more details.&#10;&#10;You should have received a copy of the GNU General Public License&#10;along with this program. If not, see &lt;https://www.gnu.org/licenses/&gt;." />
- <option name="myName" value="MURENA" />
- </copyright>
-</component> \ No newline at end of file
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
deleted file mode 100644
index e9f8efd..0000000
--- a/.idea/copyright/profiles_settings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<component name="CopyrightManager">
- <settings default="E_Foundation" />
-</component> \ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
deleted file mode 100644
index 97626ba..0000000
--- a/.idea/encodings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
- <component name="Encoding">
- <file url="PROJECT" charset="UTF-8" />
- </component>
-</project> \ No newline at end of file
diff --git a/.idea/inspectionProfiles/ktlint.xml b/.idea/inspectionProfiles/ktlint.xml
deleted file mode 100644
index 7d04a74..0000000
--- a/.idea/inspectionProfiles/ktlint.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<component name="InspectionProjectProfileManager">
- <profile version="1.0">
- <option name="myName" value="ktlint" />
- <inspection_tool class="KotlinUnusedImport" enabled="true" level="ERROR" enabled_by_default="true" />
- <inspection_tool class="RedundantSemicolon" enabled="true" level="ERROR" enabled_by_default="true" />
- </profile>
-</component>
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
deleted file mode 100644
index 64580d1..0000000
--- a/.idea/inspectionProfiles/profiles_settings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<component name="InspectionProjectProfileManager">
- <settings>
- <option name="PROJECT_PROFILE" value="ktlint" />
- <version value="1.0" />
- </settings>
-</component>
diff --git a/.idea/runConfigurations/Build_system_app.xml b/.idea/runConfigurations/Build_system_app.xml
deleted file mode 100644
index 755d57a..0000000
--- a/.idea/runConfigurations/Build_system_app.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<component name="ProjectRunConfigurationManager">
- <configuration default="false" name="Build system app" type="ShConfigurationType">
- <option name="INDEPENDENT_SCRIPT_PATH" value="true" />
- <option name="SCRIPT_PATH" value="$PROJECT_DIR$/scripts/sign_and_push.sh" />
- <option name="SCRIPT_OPTIONS" value="" />
- <option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
- <option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
- <option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
- <option name="INTERPRETER_PATH" value="/bin/bash" />
- <option name="INTERPRETER_OPTIONS" value="" />
- <method v="2" />
- </configuration>
-</component> \ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 94a25f7..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
- <component name="VcsDirectoryMappings">
- <mapping directory="$PROJECT_DIR$" vcs="Git" />
- </component>
-</project> \ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index ef54d45..216b81a 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -97,6 +97,7 @@ android {
persistent: "false",
mainActivityIntentFilterCategory: "android.intent.category.LAUNCHER"
]
+
signingConfig signingConfigs.debug
}
}
@@ -152,6 +153,8 @@ dependencies {
implementation project(':trackers')
implementation project(':ipscrambling')
+ eImplementation project(':trackersservicee')
+ standaloneImplementation project(':trackersservicestandalone')
implementation (
libs.e.elib,
diff --git a/app/src/main/java/foundation/e/advancedprivacy/AdvancedPrivacyApplication.kt b/app/src/main/java/foundation/e/advancedprivacy/AdvancedPrivacyApplication.kt
index 0af2a0e..71fef00 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/AdvancedPrivacyApplication.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/AdvancedPrivacyApplication.kt
@@ -18,7 +18,6 @@
package foundation.e.advancedprivacy
import android.app.Application
-import android.content.Intent
import foundation.e.advancedprivacy.common.WarningDialog
import foundation.e.advancedprivacy.domain.usecases.FakeLocationStateUseCase
import foundation.e.advancedprivacy.domain.usecases.GetQuickPrivacyStateUseCase
@@ -27,7 +26,6 @@ import foundation.e.advancedprivacy.domain.usecases.ShowFeaturesWarningUseCase
import foundation.e.advancedprivacy.domain.usecases.TrackersStateUseCase
import foundation.e.advancedprivacy.domain.usecases.TrackersStatisticsUseCase
import foundation.e.advancedprivacy.externalinterfaces.permissions.IPermissionsPrivacyModule
-import foundation.e.advancedprivacy.trackers.services.DNSBlockerService
import foundation.e.advancedprivacy.trackers.services.UpdateTrackersWorker
import foundation.e.lib.telemetry.Telemetry
import kotlinx.coroutines.CoroutineScope
@@ -70,12 +68,7 @@ class AdvancedPrivacyApplication : Application() {
)
get<IpScramblingStateUseCase>(IpScramblingStateUseCase::class.java)
- get<FakeLocationStateUseCase>(FakeLocationStateUseCase::class.java)
get<TrackersStateUseCase>(TrackersStateUseCase::class.java)
-
- val intent = Intent(this, DNSBlockerService::class.java)
- intent.action = DNSBlockerService.ACTION_START
- intent.putExtra(DNSBlockerService.EXTRA_ENABLE_NOTIFICATION, false)
- startService(intent)
+ get<FakeLocationStateUseCase>(FakeLocationStateUseCase::class.java)
}
}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/DependencyContainer.kt b/app/src/main/java/foundation/e/advancedprivacy/DependencyContainer.kt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/app/src/main/java/foundation/e/advancedprivacy/DependencyContainer.kt
diff --git a/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt b/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt
index 3fbb636..20cefd5 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt
@@ -22,6 +22,8 @@ import android.os.Process
import foundation.e.advancedprivacy.core.coreModule
import foundation.e.advancedprivacy.data.repositories.LocalStateRepository
import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
+import foundation.e.advancedprivacy.domain.entities.CHANNEL_TRACKER_FLAG
+import foundation.e.advancedprivacy.domain.entities.NotificationContent
import foundation.e.advancedprivacy.domain.entities.ProfileType
import foundation.e.advancedprivacy.domain.usecases.AppListUseCase
import foundation.e.advancedprivacy.domain.usecases.FakeLocationStateUseCase
@@ -39,7 +41,10 @@ import foundation.e.advancedprivacy.features.location.FakeLocationViewModel
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.PermissionsPrivacyModule
+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
import org.koin.androidx.viewmodel.dsl.viewModel
@@ -49,7 +54,7 @@ import org.koin.core.qualifier.named
import org.koin.dsl.module
val appModule = module {
- includes(coreModule, trackersModule, fakelocationModule, ipScramblerModule)
+ includes(coreModule, trackersModule, fakelocationModule, ipScramblerModule, trackerServiceModule)
factory<Resources> { androidContext().resources }
single {
@@ -89,6 +94,16 @@ val appModule = module {
)
}
+ single<NotificationContent>(named("notificationTrackerFlag")) {
+ NotificationContent(
+ channelId = CHANNEL_TRACKER_FLAG,
+ icon = R.drawable.ic_e_app_logo,
+ title = R.string.notifications_tracker_title,
+ description = R.string.notifications_tracker_content,
+ pendingIntent = null
+ )
+ }
+
single { CityDataSource }
singleOf(::AppListUseCase)
@@ -120,7 +135,13 @@ val appModule = module {
singleOf(::TrackersStatisticsUseCase)
single<IPermissionsPrivacyModule> {
- PermissionsPrivacyModule(context = androidContext())
+ PermissionsPrivacyModuleImpl(context = androidContext())
+ }
+
+ single<TrackersServiceSupervisor> {
+ TrackersServiceSupervisorImpl(
+ context = androidContext(),
+ )
}
viewModel { parameters ->
diff --git a/app/src/main/java/foundation/e/advancedprivacy/Notifications.kt b/app/src/main/java/foundation/e/advancedprivacy/Notifications.kt
index cd85e9a..455b1a7 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/Notifications.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/Notifications.kt
@@ -20,13 +20,21 @@ package foundation.e.advancedprivacy
import android.app.NotificationChannel
import android.app.NotificationManager
-import android.app.PendingIntent
import android.content.Context
import androidx.annotation.StringRes
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
+import foundation.e.advancedprivacy.core.utils.notificationBuilder
+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.MainFeatures
+import foundation.e.advancedprivacy.domain.entities.NOTIFICATION_FAKE_LOCATION_FLAG
+import foundation.e.advancedprivacy.domain.entities.NOTIFICATION_FIRST_BOOT
+import foundation.e.advancedprivacy.domain.entities.NOTIFICATION_IPSCRAMBLING_FLAG
+import foundation.e.advancedprivacy.domain.entities.NotificationContent
import foundation.e.advancedprivacy.domain.usecases.GetQuickPrivacyStateUseCase
import foundation.e.advancedprivacy.externalinterfaces.permissions.IPermissionsPrivacyModule
import foundation.e.advancedprivacy.main.MainActivity
@@ -37,14 +45,6 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
object Notifications {
- const val CHANNEL_FIRST_BOOT = "first_boot_notification"
- const val CHANNEL_FAKE_LOCATION_FLAG = "fake_location_flag"
- const val CHANNEL_IPSCRAMBLING_FLAG = "ipscrambling_flag"
-
- const val NOTIFICATION_FIRST_BOOT = 1000
- const val NOTIFICATION_FAKE_LOCATION_FLAG = NOTIFICATION_FIRST_BOOT + 1
- const val NOTIFICATION_IPSCRAMBLING_FLAG = NOTIFICATION_FAKE_LOCATION_FLAG + 1
-
fun showFirstBootNotification(context: Context) {
createNotificationFirstBootChannel(context)
val notificationBuilder: NotificationCompat.Builder = notificationBuilder(
@@ -88,6 +88,14 @@ object Notifications {
channelDescription = R.string.notifications_ipscrambling_channel_description
)
+ createNotificationFlagChannel(
+ context = appContext,
+ permissionsPrivacyModule = permissionsPrivacyModule,
+ channelId = CHANNEL_TRACKER_FLAG,
+ channelName = R.string.notifications_tracker_channel_name,
+ channelDescription = R.string.notifications_ipscrambling_channel_description
+ )
+
getQuickPrivacyStateUseCase.isLocationHidden.onEach {
if (it) {
showFlagNotification(appContext, MainFeatures.FAKE_LOCATION)
@@ -183,26 +191,4 @@ object Notifications {
}
NotificationManagerCompat.from(context).cancel(id)
}
-
- private data class NotificationContent(
- val channelId: String,
- val icon: Int,
- val title: Int,
- val description: Int,
- val pendingIntent: PendingIntent?
- )
-
- private fun notificationBuilder(
- context: Context,
- content: NotificationContent
- ): NotificationCompat.Builder {
- val builder = NotificationCompat.Builder(context, content.channelId)
- .setSmallIcon(content.icon)
- .setPriority(NotificationCompat.PRIORITY_LOW)
- .setContentTitle(context.getString(content.title))
- .setStyle(NotificationCompat.BigTextStyle().bigText(context.getString(content.description)))
- content.pendingIntent?.let { builder.setContentIntent(it) }
-
- return builder
- }
}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/common/BuildFlavor.kt b/app/src/main/java/foundation/e/advancedprivacy/common/BuildFlavor.kt
new file mode 100644
index 0000000..ecb24ce
--- /dev/null
+++ b/app/src/main/java/foundation/e/advancedprivacy/common/BuildFlavor.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.common
+
+import foundation.e.advancedprivacy.BuildConfig
+
+const val isStandaloneBuild: Boolean = BuildConfig.FLAVOR == "standalone"
diff --git a/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt b/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt
index 80fc760..589aa74 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt
@@ -75,7 +75,7 @@ class WarningDialog : AppCompatActivity() {
val feature = try {
intent.getParcelableExtra<ShowFeaturesWarning>(PARAM_FEATURE)!!
} catch (e: Exception) {
- Timber.e("Missing mandatory activity parameter", e)
+ Timber.e(e, "Missing mandatory activity parameter")
finish()
return
}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt
index 30c8e6b..983ba71 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt
@@ -202,7 +202,7 @@ class FakeLocationStateUseCase(
lastKnownLocation?.let { localListener.onLocationChanged(it) }
} catch (se: SecurityException) {
- Timber.e("Missing permission", se)
+ Timber.e(se, "Missing permission")
}
}
}
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 27e7fe4..9c89329 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
@@ -19,6 +19,7 @@
package foundation.e.advancedprivacy.domain.usecases
import android.content.Intent
+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
@@ -190,7 +191,7 @@ class IpScramblingStateUseCase(
fun startIpScrambling() {
localStateRepository.internetPrivacyMode.value = HIDE_IP_LOADING
- ipScramblerModule.start(enableNotification = false) // change the false ?
+ ipScramblerModule.start(enableNotification = isStandaloneBuild)
}
private fun map(status: IpScramblerModule.Status): InternetPrivacyMode {
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStateUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStateUseCase.kt
index ed15a41..9b79dcc 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStateUseCase.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStateUseCase.kt
@@ -22,6 +22,7 @@ import foundation.e.advancedprivacy.data.repositories.LocalStateRepository
import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
import foundation.e.advancedprivacy.trackers.data.WhitelistRepository
import foundation.e.advancedprivacy.trackers.domain.entities.Tracker
+import foundation.e.advancedprivacy.trackers.domain.externalinterfaces.TrackersServiceSupervisor
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@@ -29,7 +30,8 @@ class TrackersStateUseCase(
private val whitelistRepository: WhitelistRepository,
private val localStateRepository: LocalStateRepository,
private val appListsRepository: AppListsRepository,
- coroutineScope: CoroutineScope
+ private val trackersServiceSupervisor: TrackersServiceSupervisor,
+ coroutineScope: CoroutineScope,
) {
init {
coroutineScope.launch {
@@ -38,6 +40,8 @@ class TrackersStateUseCase(
updateAllTrackersBlockedState()
}
}
+
+ trackersServiceSupervisor.start()
}
private fun updateAllTrackersBlockedState() {
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStatisticsUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStatisticsUseCase.kt
index b0c9f39..3d6ade0 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStatisticsUseCase.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStatisticsUseCase.kt
@@ -24,6 +24,7 @@ import foundation.e.advancedprivacy.data.repositories.AppListsRepository
import foundation.e.advancedprivacy.domain.entities.AppWithCounts
import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
import foundation.e.advancedprivacy.domain.entities.TrackersPeriodicStatistics
+import foundation.e.advancedprivacy.trackers.data.StatsDatabase
import foundation.e.advancedprivacy.trackers.data.TrackersRepository
import foundation.e.advancedprivacy.trackers.data.WhitelistRepository
import foundation.e.advancedprivacy.trackers.domain.entities.Tracker
@@ -44,6 +45,7 @@ class TrackersStatisticsUseCase(
private val whitelistRepository: WhitelistRepository,
private val trackersRepository: TrackersRepository,
private val appListsRepository: AppListsRepository,
+ private val statsDatabase: StatsDatabase,
private val resources: Resources
) {
fun initAppList() {
@@ -52,7 +54,7 @@ class TrackersStatisticsUseCase(
@OptIn(FlowPreview::class)
fun listenUpdates(debounce: Duration = 1.seconds) =
- statisticsUseCase.newDataAvailable
+ statsDatabase.newDataAvailable
.throttleFirst(windowDuration = debounce)
.onStart { emit(Unit) }
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index fac1f75..ba3ba03 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -144,4 +144,9 @@
<string name="notifications_ipscrambling_title">Real IP hidden</string>
<string name="notifications_ipscrambling_content">This could impact the functioning of some applications.</string>
+ <string name="notifications_tracker_channel_name">Tracker control flag</string>
+ <string name="notifications_tracker_channel_description">Highlight that the trackers are actually logged and blocked by Advanced Privacy</string>
+ <string name="notifications_tracker_title">Tracker control is on</string>
+ <string name="notifications_tracker_content">This could impact the functioning of some applications.</string>
+
</resources>
diff --git a/build.gradle b/build.gradle
index a57ee21..cd192ee 100644
--- a/build.gradle
+++ b/build.gradle
@@ -52,6 +52,7 @@ plugins {
alias libs.plugins.android.application apply false
alias libs.plugins.kotlin.kapt apply false
alias libs.plugins.androidx.navigation.safeargs apply false
+ alias libs.plugins.android.library apply false
}
allprojects {
diff --git a/core/src/main/java/foundation/e/advancedprivacy/core/utils/NotificationsHelper.kt b/core/src/main/java/foundation/e/advancedprivacy/core/utils/NotificationsHelper.kt
new file mode 100644
index 0000000..29721b0
--- /dev/null
+++ b/core/src/main/java/foundation/e/advancedprivacy/core/utils/NotificationsHelper.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.core.utils
+
+import android.content.Context
+import androidx.core.app.NotificationCompat
+import foundation.e.advancedprivacy.domain.entities.NotificationContent
+
+fun notificationBuilder(
+ context: Context,
+ content: NotificationContent
+): NotificationCompat.Builder {
+ val builder = NotificationCompat.Builder(context, content.channelId)
+ .setSmallIcon(content.icon)
+ .setPriority(NotificationCompat.PRIORITY_LOW)
+ .setContentTitle(context.getString(content.title))
+ .setStyle(NotificationCompat.BigTextStyle().bigText(context.getString(content.description)))
+ content.pendingIntent?.let { builder.setContentIntent(it) }
+
+ return builder
+}
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
new file mode 100644
index 0000000..6bfecbb
--- /dev/null
+++ b/core/src/main/java/foundation/e/advancedprivacy/domain/entities/FeatureServiceState.kt
@@ -0,0 +1,21 @@
+/*
+ * 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.domain.entities
+
+enum class FeatureServiceState {
+ OFF, ON, STARTING, STOPPING
+}
diff --git a/core/src/main/java/foundation/e/advancedprivacy/domain/entities/NotificationChannels.kt b/core/src/main/java/foundation/e/advancedprivacy/domain/entities/NotificationChannels.kt
new file mode 100644
index 0000000..4458e1d
--- /dev/null
+++ b/core/src/main/java/foundation/e/advancedprivacy/domain/entities/NotificationChannels.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.domain.entities
+
+const val CHANNEL_FIRST_BOOT = "first_boot_notification"
+const val CHANNEL_FAKE_LOCATION_FLAG = "fake_location_flag"
+const val CHANNEL_IPSCRAMBLING_FLAG = "ipscrambling_flag"
+const val CHANNEL_TRACKER_FLAG = "tracker_flag"
+
+const val NOTIFICATION_FIRST_BOOT = 1000
+const val NOTIFICATION_FAKE_LOCATION_FLAG = NOTIFICATION_FIRST_BOOT + 1
+const val NOTIFICATION_IPSCRAMBLING_FLAG = NOTIFICATION_FAKE_LOCATION_FLAG + 1
+const val NOTIFICATION_TRACKER_FLAG = NOTIFICATION_IPSCRAMBLING_FLAG + 1
diff --git a/core/src/main/java/foundation/e/advancedprivacy/domain/entities/NotificationContent.kt b/core/src/main/java/foundation/e/advancedprivacy/domain/entities/NotificationContent.kt
new file mode 100644
index 0000000..44b508d
--- /dev/null
+++ b/core/src/main/java/foundation/e/advancedprivacy/domain/entities/NotificationContent.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.domain.entities
+
+import android.app.PendingIntent
+
+data class NotificationContent(
+ val channelId: String,
+ val icon: Int,
+ val title: Int,
+ val description: Int,
+ val pendingIntent: PendingIntent?
+)
diff --git a/core/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/APermissionsPrivacyModule.kt b/core/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModuleBase.kt
index 78f424b..27ba17f 100644
--- a/core/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/APermissionsPrivacyModule.kt
+++ b/core/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModuleBase.kt
@@ -37,7 +37,7 @@ import foundation.e.advancedprivacy.domain.entities.ProfileType
* versions of the module.
* @param context an Android context, to retrieve packageManager for example.
*/
-abstract class APermissionsPrivacyModule(protected val context: Context) : IPermissionsPrivacyModule {
+abstract class PermissionsPrivacyModuleBase(protected val context: Context) : IPermissionsPrivacyModule {
companion object {
private const val TAG = "PermissionsModule"
diff --git a/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt b/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt
index f2e10a4..c105ceb 100644
--- a/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt
+++ b/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt
@@ -37,7 +37,7 @@ import foundation.e.advancedprivacy.domain.entities.AppOpModes
import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
import foundation.e.advancedprivacy.domain.entities.ProfileType
import foundation.e.advancedprivacy.fakelocation.domain.usecases.FakeLocationModule
-import foundation.e.advancedprivacy.permissions.externalinterfaces.PermissionsPrivacyModule
+import foundation.e.advancedprivacy.permissions.externalinterfaces.PermissionsPrivacyModuleImpl
import foundation.e.privacymodules.fakelocationdemo.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
@@ -46,7 +46,7 @@ class MainActivity : AppCompatActivity() {
}
private val fakeLocationModule: FakeLocationModule by lazy { FakeLocationModule(this) }
- private val permissionsModule by lazy { PermissionsPrivacyModule(this) }
+ private val permissionsModule by lazy { PermissionsPrivacyModuleImpl(this) }
private lateinit var binding: ActivityMainBinding
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index b9925d4..00bf753 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -9,6 +9,7 @@ androidx-lifecycle = "2.5.0"
androidx-room = "2.3.0"
orbotservice = "orbot-16.6.3-1"
retrofit = "2.9.0"
+pcap4j = "1.8.2"
[libraries]
@@ -42,6 +43,8 @@ leakcanary = { group = "com.squareup.leakcanary", name = "leakcanary-android", v
maplibre = { group = "org.maplibre.gl", name = "android-sdk", version = "10.2.0" }
mockk = { group = "io.mockk", name = "mockk", version = "1.10.5" }
mpandroidcharts = { group = "com.github.PhilJay", name = "MPAndroidChart", version = "v3.1.0" }
+pcap4j = { group = "org.pcap4j", name = "pcap4j-core", version.ref = "pcap4j" }
+pcap4j-packetfactory-static = { group = "org.pcap4j", name = "pcap4j-packetfactory-static", version.ref = "pcap4j" }
retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
retrofit-scalars = { group = "com.squareup.retrofit2", name = "converter-scalars", version.ref = "retrofit" }
timber = { group = "com.jakewharton.timber", name = "timber", version = "5.0.1" }
@@ -50,10 +53,11 @@ timber = { group = "com.jakewharton.timber", name = "timber", version = "5.0.1"
koin = ["koin-core", "koin-android"]
kotlin-android-coroutines = ["androidx-core-ktx", "kotlinx-coroutines"]
-
+pcap4j = ["pcap4j", "pcap4j-packetfactory-static"]
[plugins]
android-application = { id = "com.android.application", version = "7.2.1" }
+android-library = { id = "com.android.library", version = "7.2.1" }
androidx-navigation-safeargs = { id = "androidx.navigation.safeargs.kotlin", version.ref = "androidx-navigation" }
benmanes-versions = { id = "com.github.ben-manes.versions", version = "0.38.0" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
diff --git a/permissionse/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModule.kt b/permissionse/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModuleImpl.kt
index 59a20dd..0d32bce 100644
--- a/permissionse/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModule.kt
+++ b/permissionse/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModuleImpl.kt
@@ -39,12 +39,12 @@ import foundation.e.advancedprivacy.domain.entities.AppOpModes
import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
import foundation.e.advancedprivacy.domain.entities.ProfileType.MAIN
import foundation.e.advancedprivacy.domain.entities.ProfileType.WORK
-import foundation.e.advancedprivacy.externalinterfaces.permissions.APermissionsPrivacyModule
+import foundation.e.advancedprivacy.externalinterfaces.permissions.PermissionsPrivacyModuleBase
/**
* Implements [IPermissionsPrivacyModule] with all privileges of a system app.
*/
-class PermissionsPrivacyModule(context: Context) : APermissionsPrivacyModule(context) {
+class PermissionsPrivacyModuleImpl(context: Context) : PermissionsPrivacyModuleBase(context) {
private val appOpsManager: AppOpsManager
get() = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
diff --git a/permissionsstandalone/src/main/java/foundation/e/advancedprivacy/permissions/externalinterfaces/PermissionsPrivacyModule.kt b/permissionsstandalone/src/main/java/foundation/e/advancedprivacy/permissions/externalinterfaces/PermissionsPrivacyModuleImpl.kt
index 95f5ff0..d31bdf4 100644
--- a/permissionsstandalone/src/main/java/foundation/e/advancedprivacy/permissions/externalinterfaces/PermissionsPrivacyModule.kt
+++ b/permissionsstandalone/src/main/java/foundation/e/advancedprivacy/permissions/externalinterfaces/PermissionsPrivacyModuleImpl.kt
@@ -24,12 +24,12 @@ import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import foundation.e.advancedprivacy.domain.entities.AppOpModes
import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
-import foundation.e.advancedprivacy.externalinterfaces.permissions.APermissionsPrivacyModule
+import foundation.e.advancedprivacy.externalinterfaces.permissions.PermissionsPrivacyModuleBase
/**
* Implements [IPermissionsPrivacyModule] using only API authorized on the PlayStore.
*/
-class PermissionsPrivacyModule(context: Context) : APermissionsPrivacyModule(context) {
+class PermissionsPrivacyModuleImpl(context: Context) : PermissionsPrivacyModuleBase(context) {
override fun getApplications(
filter: ((PackageInfo) -> Boolean)?
): List<ApplicationDescription> {
diff --git a/settings.gradle b/settings.gradle
index 0b4940e..39e58c8 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -18,7 +18,8 @@ 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)
diff --git a/trackers/src/main/AndroidManifest.xml b/trackers/src/main/AndroidManifest.xml
index 615d310..b980706 100644
--- a/trackers/src/main/AndroidManifest.xml
+++ b/trackers/src/main/AndroidManifest.xml
@@ -18,21 +18,4 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="foundation.e.advancedprivacy.trackers">
-
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
- <uses-permission android:name="android.permission.WAKE_LOCK" />
- <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
-
-
- <application>
- <service
- android:name="foundation.e.advancedprivacy.trackers.services.DNSBlockerService"
- android:enabled="true"
- android:exported="true" />
- </application>
-
</manifest> \ No newline at end of file
diff --git a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/KoinModule.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/KoinModule.kt
index 0cfb69c..34b4e7a 100644
--- a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/KoinModule.kt
+++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/KoinModule.kt
@@ -21,13 +21,13 @@ import foundation.e.advancedprivacy.data.repositories.RemoteTrackersListReposito
import foundation.e.advancedprivacy.trackers.data.StatsDatabase
import foundation.e.advancedprivacy.trackers.data.TrackersRepository
import foundation.e.advancedprivacy.trackers.data.WhitelistRepository
-import foundation.e.advancedprivacy.trackers.domain.usecases.DNSBlocker
+import foundation.e.advancedprivacy.trackers.domain.usecases.FilterHostnameUseCase
import foundation.e.advancedprivacy.trackers.domain.usecases.StatisticsUseCase
-import foundation.e.advancedprivacy.trackers.domain.usecases.TrackersLogger
import foundation.e.advancedprivacy.trackers.domain.usecases.UpdateTrackerListUseCase
import org.koin.android.ext.koin.androidContext
import org.koin.core.module.dsl.factoryOf
import org.koin.core.module.dsl.singleOf
+import org.koin.core.qualifier.named
import org.koin.dsl.module
val trackersModule = module {
@@ -58,15 +58,13 @@ val trackersModule = module {
}
factory {
- DNSBlocker(
- context = androidContext(),
- trackersLogger = get(),
+ FilterHostnameUseCase(
trackersRepository = get(),
- whitelistRepository = get()
+ whitelistRepository = get(),
+ appDesc = get(named("AdvancedPrivacy")),
+ context = androidContext(),
+ database = get(),
+ appListsRepository = get()
)
}
-
- factory {
- TrackersLogger(statisticsUseCase = get())
- }
}
diff --git a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/RemoteTrackersListRepository.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/RemoteTrackersListRepository.kt
index c2c0768..64477b7 100644
--- a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/RemoteTrackersListRepository.kt
+++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/RemoteTrackersListRepository.kt
@@ -39,7 +39,7 @@ class RemoteTrackersListRepository {
}
return true
} catch (e: IOException) {
- Timber.e("While saving tracker file.", e)
+ Timber.e(e, "While saving tracker file.")
}
return false
}
diff --git a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/StatsDatabase.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/StatsDatabase.kt
index 6aa76cf..15ff813 100644
--- a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/StatsDatabase.kt
+++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/StatsDatabase.kt
@@ -32,6 +32,10 @@ import foundation.e.advancedprivacy.trackers.data.StatsDatabase.AppTrackerEntry.
import foundation.e.advancedprivacy.trackers.data.StatsDatabase.AppTrackerEntry.COLUMN_NAME_TRACKER
import foundation.e.advancedprivacy.trackers.data.StatsDatabase.AppTrackerEntry.TABLE_NAME
import foundation.e.advancedprivacy.trackers.domain.entities.Tracker
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.withContext
import timber.log.Timber
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
@@ -86,6 +90,9 @@ class StatsDatabase(
COLUMN_NAME_APPID
)
+ private val _newDataAvailable = MutableSharedFlow<Unit>()
+ val newDataAvailable: SharedFlow<Unit> = _newDataAvailable
+
private val lock = Any()
override fun onCreate(db: SQLiteDatabase) {
@@ -316,7 +323,9 @@ class StatsDatabase(
}
}
- fun logAccess(trackerId: String?, appId: String, blocked: Boolean) {
+ suspend fun logAccess(trackerId: String?, appId: String, blocked: Boolean) = withContext(
+ Dispatchers.IO
+ ) {
synchronized(lock) {
val currentHour = getCurrentHourTs()
val db = writableDatabase
@@ -364,6 +373,7 @@ class StatsDatabase(
cursor.close()
db.close()
}
+ _newDataAvailable.emit(Unit)
}
private fun cursorToEntry(cursor: Cursor): StatEntry {
diff --git a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/TrackersRepository.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/TrackersRepository.kt
index a7d5e49..fc57a8e 100644
--- a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/TrackersRepository.kt
+++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/TrackersRepository.kt
@@ -59,7 +59,7 @@ class TrackersRepository(
reader.close()
inputStream.close()
} catch (e: Exception) {
- Timber.e("While parsing trackers in assets", e)
+ Timber.e(e, "While parsing trackers in assets")
}
}
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
new file mode 100644
index 0000000..d9674fc
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/externalinterfaces/TrackersServiceSupervisor.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.domain.externalinterfaces
+
+interface TrackersServiceSupervisor {
+ fun start(): Boolean
+ fun stop(): Boolean
+ fun isRunning(): Boolean
+}
diff --git a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/FilterHostnameUseCase.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/FilterHostnameUseCase.kt
new file mode 100644
index 0000000..e229cab
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/FilterHostnameUseCase.kt
@@ -0,0 +1,108 @@
+/*
+ * 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.domain.usecases
+
+import android.content.Context
+import android.content.pm.PackageManager
+import foundation.e.advancedprivacy.core.utils.runSuspendCatching
+import foundation.e.advancedprivacy.data.repositories.AppListsRepository
+import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
+import foundation.e.advancedprivacy.trackers.data.StatsDatabase
+import foundation.e.advancedprivacy.trackers.data.TrackersRepository
+import foundation.e.advancedprivacy.trackers.data.WhitelistRepository
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
+import timber.log.Timber
+import java.util.concurrent.LinkedBlockingQueue
+
+class FilterHostnameUseCase(
+ private val trackersRepository: TrackersRepository,
+ private val whitelistRepository: WhitelistRepository,
+ context: Context,
+ private val appDesc: ApplicationDescription,
+ private val database: StatsDatabase,
+ private val appListsRepository: AppListsRepository,
+) {
+ private var eBrowserAppUid = -1
+
+ companion object {
+ private const val E_BROWSER_DOT_SERVER = "chrome.cloudflare-dns.com"
+ }
+
+ init {
+ initEBrowserDoTFix(context)
+ }
+
+ fun shouldBlock(hostname: String, appUid: Int = appDesc.uid): Boolean {
+ var isBlocked = false
+
+ if (isEBrowserDoTBlockFix(appUid, hostname)) {
+ isBlocked = true
+ } else if (trackersRepository.isTracker(hostname)) {
+ val trackerId = trackersRepository.getTrackerId(hostname)
+ if (shouldBlock(appUid, trackerId)) {
+ isBlocked = true
+ }
+ queue.offer(DetectedTracker(trackerId, appUid, isBlocked))
+ }
+ return isBlocked
+ }
+
+ private fun initEBrowserDoTFix(context: Context) {
+ try {
+ eBrowserAppUid =
+ context.packageManager.getApplicationInfo("foundation.e.browser", 0).uid
+ } catch (e: PackageManager.NameNotFoundException) {
+ Timber.i("no E Browser package found.")
+ }
+ }
+
+ private fun isEBrowserDoTBlockFix(appUid: Int, hostname: String): Boolean {
+ return appUid == eBrowserAppUid &&
+ E_BROWSER_DOT_SERVER == hostname
+ }
+
+ private fun shouldBlock(appUid: Int, trackerId: String?): Boolean {
+ return whitelistRepository.isBlockingEnabled &&
+ !whitelistRepository.isWhiteListed(appUid, trackerId)
+ }
+
+ private val queue = LinkedBlockingQueue<DetectedTracker>()
+
+ private suspend fun logAccess(detectedTracker: DetectedTracker) {
+ appListsRepository.getApp(detectedTracker.appUid)?.let { app ->
+ database.logAccess(detectedTracker.trackerId, app.apId, detectedTracker.wasBlocked)
+ }
+ }
+
+ fun writeLogJob(scope: CoroutineScope): Job {
+ return scope.launch(Dispatchers.IO) {
+ while (isActive) {
+ runSuspendCatching {
+ logAccess(queue.take())
+ }.onFailure {
+ Timber.e(it, "writeLogLoop detectedTrackersQueue.take() interrupted: ")
+ }
+ }
+ }
+ }
+
+ inner class DetectedTracker(var trackerId: String?, var appUid: Int, var wasBlocked: Boolean)
+}
diff --git a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/StatisticsUseCase.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/StatisticsUseCase.kt
index 55efeb9..e7a84b8 100644
--- a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/StatisticsUseCase.kt
+++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/StatisticsUseCase.kt
@@ -22,24 +22,12 @@ import foundation.e.advancedprivacy.data.repositories.AppListsRepository
import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
import foundation.e.advancedprivacy.trackers.data.StatsDatabase
import foundation.e.advancedprivacy.trackers.domain.entities.Tracker
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.SharedFlow
import java.time.temporal.TemporalUnit
class StatisticsUseCase(
private val database: StatsDatabase,
private val appListsRepository: AppListsRepository
) {
- private val _newDataAvailable = MutableSharedFlow<Unit>()
- val newDataAvailable: SharedFlow<Unit> = _newDataAvailable
-
- suspend fun logAccess(trackerId: String?, appUid: Int, blocked: Boolean) {
- appListsRepository.getApp(appUid)?.let { app ->
- database.logAccess(trackerId, app.apId, blocked)
- _newDataAvailable.emit(Unit)
- }
- }
-
fun getTrackersCallsOnPeriod(
periodsCount: Int,
periodUnit: TemporalUnit
diff --git a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/TrackersLogger.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/TrackersLogger.kt
deleted file mode 100644
index 411b4ab..0000000
--- a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/TrackersLogger.kt
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2023 MURENA SAS
- * Copyright (C) 2022 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.trackers.domain.usecases
-
-import foundation.e.advancedprivacy.core.utils.runSuspendCatching
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.isActive
-import kotlinx.coroutines.launch
-import timber.log.Timber
-import java.util.concurrent.LinkedBlockingQueue
-
-class TrackersLogger(
- private val statisticsUseCase: StatisticsUseCase,
-) {
- private val queue = LinkedBlockingQueue<DetectedTracker>()
-
- fun logAccess(trackerId: String?, appUid: Int, wasBlocked: Boolean) {
- queue.offer(DetectedTracker(trackerId, appUid, wasBlocked))
- }
-
- fun writeLogJob(scope: CoroutineScope): Job {
- return scope.launch(Dispatchers.IO) {
- while (isActive) {
- runSuspendCatching {
- logAccess(queue.take())
- }.onFailure {
- Timber.e(it, "writeLogLoop detectedTrackersQueue.take() interrupted: ")
- }
- }
- }
- }
-
- private suspend fun logAccess(detectedTracker: DetectedTracker) {
- statisticsUseCase.logAccess(
- detectedTracker.trackerId,
- detectedTracker.appUid,
- detectedTracker.wasBlocked
- )
- }
-
- inner class DetectedTracker(var trackerId: String?, var appUid: Int, var wasBlocked: Boolean)
-}
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 55da644..fa60431 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
@@ -40,7 +40,7 @@ class UpdateTrackerListUseCase(
remoteTrackersListRepository.saveData(trackersRepository.eTrackerFile, api.trackers())
trackersRepository.initTrackersFile()
} catch (e: Exception) {
- Timber.e("While updating trackers", e)
+ Timber.e(e, "While updating trackers")
}
}
}
diff --git a/trackersservicee/.gitignore b/trackersservicee/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/trackersservicee/.gitignore
@@ -0,0 +1 @@
+/build \ No newline at end of file
diff --git a/trackersservicee/build.gradle b/trackersservicee/build.gradle
new file mode 100644
index 0000000..f7725bf
--- /dev/null
+++ b/trackersservicee/build.gradle
@@ -0,0 +1,43 @@
+plugins {
+ id 'com.android.library'
+ id 'org.jetbrains.kotlin.android'
+}
+
+android {
+ namespace 'foundation.e.advancedprivacy.trackers.service'
+ compileSdk 33
+
+ defaultConfig {
+ minSdk 24
+ targetSdk 33
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles "consumer-rules.pro"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+}
+
+dependencies {
+ implementation project(":core")
+ implementation project(":trackers")
+
+ implementation(
+ libs.androidx.core.ktx,
+ libs.bundles.koin,
+ libs.kotlinx.coroutines,
+ libs.timber,
+ )
+}
diff --git a/trackersservicee/consumer-rules.pro b/trackersservicee/consumer-rules.pro
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/trackersservicee/consumer-rules.pro
diff --git a/trackersservicee/proguard-rules.pro b/trackersservicee/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/trackersservicee/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile \ No newline at end of file
diff --git a/trackersservicee/src/main/AndroidManifest.xml b/trackersservicee/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..2290432
--- /dev/null
+++ b/trackersservicee/src/main/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2023 MURENA SAS
+ Copyright (C) 2022 ECORP
+
+ 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/>.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="foundation.e.advancedprivacy.trackers.service">
+
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+
+
+ <application>
+ <service
+ android:name=".TrackersService"
+ android:enabled="true"
+ android:exported="true" />
+ </application>
+</manifest> \ No newline at end of file
diff --git a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/DNSBlocker.kt b/trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/DNSBlocker.kt
index fb08910..6a2b218 100644
--- a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/domain/usecases/DNSBlocker.kt
+++ b/trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/DNSBlocker.kt
@@ -16,17 +16,14 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-package foundation.e.advancedprivacy.trackers.domain.usecases
+package foundation.e.advancedprivacy.trackers.service
-import android.content.Context
-import android.content.pm.PackageManager
import android.net.LocalServerSocket
import android.system.ErrnoException
import android.system.Os
import android.system.OsConstants
import foundation.e.advancedprivacy.core.utils.runSuspendCatching
-import foundation.e.advancedprivacy.trackers.data.TrackersRepository
-import foundation.e.advancedprivacy.trackers.data.WhitelistRepository
+import foundation.e.advancedprivacy.trackers.domain.usecases.FilterHostnameUseCase
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -39,21 +36,12 @@ import java.io.InputStreamReader
import java.io.PrintWriter
class DNSBlocker(
- context: Context,
- val trackersLogger: TrackersLogger,
- private val trackersRepository: TrackersRepository,
- private val whitelistRepository: WhitelistRepository
+ val filterHostnameUseCase: FilterHostnameUseCase
) {
private var resolverReceiver: LocalServerSocket? = null
- private var eBrowserAppUid = -1
companion object {
private const val SOCKET_NAME = "foundation.e.advancedprivacy"
- private const val E_BROWSER_DOT_SERVER = "chrome.cloudflare-dns.com"
- }
-
- init {
- initEBrowserDoTFix(context)
}
private fun closeSocket() {
@@ -97,18 +85,9 @@ class DNSBlocker(
val writer = PrintWriter(output, true)
val domainName = params[0]
val appUid = params[1].toInt()
- var isBlocked = false
- if (isEBrowserDoTBlockFix(appUid, domainName)) {
- isBlocked = true
- } else if (trackersRepository.isTracker(domainName)) {
- val trackerId = trackersRepository.getTrackerId(domainName)
- if (shouldBlock(appUid, trackerId)) {
- writer.println("block")
- isBlocked = true
- }
- trackersLogger.logAccess(trackerId, appUid, isBlocked)
- }
- if (!isBlocked) {
+ if (filterHostnameUseCase.shouldBlock(domainName, appUid)) {
+ writer.println("block")
+ } else {
writer.println("pass")
}
socket.close()
@@ -122,22 +101,4 @@ class DNSBlocker(
}
}
}
-
- private fun initEBrowserDoTFix(context: Context) {
- try {
- eBrowserAppUid =
- context.packageManager.getApplicationInfo("foundation.e.browser", 0).uid
- } catch (e: PackageManager.NameNotFoundException) {
- Timber.i(e, "no E Browser package found.")
- }
- }
-
- private fun isEBrowserDoTBlockFix(appUid: Int, hostname: String): Boolean {
- return appUid == eBrowserAppUid && E_BROWSER_DOT_SERVER == hostname
- }
-
- private fun shouldBlock(appUid: Int, trackerId: String?): Boolean {
- return whitelistRepository.isBlockingEnabled &&
- !whitelistRepository.isWhiteListed(appUid, trackerId)
- }
}
diff --git a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/services/DNSBlockerService.kt b/trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt
index 25539e1..5f573b0 100644
--- a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/services/DNSBlockerService.kt
+++ b/trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt
@@ -15,27 +15,22 @@
* 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.services
+package foundation.e.advancedprivacy.trackers.service
import android.app.Service
import android.content.Intent
import android.os.IBinder
-import foundation.e.advancedprivacy.trackers.domain.usecases.DNSBlocker
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import org.koin.java.KoinJavaComponent.get
-class DNSBlockerService : Service() {
+class TrackersService : Service() {
companion object {
const val ACTION_START = "foundation.e.privacymodules.trackers.intent.action.START"
- const val EXTRA_ENABLE_NOTIFICATION =
- "foundation.e.privacymodules.trackers.intent.extra.ENABLED_NOTIFICATION"
- }
- private var coroutineScope = CoroutineScope(Dispatchers.IO)
- private var dnsBlocker: DNSBlocker? = null
+ var coroutineScope = CoroutineScope(Dispatchers.IO)
+ }
override fun onBind(intent: Intent): IBinder? {
throw UnsupportedOperationException("Not yet implemented")
@@ -43,9 +38,6 @@ class DNSBlockerService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (ACTION_START == intent?.action) {
- if (intent.getBooleanExtra(EXTRA_ENABLE_NOTIFICATION, true)) {
- ForegroundStarter.startForeground(this)
- }
stop()
start()
}
@@ -55,14 +47,12 @@ class DNSBlockerService : Service() {
private fun start() {
coroutineScope = CoroutineScope(Dispatchers.IO)
get<DNSBlocker>(DNSBlocker::class.java).apply {
- this@DNSBlockerService.dnsBlocker = this
- trackersLogger.writeLogJob(coroutineScope)
+ filterHostnameUseCase.writeLogJob(coroutineScope)
listenJob(coroutineScope)
}
}
private fun stop() {
kotlin.runCatching { coroutineScope.cancel() }
- dnsBlocker = null
}
}
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
new file mode 100644
index 0000000..3903db4
--- /dev/null
+++ b/trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt
@@ -0,0 +1,46 @@
+/*
+ * 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
+
+import android.content.Context
+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.factoryOf
+import org.koin.dsl.module
+
+class TrackersServiceSupervisorImpl(private val context: Context) : TrackersServiceSupervisor {
+
+ override fun start(): Boolean {
+ val intent = Intent(context, TrackersService::class.java)
+ intent.action = ACTION_START
+ return context.startService(intent) != null
+ }
+
+ override fun stop(): Boolean {
+ return context.stopService(Intent(context, TrackersService::class.java))
+ }
+
+ override fun isRunning(): Boolean {
+ return TrackersService.coroutineScope.isActive
+ }
+}
+
+val trackerServiceModule = module {
+ factoryOf(::DNSBlocker)
+}
diff --git a/trackersservicestandalone/.gitignore b/trackersservicestandalone/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/trackersservicestandalone/.gitignore
@@ -0,0 +1 @@
+/build \ No newline at end of file
diff --git a/trackersservicestandalone/build.gradle b/trackersservicestandalone/build.gradle
new file mode 100644
index 0000000..d5df422
--- /dev/null
+++ b/trackersservicestandalone/build.gradle
@@ -0,0 +1,43 @@
+plugins {
+ id 'com.android.library'
+ id 'org.jetbrains.kotlin.android'
+}
+
+android {
+ compileSdk 32
+
+ defaultConfig {
+ minSdk 26
+ targetSdk 32
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles "consumer-rules.pro"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+}
+
+dependencies {
+ implementation project(":core")
+ implementation project(":trackers")
+
+ implementation(
+ libs.androidx.core.ktx,
+ libs.bundles.koin,
+ libs.bundles.pcap4j,
+ libs.kotlinx.coroutines,
+ libs.timber,
+ )
+}
diff --git a/trackersservicestandalone/consumer-rules.pro b/trackersservicestandalone/consumer-rules.pro
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/trackersservicestandalone/consumer-rules.pro
diff --git a/trackersservicestandalone/proguard-rules.pro b/trackersservicestandalone/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/trackersservicestandalone/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile \ No newline at end of file
diff --git a/trackersservicestandalone/src/main/AndroidManifest.xml b/trackersservicestandalone/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..4bfa4eb
--- /dev/null
+++ b/trackersservicestandalone/src/main/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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/>.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="foundation.e.advancedprivacy.trackers.service">
+
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+ <uses-permission android:name="android.permission.INTERNET" />
+
+ <application
+ android:usesCleartextTraffic="true"
+ >
+ <service android:name=".TrackersService"
+ android:enabled="true"
+ android:permission="android.permission.BIND_VPN_SERVICE"
+ android:exported="true"
+ >
+ <intent-filter>
+ <action android:name="android.net.VpnService"/>
+ </intent-filter>
+ </service>
+
+ </application>
+</manifest> \ No newline at end of file
diff --git a/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/Config.kt b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/Config.kt
new file mode 100644
index 0000000..d079e22
--- /dev/null
+++ b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/Config.kt
@@ -0,0 +1,44 @@
+/*
+ * 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
+
+internal object Config {
+ const val SESSION_NAME = "TrackersService"
+
+ const val FALLBACK_DNS = "1.1.1.1"
+ const val VERBOSE = true
+
+ const val VIRTUALDNS_IPV4 = "10.10.10.10"
+ const val VIRTUALDNS_IPV6 = "fdc8:1095:91e1:aaaa:aaaa:aaaa:aaaa:aaa1"
+ const val ADDRESS_IPV4 = "10.0.2.15"
+ const val ADDRESS_IPV6 = "fdc8:1095:91e1:aaaa:aaaa:aaaa:aaaa:aaa2"
+
+ const val BLOCKED_IPV4 = "127.0.0.1"
+ const val BLOCKED_IPV6 = "::1"
+
+ const val MTU = 3000
+ const val LOCAL_RESOLVER_TTL = 60
+
+ const val MAX_RESOLVER_COUNT = 100
+
+ val DNS_SERVER_TO_CATCH_IPV4 = listOf(
+ "8.8.8.8", "8.8.4.4", "1.1.1.1"
+ )
+ val DNS_SERVER_TO_CATCH_IPV6 = listOf(
+ "2001:4860:4860::8888", "2001:4860:4860::8844"
+ )
+}
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
new file mode 100644
index 0000000..918977f
--- /dev/null
+++ b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt
@@ -0,0 +1,127 @@
+/*
+ * 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
+
+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.NOTIFICATION_TRACKER_FLAG
+import foundation.e.advancedprivacy.domain.entities.NotificationContent
+import foundation.e.advancedprivacy.trackers.domain.externalinterfaces.TrackersServiceSupervisor
+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
+import foundation.e.advancedprivacy.trackers.service.data.NetworkDNSAddressRepository
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import org.koin.core.qualifier.named
+import org.koin.java.KoinJavaComponent.get
+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 notificationTrackerFlag: NotificationContent = get(NotificationContent::class.java, named("notificationTrackerFlag"))
+
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ startVPN()
+
+ startForeground(
+ NOTIFICATION_TRACKER_FLAG,
+ notificationBuilder(
+ context = this,
+ content = notificationTrackerFlag
+ ).build()
+ )
+ trackersServiceSupervisor.state.value = FeatureServiceState.ON
+
+ return START_STICKY
+ }
+
+ override fun onDestroy() {
+ networkDNSAddressRepository.stop()
+ trackersServiceSupervisor.state.value = FeatureServiceState.OFF
+ super.onDestroy()
+ }
+
+ private fun startVPN() {
+ val vpnInterface = initVPN()
+
+ if (vpnInterface != null) {
+ networkDNSAddressRepository.start()
+
+ coroutineScope = CoroutineScope(Dispatchers.IO)
+ get<TunLooper>(TunLooper::class.java).apply {
+ listenJob(vpnInterface, coroutineScope)
+ }
+ } else {
+ Timber.e("Cannot get VPN interface")
+ }
+ }
+
+ private fun initVPN(): ParcelFileDescriptor? {
+ val builder = Builder()
+ builder.setSession(SESSION_NAME)
+ // IPV4:
+ builder
+ .addAddress(Config.ADDRESS_IPV4, 24)
+ .addDnsServer(Config.VIRTUALDNS_IPV4)
+ .addRoute(Config.VIRTUALDNS_IPV4, 32)
+
+ // IPV6
+ builder
+ .addAddress(Config.ADDRESS_IPV6, 48)
+ .addDnsServer(Config.VIRTUALDNS_IPV6)
+ .addRoute(Config.VIRTUALDNS_IPV6, 128)
+
+ DNS_SERVER_TO_CATCH_IPV4.forEach {
+ builder.addRoute(it, 32)
+ }
+ DNS_SERVER_TO_CATCH_IPV6.forEach {
+ builder.addRoute(it, 128)
+ }
+
+ // TODO: block private DNS.
+ // TODO 20230821: seen in privateDNSFilter, bypass filter for google apps on Android 7/8
+
+ builder.addDisallowedApplication(packageName)
+ builder.setBlocking(true)
+ builder.setMtu(Config.MTU)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ builder.setMetered(false) // take over defaults from underlying network
+ }
+
+ return builder.establish()
+ }
+}
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
new file mode 100644
index 0000000..25d3e2d
--- /dev/null
+++ b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt
@@ -0,0 +1,64 @@
+/*
+ * 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
+
+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.trackers.service.data.NetworkDNSAddressRepository
+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.singleOf
+import org.koin.dsl.module
+
+class TrackersServiceSupervisorImpl(private val context: Context) : TrackersServiceSupervisor {
+ internal val state: MutableStateFlow<FeatureServiceState> = MutableStateFlow(FeatureServiceState.OFF)
+
+ override fun start(): Boolean {
+ return if (!isRunning()) {
+ state.value = FeatureServiceState.STARTING
+ TrackersService.start(context)
+ true
+ } else false
+ }
+
+ override fun stop(): Boolean {
+ return when (state.value) {
+ FeatureServiceState.ON -> {
+ state.value = FeatureServiceState.STOPPING
+ kotlin.runCatching { TrackersService.coroutineScope.cancel() }
+ context.stopService(Intent(context, TrackersService::class.java))
+ true
+ }
+ else -> false
+ }
+ }
+
+ override fun isRunning(): Boolean {
+ return state.value != FeatureServiceState.OFF
+ }
+}
+
+val trackerServiceModule = module {
+ singleOf(::NetworkDNSAddressRepository)
+ singleOf(::RequestDNSRepository)
+ singleOf(::ResolveDNSUseCase)
+ singleOf(::TunLooper)
+}
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
new file mode 100644
index 0000000..7813c67
--- /dev/null
+++ b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/TunLooper.kt
@@ -0,0 +1,167 @@
+/*
+ * 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
+
+import android.os.ParcelFileDescriptor
+import foundation.e.advancedprivacy.trackers.service.usecases.ResolveDNSUseCase
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
+import org.pcap4j.packet.DnsPacket
+import org.pcap4j.packet.IpPacket
+import org.pcap4j.packet.IpSelector
+import org.pcap4j.packet.IpV4Packet
+import org.pcap4j.packet.IpV6Packet
+import org.pcap4j.packet.UdpPacket
+import org.pcap4j.packet.namednumber.IpNumber
+import org.pcap4j.packet.namednumber.UdpPort
+import timber.log.Timber
+import java.io.DataOutputStream
+import java.io.FileInputStream
+import java.io.FileOutputStream
+import java.io.IOException
+import java.net.Inet6Address
+import java.util.Arrays
+
+class TunLooper(
+ private val resolveDNSUseCase: ResolveDNSUseCase,
+) {
+ private var vpnInterface: ParcelFileDescriptor? = null
+ private var fileInputStream: FileInputStream? = null
+ private var dataOutputStream: DataOutputStream? = null
+
+ private fun closeStreams() {
+ fileInputStream?.close()
+ fileInputStream = null
+
+ dataOutputStream?.close()
+ dataOutputStream = null
+
+ vpnInterface?.close()
+ vpnInterface = null
+ }
+
+ fun listenJob(
+ vpnInterface: ParcelFileDescriptor,
+ scope: CoroutineScope
+ ): Job = scope.launch(Dispatchers.IO) {
+ this@TunLooper.vpnInterface = vpnInterface
+ val fis = FileInputStream(vpnInterface.fileDescriptor)
+ this@TunLooper.fileInputStream = fis
+ dataOutputStream = DataOutputStream(FileOutputStream(vpnInterface.fileDescriptor))
+
+ while (isActive) {
+ runCatching {
+ val buffer = ByteArray(Config.MTU)
+ val pLen = fis.read(buffer)
+
+ if (pLen > 0) {
+ scope.launch { handleIpPacket(buffer, pLen) }
+ }
+ }.onFailure {
+ if (it is CancellationException) {
+ closeStreams()
+ throw it
+ } else {
+ Timber.w(it, "while reading from VPN fd")
+ }
+ }
+ }
+ }
+
+ private suspend fun handleIpPacket(buffer: ByteArray, pLen: Int) {
+ val pdata = Arrays.copyOf(buffer, pLen)
+ try {
+ val packet = IpSelector.newPacket(pdata, 0, pdata.size)
+ if (packet is IpPacket) {
+ val ipPacket = packet
+ if (isPacketDNS(ipPacket)) {
+ handleDnsPacket(ipPacket)
+ }
+ }
+ } catch (e: Exception) {
+ Timber.w(e, "Can't parse packet, ignore it.")
+ }
+ }
+
+ private fun isPacketDNS(p: IpPacket): Boolean {
+ if (p.header.protocol === IpNumber.UDP) {
+ val up = p.payload as UdpPacket
+ return up.header.dstPort === UdpPort.DOMAIN
+ }
+ return false
+ }
+
+ private suspend fun handleDnsPacket(ipPacket: IpPacket) {
+ try {
+ val udpPacket = ipPacket.payload as UdpPacket
+ val dnsRequest = udpPacket.payload as DnsPacket
+ val dnsResponse = resolveDNSUseCase.processDNS(dnsRequest)
+
+ if (dnsResponse != null) {
+ val dnsBuilder = dnsResponse.builder
+
+ val udpBuilder = UdpPacket.Builder(udpPacket)
+ .srcPort(udpPacket.header.dstPort)
+ .dstPort(udpPacket.header.srcPort)
+ .srcAddr(ipPacket.getHeader().getDstAddr())
+ .dstAddr(ipPacket.getHeader().getSrcAddr())
+ .correctChecksumAtBuild(true)
+ .correctLengthAtBuild(true)
+ .payloadBuilder(dnsBuilder)
+
+ val respPacket: IpPacket? = if (ipPacket is IpV4Packet) {
+ val ipV4Packet = ipPacket
+ val ipv4Builder = IpV4Packet.Builder()
+ ipv4Builder
+ .version(ipV4Packet.header.version)
+ .protocol(ipV4Packet.header.protocol)
+ .tos(ipV4Packet.header.tos)
+ .srcAddr(ipV4Packet.header.dstAddr)
+ .dstAddr(ipV4Packet.header.srcAddr)
+ .correctChecksumAtBuild(true)
+ .correctLengthAtBuild(true)
+ .dontFragmentFlag(ipV4Packet.header.dontFragmentFlag)
+ .reservedFlag(ipV4Packet.header.reservedFlag)
+ .moreFragmentFlag(ipV4Packet.header.moreFragmentFlag)
+ .ttl(Integer.valueOf(64).toByte())
+ .payloadBuilder(udpBuilder)
+ ipv4Builder.build()
+ } else if (ipPacket is IpV6Packet) {
+ IpV6Packet.Builder(ipPacket as IpV6Packet?)
+ .srcAddr(ipPacket.getHeader().getDstAddr() as Inet6Address)
+ .dstAddr(ipPacket.getHeader().getSrcAddr() as Inet6Address)
+ .payloadBuilder(udpBuilder)
+ .build()
+ } else null
+
+ respPacket?.let {
+ try {
+ dataOutputStream?.write(it.rawData)
+ } catch (e: IOException) {
+ Timber.e(e, "error writing to VPN fd")
+ }
+ }
+ }
+ } catch (ioe: java.lang.Exception) {
+ Timber.e(ioe, "could not parse DNS packet")
+ }
+ }
+}
diff --git a/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/data/NetworkDNSAddressRepository.kt b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/data/NetworkDNSAddressRepository.kt
new file mode 100644
index 0000000..7c36ed2
--- /dev/null
+++ b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/data/NetworkDNSAddressRepository.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.data
+
+import android.content.Context
+import android.net.ConnectivityManager
+import android.net.Network
+import android.net.NetworkRequest
+import foundation.e.advancedprivacy.trackers.service.Config
+import java.net.InetAddress
+
+class NetworkDNSAddressRepository(private val context: Context) {
+ private val connectivityManager: ConnectivityManager =
+ context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+
+ fun start() {
+ connectivityManager.registerNetworkCallback(
+ NetworkRequest.Builder().build(),
+ networkCallback
+ )
+ }
+
+ fun stop() {
+ kotlin.runCatching {
+ connectivityManager.unregisterNetworkCallback(networkCallback)
+ }
+ }
+
+ var dnsAddress: InetAddress = InetAddress.getByName(Config.FALLBACK_DNS)
+ private set
+
+ private val networkCallback = object : ConnectivityManager.NetworkCallback() {
+ override fun onAvailable(network: Network) {
+ super.onAvailable(network)
+ connectivityManager.getLinkProperties(network)
+ ?.dnsServers?.firstOrNull {
+ it.hostAddress.let {
+ it != Config.VIRTUALDNS_IPV4 && it != Config.VIRTUALDNS_IPV6
+ }
+ }?.let {
+ dnsAddress = InetAddress.getByName(it.hostAddress)
+ }
+ }
+ }
+}
diff --git a/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/data/RequestDNSRepository.kt b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/data/RequestDNSRepository.kt
new file mode 100644
index 0000000..d9370be
--- /dev/null
+++ b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/data/RequestDNSRepository.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.data
+
+import foundation.e.advancedprivacy.core.utils.runSuspendCatching
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import org.pcap4j.packet.DnsPacket
+import timber.log.Timber
+import java.net.DatagramPacket
+import java.net.DatagramSocket
+
+class RequestDNSRepository {
+
+ suspend fun processDNS(request: DatagramPacket): DnsPacket? = withContext(Dispatchers.IO) {
+ runSuspendCatching {
+ var response: DnsPacket? = null
+ val datagramSocket = DatagramSocket()
+ datagramSocket.send(request)
+
+ // Await response from DNS server
+ val buf = ByteArray(1024)
+ val packet = DatagramPacket(buf, buf.size)
+ datagramSocket.receive(packet)
+ val dnsResp = packet.data
+ if (dnsResp != null) {
+ response = DnsPacket.newPacket(dnsResp, 0, dnsResp.size)
+ }
+ response
+ }.onFailure {
+ Timber.w(it, "Can't make DNS request.")
+ }.getOrNull()
+ }
+}
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
new file mode 100644
index 0000000..ac8aee0
--- /dev/null
+++ b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/usecases/ResolveDNSUseCase.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.trackers.domain.usecases.FilterHostnameUseCase
+import foundation.e.advancedprivacy.trackers.service.data.NetworkDNSAddressRepository
+import foundation.e.advancedprivacy.trackers.service.data.RequestDNSRepository
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.DelicateCoroutinesApi
+import kotlinx.coroutines.GlobalScope
+import org.pcap4j.packet.DnsPacket
+import org.pcap4j.packet.namednumber.DnsRCode
+import java.net.DatagramPacket
+
+@OptIn(DelicateCoroutinesApi::class)
+class ResolveDNSUseCase(
+ private val networkDNSAddressRepository: NetworkDNSAddressRepository,
+ private val filterHostnameUseCase: FilterHostnameUseCase,
+ private val requestDNSRepository: RequestDNSRepository,
+ private val scope: CoroutineScope = GlobalScope
+) {
+ private val DNS_PORT = 53
+
+ init {
+ filterHostnameUseCase.writeLogJob(scope)
+ }
+
+ 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 payload = dnsRequest.rawData
+ val packet = DatagramPacket(payload, payload.size, networkDNSAddressRepository.dnsAddress, DNS_PORT)
+ return requestDNSRepository.processDNS(packet)
+ }
+}