Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9c043fed authored by Satoshi Niwa's avatar Satoshi Niwa
Browse files

PermissionService: Use user-specific context for PermissionControllerManager

PermissionService was using a single, system-wide instance of PermissionControllerManager, which was created with the context of the system user (user 0).

This caused EACCES (Permission denied) errors during backup and restore operations on multi-user devices, particularly HSUM.
The PermissionController service, running as the system user, was incorrectly trying to access files in the target user's device-encrypted storage (e.g., /data/user_de/10/...).

This change fixes the issue by maintaining a cache of PermissionControllerManager instances, one for each user.

Bug: 415449393
Test: atest android.backup.cts.PermissionTest
Flag: EXEMPT bug fix
Change-Id: If1f83bc5c18a886fdac8f8d5fb450afef1020e86
parent 58668a9a
Loading
Loading
Loading
Loading
+16 −7
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ import android.util.DebugUtils
import android.util.IndentingPrintWriter
import android.util.IntArray as GrowingIntArray
import android.util.Slog
import android.util.SparseArray
import android.util.SparseBooleanArray
import com.android.internal.annotations.GuardedBy
import com.android.internal.compat.IPlatformCompat
@@ -137,7 +138,10 @@ class PermissionService(private val service: AccessCheckingService) :

    private var virtualDeviceManagerInternal: VirtualDeviceManagerInternal? = null

    private lateinit var permissionControllerManager: PermissionControllerManager
    /**
     * Cache of PermissionControllerManager instances, keyed by user ID.
     */
    private val permissionControllerManagers = SparseArray<PermissionControllerManager>()

    /**
     * A permission backup might contain apps that are not installed. In this case we delay the
@@ -2040,7 +2044,7 @@ class PermissionService(private val service: AccessCheckingService) :
    override fun backupRuntimePermissions(userId: Int): ByteArray? {
        Preconditions.checkArgumentNonnegative(userId, "userId cannot be null")
        val backup = CompletableFuture<ByteArray>()
        permissionControllerManager.getRuntimePermissionBackup(
        getPermissionControllerManager(userId).getRuntimePermissionBackup(
            UserHandle.of(userId),
            PermissionThread.getExecutor(),
            backup::complete,
@@ -2068,7 +2072,7 @@ class PermissionService(private val service: AccessCheckingService) :
        synchronized(isDelayedPermissionBackupFinished) {
            isDelayedPermissionBackupFinished -= userId
        }
        permissionControllerManager.stageAndApplyRuntimePermissionsBackup(
        getPermissionControllerManager(userId).stageAndApplyRuntimePermissionsBackup(
            backup,
            UserHandle.of(userId),
        )
@@ -2083,7 +2087,7 @@ class PermissionService(private val service: AccessCheckingService) :
                return
            }
        }
        permissionControllerManager.applyStagedRuntimePermissionBackup(
        getPermissionControllerManager(userId).applyStagedRuntimePermissionBackup(
            packageName,
            UserHandle.of(userId),
            PermissionThread.getExecutor(),
@@ -2097,6 +2101,14 @@ class PermissionService(private val service: AccessCheckingService) :
        }
    }

    private fun getPermissionControllerManager(userId: Int): PermissionControllerManager =
        synchronized(permissionControllerManagers) {
            permissionControllerManagers.getOrPut(userId) {
                val userContext = context.createContextAsUser(UserHandle.of(userId), 0)
                PermissionControllerManager(userContext, PermissionThread.getHandler())
            }
        }

    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>?) {
        if (!DumpUtils.checkDumpPermission(context, LOG_TAG, pw)) {
            return
@@ -2386,9 +2398,6 @@ class PermissionService(private val service: AccessCheckingService) :
        virtualDeviceManagerInternal?.registerPersistentDeviceIdRemovedListener { deviceId ->
            service.mutateState { with(devicePolicy) { onDeviceIdRemoved(deviceId) } }
        }

        permissionControllerManager =
            PermissionControllerManager(context, PermissionThread.getHandler())
    }

    override fun onUserCreated(userId: Int) {