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 static android.content.pm.parsing.component.ComponentParseUtils.flag; 20 21 import android.annotation.NonNull; 22 import android.content.pm.PackageParser; 23 import android.content.pm.PathPermission; 24 import android.content.pm.ProviderInfo; 25 import android.content.pm.parsing.ParsingPackage; 26 import android.content.pm.parsing.ParsingUtils; 27 import android.content.pm.parsing.result.ParseInput; 28 import android.content.pm.parsing.result.ParseResult; 29 import android.content.res.Resources; 30 import android.content.res.TypedArray; 31 import android.content.res.XmlResourceParser; 32 import android.os.Build; 33 import android.os.PatternMatcher; 34 import android.util.Slog; 35 36 import com.android.internal.R; 37 38 import org.xmlpull.v1.XmlPullParser; 39 import org.xmlpull.v1.XmlPullParserException; 40 41 import java.io.IOException; 42 import java.util.Objects; 43 44 /** @hide */ 45 public class ParsedProviderUtils { 46 47 private static final String TAG = ParsingUtils.TAG; 48 49 @NonNull parseProvider(String[] separateProcesses, ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags, boolean useRoundIcon, ParseInput input)50 public static ParseResult<ParsedProvider> parseProvider(String[] separateProcesses, 51 ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags, 52 boolean useRoundIcon, ParseInput input) 53 throws IOException, XmlPullParserException { 54 String authority; 55 boolean visibleToEphemeral; 56 57 final int targetSdkVersion = pkg.getTargetSdkVersion(); 58 final String packageName = pkg.getPackageName(); 59 final ParsedProvider provider = new ParsedProvider(); 60 final String tag = parser.getName(); 61 62 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProvider); 63 try { 64 ParseResult<ParsedProvider> result = 65 ParsedMainComponentUtils.parseMainComponent(provider, tag, separateProcesses, 66 pkg, sa, flags, useRoundIcon, input, 67 R.styleable.AndroidManifestProvider_banner, 68 R.styleable.AndroidManifestProvider_description, 69 R.styleable.AndroidManifestProvider_directBootAware, 70 R.styleable.AndroidManifestProvider_enabled, 71 R.styleable.AndroidManifestProvider_icon, 72 R.styleable.AndroidManifestProvider_label, 73 R.styleable.AndroidManifestProvider_logo, 74 R.styleable.AndroidManifestProvider_name, 75 R.styleable.AndroidManifestProvider_process, 76 R.styleable.AndroidManifestProvider_roundIcon, 77 R.styleable.AndroidManifestProvider_splitName, 78 R.styleable.AndroidManifestProvider_attributionTags); 79 if (result.isError()) { 80 return result; 81 } 82 83 authority = sa.getNonConfigurationString(R.styleable.AndroidManifestProvider_authorities, 0); 84 85 // For compatibility, applications targeting API level 16 or lower 86 // should have their content providers exported by default, unless they 87 // specify otherwise. 88 provider.exported = sa.getBoolean(R.styleable.AndroidManifestProvider_exported, 89 targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1); 90 91 provider.syncable = sa.getBoolean(R.styleable.AndroidManifestProvider_syncable, false); 92 93 String permission = sa.getNonConfigurationString( 94 R.styleable.AndroidManifestProvider_permission, 0); 95 String readPermission = sa.getNonConfigurationString( 96 R.styleable.AndroidManifestProvider_readPermission, 0); 97 if (readPermission == null) { 98 readPermission = permission; 99 } 100 if (readPermission == null) { 101 provider.setReadPermission(pkg.getPermission()); 102 } else { 103 provider.setReadPermission(readPermission); 104 } 105 String writePermission = sa.getNonConfigurationString( 106 R.styleable.AndroidManifestProvider_writePermission, 0); 107 if (writePermission == null) { 108 writePermission = permission; 109 } 110 if (writePermission == null) { 111 provider.setWritePermission(pkg.getPermission()); 112 } else { 113 provider.setWritePermission(writePermission); 114 } 115 116 provider.grantUriPermissions = sa.getBoolean(R.styleable.AndroidManifestProvider_grantUriPermissions, false); 117 provider.forceUriPermissions = sa.getBoolean(R.styleable.AndroidManifestProvider_forceUriPermissions, false); 118 provider.multiProcess = sa.getBoolean(R.styleable.AndroidManifestProvider_multiprocess, false); 119 provider.initOrder = sa.getInt(R.styleable.AndroidManifestProvider_initOrder, 0); 120 121 provider.flags |= flag(ProviderInfo.FLAG_SINGLE_USER, R.styleable.AndroidManifestProvider_singleUser, sa); 122 123 visibleToEphemeral = sa.getBoolean(R.styleable.AndroidManifestProvider_visibleToInstantApps, false); 124 if (visibleToEphemeral) { 125 provider.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP; 126 pkg.setVisibleToInstantApps(true); 127 } 128 } finally { 129 sa.recycle(); 130 } 131 132 if (pkg.isCantSaveState()) { 133 // A heavy-weight application can not have providers in its main process 134 if (Objects.equals(provider.getProcessName(), packageName)) { 135 return input.error("Heavy-weight applications can not have providers" 136 + " in main process"); 137 } 138 } 139 140 if (authority == null) { 141 return input.error("<provider> does not include authorities attribute"); 142 } 143 if (authority.length() <= 0) { 144 return input.error("<provider> has empty authorities attribute"); 145 } 146 provider.setAuthority(authority); 147 148 return parseProviderTags(pkg, tag, res, parser, visibleToEphemeral, provider, input); 149 } 150 151 @NonNull parseProviderTags(ParsingPackage pkg, String tag, Resources res, XmlResourceParser parser, boolean visibleToEphemeral, ParsedProvider provider, ParseInput input)152 private static ParseResult<ParsedProvider> parseProviderTags(ParsingPackage pkg, String tag, 153 Resources res, XmlResourceParser parser, boolean visibleToEphemeral, 154 ParsedProvider provider, ParseInput input) 155 throws XmlPullParserException, IOException { 156 final int depth = parser.getDepth(); 157 int type; 158 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 159 && (type != XmlPullParser.END_TAG 160 || parser.getDepth() > depth)) { 161 if (type != XmlPullParser.START_TAG) { 162 continue; 163 } 164 165 String name = parser.getName(); 166 final ParseResult result; 167 switch (name) { 168 case "intent-filter": 169 ParseResult<ParsedIntentInfo> intentResult = ParsedMainComponentUtils 170 .parseIntentFilter(provider, pkg, res, parser, visibleToEphemeral, 171 true /*allowGlobs*/, false /*allowAutoVerify*/, 172 false /*allowImplicitEphemeralVisibility*/, 173 false /*failOnNoActions*/, input); 174 result = intentResult; 175 if (intentResult.isSuccess()) { 176 ParsedIntentInfo intent = intentResult.getResult(); 177 provider.order = Math.max(intent.getOrder(), provider.order); 178 provider.addIntent(intent); 179 } 180 break; 181 case "meta-data": 182 result = ParsedComponentUtils.addMetaData(provider, pkg, res, parser, input); 183 break; 184 case "property": 185 result = ParsedComponentUtils.addProperty(provider, pkg, res, parser, input); 186 break; 187 case "grant-uri-permission": { 188 result = parseGrantUriPermission(provider, pkg, res, parser, input); 189 break; 190 } 191 case "path-permission": { 192 result = parsePathPermission(provider, pkg, res, parser, input); 193 break; 194 } 195 default: 196 result = ParsingUtils.unknownTag(tag, pkg, parser, input); 197 break; 198 } 199 200 if (result.isError()) { 201 return input.error(result); 202 } 203 } 204 205 return input.success(provider); 206 } 207 208 @NonNull parseGrantUriPermission(ParsedProvider provider, ParsingPackage pkg, Resources resources, XmlResourceParser parser, ParseInput input)209 private static ParseResult<ParsedProvider> parseGrantUriPermission(ParsedProvider provider, 210 ParsingPackage pkg, Resources resources, XmlResourceParser parser, ParseInput input) { 211 TypedArray sa = resources.obtainAttributes(parser, 212 R.styleable.AndroidManifestGrantUriPermission); 213 try { 214 String name = parser.getName(); 215 // Pattern has priority over pre/suffix over literal path 216 PatternMatcher pa = null; 217 String str = sa.getNonConfigurationString( 218 R.styleable.AndroidManifestGrantUriPermission_pathAdvancedPattern, 0); 219 if (str != null) { 220 pa = new PatternMatcher(str, PatternMatcher.PATTERN_ADVANCED_GLOB); 221 } else { 222 str = sa.getNonConfigurationString( 223 R.styleable.AndroidManifestGrantUriPermission_pathPattern, 0); 224 if (str != null) { 225 pa = new PatternMatcher(str, PatternMatcher.PATTERN_SIMPLE_GLOB); 226 } else { 227 str = sa.getNonConfigurationString( 228 R.styleable.AndroidManifestGrantUriPermission_pathPrefix, 0); 229 if (str != null) { 230 pa = new PatternMatcher(str, PatternMatcher.PATTERN_PREFIX); 231 } else { 232 str = sa.getNonConfigurationString( 233 R.styleable.AndroidManifestGrantUriPermission_pathSuffix, 0); 234 if (str != null) { 235 pa = new PatternMatcher(str, PatternMatcher.PATTERN_SUFFIX); 236 } else { 237 str = sa.getNonConfigurationString( 238 R.styleable.AndroidManifestGrantUriPermission_path, 0); 239 if (str != null) { 240 pa = new PatternMatcher(str, PatternMatcher.PATTERN_LITERAL); 241 } 242 } 243 } 244 } 245 } 246 247 if (pa != null) { 248 if (provider.uriPermissionPatterns == null) { 249 provider.uriPermissionPatterns = new PatternMatcher[1]; 250 provider.uriPermissionPatterns[0] = pa; 251 } else { 252 final int N = provider.uriPermissionPatterns.length; 253 PatternMatcher[] newp = new PatternMatcher[N + 1]; 254 System.arraycopy(provider.uriPermissionPatterns, 0, newp, 0, N); 255 newp[N] = pa; 256 provider.uriPermissionPatterns = newp; 257 } 258 provider.grantUriPermissions = true; 259 } else { 260 if (PackageParser.RIGID_PARSER) { 261 return input.error("No path, pathPrefix, or pathPattern for <path-permission>"); 262 } 263 264 Slog.w(TAG, "Unknown element under <path-permission>: " + name + " at " 265 + pkg.getBaseApkPath() + " " + parser.getPositionDescription()); 266 } 267 268 return input.success(provider); 269 } finally { 270 sa.recycle(); 271 } 272 } 273 274 @NonNull parsePathPermission(ParsedProvider provider, ParsingPackage pkg, Resources resources, XmlResourceParser parser, ParseInput input)275 private static ParseResult<ParsedProvider> parsePathPermission(ParsedProvider provider, 276 ParsingPackage pkg, Resources resources, XmlResourceParser parser, ParseInput input) { 277 TypedArray sa = resources.obtainAttributes(parser, 278 R.styleable.AndroidManifestPathPermission); 279 try { 280 String name = parser.getName(); 281 282 String permission = sa.getNonConfigurationString( 283 R.styleable.AndroidManifestPathPermission_permission, 0); 284 String readPermission = sa.getNonConfigurationString( 285 R.styleable.AndroidManifestPathPermission_readPermission, 0); 286 if (readPermission == null) { 287 readPermission = permission; 288 } 289 String writePermission = sa.getNonConfigurationString( 290 R.styleable.AndroidManifestPathPermission_writePermission, 0); 291 if (writePermission == null) { 292 writePermission = permission; 293 } 294 295 boolean havePerm = false; 296 if (readPermission != null) { 297 readPermission = readPermission.intern(); 298 havePerm = true; 299 } 300 if (writePermission != null) { 301 writePermission = writePermission.intern(); 302 havePerm = true; 303 } 304 305 if (!havePerm) { 306 if (PackageParser.RIGID_PARSER) { 307 return input.error( 308 "No readPermission or writePermission for <path-permission>"); 309 } 310 Slog.w(TAG, "No readPermission or writePermission for <path-permission>: " 311 + name + " at " + pkg.getBaseApkPath() + " " 312 + parser.getPositionDescription()); 313 return input.success(provider); 314 } 315 316 // Advanced has priority over simply over prefix over literal 317 PathPermission pa = null; 318 String path = sa.getNonConfigurationString(R.styleable.AndroidManifestPathPermission_pathAdvancedPattern, 0); 319 if (path != null) { 320 pa = new PathPermission(path, PatternMatcher.PATTERN_ADVANCED_GLOB, readPermission, 321 writePermission); 322 } else { 323 path = sa.getNonConfigurationString(R.styleable.AndroidManifestPathPermission_pathPattern, 0); 324 if (path != null) { 325 pa = new PathPermission(path, PatternMatcher.PATTERN_SIMPLE_GLOB, 326 readPermission, writePermission); 327 } else { 328 path = sa.getNonConfigurationString( 329 R.styleable.AndroidManifestPathPermission_pathPrefix, 0); 330 if (path != null) { 331 pa = new PathPermission(path, PatternMatcher.PATTERN_PREFIX, readPermission, 332 writePermission); 333 } else { 334 path = sa.getNonConfigurationString( 335 R.styleable.AndroidManifestPathPermission_pathSuffix, 0); 336 if (path != null) { 337 pa = new PathPermission(path, PatternMatcher.PATTERN_SUFFIX, 338 readPermission, writePermission); 339 } else { 340 path = sa.getNonConfigurationString( 341 R.styleable.AndroidManifestPathPermission_path, 0); 342 if (path != null) { 343 pa = new PathPermission(path, PatternMatcher.PATTERN_LITERAL, 344 readPermission, writePermission); 345 } 346 } 347 } 348 } 349 } 350 351 if (pa != null) { 352 if (provider.pathPermissions == null) { 353 provider.pathPermissions = new PathPermission[1]; 354 provider.pathPermissions[0] = pa; 355 } else { 356 final int N = provider.pathPermissions.length; 357 PathPermission[] newp = new PathPermission[N + 1]; 358 System.arraycopy(provider.pathPermissions, 0, newp, 0, N); 359 newp[N] = pa; 360 provider.pathPermissions = newp; 361 } 362 } else { 363 if (PackageParser.RIGID_PARSER) { 364 return input.error( 365 "No path, pathPrefix, or pathPattern for <path-permission>"); 366 } 367 368 Slog.w(TAG, "No path, pathPrefix, or pathPattern for <path-permission>: " 369 + name + " at " + pkg.getBaseApkPath() 370 + " " 371 + parser.getPositionDescription()); 372 } 373 374 return input.success(provider); 375 } finally { 376 sa.recycle(); 377 } 378 } 379 } 380