summaryrefslogtreecommitdiff
path: root/trackers/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'trackers/src/main')
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.java173
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.kt164
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerService.java86
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerService.kt80
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/ForegroundStarter.java42
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/ForegroundStarter.kt46
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.java83
-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.java125
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/api/BlockTrackersPrivacyModule.kt91
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/api/IBlockTrackersPrivacyModule.kt95
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/api/ITrackTrackersPrivacyModule.kt97
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.java150
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.kt113
-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.java507
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsDatabase.kt451
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsRepository.java95
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsRepository.kt83
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/data/TrackersRepository.java71
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/data/TrackersRepository.kt58
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/data/WhitelistRepository.java153
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/data/WhitelistRepository.kt128
23 files changed, 1503 insertions, 1485 deletions
diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.java b/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.java
deleted file mode 100644
index 80f00c1..0000000
--- a/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- Copyright (C) 2022 ECORP
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- 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, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
- */
-/*
- PersonalDNSFilter 1.5
- Copyright (C) 2017 Ingo Zenz
- Copyright (C) 2021 ECORP
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- 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, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
- */
-
-package foundation.e.privacymodules.trackers;
-
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.net.LocalServerSocket;
-import android.net.LocalSocket;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
-import android.util.Log;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-
-import foundation.e.privacymodules.trackers.data.TrackersRepository;
-import foundation.e.privacymodules.trackers.data.WhitelistRepository;
-
-
-public class DNSBlockerRunnable implements Runnable {
-
- LocalServerSocket resolverReceiver;
- boolean stopped = false;
- private final TrackersLogger trackersLogger;
- private final TrackersRepository trackersRepository;
- private final WhitelistRepository whitelistRepository;
-
- private int eBrowserAppUid = -1;
-
- private final String TAG = DNSBlockerRunnable.class.getName();
- private static final String SOCKET_NAME = "foundation.e.advancedprivacy";
-
-
- public DNSBlockerRunnable(Context ct, TrackersLogger trackersLogger, TrackersRepository trackersRepository, WhitelistRepository whitelistRepository) {
- this.trackersLogger = trackersLogger;
- this.trackersRepository = trackersRepository;
- this.whitelistRepository = whitelistRepository;
- initEBrowserDoTFix(ct);
- }
-
- public synchronized void stop() {
- stopped = true;
- closeSocket();
- }
-
- private void 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.getFileDescriptor(), OsConstants.SHUT_RDWR);
- resolverReceiver.close();
- resolverReceiver = null;
- } catch (ErrnoException e) {
- 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 (Exception e) {
- Log.e(TAG, "Exception: cannot close DNS port on stop" + SOCKET_NAME + "!", e);
- }
- }
- }
-
- @Override
- public void run() {
- try {
- resolverReceiver = new LocalServerSocket(SOCKET_NAME);
- } catch (IOException eio) {
- Log.e(TAG, "Exception:Cannot open DNS port " + SOCKET_NAME + "!", eio);
- return;
- }
- Log.d(TAG, "DNSFilterProxy running on port " + SOCKET_NAME + "!");
-
- while (!stopped) {
- try {
- LocalSocket socket = resolverReceiver.accept();
-
- BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
- String line = reader.readLine();
- String[] params = line.split(",");
- OutputStream output = socket.getOutputStream();
- PrintWriter writer = new PrintWriter(output, true);
-
- String domainName = params[0];
- int appUid = Integer.parseInt(params[1]);
- boolean isBlocked = false;
-
- if (isEBrowserDoTBlockFix(appUid, domainName)) {
- isBlocked = true;
- } else if (trackersRepository.isTracker(domainName)) {
- String 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 (IOException e) {
- Log.w(TAG, "Exception while listening DNS resolver", e);
- }
- }
- }
-
- private void initEBrowserDoTFix(Context context) {
- try {
- eBrowserAppUid = context.getPackageManager().getApplicationInfo("foundation.e.browser", 0).uid;
- } catch (PackageManager.NameNotFoundException e) {
- Log.i(TAG, "no E Browser package found.");
- }
- }
-
- private static final String E_BROWSER_DOT_SERVER = "chrome.cloudflare-dns.com";
- private boolean isEBrowserDoTBlockFix(int appUid, String hostname) {
- return appUid == eBrowserAppUid && E_BROWSER_DOT_SERVER.equals(hostname);
- }
-
- private boolean shouldBlock(int appUid, String trackerId) {
- return whitelistRepository.isBlockingEnabled() &&
- !whitelistRepository.isAppWhiteListed(appUid) &&
- !whitelistRepository.isTrackerWhiteListedForApp(trackerId, appUid);
- }
-}
diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.kt
new file mode 100644
index 0000000..01ae5b7
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.kt
@@ -0,0 +1,164 @@
+/*
+ Copyright (C) 2022 ECORP
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+ */
+/*
+ PersonalDNSFilter 1.5
+ Copyright (C) 2017 Ingo Zenz
+ Copyright (C) 2021 ECORP
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+ */
+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(
+ ct: 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(ct)
+ }
+
+ @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.isAppWhiteListed(appUid) &&
+ !whitelistRepository.isTrackerWhiteListedForApp(trackerId, appUid)
+ }
+
+
+} \ No newline at end of file
diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerService.java b/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerService.java
deleted file mode 100644
index 6250621..0000000
--- a/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerService.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- Copyright (C) 2021 ECORP
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- 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, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
- */
-
-
-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;
-
-public class DNSBlockerService extends Service {
- private static final String TAG = "DNSBlockerService";
- private static DNSBlockerRunnable sDNSBlocker;
- private TrackersLogger trackersLogger;
-
- public static final String ACTION_START = "foundation.e.privacymodules.trackers.intent.action.START";
-
- public static final String EXTRA_ENABLE_NOTIFICATION = "foundation.e.privacymodules.trackers.intent.extra.ENABLED_NOTIFICATION";
-
- public DNSBlockerService() {
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- // TODO: Return the communication channel to the service.
- throw new UnsupportedOperationException("Not yet implemented");
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- if (intent != null && intent.getBooleanExtra(EXTRA_ENABLE_NOTIFICATION, true)) {
- ForegroundStarter.startForeground(this);
- }
-
- if (intent != null && ACTION_START.equals(intent.getAction())) {
- stop();
- start();
- }
-
- return START_STICKY;
- }
-
- private void start() {
- try {
- trackersLogger = new TrackersLogger(this);
- sDNSBlocker = new DNSBlockerRunnable(this, trackersLogger,
- TrackersRepository.getInstance(), WhitelistRepository.getInstance(this));
-
- new Thread(sDNSBlocker).start();
- } catch(Exception e) {
- Log.e(TAG, "Error while starting DNSBlocker service", e);
- stop();
- }
- }
-
- private void stop() {
- if (sDNSBlocker != null) {
- sDNSBlocker.stop();
- }
- sDNSBlocker = null;
- if (trackersLogger != null) {
- trackersLogger.stop();
- }
- trackersLogger = null;
- }
-}
diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerService.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerService.kt
new file mode 100644
index 0000000..3162422
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerService.kt
@@ -0,0 +1,80 @@
+/*
+ Copyright (C) 2021 ECORP
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+ */
+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 (intent.getBooleanExtra(EXTRA_ENABLE_NOTIFICATION, true)) {
+ ForegroundStarter.startForeground(this)
+ }
+ if (ACTION_START == intent.action) {
+ stop()
+ start()
+ }
+ return START_STICKY
+ }
+
+ 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.java b/trackers/src/main/java/foundation/e/privacymodules/trackers/ForegroundStarter.java
deleted file mode 100644
index 1563163..0000000
--- a/trackers/src/main/java/foundation/e/privacymodules/trackers/ForegroundStarter.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- Copyright (C) 2021 ECORP
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- 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, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
- */
-
-package foundation.e.privacymodules.trackers;
-
-import static android.content.Context.NOTIFICATION_SERVICE;
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.Service;
-import android.os.Build;
-
-
-public class ForegroundStarter {
- private static final String NOTIFICATION_CHANNEL_ID = "blocker_service";
- public static void startForeground(Service service){
- NotificationManager mNotificationManager = (NotificationManager) service.getSystemService(NOTIFICATION_SERVICE);
- if (Build.VERSION.SDK_INT >= 26) {
- mNotificationManager.createNotificationChannel(new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_LOW));
- Notification notification = new 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/ForegroundStarter.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/ForegroundStarter.kt
new file mode 100644
index 0000000..30bba7b
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/ForegroundStarter.kt
@@ -0,0 +1,46 @@
+/*
+ Copyright (C) 2021 ECORP
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+ */
+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)
+ }
+ }
+} \ No newline at end of file
diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.java b/trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.java
deleted file mode 100644
index 3710253..0000000
--- a/trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- Copyright (C) 2022 ECORP
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- 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, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
- */
-package foundation.e.privacymodules.trackers;
-
-import android.content.Context;
-import android.util.Log;
-
-import java.util.concurrent.LinkedBlockingQueue;
-
-import foundation.e.privacymodules.trackers.data.StatsRepository;
-
-
-public class TrackersLogger {
- private static final String TAG = "TrackerModule";
- private StatsRepository statsRepository;
-
- private LinkedBlockingQueue<DetectedTracker> queue;
- private boolean stopped = false;
-
-
- public TrackersLogger(Context context) {
- statsRepository = StatsRepository.getInstance(context);
- queue = new LinkedBlockingQueue<DetectedTracker>();
- startWriteLogLoop();
- }
-
- public void stop() {
- stopped = true;
- }
-
- public void logAccess(String trackerId, int appId, boolean wasBlocked) {
- queue.offer(new DetectedTracker(trackerId, appId, wasBlocked));
- }
-
- private void startWriteLogLoop() {
- Runnable writeLogRunner = new Runnable() {
- @Override
- public void run() {
- while(!stopped) {
- try {
- logAccess(queue.take());
- } catch (InterruptedException e) {
- Log.e(TAG, "writeLogLoop detectedTrackersQueue.take() interrupted: ", e);
- }
- }
- }
- };
- new Thread(writeLogRunner).start();
- }
-
-
- public void logAccess(DetectedTracker detectedTracker) {
- statsRepository.logAccess(detectedTracker.trackerId, detectedTracker.appUid, detectedTracker.wasBlocked);
- }
-
- private class DetectedTracker {
- String trackerId;
- int appUid;
- boolean wasBlocked;
-
- public DetectedTracker(String trackerId, int appUid, boolean wasBlocked) {
- this.trackerId = trackerId;
- this.appUid = appUid;
- this.wasBlocked = wasBlocked;
- }
- }
-}
diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.kt
new file mode 100644
index 0000000..6d2abec
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.kt
@@ -0,0 +1,69 @@
+/*
+ Copyright (C) 2022 ECORP
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+ */
+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?, appId: Int, wasBlocked: Boolean) {
+ queue.offer(DetectedTracker(trackerId, appId, 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)
+} \ No newline at end of file
diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/BlockTrackersPrivacyModule.java b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/BlockTrackersPrivacyModule.java
deleted file mode 100644
index ea62766..0000000
--- a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/BlockTrackersPrivacyModule.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- Copyright (C) 2021 ECORP
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- 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, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
- */
-
-
-package foundation.e.privacymodules.trackers.api;
-
-import android.content.Context;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import foundation.e.privacymodules.permissions.data.ApplicationDescription;
-import foundation.e.privacymodules.trackers.IBlockTrackersPrivacyModule;
-import foundation.e.privacymodules.trackers.Tracker;
-import foundation.e.privacymodules.trackers.data.TrackersRepository;
-import foundation.e.privacymodules.trackers.data.WhitelistRepository;
-
-public class BlockTrackersPrivacyModule implements IBlockTrackersPrivacyModule {
-
- private final Context mContext;
- private List<Listener> mListeners = new ArrayList<>();
- private static BlockTrackersPrivacyModule sBlockTrackersPrivacyModule;
-
- private TrackersRepository trackersRepository;
- private WhitelistRepository whitelistRepository;
-
- public BlockTrackersPrivacyModule(Context context) {
- mContext = context;
- trackersRepository = TrackersRepository.getInstance();
- whitelistRepository = WhitelistRepository.getInstance(mContext);
- }
-
- public static BlockTrackersPrivacyModule getInstance(Context ct){
- if(sBlockTrackersPrivacyModule == null){
- sBlockTrackersPrivacyModule = new BlockTrackersPrivacyModule(ct);
- }
- return sBlockTrackersPrivacyModule;
- }
-
- @Override
- public void addListener(Listener listener) {
- mListeners.add(listener);
- }
-
- @Override
- public void clearListeners() {
- mListeners.clear();
- }
-
- @Override
- public void disableBlocking() {
- whitelistRepository.setBlockingEnabled(false);
- for(Listener listener:mListeners){
- listener.onBlockingToggle(false);
- }
- }
-
- @Override
- public void enableBlocking() {
- whitelistRepository.setBlockingEnabled(true);
- for(Listener listener:mListeners){
- listener.onBlockingToggle(true);
- }
- }
-
- @Override
- public List<Tracker> getWhiteList(int appUid) {
- List<Tracker> trackers = new ArrayList();
- for (String trackerId: whitelistRepository.getWhiteListForApp(appUid)) {
- trackers.add(trackersRepository.getTracker(trackerId));
- }
- return trackers;
- }
-
- @Override
- public List<Integer> getWhiteListedApp() {
- return whitelistRepository.getWhiteListedApp();
- }
-
- @Override
- public boolean isBlockingEnabled() {
- return whitelistRepository.isBlockingEnabled();
- }
-
- @Override
- public boolean isWhiteListEmpty() {
- return whitelistRepository.areWhiteListEmpty();
- }
-
- @Override
- public boolean isWhitelisted(int appUid) {
- return whitelistRepository.isAppWhiteListed(appUid);
- }
-
- @Override
- public void removeListener(Listener listener) {
- mListeners.remove(listener);
- }
-
- @Override
- public void setWhiteListed(Tracker tracker, int appUid, boolean isWhiteListed) {
- whitelistRepository.setWhiteListed(tracker, appUid, isWhiteListed);
- }
-
- @Override
- public void setWhiteListed(int appUid, boolean isWhiteListed) {
- whitelistRepository.setWhiteListed(appUid, isWhiteListed);
- }
-}
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
new file mode 100644
index 0000000..46729fd
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/BlockTrackersPrivacyModule.kt
@@ -0,0 +1,91 @@
+/*
+ Copyright (C) 2022 ECORP
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+ */
+package foundation.e.privacymodules.trackers.api
+
+import foundation.e.privacymodules.trackers.data.WhitelistRepository
+import android.content.Context
+import foundation.e.privacymodules.trackers.data.TrackersRepository
+
+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(appUid: Int): List<Tracker> {
+ return whitelistRepository.getWhiteListForApp(appUid).mapNotNull {
+ trackersRepository.getTracker(it)
+ }
+ }
+
+ override fun getWhiteListedApp(): List<Int> {
+ return whitelistRepository.whiteListedApp
+ }
+
+ override fun isBlockingEnabled(): Boolean {
+ return whitelistRepository.isBlockingEnabled
+ }
+
+ override fun isWhiteListEmpty(): Boolean {
+ return whitelistRepository.areWhiteListEmpty()
+ }
+
+ override fun isWhitelisted(appUid: Int): Boolean {
+ return whitelistRepository.isAppWhiteListed(appUid)
+ }
+
+ override fun removeListener(listener: IBlockTrackersPrivacyModule.Listener) {
+ mListeners.remove(listener)
+ }
+
+ override fun setWhiteListed(tracker: Tracker, appUid: Int, isWhiteListed: Boolean) {
+ whitelistRepository.setWhiteListed(tracker, appUid, isWhiteListed)
+ }
+
+ override fun setWhiteListed(appUid: Int, isWhiteListed: Boolean) {
+ whitelistRepository.setWhiteListed(appUid, isWhiteListed)
+ }
+
+
+} \ No newline at end of file
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
new file mode 100644
index 0000000..b07e210
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/IBlockTrackersPrivacyModule.kt
@@ -0,0 +1,95 @@
+/*
+ * 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
+
+/**
+ * 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 appUid the uid of the app
+ * @param isWhiteListed true, the app will appears in whitelist, false, it won't
+ */
+ fun setWhiteListed(appUid: Int, isWhiteListed: Boolean)
+
+ /**
+ * Set or unset in whitelist the specifid tracked, for the App specified by its uid.
+ * @param tracker the tracker
+ * @param appUid the uid of the app
+ * @param isWhiteListed true, the app will appears in whitelist, false, it won't
+ */
+ fun setWhiteListed(tracker: Tracker, appUid: Int, 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<Int>
+
+ /**
+ * Return true if the App is whitelisted for trackers blocking.
+ */
+ fun isWhitelisted(appUid: Int): Boolean
+
+
+ /**
+ * List the white listed trackers for an App specified by it uid
+ */
+ fun getWhiteList(appUid: Int): List<Tracker>
+
+ /**
+ * 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
new file mode 100644
index 0000000..f7beebd
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/ITrackTrackersPrivacyModule.kt
@@ -0,0 +1,97 @@
+/*
+ * 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
+
+/**
+ * Get reporting about trackers calls.
+ */
+interface ITrackTrackersPrivacyModule {
+
+ fun start(trackers: List<Tracker>, enableNotification: Boolean = true)
+
+ /**
+ * List all the trackers encountered for a specific app.
+ */
+ fun getTrackersForApp(appUid: Int): List<Tracker>
+
+ /**
+ * Return the number of encountered trackers since "ever", for the given [appUids],
+ * or all apps if [appUids] is null
+ */
+ fun getTrackersCount(appUids: List<Int>? = null): Int
+
+ /**
+ * Return the number of encountere trackers since "ever", for each app uid.
+ */
+ fun getTrackersCountByApp(): Map<Int, 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<Int, Pair<Int, Int>>
+
+ fun getPastDayTrackersCallsForApp(appUid: Int): Pair<Int, Int>
+
+ fun getPastDayMostLeakedApp(): Int
+
+ 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()
+} \ No newline at end of file
diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.java b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.java
deleted file mode 100644
index 38b2c8f..0000000
--- a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- Copyright (C) 2021 ECORP
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- 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, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
- */
-
-
-package foundation.e.privacymodules.trackers.api;
-
-import android.content.Context;
-import android.content.Intent;
-
-
-import org.jetbrains.annotations.NotNull;
-
-import java.time.temporal.ChronoUnit;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-import foundation.e.privacymodules.trackers.DNSBlockerService;
-import foundation.e.privacymodules.trackers.ITrackTrackersPrivacyModule;
-import foundation.e.privacymodules.trackers.Tracker;
-import foundation.e.privacymodules.trackers.data.StatsRepository;
-import foundation.e.privacymodules.trackers.data.TrackersRepository;
-import kotlin.Pair;
-
-public class TrackTrackersPrivacyModule implements ITrackTrackersPrivacyModule {
-
- private static TrackTrackersPrivacyModule sTrackTrackersPrivacyModule;
- private final Context mContext;
- private StatsRepository statsRepository;
- private List<ITrackTrackersPrivacyModule.Listener> mListeners = new ArrayList();
-
- public TrackTrackersPrivacyModule(Context context) {
- mContext = context;
- statsRepository = StatsRepository.getInstance(context);
- statsRepository.setNewDataCallback((newData) -> {
- mListeners.forEach((listener) -> { listener.onNewData(); });
- });
- }
-
- public static TrackTrackersPrivacyModule getInstance(Context context){
- if(sTrackTrackersPrivacyModule == null){
- sTrackTrackersPrivacyModule = new TrackTrackersPrivacyModule(context);
- }
- return sTrackTrackersPrivacyModule;
- }
-
- public void start(List<Tracker> trackers, boolean enableNotification) {
- TrackersRepository.getInstance().setTrackersList(trackers);
-
- Intent intent = new Intent(mContext, DNSBlockerService.class);
- intent.setAction(DNSBlockerService.ACTION_START);
- intent.putExtra(DNSBlockerService.EXTRA_ENABLE_NOTIFICATION, enableNotification);
- mContext.startService(intent);
- }
-
- @NotNull
- @Override
- public List<Pair<Integer, Integer>> getPastDayTrackersCalls() {
- return statsRepository.getTrackersCallsOnPeriod(24, ChronoUnit.HOURS);
- }
-
- @Override
- public List<Pair<Integer, Integer>> getPastMonthTrackersCalls() {
- return statsRepository.getTrackersCallsOnPeriod(30, ChronoUnit.DAYS);
- }
-
- @Override
- public List<Pair<Integer, Integer>> getPastYearTrackersCalls() {
- return statsRepository.getTrackersCallsOnPeriod(12, ChronoUnit.MONTHS);
- }
-
- @Override
- public int getTrackersCount() {
- return statsRepository.getContactedTrackersCount();
- }
-
- @Override
- public Map<Integer, Integer> getTrackersCountByApp() {
- return statsRepository.getContactedTrackersCountByApp();
- }
-
- @Override
- public List<Tracker> getTrackersForApp(int i) {
- return statsRepository.getAllTrackersOfApp(i);
- }
-
-
- @Override
- public int getPastDayTrackersCount() {
- return statsRepository.getActiveTrackersByPeriod(24, ChronoUnit.HOURS);
- }
-
- @Override
- public int getPastMonthTrackersCount() {
- return statsRepository.getActiveTrackersByPeriod(30, ChronoUnit.DAYS);
- }
-
- @Override
- public int getPastYearTrackersCount() {
- return statsRepository.getActiveTrackersByPeriod(12, ChronoUnit.MONTHS);
- }
-
- @Override
- public int getPastDayMostLeakedApp() {
- return statsRepository.getMostLeakedApp(24, ChronoUnit.HOURS);
- }
-
- @NotNull
- @Override
- public Map<Integer, Pair<Integer, Integer>> getPastDayTrackersCallsByApps() {
- return statsRepository.getCallsByApps(24, ChronoUnit.HOURS);
- }
-
- @NotNull
- @Override
- public Pair<Integer, Integer> getPastDayTrackersCallsForApp(int appUid) {
- return statsRepository.getCalls(appUid, 24, ChronoUnit.HOURS);
- }
-
- @Override
- public void addListener(ITrackTrackersPrivacyModule.Listener listener) {
- mListeners.add(listener);
- }
-
- @Override
- public void removeListener(ITrackTrackersPrivacyModule.Listener listener) {
- mListeners.remove(listener);
- }
-
- @Override
- public void clearListeners() {
- mListeners.clear();
- }
-}
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
new file mode 100644
index 0000000..e0672cc
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.kt
@@ -0,0 +1,113 @@
+/*
+ Copyright (C) 2021 ECORP
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+ */
+package foundation.e.privacymodules.trackers.api
+
+import android.content.Context
+import android.content.Intent
+import foundation.e.privacymodules.trackers.DNSBlockerService
+import foundation.e.privacymodules.trackers.data.StatsRepository
+import foundation.e.privacymodules.trackers.data.TrackersRepository
+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>, enableNotification: Boolean) {
+ TrackersRepository.getInstance().setTrackersList(trackers)
+ 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(appUids: List<Int>?): Int {
+ return statsRepository.getContactedTrackersCount(appUids)
+ }
+
+ override fun getTrackersCountByApp(): Map<Int, Int> {
+ return statsRepository.getContactedTrackersCountByApp()
+ }
+
+ override fun getTrackersForApp(appUid: Int): List<Tracker> {
+ return statsRepository.getAllTrackersOfApp(appUid)
+ }
+
+ 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(): Int {
+ return statsRepository.getMostLeakedApp(24, ChronoUnit.HOURS)
+ }
+
+ override fun getPastDayTrackersCallsByApps(): Map<Int, Pair<Int, Int>> {
+ return statsRepository.getCallsByApps(24, ChronoUnit.HOURS)
+ }
+
+ override fun getPastDayTrackersCallsForApp(appUid: Int): Pair<Int, Int> {
+ return statsRepository.getCalls(appUid, 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
new file mode 100644
index 0000000..2da5b16
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/Tracker.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.java b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsDatabase.java
deleted file mode 100644
index 0650114..0000000
--- a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsDatabase.java
+++ /dev/null
@@ -1,507 +0,0 @@
-/*
- Copyright (C) 2021 ECORP
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- 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, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
- */
-
-
-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 java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-import java.time.temporal.ChronoUnit;
-import java.time.temporal.TemporalUnit;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-import foundation.e.privacymodules.trackers.Tracker;
-import kotlin.Pair;
-
-public class StatsDatabase extends SQLiteOpenHelper {
- public static final int DATABASE_VERSION = 1;
- public static final String DATABASE_NAME = "TrackerFilterStats.db";
- private final Object lock = new Object();
- private TrackersRepository trackersRepository;
-
- public StatsDatabase(Context context) {
- super(context, DATABASE_NAME, null, DATABASE_VERSION);
- trackersRepository = TrackersRepository.getInstance();
- }
-
- public void onCreate(SQLiteDatabase db) {
- db.execSQL(SQL_CREATE_TABLE);
- }
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- onCreate(db);
- }
-
-
- public static class AppTrackerEntry implements BaseColumns {
- public static final String TABLE_NAME = "tracker_filter_stats";
- public static final String COLUMN_NAME_TIMESTAMP = "timestamp";
- public static final String COLUMN_NAME_TRACKER = "tracker";
- public static final String COLUMN_NAME_APP_UID = "app_uid";
- public static final String COLUMN_NAME_NUMBER_CONTACTED = "sum_contacted";
- public static final String COLUMN_NAME_NUMBER_BLOCKED = "sum_blocked";
-
- }
-
- String[] projection = {
- AppTrackerEntry.COLUMN_NAME_TIMESTAMP,
- AppTrackerEntry.COLUMN_NAME_APP_UID,
- AppTrackerEntry.COLUMN_NAME_TRACKER,
- AppTrackerEntry.COLUMN_NAME_NUMBER_CONTACTED,
- AppTrackerEntry.COLUMN_NAME_NUMBER_BLOCKED
- };
-
- private static final String SQL_CREATE_TABLE =
- "CREATE TABLE " + AppTrackerEntry.TABLE_NAME + " (" +
- AppTrackerEntry._ID + " INTEGER PRIMARY KEY," +
- AppTrackerEntry.COLUMN_NAME_TIMESTAMP + " INTEGER,"+
- AppTrackerEntry.COLUMN_NAME_APP_UID + " INTEGER," +
- AppTrackerEntry.COLUMN_NAME_TRACKER + " TEXT," +
- AppTrackerEntry.COLUMN_NAME_NUMBER_CONTACTED + " INTEGER," +
- AppTrackerEntry.COLUMN_NAME_NUMBER_BLOCKED + " INTEGER)";
-
- private static final String PROJECTION_NAME_PERIOD = "period";
- private static final String PROJECTION_NAME_CONTACTED_SUM = "contactedsum";
- private static final String PROJECTION_NAME_BLOCKED_SUM = "blockedsum";
- private static final String PROJECTION_NAME_LEAKED_SUM = "leakedsum";
- private static final String PROJECTION_NAME_TRACKERS_COUNT = "trackerscount";
-
- private HashMap<String, Pair<Integer, Integer>> getCallsByPeriod(
- int periodsCount,
- TemporalUnit periodUnit,
- String sqlitePeriodFormat
- ) {
- synchronized (lock) {
- long minTimestamp = getPeriodStartTs(periodsCount, periodUnit);
-
- SQLiteDatabase db = getReadableDatabase();
-
- String selection = AppTrackerEntry.COLUMN_NAME_TIMESTAMP + " >= ?";
- String[] selectionArg = new String[]{"" + minTimestamp};
- String projection =
- AppTrackerEntry.COLUMN_NAME_TIMESTAMP + ", " +
- "STRFTIME('" + sqlitePeriodFormat + "', DATETIME(" + AppTrackerEntry.COLUMN_NAME_TIMESTAMP + ", 'unixepoch', 'localtime')) " + PROJECTION_NAME_PERIOD + "," +
- "SUM(" + AppTrackerEntry.COLUMN_NAME_NUMBER_CONTACTED + ") " + PROJECTION_NAME_CONTACTED_SUM + "," +
- "SUM(" + AppTrackerEntry.COLUMN_NAME_NUMBER_BLOCKED + ") " + PROJECTION_NAME_BLOCKED_SUM;
- Cursor cursor = db.rawQuery("SELECT " + projection + " FROM " + AppTrackerEntry.TABLE_NAME
- + " WHERE " + selection +
- " GROUP BY " + PROJECTION_NAME_PERIOD +
- " ORDER BY " + AppTrackerEntry.COLUMN_NAME_TIMESTAMP + " DESC" +
- " LIMIT " + periodsCount, selectionArg);
-
- HashMap<String, Pair<Integer, Integer>> callsByPeriod = new HashMap<>();
- while (cursor.moveToNext()) {
- int contacted = cursor.getInt(cursor.getColumnIndex(PROJECTION_NAME_CONTACTED_SUM));
- int blocked = cursor.getInt(cursor.getColumnIndex(PROJECTION_NAME_BLOCKED_SUM));
-
- callsByPeriod.put(
- cursor.getString(cursor.getColumnIndex(PROJECTION_NAME_PERIOD)),
- new Pair(blocked, contacted - blocked)
- );
- }
-
- cursor.close();
- db.close();
-
- return callsByPeriod;
- }
- }
-
- private List<Pair<Integer, Integer>> callsByPeriodToPeriodsList(
- Map<String, Pair<Integer, Integer>> callsByPeriod,
- int periodsCount,
- TemporalUnit periodUnit,
- String javaPeriodFormat
- ) {
- ZonedDateTime currentDate = ZonedDateTime.now().minus(periodsCount, periodUnit);
- DateTimeFormatter formater = DateTimeFormatter.ofPattern(javaPeriodFormat);
-
- List<Pair<Integer, Integer>> calls = new ArrayList(periodsCount);
- for (int i = 0; i < periodsCount; i++) {
- currentDate = currentDate.plus(1, periodUnit);
-
- String currentPeriod = formater.format(currentDate);
- calls.add(callsByPeriod.getOrDefault(currentPeriod, new Pair(0, 0)));
- }
- return calls;
- }
-
- public List<Pair<Integer, Integer>> getTrackersCallsOnPeriod(int periodsCount, TemporalUnit periodUnit) {
- String sqlitePeriodFormat = "%Y%m";
- String 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";
- }
-
- Map<String, Pair<Integer, Integer>> callsByPeriod = getCallsByPeriod(periodsCount, periodUnit, sqlitePeriodFormat);
- return callsByPeriodToPeriodsList(callsByPeriod, periodsCount, periodUnit, javaPeriodFormat);
- }
-
-
-
- public int getActiveTrackersByPeriod(int periodsCount, TemporalUnit periodUnit) {
- synchronized (lock) {
- long minTimestamp = getPeriodStartTs(periodsCount, periodUnit);
-
-
- SQLiteDatabase db = getWritableDatabase();
-
- String selection = AppTrackerEntry.COLUMN_NAME_TIMESTAMP + " >= ? AND " +
- AppTrackerEntry.COLUMN_NAME_NUMBER_CONTACTED + " > " + AppTrackerEntry.COLUMN_NAME_NUMBER_BLOCKED;
- String[] selectionArg = new String[]{"" + minTimestamp};
- String projection =
- "COUNT(DISTINCT " + AppTrackerEntry.COLUMN_NAME_TRACKER + ") " + PROJECTION_NAME_TRACKERS_COUNT;
- Cursor cursor = db.rawQuery("SELECT " + projection + " FROM " + AppTrackerEntry.TABLE_NAME
- + " WHERE " + selection, selectionArg);
-
- int count = 0;
-
- if (cursor.moveToNext()) {
- count = cursor.getInt(0);
- }
-
- cursor.close();
- db.close();
-
- return count;
- }
-
- }
-
- public int getContactedTrackersCount() {
- synchronized (lock) {
- SQLiteDatabase db = getReadableDatabase();
- String projection =
- "COUNT(DISTINCT " + AppTrackerEntry.COLUMN_NAME_TRACKER + ") " + PROJECTION_NAME_TRACKERS_COUNT;
-
- Cursor cursor = db.rawQuery(
- "SELECT " + projection + " FROM " + AppTrackerEntry.TABLE_NAME,
- new String[]{});
-
- int count = 0;
-
- if (cursor.moveToNext()) {
- count = cursor.getInt(0);
- }
-
- cursor.close();
- db.close();
-
- return count;
- }
- }
-
-
- public Map<Integer, Integer> getContactedTrackersCountByApp() {
- synchronized (lock) {
- SQLiteDatabase db = getReadableDatabase();
- String projection =
- AppTrackerEntry.COLUMN_NAME_APP_UID + ", " +
- "COUNT(DISTINCT " + AppTrackerEntry.COLUMN_NAME_TRACKER + ") " + PROJECTION_NAME_TRACKERS_COUNT;
-
- Cursor cursor = db.rawQuery(
- "SELECT " + projection + " FROM " + AppTrackerEntry.TABLE_NAME +
- " GROUP BY " + AppTrackerEntry.COLUMN_NAME_APP_UID,
- new String[]{});
-
- HashMap<Integer, Integer> countByApp = new HashMap();
-
- while (cursor.moveToNext()) {
- countByApp.put(
- cursor.getInt(cursor.getColumnIndex(AppTrackerEntry.COLUMN_NAME_APP_UID)),
- cursor.getInt(cursor.getColumnIndex(PROJECTION_NAME_TRACKERS_COUNT))
- );
- }
-
- cursor.close();
- db.close();
-
- return countByApp;
- }
- }
-
- public Map<Integer, Pair<Integer, Integer>> getCallsByApps(int periodCount, TemporalUnit periodUnit) {
- synchronized (lock) {
- long minTimestamp = getPeriodStartTs(periodCount, periodUnit);
-
- SQLiteDatabase db = getReadableDatabase();
-
- String selection = AppTrackerEntry.COLUMN_NAME_TIMESTAMP + " >= ?";
- String[] selectionArg = new String[]{"" + minTimestamp};
- String projection =
- AppTrackerEntry.COLUMN_NAME_APP_UID + ", " +
- "SUM(" + AppTrackerEntry.COLUMN_NAME_NUMBER_CONTACTED + ") " + PROJECTION_NAME_CONTACTED_SUM + "," +
- "SUM(" + AppTrackerEntry.COLUMN_NAME_NUMBER_BLOCKED + ") " + PROJECTION_NAME_BLOCKED_SUM;
-
- Cursor cursor = db.rawQuery(
- "SELECT " + projection + " FROM " + AppTrackerEntry.TABLE_NAME +
- " WHERE " + selection +
- " GROUP BY " + AppTrackerEntry.COLUMN_NAME_APP_UID,
- selectionArg);
-
-
- HashMap<Integer, Pair<Integer, Integer>> callsByApp = new HashMap<>();
-
- while (cursor.moveToNext()) {
- int contacted = cursor.getInt(cursor.getColumnIndex(PROJECTION_NAME_CONTACTED_SUM));
- int blocked = cursor.getInt(cursor.getColumnIndex(PROJECTION_NAME_BLOCKED_SUM));
-
- callsByApp.put(
- cursor.getInt(cursor.getColumnIndex(AppTrackerEntry.COLUMN_NAME_APP_UID)),
- new Pair(blocked, contacted - blocked)
- );
- }
-
- cursor.close();
- db.close();
-
- return callsByApp;
- }
- }
-
- public Pair<Integer, Integer> getCalls(int appUid, int periodCount, TemporalUnit periodUnit) {
- synchronized (lock) {
- long minTimestamp = getPeriodStartTs(periodCount, periodUnit);
-
- SQLiteDatabase db = getReadableDatabase();
-
- String selection =
- AppTrackerEntry.COLUMN_NAME_APP_UID + " = ? AND " +
- AppTrackerEntry.COLUMN_NAME_TIMESTAMP + " >= ?";
- String[] selectionArg = new String[]{ "" + appUid, "" + minTimestamp };
- String projection =
- "SUM(" + AppTrackerEntry.COLUMN_NAME_NUMBER_CONTACTED + ") " + PROJECTION_NAME_CONTACTED_SUM + "," +
- "SUM(" + AppTrackerEntry.COLUMN_NAME_NUMBER_BLOCKED + ") " + PROJECTION_NAME_BLOCKED_SUM;
-
- Cursor cursor = db.rawQuery(
- "SELECT " + projection + " FROM " + AppTrackerEntry.TABLE_NAME +
- " WHERE " + selection,
- selectionArg);
-
- HashMap<Integer, Pair<Integer, Integer>> callsByApp = new HashMap<>();
-
- Pair<Integer, Integer> calls = new Pair(0, 0);
-
- if (cursor.moveToNext()) {
- int contacted = cursor.getInt(cursor.getColumnIndex(PROJECTION_NAME_CONTACTED_SUM));
- int blocked = cursor.getInt(cursor.getColumnIndex(PROJECTION_NAME_BLOCKED_SUM));
-
- calls = new Pair(blocked, contacted - blocked);
- }
-
- cursor.close();
- db.close();
-
- return calls;
- }
- }
-
- public int getMostLeakedApp(int periodCount, TemporalUnit periodUnit) {
- synchronized (lock) {
- long minTimestamp = getPeriodStartTs(periodCount, periodUnit);
-
- SQLiteDatabase db = getReadableDatabase();
-
- String selection = AppTrackerEntry.COLUMN_NAME_TIMESTAMP + " >= ?";
- String[] selectionArg = new String[]{"" + minTimestamp};
- String projection =
- AppTrackerEntry.COLUMN_NAME_APP_UID + ", " +
- "SUM(" + AppTrackerEntry.COLUMN_NAME_NUMBER_CONTACTED +
- " - " + AppTrackerEntry.COLUMN_NAME_NUMBER_BLOCKED +
- ") " + PROJECTION_NAME_LEAKED_SUM;
-
- Cursor cursor = db.rawQuery(
- "SELECT " + projection + " FROM " + AppTrackerEntry.TABLE_NAME +
- " WHERE " + selection +
- " GROUP BY " + AppTrackerEntry.COLUMN_NAME_APP_UID +
- " ORDER BY " + PROJECTION_NAME_LEAKED_SUM + " DESC LIMIT 1",
- selectionArg);
-
-
- int appUid = 0;
- if (cursor.moveToNext()) {
- appUid = cursor.getInt(cursor.getColumnIndex(AppTrackerEntry.COLUMN_NAME_APP_UID));
- }
-
- cursor.close();
- db.close();
-
- return appUid;
- }
- }
-
- private long getCurrentHourTs() {
- long hourInMs = TimeUnit.HOURS.toMillis(1L);
- long hourInS = TimeUnit.HOURS.toSeconds(1L);
- return (System.currentTimeMillis() / hourInMs) * hourInS;
- }
-
- private long getPeriodStartTs(
- int periodsCount,
- TemporalUnit periodUnit
- ) {
-
- ZonedDateTime start = ZonedDateTime.now()
- .minus(periodsCount, periodUnit)
- .plus(1, periodUnit);
-
- TemporalUnit truncatePeriodUnit = periodUnit;
- if (periodUnit == ChronoUnit.MONTHS) {
- start = start.withDayOfMonth(1);
- truncatePeriodUnit = ChronoUnit.DAYS;
- }
-
- return start.truncatedTo(truncatePeriodUnit).toEpochSecond();
- }
-
- public void logAccess(String trackerId, int appUid, boolean blocked){
- synchronized (lock) {
- long currentHour = getCurrentHourTs();
- SQLiteDatabase db = getWritableDatabase();
- ContentValues values = new ContentValues();
- values.put(AppTrackerEntry.COLUMN_NAME_APP_UID, appUid);
- values.put(AppTrackerEntry.COLUMN_NAME_TRACKER, trackerId);
- values.put(AppTrackerEntry.COLUMN_NAME_TIMESTAMP, currentHour);
-
- /*String query = "UPDATE product SET "+AppTrackerEntry.COLUMN_NAME_NUMBER_CONTACTED+" = "+AppTrackerEntry.COLUMN_NAME_NUMBER_CONTACTED+" + 1 ";
- if(blocked)
- query+=AppTrackerEntry.COLUMN_NAME_NUMBER_BLOCKED+" = "+AppTrackerEntry.COLUMN_NAME_NUMBER_BLOCKED+" + 1 ";
-*/
- String selection =
- AppTrackerEntry.COLUMN_NAME_TIMESTAMP + " = ? AND " +
- AppTrackerEntry.COLUMN_NAME_APP_UID + " = ? AND " +
- AppTrackerEntry.COLUMN_NAME_TRACKER + " = ? ";
-
- String[] selectionArg = new String[]{"" + currentHour, "" + appUid, trackerId};
-
- Cursor cursor = db.query(
- AppTrackerEntry.TABLE_NAME,
- projection,
- selection,
- selectionArg,
- null,
- null,
- null
- );
- if (cursor.getCount() > 0) {
- cursor.moveToFirst();
- StatEntry entry = cursorToEntry(cursor);
- if (blocked)
- values.put(AppTrackerEntry.COLUMN_NAME_NUMBER_BLOCKED, entry.sum_blocked + 1);
- else
- values.put(AppTrackerEntry.COLUMN_NAME_NUMBER_BLOCKED, entry.sum_blocked);
- values.put(AppTrackerEntry.COLUMN_NAME_NUMBER_CONTACTED, entry.sum_contacted + 1);
- db.update(AppTrackerEntry.TABLE_NAME, values, selection, selectionArg);
-
- // db.execSQL(query, new String[]{""+hour, ""+day, ""+month, ""+year, ""+appUid, ""+trackerId});
- } else {
-
- if (blocked)
- values.put(AppTrackerEntry.COLUMN_NAME_NUMBER_BLOCKED, 1);
- else
- values.put(AppTrackerEntry.COLUMN_NAME_NUMBER_BLOCKED, 0);
- values.put(AppTrackerEntry.COLUMN_NAME_NUMBER_CONTACTED, 1);
-
-
- long newRowId = db.insert(AppTrackerEntry.TABLE_NAME, null, values);
- }
-
- cursor.close();
- db.close();
- }
- }
-
-
- private StatEntry cursorToEntry(Cursor cursor){
- StatEntry entry = new StatEntry();
- entry.timestamp = cursor.getLong(cursor.getColumnIndex(AppTrackerEntry.COLUMN_NAME_TIMESTAMP));
- entry.app_uid = cursor.getInt(cursor.getColumnIndex(AppTrackerEntry.COLUMN_NAME_APP_UID));
- entry.sum_blocked = cursor.getInt(cursor.getColumnIndex(AppTrackerEntry.COLUMN_NAME_NUMBER_BLOCKED));
- entry.sum_contacted = cursor.getInt(cursor.getColumnIndex(AppTrackerEntry.COLUMN_NAME_NUMBER_CONTACTED));
- entry.tracker = cursor.getInt(cursor.getColumnIndex(AppTrackerEntry.COLUMN_NAME_TRACKER));
- return entry;
- }
-
- public List<Tracker> getAllTrackersOfApp(int appUid){
- synchronized (lock) {
- String[] columns = { AppTrackerEntry.COLUMN_NAME_TRACKER, AppTrackerEntry.COLUMN_NAME_APP_UID };
- String selection = null;
- String[] selectionArg = null;
- if (appUid >= 0) {
- selection = AppTrackerEntry.COLUMN_NAME_APP_UID + " = ?";
- selectionArg = new String[]{"" + appUid};
- }
- SQLiteDatabase db = getReadableDatabase();
- Cursor cursor = db.query(
- true,
- AppTrackerEntry.TABLE_NAME,
- columns,
- selection,
- selectionArg,
- null,
- null,
- null,
- null
- );
- List<Tracker> trackers = new ArrayList<>();
-
- while (cursor.moveToNext()) {
- String trackerId = cursor.getString(cursor.getColumnIndex(AppTrackerEntry.COLUMN_NAME_TRACKER));
- Tracker tracker = trackersRepository.getTracker(trackerId);
-
- if (tracker != null) {
- trackers.add(tracker);
- }
- }
- cursor.close();
- db.close();
- return trackers;
- }
- }
-
- public List<Tracker> getAllTrackers(){
- return getAllTrackersOfApp(-1);
- }
-
- public static class StatEntry {
- int app_uid;
- int sum_contacted;
- int sum_blocked;
- long timestamp;
- int tracker;
- }
-}
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
new file mode 100644
index 0000000..86208ad
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsDatabase.kt
@@ -0,0 +1,451 @@
+/*
+ Copyright (C) 2022 ECORP
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+ */
+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 foundation.e.privacymodules.trackers.api.Tracker
+import foundation.e.privacymodules.trackers.data.StatsDatabase.AppTrackerEntry.COLUMN_NAME_APP_UID
+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 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 = 1
+ 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_APP_UID INTEGER," +
+ "$COLUMN_NAME_TRACKER TEXT," +
+ "$COLUMN_NAME_NUMBER_CONTACTED INTEGER," +
+ "$COLUMN_NAME_NUMBER_BLOCKED INTEGER)"
+
+ 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"
+ }
+
+ 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_APP_UID = "app_uid"
+ const val COLUMN_NAME_NUMBER_CONTACTED = "sum_contacted"
+ const val COLUMN_NAME_NUMBER_BLOCKED = "sum_blocked"
+ }
+
+ private var projection = arrayOf(
+ COLUMN_NAME_TIMESTAMP,
+ COLUMN_NAME_APP_UID,
+ COLUMN_NAME_TRACKER,
+ COLUMN_NAME_NUMBER_CONTACTED,
+ COLUMN_NAME_NUMBER_BLOCKED
+ )
+
+ 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) {
+ onCreate(db)
+ }
+
+ 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 formater = DateTimeFormatter.ofPattern(javaPeriodFormat)
+ val calls = mutableListOf<Pair<Int, Int>>()
+ for (i in 0 until periodsCount) {
+ currentDate = currentDate.plus(1, periodUnit)
+ val currentPeriod = formater.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(appUids: List<Int>?): Int {
+ synchronized(lock) {
+ val db = readableDatabase
+ val projection =
+ "COUNT(DISTINCT $COLUMN_NAME_TRACKER) $PROJECTION_NAME_TRACKERS_COUNT"
+
+ var query = "SELECT $projection FROM $TABLE_NAME"
+
+ appUids?.let {
+ query += " WHERE $COLUMN_NAME_APP_UID IN (${it.joinToString(", ")})"
+ }
+
+ val cursor = db.rawQuery(query, arrayOf())
+ var count = 0
+ if (cursor.moveToNext()) {
+ count = cursor.getInt(0)
+ }
+ cursor.close()
+ db.close()
+ return count
+ }
+ }
+
+ fun getContactedTrackersCountByApp(): Map<Int, Int> {
+ synchronized(lock) {
+ val db = readableDatabase
+ val projection = "$COLUMN_NAME_APP_UID, " +
+ "COUNT(DISTINCT $COLUMN_NAME_TRACKER) $PROJECTION_NAME_TRACKERS_COUNT"
+ val cursor = db.rawQuery(
+ "SELECT $projection FROM $TABLE_NAME" +
+ " GROUP BY $COLUMN_NAME_APP_UID",
+ arrayOf()
+ )
+ val countByApp = mutableMapOf<Int, Int>()
+ while (cursor.moveToNext()) {
+ countByApp[cursor.getInt(COLUMN_NAME_APP_UID)] =
+ cursor.getInt(PROJECTION_NAME_TRACKERS_COUNT)
+ }
+ cursor.close()
+ db.close()
+ return countByApp
+ }
+ }
+
+ fun getCallsByApps(periodCount: Int, periodUnit: TemporalUnit): Map<Int, 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_APP_UID, " +
+ "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_APP_UID",
+ selectionArg
+ )
+ val callsByApp = HashMap<Int, Pair<Int, Int>>()
+ while (cursor.moveToNext()) {
+ val contacted = cursor.getInt(PROJECTION_NAME_CONTACTED_SUM)
+ val blocked = cursor.getInt(PROJECTION_NAME_BLOCKED_SUM)
+ callsByApp[cursor.getInt(COLUMN_NAME_APP_UID)] = blocked to contacted - blocked
+ }
+ cursor.close()
+ db.close()
+ return callsByApp
+ }
+ }
+
+ fun getCalls(appUid: Int, periodCount: Int, periodUnit: TemporalUnit): Pair<Int, Int> {
+ synchronized(lock) {
+ val minTimestamp = getPeriodStartTs(periodCount, periodUnit)
+ val db = readableDatabase
+ val selection = "$COLUMN_NAME_APP_UID = ? AND " +
+ "$COLUMN_NAME_TIMESTAMP >= ?"
+ val selectionArg = arrayOf("" + appUid, "" + 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 getMostLeakedApp(periodCount: Int, periodUnit: TemporalUnit): Int {
+ synchronized(lock) {
+ val minTimestamp = getPeriodStartTs(periodCount, periodUnit)
+ val db = readableDatabase
+ val selection = "$COLUMN_NAME_TIMESTAMP >= ?"
+ val selectionArg = arrayOf("" + minTimestamp)
+ val projection = "$COLUMN_NAME_APP_UID, " +
+ "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_APP_UID" +
+ " ORDER BY $PROJECTION_NAME_LEAKED_SUM DESC LIMIT 1",
+ selectionArg
+ )
+ var appUid = 0
+ if (cursor.moveToNext()) {
+ appUid = cursor.getInt(COLUMN_NAME_APP_UID)
+ }
+ cursor.close()
+ db.close()
+ return appUid
+ }
+ }
+
+ fun logAccess(trackerId: String?, appUid: Int, blocked: Boolean) {
+ synchronized(lock) {
+ val currentHour = getCurrentHourTs()
+ val db = writableDatabase
+ val values = ContentValues()
+ values.put(COLUMN_NAME_APP_UID, appUid)
+ 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_APP_UID = ? AND " +
+ "$COLUMN_NAME_TRACKER = ? "
+ val selectionArg = arrayOf("" + currentHour, "" + appUid, 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.app_uid = cursor.getInt(COLUMN_NAME_APP_UID)
+ 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 getAllTrackersOfApp(appUid: Int): List<Tracker> {
+ synchronized(lock) {
+ val columns =
+ arrayOf(COLUMN_NAME_TRACKER, COLUMN_NAME_APP_UID)
+ var selection: String? = null
+ var selectionArg: Array<String>? = null
+ if (appUid >= 0) {
+ selection = "$COLUMN_NAME_APP_UID = ?"
+ selectionArg = arrayOf("" + appUid)
+ }
+ 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
+ }
+ }
+
+ val allTrackers: List<Tracker>
+ get() = getAllTrackersOfApp(-1)
+
+ class StatEntry {
+ var app_uid = 0
+ 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) getString(columnIndex) else ""
+ }
+
+} \ No newline at end of file
diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsRepository.java b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsRepository.java
deleted file mode 100644
index bfe688f..0000000
--- a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsRepository.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- Copyright (C) 2022 ECORP
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- 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, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
- */
-
-package foundation.e.privacymodules.trackers.data;
-
-import android.content.Context;
-
-import java.time.temporal.ChronoUnit;
-import java.time.temporal.TemporalUnit;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Consumer;
-
-import foundation.e.privacymodules.trackers.Tracker;
-import kotlin.Pair;
-
-public class StatsRepository {
- private static StatsRepository instance;
-
- private StatsDatabase database;
-
- private Consumer<Boolean> newDataCallback = null;
-
- private StatsRepository(Context context) {
- database = new StatsDatabase(context);
- }
-
- public static StatsRepository getInstance(Context context) {
- if (instance == null) {
- instance = new StatsRepository(context);
- }
- return instance;
- }
-
- public void setNewDataCallback(Consumer<Boolean> callback) {
- newDataCallback = callback;
- }
-
- public void logAccess(String trackerId, int appUid, boolean blocked) {
- database.logAccess(trackerId, appUid, blocked);
- if (newDataCallback != null) {
- newDataCallback.accept(true);
- }
- }
-
- public List<Pair<Integer, Integer>> getTrackersCallsOnPeriod(int periodsCount, TemporalUnit periodUnit) {
- return database.getTrackersCallsOnPeriod(periodsCount, periodUnit);
- }
-
- public int getActiveTrackersByPeriod(int periodsCount, TemporalUnit periodUnit) {
- return database.getActiveTrackersByPeriod(periodsCount, periodUnit);
- }
-
- public Map<Integer, Integer> getContactedTrackersCountByApp() {
- return database.getContactedTrackersCountByApp();
- }
-
- public int getContactedTrackersCount() {
- return database.getContactedTrackersCount();
- }
-
- public List<Tracker> getAllTrackersOfApp(int app_uid) {
- return database.getAllTrackersOfApp(app_uid);
- }
-
- public Map<Integer, Pair<Integer, Integer>> getCallsByApps(int periodCount, TemporalUnit periodUnit) {
- return database.getCallsByApps(periodCount, periodUnit);
- }
-
- public Pair<Integer, Integer> getCalls(int appUid, int periodCount, TemporalUnit periodUnit) {
- return database.getCalls(appUid, periodCount, periodUnit);
- }
-
-
- public int getMostLeakedApp(int periodCount, TemporalUnit periodUnit) {
- return database.getMostLeakedApp(periodCount, periodUnit);
- }
-} \ No newline at end of file
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
new file mode 100644
index 0000000..0e88102
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsRepository.kt
@@ -0,0 +1,83 @@
+/*
+ Copyright (C) 2022 ECORP
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+ */
+package foundation.e.privacymodules.trackers.data
+
+import android.content.Context
+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
+
+ companion object {
+ private var instance: StatsRepository? = null
+ fun getInstance(context: Context): StatsRepository {
+ return instance ?: StatsRepository(context).apply { instance = this }
+ }
+ }
+
+ init {
+ database = StatsDatabase(context)
+ }
+
+ fun setNewDataCallback(callback: () -> Unit) {
+ newDataCallback = callback
+ }
+
+ fun logAccess(trackerId: String?, appUid: Int, blocked: Boolean) {
+ database.logAccess(trackerId, appUid, 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<Int, Int> {
+ return database.getContactedTrackersCountByApp()
+ }
+
+ fun getContactedTrackersCount(appUids: List<Int>?): Int {
+ return database.getContactedTrackersCount(appUids)
+ }
+
+ fun getAllTrackersOfApp(app_uid: Int): List<Tracker> {
+ return database.getAllTrackersOfApp(app_uid)
+ }
+
+ fun getCallsByApps(periodCount: Int, periodUnit: TemporalUnit): Map<Int, Pair<Int, Int>> {
+ return database.getCallsByApps(periodCount, periodUnit)
+ }
+
+ fun getCalls(appUid: Int, periodCount: Int, periodUnit: TemporalUnit): Pair<Int, Int> {
+ return database.getCalls(appUid, periodCount, periodUnit)
+ }
+
+ fun getMostLeakedApp(periodCount: Int, periodUnit: TemporalUnit): Int {
+ return database.getMostLeakedApp(periodCount, periodUnit)
+ }
+} \ No newline at end of file
diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/TrackersRepository.java b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/TrackersRepository.java
deleted file mode 100644
index 5c77c7a..0000000
--- a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/TrackersRepository.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- Copyright (C) 2022 ECORP
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- 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, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
- */
-
-package foundation.e.privacymodules.trackers.data;
-
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import foundation.e.privacymodules.trackers.Tracker;
-
-public class TrackersRepository {
- private static TrackersRepository instance;
-
- private TrackersRepository() { }
-
- public static TrackersRepository getInstance() {
- if (instance == null) {
- instance = new TrackersRepository();
- }
- return instance;
- }
-
- private Map<String, Tracker> trackersById = new HashMap();
- private Map<String, String> hostnameToId = new HashMap();
-
- public void setTrackersList(List<Tracker> list) {
- Map<String, Tracker> trackersById = new HashMap();
- Map<String, String> hostnameToId = new HashMap();
-
- for (Tracker tracker: list) {
- trackersById.put(tracker.getId(), tracker);
-
- for (String hostname: tracker.getHostnames()) {
- hostnameToId.put(hostname, tracker.getId());
- }
- }
-
- this.trackersById = trackersById;
- this.hostnameToId = hostnameToId;
- }
-
- public boolean isTracker(String hostname) {
- return hostnameToId.containsKey(hostname);
- }
-
- public String getTrackerId(String hostname) {
- return hostnameToId.get(hostname);
- }
-
- public Tracker getTracker(String id) {
- return trackersById.get(id);
- }
-}
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
new file mode 100644
index 0000000..bc4d50b
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/TrackersRepository.kt
@@ -0,0 +1,58 @@
+/*
+ Copyright (C) 2022 ECORP
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+ */
+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.java b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/WhitelistRepository.java
deleted file mode 100644
index 9bfca7f..0000000
--- a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/WhitelistRepository.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- Copyright (C) 2022 ECORP
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- 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, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
- */
-
-package foundation.e.privacymodules.trackers.data;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import foundation.e.privacymodules.trackers.Tracker;
-
-public class WhitelistRepository {
- private static final String SHARED_PREFS_FILE = "trackers_whitelist.prefs";
- private static final String KEY_BLOKING_ENABLED = "blocking_enabled";
- private static final String KEY_APPS_WHITELIST = "apps_whitelist";
- private static final String KEY_APP_TRACKERS_WHITELIST_PREFIX = "app_trackers_whitelist_";
- private static WhitelistRepository instance;
-
- private boolean isBlockingEnabled = false;
- private Set<Integer> appsWhitelist;
- private Map<Integer, Set<String>> trackersWhitelistByApp = new HashMap();
-
- private SharedPreferences prefs;
- private WhitelistRepository(Context context) {
- prefs = context.getSharedPreferences(SHARED_PREFS_FILE, Context.MODE_PRIVATE);
- reloadCache();
- }
-
- public static WhitelistRepository getInstance(Context context) {
- if (instance == null) {
- instance = new WhitelistRepository(context);
- }
- return instance;
- }
-
- private void reloadCache() {
- isBlockingEnabled = prefs.getBoolean(KEY_BLOKING_ENABLED, false);
- reloadAppsWhiteList();
- reloadAllAppTrackersWhiteList();
- }
-
- private void reloadAppsWhiteList() {
- HashSet<Integer> appWhiteList = new HashSet();
- for (String appUid: prefs.getStringSet(KEY_APPS_WHITELIST, new HashSet<String>())) {
- try {
- appWhiteList.add(Integer.parseInt(appUid));
- } catch (Exception e) { }
- }
- this.appsWhitelist = appWhiteList;
- }
-
- private void reloadAppTrackersWhiteList(int appUid) {
- String key = buildAppTrackersKey(appUid);
- trackersWhitelistByApp.put(appUid, prefs.getStringSet(key, new HashSet<String>()));
- }
-
- private void reloadAllAppTrackersWhiteList() {
- trackersWhitelistByApp.clear();
- for (String key: prefs.getAll().keySet()) {
- if (key.startsWith(KEY_APP_TRACKERS_WHITELIST_PREFIX)) {
- int appUid = Integer.parseInt(key.substring(KEY_APP_TRACKERS_WHITELIST_PREFIX.length()));
- reloadAppTrackersWhiteList(appUid);
- }
- }
- }
-
- public boolean isBlockingEnabled() { return isBlockingEnabled; }
-
- public void setBlockingEnabled(boolean enabled) {
- prefs.edit().putBoolean(KEY_BLOKING_ENABLED, enabled).apply();
- isBlockingEnabled = enabled;
- }
-
- public void setWhiteListed(int appUid, boolean isWhiteListed) {
- Set<String> current = new HashSet(prefs.getStringSet(KEY_APPS_WHITELIST, new HashSet<String>()));
- if (isWhiteListed) {
- current.add("" + appUid);
- } else {
- current.remove("" + appUid);
- }
-
- prefs.edit().putStringSet(KEY_APPS_WHITELIST, current).commit();
- reloadAppsWhiteList();
- }
-
- private String buildAppTrackersKey(int appUid) {
- return KEY_APP_TRACKERS_WHITELIST_PREFIX + appUid;
- }
-
- public void setWhiteListed(Tracker tracker, int appUid, boolean isWhiteListed) {
- Set<String> trackers;
- if (trackersWhitelistByApp.containsKey(appUid)) {
- trackers = trackersWhitelistByApp.get(appUid);
- } else {
- trackers = new HashSet<String>();
- trackersWhitelistByApp.put(appUid, trackers);
- }
- if (isWhiteListed) {
- trackers.add(tracker.getId());
- } else {
- trackers.remove(tracker.getId());
- }
-
- prefs.edit().putStringSet(buildAppTrackersKey(appUid), trackers).commit();
- }
-
- public boolean isAppWhiteListed(int appUid) {
- return appsWhitelist.contains(appUid);
- }
-
- public boolean isTrackerWhiteListedForApp(String trackerId, int appUid) {
- return trackersWhitelistByApp.getOrDefault(appUid, new HashSet()).contains(trackerId);
- }
-
- public boolean areWhiteListEmpty() {
- boolean empty = true;
- for (Set<String> trackers: trackersWhitelistByApp.values()) {
- empty = trackers.isEmpty();
- }
-
- return appsWhitelist.isEmpty() && empty;
- }
-
- public List<Integer> getWhiteListedApp() {
- return new ArrayList(appsWhitelist);
- }
-
- public List<String> getWhiteListForApp(int appUid) {
- return new ArrayList(trackersWhitelistByApp.getOrDefault(appUid, new HashSet()));
- }
-}
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
new file mode 100644
index 0000000..65a8c39
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/WhitelistRepository.kt
@@ -0,0 +1,128 @@
+/*
+ Copyright (C) 2022 ECORP
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+ */
+package foundation.e.privacymodules.trackers.data
+
+import android.content.Context
+import android.content.SharedPreferences
+import foundation.e.privacymodules.trackers.api.Tracker
+
+class WhitelistRepository private constructor(context: Context) {
+ private lateinit var appsWhitelist: Set<Int>
+ private val trackersWhitelistByApp: MutableMap<Int, MutableSet<String>> = HashMap()
+ private val prefs: SharedPreferences
+
+ companion object {
+ private const val SHARED_PREFS_FILE = "trackers_whitelist.prefs"
+ private const val KEY_BLOKING_ENABLED = "blocking_enabled"
+ private const val KEY_APPS_WHITELIST = "apps_whitelist"
+ private const val KEY_APP_TRACKERS_WHITELIST_PREFIX = "app_trackers_whitelist_"
+ 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()
+ }
+
+ private fun reloadCache() {
+ isBlockingEnabled = prefs.getBoolean(KEY_BLOKING_ENABLED, false)
+ reloadAppsWhiteList()
+ reloadAllAppTrackersWhiteList()
+ }
+
+ private fun reloadAppsWhiteList() {
+ appsWhitelist = prefs.getStringSet(KEY_APPS_WHITELIST, HashSet())?.mapNotNull {
+ try { it.toInt() } catch(e: Exception) { null }
+ }?.toHashSet()?: HashSet()
+ }
+
+ private fun reloadAppTrackersWhiteList(appUid: Int) {
+ val key = buildAppTrackersKey(appUid)
+ trackersWhitelistByApp[appUid] = prefs.getStringSet(key, HashSet())?: HashSet()
+ }
+
+ private fun reloadAllAppTrackersWhiteList() {
+ trackersWhitelistByApp.clear()
+ prefs.all.keys.forEach { key ->
+ if (key.startsWith(KEY_APP_TRACKERS_WHITELIST_PREFIX)) {
+ val appUid = key.substring(KEY_APP_TRACKERS_WHITELIST_PREFIX.length).toInt()
+ reloadAppTrackersWhiteList(appUid)
+ }
+ }
+ }
+
+
+
+ var isBlockingEnabled: Boolean = false
+ get() = field
+ set(enabled) {
+ prefs.edit().putBoolean(KEY_BLOKING_ENABLED, enabled).apply()
+ field = enabled
+ }
+
+
+ fun setWhiteListed(appUid: Int, isWhiteListed: Boolean) {
+ val current = prefs.getStringSet(KEY_APPS_WHITELIST, HashSet())?.toHashSet()?: HashSet()
+
+ if (isWhiteListed) {
+ current.add("" + appUid)
+ } else {
+ current.remove("" + appUid)
+ }
+ prefs.edit().putStringSet(KEY_APPS_WHITELIST, current).commit()
+ reloadAppsWhiteList()
+ }
+
+ private fun buildAppTrackersKey(appUid: Int): String {
+ return KEY_APP_TRACKERS_WHITELIST_PREFIX + appUid
+ }
+
+ fun setWhiteListed(tracker: Tracker, appUid: Int, isWhiteListed: Boolean) {
+ val trackers = trackersWhitelistByApp.getOrDefault(appUid, HashSet())
+ trackersWhitelistByApp[appUid] = trackers
+
+ if (isWhiteListed) {
+ trackers.add(tracker.id)
+ } else {
+ trackers.remove(tracker.id)
+ }
+ prefs.edit().putStringSet(buildAppTrackersKey(appUid), trackers).commit()
+ }
+
+ fun isAppWhiteListed(appUid: Int): Boolean {
+ return appsWhitelist.contains(appUid)
+ }
+
+ fun isTrackerWhiteListedForApp(trackerId: String?, appUid: Int): Boolean {
+ return trackersWhitelistByApp.getOrDefault(appUid, HashSet()).contains(trackerId)
+ }
+
+ fun areWhiteListEmpty(): Boolean {
+ return appsWhitelist.isEmpty() && trackersWhitelistByApp.all { (_, trackers) -> trackers.isEmpty() }
+ }
+
+ val whiteListedApp: List<Int> get() = appsWhitelist.toList()
+
+ fun getWhiteListForApp(appUid: Int): List<String> {
+ return trackersWhitelistByApp[appUid]?.toList()?: emptyList()
+ }
+} \ No newline at end of file