summaryrefslogtreecommitdiff
path: root/trackers/src/main/java/foundation/e/privacymodules
diff options
context:
space:
mode:
Diffstat (limited to 'trackers/src/main/java/foundation/e/privacymodules')
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.kt141
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerService.kt79
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/ForegroundStarter.kt45
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.kt69
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/api/BlockTrackersPrivacyModule.kt98
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/api/IBlockTrackersPrivacyModule.kt98
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/api/ITrackTrackersPrivacyModule.kt110
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.kt126
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/api/Tracker.kt28
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsDatabase.kt459
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsRepository.kt105
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/data/TrackersRepository.kt57
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/data/WhitelistRepository.kt207
13 files changed, 0 insertions, 1622 deletions
diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.kt
deleted file mode 100644
index 44793a4..0000000
--- a/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.kt
+++ /dev/null
@@ -1,141 +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.privacymodules.trackers
-
-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 android.util.Log
-import foundation.e.privacymodules.trackers.data.TrackersRepository
-import foundation.e.privacymodules.trackers.data.WhitelistRepository
-import java.io.BufferedReader
-import java.io.IOException
-import java.io.InputStreamReader
-import java.io.PrintWriter
-
-class DNSBlockerRunnable(
- context: Context,
- private val trackersLogger: TrackersLogger,
- private val trackersRepository: TrackersRepository,
- private val whitelistRepository: WhitelistRepository
-) : Runnable {
- var resolverReceiver: LocalServerSocket? = null
- var stopped = false
- 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"
- private const val TAG = "DNSBlockerRunnable"
- }
-
- init {
- initEBrowserDoTFix(context)
- }
-
- @Synchronized
- fun stop() {
- stopped = true
- closeSocket()
- }
-
- 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) {
- Log.w(TAG, "Socket already closed")
- } else {
- Log.e(TAG, "Exception: cannot close DNS port on stop $SOCKET_NAME !", e)
- }
- } catch (e: Exception) {
- Log.e(TAG, "Exception: cannot close DNS port on stop $SOCKET_NAME !", e)
- }
- }
- }
-
- override fun run() {
- val resolverReceiver = try {
- LocalServerSocket(SOCKET_NAME)
- } catch (eio: IOException) {
- Log.e(TAG, "Exception:Cannot open DNS port $SOCKET_NAME !", eio)
- return
- }
-
- this.resolverReceiver = resolverReceiver
- Log.d(TAG, "DNSFilterProxy running on port $SOCKET_NAME !")
-
- while (!stopped) {
- try {
- 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()
- 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) {
- writer.println("pass")
- }
- socket.close()
- // Printing bufferedreader data
- } catch (e: IOException) {
- Log.w(TAG, "Exception while listening DNS resolver", e)
- }
- }
- }
-
- private fun initEBrowserDoTFix(context: Context) {
- try {
- eBrowserAppUid =
- context.packageManager.getApplicationInfo("foundation.e.browser", 0).uid
- } catch (e: PackageManager.NameNotFoundException) {
- Log.i(TAG, "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/privacymodules/trackers/DNSBlockerService.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerService.kt
deleted file mode 100644
index c2ad16b..0000000
--- a/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerService.kt
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2021 E FOUNDATION
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package foundation.e.privacymodules.trackers
-
-import android.app.Service
-import android.content.Intent
-import android.os.IBinder
-import android.util.Log
-import foundation.e.privacymodules.trackers.data.TrackersRepository
-import foundation.e.privacymodules.trackers.data.WhitelistRepository
-
-class DNSBlockerService : Service() {
- private var trackersLogger: TrackersLogger? = null
-
- companion object {
- private const val TAG = "DNSBlockerService"
- private var sDNSBlocker: DNSBlockerRunnable? = null
- const val ACTION_START = "foundation.e.privacymodules.trackers.intent.action.START"
- const val EXTRA_ENABLE_NOTIFICATION =
- "foundation.e.privacymodules.trackers.intent.extra.ENABLED_NOTIFICATION"
- }
-
- override fun onBind(intent: Intent): IBinder? {
- // TODO: Return the communication channel to the service.
- throw UnsupportedOperationException("Not yet implemented")
- }
-
- 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()
- }
- return START_REDELIVER_INTENT
- }
-
- private fun start() {
- try {
- val trackersLogger = TrackersLogger(this)
- this.trackersLogger = trackersLogger
-
- sDNSBlocker = DNSBlockerRunnable(
- this,
- trackersLogger,
- TrackersRepository.getInstance(),
- WhitelistRepository.getInstance(this)
- )
- Thread(sDNSBlocker).start()
- } catch (e: Exception) {
- Log.e(TAG, "Error while starting DNSBlocker service", e)
- stop()
- }
- }
-
- private fun stop() {
- sDNSBlocker?.stop()
- sDNSBlocker = null
-
- trackersLogger?.stop()
- trackersLogger = null
- }
-}
diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/ForegroundStarter.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/ForegroundStarter.kt
deleted file mode 100644
index 69b4f28..0000000
--- a/trackers/src/main/java/foundation/e/privacymodules/trackers/ForegroundStarter.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2021 E FOUNDATION
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package foundation.e.privacymodules.trackers
-
-import android.app.Notification
-import android.app.NotificationChannel
-import android.app.NotificationManager
-import android.app.Service
-import android.content.Context
-import android.os.Build
-
-object ForegroundStarter {
- private const val NOTIFICATION_CHANNEL_ID = "blocker_service"
- fun startForeground(service: Service) {
- val mNotificationManager =
- service.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
- if (Build.VERSION.SDK_INT >= 26) {
- mNotificationManager.createNotificationChannel(
- NotificationChannel(
- NOTIFICATION_CHANNEL_ID,
- NOTIFICATION_CHANNEL_ID,
- NotificationManager.IMPORTANCE_LOW
- )
- )
- val notification = Notification.Builder(service, NOTIFICATION_CHANNEL_ID)
- .setContentTitle("Trackers filter").build()
- service.startForeground(1337, notification)
- }
- }
-}
diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.kt
deleted file mode 100644
index f3c4745..0000000
--- a/trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.kt
+++ /dev/null
@@ -1,69 +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.privacymodules.trackers
-
-import android.content.Context
-import android.util.Log
-import foundation.e.privacymodules.trackers.data.StatsRepository
-import java.util.concurrent.LinkedBlockingQueue
-
-class TrackersLogger(context: Context) {
- private val statsRepository = StatsRepository.getInstance(context)
- private val queue = LinkedBlockingQueue<DetectedTracker>()
- private var stopped = false
-
- companion object {
- private const val TAG = "TrackerModule"
- }
-
- init {
- startWriteLogLoop()
- }
-
- fun stop() {
- stopped = true
- }
-
- fun logAccess(trackerId: String?, appUid: Int, wasBlocked: Boolean) {
- queue.offer(DetectedTracker(trackerId, appUid, wasBlocked))
- }
-
- private fun startWriteLogLoop() {
- val writeLogRunner = Runnable {
- while (!stopped) {
- try {
- logAccess(queue.take())
- } catch (e: InterruptedException) {
- Log.e(TAG, "writeLogLoop detectedTrackersQueue.take() interrupted: ", e)
- }
- }
- }
- Thread(writeLogRunner).start()
- }
-
- fun logAccess(detectedTracker: DetectedTracker) {
- statsRepository.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/privacymodules/trackers/api/BlockTrackersPrivacyModule.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/BlockTrackersPrivacyModule.kt
deleted file mode 100644
index 7463b22..0000000
--- a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/BlockTrackersPrivacyModule.kt
+++ /dev/null
@@ -1,98 +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.privacymodules.trackers.api
-
-import android.content.Context
-import foundation.e.privacymodules.permissions.data.ApplicationDescription
-import foundation.e.privacymodules.trackers.data.TrackersRepository
-import foundation.e.privacymodules.trackers.data.WhitelistRepository
-
-class BlockTrackersPrivacyModule(context: Context) : IBlockTrackersPrivacyModule {
- private val mListeners = mutableListOf<IBlockTrackersPrivacyModule.Listener>()
- private val trackersRepository = TrackersRepository.getInstance()
- private val whitelistRepository = WhitelistRepository.getInstance(context)
-
- companion object {
- private var instance: BlockTrackersPrivacyModule? = null
-
- fun getInstance(context: Context): BlockTrackersPrivacyModule {
- return instance ?: BlockTrackersPrivacyModule(context).apply { instance = this }
- }
- }
-
- override fun addListener(listener: IBlockTrackersPrivacyModule.Listener) {
- mListeners.add(listener)
- }
-
- override fun clearListeners() {
- mListeners.clear()
- }
-
- override fun disableBlocking() {
- whitelistRepository.isBlockingEnabled = false
- mListeners.forEach { listener -> listener.onBlockingToggle(false) }
- }
-
- override fun enableBlocking() {
- whitelistRepository.isBlockingEnabled = true
- mListeners.forEach { listener -> listener.onBlockingToggle(true) }
- }
-
- override fun getWhiteList(app: ApplicationDescription): List<Tracker> {
- return whitelistRepository.getWhiteListForApp(app).mapNotNull {
- trackersRepository.getTracker(it)
- }
- }
-
- override fun getWhiteListedApp(): List<ApplicationDescription> {
- return whitelistRepository.getWhiteListedApp()
- }
-
- override fun isBlockingEnabled(): Boolean {
- return whitelistRepository.isBlockingEnabled
- }
-
- override fun isWhiteListEmpty(): Boolean {
- return whitelistRepository.areWhiteListEmpty()
- }
-
- override fun isWhitelisted(app: ApplicationDescription): Boolean {
- return whitelistRepository.isAppWhiteListed(app)
- }
-
- override fun removeListener(listener: IBlockTrackersPrivacyModule.Listener) {
- mListeners.remove(listener)
- }
-
- override fun setWhiteListed(
- tracker: Tracker,
- app: ApplicationDescription,
- isWhiteListed: Boolean
- ) {
- whitelistRepository.setWhiteListed(tracker, app.apId, isWhiteListed)
- }
-
- override fun setWhiteListed(app: ApplicationDescription, isWhiteListed: Boolean) {
- whitelistRepository.setWhiteListed(app.apId, isWhiteListed)
- }
-
- override fun clearWhiteList(app: ApplicationDescription) {
- whitelistRepository.clearWhiteList(app.apId)
- }
-}
diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/IBlockTrackersPrivacyModule.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/IBlockTrackersPrivacyModule.kt
deleted file mode 100644
index 3547b8e..0000000
--- a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/IBlockTrackersPrivacyModule.kt
+++ /dev/null
@@ -1,98 +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.privacymodules.trackers.api
-
-import foundation.e.privacymodules.permissions.data.ApplicationDescription
-
-/**
- * Manage trackers blocking and whitelisting.
- */
-interface IBlockTrackersPrivacyModule {
-
- /**
- * Get the state of the blockin module
- * @return true when blocking is enabled, false otherwise.
- */
- fun isBlockingEnabled(): Boolean
-
- /**
- * Enable blocking, using the previously configured whitelists
- */
- fun enableBlocking()
-
- /**
- * Disable blocking
- */
- fun disableBlocking()
-
- /**
- * Set or unset in whitelist the App with the specified uid.
- * @param app the ApplicationDescription of the app
- * @param isWhiteListed true, the app will appears in whitelist, false, it won't
- */
- fun setWhiteListed(app: ApplicationDescription, isWhiteListed: Boolean)
-
- /**
- * Set or unset in whitelist the specifid tracked, for the App specified by its uid.
- * @param tracker the tracker
- * @param app the ApplicationDescription of the app
- * @param isWhiteListed true, the app will appears in whitelist, false, it won't
- */
- fun setWhiteListed(tracker: Tracker, app: ApplicationDescription, isWhiteListed: Boolean)
-
- /**
- * Return true if nothing has been added to the whitelist : everything is blocked.
- */
- fun isWhiteListEmpty(): Boolean
-
- /**
- * Return the white listed App, by their UID
- */
- fun getWhiteListedApp(): List<ApplicationDescription>
-
- /**
- * Return true if the App is whitelisted for trackers blocking.
- */
- fun isWhitelisted(app: ApplicationDescription): Boolean
-
- /**
- * List the white listed trackers for an App specified by it uid
- */
- fun getWhiteList(app: ApplicationDescription): List<Tracker>
-
- fun clearWhiteList(app: ApplicationDescription)
-
- /**
- * Callback interface to get updates about the state of the Block trackers module.
- */
- interface Listener {
-
- /**
- * Called when the trackers blocking is activated or deactivated.
- * @param isBlocking true when activated, false otherwise.
- */
- fun onBlockingToggle(isBlocking: Boolean)
- }
-
- fun addListener(listener: Listener)
-
- fun removeListener(listener: Listener)
-
- fun clearListeners()
-}
diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/ITrackTrackersPrivacyModule.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/ITrackTrackersPrivacyModule.kt
deleted file mode 100644
index 8aaed4a..0000000
--- a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/ITrackTrackersPrivacyModule.kt
+++ /dev/null
@@ -1,110 +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.privacymodules.trackers.api
-
-import foundation.e.privacymodules.permissions.data.ApplicationDescription
-
-/**
- * Get reporting about trackers calls.
- */
-interface ITrackTrackersPrivacyModule {
-
- fun start(
- trackers: List<Tracker>,
- getAppByUid: (Int) -> ApplicationDescription?,
- getAppByAPId: (String) -> ApplicationDescription?,
- enableNotification: Boolean = true
- )
-
- /**
- * List all the trackers encountered for a specific app.
- */
- fun getTrackersForApp(app: ApplicationDescription): List<Tracker>
-
- /**
- * List all the trackers encountere trackers since "ever", for the given [appUids],
- * or all apps if [appUids] is null
- */
- fun getTrackers(apps: List<ApplicationDescription>? = null): List<Tracker>
-
- /**
- * Return the number of encountered trackers since "ever", for the given [appUids],
- * or all apps if [appUids] is null
- */
- fun getTrackersCount(): Int
-
- /**
- * Return the number of encountere trackers since "ever", for each app uid.
- */
- fun getTrackersCountByApp(): Map<ApplicationDescription, Int>
-
- /**
- * Return the number of encountered trackers for the last 24 hours
- */
- fun getPastDayTrackersCount(): Int
-
- /**
- * Return the number of encountered trackers for the last month
- */
- fun getPastMonthTrackersCount(): Int
-
- /**
- * Return the number of encountered trackers for the last year
- */
- fun getPastYearTrackersCount(): Int
-
- /**
- * Return number of trackers calls by hours, for the last 24hours.
- * @return list of 24 numbers of trackers calls by hours
- */
- fun getPastDayTrackersCalls(): List<Pair<Int, Int>>
-
- /**
- * Return number of trackers calls by day, for the last 30 days.
- * @return list of 30 numbers of trackers calls by day
- */
- fun getPastMonthTrackersCalls(): List<Pair<Int, Int>>
-
- /**
- * Return number of trackers calls by month, for the last 12 month.
- * @return list of 12 numbers of trackers calls by month
- */
- fun getPastYearTrackersCalls(): List<Pair<Int, Int>>
-
- fun getPastDayTrackersCallsByApps(): Map<ApplicationDescription, Pair<Int, Int>>
-
- fun getPastDayTrackersCallsForApp(app: ApplicationDescription): Pair<Int, Int>
-
- fun getPastDayMostLeakedApp(): ApplicationDescription?
-
- interface Listener {
-
- /**
- * Called when a new tracker attempt is logged. Consumer may choose to call other methods
- * to refresh the data.
- */
- fun onNewData()
- }
-
- fun addListener(listener: Listener)
-
- fun removeListener(listener: Listener)
-
- fun clearListeners()
-}
diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.kt
deleted file mode 100644
index 5fc5b6b..0000000
--- a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.kt
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * 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.privacymodules.trackers.api
-
-import android.content.Context
-import android.content.Intent
-import foundation.e.privacymodules.permissions.data.ApplicationDescription
-import foundation.e.privacymodules.trackers.DNSBlockerService
-import foundation.e.privacymodules.trackers.data.StatsRepository
-import foundation.e.privacymodules.trackers.data.TrackersRepository
-import foundation.e.privacymodules.trackers.data.WhitelistRepository
-import java.time.temporal.ChronoUnit
-
-class TrackTrackersPrivacyModule(private val context: Context) : ITrackTrackersPrivacyModule {
- private val statsRepository = StatsRepository.getInstance(context)
- private val listeners: MutableList<ITrackTrackersPrivacyModule.Listener> = mutableListOf()
-
- companion object {
- private var instance: TrackTrackersPrivacyModule? = null
-
- fun getInstance(context: Context): TrackTrackersPrivacyModule {
- return instance ?: TrackTrackersPrivacyModule(context).apply { instance = this }
- }
- }
-
- init {
- statsRepository.setNewDataCallback {
- listeners.forEach { listener -> listener.onNewData() }
- }
- }
-
- override fun start(
- trackers: List<Tracker>,
- getAppByUid: (Int) -> ApplicationDescription?,
- getAppByAPId: (String) -> ApplicationDescription?,
- enableNotification: Boolean
- ) {
- TrackersRepository.getInstance().setTrackersList(trackers)
- StatsRepository.getInstance(context).setAppGetters(getAppByUid, getAppByAPId)
- WhitelistRepository.getInstance(context).setAppGetters(context, getAppByAPId, getAppByUid)
- val intent = Intent(context, DNSBlockerService::class.java)
- intent.action = DNSBlockerService.ACTION_START
- intent.putExtra(DNSBlockerService.EXTRA_ENABLE_NOTIFICATION, enableNotification)
- context.startService(intent)
- }
-
- override fun getPastDayTrackersCalls(): List<Pair<Int, Int>> {
- return statsRepository.getTrackersCallsOnPeriod(24, ChronoUnit.HOURS)
- }
-
- override fun getPastMonthTrackersCalls(): List<Pair<Int, Int>> {
- return statsRepository.getTrackersCallsOnPeriod(30, ChronoUnit.DAYS)
- }
-
- override fun getPastYearTrackersCalls(): List<Pair<Int, Int>> {
- return statsRepository.getTrackersCallsOnPeriod(12, ChronoUnit.MONTHS)
- }
-
- override fun getTrackersCount(): Int {
- return statsRepository.getContactedTrackersCount()
- }
-
- override fun getTrackersCountByApp(): Map<ApplicationDescription, Int> {
- return statsRepository.getContactedTrackersCountByApp()
- }
-
- override fun getTrackersForApp(app: ApplicationDescription): List<Tracker> {
- return statsRepository.getTrackers(listOf(app))
- }
-
- override fun getTrackers(apps: List<ApplicationDescription>?): List<Tracker> {
- return statsRepository.getTrackers(apps)
- }
-
- override fun getPastDayTrackersCount(): Int {
- return statsRepository.getActiveTrackersByPeriod(24, ChronoUnit.HOURS)
- }
-
- override fun getPastMonthTrackersCount(): Int {
- return statsRepository.getActiveTrackersByPeriod(30, ChronoUnit.DAYS)
- }
-
- override fun getPastYearTrackersCount(): Int {
- return statsRepository.getActiveTrackersByPeriod(12, ChronoUnit.MONTHS)
- }
-
- override fun getPastDayMostLeakedApp(): ApplicationDescription? {
- return statsRepository.getMostLeakedApp(24, ChronoUnit.HOURS)
- }
-
- override fun getPastDayTrackersCallsByApps(): Map<ApplicationDescription, Pair<Int, Int>> {
- return statsRepository.getCallsByApps(24, ChronoUnit.HOURS)
- }
-
- override fun getPastDayTrackersCallsForApp(app: ApplicationDescription): Pair<Int, Int> {
- return statsRepository.getCalls(app, 24, ChronoUnit.HOURS)
- }
-
- override fun addListener(listener: ITrackTrackersPrivacyModule.Listener) {
- listeners.add(listener)
- }
-
- override fun removeListener(listener: ITrackTrackersPrivacyModule.Listener) {
- listeners.remove(listener)
- }
-
- override fun clearListeners() {
- listeners.clear()
- }
-}
diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/Tracker.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/Tracker.kt
deleted file mode 100644
index 2da5b16..0000000
--- a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/Tracker.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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.privacymodules.trackers.api
-
-/**
- * Describe a tracker.
- */
-data class Tracker(
- val id: String,
- val hostnames: Set<String>,
- val label: String,
- val exodusId: String?
-)
diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsDatabase.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsDatabase.kt
deleted file mode 100644
index 4d287d4..0000000
--- a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsDatabase.kt
+++ /dev/null
@@ -1,459 +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.privacymodules.trackers.data
-
-import android.content.ContentValues
-import android.content.Context
-import android.database.Cursor
-import android.database.sqlite.SQLiteDatabase
-import android.database.sqlite.SQLiteOpenHelper
-import android.provider.BaseColumns
-import androidx.core.database.getStringOrNull
-import foundation.e.privacymodules.trackers.api.Tracker
-import foundation.e.privacymodules.trackers.data.StatsDatabase.AppTrackerEntry.COLUMN_NAME_APPID
-import foundation.e.privacymodules.trackers.data.StatsDatabase.AppTrackerEntry.COLUMN_NAME_NUMBER_BLOCKED
-import foundation.e.privacymodules.trackers.data.StatsDatabase.AppTrackerEntry.COLUMN_NAME_NUMBER_CONTACTED
-import foundation.e.privacymodules.trackers.data.StatsDatabase.AppTrackerEntry.COLUMN_NAME_TIMESTAMP
-import foundation.e.privacymodules.trackers.data.StatsDatabase.AppTrackerEntry.COLUMN_NAME_TRACKER
-import foundation.e.privacymodules.trackers.data.StatsDatabase.AppTrackerEntry.TABLE_NAME
-import timber.log.Timber
-import java.time.ZonedDateTime
-import java.time.format.DateTimeFormatter
-import java.time.temporal.ChronoUnit
-import java.time.temporal.TemporalUnit
-import java.util.concurrent.TimeUnit
-
-class StatsDatabase(context: Context) :
- SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
-
- companion object {
- const val DATABASE_VERSION = 2
- const val DATABASE_NAME = "TrackerFilterStats.db"
- private const val SQL_CREATE_TABLE = "CREATE TABLE $TABLE_NAME (" +
- "${BaseColumns._ID} INTEGER PRIMARY KEY," +
- "$COLUMN_NAME_TIMESTAMP INTEGER," +
- "$COLUMN_NAME_TRACKER TEXT," +
- "$COLUMN_NAME_NUMBER_CONTACTED INTEGER," +
- "$COLUMN_NAME_NUMBER_BLOCKED INTEGER," +
- "$COLUMN_NAME_APPID TEXT)"
-
- private const val PROJECTION_NAME_PERIOD = "period"
- private const val PROJECTION_NAME_CONTACTED_SUM = "contactedsum"
- private const val PROJECTION_NAME_BLOCKED_SUM = "blockedsum"
- private const val PROJECTION_NAME_LEAKED_SUM = "leakedsum"
- private const val PROJECTION_NAME_TRACKERS_COUNT = "trackerscount"
-
- private val MIGRATE_1_2 = listOf(
- "ALTER TABLE $TABLE_NAME ADD COLUMN $COLUMN_NAME_APPID TEXT"
- // "ALTER TABLE $TABLE_NAME DROP COLUMN app_uid"
- // DROP COLUMN is available since sqlite 3.35.0, and sdk29 as 3.22.0, sdk32 as 3.32.2
- )
- }
-
- object AppTrackerEntry : BaseColumns {
- const val TABLE_NAME = "tracker_filter_stats"
- const val COLUMN_NAME_TIMESTAMP = "timestamp"
- const val COLUMN_NAME_TRACKER = "tracker"
- const val COLUMN_NAME_NUMBER_CONTACTED = "sum_contacted"
- const val COLUMN_NAME_NUMBER_BLOCKED = "sum_blocked"
- const val COLUMN_NAME_APPID = "app_apid"
- }
-
- private var projection = arrayOf(
- COLUMN_NAME_TIMESTAMP,
- COLUMN_NAME_TRACKER,
- COLUMN_NAME_NUMBER_CONTACTED,
- COLUMN_NAME_NUMBER_BLOCKED,
- COLUMN_NAME_APPID
- )
-
- private val lock = Any()
- private val trackersRepository = TrackersRepository.getInstance()
-
- override fun onCreate(db: SQLiteDatabase) {
- db.execSQL(SQL_CREATE_TABLE)
- }
-
- override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
- if (oldVersion == 1 && newVersion == 2) {
- MIGRATE_1_2.forEach(db::execSQL)
- } else {
- Timber.e(
- "Unexpected database versions: oldVersion: $oldVersion ; newVersion: $newVersion"
- )
- }
- }
-
- private fun getCallsByPeriod(
- periodsCount: Int,
- periodUnit: TemporalUnit,
- sqlitePeriodFormat: String
- ): Map<String, Pair<Int, Int>> {
- synchronized(lock) {
- val minTimestamp = getPeriodStartTs(periodsCount, periodUnit)
- val db = readableDatabase
- val selection = "$COLUMN_NAME_TIMESTAMP >= ?"
- val selectionArg = arrayOf("" + minTimestamp)
-
- val projection = (
- "$COLUMN_NAME_TIMESTAMP, " +
- "STRFTIME('$sqlitePeriodFormat', DATETIME($COLUMN_NAME_TIMESTAMP, 'unixepoch', 'localtime')) $PROJECTION_NAME_PERIOD," +
- "SUM($COLUMN_NAME_NUMBER_CONTACTED) $PROJECTION_NAME_CONTACTED_SUM, " +
- "SUM($COLUMN_NAME_NUMBER_BLOCKED) $PROJECTION_NAME_BLOCKED_SUM"
- )
-
- val cursor = db.rawQuery(
- "SELECT $projection FROM $TABLE_NAME WHERE $selection" +
- " GROUP BY $PROJECTION_NAME_PERIOD" +
- " ORDER BY $COLUMN_NAME_TIMESTAMP DESC LIMIT $periodsCount",
- selectionArg
- )
- val callsByPeriod = HashMap<String, Pair<Int, Int>>()
- while (cursor.moveToNext()) {
- val contacted = cursor.getInt(PROJECTION_NAME_CONTACTED_SUM)
- val blocked = cursor.getInt(PROJECTION_NAME_BLOCKED_SUM)
- callsByPeriod[cursor.getString(PROJECTION_NAME_PERIOD)] = blocked to contacted - blocked
- }
- cursor.close()
- db.close()
- return callsByPeriod
- }
- }
-
- private fun callsByPeriodToPeriodsList(
- callsByPeriod: Map<String, Pair<Int, Int>>,
- periodsCount: Int,
- periodUnit: TemporalUnit,
- javaPeriodFormat: String
- ): List<Pair<Int, Int>> {
- var currentDate = ZonedDateTime.now().minus(periodsCount.toLong(), periodUnit)
- val formatter = DateTimeFormatter.ofPattern(javaPeriodFormat)
- val calls = mutableListOf<Pair<Int, Int>>()
- for (i in 0 until periodsCount) {
- currentDate = currentDate.plus(1, periodUnit)
- val currentPeriod = formatter.format(currentDate)
- calls.add(callsByPeriod.getOrDefault(currentPeriod, 0 to 0))
- }
- return calls
- }
-
- fun getTrackersCallsOnPeriod(
- periodsCount: Int,
- periodUnit: TemporalUnit
- ): List<Pair<Int, Int>> {
- var sqlitePeriodFormat = "%Y%m"
- var javaPeriodFormat = "yyyyMM"
- if (periodUnit === ChronoUnit.MONTHS) {
- sqlitePeriodFormat = "%Y%m"
- javaPeriodFormat = "yyyyMM"
- } else if (periodUnit === ChronoUnit.DAYS) {
- sqlitePeriodFormat = "%Y%m%d"
- javaPeriodFormat = "yyyyMMdd"
- } else if (periodUnit === ChronoUnit.HOURS) {
- sqlitePeriodFormat = "%Y%m%d%H"
- javaPeriodFormat = "yyyyMMddHH"
- }
- val callsByPeriod = getCallsByPeriod(periodsCount, periodUnit, sqlitePeriodFormat)
- return callsByPeriodToPeriodsList(callsByPeriod, periodsCount, periodUnit, javaPeriodFormat)
- }
-
- fun getActiveTrackersByPeriod(periodsCount: Int, periodUnit: TemporalUnit): Int {
- synchronized(lock) {
- val minTimestamp = getPeriodStartTs(periodsCount, periodUnit)
- val db = writableDatabase
- val selection = "$COLUMN_NAME_TIMESTAMP >= ? AND " +
- "$COLUMN_NAME_NUMBER_CONTACTED > $COLUMN_NAME_NUMBER_BLOCKED"
- val selectionArg = arrayOf("" + minTimestamp)
- val projection =
- "COUNT(DISTINCT $COLUMN_NAME_TRACKER) $PROJECTION_NAME_TRACKERS_COUNT"
-
- val cursor = db.rawQuery(
- "SELECT $projection FROM $TABLE_NAME WHERE $selection",
- selectionArg
- )
- var count = 0
- if (cursor.moveToNext()) {
- count = cursor.getInt(0)
- }
- cursor.close()
- db.close()
- return count
- }
- }
-
- fun getContactedTrackersCount(): Int {
- synchronized(lock) {
- val db = readableDatabase
- var query = "SELECT DISTINCT $COLUMN_NAME_TRACKER FROM $TABLE_NAME"
-
- val cursor = db.rawQuery(query, arrayOf())
- var count = 0
- while (cursor.moveToNext()) {
- trackersRepository.getTracker(cursor.getString(COLUMN_NAME_TRACKER))?.let {
- count++
- }
- }
- cursor.close()
- db.close()
- return count
- }
- }
-
- fun getContactedTrackersCountByAppId(): Map<String, Int> {
- synchronized(lock) {
- val db = readableDatabase
- val projection = "$COLUMN_NAME_APPID, $COLUMN_NAME_TRACKER"
- val cursor = db.rawQuery(
- "SELECT DISTINCT $projection FROM $TABLE_NAME", // +
- arrayOf()
- )
- val countByApp = mutableMapOf<String, Int>()
- while (cursor.moveToNext()) {
- trackersRepository.getTracker(cursor.getString(COLUMN_NAME_TRACKER))?.let {
- val appId = cursor.getString(COLUMN_NAME_APPID)
- countByApp[appId] = countByApp.getOrDefault(appId, 0) + 1
- }
- }
- cursor.close()
- db.close()
- return countByApp
- }
- }
-
- fun getCallsByAppIds(periodCount: Int, periodUnit: TemporalUnit): Map<String, Pair<Int, Int>> {
- synchronized(lock) {
- val minTimestamp = getPeriodStartTs(periodCount, periodUnit)
- val db = readableDatabase
- val selection = "$COLUMN_NAME_TIMESTAMP >= ?"
- val selectionArg = arrayOf("" + minTimestamp)
- val projection = "$COLUMN_NAME_APPID, " +
- "SUM($COLUMN_NAME_NUMBER_CONTACTED) $PROJECTION_NAME_CONTACTED_SUM," +
- "SUM($COLUMN_NAME_NUMBER_BLOCKED) $PROJECTION_NAME_BLOCKED_SUM"
- val cursor = db.rawQuery(
- "SELECT $projection FROM $TABLE_NAME" +
- " WHERE $selection" +
- " GROUP BY $COLUMN_NAME_APPID",
- selectionArg
- )
- val callsByApp = HashMap<String, Pair<Int, Int>>()
- while (cursor.moveToNext()) {
- val contacted = cursor.getInt(PROJECTION_NAME_CONTACTED_SUM)
- val blocked = cursor.getInt(PROJECTION_NAME_BLOCKED_SUM)
- callsByApp[cursor.getString(COLUMN_NAME_APPID)] = blocked to contacted - blocked
- }
- cursor.close()
- db.close()
- return callsByApp
- }
- }
-
- fun getCalls(appId: String, periodCount: Int, periodUnit: TemporalUnit): Pair<Int, Int> {
- synchronized(lock) {
- val minTimestamp = getPeriodStartTs(periodCount, periodUnit)
- val db = readableDatabase
- val selection = "$COLUMN_NAME_APPID = ? AND " +
- "$COLUMN_NAME_TIMESTAMP >= ?"
- val selectionArg = arrayOf("" + appId, "" + minTimestamp)
- val projection =
- "SUM($COLUMN_NAME_NUMBER_CONTACTED) $PROJECTION_NAME_CONTACTED_SUM," +
- "SUM($COLUMN_NAME_NUMBER_BLOCKED) $PROJECTION_NAME_BLOCKED_SUM"
- val cursor = db.rawQuery(
- "SELECT $projection FROM $TABLE_NAME WHERE $selection",
- selectionArg
- )
- var calls: Pair<Int, Int> = 0 to 0
- if (cursor.moveToNext()) {
- val contacted = cursor.getInt(PROJECTION_NAME_CONTACTED_SUM)
- val blocked = cursor.getInt(PROJECTION_NAME_BLOCKED_SUM)
- calls = blocked to contacted - blocked
- }
- cursor.close()
- db.close()
- return calls
- }
- }
-
- fun getMostLeakedAppId(periodCount: Int, periodUnit: TemporalUnit): String {
- synchronized(lock) {
- val minTimestamp = getPeriodStartTs(periodCount, periodUnit)
- val db = readableDatabase
- val selection = "$COLUMN_NAME_TIMESTAMP >= ?"
- val selectionArg = arrayOf("" + minTimestamp)
- val projection = "$COLUMN_NAME_APPID, " +
- "SUM($COLUMN_NAME_NUMBER_CONTACTED - $COLUMN_NAME_NUMBER_BLOCKED) $PROJECTION_NAME_LEAKED_SUM"
- val cursor = db.rawQuery(
- "SELECT $projection FROM $TABLE_NAME" +
- " WHERE $selection" +
- " GROUP BY $COLUMN_NAME_APPID" +
- " ORDER BY $PROJECTION_NAME_LEAKED_SUM DESC LIMIT 1",
- selectionArg
- )
- var appId = ""
- if (cursor.moveToNext()) {
- appId = cursor.getString(COLUMN_NAME_APPID)
- }
- cursor.close()
- db.close()
- return appId
- }
- }
-
- fun logAccess(trackerId: String?, appId: String, blocked: Boolean) {
- synchronized(lock) {
- val currentHour = getCurrentHourTs()
- val db = writableDatabase
- val values = ContentValues()
- values.put(COLUMN_NAME_APPID, appId)
- values.put(COLUMN_NAME_TRACKER, trackerId)
- values.put(COLUMN_NAME_TIMESTAMP, currentHour)
-
- /*String query = "UPDATE product SET "+COLUMN_NAME_NUMBER_CONTACTED+" = "+COLUMN_NAME_NUMBER_CONTACTED+" + 1 ";
- if(blocked)
- query+=COLUMN_NAME_NUMBER_BLOCKED+" = "+COLUMN_NAME_NUMBER_BLOCKED+" + 1 ";
-*/
- val selection = "$COLUMN_NAME_TIMESTAMP = ? AND " +
- "$COLUMN_NAME_APPID = ? AND " +
- "$COLUMN_NAME_TRACKER = ? "
- val selectionArg = arrayOf("" + currentHour, "" + appId, trackerId)
- val cursor = db.query(
- TABLE_NAME,
- projection,
- selection,
- selectionArg,
- null,
- null,
- null
- )
- if (cursor.count > 0) {
- cursor.moveToFirst()
- val entry = cursorToEntry(cursor)
- if (blocked) values.put(
- COLUMN_NAME_NUMBER_BLOCKED,
- entry.sum_blocked + 1
- ) else values.put(COLUMN_NAME_NUMBER_BLOCKED, entry.sum_blocked)
- values.put(COLUMN_NAME_NUMBER_CONTACTED, entry.sum_contacted + 1)
- db.update(TABLE_NAME, values, selection, selectionArg)
-
- // db.execSQL(query, new String[]{""+hour, ""+day, ""+month, ""+year, ""+appUid, ""+trackerId});
- } else {
- if (blocked) values.put(
- COLUMN_NAME_NUMBER_BLOCKED,
- 1
- ) else values.put(COLUMN_NAME_NUMBER_BLOCKED, 0)
- values.put(COLUMN_NAME_NUMBER_CONTACTED, 1)
- db.insert(TABLE_NAME, null, values)
- }
- cursor.close()
- db.close()
- }
- }
-
- private fun cursorToEntry(cursor: Cursor): StatEntry {
- val entry = StatEntry()
- entry.timestamp = cursor.getLong(COLUMN_NAME_TIMESTAMP)
- entry.appId = cursor.getString(COLUMN_NAME_APPID)
- entry.sum_blocked = cursor.getInt(COLUMN_NAME_NUMBER_BLOCKED)
- entry.sum_contacted = cursor.getInt(COLUMN_NAME_NUMBER_CONTACTED)
- entry.tracker = cursor.getInt(COLUMN_NAME_TRACKER)
- return entry
- }
-
- fun getTrackers(appIds: List<String>?): List<Tracker> {
- synchronized(lock) {
- val columns = arrayOf(COLUMN_NAME_TRACKER, COLUMN_NAME_APPID)
- var selection: String? = null
-
- var selectionArg: Array<String>? = null
- appIds?.let { appIds ->
- selection = "$COLUMN_NAME_APPID IN (${appIds.joinToString(", ") { "'$it'" }})"
- selectionArg = arrayOf()
- }
-
- val db = readableDatabase
- val cursor = db.query(
- true,
- TABLE_NAME,
- columns,
- selection,
- selectionArg,
- null,
- null,
- null,
- null
- )
- val trackers: MutableList<Tracker> = ArrayList()
- while (cursor.moveToNext()) {
- val trackerId = cursor.getString(COLUMN_NAME_TRACKER)
- val tracker = trackersRepository.getTracker(trackerId)
- if (tracker != null) {
- trackers.add(tracker)
- }
- }
- cursor.close()
- db.close()
- return trackers
- }
- }
-
- class StatEntry {
- var appId = ""
- var sum_contacted = 0
- var sum_blocked = 0
- var timestamp: Long = 0
- var tracker = 0
- }
-
- private fun getCurrentHourTs(): Long {
- val hourInMs = TimeUnit.HOURS.toMillis(1L)
- val hourInS = TimeUnit.HOURS.toSeconds(1L)
- return System.currentTimeMillis() / hourInMs * hourInS
- }
-
- private fun getPeriodStartTs(
- periodsCount: Int,
- periodUnit: TemporalUnit
- ): Long {
- var start = ZonedDateTime.now()
- .minus(periodsCount.toLong(), periodUnit)
- .plus(1, periodUnit)
- var truncatePeriodUnit = periodUnit
- if (periodUnit === ChronoUnit.MONTHS) {
- start = start.withDayOfMonth(1)
- truncatePeriodUnit = ChronoUnit.DAYS
- }
- return start.truncatedTo(truncatePeriodUnit).toEpochSecond()
- }
-
- private fun Cursor.getInt(columnName: String): Int {
- val columnIndex = getColumnIndex(columnName)
- return if (columnIndex >= 0) getInt(columnIndex) else 0
- }
-
- private fun Cursor.getLong(columnName: String): Long {
- val columnIndex = getColumnIndex(columnName)
- return if (columnIndex >= 0) getLong(columnIndex) else 0
- }
-
- private fun Cursor.getString(columnName: String): String {
- val columnIndex = getColumnIndex(columnName)
- return if (columnIndex >= 0) {
- getStringOrNull(columnIndex) ?: ""
- } else ""
- }
-}
diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsRepository.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsRepository.kt
deleted file mode 100644
index 8f02adb..0000000
--- a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsRepository.kt
+++ /dev/null
@@ -1,105 +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.privacymodules.trackers.data
-
-import android.content.Context
-import foundation.e.privacymodules.permissions.data.ApplicationDescription
-import foundation.e.privacymodules.trackers.api.Tracker
-import java.time.temporal.TemporalUnit
-
-class StatsRepository private constructor(context: Context) {
- private val database: StatsDatabase
- private var newDataCallback: (() -> Unit)? = null
- private var getAppByUid: ((Int) -> ApplicationDescription?)? = null
- private var getAppByAPId: ((String) -> ApplicationDescription?)? = null
-
- companion object {
- private var instance: StatsRepository? = null
- fun getInstance(context: Context): StatsRepository {
- return instance ?: StatsRepository(context).apply { instance = this }
- }
- }
-
- fun setAppGetters(
- getAppByUid: (Int) -> ApplicationDescription?,
- getAppByAPId: (String) -> ApplicationDescription?
- ) {
- this.getAppByUid = getAppByUid
- this.getAppByAPId = getAppByAPId
- }
-
- init {
- database = StatsDatabase(context)
- }
-
- fun setNewDataCallback(callback: () -> Unit) {
- newDataCallback = callback
- }
-
- fun logAccess(trackerId: String?, appUid: Int, blocked: Boolean) {
- getAppByUid?.invoke(appUid)?.let { app ->
- database.logAccess(trackerId, app.apId, blocked)
- newDataCallback?.invoke()
- }
- }
-
- fun getTrackersCallsOnPeriod(
- periodsCount: Int,
- periodUnit: TemporalUnit
- ): List<Pair<Int, Int>> {
- return database.getTrackersCallsOnPeriod(periodsCount, periodUnit)
- }
-
- fun getActiveTrackersByPeriod(periodsCount: Int, periodUnit: TemporalUnit): Int {
- return database.getActiveTrackersByPeriod(periodsCount, periodUnit)
- }
-
- fun getContactedTrackersCountByApp(): Map<ApplicationDescription, Int> {
- return database.getContactedTrackersCountByAppId().mapByAppIdToApp()
- }
-
- fun getContactedTrackersCount(): Int {
- return database.getContactedTrackersCount()
- }
-
- fun getTrackers(apps: List<ApplicationDescription>?): List<Tracker> {
- return database.getTrackers(apps?.map { it.apId })
- }
-
- fun getCallsByApps(
- periodCount: Int,
- periodUnit: TemporalUnit
- ): Map<ApplicationDescription, Pair<Int, Int>> {
- return database.getCallsByAppIds(periodCount, periodUnit).mapByAppIdToApp()
- }
-
- fun getCalls(app: ApplicationDescription, periodCount: Int, periodUnit: TemporalUnit): Pair<Int, Int> {
- return database.getCalls(app.apId, periodCount, periodUnit)
- }
-
- fun getMostLeakedApp(periodCount: Int, periodUnit: TemporalUnit): ApplicationDescription? {
- return getAppByAPId?.invoke(database.getMostLeakedAppId(periodCount, periodUnit))
- }
-
- private fun <K> Map<String, K>.mapByAppIdToApp(): Map<ApplicationDescription, K> {
- return entries.mapNotNull { (apId, value) ->
- getAppByAPId?.invoke(apId)?.let { it to value }
- }.toMap()
- }
-}
diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/TrackersRepository.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/TrackersRepository.kt
deleted file mode 100644
index 994bccf..0000000
--- a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/TrackersRepository.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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.privacymodules.trackers.data
-
-import foundation.e.privacymodules.trackers.api.Tracker
-
-class TrackersRepository private constructor() {
- private var trackersById: Map<String, Tracker> = HashMap()
- private var hostnameToId: Map<String, String> = HashMap()
-
- companion object {
- private var instance: TrackersRepository? = null
- fun getInstance(): TrackersRepository {
- return instance ?: TrackersRepository().apply { instance = this }
- }
- }
-
- fun setTrackersList(list: List<Tracker>) {
- val trackersById: MutableMap<String, Tracker> = HashMap()
- val hostnameToId: MutableMap<String, String> = HashMap()
- list.forEach { tracker ->
- trackersById[tracker.id] = tracker
- for (hostname in tracker.hostnames) {
- hostnameToId[hostname] = tracker.id
- }
- }
- this.trackersById = trackersById
- this.hostnameToId = hostnameToId
- }
-
- fun isTracker(hostname: String?): Boolean {
- return hostnameToId.containsKey(hostname)
- }
-
- fun getTrackerId(hostname: String?): String? {
- return hostnameToId[hostname]
- }
-
- fun getTracker(id: String?): Tracker? {
- return trackersById[id]
- }
-}
diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/WhitelistRepository.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/WhitelistRepository.kt
deleted file mode 100644
index 2763d06..0000000
--- a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/WhitelistRepository.kt
+++ /dev/null
@@ -1,207 +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.privacymodules.trackers.data
-
-import android.content.Context
-import android.content.SharedPreferences
-import foundation.e.privacymodules.permissions.data.ApplicationDescription
-import foundation.e.privacymodules.trackers.api.Tracker
-import java.io.File
-
-class WhitelistRepository private constructor(context: Context) {
- private var appsWhitelist: Set<String> = HashSet()
- private var appUidsWhitelist: Set<Int> = HashSet()
-
- private var trackersWhitelistByApp: MutableMap<String, MutableSet<String>> = HashMap()
- private var trackersWhitelistByUid: Map<Int, MutableSet<String>> = HashMap()
-
- private val prefs: SharedPreferences
- private var getAppByAPId: ((String) -> ApplicationDescription?)? = null
-
- companion object {
- private const val SHARED_PREFS_FILE = "trackers_whitelist_v2"
- private const val KEY_BLOCKING_ENABLED = "blocking_enabled"
- private const val KEY_APPS_WHITELIST = "apps_whitelist"
- private const val KEY_APP_TRACKERS_WHITELIST_PREFIX = "app_trackers_whitelist_"
-
- private const val SHARED_PREFS_FILE_V1 = "trackers_whitelist.prefs"
-
- private var instance: WhitelistRepository? = null
- fun getInstance(context: Context): WhitelistRepository {
- return instance ?: WhitelistRepository(context).apply { instance = this }
- }
- }
-
- init {
- prefs = context.getSharedPreferences(SHARED_PREFS_FILE, Context.MODE_PRIVATE)
- reloadCache()
- }
-
- fun setAppGetters(
- context: Context,
- getAppByAPId: (String) -> ApplicationDescription?,
- getAppByUid: (Int) -> ApplicationDescription?
- ) {
- this.getAppByAPId = getAppByAPId
- migrate(context, getAppByUid)
- }
-
- private fun migrate(context: Context, getAppByUid: (Int) -> ApplicationDescription?) {
- if (context.sharedPreferencesExists(SHARED_PREFS_FILE_V1)) {
- migrate1To2(context, getAppByUid)
- }
- }
-
- private fun Context.sharedPreferencesExists(fileName: String): Boolean {
- return File(
- "${applicationInfo.dataDir}/shared_prefs/$fileName.xml"
- ).exists()
- }
-
- private fun migrate1To2(context: Context, getAppByUid: (Int) -> ApplicationDescription?) {
- val prefsV1 = context.getSharedPreferences(SHARED_PREFS_FILE_V1, Context.MODE_PRIVATE)
- val editorV2 = prefs.edit()
-
- editorV2.putBoolean(KEY_BLOCKING_ENABLED, prefsV1.getBoolean(KEY_BLOCKING_ENABLED, false))
-
- val apIds = prefsV1.getStringSet(KEY_APPS_WHITELIST, HashSet())?.mapNotNull {
- try {
- val uid = it.toInt()
- getAppByUid(uid)?.apId
- } catch (e: Exception) { null }
- }?.toSet() ?: HashSet()
-
- editorV2.putStringSet(KEY_APPS_WHITELIST, apIds)
-
- prefsV1.all.keys.forEach { key ->
- if (key.startsWith(KEY_APP_TRACKERS_WHITELIST_PREFIX)) {
- try {
- val uid = key.substring(KEY_APP_TRACKERS_WHITELIST_PREFIX.length).toInt()
- val apId = getAppByUid(uid)?.apId
- apId?.let {
- val trackers = prefsV1.getStringSet(key, emptySet())
- editorV2.putStringSet(buildAppTrackersKey(apId), trackers)
- }
- } catch (e: Exception) { }
- }
- }
- editorV2.commit()
-
- context.deleteSharedPreferences(SHARED_PREFS_FILE_V1)
-
- reloadCache()
- }
-
- private fun reloadCache() {
- isBlockingEnabled = prefs.getBoolean(KEY_BLOCKING_ENABLED, false)
- reloadAppsWhiteList()
- reloadAllAppTrackersWhiteList()
- }
-
- private fun reloadAppsWhiteList() {
- appsWhitelist = prefs.getStringSet(KEY_APPS_WHITELIST, HashSet()) ?: HashSet()
- appUidsWhitelist = appsWhitelist
- .mapNotNull { apId -> getAppByAPId?.invoke(apId)?.uid }
- .toSet()
- }
-
- private fun refreshAppUidTrackersWhiteList() {
- trackersWhitelistByUid = trackersWhitelistByApp.mapNotNull { (apId, value) ->
- getAppByAPId?.invoke(apId)?.uid?.let { uid ->
- uid to value
- }
- }.toMap()
- }
- private fun reloadAllAppTrackersWhiteList() {
- val map: MutableMap<String, MutableSet<String>> = HashMap()
- prefs.all.keys.forEach { key ->
- if (key.startsWith(KEY_APP_TRACKERS_WHITELIST_PREFIX)) {
- map[key.substring(KEY_APP_TRACKERS_WHITELIST_PREFIX.length)] = (
- prefs.getStringSet(key, HashSet()) ?: HashSet()
- )
- }
- }
- trackersWhitelistByApp = map
- }
-
- var isBlockingEnabled: Boolean = false
- get() = field
- set(enabled) {
- prefs.edit().putBoolean(KEY_BLOCKING_ENABLED, enabled).apply()
- field = enabled
- }
-
- fun setWhiteListed(apId: String, isWhiteListed: Boolean) {
- val current = prefs.getStringSet(KEY_APPS_WHITELIST, HashSet())?.toHashSet() ?: HashSet()
-
- if (isWhiteListed) {
- current.add(apId)
- } else {
- current.remove(apId)
- }
- prefs.edit().putStringSet(KEY_APPS_WHITELIST, current).commit()
- reloadAppsWhiteList()
- }
-
- private fun buildAppTrackersKey(apId: String): String {
- return KEY_APP_TRACKERS_WHITELIST_PREFIX + apId
- }
-
- fun setWhiteListed(tracker: Tracker, apId: String, isWhiteListed: Boolean) {
- val trackers = trackersWhitelistByApp.getOrDefault(apId, HashSet())
- trackersWhitelistByApp[apId] = trackers
-
- if (isWhiteListed) {
- trackers.add(tracker.id)
- } else {
- trackers.remove(tracker.id)
- }
- refreshAppUidTrackersWhiteList()
- prefs.edit().putStringSet(buildAppTrackersKey(apId), trackers).commit()
- }
-
- fun isAppWhiteListed(app: ApplicationDescription): Boolean {
- return appsWhitelist.contains(app.apId)
- }
-
- fun isWhiteListed(appUid: Int, trackerId: String?): Boolean {
- return appUidsWhitelist.contains(appUid) ||
- trackersWhitelistByUid.getOrDefault(appUid, HashSet()).contains(trackerId)
- }
-
- fun areWhiteListEmpty(): Boolean {
- return appsWhitelist.isEmpty() && trackersWhitelistByApp.all { (_, trackers) -> trackers.isEmpty() }
- }
-
- fun getWhiteListedApp(): List<ApplicationDescription> {
- return getAppByAPId?.let {
- appsWhitelist.mapNotNull(it)
- } ?: emptyList()
- }
-
- fun getWhiteListForApp(app: ApplicationDescription): List<String> {
- return trackersWhitelistByApp[app.apId]?.toList() ?: emptyList()
- }
-
- fun clearWhiteList(apId: String) {
- trackersWhitelistByApp.remove(apId)
- refreshAppUidTrackersWhiteList()
- prefs.edit().remove(buildAppTrackersKey(apId)).commit()
- }
-}