1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.permission.access 18 19 import android.util.Log 20 import com.android.modules.utils.BinaryXmlPullParser 21 import com.android.modules.utils.BinaryXmlSerializer 22 import com.android.server.SystemConfig 23 import com.android.server.permission.access.appop.PackageAppOpPolicy 24 import com.android.server.permission.access.appop.UidAppOpPolicy 25 import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports 26 import com.android.server.permission.access.permission.UidPermissionPolicy 27 import com.android.server.permission.access.util.forEachTag 28 import com.android.server.permission.access.util.tag 29 import com.android.server.permission.access.util.tagName 30 import com.android.server.pm.permission.PermissionAllowlist 31 import com.android.server.pm.pkg.PackageState 32 33 class AccessPolicy private constructor( 34 private val schemePolicies: IndexedMap<String, IndexedMap<String, SchemePolicy>> 35 ) { 36 constructor() : this( 37 IndexedMap<String, IndexedMap<String, SchemePolicy>>().apply { 38 fun addPolicy(policy: SchemePolicy) = 39 getOrPut(policy.subjectScheme) { IndexedMap() }.put(policy.objectScheme, policy) 40 addPolicy(UidPermissionPolicy()) 41 addPolicy(UidAppOpPolicy()) 42 addPolicy(PackageAppOpPolicy()) 43 } 44 ) 45 46 fun getSchemePolicy(subjectScheme: String, objectScheme: String): SchemePolicy = 47 checkNotNull(schemePolicies[subjectScheme]?.get(objectScheme)) { 48 "Scheme policy for $subjectScheme and $objectScheme does not exist" 49 } 50 51 fun GetStateScope.getDecision(subject: AccessUri, `object`: AccessUri): Int = 52 with(getSchemePolicy(subject, `object`)){ getDecision(subject, `object`) } 53 54 fun MutateStateScope.setDecision(subject: AccessUri, `object`: AccessUri, decision: Int) { 55 with(getSchemePolicy(subject, `object`)) { setDecision(subject, `object`, decision) } 56 } 57 58 fun initialize( 59 state: AccessState, 60 userIds: IntSet, 61 packageStates: Map<String, PackageState>, 62 disabledSystemPackageStates: Map<String, PackageState>, 63 knownPackages: IntMap<Array<String>>, 64 isLeanback: Boolean, 65 configPermissions: Map<String, SystemConfig.PermissionEntry>, 66 privilegedPermissionAllowlistPackages: IndexedListSet<String>, 67 permissionAllowlist: PermissionAllowlist, 68 implicitToSourcePermissions: IndexedMap<String, IndexedListSet<String>> 69 ) { 70 state.systemState.apply { 71 this.userIds += userIds 72 this.packageStates = packageStates 73 this.disabledSystemPackageStates = disabledSystemPackageStates 74 packageStates.forEach { (_, packageState) -> 75 appIds.getOrPut(packageState.appId) { IndexedListSet() } 76 .add(packageState.packageName) 77 } 78 this.knownPackages = knownPackages 79 this.isLeanback = isLeanback 80 this.configPermissions = configPermissions 81 this.privilegedPermissionAllowlistPackages = privilegedPermissionAllowlistPackages 82 this.permissionAllowlist = permissionAllowlist 83 this.implicitToSourcePermissions = implicitToSourcePermissions 84 } 85 state.userStates.apply { 86 userIds.forEachIndexed { _, userId -> 87 this[userId] = UserState() 88 } 89 } 90 } 91 92 fun GetStateScope.onStateMutated() { 93 forEachSchemePolicy { 94 with(it) { onStateMutated() } 95 } 96 } 97 98 fun MutateStateScope.onInitialized() { 99 forEachSchemePolicy { 100 with(it) { onInitialized() } 101 } 102 } 103 104 fun MutateStateScope.onUserAdded(userId: Int) { 105 newState.systemState.userIds += userId 106 newState.userStates[userId] = UserState() 107 forEachSchemePolicy { 108 with(it) { onUserAdded(userId) } 109 } 110 } 111 112 fun MutateStateScope.onUserRemoved(userId: Int) { 113 newState.systemState.userIds -= userId 114 newState.userStates -= userId 115 forEachSchemePolicy { 116 with(it) { onUserRemoved(userId) } 117 } 118 } 119 120 fun MutateStateScope.onStorageVolumeMounted( 121 packageStates: Map<String, PackageState>, 122 disabledSystemPackageStates: Map<String, PackageState>, 123 knownPackages: IntMap<Array<String>>, 124 volumeUuid: String?, 125 isSystemUpdated: Boolean 126 ) { 127 val addedAppIds = IntSet() 128 newState.systemState.apply { 129 this.packageStates = packageStates 130 this.disabledSystemPackageStates = disabledSystemPackageStates 131 packageStates.forEach { (packageName, packageState) -> 132 if (packageState.volumeUuid == volumeUuid) { 133 val appId = packageState.appId 134 appIds.getOrPut(appId) { 135 addedAppIds += appId 136 IndexedListSet() 137 } += packageName 138 } 139 } 140 this.knownPackages = knownPackages 141 } 142 addedAppIds.forEachIndexed { _, appId -> 143 forEachSchemePolicy { 144 with(it) { onAppIdAdded(appId) } 145 } 146 } 147 forEachSchemePolicy { 148 with(it) { onStorageVolumeMounted(volumeUuid, isSystemUpdated) } 149 } 150 } 151 152 fun MutateStateScope.onPackageAdded( 153 packageStates: Map<String, PackageState>, 154 disabledSystemPackageStates: Map<String, PackageState>, 155 knownPackages: IntMap<Array<String>>, 156 packageName: String 157 ) { 158 val packageState = packageStates[packageName] 159 // TODO(zhanghai): STOPSHIP: Remove check before feature enable. 160 checkNotNull(packageState) { 161 "Added package $packageName isn't found in packageStates in onPackageAdded()" 162 } 163 val appId = packageState.appId 164 var isAppIdAdded = false 165 newState.systemState.apply { 166 this.packageStates = packageStates 167 this.disabledSystemPackageStates = disabledSystemPackageStates 168 appIds.getOrPut(appId) { 169 isAppIdAdded = true 170 IndexedListSet() 171 } += packageName 172 this.knownPackages = knownPackages 173 } 174 if (isAppIdAdded) { 175 forEachSchemePolicy { 176 with(it) { onAppIdAdded(appId) } 177 } 178 } 179 forEachSchemePolicy { 180 with(it) { onPackageAdded(packageState) } 181 } 182 } 183 184 fun MutateStateScope.onPackageRemoved( 185 packageStates: Map<String, PackageState>, 186 disabledSystemPackageStates: Map<String, PackageState>, 187 knownPackages: IntMap<Array<String>>, 188 packageName: String, 189 appId: Int 190 ) { 191 // TODO(zhanghai): STOPSHIP: Remove check before feature enable. 192 check(packageName !in packageStates) { 193 "Removed package $packageName is still in packageStates in onPackageRemoved()" 194 } 195 var isAppIdRemoved = false 196 newState.systemState.apply { 197 this.packageStates = packageStates 198 this.disabledSystemPackageStates = disabledSystemPackageStates 199 appIds[appId]?.apply { 200 this -= packageName 201 if (isEmpty()) { 202 appIds -= appId 203 isAppIdRemoved = true 204 } 205 } 206 this.knownPackages = knownPackages 207 } 208 forEachSchemePolicy { 209 with(it) { onPackageRemoved(packageName, appId) } 210 } 211 if (isAppIdRemoved) { 212 forEachSchemePolicy { 213 with(it) { onAppIdRemoved(appId) } 214 } 215 } 216 } 217 218 fun MutateStateScope.onPackageInstalled( 219 packageStates: Map<String, PackageState>, 220 disabledSystemPackageStates: Map<String, PackageState>, 221 knownPackages: IntMap<Array<String>>, 222 packageName: String, 223 userId: Int 224 ) { 225 newState.systemState.apply { 226 this.packageStates = packageStates 227 this.disabledSystemPackageStates = disabledSystemPackageStates 228 this.knownPackages = knownPackages 229 } 230 val packageState = packageStates[packageName] 231 // TODO(zhanghai): STOPSHIP: Remove check before feature enable. 232 checkNotNull(packageState) { 233 "Installed package $packageName isn't found in packageStates in onPackageInstalled()" 234 } 235 forEachSchemePolicy { 236 with(it) { onPackageInstalled(packageState, userId) } 237 } 238 } 239 240 fun MutateStateScope.onPackageUninstalled( 241 packageStates: Map<String, PackageState>, 242 disabledSystemPackageStates: Map<String, PackageState>, 243 knownPackages: IntMap<Array<String>>, 244 packageName: String, 245 appId: Int, 246 userId: Int 247 ) { 248 newState.systemState.apply { 249 this.packageStates = packageStates 250 this.disabledSystemPackageStates = disabledSystemPackageStates 251 this.knownPackages = knownPackages 252 } 253 forEachSchemePolicy { 254 with(it) { onPackageUninstalled(packageName, appId, userId) } 255 } 256 } 257 258 fun MutateStateScope.onSystemReady() { 259 newState.systemState.isSystemReady = true 260 forEachSchemePolicy { 261 with(it) { onSystemReady() } 262 } 263 } 264 265 fun BinaryXmlPullParser.parseSystemState(state: AccessState) { 266 forEachTag { 267 when (tagName) { 268 TAG_ACCESS -> { 269 forEachTag { 270 forEachSchemePolicy { 271 with(it) { parseSystemState(state) } 272 } 273 } 274 } 275 else -> Log.w(LOG_TAG, "Ignoring unknown tag $tagName when parsing system state") 276 } 277 } 278 } 279 280 fun BinaryXmlSerializer.serializeSystemState(state: AccessState) { 281 tag(TAG_ACCESS) { 282 forEachSchemePolicy { 283 with(it) { serializeSystemState(state) } 284 } 285 } 286 } 287 288 fun BinaryXmlPullParser.parseUserState(state: AccessState, userId: Int) { 289 forEachTag { 290 when (tagName) { 291 TAG_ACCESS -> { 292 forEachTag { 293 forEachSchemePolicy { 294 with(it) { parseUserState(state, userId) } 295 } 296 } 297 } 298 else -> { 299 Log.w( 300 LOG_TAG, 301 "Ignoring unknown tag $tagName when parsing user state for user $userId" 302 ) 303 } 304 } 305 } 306 } 307 308 fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) { 309 tag(TAG_ACCESS) { 310 forEachSchemePolicy { 311 with(it) { serializeUserState(state, userId) } 312 } 313 } 314 } 315 316 private fun getSchemePolicy(subject: AccessUri, `object`: AccessUri): SchemePolicy = 317 getSchemePolicy(subject.scheme, `object`.scheme) 318 319 private inline fun forEachSchemePolicy(action: (SchemePolicy) -> Unit) { 320 schemePolicies.forEachValueIndexed { _, objectSchemePolicies -> 321 objectSchemePolicies.forEachValueIndexed { _, schemePolicy -> 322 action(schemePolicy) 323 } 324 } 325 } 326 327 companion object { 328 private val LOG_TAG = AccessPolicy::class.java.simpleName 329 330 private const val TAG_ACCESS = "access" 331 } 332 } 333 334 abstract class SchemePolicy { 335 abstract val subjectScheme: String 336 337 abstract val objectScheme: String 338 339 abstract fun GetStateScope.getDecision(subject: AccessUri, `object`: AccessUri): Int 340 341 abstract fun MutateStateScope.setDecision( 342 subject: AccessUri, 343 `object`: AccessUri, 344 decision: Int 345 ) 346 347 open fun GetStateScope.onStateMutated() {} 348 349 open fun MutateStateScope.onInitialized() {} 350 351 open fun MutateStateScope.onUserAdded(userId: Int) {} 352 353 open fun MutateStateScope.onUserRemoved(userId: Int) {} 354 355 open fun MutateStateScope.onAppIdAdded(appId: Int) {} 356 357 open fun MutateStateScope.onAppIdRemoved(appId: Int) {} 358 359 open fun MutateStateScope.onStorageVolumeMounted( 360 volumeUuid: String?, 361 isSystemUpdated: Boolean 362 ) {} 363 364 open fun MutateStateScope.onPackageAdded(packageState: PackageState) {} 365 366 open fun MutateStateScope.onPackageRemoved(packageName: String, appId: Int) {} 367 368 open fun MutateStateScope.onPackageInstalled(packageState: PackageState, userId: Int) {} 369 370 open fun MutateStateScope.onPackageUninstalled(packageName: String, appId: Int, userId: Int) {} 371 372 open fun MutateStateScope.onSystemReady() {} 373 374 open fun BinaryXmlPullParser.parseSystemState(state: AccessState) {} 375 376 open fun BinaryXmlSerializer.serializeSystemState(state: AccessState) {} 377 378 open fun BinaryXmlPullParser.parseUserState(state: AccessState, userId: Int) {} 379 380 open fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {} 381 } 382