blob: d53397d0625a5d7bfb6892941c711767fb5a99d4 [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.permission.access.permission
import android.content.pm.PackageManager
import android.content.pm.PermissionInfo
import android.os.Build
import android.util.Log
import com.android.permission.access.AccessState
import com.android.permission.access.AccessUri
import com.android.permission.access.PermissionUri
import com.android.permission.access.SchemePolicy
import com.android.permission.access.UidUri
import com.android.permission.access.UserState
import com.android.permission.access.collection.* // ktlint-disable no-wildcard-imports
import com.android.permission.access.data.Permission
import com.android.permission.access.external.AndroidPackage
import com.android.permission.access.external.CompatibilityPermissionInfo
import com.android.permission.access.external.KnownPackages
import com.android.permission.access.external.PackageInfoUtils
import com.android.permission.access.external.PackageState
import com.android.permission.access.external.RoSystemProperties
import com.android.permission.access.external.SigningDetails
import com.android.permission.access.util.hasBits
import com.android.permission.compat.UserHandleCompat
private const val PLATFORM_PACKAGE_NAME = "android"
private val LOG_TAG = UidPermissionPolicy::class.java.simpleName
class UidPermissionPolicy : SchemePolicy() {
override val subjectScheme: String
get() = UidUri.SCHEME
override val objectScheme: String
get() = PermissionUri.SCHEME
override fun getDecision(subject: AccessUri, `object`: AccessUri, state: AccessState): Int {
subject as UidUri
`object` as PermissionUri
return state.userStates[subject.userId]?.permissionFlags?.get(subject.appId)
?.get(`object`.permissionName) ?: 0
}
override fun setDecision(
subject: AccessUri,
`object`: AccessUri,
decision: Int,
oldState: AccessState,
newState: AccessState
) {
subject as UidUri
`object` as PermissionUri
val uidFlags = newState.userStates.getOrPut(subject.userId) { UserState() }
.permissionFlags.getOrPut(subject.appId) { IndexedMap() }
uidFlags[`object`.permissionName] = decision
}
override fun onUserAdded(userId: Int, oldState: AccessState, newState: AccessState) {
newState.systemState.packageStates.forEachValueIndexed { _, packageState ->
evaluateAllPermissionStatesForPackageAndUser(
packageState, null, userId, oldState, newState
)
}
}
override fun onAppIdAdded(appId: Int, oldState: AccessState, newState: AccessState) {
newState.userStates.forEachIndexed { _, _, userState ->
userState.permissionFlags.getOrPut(appId) { IndexedMap() }
}
}
override fun onAppIdRemoved(appId: Int, oldState: AccessState, newState: AccessState) {
newState.userStates.forEachIndexed { _, _, userState -> userState.permissionFlags -= appId }
}
override fun onPackageAdded(
packageState: PackageState,
oldState: AccessState,
newState: AccessState
) {
val changedPermissionNames = IndexedSet<String>()
adoptPermissions(packageState, changedPermissionNames, newState)
addPermissionGroups(packageState, newState)
addPermissions(packageState, changedPermissionNames, newState)
// TODO: revokeStoragePermissionsIfScopeExpandedInternal()
trimPermissions(packageState.packageName, newState)
changedPermissionNames.forEachIndexed { _, it ->
evaluatePermissionStateForAllPackages(it, packageState, oldState, newState)
}
evaluateAllPermissionStatesForPackage(packageState, packageState, oldState, newState)
}
private fun adoptPermissions(
packageState: PackageState,
changedPermissionNames: IndexedSet<String>,
newState: AccessState
) {
val `package` = packageState.androidPackage!!
`package`.adoptPermissions.forEachIndexed { _, originalPackageName ->
val packageName = `package`.packageName
if (!canAdoptPermissions(packageName, originalPackageName, newState)) {
return@forEachIndexed
}
newState.systemState.permissions.let { permissions ->
permissions.forEachIndexed { i, permissionName, oldPermission ->
if (oldPermission.packageName != originalPackageName) {
return@forEachIndexed
}
@Suppress("DEPRECATION")
val newPermissionInfo = PermissionInfo().apply {
name = oldPermission.permissionInfo.name
this.packageName = packageName
protectionLevel = oldPermission.permissionInfo.protectionLevel
}
val newPermission = Permission(newPermissionInfo, false, oldPermission.type, 0)
changedPermissionNames += permissionName
permissions.setValueAt(i, newPermission)
}
}
}
}
private fun canAdoptPermissions(
packageName: String,
originalPackageName: String,
newState: AccessState
): Boolean {
val originalPackageState = newState.systemState.packageStates[originalPackageName]
?: return false
if (!originalPackageState.isSystem) {
Log.w(
LOG_TAG, "Unable to adopt permissions from $originalPackageName to $packageName:" +
" original package not in system partition"
)
return false
}
if (originalPackageState.androidPackage != null) {
Log.w(
LOG_TAG, "Unable to adopt permissions from $originalPackageName to $packageName:" +
" original package still exists"
)
return false
}
return true
}
private fun addPermissionGroups(packageState: PackageState, newState: AccessState) {
// Different from the old implementation, which decides whether the app is an instant app by
// the install flags, now for consistent behavior we allow adding permission groups if the
// app is non-instant in at least one user.
val isInstantApp = packageState.userStates.allIndexed { _, _, it -> it.isInstantApp }
if (isInstantApp) {
Log.w(
LOG_TAG, "Ignoring permission groups declared in package" +
" ${packageState.packageName}: instant apps cannot declare permission groups"
)
return
}
packageState.androidPackage!!.permissionGroups.forEachIndexed { _, parsedPermissionGroup ->
val newPermissionGroup = PackageInfoUtils.generatePermissionGroupInfo(
parsedPermissionGroup, PackageManager.GET_META_DATA.toLong()
)
// TODO: Clear permission state on group take-over?
val permissionGroupName = newPermissionGroup.name
val oldPermissionGroup = newState.systemState.permissionGroups[permissionGroupName]
if (oldPermissionGroup != null &&
newPermissionGroup.packageName != oldPermissionGroup.packageName) {
Log.w(
LOG_TAG, "Ignoring permission group $permissionGroupName declared in package" +
" ${newPermissionGroup.packageName}: already declared in another package" +
" ${oldPermissionGroup.packageName}"
)
return@forEachIndexed
}
newState.systemState.permissionGroups[permissionGroupName] = newPermissionGroup
}
}
private fun addPermissions(
packageState: PackageState,
changedPermissionNames: IndexedSet<String>,
newState: AccessState
) {
packageState.androidPackage!!.permissions.forEachIndexed { _, parsedPermission ->
// TODO:
// parsedPermission.flags = parsedPermission.flags andInv PermissionInfo.FLAG_INSTALLED
// TODO: This seems actually unused.
// if (packageState.androidPackage.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
// parsedPermission.setParsedPermissionGroup(
// newState.systemState.permissionGroup[parsedPermission.group]
// )
// }
val newPermissionInfo = PackageInfoUtils.generatePermissionInfo(
parsedPermission, PackageManager.GET_META_DATA.toLong()
)
// TODO: newPermissionInfo.flags |= PermissionInfo.FLAG_INSTALLED
val permissionName = newPermissionInfo.name
val oldPermission = if (parsedPermission.isTree) {
newState.systemState.permissionTrees[permissionName]
} else {
newState.systemState.permissions[permissionName]
}
// Different from the old implementation, which may add an (incomplete) signature
// permission inside another package's permission tree, we now consistently ignore such
// permissions.
val permissionTree = getPermissionTree(permissionName, newState)
val newPackageName = newPermissionInfo.packageName
if (permissionTree != null && newPackageName != permissionTree.packageName) {
Log.w(
LOG_TAG, "Ignoring permission $permissionName declared in package" +
" $newPackageName: base permission tree ${permissionTree.name} is" +
" declared in another package ${permissionTree.packageName}"
)
return@forEachIndexed
}
val newPermission = if (oldPermission != null &&
newPackageName != oldPermission.packageName) {
val oldPackageName = oldPermission.packageName
// Only allow system apps to redefine non-system permissions.
if (!packageState.isSystem) {
Log.w(
LOG_TAG, "Ignoring permission $permissionName declared in package" +
" $newPackageName: already declared in another package" +
" $oldPackageName"
)
return@forEachIndexed
}
if (oldPermission.type == Permission.TYPE_CONFIG && !oldPermission.isReconciled) {
// It's a config permission and has no owner, take ownership now.
Permission(newPermissionInfo, true, Permission.TYPE_CONFIG, packageState.appId)
} else if (newState.systemState.packageStates[oldPackageName]?.isSystem != true) {
Log.w(
LOG_TAG, "Overriding permission $permissionName with new declaration in" +
" system package $newPackageName: originally declared in another" +
" package $oldPackageName"
)
// Remove permission state on owner change.
newState.userStates.forEachValueIndexed { _, userState ->
userState.permissionFlags.forEachValueIndexed { _, permissionFlags ->
permissionFlags -= newPermissionInfo.name
}
}
// TODO: Notify re-evaluation of this permission.
Permission(
newPermissionInfo, true, Permission.TYPE_MANIFEST, packageState.appId
)
} else {
Log.w(
LOG_TAG, "Ignoring permission $permissionName declared in system package" +
" $newPackageName: already declared in another system package" +
" $oldPackageName")
return@forEachIndexed
}
} else {
// TODO: STOPSHIP: Clear permission state on type or group change.
// Different from the old implementation, which doesn't update the permission
// definition upon app update, but does update it on the next boot, we now
// consistently update the permission definition upon app update.
Permission(newPermissionInfo, true, Permission.TYPE_MANIFEST, packageState.appId)
}
changedPermissionNames += permissionName
if (parsedPermission.isTree) {
newState.systemState.permissionTrees[permissionName] = newPermission
} else {
newState.systemState.permissions[permissionName] = newPermission
}
}
}
private fun trimPermissions(
packageName: String,
newState: AccessState,
) {
val packageState = newState.systemState.packageStates[packageName]
val androidPackage = packageState?.androidPackage
if (packageState != null && androidPackage == null) {
return
}
newState.systemState.permissionTrees.removeAllIndexed {
_, permissionTreeName, permissionTree ->
permissionTree.packageName == packageName && (
packageState == null || androidPackage!!.permissions.noneIndexed { _, it ->
it.isTree && it.name == permissionTreeName
}
)
}
newState.systemState.permissions.removeAllIndexed { i, permissionName, permission ->
val updatedPermission = updatePermissionIfDynamic(permission, newState)
newState.systemState.permissions.setValueAt(i, updatedPermission)
if (updatedPermission.packageName == packageName && (
packageState == null || androidPackage!!.permissions.noneIndexed { _, it ->
!it.isTree && it.name == permissionName
}
)) {
if (!isPermissionDeclaredByDisabledSystemPackage(permission, newState)) {
newState.userStates.forEachIndexed { _, userId, userState ->
userState.permissionFlags.forEachKeyIndexed { _, appId ->
setPermissionFlags(
appId, permissionName, getPermissionFlags(
appId, permissionName, userId, newState
) and PermissionFlags.INSTALL_REVOKED, userId, newState
)
}
}
}
true
} else {
false
}
}
}
private fun isPermissionDeclaredByDisabledSystemPackage(
permission: Permission,
newState: AccessState
): Boolean {
val disabledSystemPackage = newState.systemState
.disabledSystemPackageStates[permission.packageName]?.androidPackage ?: return false
return disabledSystemPackage.permissions.anyIndexed { _, it ->
it.name == permission.name && it.protectionLevel == permission.protectionLevel
}
}
private fun updatePermissionIfDynamic(
permission: Permission,
newState: AccessState
): Permission {
if (!permission.isDynamic) {
return permission
}
val permissionTree = getPermissionTree(permission.name, newState) ?: return permission
@Suppress("DEPRECATION")
return permission.copy(
permissionInfo = PermissionInfo(permission.permissionInfo).apply {
packageName = permissionTree.packageName
}, appId = permissionTree.appId, isReconciled = true
)
}
private fun getPermissionTree(permissionName: String, newState: AccessState): Permission? =
newState.systemState.permissionTrees.firstNotNullOfOrNullIndexed {
_, permissionTreeName, permissionTree ->
if (permissionName.startsWith(permissionTreeName) &&
permissionName.length > permissionTreeName.length &&
permissionName[permissionTreeName.length] == '.') {
permissionTree
} else {
null
}
}
private fun evaluatePermissionStateForAllPackages(
permissionName: String,
installedPackageState: PackageState?,
oldState: AccessState,
newState: AccessState
) {
newState.systemState.userIds.forEachIndexed { _, userId ->
oldState.userStates[userId]?.permissionFlags?.forEachIndexed {
_, appId, permissionFlags ->
if (permissionName in permissionFlags) {
evaluatePermissionState(
appId, permissionName, installedPackageState, userId, oldState, newState
)
}
}
}
}
private fun evaluateAllPermissionStatesForPackage(
packageState: PackageState,
installedPackageState: PackageState?,
oldState: AccessState,
newState: AccessState
) {
newState.systemState.userIds.forEachIndexed { _, userId ->
evaluateAllPermissionStatesForPackageAndUser(
packageState, installedPackageState, userId, oldState, newState
)
}
}
private fun evaluateAllPermissionStatesForPackageAndUser(
packageState: PackageState,
installedPackageState: PackageState?,
userId: Int,
oldState: AccessState,
newState: AccessState
) {
packageState.androidPackage?.requestedPermissions?.forEachIndexed { _, it ->
evaluatePermissionState(
packageState.appId, it, installedPackageState, userId, oldState, newState
)
}
}
private fun evaluatePermissionState(
appId: Int,
permissionName: String,
installedPackageState: PackageState?,
userId: Int,
oldState: AccessState,
newState: AccessState
) {
val packageNames = newState.systemState.appIds[appId]
val hasMissingPackage = packageNames.anyIndexed { _, packageName ->
newState.systemState.packageStates[packageName]!!.androidPackage == null
}
if (packageNames.size == 1 && hasMissingPackage) {
// For non-shared-user packages with missing androidPackage, skip evaluation.
return
}
val permission = newState.systemState.permissions[permissionName] ?: return
val oldFlags = getPermissionFlags(appId, permissionName, userId, newState)
if (permission.isNormal) {
val wasGranted = oldFlags.hasBits(PermissionFlags.INSTALL_GRANTED)
if (!wasGranted) {
val wasRevoked = oldFlags.hasBits(PermissionFlags.INSTALL_REVOKED)
val isRequestedByInstalledPackage = installedPackageState != null &&
permissionName in installedPackageState.androidPackage!!.requestedPermissions
val isRequestedBySystemPackage = anyPackageInAppId(appId, newState) {
it.isSystem && permissionName in it.androidPackage!!.requestedPermissions
}
val isCompatibilityPermission = anyPackageInAppId(appId, newState) {
isCompatibilityPermissionForPackage(it.androidPackage!!, permissionName)
}
// If this is an existing, non-system package,
// then we can't add any new permissions to it.
// Except if this is a permission that was added to the platform
val newFlags = if (!wasRevoked || isRequestedByInstalledPackage ||
isRequestedBySystemPackage || isCompatibilityPermission) {
PermissionFlags.INSTALL_GRANTED
} else {
PermissionFlags.INSTALL_REVOKED
}
setPermissionFlags(appId, permissionName, newFlags, userId, newState)
}
} else if (permission.isSignature || permission.isInternal) {
val wasProtectionGranted = oldFlags.hasBits(PermissionFlags.PROTECTION_GRANTED)
var newFlags = if (hasMissingPackage && wasProtectionGranted) {
// Keep the non-runtime permission grants for shared UID with missing androidPackage
PermissionFlags.PROTECTION_GRANTED
} else {
val mayGrantByPrivileged = !permission.isPrivileged || (
anyPackageInAppId(appId, newState) {
checkPrivilegedPermissionAllowlist(it, permission, newState)
}
)
val shouldGrantBySignature = permission.isSignature && (
anyPackageInAppId(appId, newState) {
shouldGrantPermissionBySignature(it, permission, newState)
}
)
val shouldGrantByProtectionFlags = anyPackageInAppId(appId, newState) {
shouldGrantPermissionByProtectionFlags(it, permission, newState)
}
if (mayGrantByPrivileged &&
(shouldGrantBySignature || shouldGrantByProtectionFlags)) {
PermissionFlags.PROTECTION_GRANTED
} else {
0
}
}
// Different from the old implementation, which seemingly allows granting an
// unallowlisted privileged permission via development or role but revokes it upon next
// reconciliation, we now properly allows that because the privileged protection flag
// should only affect the other static flags, but not dynamic flags like development or
// role. This may be useful in the case of an updated system app.
if (permission.isDevelopment) {
newFlags = newFlags or (oldFlags and PermissionFlags.API_GRANTED)
}
if (permission.isRole) {
newFlags = newFlags or (oldFlags and PermissionFlags.ROLE_GRANTED)
}
setPermissionFlags(appId, permissionName, newFlags, userId, newState)
} else if (permission.isRuntime) {
// TODO: add runtime permission
} else {
Log.e(LOG_TAG, "Unknown protection level ${permission.protectionLevel}" +
"for permission ${permission.name} while evaluating permission state" +
"for appId $appId and userId $userId")
}
}
private fun getPermissionFlags(
appId: Int,
permissionName: String,
userId: Int,
state: AccessState
): Int = state.userStates[userId].permissionFlags[appId].getWithDefault(permissionName, 0)
private fun setPermissionFlags(
appId: Int,
permissionName: String,
flags: Int,
userId: Int,
newState: AccessState
) {
newState.userStates[userId].permissionFlags[appId]!!
.putWithDefault(permissionName, flags, 0)
}
private fun isCompatibilityPermissionForPackage(
androidPackage: AndroidPackage,
permissionName: String
): Boolean {
for (info: CompatibilityPermissionInfo in CompatibilityPermissionInfo.COMPAT_PERMS) {
if (info.name == permissionName && androidPackage.targetSdkVersion < info.sdkVersion) {
Log.i(
LOG_TAG, "Auto-granting $permissionName to old package" +
" ${androidPackage.packageName}"
)
return true
}
}
return false
}
private fun shouldGrantPermissionBySignature(
packageState: PackageState,
permission: Permission,
newState: AccessState
): Boolean {
// check if the package is allow to use this signature permission. A package is allowed to
// use a signature permission if:
// - it has the same set of signing certificates as the source package
// - or its signing certificate was rotated from the source package's certificate
// - or its signing certificate is a previous signing certificate of the defining
// package, and the defining package still trusts the old certificate for permissions
// - or it shares a common signing certificate in its lineage with the defining package,
// and the defining package still trusts the old certificate for permissions
// - or it shares the above relationships with the system package
val sourceSigningDetails = newState.systemState
.packageStates[permission.packageName]?.signingDetails
val platformSigningDetails = newState.systemState
.packageStates[PLATFORM_PACKAGE_NAME]!!.signingDetails
return sourceSigningDetails?.hasCommonSignerWithCapability(packageState.signingDetails,
SigningDetails.CertCapabilities.PERMISSION) == true ||
packageState.signingDetails.hasAncestorOrSelf(platformSigningDetails) ||
platformSigningDetails.checkCapability(packageState.signingDetails,
SigningDetails.CertCapabilities.PERMISSION)
}
private fun checkPrivilegedPermissionAllowlist(
packageState: PackageState,
permission: Permission,
newState: AccessState
): Boolean {
if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE) {
return true
}
if (packageState.packageName == PLATFORM_PACKAGE_NAME) {
return true
}
val androidPackage = packageState.androidPackage!!
if (!androidPackage.isPrivileged) {
return true
}
if (permission.packageName !in
newState.systemState.privilegedPermissionAllowlistSourcePackageNames) {
return true
}
if (isInSystemConfigPrivAppPermissions(androidPackage, permission.name, newState)) {
return true
}
if (isInSystemConfigPrivAppDenyPermissions(androidPackage, permission.name, newState)) {
return false
}
// Updated system apps do not need to be allowlisted
if (packageState.isUpdatedSystemApp) {
return true
}
// TODO: Enforce the allowlist on boot
return !RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE
}
private fun isInSystemConfigPrivAppPermissions(
androidPackage: AndroidPackage,
permissionName: String,
newState: AccessState
): Boolean {
val apexModuleName = androidPackage.apexModuleName
val systemState = newState.systemState
val packageName = androidPackage.packageName
val permissionNames = when {
androidPackage.isVendor -> systemState.vendorPrivAppPermissions[packageName]
androidPackage.isProduct -> systemState.productPrivAppPermissions[packageName]
androidPackage.isSystemExt -> systemState.systemExtPrivAppPermissions[packageName]
apexModuleName != null -> {
val apexPrivAppPermissions = systemState.apexPrivAppPermissions[apexModuleName]
?.get(packageName)
val privAppPermissions = systemState.privAppPermissions[packageName]
when {
apexPrivAppPermissions == null -> privAppPermissions
privAppPermissions == null -> apexPrivAppPermissions
else -> apexPrivAppPermissions + privAppPermissions
}
}
else -> systemState.privAppPermissions[packageName]
}
return permissionNames?.contains(permissionName) == true
}
private fun isInSystemConfigPrivAppDenyPermissions(
androidPackage: AndroidPackage,
permissionName: String,
newState: AccessState
): Boolean {
// Different from the previous implementation, which may incorrectly use the APEX package
// name, we now use the APEX module name to be consistent with the allowlist.
val apexModuleName = androidPackage.apexModuleName
val systemState = newState.systemState
val packageName = androidPackage.packageName
val permissionNames = when {
androidPackage.isVendor -> systemState.vendorPrivAppDenyPermissions[packageName]
androidPackage.isProduct -> systemState.productPrivAppDenyPermissions[packageName]
androidPackage.isSystemExt -> systemState.systemExtPrivAppDenyPermissions[packageName]
// Different from the previous implementation, which ignores the regular priv app
// denylist in this case, we now respect it as well to be consistent with the allowlist.
apexModuleName != null -> {
val apexPrivAppDenyPermissions = systemState
.apexPrivAppDenyPermissions[apexModuleName]?.get(packageName)
val privAppDenyPermissions = systemState.privAppDenyPermissions[packageName]
when {
apexPrivAppDenyPermissions == null -> privAppDenyPermissions
privAppDenyPermissions == null -> apexPrivAppDenyPermissions
else -> apexPrivAppDenyPermissions + privAppDenyPermissions
}
}
else -> systemState.privAppDenyPermissions[packageName]
}
return permissionNames?.contains(permissionName) == true
}
private fun anyPackageInAppId(
appId: Int,
newState: AccessState,
predicate: (PackageState) -> Boolean
): Boolean {
val packageNames = newState.systemState.appIds[appId]
return packageNames.anyIndexed { _, packageName ->
val packageState = newState.systemState.packageStates[packageName]!!
packageState.androidPackage != null && predicate(packageState)
}
}
private fun shouldGrantPermissionByProtectionFlags(
packageState: PackageState,
permission: Permission,
newState: AccessState
): Boolean {
val androidPackage = packageState.androidPackage!!
val knownPackages = newState.systemState.knownPackages
val packageName = packageState.packageName
if ((permission.isPrivileged || permission.isOem) && packageState.isSystem) {
val shouldGrant = if (packageState.isUpdatedSystemApp) {
// For updated system applications, a privileged/oem permission
// is granted only if it had been defined by the original application.
val disabledSystemPackage = newState.systemState
.disabledSystemPackageStates[packageState.packageName]?.androidPackage
disabledSystemPackage != null &&
permission.name in disabledSystemPackage.requestedPermissions &&
shouldGrantPrivilegedOrOemPermission(
disabledSystemPackage, permission, newState
)
} else {
shouldGrantPrivilegedOrOemPermission(androidPackage, permission, newState)
}
if (shouldGrant) {
return true
}
}
if (permission.isPre23 && androidPackage.targetSdkVersion < Build.VERSION_CODES.M) {
// If this was a previously normal/dangerous permission that got moved
// to a system permission as part of the runtime permission redesign, then
// we still want to blindly grant it to old apps.
return true
}
if (permission.isInstaller && (
packageName in knownPackages[KnownPackages.PACKAGE_INSTALLER] ||
packageName in knownPackages[KnownPackages.PACKAGE_PERMISSION_CONTROLLER]
)) {
// If this permission is to be granted to the system installer and
// this app is an installer or permission controller, then it gets the permission.
return true
}
if (permission.isVerifier &&
packageName in knownPackages[KnownPackages.PACKAGE_VERIFIER]) {
// If this permission is to be granted to the system verifier and
// this app is a verifier, then it gets the permission.
return true
}
if (permission.isPreInstalled && packageState.isSystem) {
// Any pre-installed system app is allowed to get this permission.
return true
}
if (permission.isKnownSigner &&
packageState.signingDetails.hasAncestorOrSelfWithDigest(permission.knownCerts)) {
// If the permission is to be granted to a known signer then check if any of this
// app's signing certificates are in the trusted certificate digest Set.
return true
}
if (permission.isSetup &&
packageName in knownPackages[KnownPackages.PACKAGE_SETUP_WIZARD]) {
// If this permission is to be granted to the system setup wizard and
// this app is a setup wizard, then it gets the permission.
return true
}
if (permission.isSystemTextClassifier &&
packageName in knownPackages[KnownPackages.PACKAGE_SYSTEM_TEXT_CLASSIFIER]) {
// Special permissions for the system default text classifier.
return true
}
if (permission.isConfigurator &&
packageName in knownPackages[KnownPackages.PACKAGE_CONFIGURATOR]) {
// Special permissions for the device configurator.
return true
}
if (permission.isIncidentReportApprover &&
packageName in knownPackages[KnownPackages.PACKAGE_INCIDENT_REPORT_APPROVER]) {
// If this permission is to be granted to the incident report approver and
// this app is the incident report approver, then it gets the permission.
return true
}
if (permission.isAppPredictor &&
packageName in knownPackages[KnownPackages.PACKAGE_APP_PREDICTOR]) {
// Special permissions for the system app predictor.
return true
}
if (permission.isCompanion &&
packageName in knownPackages[KnownPackages.PACKAGE_COMPANION]) {
// Special permissions for the system companion device manager.
return true
}
if (permission.isRetailDemo &&
packageName in knownPackages[KnownPackages.PACKAGE_RETAIL_DEMO] &&
isDeviceOrProfileOwnerUid(packageState.appId, newState)) {
// Special permission granted only to the OEM specified retail demo app.
// Note that the original code was passing app ID as UID, so this behavior is kept
// unchanged.
return true
}
if (permission.isRecents &&
packageName in knownPackages[KnownPackages.PACKAGE_RECENTS]) {
// Special permission for the recents app.
return true
}
return false
}
private fun shouldGrantPrivilegedOrOemPermission(
androidPackage: AndroidPackage,
permission: Permission,
state: AccessState
): Boolean {
val permissionName = permission.name
val packageName = androidPackage.packageName
when {
permission.isPrivileged -> {
if (androidPackage.isPrivileged) {
// In any case, don't grant a privileged permission to privileged vendor apps,
// if the permission's protectionLevel does not have the extra vendorPrivileged
// flag.
if (androidPackage.isVendor && !permission.isVendorPrivileged) {
Log.w(
LOG_TAG, "Permission $permissionName cannot be granted to privileged" +
" vendor app $packageName because it isn't a vendorPrivileged" +
" permission"
)
return false
}
return true
}
}
permission.isOem -> {
if (androidPackage.isOem) {
val isOemAllowlisted = state.systemState
.oemPermissions[packageName]?.get(permissionName)
checkNotNull(isOemAllowlisted) {
"OEM permission $permissionName requested by package" +
" $packageName must be explicitly declared granted or not"
}
return isOemAllowlisted
}
}
}
return false
}
private fun isDeviceOrProfileOwnerUid(uid: Int, state: AccessState): Boolean {
val userId = UserHandleCompat.getUserId(uid)
val ownerPackageName = state.systemState.deviceAndProfileOwners[userId] ?: return false
val ownerPackageState = state.systemState.packageStates[ownerPackageName] ?: return false
val ownerUid = UserHandleCompat.getUid(userId, ownerPackageState.appId)
return uid == ownerUid
}
override fun onPackageRemoved(
packageState: PackageState,
oldState: AccessState,
newState: AccessState
) {
// TODO
}
}