diff options
10 files changed, 231 insertions, 116 deletions
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 4c794931cf98..4a2c78a86a93 100644 --- a/services/permission/java/com/android/server/permission/access/AccessState.kt +++ b/services/permission/java/com/android/server/permission/access/AccessState.kt @@ -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<IndexedMap<String, Int>>, + val uidPermissionFlags: 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 } }) + 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/collection/IndexedList.kt b/services/permission/java/com/android/server/permission/access/collection/IndexedList.kt index 5ba435c79e2f..9cb2e8660e78 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 <T> IndexedList<T>.plusAssign(element: T) { add(element) } -inline fun <T> IndexedList<T>.removeAllIndexed(predicate: (Int, T) -> Boolean) { +inline fun <T> IndexedList<T>.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 <T> IndexedList<T>.retainAllIndexed(predicate: (Int, T) -> Boolean) { +inline fun <T> IndexedList<T>.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 ac552fff6cdb..1c42c50e0241 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 <T> IndexedListSet<T>.plusAssign(element: T) { add(element) } -inline fun <T> IndexedListSet<T>.removeAllIndexed(predicate: (Int, T) -> Boolean) { +inline fun <T> IndexedListSet<T>.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 <T> IndexedListSet<T>.retainAllIndexed(predicate: (Int, T) -> Boolean) { +inline fun <T> IndexedListSet<T>.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 1251666089f5..2448ff0755be 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 <K, V> IndexedMap<K, V>.putWithDefault(key: K, value: V, defaultValue } } -inline fun <K, V> IndexedMap<K, V>.removeAllIndexed(predicate: (Int, K, V) -> Boolean) { +inline fun <K, V> IndexedMap<K, V>.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 <K, V> IndexedMap<K, V>.retainAllIndexed(predicate: (Int, K, V) -> Boolean) { +inline fun <K, V> IndexedMap<K, V>.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 36d8ff0c0087..faaa6d3a23f1 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 <T> IndexedSet<T>.plusAssign(element: T) { add(element) } -inline fun <T> IndexedSet<T>.removeAllIndexed(predicate: (Int, T) -> Boolean) { +inline fun <T> IndexedSet<T>.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 <T> IndexedSet<T>.retainAllIndexed(predicate: (Int, T) -> Boolean) { +inline fun <T> IndexedSet<T>.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 9051c66120d8..0044b7359ef3 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 <T> IntMap<T>.putWithDefault(key: Int, value: T, defaultValue: T): T } } -inline fun <T> IntMap<T>.removeAllIndexed(predicate: (Int, Int, T) -> Boolean) { +inline fun <T> IntMap<T>.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 <T> IntMap<T>.retainAllIndexed(predicate: (Int, Int, T) -> Boolean) { +inline fun <T> IntMap<T>.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 <T> IntMap<T>.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 7cc2fe498b30..0d75a4c87a89 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 @@ -113,18 +113,24 @@ operator fun IntSet.plusAssign(array: IntArray) { array.forEach { this += it } } -inline fun IntSet.removeAllIndexed(predicate: (Int, Int) -> Boolean) { +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 dc28642bc2c6..d35e69e88e72 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 <T> List<T>.noneIndexed(predicate: (Int, T) -> Boolean): Boolean { return true } -inline fun <T> MutableList<T>.removeAllIndexed(predicate: (Int, T) -> Boolean) { +inline fun <T> MutableList<T>.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 <T> MutableList<T>.retainAllIndexed(predicate: (Int, T) -> Boolean) { +inline fun <T> MutableList<T>.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/permission/PermissionFlags.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt index bb1a86c7b32d..1b055202af7c 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/UidPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt index 74e4f21e926a..3d6d2ce34141 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 @@ -35,8 +35,8 @@ 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.util.andInv import com.android.server.permission.access.util.hasAnyBit import com.android.server.permission.access.util.hasBits import com.android.server.pm.KnownPackages @@ -48,6 +48,11 @@ import com.android.server.pm.pkg.PackageState class UidPermissionPolicy : SchemePolicy() { private val persistence = UidPermissionPersistence() + @Volatile + private var onPermissionFlagsChangedListeners = + IndexedListSet<OnPermissionFlagsChangedListener>() + private val onPermissionFlagsChangedListenersLock = Any() + override val subjectScheme: String get() = UidUri.SCHEME @@ -57,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( @@ -68,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.forEach { (_, packageState) -> - evaluateAllPermissionStatesForPackageAndUser(packageState, null, userId) + 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) { @@ -96,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) } @@ -120,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 } } } @@ -212,11 +213,12 @@ class UidPermissionPolicy : SchemePolicy() { 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 @@ -246,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 ) @@ -277,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<String> + ) { + 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 -> @@ -301,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 @@ -328,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 @@ -367,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) } } } @@ -383,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 -> @@ -415,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) { @@ -437,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) @@ -476,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 { @@ -502,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 @@ -515,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 @@ -530,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 @@ -847,7 +833,10 @@ class UidPermissionPolicy : SchemePolicy() { } override fun MutateStateScope.onPackageRemoved(packageName: String, appId: Int) { - // TODO + // 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) { @@ -864,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 @@ -878,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 + ) + } } |