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