From df7f3d969e0338acbb7efff6a3361f9aed927cf7 Mon Sep 17 00:00:00 2001 From: Guillaume Jacquart Date: Wed, 7 Sep 2022 12:30:29 +0000 Subject: 2 - Convert trackers to kotlin, fix trackers count for system app --- .../trackers/DNSBlockerRunnable.java | 173 ------- .../privacymodules/trackers/DNSBlockerRunnable.kt | 164 +++++++ .../privacymodules/trackers/DNSBlockerService.java | 86 ---- .../e/privacymodules/trackers/DNSBlockerService.kt | 80 ++++ .../privacymodules/trackers/ForegroundStarter.java | 42 -- .../e/privacymodules/trackers/ForegroundStarter.kt | 46 ++ .../e/privacymodules/trackers/TrackersLogger.java | 83 ---- .../e/privacymodules/trackers/TrackersLogger.kt | 69 +++ .../trackers/api/BlockTrackersPrivacyModule.java | 125 ----- .../trackers/api/BlockTrackersPrivacyModule.kt | 91 ++++ .../trackers/api/IBlockTrackersPrivacyModule.kt | 95 ++++ .../trackers/api/ITrackTrackersPrivacyModule.kt | 97 ++++ .../trackers/api/TrackTrackersPrivacyModule.java | 150 ------ .../trackers/api/TrackTrackersPrivacyModule.kt | 113 +++++ .../e/privacymodules/trackers/api/Tracker.kt | 28 ++ .../trackers/data/StatsDatabase.java | 507 --------------------- .../privacymodules/trackers/data/StatsDatabase.kt | 451 ++++++++++++++++++ .../trackers/data/StatsRepository.java | 95 ---- .../trackers/data/StatsRepository.kt | 83 ++++ .../trackers/data/TrackersRepository.java | 71 --- .../trackers/data/TrackersRepository.kt | 58 +++ .../trackers/data/WhitelistRepository.java | 153 ------- .../trackers/data/WhitelistRepository.kt | 128 ++++++ 23 files changed, 1503 insertions(+), 1485 deletions(-) delete mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.java create mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.kt delete mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerService.java create mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerService.kt delete mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/ForegroundStarter.java create mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/ForegroundStarter.kt delete mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.java create mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.kt delete mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/api/BlockTrackersPrivacyModule.java create mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/api/BlockTrackersPrivacyModule.kt create mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/api/IBlockTrackersPrivacyModule.kt create mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/api/ITrackTrackersPrivacyModule.kt delete mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.java create mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.kt create mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/api/Tracker.kt delete mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsDatabase.java create mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsDatabase.kt delete mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsRepository.java create mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsRepository.kt delete mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/data/TrackersRepository.java create mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/data/TrackersRepository.kt delete mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/data/WhitelistRepository.java create mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/data/WhitelistRepository.kt (limited to 'trackers/src') 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 queue; - private boolean stopped = false; - - - public TrackersLogger(Context context) { - statsRepository = StatsRepository.getInstance(context); - queue = new LinkedBlockingQueue(); - 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() + 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 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 getWhiteList(int appUid) { - List trackers = new ArrayList(); - for (String trackerId: whitelistRepository.getWhiteListForApp(appUid)) { - trackers.add(trackersRepository.getTracker(trackerId)); - } - return trackers; - } - - @Override - public List 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() + 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 { + return whitelistRepository.getWhiteListForApp(appUid).mapNotNull { + trackersRepository.getTracker(it) + } + } + + override fun getWhiteListedApp(): List { + 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 . + */ + +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 + + /** + * 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 + + /** + * 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 . + */ + +package foundation.e.privacymodules.trackers.api + +/** + * Get reporting about trackers calls. + */ +interface ITrackTrackersPrivacyModule { + + fun start(trackers: List, enableNotification: Boolean = true) + + /** + * List all the trackers encountered for a specific app. + */ + fun getTrackersForApp(appUid: Int): List + + /** + * Return the number of encountered trackers since "ever", for the given [appUids], + * or all apps if [appUids] is null + */ + fun getTrackersCount(appUids: List? = null): Int + + /** + * Return the number of encountere trackers since "ever", for each app uid. + */ + fun getTrackersCountByApp(): Map + + /** + * 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> + + /** + * 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> + + /** + * 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> + + fun getPastDayTrackersCallsByApps(): Map> + + fun getPastDayTrackersCallsForApp(appUid: Int): Pair + + 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 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 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> getPastDayTrackersCalls() { - return statsRepository.getTrackersCallsOnPeriod(24, ChronoUnit.HOURS); - } - - @Override - public List> getPastMonthTrackersCalls() { - return statsRepository.getTrackersCallsOnPeriod(30, ChronoUnit.DAYS); - } - - @Override - public List> getPastYearTrackersCalls() { - return statsRepository.getTrackersCallsOnPeriod(12, ChronoUnit.MONTHS); - } - - @Override - public int getTrackersCount() { - return statsRepository.getContactedTrackersCount(); - } - - @Override - public Map getTrackersCountByApp() { - return statsRepository.getContactedTrackersCountByApp(); - } - - @Override - public List 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> getPastDayTrackersCallsByApps() { - return statsRepository.getCallsByApps(24, ChronoUnit.HOURS); - } - - @NotNull - @Override - public Pair 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 = 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, 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> { + return statsRepository.getTrackersCallsOnPeriod(24, ChronoUnit.HOURS) + } + + override fun getPastMonthTrackersCalls(): List> { + return statsRepository.getTrackersCallsOnPeriod(30, ChronoUnit.DAYS) + } + + override fun getPastYearTrackersCalls(): List> { + return statsRepository.getTrackersCallsOnPeriod(12, ChronoUnit.MONTHS) + } + + override fun getTrackersCount(appUids: List?): Int { + return statsRepository.getContactedTrackersCount(appUids) + } + + override fun getTrackersCountByApp(): Map { + return statsRepository.getContactedTrackersCountByApp() + } + + override fun getTrackersForApp(appUid: Int): List { + 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> { + return statsRepository.getCallsByApps(24, ChronoUnit.HOURS) + } + + override fun getPastDayTrackersCallsForApp(appUid: Int): Pair { + 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 . + */ + +package foundation.e.privacymodules.trackers.api + +/** + * Describe a tracker. + */ +data class Tracker( + val id: String, + val hostnames: Set, + 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> 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> 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> callsByPeriodToPeriodsList( - Map> callsByPeriod, - int periodsCount, - TemporalUnit periodUnit, - String javaPeriodFormat - ) { - ZonedDateTime currentDate = ZonedDateTime.now().minus(periodsCount, periodUnit); - DateTimeFormatter formater = DateTimeFormatter.ofPattern(javaPeriodFormat); - - List> 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> 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> 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 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 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> 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> 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 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> callsByApp = new HashMap<>(); - - Pair 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 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 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 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> { + 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>() + 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>, + periodsCount: Int, + periodUnit: TemporalUnit, + javaPeriodFormat: String + ): List> { + var currentDate = ZonedDateTime.now().minus(periodsCount.toLong(), periodUnit) + val formater = DateTimeFormatter.ofPattern(javaPeriodFormat) + val calls = mutableListOf>() + 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> { + 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 { + 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 { + 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() + 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> { + 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>() + 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 { + 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 = 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 { + synchronized(lock) { + val columns = + arrayOf(COLUMN_NAME_TRACKER, COLUMN_NAME_APP_UID) + var selection: String? = null + var selectionArg: Array? = 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 = 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 + 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 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 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> getTrackersCallsOnPeriod(int periodsCount, TemporalUnit periodUnit) { - return database.getTrackersCallsOnPeriod(periodsCount, periodUnit); - } - - public int getActiveTrackersByPeriod(int periodsCount, TemporalUnit periodUnit) { - return database.getActiveTrackersByPeriod(periodsCount, periodUnit); - } - - public Map getContactedTrackersCountByApp() { - return database.getContactedTrackersCountByApp(); - } - - public int getContactedTrackersCount() { - return database.getContactedTrackersCount(); - } - - public List getAllTrackersOfApp(int app_uid) { - return database.getAllTrackersOfApp(app_uid); - } - - public Map> getCallsByApps(int periodCount, TemporalUnit periodUnit) { - return database.getCallsByApps(periodCount, periodUnit); - } - - public Pair 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> { + return database.getTrackersCallsOnPeriod(periodsCount, periodUnit) + } + + fun getActiveTrackersByPeriod(periodsCount: Int, periodUnit: TemporalUnit): Int { + return database.getActiveTrackersByPeriod(periodsCount, periodUnit) + } + + fun getContactedTrackersCountByApp(): Map { + return database.getContactedTrackersCountByApp() + } + + fun getContactedTrackersCount(appUids: List?): Int { + return database.getContactedTrackersCount(appUids) + } + + fun getAllTrackersOfApp(app_uid: Int): List { + return database.getAllTrackersOfApp(app_uid) + } + + fun getCallsByApps(periodCount: Int, periodUnit: TemporalUnit): Map> { + return database.getCallsByApps(periodCount, periodUnit) + } + + fun getCalls(appUid: Int, periodCount: Int, periodUnit: TemporalUnit): Pair { + 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 trackersById = new HashMap(); - private Map hostnameToId = new HashMap(); - - public void setTrackersList(List list) { - Map trackersById = new HashMap(); - Map 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 = HashMap() + private var hostnameToId: Map = HashMap() + + companion object { + private var instance: TrackersRepository? = null + fun getInstance(): TrackersRepository { + return instance?: TrackersRepository().apply { instance = this } + } + } + + fun setTrackersList(list: List) { + val trackersById: MutableMap = HashMap() + val hostnameToId: MutableMap = 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 appsWhitelist; - private Map> 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 appWhiteList = new HashSet(); - for (String appUid: prefs.getStringSet(KEY_APPS_WHITELIST, new HashSet())) { - 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())); - } - - 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 current = new HashSet(prefs.getStringSet(KEY_APPS_WHITELIST, new HashSet())); - 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 trackers; - if (trackersWhitelistByApp.containsKey(appUid)) { - trackers = trackersWhitelistByApp.get(appUid); - } else { - trackers = new HashSet(); - 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 trackers: trackersWhitelistByApp.values()) { - empty = trackers.isEmpty(); - } - - return appsWhitelist.isEmpty() && empty; - } - - public List getWhiteListedApp() { - return new ArrayList(appsWhitelist); - } - - public List 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 + private val trackersWhitelistByApp: MutableMap> = 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 get() = appsWhitelist.toList() + + fun getWhiteListForApp(appUid: Int): List { + return trackersWhitelistByApp[appUid]?.toList()?: emptyList() + } +} \ No newline at end of file -- cgit v1.2.1