summaryrefslogtreecommitdiff
path: root/trackersservicee/src
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 /trackersservicee/src
parent41c0e48498979ec8310961e40c89aa6b3c0d8e0f (diff)
epic18: Trackers control on standalone app (without Ipscrambling).
Diffstat (limited to 'trackersservicee/src')
-rw-r--r--trackersservicee/src/main/AndroidManifest.xml35
-rw-r--r--trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/DNSBlocker.kt104
-rw-r--r--trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersService.kt58
-rw-r--r--trackersservicee/src/main/java/foundation/e/advancedprivacy/trackers/service/TrackersServiceSupervisorImpl.kt46
4 files changed, 243 insertions, 0 deletions
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)
+}