Loading services/permission/java/com/android/server/permission/access/AccessState.kt +4 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,8 @@ class SystemState private constructor( val knownPackages: IntMap<IndexedListSet<String>>, // A map of userId to packageName val deviceAndProfileOwners: IntMap<String>, // Whether the device supports leanback UI var isLeanback: Boolean, val privilegedPermissionAllowlistSourcePackageNames: IndexedListSet<String>, var permissionAllowlist: PermissionAllowlist, val implicitToSourcePermissions: Map<String, Set<String>>, Loading @@ -60,6 +62,7 @@ class SystemState private constructor( IntMap(), IntMap(), IntMap(), false, IndexedListSet(), PermissionAllowlist(), IndexedMap(), Loading @@ -76,6 +79,7 @@ class SystemState private constructor( appIds.copy { it.copy() }, knownPackages.copy { it.copy() }, deviceAndProfileOwners.copy { it }, isLeanback, privilegedPermissionAllowlistSourcePackageNames.copy(), permissionAllowlist, implicitToSourcePermissions, Loading services/permission/java/com/android/server/permission/access/permission/Permission.kt +5 −2 Original line number Diff line number Diff line Loading @@ -46,11 +46,14 @@ data class Permission( inline val isAppOp: Boolean get() = permissionInfo.protection == PermissionInfo.PROTECTION_FLAG_APPOP inline val isRemoved: Boolean get() = permissionInfo.flags.hasBits(PermissionInfo.FLAG_REMOVED) inline val isSoftRestricted: Boolean get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.FLAG_SOFT_RESTRICTED) get() = permissionInfo.flags.hasBits(PermissionInfo.FLAG_SOFT_RESTRICTED) inline val isHardRestricted: Boolean get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.FLAG_HARD_RESTRICTED) get() = permissionInfo.flags.hasBits(PermissionInfo.FLAG_HARD_RESTRICTED) inline val isSignature: Boolean get() = permissionInfo.protection == PermissionInfo.PROTECTION_SIGNATURE Loading services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt +5 −0 Original line number Diff line number Diff line Loading @@ -312,6 +312,11 @@ object PermissionFlags { SYSTEM_EXEMPT or UPGRADE_EXEMPT or RESTRICTION_REVOKED or SOFT_RESTRICTED or APP_OP_REVOKED or ONE_TIME or HIBERNATION or USER_SELECTED /** * Mask for all permission flags about permission exemption. */ const val MASK_EXEMPT = INSTALLER_EXEMPT or SYSTEM_EXEMPT or UPGRADE_EXEMPT /** * Mask for all API permission flags about permission restriction. */ Loading services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt +209 −32 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import com.android.server.permission.access.SchemePolicy 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.andInv import com.android.server.permission.access.util.hasAnyBit import com.android.server.permission.access.util.hasBits import com.android.server.pm.KnownPackages import com.android.server.pm.parsing.PackageInfoUtils Loading Loading @@ -76,7 +77,9 @@ class UidPermissionPolicy : SchemePolicy() { override fun MutateStateScope.onUserAdded(userId: Int) { newState.systemState.packageStates.forEach { (_, packageState) -> evaluateAllPermissionStatesForPackageAndUser(packageState, userId, null) grantImplicitPermissions(packageState, userId) } newState.systemState.appIds.forEachKeyIndexed { _, appId -> inheritImplicitPermissionStates(appId, userId) } } Loading @@ -88,6 +91,47 @@ class UidPermissionPolicy : SchemePolicy() { } } override fun MutateStateScope.onStorageVolumeMounted( volumeUuid: String?, isSystemUpdated: Boolean ) { // TODO: STOPSHIP: Either make addPermissionGroups() favor system packages // like addPermissions(), or sort system packages before non-system packages for this loop. val changedPermissionNames = IndexedSet<String>() newState.systemState.packageStates.forEach { (_, packageState) -> val androidPackage = packageState.androidPackage if (androidPackage == null || androidPackage.volumeUuid != volumeUuid) { return@forEach } adoptPermissions(packageState, changedPermissionNames) addPermissionGroups(packageState) addPermissions(packageState, changedPermissionNames) trimPermissions(packageState.packageName, changedPermissionNames) trimPermissionStates(packageState.appId) } changedPermissionNames.forEachIndexed { _, permissionName -> evaluatePermissionStateForAllPackages(permissionName, null) } newState.systemState.packageStates.forEach { (_, packageState) -> val androidPackage = packageState.androidPackage if (androidPackage == null || androidPackage.volumeUuid != volumeUuid) { return@forEach } val installedPackageState = if (isSystemUpdated) packageState else null evaluateAllPermissionStatesForPackage(packageState, installedPackageState) } newState.systemState.packageStates.forEach { (_, packageState) -> val androidPackage = packageState.androidPackage if (androidPackage == null || androidPackage.volumeUuid != volumeUuid) { return@forEach } newState.systemState.userIds.forEachIndexed { _, userId -> inheritImplicitPermissionStates(packageState.appId, userId) } } } override fun MutateStateScope.onPackageAdded(packageState: PackageState) { val changedPermissionNames = IndexedSet<String>() adoptPermissions(packageState, changedPermissionNames) Loading @@ -95,17 +139,21 @@ class UidPermissionPolicy : SchemePolicy() { addPermissions(packageState, changedPermissionNames) // TODO: revokeStoragePermissionsIfScopeExpandedInternal() trimPermissions(packageState.packageName, changedPermissionNames) trimPermissionStates(packageState.appId) changedPermissionNames.forEachIndexed { _, permissionName -> evaluatePermissionStateForAllPackages(permissionName, packageState) evaluatePermissionStateForAllPackages(permissionName, null) } evaluateAllPermissionStatesForPackage(packageState, packageState) newState.systemState.userIds.forEachIndexed { _, userId -> grantImplicitPermissions(packageState, userId) inheritImplicitPermissionStates(packageState.appId, userId) } } // TODO: add trimPermissionStates() here for removing the permission states that are // no longer requested. (equivalent to revokeUnusedSharedUserPermissionsLocked()) override fun MutateStateScope.onPackageRemoved(packageName: String, appId: Int) { // TODO: STOPSHIP: Remove this check or at least turn into logging. check(packageName !in newState.systemState.disabledSystemPackageStates) { "Package $packageName reported as removed before disabled system package is enabled" } } private fun MutateStateScope.adoptPermissions( Loading Loading @@ -361,6 +409,20 @@ class UidPermissionPolicy : SchemePolicy() { } } private fun MutateStateScope.trimPermissionStates(appId: Int) { val requestedPermissions = IndexedSet<String>() forEachPackageInAppId(appId) { requestedPermissions += it.androidPackage!!.requestedPermissions } newState.userStates.forEachIndexed { _, userId, userState -> userState.uidPermissionFlags[appId].forEachReversedIndexed { _, permissionName, _ -> if (permissionName !in requestedPermissions) { setPermissionFlags(appId, userId, permissionName, 0) } } } } private fun MutateStateScope.evaluatePermissionStateForAllPackages( permissionName: String, installedPackageState: PackageState? Loading Loading @@ -415,8 +477,17 @@ class UidPermissionPolicy : SchemePolicy() { // For non-shared-user packages with missing androidPackage, skip evaluation. return } val permission = newState.systemState.permissions[permissionName] ?: return val permission = newState.systemState.permissions[permissionName] val oldFlags = getPermissionFlags(appId, userId, permissionName) if (permission == null) { if (oldFlags == 0) { // If the permission definition is missing and we don't have any permission states // for this permission, add the INSTALL_REVOKED flag to ensure that we don't // automatically grant the permission when it's defined setPermissionFlags(appId, userId, permissionName, PermissionFlags.INSTALL_REVOKED) } return } if (permission.isNormal) { val wasGranted = oldFlags.hasBits(PermissionFlags.INSTALL_GRANTED) if (!wasGranted) { Loading Loading @@ -479,21 +550,98 @@ class UidPermissionPolicy : SchemePolicy() { } setPermissionFlags(appId, userId, permissionName, newFlags) } else if (permission.isRuntime) { // TODO: add runtime permissions var newFlags = oldFlags and PermissionFlags.MASK_RUNTIME if (getAppIdTargetSdkVersion(appId, permissionName) < Build.VERSION_CODES.M) { newFlags = newFlags or PermissionFlags.LEGACY_GRANTED // Explicitly check against the old state to determine if this permission is new. val isNewPermission = getPermissionFlags(appId, userId, permissionName, oldState) == 0 if (isNewPermission) { newFlags = newFlags or PermissionFlags.IMPLICIT } } else { newFlags = newFlags andInv PermissionFlags.LEGACY_GRANTED val wasGrantedByImplicit = newFlags.hasBits(PermissionFlags.IMPLICIT_GRANTED) val isLeanBackNotificationsPermission = newState.systemState.isLeanback && permissionName in NOTIFICATION_PERMISSIONS val isImplicitPermission = anyPackageInAppId(appId) { permissionName in it.androidPackage!!.implicitPermissions } val sourcePermissions = newState.systemState .implicitToSourcePermissions[permissionName] val isAnySourcePermissionNonRuntime = sourcePermissions?.any { val sourcePermission = newState.systemState.permissions[it] checkNotNull(sourcePermission) { "Unknown source permission $it in split permissions" } !sourcePermission.isRuntime } ?: false val shouldGrantByImplicit = isLeanBackNotificationsPermission || (isImplicitPermission && isAnySourcePermissionNonRuntime) if (shouldGrantByImplicit) { newFlags = newFlags or PermissionFlags.IMPLICIT_GRANTED } else { newFlags = newFlags andInv PermissionFlags.IMPLICIT_GRANTED } val hasImplicitFlag = newFlags.hasBits(PermissionFlags.IMPLICIT) if (!isImplicitPermission && hasImplicitFlag) { // TODO: We might not want to remove the IMPLICIT flag // for NOTIFICATION_PERMISSIONS newFlags = newFlags andInv PermissionFlags.IMPLICIT var shouldRetainAsNearbyDevices = false if (permissionName in NEARBY_DEVICES_PERMISSIONS) { val accessBackgroundLocationFlags = getPermissionFlags( appId, userId, Manifest.permission.ACCESS_BACKGROUND_LOCATION ) shouldRetainAsNearbyDevices = PermissionFlags.isAppOpGranted(accessBackgroundLocationFlags) && !accessBackgroundLocationFlags.hasBits(PermissionFlags.IMPLICIT) } // These are the permission flags that imply we shouldn't automatically // modify the permission grant state. val shouldRetainByMask = newFlags.hasAnyBit( PermissionFlags.SYSTEM_FIXED or PermissionFlags.POLICY_FIXED ) if (shouldRetainAsNearbyDevices || shouldRetainByMask) { if (wasGrantedByImplicit) { newFlags = newFlags or PermissionFlags.RUNTIME_GRANTED } } else { newFlags = newFlags andInv ( PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET or PermissionFlags.USER_FIXED ) } } } val isExempt = newFlags.hasAnyBit(PermissionFlags.MASK_EXEMPT) val isHardRestricted = permission.isHardRestricted && !isExempt newFlags = if (isHardRestricted) { newFlags or PermissionFlags.RESTRICTION_REVOKED } else { newFlags andInv PermissionFlags.RESTRICTION_REVOKED } val isSoftRestricted = permission.isSoftRestricted && !isExempt newFlags = if (isSoftRestricted) { newFlags or PermissionFlags.SOFT_RESTRICTED } else { newFlags andInv PermissionFlags.SOFT_RESTRICTED } setPermissionFlags(appId, userId, permissionName, newFlags) } else { Log.e(LOG_TAG, "Unknown protection level ${permission.protectionLevel}" + "for permission ${permission.name} while evaluating permission state" + "for appId $appId and userId $userId") } // TODO: revokePermissionsNoLongerImplicitLocked() for runtime permissions } private fun MutateStateScope.grantImplicitPermissions(packageState: PackageState, userId: Int) { val appId = packageState.appId val androidPackage = packageState.androidPackage ?: return androidPackage.implicitPermissions.forEachIndexed implicitPermissions@ { _, implicitPermissionName -> private fun MutateStateScope.inheritImplicitPermissionStates(appId: Int, userId: Int) { val implicitPermissions = IndexedSet<String>() forEachPackageInAppId(appId) { implicitPermissions += it.androidPackage!!.implicitPermissions } implicitPermissions.forEachIndexed implicitPermissions@ { _, implicitPermissionName -> val implicitPermission = newState.systemState.permissions[implicitPermissionName] checkNotNull(implicitPermission) { "Unknown implicit permission $implicitPermissionName in split permissions" Loading @@ -502,15 +650,14 @@ class UidPermissionPolicy : SchemePolicy() { return@implicitPermissions } // Explicitly check against the old state to determine if this permission is new. val isNewPermission = getPermissionFlags( appId, userId, implicitPermissionName, oldState ) == 0 val isNewPermission = getPermissionFlags(appId, userId, implicitPermissionName, oldState) == 0 if (!isNewPermission) { return@implicitPermissions } val sourcePermissions = newState.systemState .implicitToSourcePermissions[implicitPermissionName] ?: return@implicitPermissions var newFlags = 0 var newFlags = getPermissionFlags(appId, userId, implicitPermissionName) sourcePermissions.forEachIndexed sourcePermissions@ { _, sourcePermissionName -> val sourcePermission = newState.systemState.permissions[sourcePermissionName] checkNotNull(sourcePermission) { Loading @@ -525,12 +672,13 @@ class UidPermissionPolicy : SchemePolicy() { newFlags = 0 } newFlags = newFlags or (sourceFlags and PermissionFlags.MASK_RUNTIME) if (!sourcePermission.isRuntime && isSourceGranted) { newFlags = newFlags or PermissionFlags.IMPLICIT_GRANTED } } } if (implicitPermissionName in RETAIN_IMPLICIT_FLAGS_PERMISSIONS) { newFlags = newFlags andInv PermissionFlags.IMPLICIT } else { newFlags = newFlags or PermissionFlags.IMPLICIT } setPermissionFlags(appId, userId, implicitPermissionName, newFlags) } } Loading Loading @@ -654,6 +802,17 @@ class UidPermissionPolicy : SchemePolicy() { } } private fun MutateStateScope.getAppIdTargetSdkVersion(appId: Int, permissionName: String): Int { var targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT forEachPackageInAppId(appId) { packageState -> val androidPackage = packageState.androidPackage!! if (permissionName in androidPackage.requestedPermissions) { targetSdkVersion = targetSdkVersion.coerceAtMost(androidPackage.targetSdkVersion) } } return targetSdkVersion } private fun MutateStateScope.anyPackageInAppId( appId: Int, state: AccessState = newState, Loading @@ -666,6 +825,20 @@ class UidPermissionPolicy : SchemePolicy() { } } private fun MutateStateScope.forEachPackageInAppId( appId: Int, state: AccessState = newState, action: (PackageState) -> Unit ) { val packageNames = state.systemState.appIds[appId] packageNames.forEachIndexed { _, packageName -> val packageState = state.systemState.packageStates[packageName]!! if (packageState.androidPackage != null) { action(packageState) } } } private fun MutateStateScope.shouldGrantPermissionByProtectionFlags( packageState: PackageState, permission: Permission Loading Loading @@ -813,13 +986,6 @@ class UidPermissionPolicy : SchemePolicy() { return uid == ownerUid } override fun MutateStateScope.onPackageRemoved(packageName: String, appId: Int) { // TODO: STOPSHIP: Remove this check or at least turn into logging. check(packageName !in newState.systemState.disabledSystemPackageStates) { "Package $packageName reported as removed before disabled system package is enabled" } } override fun BinaryXmlPullParser.parseSystemState(state: AccessState) { with(persistence) { this@parseSystemState.parseSystemState(state) } } Loading Loading @@ -928,13 +1094,24 @@ class UidPermissionPolicy : SchemePolicy() { private const val PLATFORM_PACKAGE_NAME = "android" // A set of permissions that we don't want to revoke when they are no longer implicit. private val RETAIN_IMPLICIT_GRANT_PERMISSIONS = indexedSetOf( private val RETAIN_IMPLICIT_FLAGS_PERMISSIONS = indexedSetOf( Manifest.permission.ACCESS_MEDIA_LOCATION, Manifest.permission.ACTIVITY_RECOGNITION, Manifest.permission.READ_MEDIA_AUDIO, Manifest.permission.READ_MEDIA_IMAGES, Manifest.permission.READ_MEDIA_VIDEO, ) // TODO: also add the permission NEARBY_WIFI_DEVICES to this set private val NEARBY_DEVICES_PERMISSIONS = indexedSetOf( Manifest.permission.BLUETOOTH_ADVERTISE, Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.BLUETOOTH_SCAN ) private val NOTIFICATION_PERMISSIONS = indexedSetOf( Manifest.permission.POST_NOTIFICATIONS ) } fun interface OnPermissionFlagsChangedListener { Loading Loading
services/permission/java/com/android/server/permission/access/AccessState.kt +4 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,8 @@ class SystemState private constructor( val knownPackages: IntMap<IndexedListSet<String>>, // A map of userId to packageName val deviceAndProfileOwners: IntMap<String>, // Whether the device supports leanback UI var isLeanback: Boolean, val privilegedPermissionAllowlistSourcePackageNames: IndexedListSet<String>, var permissionAllowlist: PermissionAllowlist, val implicitToSourcePermissions: Map<String, Set<String>>, Loading @@ -60,6 +62,7 @@ class SystemState private constructor( IntMap(), IntMap(), IntMap(), false, IndexedListSet(), PermissionAllowlist(), IndexedMap(), Loading @@ -76,6 +79,7 @@ class SystemState private constructor( appIds.copy { it.copy() }, knownPackages.copy { it.copy() }, deviceAndProfileOwners.copy { it }, isLeanback, privilegedPermissionAllowlistSourcePackageNames.copy(), permissionAllowlist, implicitToSourcePermissions, Loading
services/permission/java/com/android/server/permission/access/permission/Permission.kt +5 −2 Original line number Diff line number Diff line Loading @@ -46,11 +46,14 @@ data class Permission( inline val isAppOp: Boolean get() = permissionInfo.protection == PermissionInfo.PROTECTION_FLAG_APPOP inline val isRemoved: Boolean get() = permissionInfo.flags.hasBits(PermissionInfo.FLAG_REMOVED) inline val isSoftRestricted: Boolean get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.FLAG_SOFT_RESTRICTED) get() = permissionInfo.flags.hasBits(PermissionInfo.FLAG_SOFT_RESTRICTED) inline val isHardRestricted: Boolean get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.FLAG_HARD_RESTRICTED) get() = permissionInfo.flags.hasBits(PermissionInfo.FLAG_HARD_RESTRICTED) inline val isSignature: Boolean get() = permissionInfo.protection == PermissionInfo.PROTECTION_SIGNATURE Loading
services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt +5 −0 Original line number Diff line number Diff line Loading @@ -312,6 +312,11 @@ object PermissionFlags { SYSTEM_EXEMPT or UPGRADE_EXEMPT or RESTRICTION_REVOKED or SOFT_RESTRICTED or APP_OP_REVOKED or ONE_TIME or HIBERNATION or USER_SELECTED /** * Mask for all permission flags about permission exemption. */ const val MASK_EXEMPT = INSTALLER_EXEMPT or SYSTEM_EXEMPT or UPGRADE_EXEMPT /** * Mask for all API permission flags about permission restriction. */ Loading
services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt +209 −32 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import com.android.server.permission.access.SchemePolicy 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.andInv import com.android.server.permission.access.util.hasAnyBit import com.android.server.permission.access.util.hasBits import com.android.server.pm.KnownPackages import com.android.server.pm.parsing.PackageInfoUtils Loading Loading @@ -76,7 +77,9 @@ class UidPermissionPolicy : SchemePolicy() { override fun MutateStateScope.onUserAdded(userId: Int) { newState.systemState.packageStates.forEach { (_, packageState) -> evaluateAllPermissionStatesForPackageAndUser(packageState, userId, null) grantImplicitPermissions(packageState, userId) } newState.systemState.appIds.forEachKeyIndexed { _, appId -> inheritImplicitPermissionStates(appId, userId) } } Loading @@ -88,6 +91,47 @@ class UidPermissionPolicy : SchemePolicy() { } } override fun MutateStateScope.onStorageVolumeMounted( volumeUuid: String?, isSystemUpdated: Boolean ) { // TODO: STOPSHIP: Either make addPermissionGroups() favor system packages // like addPermissions(), or sort system packages before non-system packages for this loop. val changedPermissionNames = IndexedSet<String>() newState.systemState.packageStates.forEach { (_, packageState) -> val androidPackage = packageState.androidPackage if (androidPackage == null || androidPackage.volumeUuid != volumeUuid) { return@forEach } adoptPermissions(packageState, changedPermissionNames) addPermissionGroups(packageState) addPermissions(packageState, changedPermissionNames) trimPermissions(packageState.packageName, changedPermissionNames) trimPermissionStates(packageState.appId) } changedPermissionNames.forEachIndexed { _, permissionName -> evaluatePermissionStateForAllPackages(permissionName, null) } newState.systemState.packageStates.forEach { (_, packageState) -> val androidPackage = packageState.androidPackage if (androidPackage == null || androidPackage.volumeUuid != volumeUuid) { return@forEach } val installedPackageState = if (isSystemUpdated) packageState else null evaluateAllPermissionStatesForPackage(packageState, installedPackageState) } newState.systemState.packageStates.forEach { (_, packageState) -> val androidPackage = packageState.androidPackage if (androidPackage == null || androidPackage.volumeUuid != volumeUuid) { return@forEach } newState.systemState.userIds.forEachIndexed { _, userId -> inheritImplicitPermissionStates(packageState.appId, userId) } } } override fun MutateStateScope.onPackageAdded(packageState: PackageState) { val changedPermissionNames = IndexedSet<String>() adoptPermissions(packageState, changedPermissionNames) Loading @@ -95,17 +139,21 @@ class UidPermissionPolicy : SchemePolicy() { addPermissions(packageState, changedPermissionNames) // TODO: revokeStoragePermissionsIfScopeExpandedInternal() trimPermissions(packageState.packageName, changedPermissionNames) trimPermissionStates(packageState.appId) changedPermissionNames.forEachIndexed { _, permissionName -> evaluatePermissionStateForAllPackages(permissionName, packageState) evaluatePermissionStateForAllPackages(permissionName, null) } evaluateAllPermissionStatesForPackage(packageState, packageState) newState.systemState.userIds.forEachIndexed { _, userId -> grantImplicitPermissions(packageState, userId) inheritImplicitPermissionStates(packageState.appId, userId) } } // TODO: add trimPermissionStates() here for removing the permission states that are // no longer requested. (equivalent to revokeUnusedSharedUserPermissionsLocked()) override fun MutateStateScope.onPackageRemoved(packageName: String, appId: Int) { // TODO: STOPSHIP: Remove this check or at least turn into logging. check(packageName !in newState.systemState.disabledSystemPackageStates) { "Package $packageName reported as removed before disabled system package is enabled" } } private fun MutateStateScope.adoptPermissions( Loading Loading @@ -361,6 +409,20 @@ class UidPermissionPolicy : SchemePolicy() { } } private fun MutateStateScope.trimPermissionStates(appId: Int) { val requestedPermissions = IndexedSet<String>() forEachPackageInAppId(appId) { requestedPermissions += it.androidPackage!!.requestedPermissions } newState.userStates.forEachIndexed { _, userId, userState -> userState.uidPermissionFlags[appId].forEachReversedIndexed { _, permissionName, _ -> if (permissionName !in requestedPermissions) { setPermissionFlags(appId, userId, permissionName, 0) } } } } private fun MutateStateScope.evaluatePermissionStateForAllPackages( permissionName: String, installedPackageState: PackageState? Loading Loading @@ -415,8 +477,17 @@ class UidPermissionPolicy : SchemePolicy() { // For non-shared-user packages with missing androidPackage, skip evaluation. return } val permission = newState.systemState.permissions[permissionName] ?: return val permission = newState.systemState.permissions[permissionName] val oldFlags = getPermissionFlags(appId, userId, permissionName) if (permission == null) { if (oldFlags == 0) { // If the permission definition is missing and we don't have any permission states // for this permission, add the INSTALL_REVOKED flag to ensure that we don't // automatically grant the permission when it's defined setPermissionFlags(appId, userId, permissionName, PermissionFlags.INSTALL_REVOKED) } return } if (permission.isNormal) { val wasGranted = oldFlags.hasBits(PermissionFlags.INSTALL_GRANTED) if (!wasGranted) { Loading Loading @@ -479,21 +550,98 @@ class UidPermissionPolicy : SchemePolicy() { } setPermissionFlags(appId, userId, permissionName, newFlags) } else if (permission.isRuntime) { // TODO: add runtime permissions var newFlags = oldFlags and PermissionFlags.MASK_RUNTIME if (getAppIdTargetSdkVersion(appId, permissionName) < Build.VERSION_CODES.M) { newFlags = newFlags or PermissionFlags.LEGACY_GRANTED // Explicitly check against the old state to determine if this permission is new. val isNewPermission = getPermissionFlags(appId, userId, permissionName, oldState) == 0 if (isNewPermission) { newFlags = newFlags or PermissionFlags.IMPLICIT } } else { newFlags = newFlags andInv PermissionFlags.LEGACY_GRANTED val wasGrantedByImplicit = newFlags.hasBits(PermissionFlags.IMPLICIT_GRANTED) val isLeanBackNotificationsPermission = newState.systemState.isLeanback && permissionName in NOTIFICATION_PERMISSIONS val isImplicitPermission = anyPackageInAppId(appId) { permissionName in it.androidPackage!!.implicitPermissions } val sourcePermissions = newState.systemState .implicitToSourcePermissions[permissionName] val isAnySourcePermissionNonRuntime = sourcePermissions?.any { val sourcePermission = newState.systemState.permissions[it] checkNotNull(sourcePermission) { "Unknown source permission $it in split permissions" } !sourcePermission.isRuntime } ?: false val shouldGrantByImplicit = isLeanBackNotificationsPermission || (isImplicitPermission && isAnySourcePermissionNonRuntime) if (shouldGrantByImplicit) { newFlags = newFlags or PermissionFlags.IMPLICIT_GRANTED } else { newFlags = newFlags andInv PermissionFlags.IMPLICIT_GRANTED } val hasImplicitFlag = newFlags.hasBits(PermissionFlags.IMPLICIT) if (!isImplicitPermission && hasImplicitFlag) { // TODO: We might not want to remove the IMPLICIT flag // for NOTIFICATION_PERMISSIONS newFlags = newFlags andInv PermissionFlags.IMPLICIT var shouldRetainAsNearbyDevices = false if (permissionName in NEARBY_DEVICES_PERMISSIONS) { val accessBackgroundLocationFlags = getPermissionFlags( appId, userId, Manifest.permission.ACCESS_BACKGROUND_LOCATION ) shouldRetainAsNearbyDevices = PermissionFlags.isAppOpGranted(accessBackgroundLocationFlags) && !accessBackgroundLocationFlags.hasBits(PermissionFlags.IMPLICIT) } // These are the permission flags that imply we shouldn't automatically // modify the permission grant state. val shouldRetainByMask = newFlags.hasAnyBit( PermissionFlags.SYSTEM_FIXED or PermissionFlags.POLICY_FIXED ) if (shouldRetainAsNearbyDevices || shouldRetainByMask) { if (wasGrantedByImplicit) { newFlags = newFlags or PermissionFlags.RUNTIME_GRANTED } } else { newFlags = newFlags andInv ( PermissionFlags.RUNTIME_GRANTED or PermissionFlags.USER_SET or PermissionFlags.USER_FIXED ) } } } val isExempt = newFlags.hasAnyBit(PermissionFlags.MASK_EXEMPT) val isHardRestricted = permission.isHardRestricted && !isExempt newFlags = if (isHardRestricted) { newFlags or PermissionFlags.RESTRICTION_REVOKED } else { newFlags andInv PermissionFlags.RESTRICTION_REVOKED } val isSoftRestricted = permission.isSoftRestricted && !isExempt newFlags = if (isSoftRestricted) { newFlags or PermissionFlags.SOFT_RESTRICTED } else { newFlags andInv PermissionFlags.SOFT_RESTRICTED } setPermissionFlags(appId, userId, permissionName, newFlags) } else { Log.e(LOG_TAG, "Unknown protection level ${permission.protectionLevel}" + "for permission ${permission.name} while evaluating permission state" + "for appId $appId and userId $userId") } // TODO: revokePermissionsNoLongerImplicitLocked() for runtime permissions } private fun MutateStateScope.grantImplicitPermissions(packageState: PackageState, userId: Int) { val appId = packageState.appId val androidPackage = packageState.androidPackage ?: return androidPackage.implicitPermissions.forEachIndexed implicitPermissions@ { _, implicitPermissionName -> private fun MutateStateScope.inheritImplicitPermissionStates(appId: Int, userId: Int) { val implicitPermissions = IndexedSet<String>() forEachPackageInAppId(appId) { implicitPermissions += it.androidPackage!!.implicitPermissions } implicitPermissions.forEachIndexed implicitPermissions@ { _, implicitPermissionName -> val implicitPermission = newState.systemState.permissions[implicitPermissionName] checkNotNull(implicitPermission) { "Unknown implicit permission $implicitPermissionName in split permissions" Loading @@ -502,15 +650,14 @@ class UidPermissionPolicy : SchemePolicy() { return@implicitPermissions } // Explicitly check against the old state to determine if this permission is new. val isNewPermission = getPermissionFlags( appId, userId, implicitPermissionName, oldState ) == 0 val isNewPermission = getPermissionFlags(appId, userId, implicitPermissionName, oldState) == 0 if (!isNewPermission) { return@implicitPermissions } val sourcePermissions = newState.systemState .implicitToSourcePermissions[implicitPermissionName] ?: return@implicitPermissions var newFlags = 0 var newFlags = getPermissionFlags(appId, userId, implicitPermissionName) sourcePermissions.forEachIndexed sourcePermissions@ { _, sourcePermissionName -> val sourcePermission = newState.systemState.permissions[sourcePermissionName] checkNotNull(sourcePermission) { Loading @@ -525,12 +672,13 @@ class UidPermissionPolicy : SchemePolicy() { newFlags = 0 } newFlags = newFlags or (sourceFlags and PermissionFlags.MASK_RUNTIME) if (!sourcePermission.isRuntime && isSourceGranted) { newFlags = newFlags or PermissionFlags.IMPLICIT_GRANTED } } } if (implicitPermissionName in RETAIN_IMPLICIT_FLAGS_PERMISSIONS) { newFlags = newFlags andInv PermissionFlags.IMPLICIT } else { newFlags = newFlags or PermissionFlags.IMPLICIT } setPermissionFlags(appId, userId, implicitPermissionName, newFlags) } } Loading Loading @@ -654,6 +802,17 @@ class UidPermissionPolicy : SchemePolicy() { } } private fun MutateStateScope.getAppIdTargetSdkVersion(appId: Int, permissionName: String): Int { var targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT forEachPackageInAppId(appId) { packageState -> val androidPackage = packageState.androidPackage!! if (permissionName in androidPackage.requestedPermissions) { targetSdkVersion = targetSdkVersion.coerceAtMost(androidPackage.targetSdkVersion) } } return targetSdkVersion } private fun MutateStateScope.anyPackageInAppId( appId: Int, state: AccessState = newState, Loading @@ -666,6 +825,20 @@ class UidPermissionPolicy : SchemePolicy() { } } private fun MutateStateScope.forEachPackageInAppId( appId: Int, state: AccessState = newState, action: (PackageState) -> Unit ) { val packageNames = state.systemState.appIds[appId] packageNames.forEachIndexed { _, packageName -> val packageState = state.systemState.packageStates[packageName]!! if (packageState.androidPackage != null) { action(packageState) } } } private fun MutateStateScope.shouldGrantPermissionByProtectionFlags( packageState: PackageState, permission: Permission Loading Loading @@ -813,13 +986,6 @@ class UidPermissionPolicy : SchemePolicy() { return uid == ownerUid } override fun MutateStateScope.onPackageRemoved(packageName: String, appId: Int) { // TODO: STOPSHIP: Remove this check or at least turn into logging. check(packageName !in newState.systemState.disabledSystemPackageStates) { "Package $packageName reported as removed before disabled system package is enabled" } } override fun BinaryXmlPullParser.parseSystemState(state: AccessState) { with(persistence) { this@parseSystemState.parseSystemState(state) } } Loading Loading @@ -928,13 +1094,24 @@ class UidPermissionPolicy : SchemePolicy() { private const val PLATFORM_PACKAGE_NAME = "android" // A set of permissions that we don't want to revoke when they are no longer implicit. private val RETAIN_IMPLICIT_GRANT_PERMISSIONS = indexedSetOf( private val RETAIN_IMPLICIT_FLAGS_PERMISSIONS = indexedSetOf( Manifest.permission.ACCESS_MEDIA_LOCATION, Manifest.permission.ACTIVITY_RECOGNITION, Manifest.permission.READ_MEDIA_AUDIO, Manifest.permission.READ_MEDIA_IMAGES, Manifest.permission.READ_MEDIA_VIDEO, ) // TODO: also add the permission NEARBY_WIFI_DEVICES to this set private val NEARBY_DEVICES_PERMISSIONS = indexedSetOf( Manifest.permission.BLUETOOTH_ADVERTISE, Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.BLUETOOTH_SCAN ) private val NOTIFICATION_PERMISSIONS = indexedSetOf( Manifest.permission.POST_NOTIFICATIONS ) } fun interface OnPermissionFlagsChangedListener { Loading