1 /* 2 * Copyright (C) 2020 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.permissioncontroller.tests.mocking.role.model 18 19 import android.app.AppOpsManager 20 import android.content.pm.PackageManager 21 import android.content.pm.PermissionInfo 22 import android.os.Process 23 import androidx.test.ext.junit.runners.AndroidJUnit4 24 import androidx.test.platform.app.InstrumentationRegistry 25 import com.android.permissioncontroller.role.model.RoleParserInitializer 26 import com.android.role.controller.model.AppOp 27 import com.android.role.controller.model.Permission 28 import com.android.role.controller.model.PermissionSet 29 import com.android.role.controller.model.Role 30 import com.android.role.controller.model.RoleParser 31 import org.junit.BeforeClass 32 import org.junit.Test 33 import org.junit.runner.RunWith 34 35 @RunWith(AndroidJUnit4::class) 36 class RoleParserTest { 37 companion object { 38 @BeforeClass 39 @JvmStatic setupBeforeClassnull40 fun setupBeforeClass() { 41 RoleParserInitializer.initialize() 42 } 43 } 44 45 private val instrumentation = InstrumentationRegistry.getInstrumentation() 46 private val uiAutomation = instrumentation.uiAutomation 47 private val targetContext = instrumentation.targetContext 48 private val packageManager = targetContext.packageManager 49 50 @Test parseRolesnull51 fun parseRoles() { 52 // We may need to call privileged APIs to determine availability of things. 53 uiAutomation.adoptShellPermissionIdentity() 54 try { 55 RoleParser(targetContext, true).parse() 56 } finally { 57 uiAutomation.dropShellPermissionIdentity() 58 } 59 } 60 61 @Test validateRolesnull62 fun validateRoles() { 63 // We may need to call privileged APIs to determine availability of things. 64 uiAutomation.adoptShellPermissionIdentity() 65 try { 66 val xml = RoleParser(targetContext).parseRolesXml() 67 requireNotNull(xml) 68 validateRoles(xml.first, xml.second) 69 } finally { 70 uiAutomation.dropShellPermissionIdentity() 71 } 72 } 73 validateRolesnull74 fun validateRoles(permissionSets: Map<String, PermissionSet>, roles: Map<String, Role>) { 75 for (permissionSet in permissionSets.values) { 76 for (permission in permissionSet.permissions) { 77 validatePermission(permission) 78 } 79 } 80 81 for (role in roles.values) { 82 if (!role.isAvailableByFeatureFlagAndSdkVersion) { 83 continue 84 } 85 86 for (requiredComponent in role.requiredComponents) { 87 val permission = requiredComponent.permission 88 if (permission != null) { 89 validatePermission(permission) 90 } 91 } 92 93 for (permission in role.permissions) { 94 validatePermission(permission) 95 } 96 97 for (appOp in role.appOps) { 98 validateAppOp(appOp) 99 } 100 101 for (appOpPermission in role.appOpPermissions) { 102 validateAppOpPermission(appOpPermission) 103 } 104 105 for (preferredActivity in role.preferredActivities) { 106 require(preferredActivity.activity in role.requiredComponents) { 107 "<activity> of <preferred-activity> not required in <required-components>," + 108 " role: ${role.name}, preferred activity: $preferredActivity" 109 } 110 } 111 } 112 } 113 validatePermissionnull114 private fun validatePermission(permission: Permission) { 115 if (!permission.isAvailableAsUser(Process.myUserHandle(), targetContext)) { 116 return 117 } 118 validatePermission(permission.name, true) 119 } 120 validatePermissionnull121 private fun validatePermission(permissionName: String) { 122 validatePermission(permissionName, false) 123 } 124 validatePermissionnull125 private fun validatePermission(permissionName: String, enforceIsRuntimeOrRole: Boolean) { 126 val isAutomotive = packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) 127 // Skip validation for car permissions which may not be available on all build targets. 128 if (!isAutomotive && permissionName.startsWith("android.car")) { 129 return 130 } 131 132 val permissionInfo = 133 try { 134 packageManager.getPermissionInfo(permissionName, 0) 135 } catch (e: PackageManager.NameNotFoundException) { 136 throw IllegalArgumentException("Unknown permission: $permissionName", e) 137 } 138 139 if (enforceIsRuntimeOrRole) { 140 require( 141 permissionInfo.protection == PermissionInfo.PROTECTION_DANGEROUS || 142 permissionInfo.protectionFlags and PermissionInfo.PROTECTION_FLAG_ROLE == 143 PermissionInfo.PROTECTION_FLAG_ROLE 144 ) { 145 "Permission is not a runtime or role permission: $permissionName" 146 } 147 } 148 } 149 validateAppOpPermissionnull150 private fun validateAppOpPermission(appOpPermission: Permission) { 151 if (!appOpPermission.isAvailableAsUser(Process.myUserHandle(), targetContext)) { 152 return 153 } 154 validateAppOpPermission(appOpPermission.name) 155 } 156 validateAppOpPermissionnull157 private fun validateAppOpPermission(permissionName: String) { 158 val permissionInfo = 159 try { 160 packageManager.getPermissionInfo(permissionName, 0) 161 } catch (e: PackageManager.NameNotFoundException) { 162 throw IllegalArgumentException("Unknown app op permission: $permissionName", e) 163 } 164 require( 165 permissionInfo.protectionFlags and PermissionInfo.PROTECTION_FLAG_APPOP == 166 PermissionInfo.PROTECTION_FLAG_APPOP 167 ) { 168 "Permission is not an app op permission: $permissionName" 169 } 170 } 171 validateAppOpnull172 private fun validateAppOp(appOp: AppOp) { 173 if (!appOp.isAvailableByFeatureFlagAndSdkVersion) { 174 return 175 } 176 // This throws IllegalArgumentException if app op is unknown. 177 val permissionName = AppOpsManager.opToPermission(appOp.name) 178 if (permissionName != null) { 179 val permissionInfo = packageManager.getPermissionInfo(permissionName, 0) 180 require(permissionInfo.protection != PermissionInfo.PROTECTION_DANGEROUS) { 181 "App op has an associated runtime permission: ${appOp.name}" 182 } 183 } 184 } 185 } 186