summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillaume Jacquart <guillaume.jacquart@hoodbrains.com>2023-01-02 08:35:21 +0000
committerGuillaume Jacquart <guillaume.jacquart@hoodbrains.com>2023-01-02 08:35:21 +0000
commitf1e0961daf535628252f18c06fe22001f84015b5 (patch)
tree78a37bc4da310662f44c4e6c362c0d58de2cc2e6
parent7333a0d80ef3fb879fb6d261988deb78b4857393 (diff)
parentf1d5b12dd11019508208571db5e5f57f43e3c4b6 (diff)
Merge branch '5648-apps_pro_profile' into 'main'
5648: display trackers for pro-profile app instances. See merge request e/os/advanced-privacy!100
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt1
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/common/AppsAdapter.kt8
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/data/repositories/AppListsRepository.kt107
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt10
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt14
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt6
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt8
-rw-r--r--build.gradle2
-rw-r--r--permissionse/libs/hidden-apis-stub/build.gradle2
-rw-r--r--permissionse/libs/hidden-apis-stub/src/main/java/android/content/pm/PackageManager.java25
-rw-r--r--permissionse/libs/hidden-apis-stub/src/main/java/android/content/pm/UserInfo.java36
-rw-r--r--permissionse/libs/hidden-apis-stub/src/main/java/android/os/UserManager.java48
-rw-r--r--permissionse/src/main/AndroidManifest.xml4
-rw-r--r--permissionse/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt45
-rw-r--r--permissionsstandalone/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt20
-rw-r--r--privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/APermissionsPrivacyModule.kt8
-rw-r--r--privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/IPermissionsPrivacyModule.kt13
17 files changed, 286 insertions, 71 deletions
diff --git a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
index e6d4c42..6ad84a7 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
@@ -182,7 +182,6 @@ class ViewModelsFactory(
TrackersViewModel::class.java ->
TrackersViewModel(
- getQuickPrivacyStateUseCase = getQuickPrivacyStateUseCase,
trackersStatisticsUseCase = trackersStatisticsUseCase
)
FakeLocationViewModel::class.java ->
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 16b0144..7b09c51 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
+ * Copyright (C) 2021 E FOUNDATION, 2022 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
@@ -28,11 +28,11 @@ import foundation.e.privacycentralapp.domain.entities.AppWithCounts
class AppsAdapter(
private val itemsLayout: Int,
- private val listener: (String) -> Unit
+ private val listener: (Int) -> Unit
) :
RecyclerView.Adapter<AppsAdapter.ViewHolder>() {
- class ViewHolder(view: View, private val listener: (String) -> Unit) : RecyclerView.ViewHolder(view) {
+ class ViewHolder(view: View, private val listener: (Int) -> Unit) : RecyclerView.ViewHolder(view) {
val appName: TextView = view.findViewById(R.id.title)
val counts: TextView = view.findViewById(R.id.counts)
val icon: ImageView = view.findViewById(R.id.icon)
@@ -46,7 +46,7 @@ class AppsAdapter(
)
icon.setImageDrawable(item.icon)
- itemView.setOnClickListener { listener(item.packageName) }
+ 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 febda91..65ae478 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
+ * Copyright (C) 2022 E FOUNDATION, 2022 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
@@ -21,11 +21,13 @@ import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
-import android.content.pm.PackageManager
+import android.content.pm.PackageInfo
import foundation.e.privacycentralapp.R
import foundation.e.privacymodules.permissions.PermissionsPrivacyModule
import foundation.e.privacymodules.permissions.data.ApplicationDescription
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
@@ -48,24 +50,67 @@ class AppListsRepository(
icon = context.getDrawable(R.drawable.ic_e_app_logo)
)
- fun getVisibleApps(): Flow<List<ApplicationDescription>> {
- coroutineScope.launch {
- val (visible, hidden) = splitVisibleToHidden(getAppsUsingInternet())
- appDescriptions.emit(
- Pair(
- visible.map { permissionsModule.buildApplicationDescription(it, withIcon = true) } + dummySystemApp,
- hidden.map { permissionsModule.buildApplicationDescription(it, withIcon = false) },
- )
- )
+ private suspend fun fetchAppDescriptions() {
+ val launcherPackageNames = pm.queryIntentActivities(
+ Intent(Intent.ACTION_MAIN, null).apply { addCategory(Intent.CATEGORY_LAUNCHER) },
+ 0
+ ).mapNotNull { it.activityInfo?.packageName }
+
+ val visibleAppsFilter = { packageInfo: PackageInfo ->
+ hasInternetPermission(packageInfo) &&
+ isNotHiddenSystemApp(packageInfo.applicationInfo, launcherPackageNames)
+ }
+
+ val hiddenAppsFilter = { packageInfo: PackageInfo ->
+ hasInternetPermission(packageInfo) &&
+ !isNotHiddenSystemApp(packageInfo.applicationInfo, launcherPackageNames)
+ }
+
+ val visibleApps = permissionsModule.getApplications(visibleAppsFilter, true)
+ val hiddenApps = permissionsModule.getApplications(hiddenAppsFilter, false)
+
+ val workProfileVisibleApps = permissionsModule.getWorkProfileApplications(visibleAppsFilter, true)
+ val workProfileHiddenApps = permissionsModule.getWorkProfileApplications(hiddenAppsFilter, false)
+
+ appDescriptions.emit((visibleApps + dummySystemApp) to hiddenApps)
+ allProfilesAppDescriptions.emit(
+ (visibleApps + workProfileVisibleApps + dummySystemApp)
+ to (hiddenApps + workProfileHiddenApps)
+ )
+ }
+
+ private var refreshAppJob: Job? = null
+ private fun refreshAppDescriptions() {
+ if (refreshAppJob != null) {
+ return
+ } else {
+ refreshAppJob = coroutineScope.launch(Dispatchers.IO) {
+ fetchAppDescriptions()
+ refreshAppJob = null
+ }
}
+ }
+
+ fun getVisibleApps(): Flow<List<ApplicationDescription>> {
+ refreshAppDescriptions()
return appDescriptions.map { it.first.sortedBy { app -> app.label.toString().lowercase() } }
}
+
fun getHiddenSystemApps(): List<ApplicationDescription> {
return appDescriptions.value.second
}
- fun getVisibleAndHiddenApps(): Flow<List<ApplicationDescription>> = getVisibleApps()
- .map { it + getHiddenSystemApps() }
+ fun getAllProfilesVisibleApps(): Flow<List<ApplicationDescription>> {
+ refreshAppDescriptions()
+ return allProfilesAppDescriptions.map { it.first.sortedBy { app -> app.label.toString().lowercase() } }
+ }
+
+ fun getAllProfilesHiddenSystemApps(): List<ApplicationDescription> {
+ return allProfilesAppDescriptions.value.second
+ }
+
+ fun getAllApps(): Flow<List<ApplicationDescription>> = getAllProfilesVisibleApps()
+ .map { it + getAllProfilesHiddenSystemApps() }
fun getApplicationDescription(packageName: String): ApplicationDescription? {
return appDescriptions.value.first.find { it.packageName == packageName }
@@ -77,7 +122,7 @@ class AppListsRepository(
fun foldForHiddenSystemApp(appUid: Int, appValueGetter: (Int) -> Int): Int {
return if (appUid == dummySystemApp.uid) {
- getHiddenSystemApps().fold(0) { acc, app ->
+ getAllProfilesHiddenSystemApps().fold(0) { acc, app ->
acc + appValueGetter(app.uid)
}
} else appValueGetter(appUid)
@@ -92,14 +137,18 @@ class AppListsRepository(
)
)
- private fun getAppsUsingInternet(): List<ApplicationInfo> {
- return pm.getInstalledPackages(PackageManager.GET_PERMISSIONS).filter {
- it.requestedPermissions?.contains(Manifest.permission.INTERNET) == true
- }.map {
- it.applicationInfo
- }
+ private val allProfilesAppDescriptions = MutableStateFlow(
+ Pair(
+ emptyList<ApplicationDescription>(),
+ emptyList<ApplicationDescription>()
+ )
+ )
+
+ private fun hasInternetPermission(packageInfo: PackageInfo): Boolean {
+ return packageInfo.requestedPermissions?.contains(Manifest.permission.INTERNET) == true
}
+ @Suppress("ReturnCount")
private fun isNotHiddenSystemApp(app: ApplicationInfo, launcherApps: List<String>): Boolean {
if (app.packageName == PNAME_SETTINGS) {
return false
@@ -116,22 +165,4 @@ class AppListsRepository(
}
private fun ApplicationInfo.hasFlag(flag: Int) = (flags and flag) == 1
-
- private fun splitVisibleToHidden(apps: List<ApplicationInfo>): Pair<List<ApplicationInfo>, List<ApplicationInfo>> {
- val launcherPackageNames = pm.queryIntentActivities(
- Intent(Intent.ACTION_MAIN, null).apply { addCategory(Intent.CATEGORY_LAUNCHER) },
- 0
- ).mapNotNull { it.activityInfo?.packageName }
- return apps.fold(
- mutableListOf<ApplicationInfo>() to mutableListOf<ApplicationInfo>()
- ) {
- acc, app ->
- if (isNotHiddenSystemApp(app, launcherPackageNames)) {
- acc.first.add(app)
- } else {
- acc.second.add(app)
- }
- acc
- }
- }
}
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 8b37152..11f0466 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
+ * Copyright (C) 2021 E FOUNDATION, 2022 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
@@ -68,7 +68,7 @@ class TrackersStateUseCase(
fun getTrackersWhitelistIds(appUid: Int): List<String> {
return if (appUid == appListsRepository.dummySystemApp.uid) {
- appListsRepository.getHiddenSystemApps().fold(mutableSetOf<String>()) { acc, app ->
+ appListsRepository.getAllProfilesHiddenSystemApps().fold(mutableSetOf<String>()) { acc, app ->
acc.addAll(blockTrackersPrivacyModule.getWhiteList(app.uid).map { it.id })
acc
}.toList()
@@ -77,7 +77,7 @@ class TrackersStateUseCase(
fun toggleAppWhitelist(appUid: Int, isWhitelisted: Boolean) {
if (appUid == appListsRepository.dummySystemApp.uid) {
- appListsRepository.getHiddenSystemApps().forEach {
+ appListsRepository.getAllProfilesHiddenSystemApps().forEach {
blockTrackersPrivacyModule.setWhiteListed(it.uid, isWhitelisted)
}
} else blockTrackersPrivacyModule.setWhiteListed(appUid, isWhitelisted)
@@ -87,7 +87,7 @@ class TrackersStateUseCase(
fun blockTracker(appUid: Int, tracker: Tracker, isBlocked: Boolean) {
if (appUid == appListsRepository.dummySystemApp.uid) {
- appListsRepository.getHiddenSystemApps().forEach {
+ appListsRepository.getAllProfilesHiddenSystemApps().forEach {
blockTrackersPrivacyModule.setWhiteListed(tracker, it.uid, !isBlocked)
}
} else blockTrackersPrivacyModule.setWhiteListed(tracker, appUid, !isBlocked)
@@ -107,7 +107,7 @@ fun isWhitelisted(
blockTrackersPrivacyModule: IBlockTrackersPrivacyModule
): Boolean {
return if (appUid == appListsRepository.dummySystemApp.uid) {
- appListsRepository.getHiddenSystemApps().any {
+ appListsRepository.getAllProfilesHiddenSystemApps().any {
blockTrackersPrivacyModule.isWhitelisted(it.uid)
}
} else blockTrackersPrivacyModule.isWhitelisted(appUid)
diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt
index 5103eb2..404032b 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
+ * Copyright (C) 2021 E FOUNDATION, 2022 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
@@ -47,7 +47,7 @@ class TrackersStatisticsUseCase(
private val resources: Resources
) {
fun initAppList() {
- appListsRepository.getVisibleApps()
+ appListsRepository.getAllProfilesVisibleApps()
}
private fun rawUpdates(): Flow<Unit> = callbackFlow {
@@ -163,7 +163,7 @@ class TrackersStatisticsUseCase(
fun getTrackers(appUid: Int): List<Tracker> {
val trackers = if (appUid == appListsRepository.dummySystemApp.uid) {
- appListsRepository.getHiddenSystemApps().map {
+ appListsRepository.getAllProfilesHiddenSystemApps().map {
trackTrackersPrivacyModule.getTrackersForApp(it.uid)
}.flatten().distinctBy { it.id }
} else trackTrackersPrivacyModule.getTrackersForApp(appUid)
@@ -175,7 +175,7 @@ class TrackersStatisticsUseCase(
val trackers: List<Tracker>
val whiteListedTrackersIds: Set<String>
if (appUid == appListsRepository.dummySystemApp.uid) {
- val hiddenApps = appListsRepository.getHiddenSystemApps()
+ val hiddenApps = appListsRepository.getAllProfilesHiddenSystemApps()
trackers = trackTrackersPrivacyModule.getTrackers(hiddenApps.map { it.uid })
whiteListedTrackersIds = hiddenApps.fold(HashSet<String>()) { acc, app ->
@@ -193,7 +193,7 @@ class TrackersStatisticsUseCase(
fun getCalls(appUid: Int): Pair<Int, Int> {
return if (appUid == appListsRepository.dummySystemApp.uid) {
- appListsRepository.getHiddenSystemApps().map {
+ appListsRepository.getAllProfilesHiddenSystemApps().map {
trackTrackersPrivacyModule.getPastDayTrackersCallsForApp(it.uid)
}.reduce { (accBlocked, accLeaked), (blocked, leaked) ->
accBlocked + blocked to accLeaked + leaked
@@ -206,7 +206,7 @@ class TrackersStatisticsUseCase(
val hiddenAppsTrackersWithWhiteList =
getTrackersWithWhiteList(appListsRepository.dummySystemApp.uid)
- return appListsRepository.getVisibleApps()
+ return appListsRepository.getAllProfilesVisibleApps()
.map { apps ->
val callsByApp = trackTrackersPrivacyModule.getPastDayTrackersCallsByApps()
apps.map { app ->
@@ -249,7 +249,7 @@ class TrackersStatisticsUseCase(
fun getNonBlockedTrackersCount(): Flow<Int> {
return if (blockTrackersPrivacyModule.isBlockingEnabled())
- appListsRepository.getVisibleAndHiddenApps().map { apps ->
+ appListsRepository.getAllApps().map { apps ->
val whiteListedTrackers = mutableSetOf<Tracker>()
val whiteListedAppUids = blockTrackersPrivacyModule.getWhiteListedApp()
apps.forEach { app ->
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt
index 83359e1..cb32c2c 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 E FOUNDATION
+ * Copyright (C) 2021 E FOUNDATION, 2022 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
@@ -78,9 +78,9 @@ class TrackersFragment :
binding.apps.apply {
layoutManager = LinearLayoutManager(requireContext())
setHasFixedSize(true)
- adapter = AppsAdapter(R.layout.trackers_item_app) { packageName ->
+ adapter = AppsAdapter(R.layout.trackers_item_app) { appUid ->
viewModel.submitAction(
- TrackersViewModel.Action.ClickAppAction(packageName)
+ TrackersViewModel.Action.ClickAppAction(appUid)
)
}
}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt
index 2cdfabc..8b5cc32 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 E FOUNDATION
+ * Copyright (C) 2021 E FOUNDATION, 2022 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
@@ -21,7 +21,6 @@ import android.net.Uri
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import foundation.e.privacycentralapp.domain.entities.AppWithCounts
-import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase
import foundation.e.privacycentralapp.domain.usecases.TrackersStatisticsUseCase
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
@@ -35,7 +34,6 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class TrackersViewModel(
- private val getQuickPrivacyStateUseCase: GetQuickPrivacyStateUseCase,
private val trackersStatisticsUseCase: TrackersStatisticsUseCase
) : ViewModel() {
@@ -79,7 +77,7 @@ class TrackersViewModel(
}
private suspend fun actionClickApp(action: Action.ClickAppAction) {
- state.value.apps?.find { it.packageName == action.packageName }?.let {
+ state.value.apps?.find { it.uid == action.appUid }?.let {
_singleEvents.emit(SingleEvent.OpenAppDetailsEvent(it))
}
}
@@ -91,7 +89,7 @@ class TrackersViewModel(
}
sealed class Action {
- data class ClickAppAction(val packageName: String) : Action()
+ data class ClickAppAction(val appUid: Int) : Action()
object ClickLearnMore : Action()
}
}
diff --git a/build.gradle b/build.gradle
index 83197c3..812fbfb 100644
--- a/build.gradle
+++ b/build.gradle
@@ -26,7 +26,7 @@ buildscript {
'targetSdk' : 31,
'version' : [
'major': 1,
- 'minor': 7,
+ 'minor': 8,
'patch': 0,
],
]
diff --git a/permissionse/libs/hidden-apis-stub/build.gradle b/permissionse/libs/hidden-apis-stub/build.gradle
index b239e6f..2043edc 100644
--- a/permissionse/libs/hidden-apis-stub/build.gradle
+++ b/permissionse/libs/hidden-apis-stub/build.gradle
@@ -20,7 +20,7 @@ plugins {
}
android {
- compileSdk 31
+ compileSdkVersion buildConfig.compileSdk
}
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 1c4f527..b7209ef 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
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 E FOUNDATION
+ * Copyright (C) 2022 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,16 +18,20 @@
package android.content.pm;
import android.annotation.TargetApi;
+import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import androidx.annotation.DeprecatedSinceApi;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresPermission;
+import java.util.List;
+
// 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 = 33,
@@ -51,4 +55,23 @@ public abstract class PackageManager {
@NonNull String permissionName,
@NonNull UserHandle user
);
+
+ @TargetApi(29)
+ @DeprecatedSinceApi(
+ api = 33,
+ message = "Check disponibility in SDK33"
+ )
+ @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL")
+ public abstract List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId);
+
+ // Public
+ public abstract List<PackageInfo> getInstalledPackages(int flags);
+
+ @NonNull
+ public abstract Drawable getUserBadgedIcon(
+ @NonNull Drawable drawable,
+ @NonNull UserHandle user
+ );
+
+ public static final int GET_PERMISSIONS = 0x00001000;
}
diff --git a/permissionse/libs/hidden-apis-stub/src/main/java/android/content/pm/UserInfo.java b/permissionse/libs/hidden-apis-stub/src/main/java/android/content/pm/UserInfo.java
new file mode 100644
index 0000000..9418197
--- /dev/null
+++ b/permissionse/libs/hidden-apis-stub/src/main/java/android/content/pm/UserInfo.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 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
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package android.content.pm;
+
+import android.annotation.TargetApi;
+import android.os.UserHandle;
+
+import androidx.annotation.DeprecatedSinceApi;
+
+public class UserInfo {
+ public int id;
+
+ @TargetApi(29)
+ @DeprecatedSinceApi(
+ api = 33,
+ message = "Check availability in SDK33"
+ )
+ public UserHandle getUserHandle() {
+ return null;
+ }
+}
diff --git a/permissionse/libs/hidden-apis-stub/src/main/java/android/os/UserManager.java b/permissionse/libs/hidden-apis-stub/src/main/java/android/os/UserManager.java
new file mode 100644
index 0000000..d2e80d4
--- /dev/null
+++ b/permissionse/libs/hidden-apis-stub/src/main/java/android/os/UserManager.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 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
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package android.os;
+
+import android.annotation.TargetApi;
+import android.content.pm.UserInfo;
+
+import androidx.annotation.DeprecatedSinceApi;
+import androidx.annotation.RequiresPermission;
+import java.util.List;
+
+public class UserManager {
+
+ @TargetApi(29)
+ @DeprecatedSinceApi(
+ api = 33,
+ message = "Check availability in SDK33"
+ )
+ @RequiresPermission("android.permission.MANAGE_USERS")
+ public List<UserInfo> getProfiles(int userHandle) {
+ return null;
+ }
+
+ @TargetApi(29)
+ @DeprecatedSinceApi(
+ api = 33,
+ message = "Check availability in SDK33"
+ )
+ @RequiresPermission("android.permission.MANAGE_USERS")
+ public boolean isManagedProfile(int userId) {
+ return false;
+ }
+}
diff --git a/permissionse/src/main/AndroidManifest.xml b/permissionse/src/main/AndroidManifest.xml
index 428a612..3625087 100644
--- a/permissionse/src/main/AndroidManifest.xml
+++ b/permissionse/src/main/AndroidManifest.xml
@@ -35,4 +35,8 @@
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.CONTROL_ALWAYS_ON_VPN"
tools:ignore="ProtectedPermissions" />
+ <uses-permission android:name="android.permission.MANAGE_USERS"
+ tools:ignore="ProtectedPermissions" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
+ tools:ignore="ProtectedPermissions" />
</manifest>
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 c07f367..c2e3e2c 100644
--- a/permissionse/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt
+++ b/permissionse/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 E FOUNDATION
+ * Copyright (C) 2021 E FOUNDATION, 2022 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
@@ -23,12 +23,16 @@ import android.app.AppOpsManager.OP_NONE
import android.app.AppOpsManager.strOpToOp
import android.app.NotificationChannel
import android.content.Context
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+import android.content.pm.UserInfo
import android.net.IConnectivityManager
import android.net.VpnManager
import android.net.VpnManager.TYPE_VPN_SERVICE
import android.os.Build
import android.os.ServiceManager
import android.os.UserHandle
+import android.os.UserManager
import android.util.Log
import foundation.e.privacymodules.permissions.data.AppOpModes
import foundation.e.privacymodules.permissions.data.ApplicationDescription
@@ -157,6 +161,45 @@ 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<ApplicationDescription> {
+ 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<ApplicationDescription> {
+ 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/permissionsstandalone/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt b/permissionsstandalone/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt
index 52dfd08..da7c73e 100644
--- a/permissionsstandalone/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt
+++ b/permissionsstandalone/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 E FOUNDATION
+ * Copyright (C) 2022 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
@@ -19,6 +19,8 @@ package foundation.e.privacymodules.permissions
import android.app.NotificationChannel
import android.content.Context
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
import foundation.e.privacymodules.permissions.data.AppOpModes
import foundation.e.privacymodules.permissions.data.ApplicationDescription
@@ -26,6 +28,22 @@ import foundation.e.privacymodules.permissions.data.ApplicationDescription
* Implements [IPermissionsPrivacyModule] using only API authorized on the PlayStore.
*/
class PermissionsPrivacyModule(context: Context) : APermissionsPrivacyModule(context) {
+ override fun getApplications(
+ filter: ((PackageInfo) -> Boolean)?,
+ withIcon: Boolean
+ ): List<ApplicationDescription> {
+ 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<ApplicationDescription> {
+ return emptyList()
+ }
+
/**
* @see IPermissionsPrivacyModule.toggleDangerousPermission
* Return an ManualAction to go toggle manually the permission in the ap page of the settings.
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 9d7e675..d0e2e75 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
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 E FOUNDATION
+ * Copyright (C) 2021 E FOUNDATION, 2022 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
@@ -150,7 +150,7 @@ abstract class APermissionsPrivacyModule(protected val context: Context) : IPerm
packageName = appInfo.packageName,
uid = appInfo.uid,
label = getAppLabel(appInfo),
- icon = if (withIcon) getApplicationIcon(appInfo.packageName) else null
+ icon = if (withIcon) getApplicationIcon(appInfo) else null
)
}
@@ -158,6 +158,10 @@ abstract class APermissionsPrivacyModule(protected val context: Context) : IPerm
return context.packageManager.getApplicationLabel(appInfo)
}
+ private fun getApplicationIcon(appInfo: ApplicationInfo): Drawable? {
+ return context.packageManager.getApplicationIcon(appInfo)
+ }
+
override fun getApplicationIcon(packageName: String): Drawable? {
return context.packageManager.getApplicationIcon(packageName)
}
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 ff0b3d7..b64762f 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
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 E FOUNDATION
+ * Copyright (C) 2021 E FOUNDATION, 2022 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
@@ -19,6 +19,7 @@ package foundation.e.privacymodules.permissions
import android.app.NotificationChannel
import android.content.pm.ApplicationInfo
+import android.content.pm.PackageInfo
import android.graphics.drawable.Drawable
import foundation.e.privacymodules.permissions.data.AppOpModes
import foundation.e.privacymodules.permissions.data.ApplicationDescription
@@ -34,6 +35,16 @@ interface IPermissionsPrivacyModule {
withIcon: Boolean = true
): ApplicationDescription
+ fun getApplications(
+ filter: ((PackageInfo) -> Boolean)?,
+ withIcon: Boolean
+ ): List<ApplicationDescription>
+
+ fun getWorkProfileApplications(
+ filter: ((PackageInfo) -> Boolean)?,
+ withIcon: Boolean
+ ): List<ApplicationDescription>
+
/**
* List the installed application on the device which have not the FLAGS_SYSTEM.
* @return list of filled up [ApplicationDescription]