From a8c89fe80740a58094a925f9cf73885f2c759fb4 Mon Sep 17 00:00:00 2001 From: Hai Zhang Date: Sat, 10 Dec 2022 22:46:56 -0800 Subject: [PATCH 1/3] Make AccessCheckingService a SystemService. And connect it with the existing parts of the system, so that we can remove all the stub classes created for compilation. The new implementations of the app op and permission service interface are now published via LocalServices and available in services-core. The external states are now actually available, including the package states being provided by PackageManagerLocal. The callbacks are temporarily being delivered by the new permission service because only it has the blocking callbacks at the right timing during package changes. A new intialize() function is added for the new app op and permission services. It is called when the core system services (including ours) have read the on-disk state and are ready, so that our services can now retrieve their dependent services and start working. Any call to the new services before this call can't have meaningful results and should be no-op'ed in some way. Bug: 182523293 Test: presubmit Change-Id: I9c4ec4b7d56ec9d97c4b097cacc7680fa0a08690 --- .../access/AccessCheckingService.kt | 89 +++++++++++-- .../server/permission/access/AccessPolicy.kt | 118 ++++++++++++++---- .../server/permission/access/AccessState.kt | 14 +-- .../server/permission/access/AccessUri.kt | 5 +- ...ngServiceCompatImpl.kt => AppOpService.kt} | 10 +- .../access/appop/PackageAppOpPolicy.kt | 5 +- .../permission/access/collection/IntSet.kt | 10 ++ .../server/permission/access/data/Package.kt | 43 ------- .../external/CompatibilityPermissionInfo.kt | 29 ----- .../access/external/KnownPackages.kt | 34 ----- .../access/external/PackageInfoUtils.kt | 33 ----- .../access/external/PackageState.kt | 65 ---------- .../access/external/RoSystemProperties.kt | 24 ---- .../access/external/SigningDetails.kt | 42 ------- .../permission/access/external/UserHandle.kt | 31 ----- .../access/{data => permission}/Permission.kt | 2 +- ...gerServiceImpl.kt => PermissionService.kt} | 74 ++++++++--- .../permission/UidPermissionPersistence.kt | 1 - .../access/permission/UidPermissionPolicy.kt | 64 +++++----- 19 files changed, 292 insertions(+), 401 deletions(-) rename services/permission/java/com/android/server/permission/access/appop/{AppOpsCheckingServiceCompatImpl.kt => AppOpService.kt} (95%) delete mode 100644 services/permission/java/com/android/server/permission/access/data/Package.kt delete mode 100644 services/permission/java/com/android/server/permission/access/external/CompatibilityPermissionInfo.kt delete mode 100644 services/permission/java/com/android/server/permission/access/external/KnownPackages.kt delete mode 100644 services/permission/java/com/android/server/permission/access/external/PackageInfoUtils.kt delete mode 100644 services/permission/java/com/android/server/permission/access/external/PackageState.kt delete mode 100644 services/permission/java/com/android/server/permission/access/external/RoSystemProperties.kt delete mode 100644 services/permission/java/com/android/server/permission/access/external/SigningDetails.kt delete mode 100644 services/permission/java/com/android/server/permission/access/external/UserHandle.kt rename services/permission/java/com/android/server/permission/access/{data => permission}/Permission.kt (98%) rename services/permission/java/com/android/server/permission/access/permission/{ModernPermissionManagerServiceImpl.kt => PermissionService.kt} (84%) 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..4c794931cf981 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 }, 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/IntSet.kt b/services/permission/java/com/android/server/permission/access/collection/IntSet.kt index d6dfe9d0eb371..7cc2fe498b301 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,6 +105,14 @@ inline operator fun IntSet.plusAssign(element: Int) { add(element) } +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) { for (index in lastIndex downTo 0) { if (predicate(index, elementAt(index))) { 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/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..74e4f21e926a2 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 @@ -35,16 +37,13 @@ import com.android.server.permission.access.SystemState import com.android.server.permission.access.UidUri import com.android.server.permission.access.UserState import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports -import com.android.server.permission.access.data.Permission -import com.android.server.permission.access.external.AndroidPackage -import com.android.server.permission.access.external.CompatibilityPermissionInfo -import com.android.server.permission.access.external.KnownPackages -import com.android.server.permission.access.external.PackageInfoUtils -import com.android.server.permission.access.external.PackageState -import com.android.server.permission.access.external.RoSystemProperties -import com.android.server.permission.access.external.SigningDetails import com.android.server.permission.access.util.hasAnyBit import com.android.server.permission.access.util.hasBits +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() @@ -75,7 +74,7 @@ class UidPermissionPolicy : SchemePolicy() { } override fun MutateStateScope.onUserAdded(userId: Int) { - newState.systemState.packageStates.forEachValueIndexed { _, packageState -> + newState.systemState.packageStates.forEach { (_, packageState) -> evaluateAllPermissionStatesForPackageAndUser(packageState, null, userId) grantImplicitPermissions(packageState, userId) } @@ -179,7 +178,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,7 +210,7 @@ class UidPermissionPolicy : SchemePolicy() { // } val newPermissionInfo = PackageInfoUtils.generatePermissionInfo( parsedPermission, PackageManager.GET_META_DATA.toLong() - ) + )!! // TODO: newPermissionInfo.flags |= PermissionInfo.FLAG_INSTALLED val permissionName = newPermissionInfo.name val oldPermission = if (parsedPermission.isTree) { @@ -573,23 +572,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 +629,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 +660,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 +747,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,7 +846,7 @@ class UidPermissionPolicy : SchemePolicy() { return uid == ownerUid } - override fun MutateStateScope.onPackageRemoved(packageState: PackageState) { + override fun MutateStateScope.onPackageRemoved(packageName: String, appId: Int) { // TODO } From c8a46e14553e9dca1f5e0be37898f573adebe5de Mon Sep 17 00:00:00 2001 From: Hai Zhang Date: Sat, 10 Dec 2022 23:21:10 -0800 Subject: [PATCH 2/3] Start AccessCheckingService in SystemServer. The service constructor and onStart() only does trivial work and its start time is also tracked by TimingsTraceAndSlog. The new app op and permission implementation will only be active when they are later selected inside the existing permission and app op system services and that selection will happen behind a feature flag disabled by default. Bug: 182523293 Test: presubmit Change-Id: I2d678ef2d5747440afebc92868f92c4a02565856 --- services/java/com/android/server/SystemServer.java | 6 ++++++ 1 file changed, 6 insertions(+) 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. From c6abc0023f7291ece958d943e5666c69c289cec0 Mon Sep 17 00:00:00 2001 From: Hai Zhang Date: Mon, 12 Dec 2022 01:41:11 -0800 Subject: [PATCH 3/3] Various refactors to UidPermissionPolicy code. - Use custom flag getter, setter, updater and change listener, since this allows the compat layer to call it more efficiently. The URI oriented methods are now only for future unified APIs, if there will be any. - Made the change listener actually working by calling it in the updatePermissionFlags() method which should be the one and only method that mutates a permission flag. - Made the persistence actually working by calling requestWrite() in relevant methods. - Added the boolean return value for removeAllIndexed() and retainAllIndexed() for consistency with kotlin-stdlib APIs, and used it when deciding whether to call requestWrite(). - Renamed UserState.permissionFlags to uidPermissionFlags. Now we have a consistent way to reference different levels in the hierarchy - the top level is uidPermissionFlags, the map from permission name to flags is permissionFlags, while the flags is just flags. - Switched to keeping only app ID states with non-empty maps. This is more consistent with what we do with app ops and possible future parameterized permissions, and may also save a little bit of memory for things like RRO packages. This also means we should always iterate over the user IDs and app IDs in system state instead of on userStates or uidPermissoinFlags, which may not be complete and may be modified during iteration. - Moved the userId parameter to be following appId or packageState, instead of always being at the end. It used to make sense for always being at the end because some parts of the system may not be multi-user aware so an optional userId parameter at the end made more sense. However we are always multi-user aware and passing the userId alone at the end simply started to look too unnatural. - Refactored trimPermissions() to better handle permission removal, and delegating setting INSTALL_REVOKED to evalutePermissionState() for consistency. Bug: 182523293 Test: presubmit Change-Id: I3aa177e52190c5996c6d9ecbc7030c6d7b5ae429 --- .../server/permission/access/AccessState.kt | 9 +- .../access/collection/IndexedList.kt | 10 +- .../access/collection/IndexedListSet.kt | 10 +- .../access/collection/IndexedMap.kt | 10 +- .../access/collection/IndexedSet.kt | 10 +- .../permission/access/collection/IntMap.kt | 10 +- .../permission/access/collection/IntSet.kt | 10 +- .../permission/access/collection/List.kt | 10 +- .../access/permission/PermissionFlags.kt | 1 + .../access/permission/UidPermissionPolicy.kt | 267 +++++++++++------- 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 4c794931cf981..4a2c78a86a937 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>, + 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/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 7cc2fe498b301..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 @@ -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 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/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/UidPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt index 74e4f21e926a2..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 @@ -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() + 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 + ) { + 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 + ) + } }