diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index e41e781b7c222..509d75bf9b76c 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -154,6 +154,7 @@ import com.android.server.os.DeviceIdentifiersPolicyService; import com.android.server.os.NativeTombstoneManagerService; import com.android.server.os.SchedulingPolicyService; import com.android.server.people.PeopleService; +import com.android.server.permission.access.AccessCheckingService; import com.android.server.pm.ApexManager; import com.android.server.pm.ApexSystemServiceInfo; import com.android.server.pm.BackgroundInstallControlService; @@ -1110,6 +1111,11 @@ public final class SystemServer implements Dumpable { startMemtrackProxyService(); t.traceEnd(); + // Start AccessCheckingService which provides new implementation for permission and app op. + t.traceBegin("StartAccessCheckingService"); + mSystemServiceManager.startService(AccessCheckingService.class); + t.traceEnd(); + // Activity manager runs the show. t.traceBegin("StartActivityManager"); // TODO: Might need to move after migration to WM. diff --git a/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt b/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt index 7b96d42c8abe4..73c2cccc550d0 100644 --- a/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt +++ b/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt @@ -16,11 +16,23 @@ package com.android.server.permission.access +import android.content.Context import com.android.internal.annotations.Keep -import com.android.server.permission.access.external.PackageState +import com.android.server.LocalManagerRegistry +import com.android.server.LocalServices +import com.android.server.SystemService +import com.android.server.appop.AppOpsCheckingServiceInterface +import com.android.server.permission.access.appop.AppOpService +import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports +import com.android.server.permission.access.permission.PermissionService +import com.android.server.pm.PackageManagerLocal +import com.android.server.pm.UserManagerService +import com.android.server.pm.permission.PermissionManagerServiceInterface +import com.android.server.pm.permission.PermissionManagerServiceInternal +import com.android.server.pm.pkg.PackageState @Keep -class AccessCheckingService { +class AccessCheckingService(context: Context) : SystemService(context) { @Volatile private lateinit var state: AccessState private val stateLock = Any() @@ -29,14 +41,35 @@ class AccessCheckingService { private val persistence = AccessPersistence(policy) - fun init() { + private lateinit var appOpService: AppOpService + private lateinit var permissionService: PermissionService + + private lateinit var packageManagerLocal: PackageManagerLocal + private lateinit var userManagerService: UserManagerService + + override fun onStart() { + appOpService = AppOpService(this) + permissionService = PermissionService(this) + + LocalServices.addService(AppOpsCheckingServiceInterface::class.java, appOpService) + LocalServices.addService(PermissionManagerServiceInterface::class.java, permissionService) + } + + fun initialize() { + packageManagerLocal = + LocalManagerRegistry.getManagerOrThrow(PackageManagerLocal::class.java) + userManagerService = UserManagerService.getInstance() + + val userIds = IntSet(userManagerService.userIdsIncludingPreCreated) + val packageStates = packageManagerLocal.packageStates + val state = AccessState() - state.systemState.userIds.apply { - // TODO: Get and add all user IDs. - // TODO: Maybe get and add all packages? - } + policy.initialize(state, userIds, packageStates) persistence.read(state) this.state = state + + appOpService.initialize() + permissionService.initialize() } fun getDecision(subject: AccessUri, `object`: AccessUri): Int = @@ -50,30 +83,60 @@ class AccessCheckingService { } } - fun onUserAdded(userId: Int) { + internal fun onUserAdded(userId: Int) { mutateState { with(policy) { onUserAdded(userId) } } } - fun onUserRemoved(userId: Int) { + internal fun onUserRemoved(userId: Int) { mutateState { with(policy) { onUserRemoved(userId) } } } - fun onPackageAdded(packageState: PackageState) { + internal fun onStorageVolumeMounted(volumeUuid: String?, isSystemUpdated: Boolean) { + val packageStates = packageManagerLocal.packageStates mutateState { - with(policy) { onPackageAdded(packageState) } + with(policy) { onStorageVolumeMounted(packageStates, volumeUuid, isSystemUpdated) } } } - fun onPackageRemoved(packageState: PackageState) { + internal fun onPackageAdded(packageName: String) { + val packageStates = packageManagerLocal.packageStates mutateState { - with(policy) { onPackageRemoved(packageState) } + with(policy) { onPackageAdded(packageStates, packageName) } } } + internal fun onPackageRemoved(packageName: String, appId: Int) { + val packageStates = packageManagerLocal.packageStates + mutateState { + with(policy) { onPackageRemoved(packageStates, packageName, appId) } + } + } + + internal fun onPackageInstalled( + packageName: String, + params: PermissionManagerServiceInternal.PackageInstalledParams, + userId: Int + ) { + val packageStates = packageManagerLocal.packageStates + mutateState { + with(policy) { onPackageInstalled(packageStates, packageName, params, userId) } + } + } + + internal fun onPackageUninstalled(packageName: String, appId: Int, userId: Int) { + val packageStates = packageManagerLocal.packageStates + mutateState { + with(policy) { onPackageUninstalled(packageStates, packageName, appId, userId) } + } + } + + private val PackageManagerLocal.packageStates: Map + get() = withUnfilteredSnapshot().use { it.packageStates } + internal inline fun getState(action: GetStateScope.() -> T): T = GetStateScope(state).action() diff --git a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt index e9741c69aa5be..0201dd0d0c799 100644 --- a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt +++ b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt @@ -22,11 +22,12 @@ 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 +import com.android.server.pm.permission.PermissionManagerServiceInternal +import com.android.server.pm.pkg.PackageState class AccessPolicy private constructor( private val schemePolicies: IndexedMap> @@ -53,6 +54,17 @@ class AccessPolicy private constructor( with(getSchemePolicy(subject, `object`)) { setDecision(subject, `object`, decision) } } + fun initialize(state: AccessState, userIds: IntSet, packageStates: Map) { + state.systemState.apply { + this.userIds += userIds + this.packageStates = packageStates + packageStates.forEach { (_, packageState) -> + appIds.getOrPut(packageState.appId) { IndexedListSet() } + .add(packageState.packageName) + } + } + } + fun MutateStateScope.onUserAdded(userId: Int) { newState.systemState.userIds += userId newState.userStates[userId] = UserState() @@ -69,18 +81,34 @@ class AccessPolicy private constructor( } } - fun MutateStateScope.onPackageAdded(packageState: PackageState) { - var isAppIdAdded = false - newState.systemState.apply { - packageStates[packageState.packageName] = packageState - appIds.getOrPut(packageState.appId) { - isAppIdAdded = true - IndexedListSet() - }.add(packageState.packageName) + fun MutateStateScope.onStorageVolumeMounted( + packageStates: Map, + volumeUuid: String?, + isSystemUpdated: Boolean + ) { + newState.systemState.packageStates = packageStates + forEachSchemePolicy { + with(it) { onStorageVolumeMounted(volumeUuid, isSystemUpdated) } } + } + + fun MutateStateScope.onPackageAdded( + packageStates: Map, + packageName: String + ) { + newState.systemState.packageStates = packageStates + var isAppIdAdded = false + val packageState = packageStates[packageName] + // TODO(zhanghai): Remove check before submission. + checkNotNull(packageState) + val appId = packageState.appId + newState.systemState.appIds.getOrPut(appId) { + isAppIdAdded = true + IndexedListSet() + }.add(packageName) if (isAppIdAdded) { forEachSchemePolicy { - with(it) { onAppIdAdded(packageState.appId) } + with(it) { onAppIdAdded(appId) } } } forEachSchemePolicy { @@ -88,30 +116,61 @@ class AccessPolicy private constructor( } } - fun MutateStateScope.onPackageRemoved(packageState: PackageState) { + fun MutateStateScope.onPackageRemoved( + packageStates: Map, + packageName: String, + appId: Int + ) { + newState.systemState.packageStates = packageStates 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 - } + // TODO(zhanghai): Remove check before submission. + check(packageName !in packageStates) + newState.systemState.appIds.apply appIds@{ + this[appId]?.apply { + this -= packageName + if (isEmpty()) { + this@appIds -= appId + isAppIdRemoved = true } } } forEachSchemePolicy { - with(it) { onPackageRemoved(packageState) } + with(it) { onPackageRemoved(packageName, appId) } } if (isAppIdRemoved) { forEachSchemePolicy { - with(it) { onAppIdRemoved(packageState.appId) } + with(it) { onAppIdRemoved(appId) } } } } + fun MutateStateScope.onPackageInstalled( + packageStates: Map, + packageName: String, + params: PermissionManagerServiceInternal.PackageInstalledParams, + userId: Int + ) { + newState.systemState.packageStates = packageStates + val packageState = packageStates[packageName] + // TODO(zhanghai): Remove check before submission. + checkNotNull(packageState) + forEachSchemePolicy { + with(it) { onPackageInstalled(packageState, params, userId) } + } + } + + fun MutateStateScope.onPackageUninstalled( + packageStates: Map, + packageName: String, + appId: Int, + userId: Int + ) { + newState.systemState.packageStates = packageStates + forEachSchemePolicy { + with(it) { onPackageUninstalled(packageName, appId, userId) } + } + } + fun BinaryXmlPullParser.parseSystemState(systemState: SystemState) { forEachTag { when (tagName) { @@ -230,9 +289,22 @@ abstract class SchemePolicy { open fun MutateStateScope.onAppIdRemoved(appId: Int) {} + open fun MutateStateScope.onStorageVolumeMounted( + volumeUuid: String?, + isSystemUpdated: Boolean + ) {} + open fun MutateStateScope.onPackageAdded(packageState: PackageState) {} - open fun MutateStateScope.onPackageRemoved(packageState: PackageState) {} + open fun MutateStateScope.onPackageRemoved(packageName: String, appId: Int) {} + + open fun MutateStateScope.onPackageInstalled( + packageState: PackageState, + params: PermissionManagerServiceInternal.PackageInstalledParams, + userId: Int + ) {} + + open fun MutateStateScope.onPackageUninstalled(packageName: String, appId: Int, userId: Int) {} open fun BinaryXmlPullParser.parseSystemState(systemState: SystemState) {} diff --git a/services/permission/java/com/android/server/permission/access/AccessState.kt b/services/permission/java/com/android/server/permission/access/AccessState.kt index cf8f38360cffa..4a2c78a86a937 100644 --- a/services/permission/java/com/android/server/permission/access/AccessState.kt +++ b/services/permission/java/com/android/server/permission/access/AccessState.kt @@ -18,8 +18,8 @@ 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 +import com.android.server.permission.access.permission.Permission +import com.android.server.pm.pkg.PackageState class AccessState private constructor( val systemState: SystemState, @@ -32,8 +32,8 @@ class AccessState private constructor( class SystemState private constructor( val userIds: IntSet, - val packageStates: IndexedMap, - val disabledSystemPackageStates: IndexedMap, + var packageStates: Map, + var disabledSystemPackageStates: Map, val appIds: IntMap>, // A map of KnownPackagesInt to a set of known package names val knownPackages: IntMap>, @@ -59,7 +59,7 @@ class SystemState private constructor( val permissions: IndexedMap ) : WritableState() { constructor() : this( - IntSet(), IndexedMap(), IndexedMap(), IntMap(), IntMap(), IntMap(), IndexedMap(), + IntSet(), emptyMap(), emptyMap(), IntMap(), IntMap(), IntMap(), IndexedMap(), IndexedListSet(), IndexedMap(), IndexedMap(), IndexedMap(), IndexedMap(), IndexedMap(), IndexedMap(), IndexedMap(), IndexedMap(), IndexedMap(), IndexedMap(), IndexedMap(), IndexedMap(), IndexedMap(), IndexedMap() @@ -68,8 +68,8 @@ class SystemState private constructor( fun copy(): SystemState = SystemState( userIds.copy(), - packageStates.copy { it }, - disabledSystemPackageStates.copy { it }, + packageStates, + disabledSystemPackageStates, appIds.copy { it.copy() }, knownPackages.copy { it.copy() }, deviceAndProfileOwners.copy { it }, @@ -94,14 +94,17 @@ class SystemState private constructor( class UserState private constructor( // A map of (appId to a map of (permissionName to permissionFlags)) - val permissionFlags: IntMap>, + val uidPermissionFlags: IntMap>, val uidAppOpModes: IntMap>, val packageAppOpModes: IndexedMap> ) : 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 } }) + fun copy(): UserState = UserState( + uidPermissionFlags.copy { it.copy { it } }, + uidAppOpModes.copy { it.copy { it } }, + packageAppOpModes.copy { it.copy { it } } + ) } object WriteMode { diff --git a/services/permission/java/com/android/server/permission/access/AccessUri.kt b/services/permission/java/com/android/server/permission/access/AccessUri.kt index 7e98d2cb13e76..d1abc0455245b 100644 --- a/services/permission/java/com/android/server/permission/access/AccessUri.kt +++ b/services/permission/java/com/android/server/permission/access/AccessUri.kt @@ -16,8 +16,7 @@ package com.android.server.permission.access -import com.android.server.permission.access.external.UserHandle -import com.android.server.permission.access.external.UserHandleCompat +import android.os.UserHandle sealed class AccessUri( val scheme: String @@ -70,7 +69,7 @@ data class UidUri( val uid: Int ) : AccessUri(SCHEME) { val userId: Int - get() = UserHandleCompat.getUserId(uid) + get() = UserHandle.getUserId(uid) val appId: Int get() = UserHandle.getAppId(uid) diff --git a/services/permission/java/com/android/server/permission/access/appop/AppOpsCheckingServiceCompatImpl.kt b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt similarity index 95% rename from services/permission/java/com/android/server/permission/access/appop/AppOpsCheckingServiceCompatImpl.kt rename to services/permission/java/com/android/server/permission/access/appop/AppOpService.kt index a565feb959c2c..b8d6aa3b4e496 100644 --- a/services/permission/java/com/android/server/permission/access/appop/AppOpsCheckingServiceCompatImpl.kt +++ b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt @@ -24,9 +24,13 @@ import com.android.server.appop.OnOpModeChangedListener import com.android.server.permission.access.AccessCheckingService import java.io.PrintWriter -class AppOpsCheckingServiceCompatImpl( - private val accessCheckingService: AccessCheckingService +class AppOpService( + private val service: AccessCheckingService ) : AppOpsCheckingServiceInterface { + fun initialize() { + TODO("Not yet implemented") + } + override fun getNonDefaultUidModes(uid: Int): SparseIntArray { TODO("Not yet implemented") } @@ -139,6 +143,6 @@ class AppOpsCheckingServiceCompatImpl( } companion object { - private val LOG_TAG = AppOpsCheckingServiceCompatImpl::class.java.simpleName + private val LOG_TAG = AppOpService::class.java.simpleName } } diff --git a/services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt b/services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt index 966489f72aa22..f4d6bfd0a000e 100644 --- a/services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt +++ b/services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt @@ -23,7 +23,6 @@ import com.android.server.permission.access.MutateStateScope 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 @@ -48,9 +47,9 @@ class PackageAppOpPolicy : BaseAppOpPolicy(PackageAppOpPersistence()) { newState.userStates[subject.userId]?.packageAppOpModes?.remove(subject.packageName) } - override fun MutateStateScope.onPackageRemoved(packageState: PackageState) { + override fun MutateStateScope.onPackageRemoved(packageName: String, appId: Int) { newState.userStates.forEachIndexed { _, _, userState -> - userState.packageAppOpModes -= packageState.packageName + userState.packageAppOpModes -= packageName } } } diff --git a/services/permission/java/com/android/server/permission/access/collection/IndexedList.kt b/services/permission/java/com/android/server/permission/access/collection/IndexedList.kt index 5ba435c79e2f1..9cb2e8660e78b 100644 --- a/services/permission/java/com/android/server/permission/access/collection/IndexedList.kt +++ b/services/permission/java/com/android/server/permission/access/collection/IndexedList.kt @@ -72,18 +72,24 @@ inline operator fun IndexedList.plusAssign(element: T) { add(element) } -inline fun IndexedList.removeAllIndexed(predicate: (Int, T) -> Boolean) { +inline fun IndexedList.removeAllIndexed(predicate: (Int, T) -> Boolean): Boolean { + var isChanged = false for (index in lastIndex downTo 0) { if (predicate(index, this[index])) { removeAt(index) + isChanged = true } } + return isChanged } -inline fun IndexedList.retainAllIndexed(predicate: (Int, T) -> Boolean) { +inline fun IndexedList.retainAllIndexed(predicate: (Int, T) -> Boolean): Boolean { + var isChanged = false for (index in lastIndex downTo 0) { if (!predicate(index, this[index])) { removeAt(index) + isChanged = true } } + return isChanged } diff --git a/services/permission/java/com/android/server/permission/access/collection/IndexedListSet.kt b/services/permission/java/com/android/server/permission/access/collection/IndexedListSet.kt index ac552fff6cdb6..1c42c50e0241f 100644 --- a/services/permission/java/com/android/server/permission/access/collection/IndexedListSet.kt +++ b/services/permission/java/com/android/server/permission/access/collection/IndexedListSet.kt @@ -123,18 +123,24 @@ inline operator fun IndexedListSet.plusAssign(element: T) { add(element) } -inline fun IndexedListSet.removeAllIndexed(predicate: (Int, T) -> Boolean) { +inline fun IndexedListSet.removeAllIndexed(predicate: (Int, T) -> Boolean): Boolean { + var isChanged = false for (index in lastIndex downTo 0) { if (predicate(index, elementAt(index))) { removeAt(index) + isChanged = true } } + return isChanged } -inline fun IndexedListSet.retainAllIndexed(predicate: (Int, T) -> Boolean) { +inline fun IndexedListSet.retainAllIndexed(predicate: (Int, T) -> Boolean): Boolean { + var isChanged = false for (index in lastIndex downTo 0) { if (!predicate(index, elementAt(index))) { removeAt(index) + isChanged = true } } + return isChanged } diff --git a/services/permission/java/com/android/server/permission/access/collection/IndexedMap.kt b/services/permission/java/com/android/server/permission/access/collection/IndexedMap.kt index 1251666089f5d..2448ff0755bea 100644 --- a/services/permission/java/com/android/server/permission/access/collection/IndexedMap.kt +++ b/services/permission/java/com/android/server/permission/access/collection/IndexedMap.kt @@ -111,20 +111,26 @@ inline fun IndexedMap.putWithDefault(key: K, value: V, defaultValue } } -inline fun IndexedMap.removeAllIndexed(predicate: (Int, K, V) -> Boolean) { +inline fun IndexedMap.removeAllIndexed(predicate: (Int, K, V) -> Boolean): Boolean { + var isChanged = false for (index in lastIndex downTo 0) { if (predicate(index, keyAt(index), valueAt(index))) { removeAt(index) + isChanged = true } } + return isChanged } -inline fun IndexedMap.retainAllIndexed(predicate: (Int, K, V) -> Boolean) { +inline fun IndexedMap.retainAllIndexed(predicate: (Int, K, V) -> Boolean): Boolean { + var isChanged = false for (index in lastIndex downTo 0) { if (!predicate(index, keyAt(index), valueAt(index))) { removeAt(index) + isChanged = true } } + return isChanged } @Suppress("NOTHING_TO_INLINE") diff --git a/services/permission/java/com/android/server/permission/access/collection/IndexedSet.kt b/services/permission/java/com/android/server/permission/access/collection/IndexedSet.kt index 36d8ff0c00873..faaa6d3a23f10 100644 --- a/services/permission/java/com/android/server/permission/access/collection/IndexedSet.kt +++ b/services/permission/java/com/android/server/permission/access/collection/IndexedSet.kt @@ -80,20 +80,26 @@ inline operator fun IndexedSet.plusAssign(element: T) { add(element) } -inline fun IndexedSet.removeAllIndexed(predicate: (Int, T) -> Boolean) { +inline fun IndexedSet.removeAllIndexed(predicate: (Int, T) -> Boolean): Boolean { + var isChanged = false for (index in lastIndex downTo 0) { if (predicate(index, elementAt(index))) { removeAt(index) + isChanged = true } } + return isChanged } -inline fun IndexedSet.retainAllIndexed(predicate: (Int, T) -> Boolean) { +inline fun IndexedSet.retainAllIndexed(predicate: (Int, T) -> Boolean): Boolean { + var isChanged = false for (index in lastIndex downTo 0) { if (!predicate(index, elementAt(index))) { removeAt(index) + isChanged = true } } + return isChanged } @Suppress("NOTHING_TO_INLINE") diff --git a/services/permission/java/com/android/server/permission/access/collection/IntMap.kt b/services/permission/java/com/android/server/permission/access/collection/IntMap.kt index 9051c66120d8b..0044b7359ef31 100644 --- a/services/permission/java/com/android/server/permission/access/collection/IntMap.kt +++ b/services/permission/java/com/android/server/permission/access/collection/IntMap.kt @@ -120,20 +120,26 @@ inline fun IntMap.putWithDefault(key: Int, value: T, defaultValue: T): T } } -inline fun IntMap.removeAllIndexed(predicate: (Int, Int, T) -> Boolean) { +inline fun IntMap.removeAllIndexed(predicate: (Int, Int, T) -> Boolean): Boolean { + var isChanged = false for (index in lastIndex downTo 0) { if (predicate(index, keyAt(index), valueAt(index))) { removeAt(index) + isChanged = true } } + return isChanged } -inline fun IntMap.retainAllIndexed(predicate: (Int, Int, T) -> Boolean) { +inline fun IntMap.retainAllIndexed(predicate: (Int, Int, T) -> Boolean): Boolean { + var isChanged = false for (index in lastIndex downTo 0) { if (!predicate(index, keyAt(index), valueAt(index))) { removeAt(index) + isChanged = true } } + return isChanged } inline val IntMap.size: Int diff --git a/services/permission/java/com/android/server/permission/access/collection/IntSet.kt b/services/permission/java/com/android/server/permission/access/collection/IntSet.kt index d6dfe9d0eb371..0d75a4c87a895 100644 --- a/services/permission/java/com/android/server/permission/access/collection/IntSet.kt +++ b/services/permission/java/com/android/server/permission/access/collection/IntSet.kt @@ -51,6 +51,8 @@ class IntSet private constructor( fun copy(): IntSet = IntSet(array.clone()) } +fun IntSet(values: IntArray): IntSet = IntSet().apply{ this += values } + inline fun IntSet.allIndexed(predicate: (Int, Int) -> Boolean): Boolean { for (index in 0 until size) { if (!predicate(index, elementAt(index))) { @@ -103,18 +105,32 @@ inline operator fun IntSet.plusAssign(element: Int) { add(element) } -inline fun IntSet.removeAllIndexed(predicate: (Int, Int) -> Boolean) { +operator fun IntSet.plusAssign(set: IntSet) { + set.forEachIndexed { _, it -> this += it } +} + +operator fun IntSet.plusAssign(array: IntArray) { + array.forEach { this += it } +} + +inline fun IntSet.removeAllIndexed(predicate: (Int, Int) -> Boolean): Boolean { + var isChanged = false for (index in lastIndex downTo 0) { if (predicate(index, elementAt(index))) { removeAt(index) + isChanged = true } } + return isChanged } -inline fun IntSet.retainAllIndexed(predicate: (Int, Int) -> Boolean) { +inline fun IntSet.retainAllIndexed(predicate: (Int, Int) -> Boolean): Boolean { + var isChanged = false for (index in lastIndex downTo 0) { if (!predicate(index, elementAt(index))) { removeAt(index) + isChanged = true } } + return isChanged } diff --git a/services/permission/java/com/android/server/permission/access/collection/List.kt b/services/permission/java/com/android/server/permission/access/collection/List.kt index dc28642bc2c6b..d35e69e88e72c 100644 --- a/services/permission/java/com/android/server/permission/access/collection/List.kt +++ b/services/permission/java/com/android/server/permission/access/collection/List.kt @@ -49,18 +49,24 @@ inline fun List.noneIndexed(predicate: (Int, T) -> Boolean): Boolean { return true } -inline fun MutableList.removeAllIndexed(predicate: (Int, T) -> Boolean) { +inline fun MutableList.removeAllIndexed(predicate: (Int, T) -> Boolean): Boolean { + var isChanged = false for (index in lastIndex downTo 0) { if (predicate(index, this[index])) { removeAt(index) + isChanged = true } } + return isChanged } -inline fun MutableList.retainAllIndexed(predicate: (Int, T) -> Boolean) { +inline fun MutableList.retainAllIndexed(predicate: (Int, T) -> Boolean): Boolean { + var isChanged = false for (index in lastIndex downTo 0) { if (!predicate(index, this[index])) { removeAt(index) + isChanged = true } } + return isChanged } diff --git a/services/permission/java/com/android/server/permission/access/data/Package.kt b/services/permission/java/com/android/server/permission/access/data/Package.kt deleted file mode 100644 index d6f98ab256cf1..0000000000000 --- a/services/permission/java/com/android/server/permission/access/data/Package.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 - get() = androidPackage.adoptPermissions - - val appId: Int - get() = androidPackage.appId - - val requestedPermissions: List - get() = androidPackage.requestedPermissions - - override fun equals(other: Any?): Boolean { - throw NotImplementedError() - } - - override fun hashCode(): Int { - throw NotImplementedError() - } -} diff --git a/services/permission/java/com/android/server/permission/access/external/CompatibilityPermissionInfo.kt b/services/permission/java/com/android/server/permission/access/external/CompatibilityPermissionInfo.kt deleted file mode 100644 index aadd8baedc812..0000000000000 --- a/services/permission/java/com/android/server/permission/access/external/CompatibilityPermissionInfo.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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() -} diff --git a/services/permission/java/com/android/server/permission/access/external/KnownPackages.kt b/services/permission/java/com/android/server/permission/access/external/KnownPackages.kt deleted file mode 100644 index 1239450c0963d..0000000000000 --- a/services/permission/java/com/android/server/permission/access/external/KnownPackages.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 - } -} diff --git a/services/permission/java/com/android/server/permission/access/external/PackageInfoUtils.kt b/services/permission/java/com/android/server/permission/access/external/PackageInfoUtils.kt deleted file mode 100644 index 24b28bd49fc99..0000000000000 --- a/services/permission/java/com/android/server/permission/access/external/PackageInfoUtils.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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() - } -} diff --git a/services/permission/java/com/android/server/permission/access/external/PackageState.kt b/services/permission/java/com/android/server/permission/access/external/PackageState.kt deleted file mode 100644 index b81d7946f2e9a..0000000000000 --- a/services/permission/java/com/android/server/permission/access/external/PackageState.kt +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 - 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 - val permissions: List - val permissionGroups: List - val requestedPermissions: List - val implicitPermissions: List -} - -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 -} diff --git a/services/permission/java/com/android/server/permission/access/external/RoSystemProperties.kt b/services/permission/java/com/android/server/permission/access/external/RoSystemProperties.kt deleted file mode 100644 index 528680e77879e..0000000000000 --- a/services/permission/java/com/android/server/permission/access/external/RoSystemProperties.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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 - } -} diff --git a/services/permission/java/com/android/server/permission/access/external/SigningDetails.kt b/services/permission/java/com/android/server/permission/access/external/SigningDetails.kt deleted file mode 100644 index 25917f993d4db..0000000000000 --- a/services/permission/java/com/android/server/permission/access/external/SigningDetails.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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): Boolean { - throw NotImplementedError() - } - - class CertCapabilities { - companion object { - /** grant SIGNATURE permissions to pkgs with this cert */ - var PERMISSION = 4 - } - } -} diff --git a/services/permission/java/com/android/server/permission/access/external/UserHandle.kt b/services/permission/java/com/android/server/permission/access/external/UserHandle.kt deleted file mode 100644 index e1072a760dc22..0000000000000 --- a/services/permission/java/com/android/server/permission/access/external/UserHandle.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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() - } -} diff --git a/services/permission/java/com/android/server/permission/access/data/Permission.kt b/services/permission/java/com/android/server/permission/access/permission/Permission.kt similarity index 98% rename from services/permission/java/com/android/server/permission/access/data/Permission.kt rename to services/permission/java/com/android/server/permission/access/permission/Permission.kt index 877f23b0e825f..efa5bf3118d6d 100644 --- a/services/permission/java/com/android/server/permission/access/data/Permission.kt +++ b/services/permission/java/com/android/server/permission/access/permission/Permission.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.permission.access.data +package com.android.server.permission.access.permission import android.content.pm.PermissionInfo import com.android.server.permission.access.util.hasBits diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt index bb1a86c7b32d2..1b055202af7c0 100644 --- a/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt +++ b/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt @@ -27,6 +27,7 @@ object PermissionFlags { // For the permissions that are implicit for the package const val IMPLICIT = 1 shl 5 + const val MASK_ALL = 0.inv() const val MASK_GRANTED = INSTALL_GRANTED or PROTECTION_GRANTED or OTHER_GRANTED or ROLE_GRANTED const val MASK_RUNTIME = OTHER_GRANTED or IMPLICIT } diff --git a/services/permission/java/com/android/server/permission/access/permission/ModernPermissionManagerServiceImpl.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt similarity index 84% rename from services/permission/java/com/android/server/permission/access/permission/ModernPermissionManagerServiceImpl.kt rename to services/permission/java/com/android/server/permission/access/permission/PermissionService.kt index cc51866b46954..1e6996b02721b 100644 --- a/services/permission/java/com/android/server/permission/access/permission/ModernPermissionManagerServiceImpl.kt +++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt @@ -33,8 +33,9 @@ import com.android.server.pm.permission.PermissionManagerServiceInterface import com.android.server.permission.access.AccessCheckingService import com.android.server.permission.access.PermissionUri import com.android.server.permission.access.UidUri -import com.android.server.permission.access.data.Permission +import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports import com.android.server.permission.access.util.hasBits +import com.android.server.pm.UserManagerService import com.android.server.pm.permission.LegacyPermission import com.android.server.pm.permission.LegacyPermissionSettings import com.android.server.pm.permission.LegacyPermissionState @@ -46,17 +47,24 @@ import java.io.PrintWriter /** * Modern implementation of [PermissionManagerServiceInterface]. */ -class ModernPermissionManagerServiceImpl( +class PermissionService( private val service: AccessCheckingService ) : PermissionManagerServiceInterface { private val policy = service.getSchemePolicy(UidUri.SCHEME, PermissionUri.SCHEME) as UidPermissionPolicy - private val packageManagerInternal = - LocalServices.getService(PackageManagerInternal::class.java) + private lateinit var packageManagerInternal: PackageManagerInternal + private lateinit var packageManagerLocal: PackageManagerLocal + private lateinit var userManagerService: UserManagerService - private val packageManagerLocal = - LocalManagerRegistry.getManagerOrThrow(PackageManagerLocal::class.java) + private val mountedStorageVolumes = IndexedSet() + + fun initialize() { + packageManagerInternal = LocalServices.getService(PackageManagerInternal::class.java) + packageManagerLocal = + LocalManagerRegistry.getManagerOrThrow(PackageManagerLocal::class.java) + userManagerService = UserManagerService.getInstance() + } override fun getAllPermissionGroups(flags: Int): List { TODO("Not yet implemented") @@ -351,6 +359,8 @@ class ModernPermissionManagerServiceImpl( } override fun readLegacyPermissionsTEMP(legacyPermissionSettings: LegacyPermissionSettings) { + // Package settings has been read when this method is called. + service.initialize() TODO("Not yet implemented") } @@ -375,15 +385,18 @@ class ModernPermissionManagerServiceImpl( } override fun onUserCreated(userId: Int) { - TODO("Not yet implemented") + service.onUserAdded(userId) } override fun onUserRemoved(userId: Int) { - TODO("Not yet implemented") + service.onUserRemoved(userId) } override fun onStorageVolumeMounted(volumeUuid: String, fingerprintChanged: Boolean) { - TODO("Not yet implemented") + service.onStorageVolumeMounted(volumeUuid, fingerprintChanged) + synchronized(mountedStorageVolumes) { + mountedStorageVolumes += volumeUuid + } } override fun onPackageAdded( @@ -391,7 +404,18 @@ class ModernPermissionManagerServiceImpl( isInstantApp: Boolean, oldPackage: AndroidPackage? ) { - TODO("Not yet implemented") + synchronized(mountedStorageVolumes) { + if (androidPackage.volumeUuid !in mountedStorageVolumes) { + // Wait for the storage volume to be mounted and batch the state mutation there. + return + } + } + service.onPackageAdded(androidPackage.packageName) + } + + override fun onPackageRemoved(androidPackage: AndroidPackage) { + // This may not be a full removal so ignored - we'll figure out full removal in + // onPackageUninstalled(). } override fun onPackageInstalled( @@ -400,21 +424,37 @@ class ModernPermissionManagerServiceImpl( params: PermissionManagerServiceInternal.PackageInstalledParams, userId: Int ) { - TODO("Not yet implemented") + synchronized(mountedStorageVolumes) { + if (androidPackage.volumeUuid !in mountedStorageVolumes) { + // Wait for the storage volume to be mounted and batch the state mutation there. + return + } + } + val userIds = if (userId == UserHandle.USER_ALL) { + userManagerService.userIdsIncludingPreCreated + } else { + intArrayOf(userId) + } + userIds.forEach { service.onPackageInstalled(androidPackage.packageName, params, it) } } override fun onPackageUninstalled( packageName: String, appId: Int, androidPackage: AndroidPackage?, - sharedUserPkgs: MutableList, + sharedUserPkgs: List, userId: Int ) { - TODO("Not yet implemented") - } - - override fun onPackageRemoved(androidPackage: AndroidPackage) { - TODO("Not yet implemented") + val userIds = if (userId == UserHandle.USER_ALL) { + userManagerService.userIdsIncludingPreCreated + } else { + intArrayOf(userId) + } + userIds.forEach { service.onPackageUninstalled(packageName, appId, it) } + val packageState = packageManagerInternal.packageStates[packageName] + if (packageState == null) { + service.onPackageRemoved(packageName, appId) + } } /** diff --git a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPersistence.kt b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPersistence.kt index 3489061c5bae1..061933a13fb44 100644 --- a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPersistence.kt +++ b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPersistence.kt @@ -22,7 +22,6 @@ 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 diff --git a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt index e08192444d676..3d6d2ce341419 100644 --- a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt +++ b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt @@ -20,9 +20,11 @@ import android.Manifest import android.content.pm.PackageManager import android.content.pm.PermissionGroupInfo import android.content.pm.PermissionInfo +import android.content.pm.SigningDetails import android.os.Build import android.os.UserHandle import android.util.Log +import com.android.internal.os.RoSystemProperties import com.android.modules.utils.BinaryXmlPullParser import com.android.modules.utils.BinaryXmlSerializer import com.android.server.permission.access.AccessState @@ -33,22 +35,24 @@ 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.andInv import com.android.server.permission.access.util.hasAnyBit import com.android.server.permission.access.util.hasBits +import com.android.server.pm.KnownPackages +import com.android.server.pm.parsing.PackageInfoUtils +import com.android.server.pm.permission.CompatibilityPermissionInfo +import com.android.server.pm.pkg.AndroidPackage +import com.android.server.pm.pkg.PackageState class UidPermissionPolicy : SchemePolicy() { private val persistence = UidPermissionPersistence() + @Volatile + private var onPermissionFlagsChangedListeners = + IndexedListSet() + private val onPermissionFlagsChangedListenersLock = Any() + override val subjectScheme: String get() = UidUri.SCHEME @@ -58,8 +62,7 @@ class UidPermissionPolicy : SchemePolicy() { override fun GetStateScope.getDecision(subject: AccessUri, `object`: AccessUri): Int { subject as UidUri `object` as PermissionUri - return state.userStates[subject.userId]?.permissionFlags?.get(subject.appId) - ?.get(`object`.permissionName) ?: 0 + return getPermissionFlags(subject.appId, subject.userId, `object`.permissionName) } override fun MutateStateScope.setDecision( @@ -69,26 +72,22 @@ class UidPermissionPolicy : SchemePolicy() { ) { subject as UidUri `object` as PermissionUri - val uidFlags = newState.userStates.getOrPut(subject.userId) { UserState() } - .permissionFlags.getOrPut(subject.appId) { IndexedMap() } - uidFlags[`object`.permissionName] = decision + setPermissionFlags(subject.appId, subject.userId, `object`.permissionName, decision) } override fun MutateStateScope.onUserAdded(userId: Int) { - newState.systemState.packageStates.forEachValueIndexed { _, packageState -> - evaluateAllPermissionStatesForPackageAndUser(packageState, null, userId) + newState.systemState.packageStates.forEach { (_, packageState) -> + evaluateAllPermissionStatesForPackageAndUser(packageState, userId, null) grantImplicitPermissions(packageState, userId) } } - override fun MutateStateScope.onAppIdAdded(appId: Int) { - newState.userStates.forEachIndexed { _, _, userState -> - userState.permissionFlags.getOrPut(appId) { IndexedMap() } - } - } - override fun MutateStateScope.onAppIdRemoved(appId: Int) { - newState.userStates.forEachIndexed { _, _, userState -> userState.permissionFlags -= appId } + newState.userStates.forEachValueIndexed { _, userState -> + userState.uidPermissionFlags -= appId + userState.requestWrite() + // Skip notifying the change listeners since the app ID no longer exists. + } } override fun MutateStateScope.onPackageAdded(packageState: PackageState) { @@ -97,7 +96,7 @@ class UidPermissionPolicy : SchemePolicy() { addPermissionGroups(packageState) addPermissions(packageState, changedPermissionNames) // TODO: revokeStoragePermissionsIfScopeExpandedInternal() - trimPermissions(packageState.packageName) + trimPermissions(packageState.packageName, changedPermissionNames) changedPermissionNames.forEachIndexed { _, permissionName -> evaluatePermissionStateForAllPackages(permissionName, packageState) } @@ -121,22 +120,23 @@ class UidPermissionPolicy : SchemePolicy() { if (!canAdoptPermissions(packageName, originalPackageName)) { return@forEachIndexed } - newState.systemState.permissions.let { permissions -> - permissions.forEachIndexed permissions@ { - permissionIndex, permissionName, oldPermission -> - if (oldPermission.packageName != originalPackageName) { - return@permissions - } - @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(permissionIndex, newPermission) + val systemState = newState.systemState + val permissions = systemState.permissions + permissions.forEachIndexed permissions@ { + permissionIndex, permissionName, oldPermission -> + if (oldPermission.packageName != originalPackageName) { + return@permissions } + @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) + permissions.setValueAt(permissionIndex, newPermission) + systemState.requestWrite() + changedPermissionNames += permissionName } } } @@ -179,7 +179,7 @@ class UidPermissionPolicy : SchemePolicy() { 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] @@ -211,13 +211,14 @@ class UidPermissionPolicy : SchemePolicy() { // } val newPermissionInfo = PackageInfoUtils.generatePermissionInfo( parsedPermission, PackageManager.GET_META_DATA.toLong() - ) + )!! // TODO: newPermissionInfo.flags |= PermissionInfo.FLAG_INSTALLED + val systemState = newState.systemState val permissionName = newPermissionInfo.name val oldPermission = if (parsedPermission.isTree) { - newState.systemState.permissionTrees[permissionName] + systemState.permissionTrees[permissionName] } else { - newState.systemState.permissions[permissionName] + 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 @@ -247,19 +248,18 @@ class UidPermissionPolicy : SchemePolicy() { 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) { + } else if (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 + systemState.userIds.forEachIndexed { _, userId -> + systemState.appIds.forEachKeyIndexed { _, appId -> + setPermissionFlags(appId, userId, permissionName, 0) } } - // TODO: Notify re-evaluation of this permission. Permission( newPermissionInfo, true, Permission.TYPE_MANIFEST, packageState.appId ) @@ -278,23 +278,28 @@ class UidPermissionPolicy : SchemePolicy() { Permission(newPermissionInfo, true, Permission.TYPE_MANIFEST, packageState.appId) } - changedPermissionNames += permissionName if (parsedPermission.isTree) { - newState.systemState.permissionTrees[permissionName] = newPermission + systemState.permissionTrees[permissionName] = newPermission } else { - newState.systemState.permissions[permissionName] = newPermission + systemState.permissions[permissionName] = newPermission } + systemState.requestWrite() + changedPermissionNames += permissionName } } - private fun MutateStateScope.trimPermissions(packageName: String) { - val packageState = newState.systemState.packageStates[packageName] + private fun MutateStateScope.trimPermissions( + packageName: String, + changedPermissionNames: IndexedSet + ) { + val systemState = newState.systemState + val packageState = systemState.packageStates[packageName] val androidPackage = packageState?.androidPackage if (packageState != null && androidPackage == null) { return } - newState.systemState.permissionTrees.removeAllIndexed { + val isPermissionTreeRemoved = systemState.permissionTrees.removeAllIndexed { _, permissionTreeName, permissionTree -> permissionTree.packageName == packageName && ( packageState == null || androidPackage!!.permissions.noneIndexed { _, it -> @@ -302,26 +307,30 @@ class UidPermissionPolicy : SchemePolicy() { } ) } + if (isPermissionTreeRemoved) { + systemState.requestWrite() + } - newState.systemState.permissions.removeAllIndexed { i, permissionName, permission -> + systemState.permissions.removeAllIndexed { permissionIndex, permissionName, permission -> val updatedPermission = updatePermissionIfDynamic(permission) - newState.systemState.permissions.setValueAt(i, updatedPermission) + newState.systemState.permissions.setValueAt(permissionIndex, updatedPermission) if (updatedPermission.packageName == packageName && ( packageState == null || androidPackage!!.permissions.noneIndexed { _, it -> !it.isTree && it.name == permissionName } )) { - if (!isPermissionDeclaredByDisabledSystemPackage(permission)) { - newState.userStates.forEachIndexed { _, userId, userState -> - userState.permissionFlags.forEachKeyIndexed { _, appId -> - setPermissionFlags( - appId, permissionName, getPermissionFlags( - appId, permissionName, userId - ) and PermissionFlags.INSTALL_REVOKED, userId - ) - } + // Different from the old implementation where we keep the permission state if the + // permission is declared by a disabled system package (ag/15189282), we now + // shouldn't be notified when the updated system package is removed but the disabled + // system package isn't re-enabled yet, so we don't need to maintain that brittle + // special case either. + systemState.userIds.forEachIndexed { _, userId -> + systemState.appIds.forEachKeyIndexed { _, appId -> + setPermissionFlags(appId, userId, permissionName, 0) } } + changedPermissionNames += permissionName + systemState.requestWrite() true } else { false @@ -329,16 +338,6 @@ class UidPermissionPolicy : SchemePolicy() { } } - private fun MutateStateScope.isPermissionDeclaredByDisabledSystemPackage( - permission: Permission - ): 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 MutateStateScope.updatePermissionIfDynamic(permission: Permission): Permission { if (!permission.isDynamic) { return permission @@ -368,11 +367,14 @@ class UidPermissionPolicy : SchemePolicy() { permissionName: String, installedPackageState: PackageState? ) { - newState.systemState.userIds.forEachIndexed { _, userId -> - oldState.userStates[userId]?.permissionFlags?.forEachIndexed { - _, appId, permissionFlags -> - if (permissionName in permissionFlags) { - evaluatePermissionState(appId, permissionName, installedPackageState, userId) + val systemState = newState.systemState + systemState.userIds.forEachIndexed { _, userId -> + systemState.appIds.forEachKeyIndexed { _, appId -> + val isPermissionRequested = anyPackageInAppId(appId) { packageState -> + permissionName in packageState.androidPackage!!.requestedPermissions + } + if (isPermissionRequested) { + evaluatePermissionState(appId, userId, permissionName, installedPackageState) } } } @@ -384,28 +386,28 @@ class UidPermissionPolicy : SchemePolicy() { ) { newState.systemState.userIds.forEachIndexed { _, userId -> evaluateAllPermissionStatesForPackageAndUser( - packageState, installedPackageState, userId + packageState, userId, installedPackageState ) } } private fun MutateStateScope.evaluateAllPermissionStatesForPackageAndUser( packageState: PackageState, - installedPackageState: PackageState?, - userId: Int + userId: Int, + installedPackageState: PackageState? ) { packageState.androidPackage?.requestedPermissions?.forEachIndexed { _, permissionName -> evaluatePermissionState( - packageState.appId, permissionName, installedPackageState, userId + packageState.appId, userId, permissionName, installedPackageState ) } } private fun MutateStateScope.evaluatePermissionState( appId: Int, + userId: Int, permissionName: String, - installedPackageState: PackageState?, - userId: Int + installedPackageState: PackageState? ) { val packageNames = newState.systemState.appIds[appId] val hasMissingPackage = packageNames.anyIndexed { _, packageName -> @@ -416,7 +418,7 @@ class UidPermissionPolicy : SchemePolicy() { return } val permission = newState.systemState.permissions[permissionName] ?: return - val oldFlags = getPermissionFlags(appId, permissionName, userId) + val oldFlags = getPermissionFlags(appId, userId, permissionName) if (permission.isNormal) { val wasGranted = oldFlags.hasBits(PermissionFlags.INSTALL_GRANTED) if (!wasGranted) { @@ -438,7 +440,7 @@ class UidPermissionPolicy : SchemePolicy() { } else { PermissionFlags.INSTALL_REVOKED } - setPermissionFlags(appId, permissionName, newFlags, userId) + setPermissionFlags(appId, userId, permissionName, newFlags) } } else if (permission.isSignature || permission.isInternal) { val wasProtectionGranted = oldFlags.hasBits(PermissionFlags.PROTECTION_GRANTED) @@ -477,7 +479,7 @@ class UidPermissionPolicy : SchemePolicy() { if (permission.isRole) { newFlags = newFlags or (oldFlags and PermissionFlags.ROLE_GRANTED) } - setPermissionFlags(appId, permissionName, newFlags, userId) + setPermissionFlags(appId, userId, permissionName, newFlags) } else if (permission.isRuntime) { // TODO: add runtime permissions } else { @@ -503,7 +505,7 @@ class UidPermissionPolicy : SchemePolicy() { } // Explicitly check against the old state to determine if this permission is new. val isNewPermission = getPermissionFlags( - appId, implicitPermissionName, userId, oldState + appId, userId, implicitPermissionName, oldState ) == 0 if (!isNewPermission) { return@implicitPermissions @@ -516,7 +518,7 @@ class UidPermissionPolicy : SchemePolicy() { checkNotNull(sourcePermission) { "Unknown source permission $sourcePermissionName in split permissions" } - val sourceFlags = getPermissionFlags(appId, sourcePermissionName, userId) + val sourceFlags = getPermissionFlags(appId, userId, sourcePermissionName) val isSourceGranted = sourceFlags.hasAnyBit(PermissionFlags.MASK_GRANTED) val isNewGranted = newFlags.hasAnyBit(PermissionFlags.MASK_GRANTED) val isGrantingNewFromRevoke = isSourceGranted && !isNewGranted @@ -531,27 +533,10 @@ class UidPermissionPolicy : SchemePolicy() { } } newFlags = newFlags or PermissionFlags.IMPLICIT - setPermissionFlags(appId, implicitPermissionName, newFlags, userId) + setPermissionFlags(appId, userId, implicitPermissionName, newFlags) } } - private fun MutateStateScope.getPermissionFlags( - appId: Int, - permissionName: String, - userId: Int, - state: AccessState = newState - ): Int = state.userStates[userId].permissionFlags[appId].getWithDefault(permissionName, 0) - - private fun MutateStateScope.setPermissionFlags( - appId: Int, - permissionName: String, - flags: Int, - userId: Int - ) { - newState.userStates[userId].permissionFlags[appId]!! - .putWithDefault(permissionName, flags, 0) - } - private fun isCompatibilityPermissionForPackage( androidPackage: AndroidPackage, permissionName: String @@ -573,23 +558,24 @@ class UidPermissionPolicy : SchemePolicy() { packageState: PackageState, permission: Permission ): 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 + // Check if the package is allowed 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 packageSigningDetails = packageState.androidPackage!!.signingDetails val sourceSigningDetails = newState.systemState - .packageStates[permission.packageName]?.signingDetails + .packageStates[permission.packageName]?.androidPackage?.signingDetails val platformSigningDetails = newState.systemState - .packageStates[PLATFORM_PACKAGE_NAME]!!.signingDetails - return sourceSigningDetails?.hasCommonSignerWithCapability(packageState.signingDetails, + .packageStates[PLATFORM_PACKAGE_NAME]!!.androidPackage!!.signingDetails + return sourceSigningDetails?.hasCommonSignerWithCapability(packageSigningDetails, SigningDetails.CertCapabilities.PERMISSION) == true || - packageState.signingDetails.hasAncestorOrSelf(platformSigningDetails) || - platformSigningDetails.checkCapability(packageState.signingDetails, + packageSigningDetails.hasAncestorOrSelf(platformSigningDetails) || + platformSigningDetails.checkCapability(packageSigningDetails, SigningDetails.CertCapabilities.PERMISSION) } @@ -629,7 +615,10 @@ class UidPermissionPolicy : SchemePolicy() { androidPackage: AndroidPackage, permissionName: String ): Boolean { - val apexModuleName = androidPackage.apexModuleName + // TODO(b/261913353): STOPSHIP: Add AndroidPackage.apexModuleName. The below is only for + // passing compilation but won't actually work. + //val apexModuleName = androidPackage.apexModuleName + val apexModuleName = androidPackage.packageName val systemState = newState.systemState val packageName = androidPackage.packageName val permissionNames = when { @@ -657,7 +646,10 @@ class UidPermissionPolicy : SchemePolicy() { ): 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 + // TODO(b/261913353): STOPSHIP: Add AndroidPackage.apexModuleName. The below is only for + // passing compilation but won't actually work. + //val apexModuleName = androidPackage.apexModuleName + val apexModuleName = androidPackage.packageName val systemState = newState.systemState val packageName = androidPackage.packageName val permissionNames = when { @@ -741,7 +733,7 @@ class UidPermissionPolicy : SchemePolicy() { return true } if (permission.isKnownSigner && - packageState.signingDetails.hasAncestorOrSelfWithDigest(permission.knownCerts)) { + androidPackage.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 @@ -840,8 +832,11 @@ class UidPermissionPolicy : SchemePolicy() { return uid == ownerUid } - override fun MutateStateScope.onPackageRemoved(packageState: PackageState) { - // TODO + override fun MutateStateScope.onPackageRemoved(packageName: String, appId: Int) { + // TODO: STOPSHIP: Remove this check or at least turn into logging. + check(packageName !in newState.systemState.disabledSystemPackageStates) { + "Package $packageName reported as removed before disabled system package is enabled" + } } override fun BinaryXmlPullParser.parseSystemState(systemState: SystemState) { @@ -858,6 +853,76 @@ class UidPermissionPolicy : SchemePolicy() { fun GetStateScope.getPermission(permissionName: String): Permission? = state.systemState.permissions[permissionName] + fun GetStateScope.getPermissionFlags( + appId: Int, + userId: Int, + permissionName: String + ): Int = getPermissionFlags(state, appId, userId, permissionName) + + private fun MutateStateScope.getPermissionFlags( + appId: Int, + userId: Int, + permissionName: String, + state: AccessState = newState + ): Int = getPermissionFlags(state, appId, userId, permissionName) + + private fun getPermissionFlags( + state: AccessState, + appId: Int, + userId: Int, + permissionName: String + ): Int = state.userStates[userId].uidPermissionFlags[appId].getWithDefault(permissionName, 0) + + fun MutateStateScope.setPermissionFlags( + appId: Int, + userId: Int, + permissionName: String, + flags: Int + ): Boolean = + updatePermissionFlags(appId, userId, permissionName, PermissionFlags.MASK_ALL, flags) + + fun MutateStateScope.updatePermissionFlags( + appId: Int, + userId: Int, + permissionName: String, + flagMask: Int, + flagValues: Int + ): Boolean { + val userState = newState.userStates[userId] + val uidPermissionFlags = userState.uidPermissionFlags + var permissionFlags = uidPermissionFlags[appId] + val oldFlags = permissionFlags.getWithDefault(permissionName, 0) + val newFlags = (oldFlags andInv flagMask) or flagValues + if (oldFlags == newFlags) { + return false + } + if (permissionFlags == null) { + permissionFlags = IndexedMap() + uidPermissionFlags[appId] = permissionFlags + } + permissionFlags.putWithDefault(permissionName, newFlags, 0) + if (permissionFlags.isEmpty()) { + uidPermissionFlags -= appId + } + userState.requestWrite() + onPermissionFlagsChangedListeners.forEachIndexed { _, it -> + it.onPermissionFlagsChanged(appId, userId, permissionName, oldFlags, newFlags) + } + return true + } + + fun addOnPermissionFlagsChangedListener(listener: OnPermissionFlagsChangedListener) { + synchronized(onPermissionFlagsChangedListenersLock) { + onPermissionFlagsChangedListeners = onPermissionFlagsChangedListeners + listener + } + } + + fun removeOnPermissionFlagsChangedListener(listener: OnPermissionFlagsChangedListener) { + synchronized(onPermissionFlagsChangedListenersLock) { + onPermissionFlagsChangedListeners = onPermissionFlagsChangedListeners - listener + } + } + companion object { private val LOG_TAG = UidPermissionPolicy::class.java.simpleName @@ -872,4 +937,14 @@ class UidPermissionPolicy : SchemePolicy() { Manifest.permission.READ_MEDIA_VIDEO, ) } + + fun interface OnPermissionFlagsChangedListener { + fun onPermissionFlagsChanged( + appId: Int, + userId: Int, + permissionName: String, + oldFlags: Int, + newFlags: Int + ) + } }