From 53f4a9ce311d612d43fa770cf7e8f8e98fbb43a0 Mon Sep 17 00:00:00 2001
From: Guillaume Jacquart <guillaume.jacquart@hoodbrains.com>
Date: Tue, 12 Sep 2023 06:17:39 +0000
Subject: 2: organise module with clean archi, use Koin for injection.

---
 .../permissions/PermissionsPrivacyModule.kt        | 258 +++++++++++++++++++++
 1 file changed, 258 insertions(+)
 create mode 100644 permissionse/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModule.kt

(limited to 'permissionse/src/main/java/foundation/e/advancedprivacy')

diff --git a/permissionse/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModule.kt b/permissionse/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModule.kt
new file mode 100644
index 0000000..59a20dd
--- /dev/null
+++ b/permissionse/src/main/java/foundation/e/advancedprivacy/externalinterfaces/permissions/PermissionsPrivacyModule.kt
@@ -0,0 +1,258 @@
+/*
+ * 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
+ * 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 foundation.e.advancedprivacy.permissions.externalinterfaces
+
+import android.annotation.TargetApi
+import android.app.AppOpsManager
+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.graphics.drawable.Drawable
+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.advancedprivacy.domain.entities.AppOpModes
+import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
+import foundation.e.advancedprivacy.domain.entities.ProfileType.MAIN
+import foundation.e.advancedprivacy.domain.entities.ProfileType.WORK
+import foundation.e.advancedprivacy.externalinterfaces.permissions.APermissionsPrivacyModule
+
+/**
+ * Implements [IPermissionsPrivacyModule] with all privileges of a system app.
+ */
+class PermissionsPrivacyModule(context: Context) : APermissionsPrivacyModule(context) {
+
+    private val appOpsManager: AppOpsManager
+        get() = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
+
+    /**
+     * @see IPermissionsPrivacyModule.toggleDangerousPermission
+     * Always return true, permission is set using privileged capacities.
+     */
+    override fun toggleDangerousPermission(
+        appDesc: ApplicationDescription,
+        permissionName: String,
+        grant: Boolean
+    ): Boolean {
+        try {
+            if (grant) {
+                context.packageManager.grantRuntimePermission(
+                    appDesc.packageName,
+                    permissionName,
+                    android.os.Process.myUserHandle()
+                )
+            } else {
+                context.packageManager.revokeRuntimePermission(
+                    appDesc.packageName,
+                    permissionName,
+                    android.os.Process.myUserHandle()
+                )
+            }
+        } catch (e: Exception) {
+            Log.e("Permissions-e", "Exception while setting permission", e)
+            return false
+        }
+
+        return true
+    }
+
+    override fun setAppOpMode(
+        appDesc: ApplicationDescription,
+        appOpPermissionName: String,
+        status: AppOpModes
+    ): Boolean {
+        val op = strOpToOp(appOpPermissionName)
+        if (op != OP_NONE) {
+            appOpsManager.setMode(op, appDesc.uid, appDesc.packageName, status.modeValue)
+        }
+        return true
+    }
+
+    override fun getApplications(
+        filter: ((PackageInfo) -> Boolean)?
+    ): List<ApplicationDescription> {
+        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 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 setBlockable(notificationChannel: NotificationChannel) {
+        when (Build.VERSION.SDK_INT) {
+            29 -> notificationChannel.setBlockableSystem(true)
+            30, 31, 32, 33 -> notificationChannel.setBlockable(true)
+            else -> {
+                Log.e("Permissions-e", "Bad android sdk version")
+            }
+        }
+    }
+
+    override fun setVpnPackageAuthorization(packageName: String): Boolean {
+        return when (Build.VERSION.SDK_INT) {
+            29 -> setVpnPackageAuthorizationSDK29(packageName)
+            30 -> setVpnPackageAuthorizationSDK30(packageName)
+            31, 32, 33 -> setVpnPackageAuthorizationSDK32(packageName)
+            else -> {
+                Log.e("Permissions-e", "Bad android sdk version")
+                false
+            }
+        }
+    }
+
+    @TargetApi(29)
+    private fun setVpnPackageAuthorizationSDK29(packageName: String): Boolean {
+        val service: IConnectivityManager = IConnectivityManager.Stub.asInterface(
+            ServiceManager.getService(Context.CONNECTIVITY_SERVICE)
+        )
+
+        try {
+            if (service.prepareVpn(null, packageName, UserHandle.myUserId())) {
+                // Authorize this app to initiate VPN connections in the future without user
+                // intervention.
+                service.setVpnPackageAuthorization(packageName, UserHandle.myUserId(), true)
+                return true
+            }
+        } catch (e: java.lang.Exception) {
+            Log.e("Permissions-e", "Exception while setting VpnPackageAuthorization", e)
+        } catch (e: NoSuchMethodError) {
+            Log.e("Permissions-e", "Bad android sdk version", e)
+        }
+        return false
+    }
+
+    @TargetApi(30)
+    private fun setVpnPackageAuthorizationSDK30(packageName: String): Boolean {
+        val service: IConnectivityManager = IConnectivityManager.Stub.asInterface(
+            ServiceManager.getService(Context.CONNECTIVITY_SERVICE)
+        )
+
+        try {
+            if (service.prepareVpn(null, packageName, UserHandle.myUserId())) {
+                // Authorize this app to initiate VPN connections in the future without user
+                // intervention.
+                service.setVpnPackageAuthorization(packageName, UserHandle.myUserId(), TYPE_VPN_SERVICE)
+                return true
+            }
+        } catch (e: java.lang.Exception) {
+            Log.e("Permissions-e", "Exception while setting VpnPackageAuthorization", e)
+        } catch (e: NoSuchMethodError) {
+            Log.e("Permissions-e", "Bad android sdk version", e)
+        }
+        return false
+    }
+
+    @TargetApi(31)
+    private fun setVpnPackageAuthorizationSDK32(packageName: String): Boolean {
+        val vpnManager = context.getSystemService(Context.VPN_MANAGEMENT_SERVICE) as VpnManager
+
+        try {
+            if (vpnManager.prepareVpn(null, packageName, UserHandle.myUserId())) {
+                // Authorize this app to initiate VPN connections in the future without user
+                // intervention.
+                vpnManager.setVpnPackageAuthorization(packageName, UserHandle.myUserId(), TYPE_VPN_SERVICE)
+                return true
+            }
+        } catch (e: java.lang.Exception) {
+            Log.e("Permissions-e", "Exception while setting VpnPackageAuthorization", e)
+        } catch (e: NoSuchMethodError) {
+            Log.e("Permissions-e", "Bad android sdk version", e)
+        }
+        return false
+    }
+
+    override fun getAlwaysOnVpnPackage(): String? {
+        return when (Build.VERSION.SDK_INT) {
+            29, 30 -> getAlwaysOnVpnPackageSDK29()
+            31, 32, 33 -> getAlwaysOnVpnPackageSDK32()
+            else -> {
+                Log.e("Permissions-e", "Bad android sdk version")
+                null
+            }
+        }
+    }
+
+    @TargetApi(29)
+    private fun getAlwaysOnVpnPackageSDK29(): String? {
+        val service: IConnectivityManager = IConnectivityManager.Stub.asInterface(
+            ServiceManager.getService(Context.CONNECTIVITY_SERVICE)
+        )
+
+        return try {
+            service.getAlwaysOnVpnPackage(UserHandle.myUserId())
+        } catch (e: java.lang.Exception) {
+            Log.e("Permissions-e", "Bad android sdk version ", e)
+            return null
+        }
+    }
+
+    @TargetApi(31)
+    private fun getAlwaysOnVpnPackageSDK32(): String? {
+        val vpnManager = context.getSystemService(Context.VPN_MANAGEMENT_SERVICE) as VpnManager
+        return try {
+            vpnManager.getAlwaysOnVpnPackageForUser(UserHandle.myUserId())
+        } catch (e: java.lang.Exception) {
+            Log.e("Permissions-e", "Bad android sdk version ", e)
+            return null
+        }
+    }
+
+    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
+    }
+}
-- 
cgit v1.2.1