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

Commit 14417f4b authored by Manjeet Rulhania's avatar Manjeet Rulhania
Browse files

Permission compatibility layer for new subsystem

Bug: 252884423
Test: presubmit
Change-Id: I9718cf13bec4fb4e5d3b39bddafe69e89310e2e1
parent fa5de876
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -148,6 +148,13 @@ inline fun <K, V> IndexedMap<K, V>.retainAllIndexed(predicate: (Int, K, V) -> Bo
    return isChanged
}

inline fun <K, V, R> IndexedMap<K, V>.mapNotNullIndexed(transform: (K, V) -> R?): IndexedList<R> =
    IndexedList<R>().also { destination ->
        forEachIndexed { _, key, value ->
            transform(key, value)?.let { destination += it }
        }
    }

@Suppress("NOTHING_TO_INLINE")
inline operator fun <K, V> IndexedMap<K, V>.set(key: K, value: V) {
    put(key, value)
+9 −0
Original line number Diff line number Diff line
@@ -31,6 +31,9 @@ data class Permission(
    inline val packageName: String
        get() = permissionInfo.packageName

    inline val groupName: String?
        get() = permissionInfo.group

    inline val isDynamic: Boolean
        get() = type == TYPE_DYNAMIC

@@ -40,6 +43,9 @@ data class Permission(
    inline val isRuntime: Boolean
        get() = permissionInfo.protection == PermissionInfo.PROTECTION_DANGEROUS

    inline val isAppOp: Boolean
        get() = permissionInfo.protection == PermissionInfo.PROTECTION_FLAG_APPOP

    inline val isSoftRestricted: Boolean
        get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.FLAG_SOFT_RESTRICTED)

@@ -109,6 +115,9 @@ data class Permission(
    inline val isKnownSigner: Boolean
        get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_KNOWN_SIGNER)

    inline val hasGids: Boolean
        get() = throw NotImplementedError()

    inline val protectionLevel: Int
        @Suppress("DEPRECATION")
        get() = permissionInfo.protectionLevel
+363 −14
Original line number Diff line number Diff line
@@ -16,6 +16,11 @@

package com.android.server.permission.access.permission

import android.Manifest
import android.app.ActivityManager
import android.compat.annotation.ChangeId
import android.compat.annotation.EnabledAfter
import android.content.Context
import android.content.pm.PackageManager
import android.content.pm.PackageManagerInternal
import android.content.pm.PermissionGroupInfo
@@ -23,17 +28,32 @@ import android.content.pm.PermissionInfo
import android.content.pm.permission.SplitPermissionInfoParcelable
import android.os.Binder
import android.os.Build
import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
import android.os.Message
import android.os.Process
import android.os.RemoteCallbackList
import android.os.RemoteException
import android.os.ServiceManager
import android.os.UserHandle
import android.permission.IOnPermissionsChangeListener
import android.permission.PermissionManager
import android.provider.Settings
import android.util.Log
import com.android.internal.compat.IPlatformCompat
import com.android.server.FgThread
import com.android.server.LocalManagerRegistry
import com.android.server.LocalServices
import com.android.server.ServiceThread
import com.android.server.SystemConfig
import com.android.server.pm.PackageManagerLocal
import com.android.server.pm.permission.PermissionManagerServiceInterface
import com.android.server.permission.access.AccessCheckingService
import com.android.server.permission.access.PermissionUri
import com.android.server.permission.access.UidUri
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
import com.android.server.permission.access.util.hasAnyBit
import com.android.server.permission.access.util.hasBits
import com.android.server.pm.UserManagerService
import com.android.server.pm.permission.LegacyPermission
@@ -53,21 +73,56 @@ class PermissionService(
    private val policy =
        service.getSchemePolicy(UidUri.SCHEME, PermissionUri.SCHEME) as UidPermissionPolicy

    private val context = service.context
    private lateinit var packageManagerInternal: PackageManagerInternal
    private lateinit var packageManagerLocal: PackageManagerLocal
    private lateinit var platformCompat: IPlatformCompat
    private lateinit var userManagerService: UserManagerService

    private val mountedStorageVolumes = IndexedSet<String?>()

    private lateinit var handlerThread: HandlerThread
    private lateinit var handler: Handler

    private lateinit var onPermissionsChangeListeners: OnPermissionsChangeListeners
    private lateinit var permissionFlagsListener: OnPermissionFlagsChangedListener

    fun initialize() {
        packageManagerInternal = LocalServices.getService(PackageManagerInternal::class.java)
        packageManagerLocal =
            LocalManagerRegistry.getManagerOrThrow(PackageManagerLocal::class.java)
        platformCompat = IPlatformCompat.Stub.asInterface(
            ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)
        )
        userManagerService = UserManagerService.getInstance()

        handlerThread = ServiceThread(LOG_TAG, Process.THREAD_PRIORITY_BACKGROUND, true)
        handler = Handler(handlerThread.looper)

        onPermissionsChangeListeners = OnPermissionsChangeListeners(FgThread.get().looper)
        permissionFlagsListener = OnPermissionFlagsChangedListener()
        policy.addOnPermissionFlagsChangedListener(permissionFlagsListener)
    }

    override fun getAllPermissionGroups(flags: Int): List<PermissionGroupInfo> {
        TODO("Not yet implemented")
        val callingUid = Binder.getCallingUid()
        packageManagerLocal.withUnfilteredSnapshot().use { snapshot ->
            if (snapshot.isUidInstantApp(callingUid)) {
                return emptyList()
            }

            val permissionGroups = service.getState {
                with(policy) { getPermissionGroups() }
            }

            return permissionGroups.mapNotNullIndexed { _, permissionGroup ->
                if (snapshot.isPackageVisibleToUid(permissionGroup.packageName, callingUid)) {
                    permissionGroup.generatePermissionGroupInfo(flags)
                } else {
                    null
                }
            }
        }
    }

    override fun getPermissionGroupInfo(
@@ -82,7 +137,7 @@ class PermissionService(
            }

            permissionGroup = service.getState {
                with(policy) { getPermissionGroup(permissionGroupName) }
                with(policy) { getPermissionGroups()[permissionGroupName] }
            } ?: return null

            val isPermissionGroupVisible =
@@ -120,7 +175,7 @@ class PermissionService(
            }

            permission = service.getState {
                with(policy) { getPermission(permissionName) }
                with(policy) { getPermissions()[permissionName] }
            } ?: return null

            val isPermissionVisible =
@@ -148,7 +203,7 @@ class PermissionService(
     */
    private fun Permission.generatePermissionInfo(
        flags: Int,
        targetSdkVersion: Int
        targetSdkVersion: Int = Build.VERSION_CODES.CUR_DEVELOPMENT
    ): PermissionInfo =
        @Suppress("DEPRECATION")
        PermissionInfo(permissionInfo).apply {
@@ -165,10 +220,40 @@ class PermissionService(
        }

    override fun queryPermissionsByGroup(
        permissionGroupName: String,
        permissionGroupName: String?,
        flags: Int
    ): List<PermissionInfo> {
        TODO("Not yet implemented")
    ): List<PermissionInfo>? {
        val callingUid = Binder.getCallingUid()
        packageManagerLocal.withUnfilteredSnapshot().use { snapshot ->
            if (snapshot.isUidInstantApp(callingUid)) {
                return null
            }

            if (permissionGroupName != null) {
                val permissionGroup = service.getState {
                    with(policy) { getPermissionGroups()[permissionGroupName] }
                } ?: return null

                if (!snapshot.isPackageVisibleToUid(
                        permissionGroup.packageName, callingUid
                    )) {
                    return null
                }
            }

            val permissions = service.getState {
                with(policy) { getPermissions() }
            }

            return permissions.mapNotNullIndexed { _, permission ->
                if (permission.groupName == permissionGroupName &&
                    snapshot.isPackageVisibleToUid(permission.packageName, callingUid)) {
                    permission.generatePermissionInfo(flags)
                } else {
                    null
                }
            }
        }
    }

    override fun getAllPermissionsWithProtection(protection: Int): List<PermissionInfo> {
@@ -224,11 +309,11 @@ class PermissionService(
    }

    override fun addOnPermissionsChangeListener(listener: IOnPermissionsChangeListener) {
        TODO("Not yet implemented")
        onPermissionsChangeListeners.addListener(listener)
    }

    override fun removeOnPermissionsChangeListener(listener: IOnPermissionsChangeListener) {
        TODO("Not yet implemented")
        onPermissionsChangeListeners.removeListener(listener)
    }

    override fun getPermissionFlags(packageName: String, permissionName: String, userId: Int): Int {
@@ -245,16 +330,49 @@ class PermissionService(
        permissionName: String,
        userId: Int
    ): Boolean {
        TODO("Not yet implemented")
        if (UserHandle.getCallingUserId() != userId) {
            context.enforceCallingPermission(
                Manifest.permission.INTERACT_ACROSS_USERS_FULL,
                "isPermissionRevokedByPolicy for user $userId"
            )
        }

        if (checkPermission(packageName, permissionName, userId) ==
            PackageManager.PERMISSION_GRANTED) {
            return false
        }

        val callingUid = Binder.getCallingUid()
        if (packageManagerLocal.withUnfilteredSnapshot()
            .use { !it.isPackageVisibleToUid(packageName, userId, callingUid) }) {
            return false
        }

        val permissionFlags = getPermissionFlagsUnchecked(packageName,
            permissionName, callingUid, userId)
        return permissionFlags.hasBits(PackageManager.FLAG_PERMISSION_POLICY_FIXED)
    }

    private fun getPermissionFlagsUnchecked(
        packageName: String,
        permName: String,
        callingUid: Int,
        userId: Int
    ): Int {
        throw NotImplementedError()
    }

    override fun isPermissionsReviewRequired(packageName: String, userId: Int): Boolean {
        requireNotNull(packageName) { "packageName" }
        // TODO(b/173235285): Some caller may pass USER_ALL as userId.
        // Preconditions.checkArgumentNonnegative(userId, "userId");
        val packageState = packageManagerLocal.withUnfilteredSnapshot()
            .use { it.packageStates[packageName] } ?: return false
        val permissionFlags = service.getState {
            with(policy) { getUidPermissionFlags(packageState.appId, userId) }
        } ?: return false
        return permissionFlags.anyIndexed { _, _, flags -> PermissionFlags.isReviewRequired(flags) }
        return permissionFlags.anyIndexed { _, _, flags -> PermissionFlags.isReviewRequired(flags)
        }
    }

    override fun shouldShowRequestPermissionRationale(
@@ -262,7 +380,70 @@ class PermissionService(
        permissionName: String,
        userId: Int
    ): Boolean {
        TODO("Not yet implemented")
        val callingUid = Binder.getCallingUid()
        if (UserHandle.getCallingUserId() != userId) {
            context.enforceCallingPermission(
                Manifest.permission.INTERACT_ACROSS_USERS_FULL,
                "canShowRequestPermissionRationale for user $userId"
            )
        }

        val appId = packageManagerLocal.withUnfilteredSnapshot().use { snapshot ->
            snapshot.packageStates[packageName]?.appId ?: return false
        }
        if (UserHandle.getAppId(callingUid) != appId) {
            return false
        }

        if (checkPermission(packageName, permissionName, userId) ==
            PackageManager.PERMISSION_GRANTED) {
            return false
        }

        val identity = Binder.clearCallingIdentity()
        val permissionFlags = try {
            getPermissionFlagsInternal(packageName, permissionName, callingUid, userId)
        } finally {
            Binder.restoreCallingIdentity(identity)
        }

        val fixedFlags = (PermissionFlags.SYSTEM_FIXED or PermissionFlags.POLICY_FIXED
            or PermissionFlags.USER_FIXED)

        if (permissionFlags.hasAnyBit(fixedFlags) ||
            permissionFlags.hasBits(PermissionFlags.RESTRICTION_REVOKED)) {
            return false
        }

        val token = Binder.clearCallingIdentity()
        try {
            if (permissionName == Manifest.permission.ACCESS_BACKGROUND_LOCATION &&
                platformCompat.isChangeEnabledByPackageName(
                    BACKGROUND_RATIONALE_CHANGE_ID, packageName, userId)
            ) {
                return true
            }
        } catch (e: RemoteException) {
            Log.e(LOG_TAG, "Unable to check if compatibility change is enabled.", e)
        } finally {
            Binder.restoreCallingIdentity(token)
        }

        return permissionFlags and PackageManager.FLAG_PERMISSION_USER_SET != 0
    }

    /**
     * read internal permission flags
     * @return internal permission Flags
     * @see PermissionFlags
     */
    private fun getPermissionFlagsInternal(
        packageName: String,
        permName: String,
        callingUid: Int,
        userId: Int
    ): Int {
        throw NotImplementedError()
    }

    override fun updatePermissionFlags(
@@ -327,7 +508,9 @@ class PermissionService(
    }

    override fun getSplitPermissions(): List<SplitPermissionInfoParcelable> {
        TODO("Not yet implemented")
        return PermissionManager.splitPermissionInfoListToParcelableList(
            SystemConfig.getInstance().splitPermissions
        )
    }

    override fun getAppOpPermissionPackages(permissionName: String): Array<String> {
@@ -335,7 +518,22 @@ class PermissionService(
    }

    override fun getAllAppOpPermissionPackages(): Map<String, Set<String>> {
        TODO("Not yet implemented")
        val appOpPermissionPackageNames = IndexedMap<String, IndexedSet<String>>()
        val permissions = service.getState { with(policy) { getPermissions() } }
        packageManagerLocal.withUnfilteredSnapshot().use { snapshot ->
            snapshot.packageStates.forEach packageStates@{ (_, packageState) ->
                val androidPackage = packageState.androidPackage ?: return@packageStates
                androidPackage.requestedPermissions.forEach requestedPermissions@{ permissionName ->
                    val permission = permissions[permissionName] ?: return@requestedPermissions
                    if (permission.isAppOp) {
                        val packageNames = appOpPermissionPackageNames
                            .getOrPut(permissionName) { IndexedSet() }
                        packageNames += androidPackage.packageName
                    }
                }
            }
        }
        return appOpPermissionPackageNames
    }

    override fun getGidsForUid(uid: Int): IntArray {
@@ -468,6 +666,28 @@ class PermissionService(
        }
    }


    /**
     * This method should typically only be used when granting or revoking permissions, since the
     * app may immediately restart after this call.
     *
     * If you're doing surgery on app code/data, use [PackageFreezer] to guard your work against
     * the app being relaunched.
     */
    private fun killUid(appId: Int, userId: Int, reason: String) {
        val activityManager = ActivityManager.getService()
        if (activityManager != null) {
            val identity = Binder.clearCallingIdentity()
            try {
                activityManager.killUidForPermissionChange(appId, userId, reason)
            } catch (e: RemoteException) {
                /* ignore - same process */
            } finally {
                Binder.restoreCallingIdentity(identity)
            }
        }
    }

    /**
     * Check whether a UID belongs to an instant app.
     */
@@ -504,4 +724,133 @@ class PermissionService(
        val user = UserHandle.of(userId)
        return filtered(uid, user).use { it.getPackageState(packageName) != null }
    }

    /**
     * Callback invoked when interesting actions have been taken on a permission.
     */
    private inner class OnPermissionFlagsChangedListener :
        UidPermissionPolicy.OnPermissionFlagsChangedListener {
        override fun onPermissionFlagsChanged(
            appId: Int,
            userId: Int,
            permissionName: String,
            oldFlags: Int,
            newFlags: Int
        ) {
            val uid = UserHandle.getUid(userId, appId)
            val permission = service.getState {
                with(policy) { getPermissions()[permissionName] }
            } ?: return

            val isPermissionGranted = !PermissionFlags.isPermissionGranted(oldFlags) &&
                PermissionFlags.isPermissionGranted(newFlags)
            val isPermissionRevoked = PermissionFlags.isPermissionGranted(oldFlags) &&
                !PermissionFlags.isPermissionGranted(newFlags)

            if (isPermissionGranted) {
                if (permission.isRuntime) {
                    onPermissionsChangeListeners.onPermissionsChanged(uid)
                }
                handler.post {
                    if (permission.hasGids) {
                        killUid(appId, userId, PermissionManager.KILL_APP_REASON_GIDS_CHANGED)
                    }
                }
            } else if (isPermissionRevoked) {
                // TODO: STOPSHIP skip kill for revokePostNotificationPermissionWithoutKillForTest
                if (permission.isRuntime) {
                    onPermissionsChangeListeners.onPermissionsChanged(uid)
                    handler.post {
                        if (!(permissionName == Manifest.permission.POST_NOTIFICATIONS &&
                            isAppBackupAndRestoreRunning(uid))) {
                            killUid(
                                appId, userId, PermissionManager.KILL_APP_REASON_PERMISSIONS_REVOKED
                            )
                        }
                    }
                }
            } else if (oldFlags != newFlags) {
                onPermissionsChangeListeners.onPermissionsChanged(uid)
            }
        }

        private fun isAppBackupAndRestoreRunning(uid: Int): Boolean {
            if (checkUidPermission(uid, Manifest.permission.BACKUP) !=
                PackageManager.PERMISSION_GRANTED) {
                return false
            }
            return try {
                val userId = UserHandle.getUserId(uid)
                val isInSetup = Settings.Secure.getIntForUser(
                    context.contentResolver, Settings.Secure.USER_SETUP_COMPLETE, userId
                ) == 0
                val isInDeferredSetup = Settings.Secure.getIntForUser(
                    context.contentResolver,
                    Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, userId
                ) == Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED
                isInSetup || isInDeferredSetup
            } catch (e: Settings.SettingNotFoundException) {
                Log.w(LOG_TAG, "Failed to check if the user is in restore: $e")
                false
            }
        }
    }

    private class OnPermissionsChangeListeners(looper: Looper) : Handler(looper) {
        private val listeners = RemoteCallbackList<IOnPermissionsChangeListener>()

        override fun handleMessage(msg: Message) {
            when (msg.what) {
                MSG_ON_PERMISSIONS_CHANGED -> {
                    val uid = msg.arg1
                    handleOnPermissionsChanged(uid)
                }
            }
        }

        private fun handleOnPermissionsChanged(uid: Int) {
            val count = listeners.beginBroadcast()
            try {
                for (i in 0 until count) {
                    val callback = listeners.getBroadcastItem(i)
                    try {
                        callback.onPermissionsChanged(uid)
                    } catch (e: RemoteException) {
                        Log.e(LOG_TAG, "Permission listener is dead", e)
                    }
                }
            } finally {
                listeners.finishBroadcast()
            }
        }

        fun addListener(listener: IOnPermissionsChangeListener) {
            listeners.register(listener)
        }

        fun removeListener(listener: IOnPermissionsChangeListener) {
            listeners.unregister(listener)
        }

        fun onPermissionsChanged(uid: Int) {
            if (listeners.registeredCallbackCount > 0) {
                obtainMessage(MSG_ON_PERMISSIONS_CHANGED, uid, 0).sendToTarget()
            }
        }
        companion object {
            private const val MSG_ON_PERMISSIONS_CHANGED = 1
        }
    }

    companion object {
        private val LOG_TAG = PermissionService::class.java.simpleName

        /**
         * This change makes it so that apps are told to show rationale for asking for background
         * location access every time they request.
         */
        @ChangeId
        @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
        private val BACKGROUND_RATIONALE_CHANGE_ID = 147316723L
    }
}
+10 −4
Original line number Diff line number Diff line
@@ -829,11 +829,17 @@ class UidPermissionPolicy : SchemePolicy() {
        with(persistence) { this@serializeSystemState.serializeSystemState(systemState) }
    }

    fun GetStateScope.getPermissionGroup(permissionGroupName: String): PermissionGroupInfo? =
        state.systemState.permissionGroups[permissionGroupName]
    /**
     * returns all permission group definitions available in the system
     */
    fun GetStateScope.getPermissionGroups(): IndexedMap<String, PermissionGroupInfo> =
        state.systemState.permissionGroups

    fun GetStateScope.getPermission(permissionName: String): Permission? =
        state.systemState.permissions[permissionName]
    /**
     * returns all permission definitions available in the system
     */
    fun GetStateScope.getPermissions(): IndexedMap<String, Permission> =
        state.systemState.permissions

    fun GetStateScope.getUidPermissionFlags(appId: Int, userId: Int): IndexedMap<String, Int>? =
        state.userStates[userId]?.uidPermissionFlags?.get(appId)