diff options
| author | Guillaume Jacquart <guillaume.jacquart@hoodbrains.com> | 2023-04-21 06:25:55 +0000 |
|---|---|---|
| committer | Guillaume Jacquart <guillaume.jacquart@hoodbrains.com> | 2023-04-21 06:25:55 +0000 |
| commit | ef669659db8c9fbab2a84dcbc8f3e99411eb1201 (patch) | |
| tree | 4785f6b44d121f95c840020441687bce5777a44f /app/src/main/java/foundation/e/privacycentralapp/data | |
| parent | 2df577ca97a674a4bd3875dc5137bb44df2c03ef (diff) | |
| parent | 6068cebe972e000872e4780dd9f75680a3abf073 (diff) | |
| download | advanced-privacy-ef669659db8c9fbab2a84dcbc8f3e99411eb1201.tar.gz | |
Merge branch '6556-use_pname_for_trackers_2' into 'main'
6556: add AdvancedPrivacy App Id in trackers stats to avoid appUid aliasing
See merge request e/os/advanced-privacy!122
Diffstat (limited to 'app/src/main/java/foundation/e/privacycentralapp/data')
| -rw-r--r-- | app/src/main/java/foundation/e/privacycentralapp/data/repositories/AppListsRepository.kt | 197 |
1 files changed, 125 insertions, 72 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 a97888f..a4f7487 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 @@ -25,6 +25,7 @@ import android.content.pm.PackageInfo 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 +33,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, @@ -44,7 +46,7 @@ class AppListsRepository( private const val PNAME_INTENT_VERIFICATION = "com.android.statementservice" private const val PNAME_MICROG_SERVICES_CORE = "com.google.android.gms" - val appsCompatibiltyPNames = setOf( + val compatibiltyPNames = setOf( PNAME_PWAPLAYER, PNAME_INTENT_VERIFICATION, PNAME_MICROG_SERVICES_CORE ) } @@ -53,18 +55,22 @@ class AppListsRepository( 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( + val dummyCompatibilityApp = 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() { - val launcherPackageNames = pm.queryIntentActivities( + private suspend fun fetchAppDescriptions(fetchMissingIcons: Boolean = false) { + val launcherPackageNames = context.packageManager.queryIntentActivities( Intent(Intent.ACTION_MAIN, null).apply { addCategory(Intent.CATEGORY_LAUNCHER) }, 0 ).mapNotNull { it.activityInfo?.packageName } @@ -79,104 +85,151 @@ class AppListsRepository( isHiddenSystemApp(packageInfo.applicationInfo, launcherPackageNames) } - val aCFilter = { packageInfo: PackageInfo -> - packageInfo.packageName in appsCompatibiltyPNames + val compatibilityAppsFilter = { packageInfo: PackageInfo -> + packageInfo.packageName in compatibiltyPNames } - 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 compatibilityApps = permissionsModule.getApplications(compatibilityAppsFilter) + + updateMaps(visibleApps + hiddenApps + compatibilityApps) + + allProfilesAppDescriptions.emit( + Triple( + visibleApps + dummySystemApp + dummyCompatibilityApp, + hiddenApps, + compatibilityApps + ) + ) + } + + private fun recycleIcons( + newApps: List<ApplicationDescription>, + fetchMissingIcons: Boolean + ): List<ApplicationDescription> { + 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 + ) + } + } - val workProfileVisibleApps = permissionsModule.getWorkProfileApplications(visibleAppsFilter, true) - val workProfileHiddenApps = permissionsModule.getWorkProfileApplications(hiddenAppsFilter, false) - val workProfileACApps = permissionsModule.getApplications(aCFilter, false) + private fun updateMaps(apps: List<ApplicationDescription>) { + val byUid = mutableMapOf<Int, ApplicationDescription>() + val byApId = mutableMapOf<String, ApplicationDescription>() + apps.forEach { app -> + byUid[app.uid]?.run { packageName > app.packageName } == true + if (byUid[app.uid].let { it == null || it.packageName > app.packageName }) { + byUid[app.uid] = app + } - appDescriptions.emit((visibleApps + dummySystemApp + dummyAppsCompatibilityApp) to hiddenApps) - allProfilesAppDescriptions.emit(Triple( - (visibleApps + workProfileVisibleApps + dummySystemApp + dummyAppsCompatibilityApp), - (hiddenApps + workProfileHiddenApps), - (aCApps + workProfileACApps) - )) + 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? { + if (refreshAppJob == null) { refreshAppJob = coroutineScope.launch(Dispatchers.IO) { - fetchAppDescriptions() - refreshAppJob = null + if (force || context.packageManager.getChangedPackages(lastFetchApps) != null) { + fetchAppDescriptions(fetchMissingIcons = fetchMissingIcons) + if (fetchMissingIcons) { + lastFetchApps = context.packageManager.getChangedPackages(lastFetchApps) + ?.sequenceNumber ?: lastFetchApps + } + + refreshAppJob = null + } } } + + return refreshAppJob } - fun getVisibleApps(): Flow<List<ApplicationDescription>> { + fun mainProfileApps(): Flow<List<ApplicationDescription>> { 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<ApplicationDescription> { - return appDescriptions.value.second + fun getMainProfileHiddenSystemApps(): List<ApplicationDescription> { + return allProfilesAppDescriptions.value.second.filter { it.profileType == ProfileType.MAIN } } - fun getAllProfilesVisibleApps(): Flow<List<ApplicationDescription>> { + fun apps(): Flow<List<ApplicationDescription>> { refreshAppDescriptions() - return allProfilesAppDescriptions.map { it.first.sortedBy { app -> app.label.toString().lowercase() } } + return allProfilesAppDescriptions.map { + it.first.sortedBy { app -> app.label.toString().lowercase() } + } + } + + fun allApps(): Flow<List<ApplicationDescription>> { + return allProfilesAppDescriptions.map { + it.first + it.second + it.third + } } - fun getAllProfilesHiddenSystemApps(): List<ApplicationDescription> { + private fun getHiddenSystemApps(): List<ApplicationDescription> { return allProfilesAppDescriptions.value.second } - fun getAllProfilesACApps(): List<ApplicationDescription> { + private fun getCompatibilityApps(): List<ApplicationDescription> { return allProfilesAppDescriptions.value.third } - fun getAllApps(): Flow<List<ApplicationDescription>> = getAllProfilesVisibleApps() - .map { it + getAllProfilesHiddenSystemApps() + getAllProfilesACApps()} - - 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 == dummyCompatibilityApp) { + getCompatibilityApps().any { test(it) } + } else test(app) } - 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 applyForHiddenApps(app: ApplicationDescription, action: (ApplicationDescription) -> Unit) { + mapReduceForHiddenApps(app = app, map = action, reduce = {}) } - 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 <T, R> mapReduceForHiddenApps( + app: ApplicationDescription, + map: (ApplicationDescription) -> T, + reduce: (List<T>) -> R + ): R { + return if (app == dummySystemApp) { + reduce(getHiddenSystemApps().map(map)) + } else if (app == dummyCompatibilityApp) { + reduce(getCompatibilityApps().map(map)) + } else reduce(listOf(map(app))) } - 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) - } + private var appsByUid = mapOf<Int, ApplicationDescription>() + private var appsByAPId = mapOf<String, ApplicationDescription>() + fun getApp(appUid: Int): ApplicationDescription? { + return appsByUid[appUid] ?: run { + runBlocking { refreshAppDescriptions(fetchMissingIcons = false, force = true)?.join() } + appsByUid[appUid] + } + } - private val pm get() = context.packageManager + fun getApp(apId: String): ApplicationDescription? { + if (apId.isBlank()) return null - private val appDescriptions = MutableStateFlow( - Pair( - emptyList<ApplicationDescription>(), - emptyList<ApplicationDescription>() - ) - ) + return appsByAPId[apId] ?: run { + runBlocking { refreshAppDescriptions(fetchMissingIcons = false, force = true)?.join() } + appsByAPId[apId] + } + } private val allProfilesAppDescriptions = MutableStateFlow( Triple( @@ -209,7 +262,7 @@ class AppListsRepository( private fun isStandardApp(app: ApplicationInfo, launcherApps: List<String>): Boolean { return when { app.packageName == PNAME_SETTINGS -> false - app.packageName in appsCompatibiltyPNames -> false + app.packageName in compatibiltyPNames -> false app.hasFlag(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) -> true !app.hasFlag(ApplicationInfo.FLAG_SYSTEM) -> true launcherApps.contains(app.packageName) -> true @@ -219,7 +272,7 @@ class AppListsRepository( private fun isHiddenSystemApp(app: ApplicationInfo, launcherApps: List<String>): Boolean { return when { - app.packageName in appsCompatibiltyPNames -> false + app.packageName in compatibiltyPNames -> false else -> !isNotHiddenSystemApp(app, launcherApps) } } |
