From 9539160a3219483b709c8a30f7640dc3fc11d85c Mon Sep 17 00:00:00 2001 From: Guillaume Jacquart Date: Thu, 18 Aug 2022 15:31:26 +0200 Subject: [PATCH 1/4] WIP - fix trackers counts for dummy system app. --- .../usecases/TrackersStatisticsUseCase.kt | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt index 5abe0b8a..3440c78b 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt @@ -18,6 +18,7 @@ package foundation.e.privacycentralapp.domain.usecases import android.content.res.Resources +import android.util.Log import foundation.e.privacycentralapp.R import foundation.e.privacycentralapp.common.throttleFirst import foundation.e.privacycentralapp.data.repositories.AppListsRepository @@ -167,29 +168,34 @@ class TrackersStatisticsUseCase( fun getAppsWithCounts(): Flow> { val trackersCounts = trackTrackersPrivacyModule.getTrackersCountByApp() + Log.d("DebugTrackersCounts", "trackersCount: $trackersCounts") return appListsRepository.getVisibleApps() .map { apps -> val callsByApp = trackTrackersPrivacyModule.getPastDayTrackersCallsByApps() apps.map { app -> - val (blockedLeaks, leaks) = callsByApp.getOrDefault(app.uid, 0 to 0) AppWithCounts( app = app, isWhitelisted = !blockTrackersPrivacyModule.isBlockingEnabled() || blockTrackersPrivacyModule.isWhitelisted(app.uid), - trackersCount = appListsRepository.foldForHiddenSystemApp(app.uid) { - trackersCounts.getOrDefault(it, 0) - }, + trackersCount = appListsRepository.foldForHiddenSystemApp(app.uid) { appUid -> + trackersCounts.getOrDefault(appUid, 0) + }, // TODO : for dummy app, need to filter by trackers IDS. whiteListedTrackersCount = appListsRepository.foldForHiddenSystemApp(app.uid) { - blockTrackersPrivacyModule.getWhiteList(it).size + appUid -> blockTrackersPrivacyModule.getWhiteList(appUid).size + }, // TODO : for dummy app, need to filter by trackers IDS. + blockedLeaks = appListsRepository.foldForHiddenSystemApp(app.uid) { + appUid -> callsByApp.getOrDefault(appUid, 0 to 0).first }, - blockedLeaks = blockedLeaks, - leaks = leaks + leaks = appListsRepository.foldForHiddenSystemApp(app.uid) { + appUid -> callsByApp.getOrDefault(appUid, 0 to 0).second + } ) }.sortedWith(mostLeakedAppsComparator) } } + private val mostLeakedAppsComparator: Comparator = Comparator { o1, o2 -> val leaks = o2.leaks - o1.leaks if (leaks != 0) leaks else { -- GitLab From f06342317dd1c75ed7e6f1ac7b632ca4a78e9fe6 Mon Sep 17 00:00:00 2001 From: Guillaume Jacquart Date: Fri, 19 Aug 2022 11:01:13 +0200 Subject: [PATCH 2/4] Convert trackers module to kotlin --- .../trackers/ITrackTrackersPrivacyModule.kt | 2 +- trackers/build.gradle | 5 + .../trackers/DNSBlockerRunnable.java | 173 ------ .../trackers/DNSBlockerRunnable.kt | 164 ++++++ .../trackers/DNSBlockerService.java | 86 --- .../trackers/DNSBlockerService.kt | 80 +++ .../trackers/ForegroundStarter.java | 42 -- .../trackers/ForegroundStarter.kt | 46 ++ .../trackers/TrackersLogger.java | 83 --- .../privacymodules/trackers/TrackersLogger.kt | 69 +++ .../api/BlockTrackersPrivacyModule.java | 125 ----- .../api/BlockTrackersPrivacyModule.kt | 94 ++++ .../api/TrackTrackersPrivacyModule.java | 150 ------ .../api/TrackTrackersPrivacyModule.kt | 116 ++++ .../trackers/data/StatsDatabase.java | 507 ------------------ .../trackers/data/StatsDatabase.kt | 446 +++++++++++++++ .../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 +++++ 22 files changed, 1290 insertions(+), 1486 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 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 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 diff --git a/privacymodule-api/src/main/java/foundation/e/privacymodules/trackers/ITrackTrackersPrivacyModule.kt b/privacymodule-api/src/main/java/foundation/e/privacymodules/trackers/ITrackTrackersPrivacyModule.kt index 139290e8..acafe1e2 100644 --- a/privacymodule-api/src/main/java/foundation/e/privacymodules/trackers/ITrackTrackersPrivacyModule.kt +++ b/privacymodule-api/src/main/java/foundation/e/privacymodules/trackers/ITrackTrackersPrivacyModule.kt @@ -75,7 +75,7 @@ interface ITrackTrackersPrivacyModule { fun getPastDayTrackersCallsByApps(): Map> - fun getPastDayTrackersCallsForApp(appUId: Int): Pair + fun getPastDayTrackersCallsForApp(appUid: Int): Pair fun getPastDayMostLeakedApp(): Int diff --git a/trackers/build.gradle b/trackers/build.gradle index dec05ff1..409996a5 100644 --- a/trackers/build.gradle +++ b/trackers/build.gradle @@ -43,4 +43,9 @@ android { dependencies { implementation project(':privacymodule-api') + implementation( + Libs.Kotlin.stdlib, + Libs.AndroidX.coreKtx, + Libs.Coroutines.core + ) } 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 80f00c13..00000000 --- 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 00000000..01ae5b74 --- /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 6250621a..00000000 --- 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 00000000..3162422a --- /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 15631635..00000000 --- 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 00000000..30bba7b6 --- /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 37102530..00000000 --- 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 00000000..6d2abec1 --- /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 ea62766b..00000000 --- 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 00000000..5f1c5e76 --- /dev/null +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/BlockTrackersPrivacyModule.kt @@ -0,0 +1,94 @@ +/* + 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 foundation.e.privacymodules.trackers.data.WhitelistRepository +import android.content.Context +import foundation.e.privacymodules.trackers.IBlockTrackersPrivacyModule +import foundation.e.privacymodules.trackers.Tracker +import foundation.e.privacymodules.trackers.data.TrackersRepository +import java.util.ArrayList + +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/TrackTrackersPrivacyModule.java b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.java deleted file mode 100644 index 38b2c8fd..00000000 --- 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 00000000..ae372018 --- /dev/null +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.kt @@ -0,0 +1,116 @@ +/* + 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.ITrackTrackersPrivacyModule +import foundation.e.privacymodules.trackers.Tracker +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(): Int { + return statsRepository.getContactedTrackersCount() + } + + 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() + } + + +} \ No newline at end of file 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 06501149..00000000 --- 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 00000000..01a63a5e --- /dev/null +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsDatabase.kt @@ -0,0 +1,446 @@ +/* + 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 foundation.e.privacymodules.trackers.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(): Int { + synchronized(lock) { + val db = readableDatabase + val projection = + "COUNT(DISTINCT $COLUMN_NAME_TRACKER) $PROJECTION_NAME_TRACKERS_COUNT" + val cursor = db.rawQuery( + "SELECT $projection FROM $TABLE_NAME", 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 bfe688f1..00000000 --- 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 00000000..b6f9efe8 --- /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.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(): Int { + return database.getContactedTrackersCount() + } + + 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 5c77c7a3..00000000 --- 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 00000000..51b6c60e --- /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.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 9bfca7f4..00000000 --- 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 00000000..cad4eec5 --- /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.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 -- GitLab From ba967e9e607ec79bd7cbe6304a089ef18f80d11e Mon Sep 17 00:00:00 2001 From: Guillaume Jacquart Date: Fri, 19 Aug 2022 16:06:12 +0200 Subject: [PATCH 3/4] Move trackers and location interfaces from API module. Fix blocked trackers count for system and settings app. --- .../privacycentralapp/DependencyContainer.kt | 3 +- .../data/repositories/AppListsRepository.kt | 8 +++- .../data/repositories/TrackersRepository.kt | 2 +- .../domain/usecases/TrackersStateUseCase.kt | 30 ++++++++------ .../usecases/TrackersStatisticsUseCase.kt | 40 +++++++++++++------ .../domain/usecases/UpdateWidgetUseCase.kt | 2 +- .../trackers/apptrackers/AppTrackersState.kt | 2 +- .../apptrackers/AppTrackersViewModel.kt | 2 +- .../apptrackers/ToggleTrackersAdapter.kt | 2 +- .../fakelocation}/IFakeLocationModule.kt | 2 +- .../api/BlockTrackersPrivacyModule.kt | 3 -- .../api}/IBlockTrackersPrivacyModule.kt | 3 +- .../api}/ITrackTrackersPrivacyModule.kt | 7 ++-- .../api/TrackTrackersPrivacyModule.kt | 10 ++--- .../e/privacymodules/trackers/api}/Tracker.kt | 2 +- .../trackers/data/StatsDatabase.kt | 15 ++++--- .../trackers/data/StatsRepository.kt | 6 +-- .../trackers/data/TrackersRepository.kt | 2 +- .../trackers/data/WhitelistRepository.kt | 2 +- 19 files changed, 84 insertions(+), 59 deletions(-) rename {privacymodule-api/src/main/java/foundation/e/privacymodules/location => fakelocation/src/main/java/foundation/e/privacymodules/fakelocation}/IFakeLocationModule.kt (96%) rename {privacymodule-api/src/main/java/foundation/e/privacymodules/trackers => trackers/src/main/java/foundation/e/privacymodules/trackers/api}/IBlockTrackersPrivacyModule.kt (98%) rename {privacymodule-api/src/main/java/foundation/e/privacymodules/trackers => trackers/src/main/java/foundation/e/privacymodules/trackers/api}/ITrackTrackersPrivacyModule.kt (91%) rename {privacymodule-api/src/main/java/foundation/e/privacymodules/trackers => trackers/src/main/java/foundation/e/privacymodules/trackers/api}/Tracker.kt (94%) diff --git a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt index a44a00ac..2e24d4cb 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt @@ -43,7 +43,6 @@ import foundation.e.privacycentralapp.features.trackers.apptrackers.AppTrackersV import foundation.e.privacymodules.fakelocation.FakeLocationModule import foundation.e.privacymodules.ipscrambler.IpScramblerModule import foundation.e.privacymodules.ipscramblermodule.IIpScramblerModule -import foundation.e.privacymodules.location.IFakeLocationModule import foundation.e.privacymodules.permissions.PermissionsPrivacyModule import foundation.e.privacymodules.permissions.data.ApplicationDescription import foundation.e.privacymodules.trackers.api.BlockTrackersPrivacyModule @@ -98,7 +97,7 @@ class DependencyContainer(val app: Application) { } val trackersStateUseCase by lazy { - TrackersStateUseCase(blockTrackersPrivacyModule, trackTrackersPrivacyModule, permissionsModule, localStateRepository, trackersRepository, appListsRepository, GlobalScope) + TrackersStateUseCase(blockTrackersPrivacyModule, trackTrackersPrivacyModule, localStateRepository, trackersRepository, appListsRepository, GlobalScope) } private val fakeLocationStateUseCase by lazy { diff --git a/app/src/main/java/foundation/e/privacycentralapp/data/repositories/AppListsRepository.kt b/app/src/main/java/foundation/e/privacycentralapp/data/repositories/AppListsRepository.kt index 1f23516c..e151add4 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/data/repositories/AppListsRepository.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/data/repositories/AppListsRepository.kt @@ -36,6 +36,10 @@ class AppListsRepository( private val context: Context, private val coroutineScope: CoroutineScope ) { + companion object { + private const val settingsPackageName = "com.android.settings" + + } val dummySystemApp = ApplicationDescription( packageName = "foundation.e.dummysystemapp", uid = -1, @@ -96,7 +100,9 @@ class AppListsRepository( } private fun isNotHiddenSystemApp(app: ApplicationInfo, launcherApps: List): Boolean { - if (app.hasFlag(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) { + if (app.packageName == settingsPackageName) { + return false + } else if (app.hasFlag(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) { return true } else if (!app.hasFlag(ApplicationInfo.FLAG_SYSTEM)) { return true diff --git a/app/src/main/java/foundation/e/privacycentralapp/data/repositories/TrackersRepository.kt b/app/src/main/java/foundation/e/privacycentralapp/data/repositories/TrackersRepository.kt index 2b2c1dd9..b5310e1c 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/data/repositories/TrackersRepository.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/data/repositories/TrackersRepository.kt @@ -20,7 +20,7 @@ package foundation.e.privacycentralapp.data.repositories import android.content.Context import android.util.Log import com.google.gson.Gson -import foundation.e.privacymodules.trackers.Tracker +import foundation.e.privacymodules.trackers.api.Tracker import retrofit2.Retrofit import retrofit2.converter.scalars.ScalarsConverterFactory import retrofit2.http.GET diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt index 6417fce4..10c1ad0d 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt @@ -22,17 +22,15 @@ import foundation.e.privacycentralapp.data.repositories.LocalStateRepository import foundation.e.privacycentralapp.data.repositories.TrackersRepository import foundation.e.privacymodules.permissions.PermissionsPrivacyModule import foundation.e.privacymodules.permissions.data.ApplicationDescription -import foundation.e.privacymodules.trackers.IBlockTrackersPrivacyModule -import foundation.e.privacymodules.trackers.ITrackTrackersPrivacyModule -import foundation.e.privacymodules.trackers.Tracker +import foundation.e.privacymodules.trackers.api.IBlockTrackersPrivacyModule +import foundation.e.privacymodules.trackers.api.ITrackTrackersPrivacyModule +import foundation.e.privacymodules.trackers.api.Tracker import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch class TrackersStateUseCase( private val blockTrackersPrivacyModule: IBlockTrackersPrivacyModule, private val trackersPrivacyModule: ITrackTrackersPrivacyModule, - private val permissionsPrivacyModule: PermissionsPrivacyModule, private val localStateRepository: LocalStateRepository, private val trackersRepository: TrackersRepository, private val appListsRepository: AppListsRepository, @@ -65,13 +63,8 @@ class TrackersStateUseCase( return appListsRepository.getApplicationDescription(appUid) } - fun isWhitelisted(appUid: Int): Boolean { - return if (appUid == appListsRepository.dummySystemApp.uid) { - appListsRepository.getHiddenSystemApps().any { - blockTrackersPrivacyModule.isWhitelisted(it.uid) - } - } else blockTrackersPrivacyModule.isWhitelisted(appUid) - } + fun isWhitelisted(appUid: Int) + = isWhitelisted(appUid, appListsRepository, blockTrackersPrivacyModule) fun getTrackersWhitelistIds(appUid: Int): List { return if (appUid == appListsRepository.dummySystemApp.uid) { @@ -114,3 +107,16 @@ class TrackersStateUseCase( trackersPrivacyModule.start(trackersRepository.trackers, enableNotification = false) } } + + +fun isWhitelisted( + appUid: Int, + appListsRepository: AppListsRepository, + blockTrackersPrivacyModule: IBlockTrackersPrivacyModule +): Boolean { + return if (appUid == appListsRepository.dummySystemApp.uid) { + appListsRepository.getHiddenSystemApps().any { + blockTrackersPrivacyModule.isWhitelisted(it.uid) + } + } else blockTrackersPrivacyModule.isWhitelisted(appUid) +} diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt index 3440c78b..52e0bad6 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt @@ -25,9 +25,9 @@ import foundation.e.privacycentralapp.data.repositories.AppListsRepository import foundation.e.privacycentralapp.domain.entities.AppWithCounts import foundation.e.privacycentralapp.domain.entities.TrackersPeriodicStatistics import foundation.e.privacymodules.permissions.data.ApplicationDescription -import foundation.e.privacymodules.trackers.IBlockTrackersPrivacyModule -import foundation.e.privacymodules.trackers.ITrackTrackersPrivacyModule -import foundation.e.privacymodules.trackers.Tracker +import foundation.e.privacymodules.trackers.api.IBlockTrackersPrivacyModule +import foundation.e.privacymodules.trackers.api.ITrackTrackersPrivacyModule +import foundation.e.privacymodules.trackers.api.Tracker import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow @@ -168,7 +168,6 @@ class TrackersStatisticsUseCase( fun getAppsWithCounts(): Flow> { val trackersCounts = trackTrackersPrivacyModule.getTrackersCountByApp() - Log.d("DebugTrackersCounts", "trackersCount: $trackersCounts") return appListsRepository.getVisibleApps() .map { apps -> @@ -177,24 +176,41 @@ class TrackersStatisticsUseCase( AppWithCounts( app = app, isWhitelisted = !blockTrackersPrivacyModule.isBlockingEnabled() || - blockTrackersPrivacyModule.isWhitelisted(app.uid), - trackersCount = appListsRepository.foldForHiddenSystemApp(app.uid) { appUid -> - trackersCounts.getOrDefault(appUid, 0) - }, // TODO : for dummy app, need to filter by trackers IDS. - whiteListedTrackersCount = appListsRepository.foldForHiddenSystemApp(app.uid) { - appUid -> blockTrackersPrivacyModule.getWhiteList(appUid).size - }, // TODO : for dummy app, need to filter by trackers IDS. + isWhitelisted(app.uid, appListsRepository, blockTrackersPrivacyModule), + trackersCount = if (app.uid == appListsRepository.dummySystemApp.uid) { + getHiddenSystemAppsTrackersCount() + } else { + trackersCounts.getOrDefault(app.uid, 0) + }, + whiteListedTrackersCount = if (app.uid == appListsRepository.dummySystemApp.uid) { + getHiddenSystemAppWhitelistedTrackersCount() + } else { + blockTrackersPrivacyModule.getWhiteList(app.uid).size + }, blockedLeaks = appListsRepository.foldForHiddenSystemApp(app.uid) { appUid -> callsByApp.getOrDefault(appUid, 0 to 0).first }, leaks = appListsRepository.foldForHiddenSystemApp(app.uid) { - appUid -> callsByApp.getOrDefault(appUid, 0 to 0).second + appUid -> callsByApp.getOrDefault(appUid, 0 to 0).second } ) }.sortedWith(mostLeakedAppsComparator) } } + private fun getHiddenSystemAppsTrackersCount(): Int { + return trackTrackersPrivacyModule.getTrackersCount( + appListsRepository.getHiddenSystemApps().map { it.uid } + ) + } + + private fun getHiddenSystemAppWhitelistedTrackersCount(): Int { + return appListsRepository.getHiddenSystemApps().fold(HashSet()) { acc, app -> + acc.addAll(blockTrackersPrivacyModule.getWhiteList(app.uid).map { it.id }) + acc + }.size + } + private val mostLeakedAppsComparator: Comparator = Comparator { o1, o2 -> val leaks = o2.leaks - o1.leaks diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/UpdateWidgetUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/UpdateWidgetUseCase.kt index dab0b181..f70065cb 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/UpdateWidgetUseCase.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/UpdateWidgetUseCase.kt @@ -18,7 +18,7 @@ package foundation.e.privacycentralapp.domain.usecases import foundation.e.privacycentralapp.data.repositories.LocalStateRepository -import foundation.e.privacymodules.trackers.ITrackTrackersPrivacyModule +import foundation.e.privacymodules.trackers.api.ITrackTrackersPrivacyModule class UpdateWidgetUseCase( private val localStateRepository: LocalStateRepository, diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersState.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersState.kt index 9a294e29..230f8723 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersState.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersState.kt @@ -18,7 +18,7 @@ package foundation.e.privacycentralapp.features.trackers.apptrackers import foundation.e.privacymodules.permissions.data.ApplicationDescription -import foundation.e.privacymodules.trackers.Tracker +import foundation.e.privacymodules.trackers.api.Tracker data class AppTrackersState( val appDesc: ApplicationDescription? = null, diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersViewModel.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersViewModel.kt index eef75a40..120a9b25 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersViewModel.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersViewModel.kt @@ -23,7 +23,7 @@ import androidx.lifecycle.viewModelScope import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase import foundation.e.privacycentralapp.domain.usecases.TrackersStateUseCase import foundation.e.privacycentralapp.domain.usecases.TrackersStatisticsUseCase -import foundation.e.privacymodules.trackers.Tracker +import foundation.e.privacymodules.trackers.api.Tracker import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/ToggleTrackersAdapter.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/ToggleTrackersAdapter.kt index 02a274af..197f13fe 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/ToggleTrackersAdapter.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/ToggleTrackersAdapter.kt @@ -27,7 +27,7 @@ import android.widget.TextView import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import foundation.e.privacycentralapp.R -import foundation.e.privacymodules.trackers.Tracker +import foundation.e.privacymodules.trackers.api.Tracker class ToggleTrackersAdapter( private val itemsLayout: Int, diff --git a/privacymodule-api/src/main/java/foundation/e/privacymodules/location/IFakeLocationModule.kt b/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/IFakeLocationModule.kt similarity index 96% rename from privacymodule-api/src/main/java/foundation/e/privacymodules/location/IFakeLocationModule.kt rename to fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/IFakeLocationModule.kt index ecad2a48..32906f82 100644 --- a/privacymodule-api/src/main/java/foundation/e/privacymodules/location/IFakeLocationModule.kt +++ b/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/IFakeLocationModule.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package foundation.e.privacymodules.location +package foundation.e.privacymodules.fakelocation /** * Manage a fake location on the device. 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 index 5f1c5e76..0a5a75e4 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/BlockTrackersPrivacyModule.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/BlockTrackersPrivacyModule.kt @@ -20,10 +20,7 @@ package foundation.e.privacymodules.trackers.api import foundation.e.privacymodules.trackers.data.WhitelistRepository import android.content.Context -import foundation.e.privacymodules.trackers.IBlockTrackersPrivacyModule -import foundation.e.privacymodules.trackers.Tracker import foundation.e.privacymodules.trackers.data.TrackersRepository -import java.util.ArrayList class BlockTrackersPrivacyModule(context: Context) : IBlockTrackersPrivacyModule { private val mListeners = mutableListOf() diff --git a/privacymodule-api/src/main/java/foundation/e/privacymodules/trackers/IBlockTrackersPrivacyModule.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/IBlockTrackersPrivacyModule.kt similarity index 98% rename from privacymodule-api/src/main/java/foundation/e/privacymodules/trackers/IBlockTrackersPrivacyModule.kt rename to trackers/src/main/java/foundation/e/privacymodules/trackers/api/IBlockTrackersPrivacyModule.kt index 53b540e8..b07e210a 100644 --- a/privacymodule-api/src/main/java/foundation/e/privacymodules/trackers/IBlockTrackersPrivacyModule.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/IBlockTrackersPrivacyModule.kt @@ -15,8 +15,7 @@ * along with this program. If not, see . */ -package foundation.e.privacymodules.trackers - +package foundation.e.privacymodules.trackers.api /** * Manage trackers blocking and whitelisting. diff --git a/privacymodule-api/src/main/java/foundation/e/privacymodules/trackers/ITrackTrackersPrivacyModule.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/ITrackTrackersPrivacyModule.kt similarity index 91% rename from privacymodule-api/src/main/java/foundation/e/privacymodules/trackers/ITrackTrackersPrivacyModule.kt rename to trackers/src/main/java/foundation/e/privacymodules/trackers/api/ITrackTrackersPrivacyModule.kt index acafe1e2..f7beebd0 100644 --- a/privacymodule-api/src/main/java/foundation/e/privacymodules/trackers/ITrackTrackersPrivacyModule.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/ITrackTrackersPrivacyModule.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package foundation.e.privacymodules.trackers +package foundation.e.privacymodules.trackers.api /** * Get reporting about trackers calls. @@ -30,9 +30,10 @@ interface ITrackTrackersPrivacyModule { fun getTrackersForApp(appUid: Int): List /** - * Return the number of encountered trackers since "ever" + * Return the number of encountered trackers since "ever", for the given [appUids], + * or all apps if [appUids] is null */ - fun getTrackersCount(): Int + fun getTrackersCount(appUids: List? = null): Int /** * Return the number of encountere trackers since "ever", for each app uid. 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 index ae372018..7b663219 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.kt @@ -21,8 +21,6 @@ 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.ITrackTrackersPrivacyModule -import foundation.e.privacymodules.trackers.Tracker import foundation.e.privacymodules.trackers.data.StatsRepository import foundation.e.privacymodules.trackers.data.TrackersRepository import java.time.temporal.ChronoUnit @@ -64,8 +62,8 @@ class TrackTrackersPrivacyModule(private val context: Context) : ITrackTrackersP return statsRepository.getTrackersCallsOnPeriod(12, ChronoUnit.MONTHS) } - override fun getTrackersCount(): Int { - return statsRepository.getContactedTrackersCount() + override fun getTrackersCount(appUids: List?): Int { + return statsRepository.getContactedTrackersCount(appUids) } override fun getTrackersCountByApp(): Map { @@ -111,6 +109,4 @@ class TrackTrackersPrivacyModule(private val context: Context) : ITrackTrackersP override fun clearListeners() { listeners.clear() } - - -} \ No newline at end of file +} diff --git a/privacymodule-api/src/main/java/foundation/e/privacymodules/trackers/Tracker.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/Tracker.kt similarity index 94% rename from privacymodule-api/src/main/java/foundation/e/privacymodules/trackers/Tracker.kt rename to trackers/src/main/java/foundation/e/privacymodules/trackers/api/Tracker.kt index 0a4395ad..2da5b162 100644 --- a/privacymodule-api/src/main/java/foundation/e/privacymodules/trackers/Tracker.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/Tracker.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package foundation.e.privacymodules.trackers +package foundation.e.privacymodules.trackers.api /** * Describe a 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 index 01a63a5e..ba8c53c5 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsDatabase.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsDatabase.kt @@ -24,7 +24,7 @@ import android.database.Cursor import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteOpenHelper import android.provider.BaseColumns -import foundation.e.privacymodules.trackers.Tracker +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 @@ -181,14 +181,19 @@ class StatsDatabase(context: Context) : } } - fun getContactedTrackersCount(): Int { + fun getContactedTrackersCount(appUids: List?): Int { synchronized(lock) { val db = readableDatabase val projection = "COUNT(DISTINCT $COLUMN_NAME_TRACKER) $PROJECTION_NAME_TRACKERS_COUNT" - val cursor = db.rawQuery( - "SELECT $projection FROM $TABLE_NAME", arrayOf() - ) + + 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) 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 index b6f9efe8..0e881029 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsRepository.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsRepository.kt @@ -19,7 +19,7 @@ package foundation.e.privacymodules.trackers.data import android.content.Context -import foundation.e.privacymodules.trackers.Tracker +import foundation.e.privacymodules.trackers.api.Tracker import java.time.temporal.TemporalUnit class StatsRepository private constructor(context: Context) { @@ -61,8 +61,8 @@ class StatsRepository private constructor(context: Context) { return database.getContactedTrackersCountByApp() } - fun getContactedTrackersCount(): Int { - return database.getContactedTrackersCount() + fun getContactedTrackersCount(appUids: List?): Int { + return database.getContactedTrackersCount(appUids) } fun getAllTrackersOfApp(app_uid: Int): List { 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 index 51b6c60e..bc4d50b6 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/TrackersRepository.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/TrackersRepository.kt @@ -18,7 +18,7 @@ */ package foundation.e.privacymodules.trackers.data -import foundation.e.privacymodules.trackers.Tracker +import foundation.e.privacymodules.trackers.api.Tracker class TrackersRepository private constructor() { private var trackersById: Map = HashMap() 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 index cad4eec5..65a8c39a 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/WhitelistRepository.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/WhitelistRepository.kt @@ -20,7 +20,7 @@ package foundation.e.privacymodules.trackers.data import android.content.Context import android.content.SharedPreferences -import foundation.e.privacymodules.trackers.Tracker +import foundation.e.privacymodules.trackers.api.Tracker class WhitelistRepository private constructor(context: Context) { private lateinit var appsWhitelist: Set -- GitLab From 2b2fd8813ed18d7aaf9d97f60d7b2508e3b369bc Mon Sep 17 00:00:00 2001 From: Fahim Salam Chowdhury Date: Mon, 5 Sep 2022 06:08:47 +0000 Subject: [PATCH 4/4] MR suggestions --- .../e/privacycentralapp/data/repositories/AppListsRepository.kt | 2 +- .../e/privacymodules/trackers/api/BlockTrackersPrivacyModule.kt | 2 +- .../e/privacymodules/trackers/api/TrackTrackersPrivacyModule.kt | 1 + .../foundation/e/privacymodules/trackers/data/StatsDatabase.kt | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/foundation/e/privacycentralapp/data/repositories/AppListsRepository.kt b/app/src/main/java/foundation/e/privacycentralapp/data/repositories/AppListsRepository.kt index e151add4..d6af1e01 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/data/repositories/AppListsRepository.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/data/repositories/AppListsRepository.kt @@ -38,8 +38,8 @@ class AppListsRepository( ) { companion object { private const val settingsPackageName = "com.android.settings" - } + val dummySystemApp = ApplicationDescription( packageName = "foundation.e.dummysystemapp", uid = -1, 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 index 0a5a75e4..46729fdc 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/BlockTrackersPrivacyModule.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/BlockTrackersPrivacyModule.kt @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 ECORP + 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 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 index 7b663219..e0672ccc 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.kt @@ -31,6 +31,7 @@ class TrackTrackersPrivacyModule(private val context: Context) : ITrackTrackersP companion object { private var instance: TrackTrackersPrivacyModule? = null + fun getInstance(context: Context): TrackTrackersPrivacyModule { return instance?: TrackTrackersPrivacyModule(context).apply { instance = this } } 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 index ba8c53c5..86208ade 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsDatabase.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsDatabase.kt @@ -1,5 +1,5 @@ /* - Copyright (C) 2021 ECORP + 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 -- GitLab