Merge "Move new unified permission subsystem into platform."
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.server.permission.access
|
||||
|
||||
import com.android.internal.annotations.Keep
|
||||
import com.android.server.permission.access.external.PackageState
|
||||
|
||||
@Keep
|
||||
class AccessCheckingService {
|
||||
@Volatile
|
||||
private lateinit var state: AccessState
|
||||
private val stateLock = Any()
|
||||
|
||||
private val policy = AccessPolicy()
|
||||
|
||||
private val persistence = AccessPersistence(policy)
|
||||
|
||||
fun init() {
|
||||
val state = AccessState()
|
||||
state.systemState.userIds.apply {
|
||||
// TODO: Get and add all user IDs.
|
||||
// TODO: Maybe get and add all packages?
|
||||
}
|
||||
persistence.read(state)
|
||||
this.state = state
|
||||
}
|
||||
|
||||
fun getDecision(subject: AccessUri, `object`: AccessUri): Int =
|
||||
policy.getDecision(subject, `object`, state)
|
||||
|
||||
fun setDecision(subject: AccessUri, `object`: AccessUri, decision: Int) {
|
||||
mutateState { oldState, newState ->
|
||||
policy.setDecision(subject, `object`, decision, oldState, newState)
|
||||
}
|
||||
}
|
||||
|
||||
fun onUserAdded(userId: Int) {
|
||||
mutateState { oldState, newState ->
|
||||
policy.onUserAdded(userId, oldState, newState)
|
||||
}
|
||||
}
|
||||
|
||||
fun onUserRemoved(userId: Int) {
|
||||
mutateState { oldState, newState ->
|
||||
policy.onUserRemoved(userId, oldState, newState)
|
||||
}
|
||||
}
|
||||
|
||||
fun onPackageAdded(packageState: PackageState) {
|
||||
mutateState { oldState, newState ->
|
||||
policy.onPackageAdded(packageState, oldState, newState)
|
||||
}
|
||||
}
|
||||
|
||||
fun onPackageRemoved(packageState: PackageState) {
|
||||
mutateState { oldState, newState ->
|
||||
policy.onPackageRemoved(packageState, oldState, newState)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Replace (oldState, newState) with Kotlin context receiver once it's stabilized.
|
||||
private inline fun mutateState(action: (oldState: AccessState, newState: AccessState) -> Unit) {
|
||||
synchronized(stateLock) {
|
||||
val oldState = state
|
||||
val newState = oldState.copy()
|
||||
action(oldState, newState)
|
||||
persistence.write(newState)
|
||||
state = newState
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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.server.permission.access
|
||||
|
||||
import android.util.AtomicFile
|
||||
import android.util.Log
|
||||
import com.android.modules.utils.BinaryXmlPullParser
|
||||
import com.android.modules.utils.BinaryXmlSerializer
|
||||
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
|
||||
import com.android.server.permission.access.util.PermissionApex
|
||||
import com.android.server.permission.access.util.parseBinaryXml
|
||||
import com.android.server.permission.access.util.read
|
||||
import com.android.server.permission.access.util.serializeBinaryXml
|
||||
import com.android.server.permission.access.util.writeInlined
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
|
||||
class AccessPersistence(
|
||||
private val policy: AccessPolicy
|
||||
) {
|
||||
fun read(state: AccessState) {
|
||||
readSystemState(state.systemState)
|
||||
val userStates = state.userStates
|
||||
state.systemState.userIds.forEachIndexed { _, userId ->
|
||||
readUserState(userId, userStates[userId])
|
||||
}
|
||||
}
|
||||
|
||||
private fun readSystemState(systemState: SystemState) {
|
||||
systemFile.parse {
|
||||
// This is the canonical way to call an extension function in a different class.
|
||||
// TODO(b/259469752): Use context receiver for this when it becomes stable.
|
||||
with(policy) { this@parse.parseSystemState(systemState) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun readUserState(userId: Int, userState: UserState) {
|
||||
getUserFile(userId).parse {
|
||||
with(policy) { this@parse.parseUserState(userId, userState) }
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun File.parse(block: BinaryXmlPullParser.() -> Unit) {
|
||||
try {
|
||||
AtomicFile(this).read { it.parseBinaryXml(block) }
|
||||
} catch (e: FileNotFoundException) {
|
||||
Log.i(LOG_TAG, "$this not found")
|
||||
} catch (e: Exception) {
|
||||
throw IllegalStateException("Failed to read $this", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun write(state: AccessState) {
|
||||
writeState(state.systemState, ::writeSystemState)
|
||||
state.userStates.forEachIndexed { _, userId, userState ->
|
||||
writeState(userState) { writeUserState(userId, it) }
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <T : WritableState> writeState(state: T, write: (T) -> Unit) {
|
||||
when (val writeMode = state.writeMode) {
|
||||
WriteMode.NONE -> {}
|
||||
WriteMode.SYNC -> write(state)
|
||||
WriteMode.ASYNC -> TODO()
|
||||
else -> error(writeMode)
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeSystemState(systemState: SystemState) {
|
||||
systemFile.serialize {
|
||||
with(policy) { this@serialize.serializeSystemState(systemState) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeUserState(userId: Int, userState: UserState) {
|
||||
getUserFile(userId).serialize {
|
||||
with(policy) { this@serialize.serializeUserState(userId, userState) }
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun File.serialize(block: BinaryXmlSerializer.() -> Unit) {
|
||||
try {
|
||||
AtomicFile(this).writeInlined { it.serializeBinaryXml(block) }
|
||||
} catch (e: Exception) {
|
||||
Log.e(LOG_TAG, "Failed to serialize $this", e)
|
||||
}
|
||||
}
|
||||
|
||||
private val systemFile: File
|
||||
get() = File(PermissionApex.systemDataDirectory, FILE_NAME)
|
||||
|
||||
private fun getUserFile(userId: Int): File =
|
||||
File(PermissionApex.getUserDataDirectory(userId), FILE_NAME)
|
||||
|
||||
companion object {
|
||||
private val LOG_TAG = AccessPersistence::class.java.simpleName
|
||||
|
||||
private const val FILE_NAME = "access.abx"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* 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.server.permission.access
|
||||
|
||||
import android.util.Log
|
||||
import com.android.modules.utils.BinaryXmlPullParser
|
||||
import com.android.modules.utils.BinaryXmlSerializer
|
||||
import com.android.server.permission.access.appop.PackageAppOpPolicy
|
||||
import com.android.server.permission.access.appop.UidAppOpPolicy
|
||||
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
|
||||
import com.android.server.permission.access.external.PackageState
|
||||
import com.android.server.permission.access.permission.UidPermissionPolicy
|
||||
import com.android.server.permission.access.util.forEachTag
|
||||
import com.android.server.permission.access.util.tag
|
||||
import com.android.server.permission.access.util.tagName
|
||||
|
||||
class AccessPolicy private constructor(
|
||||
private val schemePolicies: IndexedMap<String, IndexedMap<String, SchemePolicy>>
|
||||
) {
|
||||
constructor() : this(
|
||||
IndexedMap<String, IndexedMap<String, SchemePolicy>>().apply {
|
||||
fun addPolicy(policy: SchemePolicy) =
|
||||
getOrPut(policy.subjectScheme) { IndexedMap() }.put(policy.objectScheme, policy)
|
||||
addPolicy(UidPermissionPolicy())
|
||||
addPolicy(UidAppOpPolicy())
|
||||
addPolicy(PackageAppOpPolicy())
|
||||
}
|
||||
)
|
||||
|
||||
fun getDecision(subject: AccessUri, `object`: AccessUri, state: AccessState): Int =
|
||||
getSchemePolicy(subject, `object`).getDecision(subject, `object`, state)
|
||||
|
||||
fun setDecision(
|
||||
subject: AccessUri,
|
||||
`object`: AccessUri,
|
||||
decision: Int,
|
||||
oldState: AccessState,
|
||||
newState: AccessState
|
||||
) {
|
||||
getSchemePolicy(subject, `object`)
|
||||
.setDecision(subject, `object`, decision, oldState, newState)
|
||||
}
|
||||
|
||||
private fun getSchemePolicy(subject: AccessUri, `object`: AccessUri): SchemePolicy =
|
||||
checkNotNull(schemePolicies[subject.scheme]?.get(`object`.scheme)) {
|
||||
"Scheme policy for subject=$subject object=$`object` does not exist"
|
||||
}
|
||||
|
||||
fun onUserAdded(userId: Int, oldState: AccessState, newState: AccessState) {
|
||||
newState.systemState.userIds += userId
|
||||
newState.userStates[userId] = UserState()
|
||||
forEachSchemePolicy { it.onUserAdded(userId, oldState, newState) }
|
||||
}
|
||||
|
||||
fun onUserRemoved(userId: Int, oldState: AccessState, newState: AccessState) {
|
||||
newState.systemState.userIds -= userId
|
||||
newState.userStates -= userId
|
||||
forEachSchemePolicy { it.onUserRemoved(userId, oldState, newState) }
|
||||
}
|
||||
|
||||
fun onPackageAdded(packageState: PackageState, oldState: AccessState, newState: AccessState) {
|
||||
var isAppIdAdded = false
|
||||
newState.systemState.apply {
|
||||
packageStates[packageState.packageName] = packageState
|
||||
appIds.getOrPut(packageState.appId) {
|
||||
isAppIdAdded = true
|
||||
IndexedListSet()
|
||||
}.add(packageState.packageName)
|
||||
}
|
||||
if (isAppIdAdded) {
|
||||
forEachSchemePolicy { it.onAppIdAdded(packageState.appId, oldState, newState) }
|
||||
}
|
||||
forEachSchemePolicy { it.onPackageAdded(packageState, oldState, newState) }
|
||||
}
|
||||
|
||||
fun onPackageRemoved(packageState: PackageState, oldState: AccessState, newState: AccessState) {
|
||||
var isAppIdRemoved = false
|
||||
newState.systemState.apply {
|
||||
packageStates -= packageState.packageName
|
||||
appIds.apply appIds@{
|
||||
this[packageState.appId]?.apply {
|
||||
this -= packageState.packageName
|
||||
if (isEmpty()) {
|
||||
this@appIds -= packageState.appId
|
||||
isAppIdRemoved = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
forEachSchemePolicy { it.onPackageRemoved(packageState, oldState, newState) }
|
||||
if (isAppIdRemoved) {
|
||||
forEachSchemePolicy { it.onAppIdRemoved(packageState.appId, oldState, newState) }
|
||||
}
|
||||
}
|
||||
|
||||
fun BinaryXmlPullParser.parseSystemState(systemState: SystemState) {
|
||||
forEachTag {
|
||||
when (tagName) {
|
||||
TAG_ACCESS -> {
|
||||
forEachTag {
|
||||
forEachSchemePolicy {
|
||||
with(it) { this@parseSystemState.parseSystemState(systemState) }
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> Log.w(LOG_TAG, "Ignoring unknown tag $tagName when parsing system state")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun BinaryXmlSerializer.serializeSystemState(systemState: SystemState) {
|
||||
tag(TAG_ACCESS) {
|
||||
forEachSchemePolicy {
|
||||
with(it) { this@serializeSystemState.serializeSystemState(systemState) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun BinaryXmlPullParser.parseUserState(userId: Int, userState: UserState) {
|
||||
forEachTag {
|
||||
when (tagName) {
|
||||
TAG_ACCESS -> {
|
||||
forEachTag {
|
||||
forEachSchemePolicy {
|
||||
with(it) { this@parseUserState.parseUserState(userId, userState) }
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
Log.w(
|
||||
LOG_TAG,
|
||||
"Ignoring unknown tag $tagName when parsing user state for user $userId"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun BinaryXmlSerializer.serializeUserState(userId: Int, userState: UserState) {
|
||||
tag(TAG_ACCESS) {
|
||||
forEachSchemePolicy {
|
||||
with(it) { this@serializeUserState.serializeUserState(userId, userState) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun forEachSchemePolicy(action: (SchemePolicy) -> Unit) {
|
||||
schemePolicies.forEachValueIndexed { _, objectSchemePolicies ->
|
||||
objectSchemePolicies.forEachValueIndexed { _, schemePolicy ->
|
||||
action(schemePolicy)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG_TAG = AccessPolicy::class.java.simpleName
|
||||
|
||||
private const val TAG_ACCESS = "access"
|
||||
}
|
||||
}
|
||||
|
||||
abstract class SchemePolicy {
|
||||
@Volatile
|
||||
private var onDecisionChangedListeners = IndexedListSet<OnDecisionChangedListener>()
|
||||
private val onDecisionChangedListenersLock = Any()
|
||||
|
||||
abstract val subjectScheme: String
|
||||
|
||||
abstract val objectScheme: String
|
||||
|
||||
abstract fun getDecision(subject: AccessUri, `object`: AccessUri, state: AccessState): Int
|
||||
|
||||
abstract fun setDecision(
|
||||
subject: AccessUri,
|
||||
`object`: AccessUri,
|
||||
decision: Int,
|
||||
oldState: AccessState,
|
||||
newState: AccessState
|
||||
)
|
||||
|
||||
fun addOnDecisionChangedListener(listener: OnDecisionChangedListener) {
|
||||
synchronized(onDecisionChangedListenersLock) {
|
||||
onDecisionChangedListeners = onDecisionChangedListeners + listener
|
||||
}
|
||||
}
|
||||
|
||||
fun removeOnDecisionChangedListener(listener: OnDecisionChangedListener) {
|
||||
synchronized(onDecisionChangedListenersLock) {
|
||||
onDecisionChangedListeners = onDecisionChangedListeners - listener
|
||||
}
|
||||
}
|
||||
|
||||
protected fun notifyOnDecisionChangedListeners(
|
||||
subject: AccessUri,
|
||||
`object`: AccessUri,
|
||||
oldDecision: Int,
|
||||
newDecision: Int
|
||||
) {
|
||||
val listeners = onDecisionChangedListeners
|
||||
listeners.forEachIndexed { _, it ->
|
||||
it.onDecisionChanged(subject, `object`, oldDecision, newDecision)
|
||||
}
|
||||
}
|
||||
|
||||
open fun onUserAdded(userId: Int, oldState: AccessState, newState: AccessState) {}
|
||||
|
||||
open fun onUserRemoved(userId: Int, oldState: AccessState, newState: AccessState) {}
|
||||
|
||||
open fun onAppIdAdded(appId: Int, oldState: AccessState, newState: AccessState) {}
|
||||
|
||||
open fun onAppIdRemoved(appId: Int, oldState: AccessState, newState: AccessState) {}
|
||||
|
||||
open fun onPackageAdded(
|
||||
packageState: PackageState,
|
||||
oldState: AccessState,
|
||||
newState: AccessState
|
||||
) {}
|
||||
|
||||
open fun onPackageRemoved(
|
||||
packageState: PackageState,
|
||||
oldState: AccessState,
|
||||
newState: AccessState
|
||||
) {}
|
||||
|
||||
open fun BinaryXmlPullParser.parseSystemState(systemState: SystemState) {}
|
||||
|
||||
open fun BinaryXmlSerializer.serializeSystemState(systemState: SystemState) {}
|
||||
|
||||
open fun BinaryXmlPullParser.parseUserState(userId: Int, userState: UserState) {}
|
||||
|
||||
open fun BinaryXmlSerializer.serializeUserState(userId: Int, userState: UserState) {}
|
||||
|
||||
fun interface OnDecisionChangedListener {
|
||||
fun onDecisionChanged(
|
||||
subject: AccessUri,
|
||||
`object`: AccessUri,
|
||||
oldDecision: Int,
|
||||
newDecision: Int
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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.server.permission.access
|
||||
|
||||
import android.content.pm.PermissionGroupInfo
|
||||
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
|
||||
import com.android.server.permission.access.data.Permission
|
||||
import com.android.server.permission.access.external.PackageState
|
||||
|
||||
class AccessState private constructor(
|
||||
val systemState: SystemState,
|
||||
val userStates: IntMap<UserState>
|
||||
) {
|
||||
constructor() : this(SystemState(), IntMap())
|
||||
|
||||
fun copy(): AccessState = AccessState(systemState.copy(), userStates.copy { it.copy() })
|
||||
}
|
||||
|
||||
class SystemState private constructor(
|
||||
val userIds: IntSet,
|
||||
val packageStates: IndexedMap<String, PackageState>,
|
||||
val disabledSystemPackageStates: IndexedMap<String, PackageState>,
|
||||
val appIds: IntMap<IndexedListSet<String>>,
|
||||
// A map of KnownPackagesInt to a set of known package names
|
||||
val knownPackages: IntMap<IndexedListSet<String>>,
|
||||
// A map of userId to packageName
|
||||
val deviceAndProfileOwners: IntMap<String>,
|
||||
// A map of packageName to (A map of oem permission name to whether it's granted)
|
||||
val oemPermissions: IndexedMap<String, IndexedMap<String, Boolean>>,
|
||||
val privilegedPermissionAllowlistSourcePackageNames: IndexedListSet<String>,
|
||||
// A map of packageName to a set of vendor priv app permission names
|
||||
val vendorPrivAppPermissions: Map<String, Set<String>>,
|
||||
val productPrivAppPermissions: Map<String, Set<String>>,
|
||||
val systemExtPrivAppPermissions: Map<String, Set<String>>,
|
||||
val privAppPermissions: Map<String, Set<String>>,
|
||||
val apexPrivAppPermissions: Map<String, Map<String, Set<String>>>,
|
||||
val vendorPrivAppDenyPermissions: Map<String, Set<String>>,
|
||||
val productPrivAppDenyPermissions: Map<String, Set<String>>,
|
||||
val systemExtPrivAppDenyPermissions: Map<String, Set<String>>,
|
||||
val apexPrivAppDenyPermissions: Map<String, Map<String, Set<String>>>,
|
||||
val privAppDenyPermissions: Map<String, Set<String>>,
|
||||
val implicitToSourcePermissions: Map<String, Set<String>>,
|
||||
val permissionGroups: IndexedMap<String, PermissionGroupInfo>,
|
||||
val permissionTrees: IndexedMap<String, Permission>,
|
||||
val permissions: IndexedMap<String, Permission>
|
||||
) : WritableState() {
|
||||
constructor() : this(
|
||||
IntSet(), IndexedMap(), IndexedMap(), IntMap(), IntMap(), IntMap(), IndexedMap(),
|
||||
IndexedListSet(), IndexedMap(), IndexedMap(), IndexedMap(), IndexedMap(), IndexedMap(),
|
||||
IndexedMap(), IndexedMap(), IndexedMap(), IndexedMap(), IndexedMap(), IndexedMap(),
|
||||
IndexedMap(), IndexedMap(), IndexedMap()
|
||||
)
|
||||
|
||||
fun copy(): SystemState =
|
||||
SystemState(
|
||||
userIds.copy(),
|
||||
packageStates.copy { it },
|
||||
disabledSystemPackageStates.copy { it },
|
||||
appIds.copy { it.copy() },
|
||||
knownPackages.copy { it.copy() },
|
||||
deviceAndProfileOwners.copy { it },
|
||||
oemPermissions.copy { it.copy { it } },
|
||||
privilegedPermissionAllowlistSourcePackageNames.copy(),
|
||||
vendorPrivAppPermissions,
|
||||
productPrivAppPermissions,
|
||||
systemExtPrivAppPermissions,
|
||||
privAppPermissions,
|
||||
apexPrivAppPermissions,
|
||||
vendorPrivAppDenyPermissions,
|
||||
productPrivAppDenyPermissions,
|
||||
systemExtPrivAppDenyPermissions,
|
||||
apexPrivAppDenyPermissions,
|
||||
privAppDenyPermissions,
|
||||
implicitToSourcePermissions,
|
||||
permissionGroups.copy { it },
|
||||
permissionTrees.copy { it },
|
||||
permissions.copy { it }
|
||||
)
|
||||
}
|
||||
|
||||
class UserState private constructor(
|
||||
// A map of (appId to a map of (permissionName to permissionFlags))
|
||||
val permissionFlags: IntMap<IndexedMap<String, Int>>,
|
||||
val uidAppOpModes: IntMap<IndexedMap<String, Int>>,
|
||||
val packageAppOpModes: IndexedMap<String, IndexedMap<String, Int>>
|
||||
) : WritableState() {
|
||||
constructor() : this(IntMap(), IntMap(), IndexedMap())
|
||||
|
||||
fun copy(): UserState = UserState(permissionFlags.copy { it.copy { it } },
|
||||
uidAppOpModes.copy { it.copy { it } }, packageAppOpModes.copy { it.copy { it } })
|
||||
}
|
||||
|
||||
object WriteMode {
|
||||
const val NONE = 0
|
||||
const val SYNC = 1
|
||||
const val ASYNC = 2
|
||||
}
|
||||
|
||||
abstract class WritableState {
|
||||
var writeMode: Int = WriteMode.NONE
|
||||
private set
|
||||
|
||||
fun requestWrite(sync: Boolean = false) {
|
||||
if (sync) {
|
||||
writeMode = WriteMode.SYNC
|
||||
} else {
|
||||
if (writeMode != WriteMode.SYNC) {
|
||||
writeMode = WriteMode.ASYNC
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.server.permission.access
|
||||
|
||||
import com.android.server.permission.access.external.UserHandle
|
||||
import com.android.server.permission.access.external.UserHandleCompat
|
||||
|
||||
sealed class AccessUri(
|
||||
val scheme: String
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
throw NotImplementedError()
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
throw NotImplementedError()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
throw NotImplementedError()
|
||||
}
|
||||
}
|
||||
|
||||
data class AppOpUri(
|
||||
val appOpName: String
|
||||
) : AccessUri(SCHEME) {
|
||||
override fun toString(): String = "$scheme:///$appOpName"
|
||||
|
||||
companion object {
|
||||
const val SCHEME = "app-op"
|
||||
}
|
||||
}
|
||||
|
||||
data class PackageUri(
|
||||
val packageName: String,
|
||||
val userId: Int
|
||||
) : AccessUri(SCHEME) {
|
||||
override fun toString(): String = "$scheme:///$packageName/$userId"
|
||||
|
||||
companion object {
|
||||
const val SCHEME = "package"
|
||||
}
|
||||
}
|
||||
|
||||
data class PermissionUri(
|
||||
val permissionName: String
|
||||
) : AccessUri(SCHEME) {
|
||||
override fun toString(): String = "$scheme:///$permissionName"
|
||||
|
||||
companion object {
|
||||
const val SCHEME = "permission"
|
||||
}
|
||||
}
|
||||
|
||||
data class UidUri(
|
||||
val uid: Int
|
||||
) : AccessUri(SCHEME) {
|
||||
val userId: Int
|
||||
get() = UserHandleCompat.getUserId(uid)
|
||||
|
||||
val appId: Int
|
||||
get() = UserHandle.getAppId(uid)
|
||||
|
||||
override fun toString(): String = "$scheme:///$uid"
|
||||
|
||||
companion object {
|
||||
const val SCHEME = "uid"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.server.permission.access.appop
|
||||
|
||||
import android.app.AppOpsManager
|
||||
|
||||
object AppOpModes {
|
||||
const val MODE_ALLOWED = AppOpsManager.MODE_ALLOWED
|
||||
const val MODE_IGNORED = AppOpsManager.MODE_IGNORED
|
||||
const val MODE_ERRORED = AppOpsManager.MODE_ERRORED
|
||||
const val MODE_DEFAULT = AppOpsManager.MODE_DEFAULT
|
||||
const val MODE_FOREGROUND = AppOpsManager.MODE_FOREGROUND
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.server.permission.access.appop
|
||||
|
||||
import android.util.Log
|
||||
import com.android.modules.utils.BinaryXmlPullParser
|
||||
import com.android.modules.utils.BinaryXmlSerializer
|
||||
import com.android.server.permission.access.UserState
|
||||
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
|
||||
import com.android.server.permission.access.util.attributeInt
|
||||
import com.android.server.permission.access.util.attributeInterned
|
||||
import com.android.server.permission.access.util.forEachTag
|
||||
import com.android.server.permission.access.util.getAttributeIntOrThrow
|
||||
import com.android.server.permission.access.util.getAttributeValueOrThrow
|
||||
import com.android.server.permission.access.util.tag
|
||||
import com.android.server.permission.access.util.tagName
|
||||
|
||||
abstract class BaseAppOpPersistence {
|
||||
abstract fun BinaryXmlPullParser.parseUserState(userId: Int, userState: UserState)
|
||||
|
||||
abstract fun BinaryXmlSerializer.serializeUserState(userId: Int, userState: UserState)
|
||||
|
||||
protected fun BinaryXmlPullParser.parseAppOps(appOpModes: IndexedMap<String, Int>) {
|
||||
forEachTag {
|
||||
when (tagName) {
|
||||
TAG_APP_OP -> parseAppOp(appOpModes)
|
||||
else -> Log.w(LOG_TAG, "Ignoring unknown tag $name when parsing app-op state")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun BinaryXmlPullParser.parseAppOp(appOpModes: IndexedMap<String, Int>) {
|
||||
val name = getAttributeValueOrThrow(ATTR_NAME).intern()
|
||||
val mode = getAttributeIntOrThrow(ATTR_MODE)
|
||||
appOpModes[name] = mode
|
||||
}
|
||||
|
||||
protected fun BinaryXmlSerializer.serializeAppOps(appOpModes: IndexedMap<String, Int>) {
|
||||
appOpModes.forEachIndexed { _, name, mode ->
|
||||
serializeAppOp(name, mode)
|
||||
}
|
||||
}
|
||||
|
||||
private fun BinaryXmlSerializer.serializeAppOp(name: String, mode: Int) {
|
||||
tag(TAG_APP_OP) {
|
||||
attributeInterned(ATTR_NAME, name)
|
||||
attributeInt(ATTR_MODE, mode)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG_TAG = BaseAppOpPersistence::class.java.simpleName
|
||||
|
||||
private const val TAG_APP_OP = "app-op"
|
||||
|
||||
private const val ATTR_NAME = "name"
|
||||
private const val ATTR_MODE = "mode"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.server.permission.access.appop
|
||||
|
||||
import android.app.AppOpsManager
|
||||
import com.android.modules.utils.BinaryXmlPullParser
|
||||
import com.android.modules.utils.BinaryXmlSerializer
|
||||
import com.android.server.permission.access.AccessState
|
||||
import com.android.server.permission.access.AccessUri
|
||||
import com.android.server.permission.access.AppOpUri
|
||||
import com.android.server.permission.access.SchemePolicy
|
||||
import com.android.server.permission.access.UserState
|
||||
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
|
||||
|
||||
abstract class BaseAppOpPolicy(private val persistence: BaseAppOpPersistence) : SchemePolicy() {
|
||||
override fun getDecision(subject: AccessUri, `object`: AccessUri, state: AccessState): Int {
|
||||
`object` as AppOpUri
|
||||
return getModes(subject, state)
|
||||
.getWithDefault(`object`.appOpName, opToDefaultMode(`object`.appOpName))
|
||||
}
|
||||
|
||||
override fun setDecision(
|
||||
subject: AccessUri,
|
||||
`object`: AccessUri,
|
||||
decision: Int,
|
||||
oldState: AccessState,
|
||||
newState: AccessState
|
||||
) {
|
||||
`object` as AppOpUri
|
||||
val modes = getOrCreateModes(subject, newState)
|
||||
val oldMode = modes.putWithDefault(`object`.appOpName, decision,
|
||||
opToDefaultMode(`object`.appOpName))
|
||||
if (modes.isEmpty()) {
|
||||
removeModes(subject, newState)
|
||||
}
|
||||
if (oldMode != decision) {
|
||||
notifyOnDecisionChangedListeners(subject, `object`, oldMode, decision)
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun getModes(subject: AccessUri, state: AccessState): IndexedMap<String, Int>?
|
||||
|
||||
abstract fun getOrCreateModes(subject: AccessUri, state: AccessState): IndexedMap<String, Int>
|
||||
|
||||
abstract fun removeModes(subject: AccessUri, state: AccessState)
|
||||
|
||||
// TODO need to check that [AppOpsManager.getSystemAlertWindowDefault] works; likely no issue
|
||||
// since running in system process.
|
||||
private fun opToDefaultMode(appOpName: String) = AppOpsManager.opToDefaultMode(appOpName)
|
||||
|
||||
override fun BinaryXmlPullParser.parseUserState(userId: Int, userState: UserState) {
|
||||
with(persistence) { this@parseUserState.parseUserState(userId, userState) }
|
||||
}
|
||||
|
||||
override fun BinaryXmlSerializer.serializeUserState(userId: Int, userState: UserState) {
|
||||
with(persistence) { this@serializeUserState.serializeUserState(userId, userState) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.server.permission.access.appop
|
||||
|
||||
import android.util.Log
|
||||
import com.android.modules.utils.BinaryXmlPullParser
|
||||
import com.android.modules.utils.BinaryXmlSerializer
|
||||
import com.android.server.permission.access.UserState
|
||||
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
|
||||
import com.android.server.permission.access.util.attributeInterned
|
||||
import com.android.server.permission.access.util.forEachTag
|
||||
import com.android.server.permission.access.util.getAttributeValueOrThrow
|
||||
import com.android.server.permission.access.util.tag
|
||||
import com.android.server.permission.access.util.tagName
|
||||
|
||||
class PackageAppOpPersistence : BaseAppOpPersistence() {
|
||||
override fun BinaryXmlPullParser.parseUserState(userId: Int, userState: UserState) {
|
||||
when (tagName) {
|
||||
TAG_PACKAGE_APP_OPS -> parsePackageAppOps(userState)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
private fun BinaryXmlPullParser.parsePackageAppOps(userState: UserState) {
|
||||
forEachTag {
|
||||
when (tagName) {
|
||||
TAG_PACKAGE -> parsePackage(userState)
|
||||
else -> Log.w(LOG_TAG, "Ignoring unknown tag $name when parsing app-op state")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun BinaryXmlPullParser.parsePackage(userState: UserState) {
|
||||
val packageName = getAttributeValueOrThrow(ATTR_NAME).intern()
|
||||
val appOpModes = IndexedMap<String, Int>()
|
||||
userState.packageAppOpModes[packageName] = appOpModes
|
||||
parseAppOps(appOpModes)
|
||||
}
|
||||
|
||||
override fun BinaryXmlSerializer.serializeUserState(userId: Int, userState: UserState) {
|
||||
serializePackageAppOps(userState)
|
||||
}
|
||||
|
||||
private fun BinaryXmlSerializer.serializePackageAppOps(userState: UserState) {
|
||||
tag(TAG_PACKAGE_APP_OPS) {
|
||||
userState.packageAppOpModes.forEachIndexed { _, packageName, appOpModes ->
|
||||
serializePackage(packageName, appOpModes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun BinaryXmlSerializer.serializePackage(
|
||||
packageName: String,
|
||||
appOpModes: IndexedMap<String, Int>
|
||||
) {
|
||||
tag(TAG_PACKAGE) {
|
||||
attributeInterned(ATTR_NAME, packageName)
|
||||
serializeAppOps(appOpModes)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG_TAG = PackageAppOpPersistence::class.java.simpleName
|
||||
|
||||
private const val TAG_PACKAGE_APP_OPS = "package-app-ops"
|
||||
private const val TAG_PACKAGE = "package"
|
||||
|
||||
private const val ATTR_NAME = "name"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.server.permission.access.appop
|
||||
|
||||
import com.android.server.permission.access.AccessState
|
||||
import com.android.server.permission.access.AccessUri
|
||||
import com.android.server.permission.access.AppOpUri
|
||||
import com.android.server.permission.access.PackageUri
|
||||
import com.android.server.permission.access.UserState
|
||||
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
|
||||
import com.android.server.permission.access.external.PackageState
|
||||
|
||||
class PackageAppOpPolicy : BaseAppOpPolicy(PackageAppOpPersistence()) {
|
||||
override val subjectScheme: String
|
||||
get() = PackageUri.SCHEME
|
||||
|
||||
override val objectScheme: String
|
||||
get() = AppOpUri.SCHEME
|
||||
|
||||
override fun getModes(subject: AccessUri, state: AccessState): IndexedMap<String, Int>? {
|
||||
subject as PackageUri
|
||||
return state.userStates[subject.userId]?.packageAppOpModes?.get(subject.packageName)
|
||||
}
|
||||
|
||||
override fun getOrCreateModes(subject: AccessUri, state: AccessState): IndexedMap<String, Int> {
|
||||
subject as PackageUri
|
||||
return state.userStates.getOrPut(subject.userId) { UserState() }
|
||||
.packageAppOpModes.getOrPut(subject.packageName) { IndexedMap() }
|
||||
}
|
||||
|
||||
override fun removeModes(subject: AccessUri, state: AccessState) {
|
||||
subject as PackageUri
|
||||
state.userStates[subject.userId]?.packageAppOpModes?.remove(subject.packageName)
|
||||
}
|
||||
|
||||
override fun onPackageRemoved(
|
||||
packageState: PackageState,
|
||||
oldState: AccessState,
|
||||
newState: AccessState
|
||||
) {
|
||||
newState.userStates.forEachIndexed { _, _, userState ->
|
||||
userState.packageAppOpModes -= packageState.packageName
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.server.permission.access.appop
|
||||
|
||||
import android.util.Log
|
||||
import com.android.modules.utils.BinaryXmlPullParser
|
||||
import com.android.modules.utils.BinaryXmlSerializer
|
||||
import com.android.server.permission.access.UserState
|
||||
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
|
||||
import com.android.server.permission.access.util.attributeInt
|
||||
import com.android.server.permission.access.util.forEachTag
|
||||
import com.android.server.permission.access.util.getAttributeIntOrThrow
|
||||
import com.android.server.permission.access.util.tag
|
||||
import com.android.server.permission.access.util.tagName
|
||||
|
||||
class UidAppOpPersistence : BaseAppOpPersistence() {
|
||||
override fun BinaryXmlPullParser.parseUserState(userId: Int, userState: UserState) {
|
||||
when (tagName) {
|
||||
TAG_UID_APP_OPS -> parseUidAppOps(userState)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
private fun BinaryXmlPullParser.parseUidAppOps(userState: UserState) {
|
||||
forEachTag {
|
||||
when (tagName) {
|
||||
TAG_UID -> parseUid(userState)
|
||||
else -> Log.w(LOG_TAG, "Ignoring unknown tag $name when parsing app-op state")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun BinaryXmlPullParser.parseUid(userState: UserState) {
|
||||
val uid = getAttributeIntOrThrow(ATTR_UID)
|
||||
val appOpModes = IndexedMap<String, Int>()
|
||||
userState.uidAppOpModes[uid] = appOpModes
|
||||
parseAppOps(appOpModes)
|
||||
}
|
||||
|
||||
override fun BinaryXmlSerializer.serializeUserState(userId: Int, userState: UserState) {
|
||||
serializeUidAppOps(userState)
|
||||
}
|
||||
|
||||
private fun BinaryXmlSerializer.serializeUidAppOps(userState: UserState) {
|
||||
tag(TAG_UID_APP_OPS) {
|
||||
userState.uidAppOpModes.forEachIndexed { _, uid, appOpModes ->
|
||||
serializeUid(uid, appOpModes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun BinaryXmlSerializer.serializeUid(uid: Int, appOpModes: IndexedMap<String, Int>) {
|
||||
tag(TAG_UID) {
|
||||
attributeInt(ATTR_UID, uid)
|
||||
serializeAppOps(appOpModes)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG_TAG = UidAppOpPersistence::class.java.simpleName
|
||||
|
||||
private const val TAG_UID_APP_OPS = "uid-app-ops"
|
||||
private const val TAG_UID = "uid"
|
||||
|
||||
private const val ATTR_UID = "uid"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.server.permission.access.appop
|
||||
|
||||
import com.android.server.permission.access.AccessState
|
||||
import com.android.server.permission.access.AccessUri
|
||||
import com.android.server.permission.access.AppOpUri
|
||||
import com.android.server.permission.access.UidUri
|
||||
import com.android.server.permission.access.UserState
|
||||
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
|
||||
|
||||
class UidAppOpPolicy : BaseAppOpPolicy(UidAppOpPersistence()) {
|
||||
override val subjectScheme: String
|
||||
get() = UidUri.SCHEME
|
||||
|
||||
override val objectScheme: String
|
||||
get() = AppOpUri.SCHEME
|
||||
|
||||
override fun getModes(subject: AccessUri, state: AccessState): IndexedMap<String, Int>? {
|
||||
subject as UidUri
|
||||
return state.userStates[subject.userId]?.uidAppOpModes?.get(subject.appId)
|
||||
}
|
||||
|
||||
override fun getOrCreateModes(subject: AccessUri, state: AccessState): IndexedMap<String, Int> {
|
||||
subject as UidUri
|
||||
return state.userStates.getOrPut(subject.userId) { UserState() }
|
||||
.uidAppOpModes.getOrPut(subject.appId) { IndexedMap() }
|
||||
}
|
||||
|
||||
override fun removeModes(subject: AccessUri, state: AccessState) {
|
||||
subject as UidUri
|
||||
state.userStates[subject.userId]?.uidAppOpModes?.remove(subject.appId)
|
||||
}
|
||||
|
||||
override fun onAppIdRemoved(appId: Int, oldState: AccessState, newState: AccessState) {
|
||||
newState.userStates.forEachIndexed { _, _, userState ->
|
||||
userState.uidAppOpModes -= appId
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.server.permission.access.collection
|
||||
|
||||
typealias IndexedList<T> = ArrayList<T>
|
||||
|
||||
inline fun <T> IndexedList<T>.allIndexed(predicate: (Int, T) -> Boolean): Boolean {
|
||||
for (index in 0 until size) {
|
||||
if (!predicate(index, this[index])) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
inline fun <T> IndexedList<T>.anyIndexed(predicate: (Int, T) -> Boolean): Boolean {
|
||||
for (index in 0 until size) {
|
||||
if (predicate(index, this[index])) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun <T> IndexedList<T>.copy(): IndexedList<T> = IndexedList(this)
|
||||
|
||||
inline fun <T> IndexedList<T>.forEachIndexed(action: (Int, T) -> Unit) {
|
||||
for (index in indices) {
|
||||
action(index, this[index])
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline operator fun <T> IndexedList<T>.minus(element: T): IndexedList<T> =
|
||||
copy().apply { this -= element }
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline operator fun <T> IndexedList<T>.minusAssign(element: T) {
|
||||
remove(element)
|
||||
}
|
||||
|
||||
inline fun <T> IndexedList<T>.noneIndexed(predicate: (Int, T) -> Boolean): Boolean {
|
||||
for (index in 0 until size) {
|
||||
if (predicate(index, this[index])) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline operator fun <T> IndexedList<T>.plus(element: T): IndexedList<T> =
|
||||
copy().apply { this += element }
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline operator fun <T> IndexedList<T>.plusAssign(element: T) {
|
||||
add(element)
|
||||
}
|
||||
|
||||
inline fun <T> IndexedList<T>.removeAllIndexed(predicate: (Int, T) -> Boolean) {
|
||||
for (index in lastIndex downTo 0) {
|
||||
if (predicate(index, this[index])) {
|
||||
removeAt(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T> IndexedList<T>.retainAllIndexed(predicate: (Int, T) -> Boolean) {
|
||||
for (index in lastIndex downTo 0) {
|
||||
if (!predicate(index, this[index])) {
|
||||
removeAt(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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.server.permission.access.collection
|
||||
|
||||
class IndexedListSet<T> private constructor(
|
||||
private val list: ArrayList<T>
|
||||
) : MutableSet<T> {
|
||||
constructor() : this(ArrayList())
|
||||
|
||||
override val size: Int
|
||||
get() = list.size
|
||||
|
||||
override fun contains(element: T): Boolean = list.contains(element)
|
||||
|
||||
override fun isEmpty(): Boolean = list.isEmpty()
|
||||
|
||||
override fun iterator(): MutableIterator<T> = list.iterator()
|
||||
|
||||
override fun containsAll(elements: Collection<T>): Boolean {
|
||||
throw NotImplementedError()
|
||||
}
|
||||
|
||||
fun elementAt(index: Int): T = list[index]
|
||||
|
||||
fun indexOf(element: T): Int = list.indexOf(element)
|
||||
|
||||
override fun add(element: T): Boolean =
|
||||
if (list.contains(element)) {
|
||||
false
|
||||
} else {
|
||||
list.add(element)
|
||||
true
|
||||
}
|
||||
|
||||
override fun remove(element: T): Boolean = list.remove(element)
|
||||
|
||||
override fun clear() {
|
||||
list.clear()
|
||||
}
|
||||
|
||||
override fun addAll(elements: Collection<T>): Boolean {
|
||||
throw NotImplementedError()
|
||||
}
|
||||
|
||||
override fun removeAll(elements: Collection<T>): Boolean {
|
||||
throw NotImplementedError()
|
||||
}
|
||||
|
||||
override fun retainAll(elements: Collection<T>): Boolean {
|
||||
throw NotImplementedError()
|
||||
}
|
||||
|
||||
fun removeAt(index: Int): T? = list.removeAt(index)
|
||||
|
||||
fun copy(): IndexedListSet<T> = IndexedListSet(ArrayList(list))
|
||||
}
|
||||
|
||||
inline fun <T> IndexedListSet<T>.allIndexed(predicate: (Int, T) -> Boolean): Boolean {
|
||||
for (index in 0 until size) {
|
||||
if (!predicate(index, elementAt(index))) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
inline fun <T> IndexedListSet<T>.anyIndexed(predicate: (Int, T) -> Boolean): Boolean {
|
||||
for (index in 0 until size) {
|
||||
if (predicate(index, elementAt(index))) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
inline fun <T> IndexedListSet<T>.forEachIndexed(action: (Int, T) -> Unit) {
|
||||
for (index in indices) {
|
||||
action(index, elementAt(index))
|
||||
}
|
||||
}
|
||||
|
||||
inline val <T> IndexedListSet<T>.lastIndex: Int
|
||||
get() = size - 1
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline operator fun <T> IndexedListSet<T>.minus(element: T): IndexedListSet<T> =
|
||||
copy().apply { this -= element }
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline operator fun <T> IndexedListSet<T>.minusAssign(element: T) {
|
||||
remove(element)
|
||||
}
|
||||
|
||||
inline fun <T> IndexedListSet<T>.noneIndexed(predicate: (Int, T) -> Boolean): Boolean {
|
||||
for (index in 0 until size) {
|
||||
if (predicate(index, elementAt(index))) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline operator fun <T> IndexedListSet<T>.plus(element: T): IndexedListSet<T> =
|
||||
copy().apply { this += element }
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline operator fun <T> IndexedListSet<T>.plusAssign(element: T) {
|
||||
add(element)
|
||||
}
|
||||
|
||||
inline fun <T> IndexedListSet<T>.removeAllIndexed(predicate: (Int, T) -> Boolean) {
|
||||
for (index in lastIndex downTo 0) {
|
||||
if (predicate(index, elementAt(index))) {
|
||||
removeAt(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T> IndexedListSet<T>.retainAllIndexed(predicate: (Int, T) -> Boolean) {
|
||||
for (index in lastIndex downTo 0) {
|
||||
if (!predicate(index, elementAt(index))) {
|
||||
removeAt(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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.server.permission.access.collection
|
||||
|
||||
import android.util.ArrayMap
|
||||
|
||||
typealias IndexedMap<K, V> = ArrayMap<K, V>
|
||||
|
||||
inline fun <K, V> IndexedMap<K, V>.allIndexed(predicate: (Int, K, V) -> Boolean): Boolean {
|
||||
for (index in 0 until size) {
|
||||
if (!predicate(index, keyAt(index), valueAt(index))) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
inline fun <K, V> IndexedMap<K, V>.anyIndexed(predicate: (Int, K, V) -> Boolean): Boolean {
|
||||
for (index in 0 until size) {
|
||||
if (predicate(index, keyAt(index), valueAt(index))) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
inline fun <K, V> IndexedMap<K, V>.copy(copyValue: (V) -> V): IndexedMap<K, V> =
|
||||
IndexedMap(this).apply {
|
||||
forEachValueIndexed { index, value ->
|
||||
setValueAt(index, copyValue(value))
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <K, V, R> IndexedMap<K, V>.firstNotNullOfOrNullIndexed(transform: (Int, K, V) -> R): R? {
|
||||
for (index in 0 until size) {
|
||||
transform(index, keyAt(index), valueAt(index))?.let { return it }
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
inline fun <K, V> IndexedMap<K, V>.forEachIndexed(action: (Int, K, V) -> Unit) {
|
||||
for (index in 0 until size) {
|
||||
action(index, keyAt(index), valueAt(index))
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <K, V> IndexedMap<K, V>.forEachKeyIndexed(action: (Int, K) -> Unit) {
|
||||
for (index in 0 until size) {
|
||||
action(index, keyAt(index))
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <K, V> IndexedMap<K, V>.forEachValueIndexed(action: (Int, V) -> Unit) {
|
||||
for (index in 0 until size) {
|
||||
action(index, valueAt(index))
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <K, V> IndexedMap<K, V>.getOrPut(key: K, defaultValue: () -> V): V {
|
||||
get(key)?.let { return it }
|
||||
return defaultValue().also { put(key, it) }
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun <K, V> IndexedMap<K, V>?.getWithDefault(key: K, defaultValue: V): V {
|
||||
this ?: return defaultValue
|
||||
val index = indexOfKey(key)
|
||||
return if (index >= 0) valueAt(index) else defaultValue
|
||||
}
|
||||
|
||||
inline val <K, V> IndexedMap<K, V>.lastIndex: Int
|
||||
get() = size - 1
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline operator fun <K, V> IndexedMap<K, V>.minusAssign(key: K) {
|
||||
remove(key)
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun <K, V> IndexedMap<K, V>.putWithDefault(key: K, value: V, defaultValue: V): V {
|
||||
val index = indexOfKey(key)
|
||||
if (index >= 0) {
|
||||
val oldValue = valueAt(index)
|
||||
if (value != oldValue) {
|
||||
if (value == defaultValue) {
|
||||
removeAt(index)
|
||||
} else {
|
||||
setValueAt(index, value)
|
||||
}
|
||||
}
|
||||
return oldValue
|
||||
} else {
|
||||
if (value != defaultValue) {
|
||||
put(key, value)
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <K, V> IndexedMap<K, V>.removeAllIndexed(predicate: (Int, K, V) -> Boolean) {
|
||||
for (index in lastIndex downTo 0) {
|
||||
if (predicate(index, keyAt(index), valueAt(index))) {
|
||||
removeAt(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <K, V> IndexedMap<K, V>.retainAllIndexed(predicate: (Int, K, V) -> Boolean) {
|
||||
for (index in lastIndex downTo 0) {
|
||||
if (!predicate(index, keyAt(index), valueAt(index))) {
|
||||
removeAt(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline operator fun <K, V> IndexedMap<K, V>.set(key: K, value: V) {
|
||||
put(key, value)
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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.server.permission.access.collection
|
||||
|
||||
import android.util.ArraySet
|
||||
|
||||
typealias IndexedSet<T> = ArraySet<T>
|
||||
|
||||
inline fun <T> IndexedSet<T>.allIndexed(predicate: (Int, T) -> Boolean): Boolean {
|
||||
for (index in 0 until size) {
|
||||
if (!predicate(index, elementAt(index))) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
inline fun <T> IndexedSet<T>.anyIndexed(predicate: (Int, T) -> Boolean): Boolean {
|
||||
for (index in 0 until size) {
|
||||
if (predicate(index, elementAt(index))) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun <T> IndexedSet<T>.copy(): IndexedSet<T> = IndexedSet(this)
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun <T> IndexedSet<T>.elementAt(index: Int): T = valueAt(index)
|
||||
|
||||
inline fun <T> IndexedSet<T>.forEachIndexed(action: (Int, T) -> Unit) {
|
||||
for (index in indices) {
|
||||
action(index, elementAt(index))
|
||||
}
|
||||
}
|
||||
|
||||
inline val <T> IndexedSet<T>.lastIndex: Int
|
||||
get() = size - 1
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline operator fun <T> IndexedSet<T>.minus(element: T): IndexedSet<T> =
|
||||
copy().apply { this -= element }
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline operator fun <T> IndexedSet<T>.minusAssign(element: T) {
|
||||
remove(element)
|
||||
}
|
||||
|
||||
inline fun <T> IndexedSet<T>.noneIndexed(predicate: (Int, T) -> Boolean): Boolean {
|
||||
for (index in 0 until size) {
|
||||
if (predicate(index, elementAt(index))) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline operator fun <T> IndexedSet<T>.plus(element: T): IndexedSet<T> =
|
||||
copy().apply { this += element }
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline operator fun <T> IndexedSet<T>.plusAssign(element: T) {
|
||||
add(element)
|
||||
}
|
||||
|
||||
inline fun <T> IndexedSet<T>.removeAllIndexed(predicate: (Int, T) -> Boolean) {
|
||||
for (index in lastIndex downTo 0) {
|
||||
if (predicate(index, elementAt(index))) {
|
||||
removeAt(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T> IndexedSet<T>.retainAllIndexed(predicate: (Int, T) -> Boolean) {
|
||||
for (index in lastIndex downTo 0) {
|
||||
if (!predicate(index, elementAt(index))) {
|
||||
removeAt(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun <T> indexedSetOf(vararg elements: T): IndexedSet<T> = IndexedSet(elements.asList())
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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.server.permission.access.collection
|
||||
|
||||
import android.util.SparseArray
|
||||
|
||||
typealias IntMap<T> = SparseArray<T>
|
||||
|
||||
inline fun <T> IntMap<T>.allIndexed(predicate: (Int, Int, T) -> Boolean): Boolean {
|
||||
for (index in 0 until size) {
|
||||
if (!predicate(index, keyAt(index), valueAt(index))) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
inline fun <T> IntMap<T>.anyIndexed(predicate: (Int, Int, T) -> Boolean): Boolean {
|
||||
for (index in 0 until size) {
|
||||
if (predicate(index, keyAt(index), valueAt(index))) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
inline fun <T> IntMap<T>.copy(copyValue: (T) -> T): IntMap<T> =
|
||||
this.clone().apply {
|
||||
forEachValueIndexed { index, value ->
|
||||
setValueAt(index, copyValue(value))
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T, R> IntMap<T>.firstNotNullOfOrNullIndexed(transform: (Int, Int, T) -> R): R? {
|
||||
for (index in 0 until size) {
|
||||
transform(index, keyAt(index), valueAt(index))?.let { return it }
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
inline fun <T> IntMap<T>.forEachIndexed(action: (Int, Int, T) -> Unit) {
|
||||
for (index in 0 until size) {
|
||||
action(index, keyAt(index), valueAt(index))
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T> IntMap<T>.forEachKeyIndexed(action: (Int, Int) -> Unit) {
|
||||
for (index in 0 until size) {
|
||||
action(index, keyAt(index))
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T> IntMap<T>.forEachValueIndexed(action: (Int, T) -> Unit) {
|
||||
for (index in 0 until size) {
|
||||
action(index, valueAt(index))
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T> IntMap<T>.getOrPut(key: Int, defaultValue: () -> T): T {
|
||||
get(key)?.let { return it }
|
||||
return defaultValue().also { put(key, it) }
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun <T> IntMap<T>?.getWithDefault(key: Int, defaultValue: T): T {
|
||||
this ?: return defaultValue
|
||||
val index = indexOfKey(key)
|
||||
return if (index >= 0) valueAt(index) else defaultValue
|
||||
}
|
||||
|
||||
inline val <T> IntMap<T>.lastIndex: Int
|
||||
get() = size - 1
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline operator fun <T> IntMap<T>.minusAssign(key: Int) {
|
||||
remove(key)
|
||||
}
|
||||
|
||||
inline fun <T> IntMap<T>.noneIndexed(predicate: (Int, Int, T) -> Boolean): Boolean {
|
||||
for (index in 0 until size) {
|
||||
if (predicate(index, keyAt(index), valueAt(index))) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun <T> IntMap<T>.putWithDefault(key: Int, value: T, defaultValue: T): T {
|
||||
val index = indexOfKey(key)
|
||||
if (index >= 0) {
|
||||
val oldValue = valueAt(index)
|
||||
if (value != oldValue) {
|
||||
if (value == defaultValue) {
|
||||
removeAt(index)
|
||||
} else {
|
||||
setValueAt(index, value)
|
||||
}
|
||||
}
|
||||
return oldValue
|
||||
} else {
|
||||
if (value != defaultValue) {
|
||||
put(key, value)
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T> IntMap<T>.removeAllIndexed(predicate: (Int, Int, T) -> Boolean) {
|
||||
for (index in lastIndex downTo 0) {
|
||||
if (predicate(index, keyAt(index), valueAt(index))) {
|
||||
removeAt(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T> IntMap<T>.retainAllIndexed(predicate: (Int, Int, T) -> Boolean) {
|
||||
for (index in lastIndex downTo 0) {
|
||||
if (!predicate(index, keyAt(index), valueAt(index))) {
|
||||
removeAt(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline val <T> IntMap<T>.size: Int
|
||||
get() = size()
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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.server.permission.access.collection
|
||||
|
||||
import android.util.SparseBooleanArray
|
||||
|
||||
class IntSet private constructor(
|
||||
private val array: SparseBooleanArray
|
||||
) {
|
||||
constructor() : this(SparseBooleanArray())
|
||||
|
||||
val size: Int
|
||||
get() = array.size()
|
||||
|
||||
operator fun contains(element: Int): Boolean = array[element]
|
||||
|
||||
fun elementAt(index: Int): Int = array.keyAt(index)
|
||||
|
||||
fun indexOf(element: Int): Int = array.indexOfKey(element)
|
||||
|
||||
fun add(element: Int) {
|
||||
array.put(element, true)
|
||||
}
|
||||
|
||||
fun remove(element: Int) {
|
||||
array.delete(element)
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
array.clear()
|
||||
}
|
||||
|
||||
fun removeAt(index: Int) {
|
||||
array.removeAt(index)
|
||||
}
|
||||
|
||||
fun copy(): IntSet = IntSet(array.clone())
|
||||
}
|
||||
|
||||
inline fun IntSet.allIndexed(predicate: (Int, Int) -> Boolean): Boolean {
|
||||
for (index in 0 until size) {
|
||||
if (!predicate(index, elementAt(index))) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
inline fun IntSet.anyIndexed(predicate: (Int, Int) -> Boolean): Boolean {
|
||||
for (index in 0 until size) {
|
||||
if (predicate(index, elementAt(index))) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
inline fun IntSet.forEachIndexed(action: (Int, Int) -> Unit) {
|
||||
for (index in 0 until size) {
|
||||
action(index, elementAt(index))
|
||||
}
|
||||
}
|
||||
|
||||
inline val IntSet.lastIndex: Int
|
||||
get() = size - 1
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline operator fun IntSet.minus(element: Int): IntSet = copy().apply { this -= element }
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline operator fun IntSet.minusAssign(element: Int) {
|
||||
remove(element)
|
||||
}
|
||||
|
||||
inline fun IntSet.noneIndexed(predicate: (Int, Int) -> Boolean): Boolean {
|
||||
for (index in 0 until size) {
|
||||
if (predicate(index, elementAt(index))) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline operator fun IntSet.plus(element: Int): IntSet = copy().apply { this += element }
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline operator fun IntSet.plusAssign(element: Int) {
|
||||
add(element)
|
||||
}
|
||||
|
||||
inline fun IntSet.removeAllIndexed(predicate: (Int, Int) -> Boolean) {
|
||||
for (index in lastIndex downTo 0) {
|
||||
if (predicate(index, elementAt(index))) {
|
||||
removeAt(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun IntSet.retainAllIndexed(predicate: (Int, Int) -> Boolean) {
|
||||
for (index in lastIndex downTo 0) {
|
||||
if (!predicate(index, elementAt(index))) {
|
||||
removeAt(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.server.permission.access.collection
|
||||
|
||||
inline fun <T> List<T>.allIndexed(predicate: (Int, T) -> Boolean): Boolean {
|
||||
for (index in 0 until size) {
|
||||
if (!predicate(index, this[index])) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
inline fun <T> List<T>.anyIndexed(predicate: (Int, T) -> Boolean): Boolean {
|
||||
for (index in 0 until size) {
|
||||
if (predicate(index, this[index])) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
inline fun <T> List<T>.forEachIndexed(action: (Int, T) -> Unit) {
|
||||
for (index in indices) {
|
||||
action(index, this[index])
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T> List<T>.noneIndexed(predicate: (Int, T) -> Boolean): Boolean {
|
||||
for (index in 0 until size) {
|
||||
if (predicate(index, this[index])) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
inline fun <T> MutableList<T>.removeAllIndexed(predicate: (Int, T) -> Boolean) {
|
||||
for (index in lastIndex downTo 0) {
|
||||
if (predicate(index, this[index])) {
|
||||
removeAt(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T> MutableList<T>.retainAllIndexed(predicate: (Int, T) -> Boolean) {
|
||||
for (index in lastIndex downTo 0) {
|
||||
if (!predicate(index, this[index])) {
|
||||
removeAt(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.server.permission.access.data
|
||||
|
||||
import com.android.server.permission.access.external.AndroidPackage
|
||||
|
||||
class Package(
|
||||
private val androidPackage: AndroidPackage
|
||||
) {
|
||||
val name: String
|
||||
get() = androidPackage.packageName
|
||||
|
||||
val adoptPermissions: List<String>
|
||||
get() = androidPackage.adoptPermissions
|
||||
|
||||
val appId: Int
|
||||
get() = androidPackage.appId
|
||||
|
||||
val requestedPermissions: List<String>
|
||||
get() = androidPackage.requestedPermissions
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
throw NotImplementedError()
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
throw NotImplementedError()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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.server.permission.access.data
|
||||
|
||||
import android.content.pm.PermissionInfo
|
||||
import com.android.server.permission.access.util.hasBits
|
||||
|
||||
data class Permission(
|
||||
val permissionInfo: PermissionInfo,
|
||||
val isReconciled: Boolean,
|
||||
val type: Int,
|
||||
val appId: Int
|
||||
) {
|
||||
inline val name: String
|
||||
get() = permissionInfo.name
|
||||
|
||||
inline val packageName: String
|
||||
get() = permissionInfo.packageName
|
||||
|
||||
inline val isDynamic: Boolean
|
||||
get() = type == TYPE_DYNAMIC
|
||||
|
||||
inline val isNormal: Boolean
|
||||
get() = permissionInfo.protection == PermissionInfo.PROTECTION_NORMAL
|
||||
|
||||
inline val isRuntime: Boolean
|
||||
get() = permissionInfo.protection == PermissionInfo.PROTECTION_DANGEROUS
|
||||
|
||||
inline val isSoftRestricted: Boolean
|
||||
get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.FLAG_SOFT_RESTRICTED)
|
||||
|
||||
inline val isHardRestricted: Boolean
|
||||
get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.FLAG_HARD_RESTRICTED)
|
||||
|
||||
inline val isSignature: Boolean
|
||||
get() = permissionInfo.protection == PermissionInfo.PROTECTION_SIGNATURE
|
||||
|
||||
inline val isInternal: Boolean
|
||||
get() = permissionInfo.protection == PermissionInfo.PROTECTION_INTERNAL
|
||||
|
||||
inline val isDevelopment: Boolean
|
||||
get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_DEVELOPMENT)
|
||||
|
||||
inline val isInstaller: Boolean
|
||||
get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_INSTALLER)
|
||||
|
||||
inline val isOem: Boolean
|
||||
get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_OEM)
|
||||
|
||||
inline val isPre23: Boolean
|
||||
get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_PRE23)
|
||||
|
||||
inline val isPreInstalled: Boolean
|
||||
get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_PREINSTALLED)
|
||||
|
||||
inline val isPrivileged: Boolean
|
||||
get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_PRIVILEGED)
|
||||
|
||||
inline val isSetup: Boolean
|
||||
get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_SETUP)
|
||||
|
||||
inline val isVerifier: Boolean
|
||||
get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_VERIFIER)
|
||||
|
||||
inline val isVendorPrivileged: Boolean
|
||||
get() = permissionInfo.protectionFlags
|
||||
.hasBits(PROTECTION_FLAG_VENDOR_PRIVILEGED)
|
||||
|
||||
inline val isSystemTextClassifier: Boolean
|
||||
get() = permissionInfo.protectionFlags
|
||||
.hasBits(PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER)
|
||||
|
||||
inline val isConfigurator: Boolean
|
||||
get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_CONFIGURATOR)
|
||||
|
||||
inline val isIncidentReportApprover: Boolean
|
||||
get() = permissionInfo.protectionFlags
|
||||
.hasBits(PermissionInfo.PROTECTION_FLAG_INCIDENT_REPORT_APPROVER)
|
||||
|
||||
inline val isAppPredictor: Boolean
|
||||
get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR)
|
||||
|
||||
inline val isCompanion: Boolean
|
||||
get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_COMPANION)
|
||||
|
||||
inline val isRetailDemo: Boolean
|
||||
get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO)
|
||||
|
||||
inline val isRecents: Boolean
|
||||
get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_RECENTS)
|
||||
|
||||
inline val isRole: Boolean
|
||||
get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_ROLE)
|
||||
|
||||
inline val isKnownSigner: Boolean
|
||||
get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_KNOWN_SIGNER)
|
||||
|
||||
inline val protectionLevel: Int
|
||||
@Suppress("DEPRECATION")
|
||||
get() = permissionInfo.protectionLevel
|
||||
|
||||
inline val knownCerts: Set<String>
|
||||
get() = permissionInfo.knownCerts
|
||||
|
||||
companion object {
|
||||
// The permission is defined in an application manifest.
|
||||
const val TYPE_MANIFEST = 0
|
||||
// The permission is defined in a system config.
|
||||
const val TYPE_CONFIG = 1
|
||||
// The permission is defined dynamically.
|
||||
const val TYPE_DYNAMIC = 2
|
||||
|
||||
// TODO: PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED is a testApi
|
||||
const val PROTECTION_FLAG_VENDOR_PRIVILEGED = 0x8000
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.server.permission.access.external
|
||||
|
||||
class CompatibilityPermissionInfo {
|
||||
companion object {
|
||||
val COMPAT_PERMS = arrayOf(CompatibilityPermissionInfo())
|
||||
}
|
||||
|
||||
val name: String
|
||||
get() = throw NotImplementedError()
|
||||
|
||||
val sdkVersion: Int
|
||||
get() = throw NotImplementedError()
|
||||
}
|
||||
34
services/permission/java/com/android/server/permission/access/external/KnownPackages.kt
vendored
Normal file
34
services/permission/java/com/android/server/permission/access/external/KnownPackages.kt
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.server.permission.access.external
|
||||
|
||||
class KnownPackages {
|
||||
companion object {
|
||||
const val PACKAGE_SYSTEM = 0
|
||||
const val PACKAGE_SETUP_WIZARD = 1
|
||||
const val PACKAGE_INSTALLER = 2
|
||||
const val PACKAGE_VERIFIER = 4
|
||||
const val PACKAGE_SYSTEM_TEXT_CLASSIFIER = 6
|
||||
const val PACKAGE_PERMISSION_CONTROLLER = 7
|
||||
const val PACKAGE_CONFIGURATOR = 10
|
||||
const val PACKAGE_INCIDENT_REPORT_APPROVER = 11
|
||||
const val PACKAGE_APP_PREDICTOR = 12
|
||||
const val PACKAGE_COMPANION = 15
|
||||
const val PACKAGE_RETAIL_DEMO = 16
|
||||
const val PACKAGE_RECENTS = 17
|
||||
}
|
||||
}
|
||||
33
services/permission/java/com/android/server/permission/access/external/PackageInfoUtils.kt
vendored
Normal file
33
services/permission/java/com/android/server/permission/access/external/PackageInfoUtils.kt
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.server.permission.access.external
|
||||
|
||||
import android.content.pm.PermissionGroupInfo
|
||||
import android.content.pm.PermissionInfo
|
||||
|
||||
object PackageInfoUtils {
|
||||
fun generatePermissionInfo(parsedPermission: ParsedPermission, flags: Long): PermissionInfo {
|
||||
throw NotImplementedError()
|
||||
}
|
||||
|
||||
fun generatePermissionGroupInfo(
|
||||
parsedPermissionGroup: ParsedPermissionGroup,
|
||||
flags: Long
|
||||
): PermissionGroupInfo {
|
||||
throw NotImplementedError()
|
||||
}
|
||||
}
|
||||
65
services/permission/java/com/android/server/permission/access/external/PackageState.kt
vendored
Normal file
65
services/permission/java/com/android/server/permission/access/external/PackageState.kt
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.server.permission.access.external
|
||||
|
||||
import android.util.SparseArray
|
||||
|
||||
interface PackageState {
|
||||
val androidPackage: AndroidPackage?
|
||||
val appId: Int
|
||||
val isSystem: Boolean
|
||||
val isUpdatedSystemApp: Boolean
|
||||
val packageName: String
|
||||
val userStates: SparseArray<PackageUserState>
|
||||
val hasSharedUser: Boolean
|
||||
val sharedUserAppId: Int
|
||||
val signingDetails: SigningDetails
|
||||
}
|
||||
|
||||
interface AndroidPackage {
|
||||
val packageName: String
|
||||
val apexModuleName: String?
|
||||
val appId: Int
|
||||
val isPrivileged: Boolean
|
||||
val isOem: Boolean
|
||||
val isVendor: Boolean
|
||||
val isProduct: Boolean
|
||||
val isSystemExt: Boolean
|
||||
val targetSdkVersion: Int
|
||||
val adoptPermissions: List<String>
|
||||
val permissions: List<ParsedPermission>
|
||||
val permissionGroups: List<ParsedPermissionGroup>
|
||||
val requestedPermissions: List<String>
|
||||
val implicitPermissions: List<String>
|
||||
}
|
||||
|
||||
interface ParsedPermission {
|
||||
val name: String
|
||||
val isTree: Boolean
|
||||
val packageName: String
|
||||
val isSignature: Boolean
|
||||
val protectionLevel: Int
|
||||
}
|
||||
|
||||
interface ParsedPermissionGroup {
|
||||
val name: String
|
||||
val packageName: String
|
||||
}
|
||||
|
||||
interface PackageUserState {
|
||||
val isInstantApp: Boolean
|
||||
}
|
||||
24
services/permission/java/com/android/server/permission/access/external/RoSystemProperties.kt
vendored
Normal file
24
services/permission/java/com/android/server/permission/access/external/RoSystemProperties.kt
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.server.permission.access.external
|
||||
|
||||
class RoSystemProperties {
|
||||
companion object {
|
||||
const val CONTROL_PRIVAPP_PERMISSIONS_DISABLE = false
|
||||
const val CONTROL_PRIVAPP_PERMISSIONS_ENFORCE = false
|
||||
}
|
||||
}
|
||||
42
services/permission/java/com/android/server/permission/access/external/SigningDetails.kt
vendored
Normal file
42
services/permission/java/com/android/server/permission/access/external/SigningDetails.kt
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.server.permission.access.external
|
||||
|
||||
object SigningDetails {
|
||||
fun hasCommonSignerWithCapability(otherDetails: SigningDetails, flags: Int): Boolean {
|
||||
throw NotImplementedError()
|
||||
}
|
||||
|
||||
fun hasAncestorOrSelf(oldDetails: SigningDetails): Boolean {
|
||||
throw NotImplementedError()
|
||||
}
|
||||
|
||||
fun checkCapability(oldDetails: SigningDetails, flags: Int): Boolean {
|
||||
throw NotImplementedError()
|
||||
}
|
||||
|
||||
fun hasAncestorOrSelfWithDigest(certDigests: Set<String>): Boolean {
|
||||
throw NotImplementedError()
|
||||
}
|
||||
|
||||
class CertCapabilities {
|
||||
companion object {
|
||||
/** grant SIGNATURE permissions to pkgs with this cert */
|
||||
var PERMISSION = 4
|
||||
}
|
||||
}
|
||||
}
|
||||
31
services/permission/java/com/android/server/permission/access/external/UserHandle.kt
vendored
Normal file
31
services/permission/java/com/android/server/permission/access/external/UserHandle.kt
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.server.permission.access.external
|
||||
|
||||
interface UserHandle {
|
||||
companion object {
|
||||
fun getAppId(uid: Int): Int {
|
||||
throw NotImplementedError()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object UserHandleCompat {
|
||||
fun getUserId(uid: Int): Int {
|
||||
throw NotImplementedError()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.server.permission.access.permission
|
||||
|
||||
object PermissionFlags {
|
||||
const val INSTALL_GRANTED = 1 shl 0
|
||||
const val INSTALL_REVOKED = 1 shl 1
|
||||
const val PROTECTION_GRANTED = 1 shl 2
|
||||
const val ROLE_GRANTED = 1 shl 3
|
||||
// For permissions that are granted in other ways,
|
||||
// ex: via an API or implicit permissions that inherit from granted install permissions
|
||||
const val OTHER_GRANTED = 1 shl 4
|
||||
// For the permissions that are implicit for the package
|
||||
const val IMPLICIT = 1 shl 5
|
||||
|
||||
const val MASK_GRANTED = INSTALL_GRANTED or PROTECTION_GRANTED or OTHER_GRANTED or ROLE_GRANTED
|
||||
const val MASK_RUNTIME = OTHER_GRANTED or IMPLICIT
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.server.permission.access.permission
|
||||
|
||||
import android.content.pm.PermissionInfo
|
||||
import android.util.Log
|
||||
import com.android.modules.utils.BinaryXmlPullParser
|
||||
import com.android.modules.utils.BinaryXmlSerializer
|
||||
import com.android.server.permission.access.SystemState
|
||||
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
|
||||
import com.android.server.permission.access.data.Permission
|
||||
import com.android.server.permission.access.util.attribute
|
||||
import com.android.server.permission.access.util.attributeInt
|
||||
import com.android.server.permission.access.util.attributeIntHex
|
||||
import com.android.server.permission.access.util.attributeIntHexWithDefault
|
||||
import com.android.server.permission.access.util.attributeInterned
|
||||
import com.android.server.permission.access.util.forEachTag
|
||||
import com.android.server.permission.access.util.getAttributeIntHexOrDefault
|
||||
import com.android.server.permission.access.util.getAttributeIntHexOrThrow
|
||||
import com.android.server.permission.access.util.getAttributeIntOrThrow
|
||||
import com.android.server.permission.access.util.getAttributeValue
|
||||
import com.android.server.permission.access.util.getAttributeValueOrThrow
|
||||
import com.android.server.permission.access.util.tag
|
||||
import com.android.server.permission.access.util.tagName
|
||||
|
||||
class UidPermissionPersistence {
|
||||
fun BinaryXmlPullParser.parseSystemState(systemState: SystemState) {
|
||||
when (tagName) {
|
||||
TAG_PERMISSION_TREES -> parsePermissions(systemState.permissionTrees)
|
||||
TAG_PERMISSIONS -> parsePermissions(systemState.permissions)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
private fun BinaryXmlPullParser.parsePermissions(permissions: IndexedMap<String, Permission>) {
|
||||
forEachTag {
|
||||
when (val tagName = tagName) {
|
||||
TAG_PERMISSION -> parsePermission(permissions)
|
||||
else -> Log.w(LOG_TAG, "Ignoring unknown tag $tagName when parsing permissions")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun BinaryXmlPullParser.parsePermission(permissions: IndexedMap<String, Permission>) {
|
||||
val name = getAttributeValueOrThrow(ATTR_NAME).intern()
|
||||
@Suppress("DEPRECATION")
|
||||
val permissionInfo = PermissionInfo().apply {
|
||||
this.name = name
|
||||
packageName = getAttributeValueOrThrow(ATTR_PACKAGE_NAME).intern()
|
||||
protectionLevel = getAttributeIntHexOrThrow(ATTR_PROTECTION_LEVEL)
|
||||
}
|
||||
val type = getAttributeIntOrThrow(ATTR_TYPE)
|
||||
when (type) {
|
||||
Permission.TYPE_MANIFEST -> {}
|
||||
Permission.TYPE_CONFIG -> {
|
||||
Log.w(LOG_TAG, "Ignoring unexpected config permission $name")
|
||||
return
|
||||
}
|
||||
Permission.TYPE_DYNAMIC -> {
|
||||
permissionInfo.apply {
|
||||
icon = getAttributeIntHexOrDefault(ATTR_ICON, 0)
|
||||
nonLocalizedLabel = getAttributeValue(ATTR_LABEL)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
Log.w(LOG_TAG, "Ignoring permission $name with unknown type $type")
|
||||
return
|
||||
}
|
||||
}
|
||||
val permission = Permission(permissionInfo, false, type, 0)
|
||||
permissions[name] = permission
|
||||
}
|
||||
|
||||
fun BinaryXmlSerializer.serializeSystemState(systemState: SystemState) {
|
||||
serializePermissions(TAG_PERMISSION_TREES, systemState.permissionTrees)
|
||||
serializePermissions(TAG_PERMISSIONS, systemState.permissions)
|
||||
}
|
||||
|
||||
private fun BinaryXmlSerializer.serializePermissions(
|
||||
tagName: String,
|
||||
permissions: IndexedMap<String, Permission>
|
||||
) {
|
||||
tag(tagName) {
|
||||
permissions.forEachValueIndexed { _, it -> serializePermission(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun BinaryXmlSerializer.serializePermission(permission: Permission) {
|
||||
val type = permission.type
|
||||
when (type) {
|
||||
Permission.TYPE_MANIFEST, Permission.TYPE_DYNAMIC -> {}
|
||||
Permission.TYPE_CONFIG -> return
|
||||
else -> {
|
||||
Log.w(LOG_TAG, "Skipping serializing permission $name with unknown type $type")
|
||||
return
|
||||
}
|
||||
}
|
||||
tag(TAG_PERMISSION) {
|
||||
attributeInterned(ATTR_NAME, permission.name)
|
||||
attributeInterned(ATTR_PACKAGE_NAME, permission.packageName)
|
||||
attributeIntHex(ATTR_PROTECTION_LEVEL, permission.protectionLevel)
|
||||
attributeInt(ATTR_TYPE, type)
|
||||
if (type == Permission.TYPE_DYNAMIC) {
|
||||
val permissionInfo = permission.permissionInfo
|
||||
attributeIntHexWithDefault(ATTR_ICON, permissionInfo.icon, 0)
|
||||
permissionInfo.nonLocalizedLabel?.toString()?.let { attribute(ATTR_LABEL, it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG_TAG = UidPermissionPersistence::class.java.simpleName
|
||||
|
||||
private const val TAG_PERMISSION = "permission"
|
||||
private const val TAG_PERMISSION_TREES = "permission-trees"
|
||||
private const val TAG_PERMISSIONS = "permissions"
|
||||
|
||||
private const val ATTR_ICON = "icon"
|
||||
private const val ATTR_LABEL = "label"
|
||||
private const val ATTR_NAME = "name"
|
||||
private const val ATTR_PACKAGE_NAME = "packageName"
|
||||
private const val ATTR_PROTECTION_LEVEL = "protectionLevel"
|
||||
private const val ATTR_TYPE = "type"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,909 @@
|
||||
/*
|
||||
* 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.server.permission.access.permission
|
||||
|
||||
import android.Manifest
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.PermissionInfo
|
||||
import android.os.Build
|
||||
import android.os.UserHandle
|
||||
import android.util.Log
|
||||
import com.android.modules.utils.BinaryXmlPullParser
|
||||
import com.android.modules.utils.BinaryXmlSerializer
|
||||
import com.android.server.permission.access.AccessState
|
||||
import com.android.server.permission.access.AccessUri
|
||||
import com.android.server.permission.access.PermissionUri
|
||||
import com.android.server.permission.access.SchemePolicy
|
||||
import com.android.server.permission.access.SystemState
|
||||
import com.android.server.permission.access.UidUri
|
||||
import com.android.server.permission.access.UserState
|
||||
import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
|
||||
import com.android.server.permission.access.data.Permission
|
||||
import com.android.server.permission.access.external.AndroidPackage
|
||||
import com.android.server.permission.access.external.CompatibilityPermissionInfo
|
||||
import com.android.server.permission.access.external.KnownPackages
|
||||
import com.android.server.permission.access.external.PackageInfoUtils
|
||||
import com.android.server.permission.access.external.PackageState
|
||||
import com.android.server.permission.access.external.RoSystemProperties
|
||||
import com.android.server.permission.access.external.SigningDetails
|
||||
import com.android.server.permission.access.util.hasAnyBit
|
||||
import com.android.server.permission.access.util.hasBits
|
||||
|
||||
class UidPermissionPolicy : SchemePolicy() {
|
||||
private val persistence = UidPermissionPersistence()
|
||||
|
||||
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
|
||||
)
|
||||
grantImplicitPermissions(packageState, 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)
|
||||
newState.systemState.userIds.forEachIndexed { _, it ->
|
||||
grantImplicitPermissions(packageState, it, oldState, newState)
|
||||
}
|
||||
|
||||
// TODO: add trimPermissionStates() here for removing the permission states that are
|
||||
// no longer requested. (equivalent to revokeUnusedSharedUserPermissionsLocked())
|
||||
}
|
||||
|
||||
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.OTHER_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 permissions
|
||||
} 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 grantImplicitPermissions(
|
||||
packageState: PackageState,
|
||||
userId: Int,
|
||||
oldState: AccessState,
|
||||
newState: AccessState
|
||||
) {
|
||||
val appId = packageState.appId
|
||||
val androidPackage = packageState.androidPackage ?: return
|
||||
androidPackage.implicitPermissions.forEachIndexed implicitPermissions@ {
|
||||
_, implicitPermissionName ->
|
||||
val implicitPermission = newState.systemState.permissions[implicitPermissionName]
|
||||
checkNotNull(implicitPermission) {
|
||||
"Unknown implicit permission $implicitPermissionName in split permissions"
|
||||
}
|
||||
if (!implicitPermission.isRuntime) {
|
||||
return@implicitPermissions
|
||||
}
|
||||
val isNewPermission = getPermissionFlags(
|
||||
appId, implicitPermissionName, userId, oldState
|
||||
) == 0
|
||||
if (!isNewPermission) {
|
||||
return@implicitPermissions
|
||||
}
|
||||
val sourcePermissions = newState.systemState
|
||||
.implicitToSourcePermissions[implicitPermissionName] ?: return@implicitPermissions
|
||||
var newFlags = 0
|
||||
sourcePermissions.forEachIndexed sourcePermissions@ { _, sourcePermissionName ->
|
||||
val sourcePermission = newState.systemState.permissions[sourcePermissionName]
|
||||
checkNotNull(sourcePermission) {
|
||||
"Unknown source permission $sourcePermissionName in split permissions"
|
||||
}
|
||||
val sourceFlags = getPermissionFlags(appId, sourcePermissionName, userId, newState)
|
||||
val isSourceGranted = sourceFlags.hasAnyBit(PermissionFlags.MASK_GRANTED)
|
||||
val isNewGranted = newFlags.hasAnyBit(PermissionFlags.MASK_GRANTED)
|
||||
val isGrantingNewFromRevoke = isSourceGranted && !isNewGranted
|
||||
if (isSourceGranted == isNewGranted || isGrantingNewFromRevoke) {
|
||||
if (isGrantingNewFromRevoke) {
|
||||
newFlags = 0
|
||||
}
|
||||
newFlags = newFlags or (sourceFlags and PermissionFlags.MASK_RUNTIME)
|
||||
if (!sourcePermission.isRuntime && isSourceGranted) {
|
||||
newFlags = newFlags or PermissionFlags.OTHER_GRANTED
|
||||
}
|
||||
}
|
||||
}
|
||||
newFlags = newFlags or PermissionFlags.IMPLICIT
|
||||
setPermissionFlags(appId, implicitPermissionName, newFlags, userId, newState)
|
||||
}
|
||||
}
|
||||
|
||||
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 = UserHandle.getUserId(uid)
|
||||
val ownerPackageName = state.systemState.deviceAndProfileOwners[userId] ?: return false
|
||||
val ownerPackageState = state.systemState.packageStates[ownerPackageName] ?: return false
|
||||
val ownerUid = UserHandle.getUid(userId, ownerPackageState.appId)
|
||||
return uid == ownerUid
|
||||
}
|
||||
|
||||
override fun onPackageRemoved(
|
||||
packageState: PackageState,
|
||||
oldState: AccessState,
|
||||
newState: AccessState
|
||||
) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
override fun BinaryXmlPullParser.parseSystemState(systemState: SystemState) {
|
||||
with(persistence) { this@parseSystemState.parseSystemState(systemState) }
|
||||
}
|
||||
|
||||
override fun BinaryXmlSerializer.serializeSystemState(systemState: SystemState) {
|
||||
with(persistence) { this@serializeSystemState.serializeSystemState(systemState) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG_TAG = UidPermissionPolicy::class.java.simpleName
|
||||
|
||||
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(
|
||||
Manifest.permission.ACCESS_MEDIA_LOCATION,
|
||||
Manifest.permission.ACTIVITY_RECOGNITION,
|
||||
Manifest.permission.READ_MEDIA_AUDIO,
|
||||
Manifest.permission.READ_MEDIA_IMAGES,
|
||||
Manifest.permission.READ_MEDIA_VIDEO,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.server.permission.access.util
|
||||
|
||||
import android.util.AtomicFile
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* Read from an [AtomicFile] and close everything safely when done.
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
inline fun AtomicFile.read(block: (FileInputStream) -> Unit) {
|
||||
openRead().use(block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to an [AtomicFile] and close everything safely when done.
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
// Renamed to writeInlined() to avoid conflict with the hidden AtomicFile.write() that isn't inline.
|
||||
inline fun AtomicFile.writeInlined(block: (FileOutputStream) -> Unit) {
|
||||
startWrite().use {
|
||||
try {
|
||||
block(it)
|
||||
finishWrite(it)
|
||||
} catch (t: Throwable) {
|
||||
failWrite(it)
|
||||
throw t
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.server.permission.access.util
|
||||
|
||||
import com.android.modules.utils.BinaryXmlPullParser
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import org.xmlpull.v1.XmlPullParserException
|
||||
|
||||
/**
|
||||
* Parse content from [InputStream] with [BinaryXmlPullParser].
|
||||
*/
|
||||
@Throws(IOException::class, XmlPullParserException::class)
|
||||
inline fun InputStream.parseBinaryXml(block: BinaryXmlPullParser.() -> Unit) {
|
||||
BinaryXmlPullParser().apply {
|
||||
setInput(this@parseBinaryXml, null)
|
||||
block()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate through child tags of the current tag.
|
||||
* <p>
|
||||
* Attributes for the current tag needs to be accessed before this method is called because this
|
||||
* method will advance the parser past the start tag of the current tag. The code inspecting each
|
||||
* child tag may access the attributes of the child tag, and/or call [forEachTag] recursively to
|
||||
* inspect grandchild tags, which will naturally leave the parser at either the start tag or the end
|
||||
* tag of the child tag it inspected.
|
||||
*
|
||||
* @see BinaryXmlPullParser.next
|
||||
* @see BinaryXmlPullParser.getEventType
|
||||
* @see BinaryXmlPullParser.getDepth
|
||||
*/
|
||||
@Throws(IOException::class, XmlPullParserException::class)
|
||||
inline fun BinaryXmlPullParser.forEachTag(block: BinaryXmlPullParser.() -> Unit) {
|
||||
when (val eventType = eventType) {
|
||||
// Document start or start tag of the parent tag.
|
||||
XmlPullParser.START_DOCUMENT, XmlPullParser.START_TAG -> nextTagOrEnd()
|
||||
else -> throw XmlPullParserException("Unexpected event type $eventType")
|
||||
}
|
||||
while (true) {
|
||||
when (val eventType = eventType) {
|
||||
// Start tag of a child tag.
|
||||
XmlPullParser.START_TAG -> {
|
||||
val childDepth = depth
|
||||
block()
|
||||
// block() should leave the parser at either the start tag (no grandchild tags
|
||||
// expected) or the end tag (grandchild tags parsed with forEachTag()) of this child
|
||||
// tag.
|
||||
val postBlockDepth = depth
|
||||
if (postBlockDepth != childDepth) {
|
||||
throw XmlPullParserException(
|
||||
"Unexpected post-block depth $postBlockDepth, expected $childDepth"
|
||||
)
|
||||
}
|
||||
// Skip the parser to the end tag of this child tag.
|
||||
while (true) {
|
||||
when (val childEventType = this.eventType) {
|
||||
// Start tag of either this child tag or a grandchild tag.
|
||||
XmlPullParser.START_TAG -> nextTagOrEnd()
|
||||
XmlPullParser.END_TAG -> {
|
||||
if (depth > childDepth) {
|
||||
// End tag of a grandchild tag.
|
||||
nextTagOrEnd()
|
||||
} else {
|
||||
// End tag of this child tag.
|
||||
break
|
||||
}
|
||||
}
|
||||
else ->
|
||||
throw XmlPullParserException("Unexpected event type $childEventType")
|
||||
}
|
||||
}
|
||||
// Skip the end tag of this child tag.
|
||||
nextTagOrEnd()
|
||||
}
|
||||
// End tag of the parent tag, or document end.
|
||||
XmlPullParser.END_TAG, XmlPullParser.END_DOCUMENT -> break
|
||||
else -> throw XmlPullParserException("Unexpected event type $eventType")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Advance the parser until the current event is one of [XmlPullParser.START_TAG],
|
||||
* [XmlPullParser.START_TAG] and [XmlPullParser.START_TAG]
|
||||
*
|
||||
* @see BinaryXmlPullParser.next
|
||||
*/
|
||||
@Throws(IOException::class, XmlPullParserException::class)
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun BinaryXmlPullParser.nextTagOrEnd(): Int {
|
||||
while (true) {
|
||||
when (val eventType = next()) {
|
||||
XmlPullParser.START_TAG, XmlPullParser.END_TAG, XmlPullParser.END_DOCUMENT ->
|
||||
return eventType
|
||||
else -> continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BinaryXmlPullParser.getName
|
||||
*/
|
||||
inline val BinaryXmlPullParser.tagName: String
|
||||
get() = name
|
||||
|
||||
/**
|
||||
* Check whether an attribute exists for the current tag.
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun BinaryXmlPullParser.hasAttribute(name: String): Boolean = getAttributeIndex(name) != -1
|
||||
|
||||
/**
|
||||
* @see BinaryXmlPullParser.getAttributeIndex
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun BinaryXmlPullParser.getAttributeIndex(name: String): Int = getAttributeIndex(null, name)
|
||||
|
||||
/**
|
||||
* @see BinaryXmlPullParser.getAttributeIndexOrThrow
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(XmlPullParserException::class)
|
||||
inline fun BinaryXmlPullParser.getAttributeIndexOrThrow(name: String): Int =
|
||||
getAttributeIndexOrThrow(null, name)
|
||||
|
||||
/**
|
||||
* @see BinaryXmlPullParser.getAttributeValue
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(XmlPullParserException::class)
|
||||
inline fun BinaryXmlPullParser.getAttributeValue(name: String): String? =
|
||||
getAttributeValue(null, name)
|
||||
|
||||
/**
|
||||
* @see BinaryXmlPullParser.getAttributeValue
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(XmlPullParserException::class)
|
||||
inline fun BinaryXmlPullParser.getAttributeValueOrThrow(name: String): String =
|
||||
getAttributeValue(getAttributeIndexOrThrow(name))
|
||||
|
||||
/**
|
||||
* @see BinaryXmlPullParser.getAttributeBytesHex
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun BinaryXmlPullParser.getAttributeBytesHex(name: String): ByteArray? =
|
||||
getAttributeBytesHex(null, name, null)
|
||||
|
||||
/**
|
||||
* @see BinaryXmlPullParser.getAttributeBytesHex
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(XmlPullParserException::class)
|
||||
inline fun BinaryXmlPullParser.getAttributeBytesHexOrThrow(name: String): ByteArray =
|
||||
getAttributeBytesHex(null, name)
|
||||
|
||||
/**
|
||||
* @see BinaryXmlPullParser.getAttributeBytesBase64
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun BinaryXmlPullParser.getAttributeBytesBase64(name: String): ByteArray? =
|
||||
getAttributeBytesBase64(null, name, null)
|
||||
|
||||
/**
|
||||
* @see BinaryXmlPullParser.getAttributeBytesBase64
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(XmlPullParserException::class)
|
||||
inline fun BinaryXmlPullParser.getAttributeBytesBase64OrThrow(name: String): ByteArray =
|
||||
getAttributeBytesBase64(null, name)
|
||||
|
||||
/**
|
||||
* @see BinaryXmlPullParser.getAttributeInt
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun BinaryXmlPullParser.getAttributeIntOrDefault(name: String, defaultValue: Int): Int =
|
||||
getAttributeInt(null, name, defaultValue)
|
||||
|
||||
/**
|
||||
* @see BinaryXmlPullParser.getAttributeInt
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(XmlPullParserException::class)
|
||||
inline fun BinaryXmlPullParser.getAttributeIntOrThrow(name: String): Int =
|
||||
getAttributeInt(null, name)
|
||||
|
||||
/**
|
||||
* @see BinaryXmlPullParser.getAttributeIntHex
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun BinaryXmlPullParser.getAttributeIntHexOrDefault(name: String, defaultValue: Int): Int =
|
||||
getAttributeIntHex(null, name, defaultValue)
|
||||
|
||||
/**
|
||||
* @see BinaryXmlPullParser.getAttributeIntHex
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(XmlPullParserException::class)
|
||||
inline fun BinaryXmlPullParser.getAttributeIntHexOrThrow(name: String): Int =
|
||||
getAttributeIntHex(null, name)
|
||||
|
||||
/**
|
||||
* @see BinaryXmlPullParser.getAttributeLong
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun BinaryXmlPullParser.getAttributeLongOrDefault(name: String, defaultValue: Long): Long =
|
||||
getAttributeLong(null, name, defaultValue)
|
||||
|
||||
/**
|
||||
* @see BinaryXmlPullParser.getAttributeLong
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(XmlPullParserException::class)
|
||||
inline fun BinaryXmlPullParser.getAttributeLongOrThrow(name: String): Long =
|
||||
getAttributeLong(null, name)
|
||||
|
||||
/**
|
||||
* @see BinaryXmlPullParser.getAttributeLongHex
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun BinaryXmlPullParser.getAttributeLongHexOrDefault(
|
||||
name: String,
|
||||
defaultValue: Long
|
||||
): Long = getAttributeLongHex(null, name, defaultValue)
|
||||
|
||||
/**
|
||||
* @see BinaryXmlPullParser.getAttributeLongHex
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(XmlPullParserException::class)
|
||||
inline fun BinaryXmlPullParser.getAttributeLongHexOrThrow(name: String): Long =
|
||||
getAttributeLongHex(null, name)
|
||||
|
||||
/**
|
||||
* @see BinaryXmlPullParser.getAttributeFloat
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun BinaryXmlPullParser.getAttributeFloatOrDefault(
|
||||
name: String,
|
||||
defaultValue: Float
|
||||
): Float = getAttributeFloat(null, name, defaultValue)
|
||||
|
||||
/**
|
||||
* @see BinaryXmlPullParser.getAttributeFloat
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(XmlPullParserException::class)
|
||||
inline fun BinaryXmlPullParser.getAttributeFloatOrThrow(name: String): Float =
|
||||
getAttributeFloat(null, name)
|
||||
|
||||
/**
|
||||
* @see BinaryXmlPullParser.getAttributeDouble
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun BinaryXmlPullParser.getAttributeDoubleOrDefault(
|
||||
name: String,
|
||||
defaultValue: Double
|
||||
): Double = getAttributeDouble(null, name, defaultValue)
|
||||
|
||||
/**
|
||||
* @see BinaryXmlPullParser.getAttributeDouble
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(XmlPullParserException::class)
|
||||
inline fun BinaryXmlPullParser.getAttributeDoubleOrThrow(name: String): Double =
|
||||
getAttributeDouble(null, name)
|
||||
|
||||
/**
|
||||
* @see BinaryXmlPullParser.getAttributeBoolean
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun BinaryXmlPullParser.getAttributeBooleanOrDefault(
|
||||
name: String,
|
||||
defaultValue: Boolean
|
||||
): Boolean = getAttributeBoolean(null, name, defaultValue)
|
||||
|
||||
/**
|
||||
* @see BinaryXmlPullParser.getAttributeBoolean
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(XmlPullParserException::class)
|
||||
inline fun BinaryXmlPullParser.getAttributeBooleanOrThrow(name: String): Boolean =
|
||||
getAttributeBoolean(null, name)
|
||||
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.server.permission.access.util
|
||||
|
||||
import com.android.modules.utils.BinaryXmlSerializer
|
||||
import java.io.IOException
|
||||
import java.io.OutputStream
|
||||
|
||||
/**
|
||||
* Serialize content into [OutputStream] with [BinaryXmlSerializer].
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
inline fun OutputStream.serializeBinaryXml(block: BinaryXmlSerializer.() -> Unit) {
|
||||
BinaryXmlSerializer().apply {
|
||||
setOutput(this@serializeBinaryXml, null)
|
||||
document(block)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a document with [BinaryXmlSerializer].
|
||||
*
|
||||
* @see BinaryXmlSerializer.startDocument
|
||||
* @see BinaryXmlSerializer.endDocument
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
inline fun BinaryXmlSerializer.document(block: BinaryXmlSerializer.() -> Unit) {
|
||||
startDocument(null, true)
|
||||
block()
|
||||
endDocument()
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a tag with [BinaryXmlSerializer].
|
||||
*
|
||||
* @see BinaryXmlSerializer.startTag
|
||||
* @see BinaryXmlSerializer.endTag
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
inline fun BinaryXmlSerializer.tag(name: String, block: BinaryXmlSerializer.() -> Unit) {
|
||||
startTag(null, name)
|
||||
block()
|
||||
endTag(null, name)
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BinaryXmlSerializer.attribute
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(IOException::class)
|
||||
inline fun BinaryXmlSerializer.attribute(name: String, value: String) {
|
||||
attribute(null, name, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BinaryXmlSerializer.attributeInterned
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(IOException::class)
|
||||
inline fun BinaryXmlSerializer.attributeInterned(name: String, value: String) {
|
||||
attributeInterned(null, name, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BinaryXmlSerializer.attributeBytesHex
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(IOException::class)
|
||||
inline fun BinaryXmlSerializer.attributeBytesHex(name: String, value: ByteArray) {
|
||||
attributeBytesHex(null, name, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BinaryXmlSerializer.attributeBytesBase64
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(IOException::class)
|
||||
inline fun BinaryXmlSerializer.attributeBytesBase64(name: String, value: ByteArray) {
|
||||
attributeBytesBase64(null, name, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BinaryXmlSerializer.attributeInt
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(IOException::class)
|
||||
inline fun BinaryXmlSerializer.attributeInt(name: String, value: Int) {
|
||||
attributeInt(null, name, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BinaryXmlSerializer.attributeInt
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(IOException::class)
|
||||
inline fun BinaryXmlSerializer.attributeIntWithDefault(
|
||||
name: String,
|
||||
value: Int,
|
||||
defaultValue: Int
|
||||
) {
|
||||
if (value != defaultValue) {
|
||||
attributeInt(null, name, value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BinaryXmlSerializer.attributeIntHex
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(IOException::class)
|
||||
inline fun BinaryXmlSerializer.attributeIntHex(name: String, value: Int) {
|
||||
attributeIntHex(null, name, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BinaryXmlSerializer.attributeIntHex
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(IOException::class)
|
||||
inline fun BinaryXmlSerializer.attributeIntHexWithDefault(
|
||||
name: String,
|
||||
value: Int,
|
||||
defaultValue: Int
|
||||
) {
|
||||
if (value != defaultValue) {
|
||||
attributeIntHex(null, name, value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BinaryXmlSerializer.attributeLong
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(IOException::class)
|
||||
inline fun BinaryXmlSerializer.attributeLong(name: String, value: Long) {
|
||||
attributeLong(null, name, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BinaryXmlSerializer.attributeLong
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(IOException::class)
|
||||
inline fun BinaryXmlSerializer.attributeLongWithDefault(
|
||||
name: String,
|
||||
value: Long,
|
||||
defaultValue: Long
|
||||
) {
|
||||
if (value != defaultValue) {
|
||||
attributeLong(null, name, value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BinaryXmlSerializer.attributeLongHex
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(IOException::class)
|
||||
inline fun BinaryXmlSerializer.attributeLongHex(name: String, value: Long) {
|
||||
attributeLongHex(null, name, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BinaryXmlSerializer.attributeLongHex
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(IOException::class)
|
||||
inline fun BinaryXmlSerializer.attributeLongHexWithDefault(
|
||||
name: String,
|
||||
value: Long,
|
||||
defaultValue: Long
|
||||
) {
|
||||
if (value != defaultValue) {
|
||||
attributeLongHex(null, name, value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BinaryXmlSerializer.attributeFloat
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(IOException::class)
|
||||
inline fun BinaryXmlSerializer.attributeFloat(name: String, value: Float) {
|
||||
attributeFloat(null, name, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BinaryXmlSerializer.attributeFloat
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(IOException::class)
|
||||
inline fun BinaryXmlSerializer.attributeFloatWithDefault(
|
||||
name: String,
|
||||
value: Float,
|
||||
defaultValue: Float
|
||||
) {
|
||||
if (value != defaultValue) {
|
||||
attributeFloat(null, name, value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BinaryXmlSerializer.attributeDouble
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(IOException::class)
|
||||
inline fun BinaryXmlSerializer.attributeDouble(name: String, value: Double) {
|
||||
attributeDouble(null, name, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BinaryXmlSerializer.attributeDouble
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(IOException::class)
|
||||
inline fun BinaryXmlSerializer.attributeDoubleWithDefault(
|
||||
name: String,
|
||||
value: Double,
|
||||
defaultValue: Double
|
||||
) {
|
||||
if (value != defaultValue) {
|
||||
attributeDouble(null, name, value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BinaryXmlSerializer.attributeBoolean
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(IOException::class)
|
||||
inline fun BinaryXmlSerializer.attributeBoolean(name: String, value: Boolean) {
|
||||
attributeBoolean(null, name, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BinaryXmlSerializer.attributeBoolean
|
||||
*/
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
@Throws(IOException::class)
|
||||
inline fun BinaryXmlSerializer.attributeBooleanWithDefault(
|
||||
name: String,
|
||||
value: Boolean,
|
||||
defaultValue: Boolean
|
||||
) {
|
||||
if (value != defaultValue) {
|
||||
attributeBoolean(null, name, value)
|
||||
}
|
||||
}
|
||||
@@ -14,13 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.permission
|
||||
package com.android.server.permission.access.util
|
||||
|
||||
import com.android.internal.annotations.Keep
|
||||
import com.android.server.pm.permission.PermissionManagerServiceInterface
|
||||
fun Int.hasAnyBit(bits: Int): Boolean = this and bits != 0
|
||||
|
||||
/**
|
||||
* Modern implementation of [PermissionManagerServiceInterface].
|
||||
*/
|
||||
@Keep
|
||||
class ModernPermissionManagerServiceImpl
|
||||
fun Int.hasBits(bits: Int): Boolean = this and bits == bits
|
||||
|
||||
infix fun Int.andInv(other: Int): Int = this and other.inv()
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.server.permission.access.util
|
||||
|
||||
import android.content.ApexEnvironment
|
||||
import android.os.UserHandle
|
||||
import java.io.File
|
||||
|
||||
object PermissionApex {
|
||||
private const val MODULE_NAME = "com.android.permission"
|
||||
|
||||
/**
|
||||
* @see ApexEnvironment.getDeviceProtectedDataDir
|
||||
*/
|
||||
val systemDataDirectory: File
|
||||
get() = apexEnvironment.deviceProtectedDataDir
|
||||
|
||||
/**
|
||||
* @see ApexEnvironment.getDeviceProtectedDataDirForUser
|
||||
*/
|
||||
fun getUserDataDirectory(userId: Int): File =
|
||||
apexEnvironment.getDeviceProtectedDataDirForUser(UserHandle.of(userId))
|
||||
|
||||
private val apexEnvironment: ApexEnvironment
|
||||
get() = ApexEnvironment.getApexEnvironment(MODULE_NAME)
|
||||
}
|
||||
Reference in New Issue
Block a user