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