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 android.content.pm.parsing.component; 18 19 import android.annotation.NonNull; 20 import android.content.pm.PermissionInfo; 21 import android.content.pm.parsing.ParsingPackage; 22 import android.content.pm.parsing.ParsingUtils; 23 import android.content.pm.parsing.result.ParseInput; 24 import android.content.pm.parsing.result.ParseResult; 25 import android.content.res.Resources; 26 import android.content.res.TypedArray; 27 import android.content.res.XmlResourceParser; 28 import android.util.Slog; 29 30 import com.android.internal.R; 31 32 import org.xmlpull.v1.XmlPullParserException; 33 34 import java.io.IOException; 35 36 /** @hide */ 37 public class ParsedPermissionUtils { 38 39 private static final String TAG = ParsingUtils.TAG; 40 41 @NonNull parsePermission(ParsingPackage pkg, Resources res, XmlResourceParser parser, boolean useRoundIcon, ParseInput input)42 public static ParseResult<ParsedPermission> parsePermission(ParsingPackage pkg, Resources res, 43 XmlResourceParser parser, boolean useRoundIcon, ParseInput input) 44 throws IOException, XmlPullParserException { 45 String packageName = pkg.getPackageName(); 46 ParsedPermission 47 permission = new ParsedPermission(); 48 String tag = "<" + parser.getName() + ">"; 49 final ParseResult<ParsedPermission> result; 50 51 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermission); 52 try { 53 result = ParsedComponentUtils.parseComponent( 54 permission, tag, pkg, sa, useRoundIcon, input, 55 R.styleable.AndroidManifestPermission_banner, 56 R.styleable.AndroidManifestPermission_description, 57 R.styleable.AndroidManifestPermission_icon, 58 R.styleable.AndroidManifestPermission_label, 59 R.styleable.AndroidManifestPermission_logo, 60 R.styleable.AndroidManifestPermission_name, 61 R.styleable.AndroidManifestPermission_roundIcon); 62 if (result.isError()) { 63 return result; 64 } 65 66 if (sa.hasValue( 67 R.styleable.AndroidManifestPermission_backgroundPermission)) { 68 if ("android".equals(packageName)) { 69 permission.backgroundPermission = sa.getNonResourceString( 70 R.styleable 71 .AndroidManifestPermission_backgroundPermission); 72 } else { 73 Slog.w(TAG, packageName + " defines a background permission. Only the " 74 + "'android' package can do that."); 75 } 76 } 77 78 // Note: don't allow this value to be a reference to a resource 79 // that may change. 80 permission.setGroup(sa.getNonResourceString( 81 R.styleable.AndroidManifestPermission_permissionGroup)); 82 83 permission.requestRes = sa.getResourceId( 84 R.styleable.AndroidManifestPermission_request, 0); 85 86 permission.protectionLevel = sa.getInt( 87 R.styleable.AndroidManifestPermission_protectionLevel, 88 PermissionInfo.PROTECTION_NORMAL); 89 90 permission.flags = sa.getInt( 91 R.styleable.AndroidManifestPermission_permissionFlags, 0); 92 93 final int knownCertsResource = sa.getResourceId( 94 R.styleable.AndroidManifestPermission_knownCerts, 0); 95 if (knownCertsResource != 0) { 96 // The knownCerts attribute supports both a string array resource as well as a 97 // string resource for the case where the permission should only be granted to a 98 // single known signer. 99 final String resourceType = res.getResourceTypeName(knownCertsResource); 100 if (resourceType.equals("array")) { 101 final String[] knownCerts = res.getStringArray(knownCertsResource); 102 if (knownCerts != null) { 103 permission.setKnownCerts(knownCerts); 104 } 105 } else { 106 final String knownCert = res.getString(knownCertsResource); 107 if (knownCert != null) { 108 permission.setKnownCert(knownCert); 109 } 110 } 111 if (permission.knownCerts == null) { 112 Slog.w(TAG, packageName + " defines a knownSigner permission but" 113 + " the provided knownCerts resource is null"); 114 } 115 } else { 116 // If the knownCerts resource ID is null check if the app specified a string 117 // value for the attribute representing a single trusted signer. 118 final String knownCert = sa.getString( 119 R.styleable.AndroidManifestPermission_knownCerts); 120 if (knownCert != null) { 121 permission.setKnownCert(knownCert); 122 } 123 } 124 125 // For now only platform runtime permissions can be restricted 126 if (!permission.isRuntime() || !"android".equals(permission.getPackageName())) { 127 permission.flags &= ~PermissionInfo.FLAG_HARD_RESTRICTED; 128 permission.flags &= ~PermissionInfo.FLAG_SOFT_RESTRICTED; 129 } else { 130 // The platform does not get to specify conflicting permissions 131 if ((permission.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0 132 && (permission.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0) { 133 throw new IllegalStateException("Permission cannot be both soft and hard" 134 + " restricted: " + permission.getName()); 135 } 136 } 137 } finally { 138 sa.recycle(); 139 } 140 141 permission.protectionLevel = PermissionInfo.fixProtectionLevel(permission.protectionLevel); 142 143 final int otherProtectionFlags = permission.getProtectionFlags() 144 & ~(PermissionInfo.PROTECTION_FLAG_APPOP | PermissionInfo.PROTECTION_FLAG_INSTANT 145 | PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY); 146 if (otherProtectionFlags != 0 147 && permission.getProtection() != PermissionInfo.PROTECTION_SIGNATURE 148 && permission.getProtection() != PermissionInfo.PROTECTION_INTERNAL) { 149 return input.error("<permission> protectionLevel specifies a non-instant, non-appop," 150 + " non-runtimeOnly flag but is not based on signature or internal type"); 151 } 152 153 return ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permission, input); 154 } 155 156 @NonNull parsePermissionTree(ParsingPackage pkg, Resources res, XmlResourceParser parser, boolean useRoundIcon, ParseInput input)157 public static ParseResult<ParsedPermission> parsePermissionTree(ParsingPackage pkg, Resources res, 158 XmlResourceParser parser, boolean useRoundIcon, ParseInput input) 159 throws IOException, XmlPullParserException { 160 ParsedPermission permission = new ParsedPermission(); 161 String tag = "<" + parser.getName() + ">"; 162 final ParseResult<ParsedPermission> result; 163 164 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermissionTree); 165 try { 166 result = ParsedComponentUtils.parseComponent( 167 permission, tag, pkg, sa, useRoundIcon, input, 168 R.styleable.AndroidManifestPermissionTree_banner, 169 null /*descriptionAttr*/, 170 R.styleable.AndroidManifestPermissionTree_icon, 171 R.styleable.AndroidManifestPermissionTree_label, 172 R.styleable.AndroidManifestPermissionTree_logo, 173 R.styleable.AndroidManifestPermissionTree_name, 174 R.styleable.AndroidManifestPermissionTree_roundIcon); 175 if (result.isError()) { 176 return result; 177 } 178 } finally { 179 sa.recycle(); 180 } 181 182 int index = permission.getName().indexOf('.'); 183 if (index > 0) { 184 index = permission.getName().indexOf('.', index + 1); 185 } 186 if (index < 0) { 187 return input.error("<permission-tree> name has less than three segments: " 188 + permission.getName()); 189 } 190 191 permission.protectionLevel = PermissionInfo.PROTECTION_NORMAL; 192 permission.tree = true; 193 194 return ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permission, 195 input); 196 } 197 198 @NonNull parsePermissionGroup(ParsingPackage pkg, Resources res, XmlResourceParser parser, boolean useRoundIcon, ParseInput input)199 public static ParseResult<ParsedPermissionGroup> parsePermissionGroup(ParsingPackage pkg, 200 Resources res, XmlResourceParser parser, boolean useRoundIcon, ParseInput input) 201 throws IOException, XmlPullParserException { 202 ParsedPermissionGroup 203 permissionGroup = new ParsedPermissionGroup(); 204 String tag = "<" + parser.getName() + ">"; 205 206 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermissionGroup); 207 try { 208 ParseResult<ParsedPermissionGroup> result = ParsedComponentUtils.parseComponent( 209 permissionGroup, tag, pkg, sa, useRoundIcon, input, 210 R.styleable.AndroidManifestPermissionGroup_banner, 211 R.styleable.AndroidManifestPermissionGroup_description, 212 R.styleable.AndroidManifestPermissionGroup_icon, 213 R.styleable.AndroidManifestPermissionGroup_label, 214 R.styleable.AndroidManifestPermissionGroup_logo, 215 R.styleable.AndroidManifestPermissionGroup_name, 216 R.styleable.AndroidManifestPermissionGroup_roundIcon); 217 if (result.isError()) { 218 return result; 219 } 220 221 // @formatter:off 222 permissionGroup.requestDetailResourceId = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_requestDetail, 0); 223 permissionGroup.backgroundRequestResourceId = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_backgroundRequest, 0); 224 permissionGroup.backgroundRequestDetailResourceId = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_backgroundRequestDetail, 0); 225 permissionGroup.requestRes = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_request, 0); 226 permissionGroup.flags = sa.getInt(R.styleable.AndroidManifestPermissionGroup_permissionGroupFlags,0); 227 permissionGroup.priority = sa.getInt(R.styleable.AndroidManifestPermissionGroup_priority, 0); 228 // @formatter:on 229 } finally { 230 sa.recycle(); 231 } 232 233 return ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permissionGroup, 234 input); 235 } 236 } 237