diff options
author | Guillaume Jacquart <guillaume.jacquart@hoodbrains.com> | 2023-09-12 06:17:39 +0000 |
---|---|---|
committer | Guillaume Jacquart <guillaume.jacquart@hoodbrains.com> | 2023-09-12 06:17:39 +0000 |
commit | 53f4a9ce311d612d43fa770cf7e8f8e98fbb43a0 (patch) | |
tree | 59c58e58cfef0e370f39bd9c150e36c6dfcb50c0 /trackers/src/main/java/foundation/e/privacymodules | |
parent | 1a77e3924bc78eabca7b859ef62be30bbf2476ad (diff) |
2: organise module with clean archi, use Koin for injection.
Diffstat (limited to 'trackers/src/main/java/foundation/e/privacymodules')
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() - } -} |