diff options
author | Guillaume Jacquart <guillaume.jacquart@hoodbrains.com> | 2023-10-11 16:36:02 +0000 |
---|---|---|
committer | Guillaume Jacquart <guillaume.jacquart@hoodbrains.com> | 2023-10-11 16:36:02 +0000 |
commit | 95e68cbbe748f81af1113753c5b99929e3db9ea2 (patch) | |
tree | 776b2e1fb3d5c16a48beb1c947eef887b2475db8 /trackersservicee | |
parent | 41c0e48498979ec8310961e40c89aa6b3c0d8e0f (diff) |
epic18: Trackers control on standalone app (without Ipscrambling).
Diffstat (limited to 'trackersservicee')
8 files changed, 308 insertions, 0 deletions
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/trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/DNSBlocker.kt b/trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/DNSBlocker.kt new file mode 100644 index 0000000..6a2b218 --- /dev/null +++ b/trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/DNSBlocker.kt @@ -0,0 +1,104 @@ +/* + * 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.service + +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.domain.usecases.FilterHostnameUseCase +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 timber.log.Timber +import java.io.BufferedReader +import java.io.InputStreamReader +import java.io.PrintWriter + +class DNSBlocker( + val filterHostnameUseCase: FilterHostnameUseCase +) { + private var resolverReceiver: LocalServerSocket? = null + + companion object { + private const val SOCKET_NAME = "foundation.e.advancedprivacy" + } + + private fun closeSocket() { + // Known bug and workaround that LocalServerSocket::close is not working well + // https://issuetracker.google.com/issues/36945762 + if (resolverReceiver != null) { + try { + Os.shutdown(resolverReceiver!!.fileDescriptor, OsConstants.SHUT_RDWR) + resolverReceiver!!.close() + resolverReceiver = null + } catch (e: ErrnoException) { + if (e.errno != OsConstants.EBADF) { + Timber.w("Socket already closed") + } else { + Timber.e(e, "Exception: cannot close DNS port on stop $SOCKET_NAME !") + } + } catch (e: Exception) { + Timber.e(e, "Exception: cannot close DNS port on stop $SOCKET_NAME !") + } + } + } + + fun listenJob(scope: CoroutineScope): Job = scope.launch(Dispatchers.IO) { + val resolverReceiver = runSuspendCatching { + LocalServerSocket(SOCKET_NAME) + }.getOrElse { + Timber.e(it, "Exception: cannot open DNS port on $SOCKET_NAME") + return@launch + } + + this@DNSBlocker.resolverReceiver = resolverReceiver + Timber.d("DNSFilterProxy running on port $SOCKET_NAME") + + while (isActive) { + runSuspendCatching { + val socket = resolverReceiver.accept() + val reader = BufferedReader(InputStreamReader(socket.inputStream)) + val line = reader.readLine() + val params = line.split(",").toTypedArray() + val output = socket.outputStream + val writer = PrintWriter(output, true) + val domainName = params[0] + val appUid = params[1].toInt() + if (filterHostnameUseCase.shouldBlock(domainName, appUid)) { + writer.println("block") + } else { + writer.println("pass") + } + socket.close() + }.onFailure { + if (it is CancellationException) { + closeSocket() + throw it + } else { + Timber.w(it, "Exception while listening DNS resolver") + } + } + } + } +} diff --git a/trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt b/trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt new file mode 100644 index 0000000..5f573b0 --- /dev/null +++ b/trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023 MURENA SAS + * Copyright (C) 2021 E FOUNDATION + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +package foundation.e.advancedprivacy.trackers.service + +import android.app.Service +import android.content.Intent +import android.os.IBinder +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancel +import org.koin.java.KoinJavaComponent.get + +class TrackersService : Service() { + companion object { + const val ACTION_START = "foundation.e.privacymodules.trackers.intent.action.START" + + var coroutineScope = CoroutineScope(Dispatchers.IO) + } + + override fun onBind(intent: Intent): IBinder? { + throw UnsupportedOperationException("Not yet implemented") + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + if (ACTION_START == intent?.action) { + stop() + start() + } + return START_REDELIVER_INTENT + } + + private fun start() { + coroutineScope = CoroutineScope(Dispatchers.IO) + get<DNSBlocker>(DNSBlocker::class.java).apply { + filterHostnameUseCase.writeLogJob(coroutineScope) + listenJob(coroutineScope) + } + } + + private fun stop() { + kotlin.runCatching { coroutineScope.cancel() } + } +} 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) +} |