summaryrefslogtreecommitdiff
path: root/trackers/src
diff options
context:
space:
mode:
Diffstat (limited to 'trackers/src')
-rw-r--r--trackers/src/main/AndroidManifest.xml37
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.java173
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerService.java86
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/ForegroundStarter.java42
-rw-r--r--trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.java83
-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/TrackTrackersPrivacyModule.java150
-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/StatsRepository.java95
-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/WhitelistRepository.java153
11 files changed, 1522 insertions, 0 deletions
diff --git a/trackers/src/main/AndroidManifest.xml b/trackers/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..debdf61
--- /dev/null
+++ b/trackers/src/main/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 ECORP
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="foundation.e.privacymodules.trackers">
+
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+
+
+ <application>
+ <service
+ android:name="foundation.e.privacymodules.trackers.DNSBlockerService"
+ android:enabled="true"
+ android:exported="true" />
+ </application>
+
+</manifest> \ No newline at end of file
diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.java b/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.java
new file mode 100644
index 0000000..80f00c1
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.java
@@ -0,0 +1,173 @@
+/*
+ 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/DNSBlockerService.java b/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerService.java
new file mode 100644
index 0000000..6250621
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerService.java
@@ -0,0 +1,86 @@
+/*
+ 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/ForegroundStarter.java b/trackers/src/main/java/foundation/e/privacymodules/trackers/ForegroundStarter.java
new file mode 100644
index 0000000..1563163
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/ForegroundStarter.java
@@ -0,0 +1,42 @@
+/*
+ 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/TrackersLogger.java b/trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.java
new file mode 100644
index 0000000..3710253
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.java
@@ -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;
+
+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/api/BlockTrackersPrivacyModule.java b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/BlockTrackersPrivacyModule.java
new file mode 100644
index 0000000..ea62766
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/BlockTrackersPrivacyModule.java
@@ -0,0 +1,125 @@
+/*
+ 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/TrackTrackersPrivacyModule.java b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.java
new file mode 100644
index 0000000..38b2c8f
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.java
@@ -0,0 +1,150 @@
+/*
+ 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/data/StatsDatabase.java b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsDatabase.java
new file mode 100644
index 0000000..0650114
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsDatabase.java
@@ -0,0 +1,507 @@
+/*
+ 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/StatsRepository.java b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsRepository.java
new file mode 100644
index 0000000..bfe688f
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsRepository.java
@@ -0,0 +1,95 @@
+/*
+ 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/TrackersRepository.java b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/TrackersRepository.java
new file mode 100644
index 0000000..5c77c7a
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/TrackersRepository.java
@@ -0,0 +1,71 @@
+/*
+ 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/WhitelistRepository.java b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/WhitelistRepository.java
new file mode 100644
index 0000000..9bfca7f
--- /dev/null
+++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/WhitelistRepository.java
@@ -0,0 +1,153 @@
+/*
+ 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()));
+ }
+}