From 0f0b9c92953fd9c37358111a17a96bb691473e39 Mon Sep 17 00:00:00 2001 From: Guillaume Jacquart Date: Mon, 6 Mar 2023 11:28:01 +0100 Subject: [PATCH 1/7] 6556: use package name and profile id to identify app instances --- .../privacycentralapp/DependencyContainer.kt | 13 +- .../data/repositories/AppListsRepository.kt | 198 ++++++++++++------ .../domain/entities/AppWithCounts.kt | 2 + .../domain/usecases/AppListUseCase.kt | 5 +- .../usecases/IpScramblingStateUseCase.kt | 2 +- .../domain/usecases/TrackersStateUseCase.kt | 38 ++-- .../usecases/TrackersStatisticsUseCase.kt | 169 +++++++-------- .../features/dashboard/DashboardViewModel.kt | 3 +- .../apptrackers/AppTrackersFragment.kt | 1 + .../apptrackers/AppTrackersViewModel.kt | 23 +- app/src/main/res/values/strings.xml | 2 +- .../android/content/pm/PackageManager.java | 27 +++ .../permissions/PermissionsPrivacyModule.kt | 116 ++++++---- .../permissions/APermissionsPrivacyModule.kt | 47 +++-- .../permissions/IPermissionsPrivacyModule.kt | 43 ++-- .../data/ApplicationDescription.kt | 22 +- .../trackers/DNSBlockerRunnable.kt | 3 +- .../api/BlockTrackersPrivacyModule.kt | 23 +- .../api/IBlockTrackersPrivacyModule.kt | 16 +- .../api/ITrackTrackersPrivacyModule.kt | 22 +- .../api/TrackTrackersPrivacyModule.kt | 33 +-- .../trackers/data/StatsDatabase.kt | 94 +++++---- .../trackers/data/StatsRepository.kt | 52 +++-- .../trackers/data/WhitelistRepository.kt | 82 +++++--- 24 files changed, 637 insertions(+), 399 deletions(-) diff --git a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt index 6ad84a7f..bad1fd08 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt @@ -47,6 +47,7 @@ import foundation.e.privacymodules.ipscrambler.IpScramblerModule import foundation.e.privacymodules.ipscramblermodule.IIpScramblerModule import foundation.e.privacymodules.permissions.PermissionsPrivacyModule import foundation.e.privacymodules.permissions.data.ApplicationDescription +import foundation.e.privacymodules.permissions.data.ProfileType import foundation.e.privacymodules.trackers.api.BlockTrackersPrivacyModule import foundation.e.privacymodules.trackers.api.TrackTrackersPrivacyModule import kotlinx.coroutines.DelicateCoroutinesApi @@ -70,7 +71,9 @@ class DependencyContainer(val app: Application) { packageName = context.packageName, uid = Process.myUid(), label = context.resources.getString(R.string.app_name), - icon = null + icon = null, + profileId = -1, + profileType = ProfileType.MAIN ) } @@ -168,12 +171,12 @@ class ViewModelsFactory( override fun create(modelClass: Class, extras: CreationExtras): T { return when (modelClass) { AppTrackersViewModel::class.java -> { - val fallbackUid = android.os.Process.myPid() - val appUid = extras[DEFAULT_ARGS_KEY] - ?.getInt(AppTrackersFragment.PARAM_APP_UID, fallbackUid) ?: fallbackUid + val app = extras[DEFAULT_ARGS_KEY]?.getInt(AppTrackersFragment.PARAM_APP_UID)?.let { + appListUseCase.getApp(it) + } ?: appListUseCase.dummySystemApp AppTrackersViewModel( - appUid = appUid, + app = app, trackersStateUseCase = trackersStateUseCase, trackersStatisticsUseCase = trackersStatisticsUseCase, getQuickPrivacyStateUseCase = getQuickPrivacyStateUseCase 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 a97888fe..7dd3e331 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 @@ -22,9 +22,11 @@ import android.content.Context import android.content.Intent import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo +import android.util.Log import foundation.e.privacycentralapp.R import foundation.e.privacymodules.permissions.PermissionsPrivacyModule import foundation.e.privacymodules.permissions.data.ApplicationDescription +import foundation.e.privacymodules.permissions.data.ProfileType import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -32,6 +34,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking class AppListsRepository( private val permissionsModule: PermissionsPrivacyModule, @@ -49,21 +52,27 @@ class AppListsRepository( ) } + private val pm get() = context.packageManager + val dummySystemApp = ApplicationDescription( packageName = "foundation.e.dummysystemapp", uid = -1, label = context.getString(R.string.dummy_system_app_label), - icon = context.getDrawable(R.drawable.ic_e_app_logo) + icon = context.getDrawable(R.drawable.ic_e_app_logo), + profileId = -1, + profileType = ProfileType.MAIN ) val dummyAppsCompatibilityApp = ApplicationDescription( packageName = "foundation.e.dummyappscompatibilityapp", uid = -2, label = context.getString(R.string.dummy_apps_compatibility_app_label), - icon = context.getDrawable(R.drawable.ic_apps_compatibility_components) + icon = context.getDrawable(R.drawable.ic_apps_compatibility_components), + profileId = -1, + profileType = ProfileType.MAIN ) - private suspend fun fetchAppDescriptions() { + private suspend fun fetchAppDescriptions(fetchMissingIcons: Boolean = false) { val launcherPackageNames = pm.queryIntentActivities( Intent(Intent.ACTION_MAIN, null).apply { addCategory(Intent.CATEGORY_LAUNCHER) }, 0 @@ -83,100 +92,163 @@ class AppListsRepository( packageInfo.packageName in appsCompatibiltyPNames } - val visibleApps = permissionsModule.getApplications(visibleAppsFilter, true) - val hiddenApps = permissionsModule.getApplications(hiddenAppsFilter, false) - val aCApps = permissionsModule.getApplications(aCFilter, false) + val visibleApps = recycleIcons( + newApps = permissionsModule.getApplications(visibleAppsFilter), + fetchMissingIcons = fetchMissingIcons + ) + val hiddenApps = permissionsModule.getApplications(hiddenAppsFilter) + val aCApps = permissionsModule.getApplications(aCFilter) - val workProfileVisibleApps = permissionsModule.getWorkProfileApplications(visibleAppsFilter, true) - val workProfileHiddenApps = permissionsModule.getWorkProfileApplications(hiddenAppsFilter, false) - val workProfileACApps = permissionsModule.getApplications(aCFilter, false) + updateMaps(visibleApps + hiddenApps + aCApps) - appDescriptions.emit((visibleApps + dummySystemApp + dummyAppsCompatibilityApp) to hiddenApps) allProfilesAppDescriptions.emit(Triple( - (visibleApps + workProfileVisibleApps + dummySystemApp + dummyAppsCompatibilityApp), - (hiddenApps + workProfileHiddenApps), - (aCApps + workProfileACApps) + visibleApps + dummySystemApp + dummyAppsCompatibilityApp, + hiddenApps, + aCApps )) } + private fun recycleIcons( + newApps: List, + fetchMissingIcons: Boolean + ): List { + val oldVisibleApps = allProfilesAppDescriptions.value.first + return newApps.map { app -> + app.copy(icon = oldVisibleApps.find { app.apId == it.apId }?.icon + ?: if (fetchMissingIcons) permissionsModule.getApplicationIcon(app) else null + ) + } + } + + private fun updateMaps(apps: List) { + val byUid = mutableMapOf() + val byApId = mutableMapOf() + apps.forEach { app -> + if (byUid[app.uid].let { it == null || it.packageName > app.packageName }) { + byUid[app.uid] = app + } + + byApId[app.apId] = app + } + appsByUid = byUid + appsByAPId = byApId + } + + private var lastFetchApps = 0 private var refreshAppJob: Job? = null - private fun refreshAppDescriptions() { - if (refreshAppJob != null) { - return - } else { + private fun refreshAppDescriptions(fetchMissingIcons: Boolean = true, force: Boolean = false): Job? { + Log.d("DebugFetchApps", "refreshAppDescriptions: $fetchMissingIcons - $force") + if (refreshAppJob == null) { refreshAppJob = coroutineScope.launch(Dispatchers.IO) { - fetchAppDescriptions() - refreshAppJob = null + if (force || pm.getChangedPackages(lastFetchApps) != null) { + Log.d("DebugFetchApps", "do fetchAppDescriptions") + fetchAppDescriptions(fetchMissingIcons = fetchMissingIcons) + lastFetchApps = pm.getChangedPackages(lastFetchApps)?.sequenceNumber + ?: lastFetchApps + Log.d("DebugFetchApps", "lastFetchApps updated : $lastFetchApps") + + refreshAppJob = null + } } } + + return refreshAppJob } - fun getVisibleApps(): Flow> { + // // Visible apps. + // fun getApps(): List { + // + // } + + // fun apps(): Flow> { + // + // } + // + // /// Visible AND hidden apps + // fun allApps(): Flow> { + // + // } + + fun mainProfileApps(): Flow> { refreshAppDescriptions() - return appDescriptions.map { it.first.sortedBy { app -> app.label.toString().lowercase() } } + return allProfilesAppDescriptions.map { + it.first.filter { app -> app.profileType == ProfileType.MAIN } + .sortedBy { app -> app.label.toString().lowercase() } + } } - fun getHiddenSystemApps(): List { - return appDescriptions.value.second + fun getMainProfileHiddenSystemApps(): List { + return allProfilesAppDescriptions.value.second.filter { it.profileType == ProfileType.MAIN } } - fun getAllProfilesVisibleApps(): Flow> { + fun apps(): Flow> { refreshAppDescriptions() - return allProfilesAppDescriptions.map { it.first.sortedBy { app -> app.label.toString().lowercase() } } + return allProfilesAppDescriptions.map { + it.first.sortedBy { app -> app.label.toString().lowercase() } + } } - fun getAllProfilesHiddenSystemApps(): List { - return allProfilesAppDescriptions.value.second + fun allApps(): Flow> { + return allProfilesAppDescriptions.map { + it.first + it.second + it.third + } } - fun getAllProfilesACApps(): List { - return allProfilesAppDescriptions.value.third + private fun getHiddenSystemApps(): List { + return allProfilesAppDescriptions.value.second } - fun getAllApps(): Flow> = getAllProfilesVisibleApps() - .map { it + getAllProfilesHiddenSystemApps() + getAllProfilesACApps()} - - fun getApplicationDescription(appUid: Int): ApplicationDescription? { - return allProfilesAppDescriptions.value.first.find { it.uid == appUid } + private fun getACApps(): List { + return allProfilesAppDescriptions.value.third } - fun foldForHiddenApp(appUid: Int, appValueGetter: (Int) -> Int): Int { - return if (appUid == dummySystemApp.uid) { - getAllProfilesHiddenSystemApps().fold(0) { acc, app -> - acc + appValueGetter(app.uid) - } - } else if (appUid == dummyAppsCompatibilityApp.uid) { - getAllProfilesACApps().fold(0) { acc, app -> - acc + appValueGetter(app.uid) - } - } else appValueGetter(appUid) + // fun getAllApps(): Flow> = apps() + // .map { it + getHiddenSystemApps() + getACApps()} + // + // fun getApplicationDescription(appUid: Int): ApplicationDescription? { + // return allProfilesAppDescriptions.value.first.find { it.uid == appUid } + // } + + fun anyForHiddenApps(app: ApplicationDescription, test: (ApplicationDescription) -> Boolean): Boolean { + return if (app == dummySystemApp) { + getHiddenSystemApps().any { test(it) } + } else if (app == dummyAppsCompatibilityApp) { + getACApps().any { test(it) } + } else test(app) } - fun anyForHiddenApps(appUid: Int, test: (Int) -> Boolean): Boolean { - return if (appUid == dummySystemApp.uid) { - getAllProfilesHiddenSystemApps().any { test(it.uid) } - } else if (appUid == dummyAppsCompatibilityApp.uid) { - getAllProfilesACApps().any { test(it.uid) } - } else test(appUid) + fun applyForHiddenApps(app: ApplicationDescription, action: (ApplicationDescription) -> Unit) { + mapReduceForHiddenApps(app = app, map = action, reduce = {}) } - fun applyForHiddenApps(appUid: Int, action: (Int) -> Unit) { - if (appUid == dummySystemApp.uid) { - getAllProfilesHiddenSystemApps().forEach { action(it.uid) } - } else if (appUid == dummyAppsCompatibilityApp.uid) { - getAllProfilesACApps().forEach { action(it.uid) } - } else action(appUid) + fun mapReduceForHiddenApps( + app: ApplicationDescription, + map: (ApplicationDescription) -> T, + reduce: (List) -> R + ): R { + return if (app == dummySystemApp) { + reduce(getHiddenSystemApps().map(map)) + } else if (app == dummyAppsCompatibilityApp) { + reduce(getACApps().map(map)) + } else reduce(listOf(map(app))) } + private var appsByUid = mapOf() + private var appsByAPId = mapOf() - private val pm get() = context.packageManager + fun getApp(appUid: Int): ApplicationDescription? { + return appsByUid[appUid] ?: run { + runBlocking { refreshAppDescriptions(fetchMissingIcons = false, force = true)?.join() } + appsByUid[appUid] + } + } - private val appDescriptions = MutableStateFlow( - Pair( - emptyList(), - emptyList() - ) - ) + fun getApp(apId: String): ApplicationDescription? { + return appsByAPId[apId]?: run { + runBlocking { refreshAppDescriptions(fetchMissingIcons = false, force = true)?.join() } + appsByAPId[apId] + } + } private val allProfilesAppDescriptions = MutableStateFlow( Triple( diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/entities/AppWithCounts.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/AppWithCounts.kt index 0b76c7b9..a1d914f4 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/domain/entities/AppWithCounts.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/AppWithCounts.kt @@ -21,6 +21,7 @@ import android.graphics.drawable.Drawable import foundation.e.privacymodules.permissions.data.ApplicationDescription data class AppWithCounts( + val appDesc: ApplicationDescription, val packageName: String, val uid: Int, var label: CharSequence?, @@ -40,6 +41,7 @@ data class AppWithCounts( leaks: Int, ) : this( + appDesc = app, packageName = app.packageName, uid = app.uid, label = app.label, diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/AppListUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/AppListUseCase.kt index 48213496..8db31187 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/AppListUseCase.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/AppListUseCase.kt @@ -24,8 +24,9 @@ import kotlinx.coroutines.flow.Flow class AppListUseCase( private val appListsRepository: AppListsRepository ) { - + val dummySystemApp = appListsRepository.dummySystemApp + fun getApp(uid: Int): ApplicationDescription? = appListsRepository.getApp(uid) fun getAppsUsingInternet(): Flow> { - return appListsRepository.getVisibleApps() + return appListsRepository.mainProfileApps() } } diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/IpScramblingStateUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/IpScramblingStateUseCase.kt index caba132d..ac853f1d 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/IpScramblingStateUseCase.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/IpScramblingStateUseCase.kt @@ -86,7 +86,7 @@ class IpScramblingStateUseCase( } private fun getHiddenPackageNames(): List { - return appListsRepository.getHiddenSystemApps().map { it.packageName } + return appListsRepository.getMainProfileHiddenSystemApps().map { it.packageName } } val bypassTorApps: Set 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 820073bc..aa3c209c 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 @@ -36,7 +36,11 @@ class TrackersStateUseCase( private val coroutineScope: CoroutineScope ) { init { - trackersPrivacyModule.start(trackersRepository.trackers, enableNotification = false) + trackersPrivacyModule.start( + trackers = trackersRepository.trackers, + getAppByAPId = appListsRepository::getApp, + getAppByUid = appListsRepository::getApp, + enableNotification = false) coroutineScope.launch { localStateRepository.blockTrackers.collect { enabled -> if (enabled) { @@ -54,39 +58,39 @@ class TrackersStateUseCase( blockTrackersPrivacyModule.isWhiteListEmpty() } - fun getApplicationDescription(appUid: Int): ApplicationDescription? { - return appListsRepository.getApplicationDescription(appUid) + fun isWhitelisted(app: ApplicationDescription): Boolean { + return isWhitelisted(app, appListsRepository, blockTrackersPrivacyModule) } - fun isWhitelisted(appUid: Int): Boolean { - return isWhitelisted(appUid, appListsRepository, blockTrackersPrivacyModule) - } - - fun toggleAppWhitelist(appUid: Int, isWhitelisted: Boolean) { - appListsRepository.applyForHiddenApps(appUid) { uid -> - blockTrackersPrivacyModule.setWhiteListed(uid, isWhitelisted) + fun toggleAppWhitelist(app: ApplicationDescription, isWhitelisted: Boolean) { + appListsRepository.applyForHiddenApps(app) { + blockTrackersPrivacyModule.setWhiteListed(it, isWhitelisted) } - updateAllTrackersBlockedState() } - fun blockTracker(appUid: Int, tracker: Tracker, isBlocked: Boolean) { - appListsRepository.applyForHiddenApps(appUid) { uid -> - blockTrackersPrivacyModule.setWhiteListed(tracker, uid, !isBlocked) + fun blockTracker(app: ApplicationDescription, tracker: Tracker, isBlocked: Boolean) { + appListsRepository.applyForHiddenApps(app) { + blockTrackersPrivacyModule.setWhiteListed(tracker, it, !isBlocked) } updateAllTrackersBlockedState() } fun updateTrackers() = coroutineScope.launch { trackersRepository.update() - trackersPrivacyModule.start(trackersRepository.trackers, enableNotification = false) + trackersPrivacyModule.start( + trackers = trackersRepository.trackers, + getAppByAPId = appListsRepository::getApp, + getAppByUid = appListsRepository::getApp, + enableNotification = false + ) } } fun isWhitelisted( - appUid: Int, + app: ApplicationDescription, appListsRepository: AppListsRepository, blockTrackersPrivacyModule: IBlockTrackersPrivacyModule ): Boolean { - return appListsRepository.anyForHiddenApps(appUid, blockTrackersPrivacyModule::isWhitelisted) + return appListsRepository.anyForHiddenApps(app, blockTrackersPrivacyModule::isWhitelisted) } 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 cc6ec451..487a61e4 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 @@ -47,7 +47,7 @@ class TrackersStatisticsUseCase( private val resources: Resources ) { fun initAppList() { - appListsRepository.getAllProfilesVisibleApps() + appListsRepository.apps() } private fun rawUpdates(): Flow = callbackFlow { @@ -65,6 +65,7 @@ class TrackersStatisticsUseCase( .throttleFirst(windowDuration = debounce) .onStart { emit(Unit) } + fun getDayStatistics(): Pair { return TrackersPeriodicStatistics( callsBlockedNLeaked = trackTrackersPrivacyModule.getPastDayTrackersCalls(), @@ -74,10 +75,26 @@ class TrackersStatisticsUseCase( ) to trackTrackersPrivacyModule.getTrackersCount() } + fun getNonBlockedTrackersCount(): Flow { + return if (blockTrackersPrivacyModule.isBlockingEnabled()) + appListsRepository.allApps().map { apps -> + val whiteListedTrackers = mutableSetOf() + val whiteListedApps = blockTrackersPrivacyModule.getWhiteListedApp() + apps.forEach { app -> + if (app in whiteListedApps) { + whiteListedTrackers.addAll(trackTrackersPrivacyModule.getTrackersForApp(app)) + } else { + whiteListedTrackers.addAll(blockTrackersPrivacyModule.getWhiteList(app)) + } + } + whiteListedTrackers.size + } + else flowOf(trackTrackersPrivacyModule.getTrackersCount()) + } + + fun getMostLeakedApp(): ApplicationDescription? { - return appListsRepository.getApplicationDescription( - trackTrackersPrivacyModule.getPastDayMostLeakedApp() - ) + return trackTrackersPrivacyModule.getPastDayMostLeakedApp() } fun getDayTrackersCalls() = trackTrackersPrivacyModule.getPastDayTrackersCalls() @@ -161,103 +178,78 @@ class TrackersStatisticsUseCase( } } - fun getTrackers(appUid: Int): List { - val trackers = if (appUid == appListsRepository.dummySystemApp.uid) { - appListsRepository.getAllProfilesHiddenSystemApps().map { - trackTrackersPrivacyModule.getTrackersForApp(it.uid) - }.flatten().distinctBy { it.id } - } else if (appUid == appListsRepository.dummyAppsCompatibilityApp.uid) { - appListsRepository.getAllProfilesACApps().map { - trackTrackersPrivacyModule.getTrackersForApp(it.uid) - }.flatten().distinctBy { it.id } - } else trackTrackersPrivacyModule.getTrackersForApp(appUid) - - return trackers.sortedBy { it.label.lowercase() } - } + fun getTrackersWithWhiteList(app: ApplicationDescription): List> { + return appListsRepository.mapReduceForHiddenApps( + app = app, + map = { appDesc: ApplicationDescription -> + trackTrackersPrivacyModule.getTrackersForApp(appDesc) to + blockTrackersPrivacyModule.getWhiteList(appDesc) + }, + reduce = { lists -> + lists.unzip().let { (trackerLists, whiteListedIdLists) -> + val whiteListedIds = whiteListedIdLists.flatten().map { it.id }.toSet() - fun getTrackersWithWhiteList(appUid: Int): List> { - val trackers: List - val whiteListedTrackersIds: Set - if (appUid == appListsRepository.dummySystemApp.uid) { - val hiddenApps = appListsRepository.getAllProfilesHiddenSystemApps() - trackers = trackTrackersPrivacyModule.getTrackers(hiddenApps.map { it.uid }) - - whiteListedTrackersIds = hiddenApps.fold(HashSet()) { acc, app -> - acc.addAll(blockTrackersPrivacyModule.getWhiteList(app.uid).map { it.id }) - acc - } - } else if (appUid == appListsRepository.dummyAppsCompatibilityApp.uid) { - val acApps = appListsRepository.getAllProfilesACApps() - trackers = trackTrackersPrivacyModule.getTrackers(acApps.map { it.uid }) - - whiteListedTrackersIds = acApps.fold(HashSet()) { acc, app -> - acc.addAll(blockTrackersPrivacyModule.getWhiteList(app.uid).map { it.id }) - acc + trackerLists.flatten().distinctBy { it.id }.sortedBy { it.label.lowercase() } + .map { tracker -> tracker to (tracker.id in whiteListedIds) } + } } - } else { - trackers = trackTrackersPrivacyModule.getTrackersForApp(appUid) - whiteListedTrackersIds = blockTrackersPrivacyModule.getWhiteList(appUid) - .map { it.id }.toSet() - } - - return trackers.sortedBy { it.label.lowercase() }.map { tracker -> tracker to whiteListedTrackersIds.any { tracker.id == it } } + ) } - fun getCalls(appUid: Int): Pair { - return if (appUid == appListsRepository.dummySystemApp.uid) { - appListsRepository.getAllProfilesHiddenSystemApps().map { - trackTrackersPrivacyModule.getPastDayTrackersCallsForApp(it.uid) - }.reduce { (accBlocked, accLeaked), (blocked, leaked) -> - accBlocked + blocked to accLeaked + leaked - } - } else if (appUid == appListsRepository.dummyAppsCompatibilityApp.uid) { - appListsRepository.getAllProfilesACApps().map { - trackTrackersPrivacyModule.getPastDayTrackersCallsForApp(it.uid) - }.reduce { (accBlocked, accLeaked), (blocked, leaked) -> - accBlocked + blocked to accLeaked + leaked + fun getCalls(app: ApplicationDescription): Pair { + return appListsRepository.mapReduceForHiddenApps( + app = app, + map = trackTrackersPrivacyModule::getPastDayTrackersCallsForApp, + reduce = { zip -> + zip.unzip().let { (blocked, leaked) -> + blocked.sum() to leaked.sum() + } } - } else trackTrackersPrivacyModule.getPastDayTrackersCallsForApp(appUid) + ) } fun getAppsWithCounts(): Flow> { val trackersCounts = trackTrackersPrivacyModule.getTrackersCountByApp() val hiddenAppsTrackersWithWhiteList = - getTrackersWithWhiteList(appListsRepository.dummySystemApp.uid) + getTrackersWithWhiteList(appListsRepository.dummySystemApp) val acAppsTrackersWithWhiteList = - getTrackersWithWhiteList(appListsRepository.dummyAppsCompatibilityApp.uid) + getTrackersWithWhiteList(appListsRepository.dummyAppsCompatibilityApp) - return appListsRepository.getAllProfilesVisibleApps() + return appListsRepository.apps() .map { apps -> val callsByApp = trackTrackersPrivacyModule.getPastDayTrackersCallsByApps() apps.map { app -> + val calls = appListsRepository.mapReduceForHiddenApps( + app = app, + map = { callsByApp.getOrDefault(app, 0 to 0) }, + reduce = { it.unzip().let { (blocked, leaked) -> + blocked.sum() to leaked.sum() } } + ) + AppWithCounts( app = app, isWhitelisted = !blockTrackersPrivacyModule.isBlockingEnabled() || - isWhitelisted(app.uid, appListsRepository, blockTrackersPrivacyModule), - trackersCount = if (app.uid == appListsRepository.dummySystemApp.uid) { - hiddenAppsTrackersWithWhiteList.size - } else if (app.uid == appListsRepository.dummyAppsCompatibilityApp.uid) { - acAppsTrackersWithWhiteList.size - } else { - trackersCounts.getOrDefault(app.uid, 0) - }, - whiteListedTrackersCount = if (app.uid == appListsRepository.dummySystemApp.uid) { - hiddenAppsTrackersWithWhiteList.count { it.second } - } else if (app.uid == appListsRepository.dummyAppsCompatibilityApp.uid) { - acAppsTrackersWithWhiteList.count { it.second } - } else { - blockTrackersPrivacyModule.getWhiteList(app.uid).size + isWhitelisted(app, appListsRepository, blockTrackersPrivacyModule), + trackersCount = when(app) { + appListsRepository.dummySystemApp -> + hiddenAppsTrackersWithWhiteList.size + appListsRepository.dummyAppsCompatibilityApp -> + acAppsTrackersWithWhiteList.size + else -> trackersCounts.getOrDefault(app, 0) }, - blockedLeaks = appListsRepository.foldForHiddenApp(app.uid) { - appUid -> - callsByApp.getOrDefault(appUid, 0 to 0).first + whiteListedTrackersCount = when(app) { + appListsRepository.dummySystemApp -> + hiddenAppsTrackersWithWhiteList.count { it.second } + appListsRepository.dummyAppsCompatibilityApp -> + acAppsTrackersWithWhiteList.count { it.second } + else -> + blockTrackersPrivacyModule.getWhiteList(app).size }, - leaks = appListsRepository.foldForHiddenApp(app.uid) { - appUid -> - callsByApp.getOrDefault(appUid, 0 to 0).second - } + blockedLeaks = calls.first, + leaks = calls.second ) - }.sortedWith(mostLeakedAppsComparator) + } + .sortedWith(mostLeakedAppsComparator) } } @@ -270,21 +262,4 @@ class TrackersStatisticsUseCase( } } } - - fun getNonBlockedTrackersCount(): Flow { - return if (blockTrackersPrivacyModule.isBlockingEnabled()) - appListsRepository.getAllApps().map { apps -> - val whiteListedTrackers = mutableSetOf() - val whiteListedAppUids = blockTrackersPrivacyModule.getWhiteListedApp() - apps.forEach { app -> - if (app.uid in whiteListedAppUids) { - whiteListedTrackers.addAll(getTrackers(app.uid)) - } else { - whiteListedTrackers.addAll(blockTrackersPrivacyModule.getWhiteList(app.uid)) - } - } - whiteListedTrackers.size - } - else flowOf(trackTrackersPrivacyModule.getTrackersCount()) - } } diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardViewModel.kt b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardViewModel.kt index ead01a51..dd2e5d38 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardViewModel.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardViewModel.kt @@ -51,7 +51,8 @@ class DashboardViewModel( val singleEvents = _singleEvents.asSharedFlow() init { - viewModelScope.launch(Dispatchers.IO) { trackersStatisticsUseCase.initAppList() } + viewModelScope.launch(Dispatchers.IO) { trackersStatisticsUseCase.initAppList() // TODO get rid of that ? + } } suspend fun doOnStartedState() = withContext(Dispatchers.IO) { diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt index f15119e8..32461494 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt @@ -35,6 +35,7 @@ import foundation.e.privacycentralapp.PrivacyCentralApplication import foundation.e.privacycentralapp.R import foundation.e.privacycentralapp.common.NavToolbarFragment import foundation.e.privacycentralapp.databinding.ApptrackersFragmentBinding +import foundation.e.privacymodules.permissions.data.ApplicationDescription import kotlinx.coroutines.launch class AppTrackersFragment : NavToolbarFragment(R.layout.apptrackers_fragment) { 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 1a338449..07b9152a 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 @@ -25,6 +25,7 @@ import foundation.e.privacycentralapp.domain.entities.TrackerMode 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.permissions.data.ApplicationDescription import foundation.e.privacymodules.trackers.api.Tracker import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableSharedFlow @@ -38,7 +39,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext class AppTrackersViewModel( - private val appUid: Int, + private val app: ApplicationDescription, private val trackersStateUseCase: TrackersStateUseCase, private val trackersStatisticsUseCase: TrackersStatisticsUseCase, private val getQuickPrivacyStateUseCase: GetQuickPrivacyStateUseCase @@ -57,9 +58,11 @@ class AppTrackersViewModel( viewModelScope.launch(Dispatchers.IO) { _state.update { it.copy( - appDesc = trackersStateUseCase.getApplicationDescription(appUid), - isBlockingActivated = !trackersStateUseCase.isWhitelisted(appUid), - trackersWithWhiteList = trackersStatisticsUseCase.getTrackersWithWhiteList(appUid), + appDesc = app, + isBlockingActivated = !trackersStateUseCase.isWhitelisted(app), + trackersWithWhiteList = trackersStatisticsUseCase.getTrackersWithWhiteList( + app + ), ) } } @@ -87,10 +90,10 @@ class AppTrackersViewModel( if (!state.value.isTrackersBlockingEnabled) { _singleEvents.emit(SingleEvent.ToastTrackersControlDisabled) } - trackersStateUseCase.toggleAppWhitelist(appUid, !action.isBlocked) + trackersStateUseCase.toggleAppWhitelist(app, !action.isBlocked) _state.update { it.copy( - isBlockingActivated = !trackersStateUseCase.isWhitelisted(appUid) + isBlockingActivated = !trackersStateUseCase.isWhitelisted(app) ) } } @@ -103,11 +106,11 @@ class AppTrackersViewModel( } if (state.value.isBlockingActivated) { - trackersStateUseCase.blockTracker(appUid, action.tracker, action.isBlocked) + trackersStateUseCase.blockTracker(app, action.tracker, action.isBlocked) _state.update { it.copy( trackersWithWhiteList = trackersStatisticsUseCase.getTrackersWithWhiteList( - appUid + app ) ) } @@ -131,10 +134,10 @@ class AppTrackersViewModel( } private fun fetchStatistics() { - val (blocked, leaked) = trackersStatisticsUseCase.getCalls(appUid) + val (blocked, leaked) = trackersStatisticsUseCase.getCalls(app) return _state.update { s -> s.copy( - trackersWithWhiteList = trackersStatisticsUseCase.getTrackersWithWhiteList(appUid), + trackersWithWhiteList = trackersStatisticsUseCase.getTrackersWithWhiteList(app), leaked = leaked, blocked = blocked, ) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 38cd38bd..fc53c3d9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,6 +1,6 @@ - Advanced Privacy + Advanced Privacy_2 Close diff --git a/permissionse/libs/hidden-apis-stub/src/main/java/android/content/pm/PackageManager.java b/permissionse/libs/hidden-apis-stub/src/main/java/android/content/pm/PackageManager.java index 3f232bfa..c6232ce3 100644 --- a/permissionse/libs/hidden-apis-stub/src/main/java/android/content/pm/PackageManager.java +++ b/permissionse/libs/hidden-apis-stub/src/main/java/android/content/pm/PackageManager.java @@ -26,11 +26,26 @@ import androidx.annotation.NonNull; import androidx.annotation.RequiresPermission; import java.util.List; +import android.util.AndroidException; // Stub based on: // https://gitlab.e.foundation/e/os/android_frameworks_base/-/blob/[SDK_VERSION]/core/java/android/content/pm/PackageManager.java public abstract class PackageManager { + @TargetApi(29) + @DeprecatedSinceApi( + api = 34, + message = "Check availability in SDK34" + ) + public static class NameNotFoundException extends AndroidException { + public NameNotFoundException() { + } + + public NameNotFoundException(String name) { + super(name); + } + } + @TargetApi(29) @DeprecatedSinceApi( @@ -56,6 +71,18 @@ public abstract class PackageManager { @NonNull UserHandle user ); + @TargetApi(29) + @DeprecatedSinceApi( + api = 33, + message = "@deprecated Use {@link #getApplicationInfoAsUser(String, ApplicationInfoFlags, int)} instead." + ) + public abstract ApplicationInfo getApplicationInfoAsUser( + @NonNull String packageName, + int flags, + int userId + ) throws NameNotFoundException; + + @TargetApi(29) @DeprecatedSinceApi( api = 34, diff --git a/permissionse/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt b/permissionse/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt index cde4afba..51042e7b 100644 --- a/permissionse/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt +++ b/permissionse/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt @@ -26,6 +26,7 @@ import android.content.Context import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.content.pm.UserInfo +import android.graphics.drawable.Drawable import android.net.IConnectivityManager import android.net.VpnManager import android.net.VpnManager.TYPE_VPN_SERVICE @@ -36,6 +37,9 @@ import android.os.UserManager import android.util.Log import foundation.e.privacymodules.permissions.data.AppOpModes import foundation.e.privacymodules.permissions.data.ApplicationDescription +import foundation.e.privacymodules.permissions.data.ProfileType +import foundation.e.privacymodules.permissions.data.ProfileType.MAIN +import foundation.e.privacymodules.permissions.data.ProfileType.WORK /** * Implements [IPermissionsPrivacyModule] with all privileges of a system app. @@ -88,6 +92,79 @@ class PermissionsPrivacyModule(context: Context) : APermissionsPrivacyModule(con return true } + private fun getWorkProfile(): UserInfo? { + val userManager: UserManager = context.getSystemService(UserManager::class.java) + val userId = UserHandle.myUserId() + for (user in userManager.getProfiles(UserHandle.myUserId())) { + if (user.id != userId && userManager.isManagedProfile(user.id)) { + return user + } + } + return null + } + + override fun getApplications( + filter: ((PackageInfo) -> Boolean)? + ): List { + val pm = context.packageManager + val mainUserId = UserHandle.myUserId() + val workProfileId = getWorkProfile()?.id + + val userIds = listOf(mainUserId, workProfileId).filterNotNull() + return userIds.map { profileId -> + pm.getInstalledPackagesAsUser(PackageManager.GET_PERMISSIONS, profileId) + .filter { filter?.invoke(it) ?: true } + .map { + buildApplicationDescription( + appInfo = it.applicationInfo, + profileId = profileId, + profileType = if (profileId == mainUserId) MAIN else WORK + ) + } + }.flatten() + } + + // override fun getApplications( + // filter: ((PackageInfo) -> Boolean)?, + // withIcon: Boolean + // ): List { + // return context.packageManager + // .getInstalledPackages(PackageManager.GET_PERMISSIONS) + // .filter { filter?.invoke(it) ?: true } + // .map { buildApplicationDescription(it.applicationInfo, withIcon = withIcon) } + // } + // + // override fun getWorkProfileApplications( + // filter: ((PackageInfo) -> Boolean)?, + // withIcon: Boolean + // ): List { + // val pm = context.packageManager + // return getWorkProfile()?.let { workProfile -> + // pm.getInstalledPackagesAsUser(PackageManager.GET_PERMISSIONS, workProfile.id) + // .filter { filter?.invoke(it) ?: true } + // .map { + // val appDesc = buildApplicationDescription(it.applicationInfo, withIcon = withIcon) + // appDesc.icon = appDesc.icon?.let { + // pm.getUserBadgedIcon(it, workProfile.getUserHandle()) + // } + // appDesc + // } + // } ?: emptyList() + // } + + override fun getApplicationIcon(app: ApplicationDescription): Drawable? { + return if (app.profileType == WORK) { + getWorkProfile()?.let { workProfile -> + val pm = context.packageManager + getApplicationIcon( + pm.getApplicationInfoAsUser(app.packageName, 0, workProfile.id) + )?.let { + pm.getUserBadgedIcon(it, workProfile.getUserHandle()) + } + } + } else getApplicationIcon(app.packageName) + } + override fun setVpnPackageAuthorization(packageName: String): Boolean { return when (Build.VERSION.SDK_INT) { 29 -> setVpnPackageAuthorizationSDK29(packageName) @@ -161,45 +238,6 @@ class PermissionsPrivacyModule(context: Context) : APermissionsPrivacyModule(con return false } - private fun getWorkProfile(): UserInfo? { - val userManager: UserManager = context.getSystemService(UserManager::class.java) - val userId = UserHandle.myUserId() - for (user in userManager.getProfiles(UserHandle.myUserId())) { - if (user.id != userId && userManager.isManagedProfile(user.id)) { - return user - } - } - return null - } - - override fun getApplications( - filter: ((PackageInfo) -> Boolean)?, - withIcon: Boolean - ): List { - return context.packageManager - .getInstalledPackages(PackageManager.GET_PERMISSIONS) - .filter { filter?.invoke(it) ?: true } - .map { buildApplicationDescription(it.applicationInfo, withIcon = withIcon) } - } - - override fun getWorkProfileApplications( - filter: ((PackageInfo) -> Boolean)?, - withIcon: Boolean - ): List { - val pm = context.packageManager - return getWorkProfile()?.let { workProfile -> - pm.getInstalledPackagesAsUser(PackageManager.GET_PERMISSIONS, workProfile.id) - .filter { filter?.invoke(it) ?: true } - .map { - val appDesc = buildApplicationDescription(it.applicationInfo, withIcon = withIcon) - appDesc.icon = appDesc.icon?.let { - pm.getUserBadgedIcon(it, workProfile.getUserHandle()) - } - appDesc - } - } ?: emptyList() - } - override fun getAlwaysOnVpnPackage(): String? { return when (Build.VERSION.SDK_INT) { 29, 30 -> getAlwaysOnVpnPackageSDK29() diff --git a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/APermissionsPrivacyModule.kt b/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/APermissionsPrivacyModule.kt index d0e2e751..70c2214c 100644 --- a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/APermissionsPrivacyModule.kt +++ b/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/APermissionsPrivacyModule.kt @@ -29,6 +29,7 @@ import android.util.Log import foundation.e.privacymodules.permissions.data.AppOpModes import foundation.e.privacymodules.permissions.data.ApplicationDescription import foundation.e.privacymodules.permissions.data.PermissionDescription +import foundation.e.privacymodules.permissions.data.ProfileType /** * Implementation of the commons functionality between privileged and standard @@ -40,28 +41,28 @@ abstract class APermissionsPrivacyModule(protected val context: Context) : IPerm companion object { private const val TAG = "PermissionsModule" } - /** - * @see IPermissionsPrivacyModule.getAllApplications - */ - override fun getAllApplications(): List { - val appInfos = context.packageManager.getInstalledApplications(0) - return appInfos.map { buildApplicationDescription(it, false) } - } - - /** - * @see IPermissionsPrivacyModule.getInstalledApplications - */ - override fun getInstalledApplications(): List { - return context.packageManager.getInstalledApplications(0) - .filter { it.flags and ApplicationInfo.FLAG_SYSTEM == 0 } - .map { buildApplicationDescription(it, false) } - } + // /** + // * @see IPermissionsPrivacyModule.getAllApplications + // */ + // override fun getAllApplications(): List { + // val appInfos = context.packageManager.getInstalledApplications(0) + // return appInfos.map { buildApplicationDescription(it, false) } + // } + // + // /** + // * @see IPermissionsPrivacyModule.getInstalledApplications + // */ + // override fun getInstalledApplications(): List { + // return context.packageManager.getInstalledApplications(0) + // .filter { it.flags and ApplicationInfo.FLAG_SYSTEM == 0 } + // .map { buildApplicationDescription(it) } + // } /** * @see IPermissionsPrivacyModule.getInstalledApplications */ override fun getApplicationDescription(packageName: String, withIcon: Boolean): ApplicationDescription { - val appDesc = buildApplicationDescription(context.packageManager.getApplicationInfo(packageName, 0), false) + val appDesc = buildApplicationDescription(context.packageManager.getApplicationInfo(packageName, 0)) if (withIcon) { appDesc.icon = getApplicationIcon(appDesc.packageName) } @@ -144,13 +145,19 @@ abstract class APermissionsPrivacyModule(protected val context: Context) : IPerm } } - override fun buildApplicationDescription(appInfo: ApplicationInfo, withIcon: Boolean): + override fun buildApplicationDescription( + appInfo: ApplicationInfo, + profileId: Int, + profileType: ProfileType + ): ApplicationDescription { return ApplicationDescription( packageName = appInfo.packageName, uid = appInfo.uid, label = getAppLabel(appInfo), - icon = if (withIcon) getApplicationIcon(appInfo) else null + icon = null, + profileId = profileId, + profileType = profileType, ) } @@ -158,7 +165,7 @@ abstract class APermissionsPrivacyModule(protected val context: Context) : IPerm return context.packageManager.getApplicationLabel(appInfo) } - private fun getApplicationIcon(appInfo: ApplicationInfo): Drawable? { + fun getApplicationIcon(appInfo: ApplicationInfo): Drawable? { return context.packageManager.getApplicationIcon(appInfo) } diff --git a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/IPermissionsPrivacyModule.kt b/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/IPermissionsPrivacyModule.kt index b64762f3..bcc6467e 100644 --- a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/IPermissionsPrivacyModule.kt +++ b/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/IPermissionsPrivacyModule.kt @@ -24,6 +24,7 @@ import android.graphics.drawable.Drawable import foundation.e.privacymodules.permissions.data.AppOpModes import foundation.e.privacymodules.permissions.data.ApplicationDescription import foundation.e.privacymodules.permissions.data.PermissionDescription +import foundation.e.privacymodules.permissions.data.ProfileType /** * List applications and manage theirs permissions. @@ -32,30 +33,30 @@ interface IPermissionsPrivacyModule { fun buildApplicationDescription( appInfo: ApplicationInfo, - withIcon: Boolean = true + profileId: Int = -1, + profileType: ProfileType = ProfileType.MAIN ): ApplicationDescription fun getApplications( filter: ((PackageInfo) -> Boolean)?, - withIcon: Boolean ): List - fun getWorkProfileApplications( - filter: ((PackageInfo) -> Boolean)?, - withIcon: Boolean - ): List - - /** - * List the installed application on the device which have not the FLAGS_SYSTEM. - * @return list of filled up [ApplicationDescription] - */ - fun getInstalledApplications(): List - - /** - * List all the installed application on the device. - * @return list of filled up [ApplicationDescription] - */ - fun getAllApplications(): List + // fun getWorkProfileApplications( + // filter: ((PackageInfo) -> Boolean)?, + // withIcon: Boolean + // ): List + // + // /** + // * List the installed application on the device which have not the FLAGS_SYSTEM. + // * @return list of filled up [ApplicationDescription] + // */ + // fun getInstalledApplications(): List + // + // /** + // * List all the installed application on the device. + // * @return list of filled up [ApplicationDescription] + // */ + // fun getAllApplications(): List /** * List of permissions names used by an app, specified by its [packageName]. @@ -131,6 +132,12 @@ interface IPermissionsPrivacyModule { fun getApplicationIcon(packageName: String): Drawable? /** + * Get the application icon. + */ + fun getApplicationIcon(app: ApplicationDescription): Drawable? + + + /** * Authorize the specified package to be used as Vpn. * @return true if authorization has been set, false if an error has occurred. */ diff --git a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/data/ApplicationDescription.kt b/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/data/ApplicationDescription.kt index cafe2569..30d0076e 100644 --- a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/data/ApplicationDescription.kt +++ b/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/data/ApplicationDescription.kt @@ -25,6 +25,26 @@ import android.graphics.drawable.Drawable data class ApplicationDescription( val packageName: String, val uid: Int, + val profileId: Int, + val profileType: ProfileType, var label: CharSequence?, var icon: Drawable? -) +) { + val profileFlag = when(profileType) { + ProfileType.MAIN -> PROFILE_FLAG_MAIN + ProfileType.WORK -> PROFILE_FLAG_WORK + else -> profileId + } + + val apId: String get() = "${profileFlag}_${packageName}" + + companion object { + const val PROFILE_FLAG_MAIN = -1 + const val PROFILE_FLAG_WORK = -2 + } + +} + +enum class ProfileType { + MAIN, WORK, OTHER +} diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.kt index 737aa4ae..a9641847 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.kt @@ -135,7 +135,6 @@ class DNSBlockerRunnable( private fun shouldBlock(appUid: Int, trackerId: String?): Boolean { return whitelistRepository.isBlockingEnabled && - !whitelistRepository.isAppWhiteListed(appUid) && - !whitelistRepository.isTrackerWhiteListedForApp(trackerId, appUid) + !whitelistRepository.isWhiteListed(appUid, trackerId) } } 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 25f0f2a8..1f1f5634 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 @@ -18,6 +18,7 @@ package foundation.e.privacymodules.trackers.api import android.content.Context +import foundation.e.privacymodules.permissions.data.ApplicationDescription import foundation.e.privacymodules.trackers.data.TrackersRepository import foundation.e.privacymodules.trackers.data.WhitelistRepository @@ -52,14 +53,14 @@ class BlockTrackersPrivacyModule(context: Context) : IBlockTrackersPrivacyModule mListeners.forEach { listener -> listener.onBlockingToggle(true) } } - override fun getWhiteList(appUid: Int): List { - return whitelistRepository.getWhiteListForApp(appUid).mapNotNull { + override fun getWhiteList(app: ApplicationDescription): List { + return whitelistRepository.getWhiteListForApp(app).mapNotNull { trackersRepository.getTracker(it) } } - override fun getWhiteListedApp(): List { - return whitelistRepository.whiteListedApp + override fun getWhiteListedApp(): List { + return whitelistRepository.getWhiteListedApp() } override fun isBlockingEnabled(): Boolean { @@ -70,19 +71,21 @@ class BlockTrackersPrivacyModule(context: Context) : IBlockTrackersPrivacyModule return whitelistRepository.areWhiteListEmpty() } - override fun isWhitelisted(appUid: Int): Boolean { - return whitelistRepository.isAppWhiteListed(appUid) + override fun isWhitelisted(app: ApplicationDescription): Boolean { + return whitelistRepository.isAppWhiteListed(app) } 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( + tracker: Tracker, app: ApplicationDescription, isWhiteListed: Boolean + ) { + whitelistRepository.setWhiteListed(tracker, app.apId, isWhiteListed) } - override fun setWhiteListed(appUid: Int, isWhiteListed: Boolean) { - whitelistRepository.setWhiteListed(appUid, isWhiteListed) + override fun setWhiteListed(app: ApplicationDescription, isWhiteListed: Boolean) { + whitelistRepository.setWhiteListed(app.apId, isWhiteListed) } } diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/IBlockTrackersPrivacyModule.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/IBlockTrackersPrivacyModule.kt index 9e1a0411..0086629a 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/IBlockTrackersPrivacyModule.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/IBlockTrackersPrivacyModule.kt @@ -17,6 +17,8 @@ package foundation.e.privacymodules.trackers.api +import foundation.e.privacymodules.permissions.data.ApplicationDescription + /** * Manage trackers blocking and whitelisting. */ @@ -40,18 +42,18 @@ interface IBlockTrackersPrivacyModule { /** * Set or unset in whitelist the App with the specified uid. - * @param appUid the uid of the app + * @param app the ApplicationDescription of the app * @param isWhiteListed true, the app will appears in whitelist, false, it won't */ - fun setWhiteListed(appUid: Int, isWhiteListed: Boolean) + fun setWhiteListed(app: ApplicationDescription, isWhiteListed: Boolean) /** * Set or unset in whitelist the specifid tracked, for the App specified by its uid. * @param tracker the tracker - * @param appUid the uid of the app + * @param app the ApplicationDescription of the app * @param isWhiteListed true, the app will appears in whitelist, false, it won't */ - fun setWhiteListed(tracker: Tracker, appUid: Int, isWhiteListed: Boolean) + fun setWhiteListed(tracker: Tracker, app: ApplicationDescription, isWhiteListed: Boolean) /** * Return true if nothing has been added to the whitelist : everything is blocked. @@ -61,17 +63,17 @@ interface IBlockTrackersPrivacyModule { /** * Return the white listed App, by their UID */ - fun getWhiteListedApp(): List + fun getWhiteListedApp(): List /** * Return true if the App is whitelisted for trackers blocking. */ - fun isWhitelisted(appUid: Int): Boolean + fun isWhitelisted(app: ApplicationDescription): Boolean /** * List the white listed trackers for an App specified by it uid */ - fun getWhiteList(appUid: Int): List + fun getWhiteList(app: ApplicationDescription): List /** * Callback interface to get updates about the state of the Block trackers module. diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/ITrackTrackersPrivacyModule.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/ITrackTrackersPrivacyModule.kt index 264f247f..e6d39b9e 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/ITrackTrackersPrivacyModule.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/ITrackTrackersPrivacyModule.kt @@ -17,34 +17,40 @@ package foundation.e.privacymodules.trackers.api +import foundation.e.privacymodules.permissions.data.ApplicationDescription + /** * Get reporting about trackers calls. */ interface ITrackTrackersPrivacyModule { - fun start(trackers: List, enableNotification: Boolean = true) + fun start( + trackers: List, + getAppByUid: (Int) -> ApplicationDescription?, + getAppByAPId: (String) -> ApplicationDescription?, + enableNotification: Boolean = true) /** * List all the trackers encountered for a specific app. */ - fun getTrackersForApp(appUid: Int): List + fun getTrackersForApp(app: ApplicationDescription): List /** * List all the trackers encountere trackers since "ever", for the given [appUids], * or all apps if [appUids] is null */ - fun getTrackers(appUids: List? = null): List + fun getTrackers(apps: List? = null): List /** * Return the number of encountered trackers since "ever", for the given [appUids], * or all apps if [appUids] is null */ - fun getTrackersCount(appUids: List? = null): Int + fun getTrackersCount(): Int /** * Return the number of encountere trackers since "ever", for each app uid. */ - fun getTrackersCountByApp(): Map + fun getTrackersCountByApp(): Map /** * Return the number of encountered trackers for the last 24 hours @@ -79,11 +85,11 @@ interface ITrackTrackersPrivacyModule { */ fun getPastYearTrackersCalls(): List> - fun getPastDayTrackersCallsByApps(): Map> + fun getPastDayTrackersCallsByApps(): Map> - fun getPastDayTrackersCallsForApp(appUid: Int): Pair + fun getPastDayTrackersCallsForApp(app: ApplicationDescription): Pair - fun getPastDayMostLeakedApp(): Int + fun getPastDayMostLeakedApp(): ApplicationDescription? interface Listener { 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 18c56c9d..5e47e3e5 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 @@ -19,9 +19,11 @@ package foundation.e.privacymodules.trackers.api import android.content.Context import android.content.Intent +import foundation.e.privacymodules.permissions.data.ApplicationDescription import foundation.e.privacymodules.trackers.DNSBlockerService import foundation.e.privacymodules.trackers.data.StatsRepository import foundation.e.privacymodules.trackers.data.TrackersRepository +import foundation.e.privacymodules.trackers.data.WhitelistRepository import java.time.temporal.ChronoUnit class TrackTrackersPrivacyModule(private val context: Context) : ITrackTrackersPrivacyModule { @@ -42,8 +44,15 @@ class TrackTrackersPrivacyModule(private val context: Context) : ITrackTrackersP } } - override fun start(trackers: List, enableNotification: Boolean) { + override fun start( + trackers: List, + getAppByUid: (Int) -> ApplicationDescription?, + getAppByAPId: (String) -> ApplicationDescription?, + enableNotification: Boolean + ) { TrackersRepository.getInstance().setTrackersList(trackers) + StatsRepository.getInstance(context).setAppGetters(getAppByUid, getAppByAPId) + WhitelistRepository.getInstance(context).setAppGetters(getAppByAPId) val intent = Intent(context, DNSBlockerService::class.java) intent.action = DNSBlockerService.ACTION_START intent.putExtra(DNSBlockerService.EXTRA_ENABLE_NOTIFICATION, enableNotification) @@ -62,20 +71,20 @@ class TrackTrackersPrivacyModule(private val context: Context) : ITrackTrackersP return statsRepository.getTrackersCallsOnPeriod(12, ChronoUnit.MONTHS) } - override fun getTrackersCount(appUids: List?): Int { - return statsRepository.getContactedTrackersCount(appUids) + override fun getTrackersCount(): Int { + return statsRepository.getContactedTrackersCount() } - override fun getTrackersCountByApp(): Map { + override fun getTrackersCountByApp(): Map { return statsRepository.getContactedTrackersCountByApp() } - override fun getTrackersForApp(appUid: Int): List { - return statsRepository.getTrackers(listOf(appUid)) + override fun getTrackersForApp(app: ApplicationDescription): List { + return statsRepository.getTrackers(listOf(app)) } - override fun getTrackers(appUids: List?): List { - return statsRepository.getTrackers(appUids) + override fun getTrackers(apps: List?): List { + return statsRepository.getTrackers(apps) } override fun getPastDayTrackersCount(): Int { @@ -90,16 +99,16 @@ class TrackTrackersPrivacyModule(private val context: Context) : ITrackTrackersP return statsRepository.getActiveTrackersByPeriod(12, ChronoUnit.MONTHS) } - override fun getPastDayMostLeakedApp(): Int { + override fun getPastDayMostLeakedApp(): ApplicationDescription? { return statsRepository.getMostLeakedApp(24, ChronoUnit.HOURS) } - override fun getPastDayTrackersCallsByApps(): Map> { + 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 getPastDayTrackersCallsForApp(app: ApplicationDescription): Pair { + return statsRepository.getCalls(app, 24, ChronoUnit.HOURS) } override fun addListener(listener: ITrackTrackersPrivacyModule.Listener) { 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 21edb56f..8d44a4f3 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.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteOpenHelper import android.provider.BaseColumns import foundation.e.privacymodules.trackers.api.Tracker -import foundation.e.privacymodules.trackers.data.StatsDatabase.AppTrackerEntry.COLUMN_NAME_APP_UID +import foundation.e.privacymodules.trackers.data.StatsDatabase.AppTrackerEntry.COLUMN_NAME_APPID import foundation.e.privacymodules.trackers.data.StatsDatabase.AppTrackerEntry.COLUMN_NAME_NUMBER_BLOCKED import foundation.e.privacymodules.trackers.data.StatsDatabase.AppTrackerEntry.COLUMN_NAME_NUMBER_CONTACTED import foundation.e.privacymodules.trackers.data.StatsDatabase.AppTrackerEntry.COLUMN_NAME_TIMESTAMP @@ -40,38 +40,46 @@ class StatsDatabase(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) { companion object { - const val DATABASE_VERSION = 1 + const val DATABASE_VERSION = 2 const val DATABASE_NAME = "TrackerFilterStats.db" private const val SQL_CREATE_TABLE = "CREATE TABLE $TABLE_NAME (" + "${BaseColumns._ID} INTEGER PRIMARY KEY," + "$COLUMN_NAME_TIMESTAMP INTEGER," + - "$COLUMN_NAME_APP_UID INTEGER," + "$COLUMN_NAME_TRACKER TEXT," + "$COLUMN_NAME_NUMBER_CONTACTED INTEGER," + - "$COLUMN_NAME_NUMBER_BLOCKED INTEGER)" + "$COLUMN_NAME_NUMBER_BLOCKED INTEGER," + + "$COLUMN_NAME_APPID TEXT)" private const val PROJECTION_NAME_PERIOD = "period" private const val PROJECTION_NAME_CONTACTED_SUM = "contactedsum" private const val PROJECTION_NAME_BLOCKED_SUM = "blockedsum" private const val PROJECTION_NAME_LEAKED_SUM = "leakedsum" private const val PROJECTION_NAME_TRACKERS_COUNT = "trackerscount" + + // TODO update migration toappid -> pname_profileflag + // private val MIGRATE_1_2 = listOf( + // "ALTER TABLE $TABLE_NAME ADD COLUMN $COLUMN_NAME_APPID TEXT", + // "UPDATE $TABLE_NAME SET $COLUMN_NAME_APPID = app_uid || '_'", + // // "ALTER TABLE $TABLE_NAME DROP COLUMN app_uid" + // // DROP COLUMN is available since sqlite 3.35.0, and sdk29 as 3.22.0, sdk32 as 3.32.2 + // ) } object AppTrackerEntry : BaseColumns { const val TABLE_NAME = "tracker_filter_stats" const val COLUMN_NAME_TIMESTAMP = "timestamp" const val COLUMN_NAME_TRACKER = "tracker" - const val COLUMN_NAME_APP_UID = "app_uid" const val COLUMN_NAME_NUMBER_CONTACTED = "sum_contacted" const val COLUMN_NAME_NUMBER_BLOCKED = "sum_blocked" + const val COLUMN_NAME_APPID = "app_apid" } private var projection = arrayOf( COLUMN_NAME_TIMESTAMP, - COLUMN_NAME_APP_UID, COLUMN_NAME_TRACKER, COLUMN_NAME_NUMBER_CONTACTED, - COLUMN_NAME_NUMBER_BLOCKED + COLUMN_NAME_NUMBER_BLOCKED, + COLUMN_NAME_APPID ) private val lock = Any() @@ -83,6 +91,11 @@ class StatsDatabase(context: Context) : override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { onCreate(db) + // if (oldVersion == 1 && newVersion == 2) { + // MIGRATE_1_2.forEach(db::execSQL) + // } else { + // Timber.e(Exception("Unexpected database versions: oldVersion: $oldVersion ; newVersion: $newVersion")) + // } } private fun getCallsByPeriod( @@ -182,15 +195,11 @@ class StatsDatabase(context: Context) : } } - fun getContactedTrackersCount(appUids: List?): Int { + fun getContactedTrackersCount(): Int { synchronized(lock) { val db = readableDatabase var query = "SELECT DISTINCT $COLUMN_NAME_TRACKER FROM $TABLE_NAME" - appUids?.let { - query += " WHERE $COLUMN_NAME_APP_UID IN (${it.joinToString(", ")})" - } - val cursor = db.rawQuery(query, arrayOf()) var count = 0 while (cursor.moveToNext()) { @@ -204,19 +213,19 @@ class StatsDatabase(context: Context) : } } - fun getContactedTrackersCountByApp(): Map { + fun getContactedTrackersCountByAppId(): Map { synchronized(lock) { val db = readableDatabase - val projection = "$COLUMN_NAME_APP_UID, $COLUMN_NAME_TRACKER" + val projection = "$COLUMN_NAME_APPID, $COLUMN_NAME_TRACKER" val cursor = db.rawQuery( "SELECT DISTINCT $projection FROM $TABLE_NAME", // + arrayOf() ) - val countByApp = mutableMapOf() + val countByApp = mutableMapOf() while (cursor.moveToNext()) { trackersRepository.getTracker(cursor.getString(COLUMN_NAME_TRACKER))?.let { - val appUid = cursor.getInt(COLUMN_NAME_APP_UID) - countByApp[appUid] = countByApp.getOrDefault(appUid, 0) + 1 + val appId = cursor.getString(COLUMN_NAME_APPID) + countByApp[appId] = countByApp.getOrDefault(appId, 0) + 1 } } cursor.close() @@ -225,26 +234,26 @@ class StatsDatabase(context: Context) : } } - fun getCallsByApps(periodCount: Int, periodUnit: TemporalUnit): Map> { + fun getCallsByAppIds(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, " + + val projection = "$COLUMN_NAME_APPID, " + "SUM($COLUMN_NAME_NUMBER_CONTACTED) $PROJECTION_NAME_CONTACTED_SUM," + "SUM($COLUMN_NAME_NUMBER_BLOCKED) $PROJECTION_NAME_BLOCKED_SUM" val cursor = db.rawQuery( "SELECT $projection FROM $TABLE_NAME" + " WHERE $selection" + - " GROUP BY $COLUMN_NAME_APP_UID", + " GROUP BY $COLUMN_NAME_APPID", selectionArg ) - val callsByApp = HashMap>() + 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 + callsByApp[cursor.getString(COLUMN_NAME_APPID)] = blocked to contacted - blocked } cursor.close() db.close() @@ -252,13 +261,13 @@ class StatsDatabase(context: Context) : } } - fun getCalls(appUid: Int, periodCount: Int, periodUnit: TemporalUnit): Pair { + fun getCalls(appId: String, periodCount: Int, periodUnit: TemporalUnit): Pair { synchronized(lock) { val minTimestamp = getPeriodStartTs(periodCount, periodUnit) val db = readableDatabase - val selection = "$COLUMN_NAME_APP_UID = ? AND " + + val selection = "$COLUMN_NAME_APPID = ? AND " + "$COLUMN_NAME_TIMESTAMP >= ?" - val selectionArg = arrayOf("" + appUid, "" + minTimestamp) + val selectionArg = arrayOf("" + appId, "" + minTimestamp) val projection = "SUM($COLUMN_NAME_NUMBER_CONTACTED) $PROJECTION_NAME_CONTACTED_SUM," + "SUM($COLUMN_NAME_NUMBER_BLOCKED) $PROJECTION_NAME_BLOCKED_SUM" @@ -278,37 +287,37 @@ class StatsDatabase(context: Context) : } } - fun getMostLeakedApp(periodCount: Int, periodUnit: TemporalUnit): Int { + fun getMostLeakedAppId(periodCount: Int, periodUnit: TemporalUnit): String { synchronized(lock) { val minTimestamp = getPeriodStartTs(periodCount, periodUnit) val db = readableDatabase val selection = "$COLUMN_NAME_TIMESTAMP >= ?" val selectionArg = arrayOf("" + minTimestamp) - val projection = "$COLUMN_NAME_APP_UID, " + + val projection = "$COLUMN_NAME_APPID, " + "SUM($COLUMN_NAME_NUMBER_CONTACTED - $COLUMN_NAME_NUMBER_BLOCKED) $PROJECTION_NAME_LEAKED_SUM" val cursor = db.rawQuery( "SELECT $projection FROM $TABLE_NAME" + " WHERE $selection" + - " GROUP BY $COLUMN_NAME_APP_UID" + + " GROUP BY $COLUMN_NAME_APPID" + " ORDER BY $PROJECTION_NAME_LEAKED_SUM DESC LIMIT 1", selectionArg ) - var appUid = 0 + var appId = "" if (cursor.moveToNext()) { - appUid = cursor.getInt(COLUMN_NAME_APP_UID) + appId = cursor.getString(COLUMN_NAME_APPID) } cursor.close() db.close() - return appUid + return appId } } - fun logAccess(trackerId: String?, appUid: Int, blocked: Boolean) { + fun logAccess(trackerId: String?, appId: String, blocked: Boolean) { synchronized(lock) { val currentHour = getCurrentHourTs() val db = writableDatabase val values = ContentValues() - values.put(COLUMN_NAME_APP_UID, appUid) + values.put(COLUMN_NAME_APPID, appId) values.put(COLUMN_NAME_TRACKER, trackerId) values.put(COLUMN_NAME_TIMESTAMP, currentHour) @@ -317,9 +326,9 @@ class StatsDatabase(context: Context) : query+=COLUMN_NAME_NUMBER_BLOCKED+" = "+COLUMN_NAME_NUMBER_BLOCKED+" + 1 "; */ val selection = "$COLUMN_NAME_TIMESTAMP = ? AND " + - "$COLUMN_NAME_APP_UID = ? AND " + + "$COLUMN_NAME_APPID = ? AND " + "$COLUMN_NAME_TRACKER = ? " - val selectionArg = arrayOf("" + currentHour, "" + appUid, trackerId) + val selectionArg = arrayOf("" + currentHour, "" + appId, trackerId) val cursor = db.query( TABLE_NAME, projection, @@ -355,23 +364,22 @@ class StatsDatabase(context: Context) : 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.timestamp = cursor.getLong(COLUMN_NAME_TIMESTAMP) + entry.appId = cursor.getString(COLUMN_NAME_APPID) entry.sum_blocked = cursor.getInt(COLUMN_NAME_NUMBER_BLOCKED) entry.sum_contacted = cursor.getInt(COLUMN_NAME_NUMBER_CONTACTED) entry.tracker = cursor.getInt(COLUMN_NAME_TRACKER) return entry } - fun getTrackers(appUids: List?): List { + fun getTrackers(appIds: List?): List { synchronized(lock) { - val columns = arrayOf(COLUMN_NAME_TRACKER, COLUMN_NAME_APP_UID) + val columns = arrayOf(COLUMN_NAME_TRACKER, COLUMN_NAME_APPID) var selection: String? = null var selectionArg: Array? = null - appUids?.let { - selection = "$COLUMN_NAME_APP_UID IN (${it.joinToString(", ")})" + appIds?.let { appIds -> + selection = "$COLUMN_NAME_APPID IN (${appIds.joinToString(", ") { "'$it'" }})" selectionArg = arrayOf() } @@ -402,7 +410,7 @@ class StatsDatabase(context: Context) : } class StatEntry { - var app_uid = 0 + var appId = "" var sum_contacted = 0 var sum_blocked = 0 var timestamp: Long = 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 16d8ec67..3573ffaf 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 @@ -18,12 +18,16 @@ package foundation.e.privacymodules.trackers.data import android.content.Context +import foundation.e.privacymodules.permissions.data.ApplicationDescription import foundation.e.privacymodules.trackers.api.Tracker import java.time.temporal.TemporalUnit class StatsRepository private constructor(context: Context) { private val database: StatsDatabase private var newDataCallback: (() -> Unit)? = null + private var getAppByUid: ((Int) -> ApplicationDescription?)? = null + private var getAppByAPId: ((String) -> ApplicationDescription?)? = null + companion object { private var instance: StatsRepository? = null @@ -32,6 +36,15 @@ class StatsRepository private constructor(context: Context) { } } + fun setAppGetters( + getAppByUid: (Int) -> ApplicationDescription?, + getAppByAPId: (String) -> ApplicationDescription? + ) { + this.getAppByUid = getAppByUid + this.getAppByAPId = getAppByAPId + } + + init { database = StatsDatabase(context) } @@ -41,8 +54,10 @@ class StatsRepository private constructor(context: Context) { } fun logAccess(trackerId: String?, appUid: Int, blocked: Boolean) { - database.logAccess(trackerId, appUid, blocked) - newDataCallback?.invoke() + getAppByUid?.invoke(appUid)?.let { app -> + database.logAccess(trackerId, app.apId, blocked) + newDataCallback?.invoke() + } } fun getTrackersCallsOnPeriod( @@ -56,27 +71,36 @@ class StatsRepository private constructor(context: Context) { return database.getActiveTrackersByPeriod(periodsCount, periodUnit) } - fun getContactedTrackersCountByApp(): Map { - return database.getContactedTrackersCountByApp() + fun getContactedTrackersCountByApp(): Map { + return database.getContactedTrackersCountByAppId().mapByAppIdToApp() + } + + fun getContactedTrackersCount(): Int { + return database.getContactedTrackersCount() } - fun getContactedTrackersCount(appUids: List?): Int { - return database.getContactedTrackersCount(appUids) + fun getTrackers(apps: List?): List { + return database.getTrackers(apps?.map { it.apId }) } - fun getTrackers(appUids: List?): List { - return database.getTrackers(appUids) + fun getCallsByApps( + periodCount: Int, periodUnit: TemporalUnit + ): Map> { + return database.getCallsByAppIds(periodCount, periodUnit).mapByAppIdToApp() } - fun getCallsByApps(periodCount: Int, periodUnit: TemporalUnit): Map> { - return database.getCallsByApps(periodCount, periodUnit) + fun getCalls(app: ApplicationDescription, periodCount: Int, periodUnit: TemporalUnit): Pair { + return database.getCalls(app.apId, periodCount, periodUnit) } - fun getCalls(appUid: Int, periodCount: Int, periodUnit: TemporalUnit): Pair { - return database.getCalls(appUid, periodCount, periodUnit) + fun getMostLeakedApp(periodCount: Int, periodUnit: TemporalUnit): ApplicationDescription? { + return getAppByAPId?.invoke(database.getMostLeakedAppId(periodCount, periodUnit)) } - fun getMostLeakedApp(periodCount: Int, periodUnit: TemporalUnit): Int { - return database.getMostLeakedApp(periodCount, periodUnit) + + private fun Map.mapByAppIdToApp(): Map { + return entries.mapNotNull { (apId, value) -> + getAppByAPId?.invoke(apId)?.let { it to value } + }.toMap() } } diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/WhitelistRepository.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/WhitelistRepository.kt index e9f049da..e34461b8 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 @@ -19,12 +19,19 @@ package foundation.e.privacymodules.trackers.data import android.content.Context import android.content.SharedPreferences +import foundation.e.privacymodules.permissions.data.ApplicationDescription import foundation.e.privacymodules.trackers.api.Tracker class WhitelistRepository private constructor(context: Context) { - private lateinit var appsWhitelist: Set - private val trackersWhitelistByApp: MutableMap> = HashMap() + private var appsWhitelist: Set = HashSet() + private var appUidsWhitelist: Set = HashSet() + + private var trackersWhitelistByApp: MutableMap> = HashMap() + private var trackersWhitelistByUid: Map> = HashMap() + private val prefs: SharedPreferences + private var getAppByAPId: ((String) -> ApplicationDescription?)? = null + companion object { private const val SHARED_PREFS_FILE = "trackers_whitelist.prefs" @@ -42,6 +49,14 @@ class WhitelistRepository private constructor(context: Context) { reloadCache() } + fun setAppGetters( + getAppByAPId: (String) -> ApplicationDescription? + ) { + this.getAppByAPId = getAppByAPId + } + + // TODO: migration before reloadCache !! + private fun reloadCache() { isBlockingEnabled = prefs.getBoolean(KEY_BLOKING_ENABLED, false) reloadAppsWhiteList() @@ -49,24 +64,29 @@ class WhitelistRepository private constructor(context: Context) { } private fun reloadAppsWhiteList() { - appsWhitelist = prefs.getStringSet(KEY_APPS_WHITELIST, HashSet())?.mapNotNull { - try { it.toInt() } catch (e: Exception) { null } - }?.toHashSet() ?: HashSet() + appsWhitelist = prefs.getStringSet(KEY_APPS_WHITELIST, HashSet()) ?: HashSet() + appUidsWhitelist = appsWhitelist + .mapNotNull { apId -> getAppByAPId?.invoke(apId)?.uid } + .toSet() } - private fun reloadAppTrackersWhiteList(appUid: Int) { - val key = buildAppTrackersKey(appUid) - trackersWhitelistByApp[appUid] = prefs.getStringSet(key, HashSet()) ?: HashSet() + private fun refreshAppUidTrackersWhiteList() { + trackersWhitelistByUid = trackersWhitelistByApp.mapNotNull { (apId, value) -> + getAppByAPId?.invoke(apId)?.uid?.let { uid -> + uid to value + } + }.toMap() } - private fun reloadAllAppTrackersWhiteList() { - trackersWhitelistByApp.clear() + val map: MutableMap> = HashMap() 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) + map[key.substring(KEY_APP_TRACKERS_WHITELIST_PREFIX.length)] = ( + prefs.getStringSet(key, HashSet())?: HashSet() + ) } } + trackersWhitelistByApp = map } var isBlockingEnabled: Boolean = false @@ -76,49 +96,55 @@ class WhitelistRepository private constructor(context: Context) { field = enabled } - fun setWhiteListed(appUid: Int, isWhiteListed: Boolean) { + fun setWhiteListed(apId: String, isWhiteListed: Boolean) { val current = prefs.getStringSet(KEY_APPS_WHITELIST, HashSet())?.toHashSet() ?: HashSet() if (isWhiteListed) { - current.add("" + appUid) + current.add(apId) } else { - current.remove("" + appUid) + current.remove(apId) } prefs.edit().putStringSet(KEY_APPS_WHITELIST, current).commit() reloadAppsWhiteList() } - private fun buildAppTrackersKey(appUid: Int): String { - return KEY_APP_TRACKERS_WHITELIST_PREFIX + appUid + private fun buildAppTrackersKey(apId: String): String { + return KEY_APP_TRACKERS_WHITELIST_PREFIX + apId } - fun setWhiteListed(tracker: Tracker, appUid: Int, isWhiteListed: Boolean) { - val trackers = trackersWhitelistByApp.getOrDefault(appUid, HashSet()) - trackersWhitelistByApp[appUid] = trackers + fun setWhiteListed(tracker: Tracker, apId: String, isWhiteListed: Boolean) { + val trackers = trackersWhitelistByApp.getOrDefault(apId, HashSet()) + trackersWhitelistByApp[apId] = trackers if (isWhiteListed) { trackers.add(tracker.id) } else { trackers.remove(tracker.id) } - prefs.edit().putStringSet(buildAppTrackersKey(appUid), trackers).commit() + refreshAppUidTrackersWhiteList() + prefs.edit().putStringSet(buildAppTrackersKey(apId), trackers).commit() } - fun isAppWhiteListed(appUid: Int): Boolean { - return appsWhitelist.contains(appUid) + fun isAppWhiteListed(app: ApplicationDescription): Boolean { + return appsWhitelist.contains(app.apId) } - fun isTrackerWhiteListedForApp(trackerId: String?, appUid: Int): Boolean { - return trackersWhitelistByApp.getOrDefault(appUid, HashSet()).contains(trackerId) + fun isWhiteListed(appUid: Int, trackerId: String?): Boolean { + return appUidsWhitelist.contains(appUid) || + trackersWhitelistByUid.getOrDefault(appUid, HashSet()).contains(trackerId) } fun areWhiteListEmpty(): Boolean { return appsWhitelist.isEmpty() && trackersWhitelistByApp.all { (_, trackers) -> trackers.isEmpty() } } - val whiteListedApp: List get() = appsWhitelist.toList() + fun getWhiteListedApp(): List { + return getAppByAPId?.let { + appsWhitelist.mapNotNull(it) + } ?: emptyList() + } - fun getWhiteListForApp(appUid: Int): List { - return trackersWhitelistByApp[appUid]?.toList() ?: emptyList() + fun getWhiteListForApp(app: ApplicationDescription): List { + return trackersWhitelistByApp[app.apId]?.toList() ?: emptyList() } } -- GitLab From 6a93fab2498bade3382791a00fe8f544e776412a Mon Sep 17 00:00:00 2001 From: Guillaume Jacquart Date: Thu, 23 Mar 2023 19:33:43 +0100 Subject: [PATCH 2/7] Do migration stats db migration to APId --- .../data/repositories/AppListsRepository.kt | 33 ++-------- app/src/main/res/values/strings.xml | 2 +- .../privacymodules/trackers/TrackersLogger.kt | 4 +- .../api/TrackTrackersPrivacyModule.kt | 2 +- .../trackers/data/StatsDatabase.kt | 36 ++++++----- .../trackers/data/WhitelistRepository.kt | 63 +++++++++++++++++-- 6 files changed, 87 insertions(+), 53 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 7dd3e331..8b321bb9 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 @@ -22,7 +22,6 @@ import android.content.Context import android.content.Intent import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo -import android.util.Log import foundation.e.privacycentralapp.R import foundation.e.privacymodules.permissions.PermissionsPrivacyModule import foundation.e.privacymodules.permissions.data.ApplicationDescription @@ -137,15 +136,14 @@ class AppListsRepository( private var lastFetchApps = 0 private var refreshAppJob: Job? = null private fun refreshAppDescriptions(fetchMissingIcons: Boolean = true, force: Boolean = false): Job? { - Log.d("DebugFetchApps", "refreshAppDescriptions: $fetchMissingIcons - $force") if (refreshAppJob == null) { refreshAppJob = coroutineScope.launch(Dispatchers.IO) { if (force || pm.getChangedPackages(lastFetchApps) != null) { - Log.d("DebugFetchApps", "do fetchAppDescriptions") fetchAppDescriptions(fetchMissingIcons = fetchMissingIcons) - lastFetchApps = pm.getChangedPackages(lastFetchApps)?.sequenceNumber - ?: lastFetchApps - Log.d("DebugFetchApps", "lastFetchApps updated : $lastFetchApps") + if (fetchMissingIcons) { + lastFetchApps = pm.getChangedPackages(lastFetchApps)?.sequenceNumber + ?: lastFetchApps + } refreshAppJob = null } @@ -155,20 +153,6 @@ class AppListsRepository( return refreshAppJob } - // // Visible apps. - // fun getApps(): List { - // - // } - - // fun apps(): Flow> { - // - // } - // - // /// Visible AND hidden apps - // fun allApps(): Flow> { - // - // } - fun mainProfileApps(): Flow> { refreshAppDescriptions() return allProfilesAppDescriptions.map { @@ -202,13 +186,6 @@ class AppListsRepository( return allProfilesAppDescriptions.value.third } - // fun getAllApps(): Flow> = apps() - // .map { it + getHiddenSystemApps() + getACApps()} - // - // fun getApplicationDescription(appUid: Int): ApplicationDescription? { - // return allProfilesAppDescriptions.value.first.find { it.uid == appUid } - // } - fun anyForHiddenApps(app: ApplicationDescription, test: (ApplicationDescription) -> Boolean): Boolean { return if (app == dummySystemApp) { getHiddenSystemApps().any { test(it) } @@ -244,6 +221,8 @@ class AppListsRepository( } fun getApp(apId: String): ApplicationDescription? { + if (apId.isBlank()) return null + return appsByAPId[apId]?: run { runBlocking { refreshAppDescriptions(fetchMissingIcons = false, force = true)?.join() } appsByAPId[apId] diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fc53c3d9..38cd38bd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,6 +1,6 @@ - Advanced Privacy_2 + Advanced Privacy Close diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.kt index 99e21482..8ef2dd68 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.kt @@ -39,8 +39,8 @@ class TrackersLogger(context: Context) { stopped = true } - fun logAccess(trackerId: String?, appId: Int, wasBlocked: Boolean) { - queue.offer(DetectedTracker(trackerId, appId, wasBlocked)) + fun logAccess(trackerId: String?, appUid: Int, wasBlocked: Boolean) { + queue.offer(DetectedTracker(trackerId, appUid, wasBlocked)) } private fun startWriteLogLoop() { 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 5e47e3e5..cdafe43f 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 @@ -52,7 +52,7 @@ class TrackTrackersPrivacyModule(private val context: Context) : ITrackTrackersP ) { TrackersRepository.getInstance().setTrackersList(trackers) StatsRepository.getInstance(context).setAppGetters(getAppByUid, getAppByAPId) - WhitelistRepository.getInstance(context).setAppGetters(getAppByAPId) + WhitelistRepository.getInstance(context).setAppGetters(context, getAppByAPId, getAppByUid) val intent = Intent(context, DNSBlockerService::class.java) intent.action = DNSBlockerService.ACTION_START intent.putExtra(DNSBlockerService.EXTRA_ENABLE_NOTIFICATION, enableNotification) 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 8d44a4f3..846005e3 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 @@ -23,6 +23,8 @@ import android.database.Cursor import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteOpenHelper import android.provider.BaseColumns +import android.util.Log +import androidx.core.database.getStringOrNull import foundation.e.privacymodules.trackers.api.Tracker import foundation.e.privacymodules.trackers.data.StatsDatabase.AppTrackerEntry.COLUMN_NAME_APPID import foundation.e.privacymodules.trackers.data.StatsDatabase.AppTrackerEntry.COLUMN_NAME_NUMBER_BLOCKED @@ -56,13 +58,11 @@ class StatsDatabase(context: Context) : private const val PROJECTION_NAME_LEAKED_SUM = "leakedsum" private const val PROJECTION_NAME_TRACKERS_COUNT = "trackerscount" - // TODO update migration toappid -> pname_profileflag - // private val MIGRATE_1_2 = listOf( - // "ALTER TABLE $TABLE_NAME ADD COLUMN $COLUMN_NAME_APPID TEXT", - // "UPDATE $TABLE_NAME SET $COLUMN_NAME_APPID = app_uid || '_'", - // // "ALTER TABLE $TABLE_NAME DROP COLUMN app_uid" - // // DROP COLUMN is available since sqlite 3.35.0, and sdk29 as 3.22.0, sdk32 as 3.32.2 - // ) + private val MIGRATE_1_2 = listOf( + "ALTER TABLE $TABLE_NAME ADD COLUMN $COLUMN_NAME_APPID TEXT" + // "ALTER TABLE $TABLE_NAME DROP COLUMN app_uid" + // DROP COLUMN is available since sqlite 3.35.0, and sdk29 as 3.22.0, sdk32 as 3.32.2 + ) } object AppTrackerEntry : BaseColumns { @@ -90,12 +90,14 @@ class StatsDatabase(context: Context) : } override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { - onCreate(db) - // if (oldVersion == 1 && newVersion == 2) { - // MIGRATE_1_2.forEach(db::execSQL) - // } else { - // Timber.e(Exception("Unexpected database versions: oldVersion: $oldVersion ; newVersion: $newVersion")) - // } + if (oldVersion == 1 && newVersion == 2) { + MIGRATE_1_2.forEach(db::execSQL) + } else { + Log.e( + "StatsDatabase", + "Unexpected database versions: oldVersion: $oldVersion ; newVersion: $newVersion" + ) + } } private fun getCallsByPeriod( @@ -141,11 +143,11 @@ class StatsDatabase(context: Context) : javaPeriodFormat: String ): List> { var currentDate = ZonedDateTime.now().minus(periodsCount.toLong(), periodUnit) - val formater = DateTimeFormatter.ofPattern(javaPeriodFormat) + val formatter = DateTimeFormatter.ofPattern(javaPeriodFormat) val calls = mutableListOf>() for (i in 0 until periodsCount) { currentDate = currentDate.plus(1, periodUnit) - val currentPeriod = formater.format(currentDate) + val currentPeriod = formatter.format(currentDate) calls.add(callsByPeriod.getOrDefault(currentPeriod, 0 to 0)) } return calls @@ -450,6 +452,8 @@ class StatsDatabase(context: Context) : private fun Cursor.getString(columnName: String): String { val columnIndex = getColumnIndex(columnName) - return if (columnIndex >= 0) getString(columnIndex) else "" + return if (columnIndex >= 0) { + getStringOrNull(columnIndex)?: "" + } else "" } } 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 e34461b8..9fda1a9f 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 @@ -21,6 +21,7 @@ import android.content.Context import android.content.SharedPreferences import foundation.e.privacymodules.permissions.data.ApplicationDescription import foundation.e.privacymodules.trackers.api.Tracker +import java.io.File class WhitelistRepository private constructor(context: Context) { private var appsWhitelist: Set = HashSet() @@ -34,10 +35,13 @@ class WhitelistRepository private constructor(context: Context) { companion object { - private const val SHARED_PREFS_FILE = "trackers_whitelist.prefs" - private const val KEY_BLOKING_ENABLED = "blocking_enabled" + private const val SHARED_PREFS_FILE = "trackers_whitelist_v2" + private const val KEY_BLOCKING_ENABLED = "blocking_enabled" private const val KEY_APPS_WHITELIST = "apps_whitelist" private const val KEY_APP_TRACKERS_WHITELIST_PREFIX = "app_trackers_whitelist_" + + private const val SHARED_PREFS_FILE_V1 = "trackers_whitelist.prefs" + private var instance: WhitelistRepository? = null fun getInstance(context: Context): WhitelistRepository { return instance ?: WhitelistRepository(context).apply { instance = this } @@ -50,15 +54,62 @@ class WhitelistRepository private constructor(context: Context) { } fun setAppGetters( - getAppByAPId: (String) -> ApplicationDescription? + context: Context, + getAppByAPId: (String) -> ApplicationDescription?, + getAppByUid: (Int) -> ApplicationDescription? ) { this.getAppByAPId = getAppByAPId + migrate(context, getAppByUid) + } + + private fun migrate(context: Context, getAppByUid: (Int) -> ApplicationDescription?) { + if (context.sharedPreferencesExists(SHARED_PREFS_FILE_V1)) { + migrate1To2(context, getAppByUid) + } } - // TODO: migration before reloadCache !! + private fun Context.sharedPreferencesExists(fileName: String): Boolean { + return File( + "${applicationInfo.dataDir}/shared_prefs/$fileName.xml" + ).exists() + } + + private fun migrate1To2(context: Context, getAppByUid: (Int) -> ApplicationDescription?) { + val prefsV1 = context.getSharedPreferences(SHARED_PREFS_FILE_V1, Context.MODE_PRIVATE) + val editorV2 = prefs.edit() + + editorV2.putBoolean(KEY_BLOCKING_ENABLED, prefsV1.getBoolean(KEY_BLOCKING_ENABLED, false)) + + val apIds = prefsV1.getStringSet(KEY_APPS_WHITELIST, HashSet())?.mapNotNull { + try { + val uid = it.toInt() + getAppByUid(uid)?.apId + } catch (e: Exception) { null } + }?.toSet() ?: HashSet() + + editorV2.putStringSet(KEY_APPS_WHITELIST, apIds) + + prefsV1.all.keys.forEach { key -> + if (key.startsWith(KEY_APP_TRACKERS_WHITELIST_PREFIX)) { + try { + val uid = key.substring(KEY_APP_TRACKERS_WHITELIST_PREFIX.length).toInt() + val apId = getAppByUid(uid)?.apId + apId?.let { + val trackers = prefsV1.getStringSet(key, emptySet()) + editorV2.putStringSet(buildAppTrackersKey(apId), trackers) + } + } catch (e: Exception) { } + } + } + editorV2.commit() + + context.deleteSharedPreferences(SHARED_PREFS_FILE_V1) + + reloadCache() + } private fun reloadCache() { - isBlockingEnabled = prefs.getBoolean(KEY_BLOKING_ENABLED, false) + isBlockingEnabled = prefs.getBoolean(KEY_BLOCKING_ENABLED, false) reloadAppsWhiteList() reloadAllAppTrackersWhiteList() } @@ -92,7 +143,7 @@ class WhitelistRepository private constructor(context: Context) { var isBlockingEnabled: Boolean = false get() = field set(enabled) { - prefs.edit().putBoolean(KEY_BLOKING_ENABLED, enabled).apply() + prefs.edit().putBoolean(KEY_BLOCKING_ENABLED, enabled).apply() field = enabled } -- GitLab From 1179d8e16a846deba57c16317ff033e98e477abb Mon Sep 17 00:00:00 2001 From: Guillaume Jacquart Date: Mon, 27 Mar 2023 08:48:47 +0200 Subject: [PATCH 3/7] Quick fix blocked tracker counts --- .../e/privacycentralapp/domain/entities/AppWithCounts.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/entities/AppWithCounts.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/AppWithCounts.kt index a1d914f4..507dfde6 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/domain/entities/AppWithCounts.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/AppWithCounts.kt @@ -54,5 +54,5 @@ data class AppWithCounts( ) val blockedTrackersCount get() = if (isWhitelisted) 0 - else trackersCount - whiteListedTrackersCount + else Math.max(trackersCount - whiteListedTrackersCount, 0) } -- GitLab From dc57e62e2facdd7b72984d0ff73a62496b7e224d Mon Sep 17 00:00:00 2001 From: Guillaume Jacquart Date: Fri, 7 Apr 2023 09:31:21 +0200 Subject: [PATCH 4/7] reset all whitelist trackers --- .../e/privacycentralapp/common/AppsAdapter.kt | 4 +-- .../data/repositories/AppListsRepository.kt | 2 ++ .../domain/usecases/AppListUseCase.kt | 9 +++++- .../domain/usecases/TrackersStateUseCase.kt | 7 +++++ .../usecases/TrackersStatisticsUseCase.kt | 19 ++++++++++-- .../apptrackers/AppTrackersFragment.kt | 16 +++++++--- .../trackers/apptrackers/AppTrackersState.kt | 1 + .../apptrackers/AppTrackersViewModel.kt | 31 ++++++++++++++----- .../main/res/layout/apptrackers_fragment.xml | 19 ++++++++++-- app/src/main/res/values/strings.xml | 2 ++ .../api/BlockTrackersPrivacyModule.kt | 4 +++ .../api/IBlockTrackersPrivacyModule.kt | 3 ++ .../trackers/data/WhitelistRepository.kt | 6 ++++ 13 files changed, 102 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/foundation/e/privacycentralapp/common/AppsAdapter.kt b/app/src/main/java/foundation/e/privacycentralapp/common/AppsAdapter.kt index 7b09c513..7fedf5d2 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/common/AppsAdapter.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/common/AppsAdapter.kt @@ -38,12 +38,12 @@ class AppsAdapter( val icon: ImageView = view.findViewById(R.id.icon) fun bind(item: AppWithCounts) { appName.text = item.label - counts.text = itemView.context.getString( + counts.text = if (item.trackersCount > 0) itemView.context.getString( R.string.trackers_app_trackers_counts, item.blockedTrackersCount, item.trackersCount, item.leaks - ) + ) else "" icon.setImageDrawable(item.icon) itemView.setOnClickListener { listener(item.uid) } 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 8b321bb9..ba342baf 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 @@ -22,6 +22,7 @@ import android.content.Context import android.content.Intent import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo +import android.util.Log import foundation.e.privacycentralapp.R import foundation.e.privacymodules.permissions.PermissionsPrivacyModule import foundation.e.privacymodules.permissions.data.ApplicationDescription @@ -203,6 +204,7 @@ class AppListsRepository( map: (ApplicationDescription) -> T, reduce: (List) -> R ): R { + Log.d("DebugTrackers", "mapReduceForHiddenApps: ${app}") return if (app == dummySystemApp) { reduce(getHiddenSystemApps().map(map)) } else if (app == dummyAppsCompatibilityApp) { diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/AppListUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/AppListUseCase.kt index 8db31187..507522b3 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/AppListUseCase.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/AppListUseCase.kt @@ -25,7 +25,14 @@ class AppListUseCase( private val appListsRepository: AppListsRepository ) { val dummySystemApp = appListsRepository.dummySystemApp - fun getApp(uid: Int): ApplicationDescription? = appListsRepository.getApp(uid) + fun getApp(uid: Int): ApplicationDescription { + return when (uid) { + dummySystemApp.uid -> dummySystemApp + appListsRepository.dummyAppsCompatibilityApp.uid -> + appListsRepository.dummyAppsCompatibilityApp + else -> appListsRepository.getApp(uid) ?: dummySystemApp + } + } fun getAppsUsingInternet(): Flow> { return appListsRepository.mainProfileApps() } 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 aa3c209c..a706cd8c 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 @@ -76,6 +76,13 @@ class TrackersStateUseCase( updateAllTrackersBlockedState() } + fun clearWhitelist(app: ApplicationDescription) { + appListsRepository.applyForHiddenApps(app, + blockTrackersPrivacyModule::clearWhiteList + ) + updateAllTrackersBlockedState() + } + fun updateTrackers() = coroutineScope.launch { trackersRepository.update() trackersPrivacyModule.start( 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 487a61e4..6b77fb28 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 @@ -182,8 +183,12 @@ class TrackersStatisticsUseCase( return appListsRepository.mapReduceForHiddenApps( app = app, map = { appDesc: ApplicationDescription -> - trackTrackersPrivacyModule.getTrackersForApp(appDesc) to - blockTrackersPrivacyModule.getWhiteList(appDesc) + Log.d("DebugTrackers", "geTrackersWithWhiteList: ${appDesc.apId}") + (trackTrackersPrivacyModule.getTrackersForApp(appDesc) to + blockTrackersPrivacyModule.getWhiteList(appDesc)).let { + Log.d("DebugTrackers", "geTrackersWithWhiteList: value $it") + it + } }, reduce = { lists -> lists.unzip().let { (trackerLists, whiteListedIdLists) -> @@ -196,6 +201,16 @@ class TrackersStatisticsUseCase( ) } + fun isWhiteListEmpty(app: ApplicationDescription): Boolean { + return appListsRepository.mapReduceForHiddenApps( + app = app, + map = { appDesc: ApplicationDescription -> + blockTrackersPrivacyModule.getWhiteList(appDesc).isEmpty() + }, + reduce = { areEmpty -> areEmpty.all { it } } + ) + } + fun getCalls(app: ApplicationDescription): Pair { return appListsRepository.mapReduceForHiddenApps( app = app, diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt index 32461494..721d9cbc 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt @@ -35,7 +35,6 @@ import foundation.e.privacycentralapp.PrivacyCentralApplication import foundation.e.privacycentralapp.R import foundation.e.privacycentralapp.common.NavToolbarFragment import foundation.e.privacycentralapp.databinding.ApptrackersFragmentBinding -import foundation.e.privacymodules.permissions.data.ApplicationDescription import kotlinx.coroutines.launch class AppTrackersFragment : NavToolbarFragment(R.layout.apptrackers_fragment) { @@ -86,6 +85,9 @@ class AppTrackersFragment : NavToolbarFragment(R.layout.apptrackers_fragment) { binding.blockAllToggle.setOnClickListener { viewModel.submitAction(AppTrackersViewModel.Action.BlockAllToggleAction(binding.blockAllToggle.isChecked)) } + binding.btnReset.setOnClickListener { + viewModel.submitAction(AppTrackersViewModel.Action.ResetAllTrackers) + } binding.trackers.apply { layoutManager = LinearLayoutManager(requireContext()) @@ -95,7 +97,7 @@ class AppTrackersFragment : NavToolbarFragment(R.layout.apptrackers_fragment) { onToggleSwitch = { tracker, isBlocked -> viewModel.submitAction(AppTrackersViewModel.Action.ToggleTrackerAction(tracker, isBlocked)) }, - onClickTitle = { viewModel.submitAction(AppTrackersViewModel.Action.ClickTracker(it)) } + onClickTitle = { viewModel.submitAction(AppTrackersViewModel.Action.ClickTracker(it)) }, ) } @@ -163,15 +165,19 @@ class AppTrackersFragment : NavToolbarFragment(R.layout.apptrackers_fragment) { ) } binding.noTrackersYet.isVisible = false + binding.btnReset.isVisible = true } else { binding.trackersListTitle.isVisible = false binding.trackers.isVisible = false binding.noTrackersYet.isVisible = true binding.noTrackersYet.text = getString( - if (state.isBlockingActivated) - R.string.apptrackers_no_trackers_yet_block_on - else R.string.apptrackers_no_trackers_yet_block_off + when { + !state.isBlockingActivated -> R.string.apptrackers_no_trackers_yet_block_off + state.isWhitelistEmpty -> R.string.apptrackers_no_trackers_yet_block_on + else -> R.string.app_trackers_no_trackers_yet_remaining_whitelist + } ) + binding.btnReset.isVisible = state.isBlockingActivated && !state.isWhitelistEmpty } } 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 80884433..87afc094 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 @@ -27,6 +27,7 @@ data class AppTrackersState( val leaked: Int = 0, val blocked: Int = 0, val isTrackersBlockingEnabled: Boolean = false, + val isWhitelistEmpty: Boolean = true, val showQuickPrivacyDisabledMessage: Boolean = false, ) { fun getTrackersStatus(): List>? { 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 07b9152a..250c72ec 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 @@ -18,6 +18,7 @@ package foundation.e.privacycentralapp.features.trackers.apptrackers import android.net.Uri +import android.util.Log import androidx.annotation.StringRes import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -62,7 +63,9 @@ class AppTrackersViewModel( isBlockingActivated = !trackersStateUseCase.isWhitelisted(app), trackersWithWhiteList = trackersStatisticsUseCase.getTrackersWithWhiteList( app - ), + ).let { Log.d("DebugTrackers", "trackersWithWhiteList: $it") + it}, + isWhitelistEmpty = trackersStatisticsUseCase.isWhiteListEmpty(app) ) } } @@ -82,6 +85,7 @@ class AppTrackersViewModel( is Action.BlockAllToggleAction -> blockAllToggleAction(action) is Action.ToggleTrackerAction -> toggleTrackerAction(action) is Action.ClickTracker -> actionClickTracker(action) + is Action.ResetAllTrackers -> resetAllTrackers() } } @@ -107,13 +111,7 @@ class AppTrackersViewModel( if (state.value.isBlockingActivated) { trackersStateUseCase.blockTracker(app, action.tracker, action.isBlocked) - _state.update { - it.copy( - trackersWithWhiteList = trackersStatisticsUseCase.getTrackersWithWhiteList( - app - ) - ) - } + updateWhitelist() } } } @@ -133,6 +131,12 @@ class AppTrackersViewModel( } } + private suspend fun resetAllTrackers() { + withContext(Dispatchers.IO) { + trackersStateUseCase.clearWhitelist(app) + updateWhitelist() + } + } private fun fetchStatistics() { val (blocked, leaked) = trackersStatisticsUseCase.getCalls(app) return _state.update { s -> @@ -140,6 +144,16 @@ class AppTrackersViewModel( trackersWithWhiteList = trackersStatisticsUseCase.getTrackersWithWhiteList(app), leaked = leaked, blocked = blocked, + isWhitelistEmpty = trackersStatisticsUseCase.isWhiteListEmpty(app) + ) + } + } + + private fun updateWhitelist() { + _state.update { s -> + s.copy( + trackersWithWhiteList = trackersStatisticsUseCase.getTrackersWithWhiteList(app), + isWhitelistEmpty = trackersStatisticsUseCase.isWhiteListEmpty(app) ) } } @@ -154,5 +168,6 @@ class AppTrackersViewModel( data class BlockAllToggleAction(val isBlocked: Boolean) : Action() data class ToggleTrackerAction(val tracker: Tracker, val isBlocked: Boolean) : Action() data class ClickTracker(val tracker: Tracker) : Action() + object ResetAllTrackers: Action() } } diff --git a/app/src/main/res/layout/apptrackers_fragment.xml b/app/src/main/res/layout/apptrackers_fragment.xml index e6e226fb..727eb918 100644 --- a/app/src/main/res/layout/apptrackers_fragment.xml +++ b/app/src/main/res/layout/apptrackers_fragment.xml @@ -84,18 +84,31 @@ android:id="@+id/trackers" android:layout_height="match_parent" android:layout_width="match_parent" - android:layout_marginBottom="32dp" + android:layout_marginBottom="16dp" tools:listitem="@layout/apptrackers_item_tracker_toggle" android:visibility="gone" /> + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 38cd38bd..49d9182a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -92,10 +92,12 @@ Opt for the trackers you want to activate/deactivate. No trackers were detected yet. If new trackers are detected they will be updated here. No trackers were detected yet. All future trackers will be blocked. + No trackers were detected yet. Some trackers were unblocked previously. Enable Quick Privacy to be able to activate/deactivate trackers. %1$d blocked trackers out of %2$d detected trackers, %3$d blocked leaks and %4$d allowed leaks. App not installed. Changes will take effect when tracker blocker is on. + Reset trackers Do not show again 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 1f1f5634..e12aca9d 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 @@ -88,4 +88,8 @@ class BlockTrackersPrivacyModule(context: Context) : IBlockTrackersPrivacyModule override fun setWhiteListed(app: ApplicationDescription, isWhiteListed: Boolean) { whitelistRepository.setWhiteListed(app.apId, isWhiteListed) } + + override fun clearWhiteList(app: ApplicationDescription) { + whitelistRepository.clearWhiteList(app.apId) + } } diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/IBlockTrackersPrivacyModule.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/IBlockTrackersPrivacyModule.kt index 0086629a..bf0e26e9 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/IBlockTrackersPrivacyModule.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/IBlockTrackersPrivacyModule.kt @@ -75,6 +75,9 @@ interface IBlockTrackersPrivacyModule { */ fun getWhiteList(app: ApplicationDescription): List + + fun clearWhiteList(app: ApplicationDescription) + /** * Callback interface to get updates about the state of the Block trackers module. */ 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 9fda1a9f..829ad850 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 @@ -198,4 +198,10 @@ class WhitelistRepository private constructor(context: Context) { fun getWhiteListForApp(app: ApplicationDescription): List { return trackersWhitelistByApp[app.apId]?.toList() ?: emptyList() } + + fun clearWhiteList(apId: String) { + trackersWhitelistByApp.remove(apId) + refreshAppUidTrackersWhiteList() + prefs.edit().remove(buildAppTrackersKey(apId)).commit() + } } -- GitLab From eeb1e5550b15eba8cc5fc232e5ff7c3de44e6e95 Mon Sep 17 00:00:00 2001 From: Guillaume Jacquart Date: Fri, 14 Apr 2023 19:45:24 +0200 Subject: [PATCH 5/7] fix lint issues. --- .../privacycentralapp/DependencyContainer.kt | 1 + .../e/privacycentralapp/common/AppsAdapter.kt | 2 +- .../data/repositories/AppListsRepository.kt | 25 +++++++++-------- .../domain/entities/AppWithCounts.kt | 1 + .../usecases/IpScramblingStateUseCase.kt | 1 - .../domain/usecases/TrackersStateUseCase.kt | 8 ++++-- .../usecases/TrackersStatisticsUseCase.kt | 28 +++++++++---------- .../features/dashboard/DashboardViewModel.kt | 4 +-- .../apptrackers/AppTrackersFragment.kt | 1 + .../trackers/apptrackers/AppTrackersState.kt | 1 + .../apptrackers/AppTrackersViewModel.kt | 7 ++--- .../main/res/layout/apptrackers_fragment.xml | 3 +- .../permissions/PermissionsPrivacyModule.kt | 1 - .../permissions/APermissionsPrivacyModule.kt | 19 ++----------- .../permissions/IPermissionsPrivacyModule.kt | 23 ++------------- .../data/ApplicationDescription.kt | 6 ++-- trackers/build.gradle | 3 +- .../trackers/DNSBlockerRunnable.kt | 1 + .../privacymodules/trackers/TrackersLogger.kt | 1 + .../api/BlockTrackersPrivacyModule.kt | 7 +++-- .../api/IBlockTrackersPrivacyModule.kt | 2 +- .../api/ITrackTrackersPrivacyModule.kt | 4 ++- .../api/TrackTrackersPrivacyModule.kt | 1 + .../trackers/data/StatsDatabase.kt | 8 +++--- .../trackers/data/StatsRepository.kt | 7 ++--- .../trackers/data/WhitelistRepository.kt | 18 ++++++------ 26 files changed, 81 insertions(+), 102 deletions(-) diff --git a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt index bad1fd08..aab81d5d 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt @@ -1,4 +1,5 @@ /* + * Copyright (C) 2023 MURENA SAS * Copyright (C) 2021 E FOUNDATION * * This program is free software: you can redistribute it and/or modify diff --git a/app/src/main/java/foundation/e/privacycentralapp/common/AppsAdapter.kt b/app/src/main/java/foundation/e/privacycentralapp/common/AppsAdapter.kt index 7fedf5d2..2fbbc34a 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/common/AppsAdapter.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/common/AppsAdapter.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 E FOUNDATION, 2022 MURENA SAS + * Copyright (C) 2021 E FOUNDATION, 2022 - 2023 MURENA SAS * * 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 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 ba342baf..12c6c453 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 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 E FOUNDATION, 2022 MURENA SAS + * Copyright (C) 2022 E FOUNDATION, 2022 - 2023 MURENA SAS * * 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 @@ -22,7 +22,6 @@ import android.content.Context import android.content.Intent import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo -import android.util.Log import foundation.e.privacycentralapp.R import foundation.e.privacymodules.permissions.PermissionsPrivacyModule import foundation.e.privacymodules.permissions.data.ApplicationDescription @@ -101,11 +100,13 @@ class AppListsRepository( updateMaps(visibleApps + hiddenApps + aCApps) - allProfilesAppDescriptions.emit(Triple( - visibleApps + dummySystemApp + dummyAppsCompatibilityApp, - hiddenApps, - aCApps - )) + allProfilesAppDescriptions.emit( + Triple( + visibleApps + dummySystemApp + dummyAppsCompatibilityApp, + hiddenApps, + aCApps + ) + ) } private fun recycleIcons( @@ -114,15 +115,16 @@ class AppListsRepository( ): List { val oldVisibleApps = allProfilesAppDescriptions.value.first return newApps.map { app -> - app.copy(icon = oldVisibleApps.find { app.apId == it.apId }?.icon - ?: if (fetchMissingIcons) permissionsModule.getApplicationIcon(app) else null + app.copy( + icon = oldVisibleApps.find { app.apId == it.apId }?.icon + ?: if (fetchMissingIcons) permissionsModule.getApplicationIcon(app) else null ) } } private fun updateMaps(apps: List) { val byUid = mutableMapOf() - val byApId = mutableMapOf() + val byApId = mutableMapOf() apps.forEach { app -> if (byUid[app.uid].let { it == null || it.packageName > app.packageName }) { byUid[app.uid] = app @@ -204,7 +206,6 @@ class AppListsRepository( map: (ApplicationDescription) -> T, reduce: (List) -> R ): R { - Log.d("DebugTrackers", "mapReduceForHiddenApps: ${app}") return if (app == dummySystemApp) { reduce(getHiddenSystemApps().map(map)) } else if (app == dummyAppsCompatibilityApp) { @@ -225,7 +226,7 @@ class AppListsRepository( fun getApp(apId: String): ApplicationDescription? { if (apId.isBlank()) return null - return appsByAPId[apId]?: run { + return appsByAPId[apId] ?: run { runBlocking { refreshAppDescriptions(fetchMissingIcons = false, force = true)?.join() } appsByAPId[apId] } diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/entities/AppWithCounts.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/AppWithCounts.kt index 507dfde6..afdd2d58 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/domain/entities/AppWithCounts.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/AppWithCounts.kt @@ -1,4 +1,5 @@ /* + * Copyright (C) 2023 MURENA SAS * Copyright (C) 2022 E FOUNDATION * * This program is free software: you can redistribute it and/or modify diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/IpScramblingStateUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/IpScramblingStateUseCase.kt index ac853f1d..2c7998a1 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/IpScramblingStateUseCase.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/IpScramblingStateUseCase.kt @@ -17,7 +17,6 @@ package foundation.e.privacycentralapp.domain.usecases -import android.util.Log import foundation.e.privacycentralapp.data.repositories.AppListsRepository import foundation.e.privacycentralapp.data.repositories.LocalStateRepository import foundation.e.privacycentralapp.domain.entities.InternetPrivacyMode 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 a706cd8c..afb6d1ee 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 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 E FOUNDATION, 2022 MURENA SAS + * Copyright (C) 2021 E FOUNDATION, 2022 - 2023 MURENA SAS * * 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 @@ -40,7 +40,8 @@ class TrackersStateUseCase( trackers = trackersRepository.trackers, getAppByAPId = appListsRepository::getApp, getAppByUid = appListsRepository::getApp, - enableNotification = false) + enableNotification = false + ) coroutineScope.launch { localStateRepository.blockTrackers.collect { enabled -> if (enabled) { @@ -77,7 +78,8 @@ class TrackersStateUseCase( } fun clearWhitelist(app: ApplicationDescription) { - appListsRepository.applyForHiddenApps(app, + appListsRepository.applyForHiddenApps( + app, blockTrackersPrivacyModule::clearWhiteList ) updateAllTrackersBlockedState() 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 6b77fb28..41fd4c01 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 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 E FOUNDATION, 2022 MURENA SAS + * Copyright (C) 2021 E FOUNDATION, 2022 - 2023 MURENA SAS * * 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 @@ -18,7 +18,6 @@ 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 @@ -66,7 +65,6 @@ class TrackersStatisticsUseCase( .throttleFirst(windowDuration = debounce) .onStart { emit(Unit) } - fun getDayStatistics(): Pair { return TrackersPeriodicStatistics( callsBlockedNLeaked = trackTrackersPrivacyModule.getPastDayTrackersCalls(), @@ -93,7 +91,6 @@ class TrackersStatisticsUseCase( else flowOf(trackTrackersPrivacyModule.getTrackersCount()) } - fun getMostLeakedApp(): ApplicationDescription? { return trackTrackersPrivacyModule.getPastDayMostLeakedApp() } @@ -183,12 +180,10 @@ class TrackersStatisticsUseCase( return appListsRepository.mapReduceForHiddenApps( app = app, map = { appDesc: ApplicationDescription -> - Log.d("DebugTrackers", "geTrackersWithWhiteList: ${appDesc.apId}") - (trackTrackersPrivacyModule.getTrackersForApp(appDesc) to - blockTrackersPrivacyModule.getWhiteList(appDesc)).let { - Log.d("DebugTrackers", "geTrackersWithWhiteList: value $it") - it - } + ( + trackTrackersPrivacyModule.getTrackersForApp(appDesc) to + blockTrackersPrivacyModule.getWhiteList(appDesc) + ) }, reduce = { lists -> lists.unzip().let { (trackerLists, whiteListedIdLists) -> @@ -237,22 +232,25 @@ class TrackersStatisticsUseCase( val calls = appListsRepository.mapReduceForHiddenApps( app = app, map = { callsByApp.getOrDefault(app, 0 to 0) }, - reduce = { it.unzip().let { (blocked, leaked) -> - blocked.sum() to leaked.sum() } } + reduce = { + it.unzip().let { (blocked, leaked) -> + blocked.sum() to leaked.sum() + } + } ) AppWithCounts( app = app, isWhitelisted = !blockTrackersPrivacyModule.isBlockingEnabled() || isWhitelisted(app, appListsRepository, blockTrackersPrivacyModule), - trackersCount = when(app) { + trackersCount = when (app) { appListsRepository.dummySystemApp -> hiddenAppsTrackersWithWhiteList.size appListsRepository.dummyAppsCompatibilityApp -> acAppsTrackersWithWhiteList.size else -> trackersCounts.getOrDefault(app, 0) }, - whiteListedTrackersCount = when(app) { + whiteListedTrackersCount = when (app) { appListsRepository.dummySystemApp -> hiddenAppsTrackersWithWhiteList.count { it.second } appListsRepository.dummyAppsCompatibilityApp -> @@ -264,7 +262,7 @@ class TrackersStatisticsUseCase( leaks = calls.second ) } - .sortedWith(mostLeakedAppsComparator) + .sortedWith(mostLeakedAppsComparator) } } diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardViewModel.kt b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardViewModel.kt index dd2e5d38..f3a97742 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardViewModel.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardViewModel.kt @@ -1,4 +1,5 @@ /* +* Copyright (C) 2023 MURENA SAS * Copyright (C) 2021 E FOUNDATION * * This program is free software: you can redistribute it and/or modify @@ -51,8 +52,7 @@ class DashboardViewModel( val singleEvents = _singleEvents.asSharedFlow() init { - viewModelScope.launch(Dispatchers.IO) { trackersStatisticsUseCase.initAppList() // TODO get rid of that ? - } + viewModelScope.launch(Dispatchers.IO) { trackersStatisticsUseCase.initAppList() } } suspend fun doOnStartedState() = withContext(Dispatchers.IO) { diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt index 721d9cbc..888c1407 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt @@ -1,4 +1,5 @@ /* + * Copyright (C) 2023 MURENA SAS * Copyright (C) 2021 E FOUNDATION * * This program is free software: you can redistribute it and/or modify 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 87afc094..a190a744 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 @@ -1,4 +1,5 @@ /* + * Copyright (C) 2023 MURENA SAS * Copyright (C) 2022 E FOUNDATION * * This program is free software: you can redistribute it and/or modify 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 250c72ec..e5a94f92 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 @@ -1,4 +1,5 @@ /* + * Copyright (C) 2023 MURENA SAS * Copyright (C) 2021 E FOUNDATION * * This program is free software: you can redistribute it and/or modify @@ -18,7 +19,6 @@ package foundation.e.privacycentralapp.features.trackers.apptrackers import android.net.Uri -import android.util.Log import androidx.annotation.StringRes import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -63,8 +63,7 @@ class AppTrackersViewModel( isBlockingActivated = !trackersStateUseCase.isWhitelisted(app), trackersWithWhiteList = trackersStatisticsUseCase.getTrackersWithWhiteList( app - ).let { Log.d("DebugTrackers", "trackersWithWhiteList: $it") - it}, + ), isWhitelistEmpty = trackersStatisticsUseCase.isWhiteListEmpty(app) ) } @@ -168,6 +167,6 @@ class AppTrackersViewModel( data class BlockAllToggleAction(val isBlocked: Boolean) : Action() data class ToggleTrackerAction(val tracker: Tracker, val isBlocked: Boolean) : Action() data class ClickTracker(val tracker: Tracker) : Action() - object ResetAllTrackers: Action() + object ResetAllTrackers : Action() } } diff --git a/app/src/main/res/layout/apptrackers_fragment.xml b/app/src/main/res/layout/apptrackers_fragment.xml index 727eb918..d0a72d58 100644 --- a/app/src/main/res/layout/apptrackers_fragment.xml +++ b/app/src/main/res/layout/apptrackers_fragment.xml @@ -1,4 +1,5 @@