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; 18 19 import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE; 20 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; 21 import static android.content.pm.PackageManager.FEATURE_WATCH; 22 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST; 23 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; 24 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED; 25 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED; 26 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION; 27 import static android.os.Build.VERSION_CODES.DONUT; 28 import static android.os.Build.VERSION_CODES.O; 29 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; 30 31 import android.annotation.AnyRes; 32 import android.annotation.CheckResult; 33 import android.annotation.IntRange; 34 import android.annotation.NonNull; 35 import android.annotation.Nullable; 36 import android.annotation.StyleableRes; 37 import android.app.ActivityThread; 38 import android.content.Intent; 39 import android.content.IntentFilter; 40 import android.content.pm.ApplicationInfo; 41 import android.content.pm.ConfigurationInfo; 42 import android.content.pm.FeatureGroupInfo; 43 import android.content.pm.FeatureInfo; 44 import android.content.pm.PackageInfo; 45 import android.content.pm.PackageManager; 46 import android.content.pm.PackageParser; 47 import android.content.pm.PackageParser.PackageParserException; 48 import android.content.pm.PackageParser.SigningDetails; 49 import android.content.pm.Signature; 50 import android.content.pm.parsing.component.ComponentParseUtils; 51 import android.content.pm.parsing.component.ParsedActivity; 52 import android.content.pm.parsing.component.ParsedActivityUtils; 53 import android.content.pm.parsing.component.ParsedAttribution; 54 import android.content.pm.parsing.component.ParsedAttributionUtils; 55 import android.content.pm.parsing.component.ParsedInstrumentation; 56 import android.content.pm.parsing.component.ParsedInstrumentationUtils; 57 import android.content.pm.parsing.component.ParsedIntentInfo; 58 import android.content.pm.parsing.component.ParsedIntentInfoUtils; 59 import android.content.pm.parsing.component.ParsedMainComponent; 60 import android.content.pm.parsing.component.ParsedPermission; 61 import android.content.pm.parsing.component.ParsedPermissionGroup; 62 import android.content.pm.parsing.component.ParsedPermissionUtils; 63 import android.content.pm.parsing.component.ParsedProcess; 64 import android.content.pm.parsing.component.ParsedProcessUtils; 65 import android.content.pm.parsing.component.ParsedProvider; 66 import android.content.pm.parsing.component.ParsedProviderUtils; 67 import android.content.pm.parsing.component.ParsedService; 68 import android.content.pm.parsing.component.ParsedServiceUtils; 69 import android.content.pm.parsing.result.ParseInput; 70 import android.content.pm.parsing.result.ParseInput.DeferredError; 71 import android.content.pm.parsing.result.ParseResult; 72 import android.content.pm.parsing.result.ParseTypeImpl; 73 import android.content.pm.permission.SplitPermissionInfoParcelable; 74 import android.content.pm.split.DefaultSplitAssetLoader; 75 import android.content.pm.split.SplitAssetDependencyLoader; 76 import android.content.pm.split.SplitAssetLoader; 77 import android.content.res.ApkAssets; 78 import android.content.res.AssetManager; 79 import android.content.res.Configuration; 80 import android.content.res.Resources; 81 import android.content.res.TypedArray; 82 import android.content.res.XmlResourceParser; 83 import android.net.Uri; 84 import android.os.Build; 85 import android.os.Bundle; 86 import android.os.FileUtils; 87 import android.os.RemoteException; 88 import android.os.Trace; 89 import android.os.ext.SdkExtensions; 90 import android.text.TextUtils; 91 import android.util.ArrayMap; 92 import android.util.ArraySet; 93 import android.util.AttributeSet; 94 import android.util.DisplayMetrics; 95 import android.util.Pair; 96 import android.util.Slog; 97 import android.util.SparseArray; 98 import android.util.SparseIntArray; 99 import android.util.TypedValue; 100 import android.util.apk.ApkSignatureVerifier; 101 102 import com.android.internal.R; 103 import com.android.internal.os.ClassLoaderFactory; 104 import com.android.internal.util.ArrayUtils; 105 import com.android.internal.util.XmlUtils; 106 107 import libcore.io.IoUtils; 108 import libcore.util.EmptyArray; 109 110 import org.xmlpull.v1.XmlPullParser; 111 import org.xmlpull.v1.XmlPullParserException; 112 113 import java.io.File; 114 import java.io.IOException; 115 import java.security.PublicKey; 116 import java.util.ArrayList; 117 import java.util.Arrays; 118 import java.util.List; 119 import java.util.Map; 120 import java.util.Set; 121 import java.util.StringTokenizer; 122 123 /** 124 * TODO(b/135203078): Differentiate between parse_ methods and some add_ method for whether it 125 * mutates the passed-in component or not. Or consolidate so all parse_ methods mutate. 126 * 127 * @hide 128 */ 129 public class ParsingPackageUtils { 130 131 public static final String TAG = ParsingUtils.TAG; 132 133 /** 134 * @see #parseDefault(ParseInput, File, int, boolean) 135 */ 136 @NonNull parseDefaultOneTime(File file, @PackageParser.ParseFlags int parseFlags, boolean collectCertificates)137 public static ParseResult<ParsingPackage> parseDefaultOneTime(File file, 138 @PackageParser.ParseFlags int parseFlags, boolean collectCertificates) { 139 ParseInput input = ParseTypeImpl.forDefaultParsing().reset(); 140 return parseDefault(input, file, parseFlags, collectCertificates); 141 } 142 143 /** 144 * For cases outside of PackageManagerService when an APK needs to be parsed as a one-off 145 * request, without caching the input object and without querying the internal system state 146 * for feature support. 147 */ 148 @NonNull parseDefault(ParseInput input, File file, @PackageParser.ParseFlags int parseFlags, boolean collectCertificates)149 public static ParseResult<ParsingPackage> parseDefault(ParseInput input, File file, 150 @PackageParser.ParseFlags int parseFlags, boolean collectCertificates) { 151 ParseResult<ParsingPackage> result; 152 153 ParsingPackageUtils parser = new ParsingPackageUtils(false, null, null, new Callback() { 154 @Override 155 public boolean hasFeature(String feature) { 156 // Assume the device doesn't support anything. This will affect permission parsing 157 // and will force <uses-permission/> declarations to include all requiredNotFeature 158 // permissions and exclude all requiredFeature permissions. This mirrors the old 159 // behavior. 160 return false; 161 } 162 163 @Override 164 public ParsingPackage startParsingPackage( 165 @NonNull String packageName, 166 @NonNull String baseCodePath, 167 @NonNull String codePath, 168 @NonNull TypedArray manifestArray, boolean isCoreApp) { 169 return new ParsingPackageImpl(packageName, baseCodePath, codePath, manifestArray); 170 } 171 }); 172 try { 173 result = parser.parsePackage(input, file, parseFlags); 174 if (result.isError()) { 175 return result; 176 } 177 } catch (PackageParser.PackageParserException e) { 178 return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, 179 "Error parsing package", e); 180 } 181 182 try { 183 ParsingPackage pkg = result.getResult(); 184 if (collectCertificates) { 185 pkg.setSigningDetails( 186 ParsingPackageUtils.getSigningDetails(pkg, false /* skipVerify */)); 187 } 188 189 return input.success(pkg); 190 } catch (PackageParser.PackageParserException e) { 191 return input.error(PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, 192 "Error collecting package certificates", e); 193 } 194 } 195 196 private boolean mOnlyCoreApps; 197 private String[] mSeparateProcesses; 198 private DisplayMetrics mDisplayMetrics; 199 private Callback mCallback; 200 ParsingPackageUtils(boolean onlyCoreApps, String[] separateProcesses, DisplayMetrics displayMetrics, @NonNull Callback callback)201 public ParsingPackageUtils(boolean onlyCoreApps, String[] separateProcesses, 202 DisplayMetrics displayMetrics, @NonNull Callback callback) { 203 mOnlyCoreApps = onlyCoreApps; 204 mSeparateProcesses = separateProcesses; 205 mDisplayMetrics = displayMetrics; 206 mCallback = callback; 207 } 208 209 /** 210 * Parse the package at the given location. Automatically detects if the 211 * package is a monolithic style (single APK file) or cluster style 212 * (directory of APKs). 213 * <p> 214 * This performs sanity checking on cluster style packages, such as 215 * requiring identical package name and version codes, a single base APK, 216 * and unique split names. 217 * <p> 218 * Note that this <em>does not</em> perform signature verification; that 219 * must be done separately in {@link #getSigningDetails(ParsingPackageRead, boolean)}. 220 * 221 * If {@code useCaches} is true, the package parser might return a cached 222 * result from a previous parse of the same {@code packageFile} with the same 223 * {@code flags}. Note that this method does not check whether {@code packageFile} 224 * has changed since the last parse, it's up to callers to do so. 225 * 226 * @see PackageParser#parsePackageLite(File, int) 227 */ parsePackage(ParseInput input, File packageFile, int flags)228 public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile, 229 int flags) 230 throws PackageParserException { 231 if (packageFile.isDirectory()) { 232 return parseClusterPackage(input, packageFile, flags); 233 } else { 234 return parseMonolithicPackage(input, packageFile, flags); 235 } 236 } 237 238 /** 239 * Parse all APKs contained in the given directory, treating them as a 240 * single package. This also performs sanity checking, such as requiring 241 * identical package name and version codes, a single base APK, and unique 242 * split names. 243 * <p> 244 * Note that this <em>does not</em> perform signature verification; that 245 * must be done separately in {@link #getSigningDetails(ParsingPackageRead, boolean)}. 246 */ parseClusterPackage(ParseInput input, File packageDir, int flags)247 private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir, 248 int flags) { 249 ParseResult<PackageParser.PackageLite> liteResult = 250 ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, 0); 251 if (liteResult.isError()) { 252 return input.error(liteResult); 253 } 254 255 final PackageParser.PackageLite lite = liteResult.getResult(); 256 if (mOnlyCoreApps && !lite.coreApp) { 257 return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED, 258 "Not a coreApp: " + packageDir); 259 } 260 261 // Build the split dependency tree. 262 SparseArray<int[]> splitDependencies = null; 263 final SplitAssetLoader assetLoader; 264 if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) { 265 try { 266 splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite); 267 assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags); 268 } catch (SplitAssetDependencyLoader.IllegalDependencyException e) { 269 return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage()); 270 } 271 } else { 272 assetLoader = new DefaultSplitAssetLoader(lite, flags); 273 } 274 275 try { 276 final AssetManager assets = assetLoader.getBaseAssetManager(); 277 final File baseApk = new File(lite.baseCodePath); 278 ParseResult<ParsingPackage> result = parseBaseApk(input, baseApk, 279 lite.codePath, assets, flags); 280 if (result.isError()) { 281 return input.error(result); 282 } 283 284 ParsingPackage pkg = result.getResult(); 285 if (!ArrayUtils.isEmpty(lite.splitNames)) { 286 pkg.asSplit( 287 lite.splitNames, 288 lite.splitCodePaths, 289 lite.splitRevisionCodes, 290 splitDependencies 291 ); 292 final int num = lite.splitNames.length; 293 294 for (int i = 0; i < num; i++) { 295 final AssetManager splitAssets = assetLoader.getSplitAssetManager(i); 296 parseSplitApk(input, pkg, i, splitAssets, flags); 297 } 298 } 299 300 pkg.setUse32BitAbi(lite.use32bitAbi); 301 return input.success(pkg); 302 } catch (PackageParserException e) { 303 return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, 304 "Failed to load assets: " + lite.baseCodePath, e); 305 } finally { 306 IoUtils.closeQuietly(assetLoader); 307 } 308 } 309 310 /** 311 * Parse the given APK file, treating it as as a single monolithic package. 312 * <p> 313 * Note that this <em>does not</em> perform signature verification; that 314 * must be done separately in {@link #getSigningDetails(ParsingPackageRead, boolean)}. 315 */ parseMonolithicPackage(ParseInput input, File apkFile, int flags)316 private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile, 317 int flags) throws PackageParserException { 318 ParseResult<PackageParser.PackageLite> liteResult = 319 ApkLiteParseUtils.parseMonolithicPackageLite(input, apkFile, flags); 320 if (liteResult.isError()) { 321 return input.error(liteResult); 322 } 323 324 final PackageParser.PackageLite lite = liteResult.getResult(); 325 if (mOnlyCoreApps && !lite.coreApp) { 326 return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED, 327 "Not a coreApp: " + apkFile); 328 } 329 330 final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags); 331 try { 332 ParseResult<ParsingPackage> result = parseBaseApk(input, 333 apkFile, 334 apkFile.getCanonicalPath(), 335 assetLoader.getBaseAssetManager(), flags); 336 if (result.isError()) { 337 return input.error(result); 338 } 339 340 return input.success(result.getResult() 341 .setUse32BitAbi(lite.use32bitAbi)); 342 } catch (IOException e) { 343 return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, 344 "Failed to get path: " + apkFile, e); 345 } finally { 346 IoUtils.closeQuietly(assetLoader); 347 } 348 } 349 parseBaseApk(ParseInput input, File apkFile, String codePath, AssetManager assets, int flags)350 private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile, 351 String codePath, AssetManager assets, int flags) { 352 final String apkPath = apkFile.getAbsolutePath(); 353 354 String volumeUuid = null; 355 if (apkPath.startsWith(PackageParser.MNT_EXPAND)) { 356 final int end = apkPath.indexOf('/', PackageParser.MNT_EXPAND.length()); 357 volumeUuid = apkPath.substring(PackageParser.MNT_EXPAND.length(), end); 358 } 359 360 if (PackageParser.DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath); 361 362 final int cookie = assets.findCookieForPath(apkPath); 363 if (cookie == 0) { 364 return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST, 365 "Failed adding asset path: " + apkPath); 366 } 367 368 try (XmlResourceParser parser = assets.openXmlResourceParser(cookie, 369 PackageParser.ANDROID_MANIFEST_FILENAME)) { 370 final Resources res = new Resources(assets, mDisplayMetrics, null); 371 372 ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res, 373 parser, flags); 374 if (result.isError()) { 375 return input.error(result.getErrorCode(), 376 apkPath + " (at " + parser.getPositionDescription() + "): " 377 + result.getErrorMessage()); 378 } 379 380 final ParsingPackage pkg = result.getResult(); 381 if (assets.containsAllocatedTable()) { 382 final ParseResult<?> deferResult = input.deferError( 383 "Targeting R+ (version " + Build.VERSION_CODES.R + " and above) requires" 384 + " the resources.arsc of installed APKs to be stored uncompressed" 385 + " and aligned on a 4-byte boundary", 386 DeferredError.RESOURCES_ARSC_COMPRESSED); 387 if (deferResult.isError()) { 388 return input.error(INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED, 389 deferResult.getErrorMessage()); 390 } 391 } 392 393 ApkAssets apkAssets = assets.getApkAssets()[0]; 394 if (apkAssets.definesOverlayable()) { 395 SparseArray<String> packageNames = assets.getAssignedPackageIdentifiers(); 396 int size = packageNames.size(); 397 for (int index = 0; index < size; index++) { 398 String packageName = packageNames.get(index); 399 Map<String, String> overlayableToActor = assets.getOverlayableMap(packageName); 400 if (overlayableToActor != null && !overlayableToActor.isEmpty()) { 401 for (String overlayable : overlayableToActor.keySet()) { 402 pkg.addOverlayable(overlayable, overlayableToActor.get(overlayable)); 403 } 404 } 405 } 406 } 407 408 pkg.setVolumeUuid(volumeUuid); 409 410 if ((flags & PackageParser.PARSE_COLLECT_CERTIFICATES) != 0) { 411 pkg.setSigningDetails(getSigningDetails(pkg, false)); 412 } else { 413 pkg.setSigningDetails(SigningDetails.UNKNOWN); 414 } 415 416 return input.success(pkg); 417 } catch (Exception e) { 418 return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, 419 "Failed to read manifest from " + apkPath, e); 420 } 421 } 422 parseSplitApk(ParseInput input, ParsingPackage pkg, int splitIndex, AssetManager assets, int flags)423 private ParseResult<ParsingPackage> parseSplitApk(ParseInput input, 424 ParsingPackage pkg, int splitIndex, AssetManager assets, int flags) { 425 final String apkPath = pkg.getSplitCodePaths()[splitIndex]; 426 427 if (PackageParser.DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath); 428 429 // This must always succeed, as the path has been added to the AssetManager before. 430 final int cookie = assets.findCookieForPath(apkPath); 431 if (cookie == 0) { 432 return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST, 433 "Failed adding asset path: " + apkPath); 434 } 435 try (XmlResourceParser parser = assets.openXmlResourceParser(cookie, 436 PackageParser.ANDROID_MANIFEST_FILENAME)) { 437 Resources res = new Resources(assets, mDisplayMetrics, null); 438 ParseResult<ParsingPackage> parseResult = parseSplitApk(input, pkg, res, 439 parser, flags, splitIndex); 440 if (parseResult.isError()) { 441 return input.error(parseResult.getErrorCode(), 442 apkPath + " (at " + parser.getPositionDescription() + "): " 443 + parseResult.getErrorMessage()); 444 } 445 446 return parseResult; 447 } catch (Exception e) { 448 return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, 449 "Failed to read manifest from " + apkPath, e); 450 } 451 } 452 453 /** 454 * Parse the manifest of a <em>base APK</em>. When adding new features you 455 * need to consider whether they should be supported by split APKs and child 456 * packages. 457 * 458 * @param apkPath The package apk file path 459 * @param res The resources from which to resolve values 460 * @param parser The manifest parser 461 * @param flags Flags how to parse 462 * @return Parsed package or null on error. 463 */ parseBaseApk(ParseInput input, String apkPath, String codePath, Resources res, XmlResourceParser parser, int flags)464 private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath, 465 String codePath, Resources res, XmlResourceParser parser, int flags) 466 throws XmlPullParserException, IOException, PackageParserException { 467 final String splitName; 468 final String pkgName; 469 470 ParseResult<Pair<String, String>> packageSplitResult = 471 ApkLiteParseUtils.parsePackageSplitNames(input, parser, parser); 472 if (packageSplitResult.isError()) { 473 return input.error(packageSplitResult); 474 } 475 476 Pair<String, String> packageSplit = packageSplitResult.getResult(); 477 pkgName = packageSplit.first; 478 splitName = packageSplit.second; 479 480 if (!TextUtils.isEmpty(splitName)) { 481 return input.error( 482 PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME, 483 "Expected base APK, but found split " + splitName 484 ); 485 } 486 487 final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest); 488 try { 489 final boolean isCoreApp = 490 parser.getAttributeBooleanValue(null, "coreApp", false); 491 final ParsingPackage pkg = mCallback.startParsingPackage( 492 pkgName, apkPath, codePath, manifestArray, isCoreApp); 493 final ParseResult<ParsingPackage> result = 494 parseBaseApkTags(input, pkg, manifestArray, res, parser, flags); 495 if (result.isError()) { 496 return result; 497 } 498 499 return input.success(pkg); 500 } finally { 501 manifestArray.recycle(); 502 } 503 } 504 505 /** 506 * Parse the manifest of a <em>split APK</em>. 507 * <p> 508 * Note that split APKs have many more restrictions on what they're capable 509 * of doing, so many valid features of a base APK have been carefully 510 * omitted here. 511 * 512 * @param pkg builder to fill 513 * @return false on failure 514 */ parseSplitApk(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags, int splitIndex)515 private ParseResult<ParsingPackage> parseSplitApk(ParseInput input, ParsingPackage pkg, 516 Resources res, XmlResourceParser parser, int flags, int splitIndex) 517 throws XmlPullParserException, IOException, PackageParserException { 518 AttributeSet attrs = parser; 519 520 // We parsed manifest tag earlier; just skip past it 521 PackageParser.parsePackageSplitNames(parser, attrs); 522 523 int type; 524 525 boolean foundApp = false; 526 527 int outerDepth = parser.getDepth(); 528 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { 529 if (outerDepth + 1 < parser.getDepth() || type != XmlPullParser.START_TAG) { 530 continue; 531 } 532 533 final ParseResult result; 534 String tagName = parser.getName(); 535 if (PackageParser.TAG_APPLICATION.equals(tagName)) { 536 if (foundApp) { 537 if (PackageParser.RIGID_PARSER) { 538 result = input.error("<manifest> has more than one <application>"); 539 } else { 540 Slog.w(TAG, "<manifest> has more than one <application>"); 541 result = input.success(null); 542 } 543 } else { 544 foundApp = true; 545 result = parseSplitApplication(input, pkg, res, parser, flags, splitIndex); 546 } 547 } else { 548 result = ParsingUtils.unknownTag("<manifest>", pkg, parser, input); 549 } 550 551 if (result.isError()) { 552 return input.error(result); 553 } 554 } 555 556 if (!foundApp) { 557 ParseResult<?> deferResult = input.deferError( 558 "<manifest> does not contain an <application>", DeferredError.MISSING_APP_TAG); 559 if (deferResult.isError()) { 560 return input.error(deferResult); 561 } 562 } 563 564 return input.success(pkg); 565 } 566 567 /** 568 * Parse the {@code application} XML tree at the current parse location in a 569 * <em>split APK</em> manifest. 570 * <p> 571 * Note that split APKs have many more restrictions on what they're capable 572 * of doing, so many valid features of a base APK have been carefully 573 * omitted here. 574 */ parseSplitApplication(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags, int splitIndex)575 private ParseResult<ParsingPackage> parseSplitApplication(ParseInput input, 576 ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags, int splitIndex) 577 throws XmlPullParserException, IOException { 578 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication); 579 try { 580 pkg.setSplitHasCode(splitIndex, sa.getBoolean( 581 R.styleable.AndroidManifestApplication_hasCode, true)); 582 583 final String classLoaderName = sa.getString( 584 R.styleable.AndroidManifestApplication_classLoader); 585 if (classLoaderName == null || ClassLoaderFactory.isValidClassLoaderName( 586 classLoaderName)) { 587 pkg.setSplitClassLoaderName(splitIndex, classLoaderName); 588 } else { 589 return input.error("Invalid class loader name: " + classLoaderName); 590 } 591 } finally { 592 sa.recycle(); 593 } 594 final int depth = parser.getDepth(); 595 int type; 596 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 597 && (type != XmlPullParser.END_TAG 598 || parser.getDepth() > depth)) { 599 if (type != XmlPullParser.START_TAG) { 600 continue; 601 } 602 603 ParsedMainComponent mainComponent = null; 604 605 final ParseResult result; 606 String tagName = parser.getName(); 607 boolean isActivity = false; 608 switch (tagName) { 609 case "activity": 610 isActivity = true; 611 // fall-through 612 case "receiver": 613 ParseResult<ParsedActivity> activityResult = 614 ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg, 615 res, 616 parser, flags, PackageParser.sUseRoundIcon, input); 617 if (activityResult.isSuccess()) { 618 ParsedActivity activity = activityResult.getResult(); 619 if (isActivity) { 620 pkg.addActivity(activity); 621 } else { 622 pkg.addReceiver(activity); 623 } 624 mainComponent = activity; 625 } 626 result = activityResult; 627 break; 628 case "service": 629 ParseResult<ParsedService> serviceResult = ParsedServiceUtils.parseService( 630 mSeparateProcesses, pkg, res, parser, flags, 631 PackageParser.sUseRoundIcon, input); 632 if (serviceResult.isSuccess()) { 633 ParsedService service = serviceResult.getResult(); 634 pkg.addService(service); 635 mainComponent = service; 636 } 637 result = serviceResult; 638 break; 639 case "provider": 640 ParseResult<ParsedProvider> providerResult = 641 ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser, 642 flags, PackageParser.sUseRoundIcon, input); 643 if (providerResult.isSuccess()) { 644 ParsedProvider provider = providerResult.getResult(); 645 pkg.addProvider(provider); 646 mainComponent = provider; 647 } 648 result = providerResult; 649 break; 650 case "activity-alias": 651 activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res, parser, 652 PackageParser.sUseRoundIcon, input); 653 if (activityResult.isSuccess()) { 654 ParsedActivity activity = activityResult.getResult(); 655 pkg.addActivity(activity); 656 mainComponent = activity; 657 } 658 659 result = activityResult; 660 break; 661 default: 662 result = parseSplitBaseAppChildTags(input, tagName, pkg, res, parser); 663 break; 664 } 665 666 if (result.isError()) { 667 return input.error(result); 668 } 669 670 if (mainComponent != null && mainComponent.getSplitName() == null) { 671 // If the loaded component did not specify a split, inherit the split name 672 // based on the split it is defined in. 673 // This is used to later load the correct split when starting this 674 // component. 675 mainComponent.setSplitName(pkg.getSplitNames()[splitIndex]); 676 } 677 } 678 679 return input.success(pkg); 680 } 681 682 /** 683 * For parsing non-MainComponents. Main ones have an order and some special handling which is 684 * done directly in {@link #parseSplitApplication(ParseInput, ParsingPackage, Resources, 685 * XmlResourceParser, int, int)}. 686 */ parseSplitBaseAppChildTags(ParseInput input, String tag, ParsingPackage pkg, Resources res, XmlResourceParser parser)687 private ParseResult parseSplitBaseAppChildTags(ParseInput input, String tag, ParsingPackage pkg, 688 Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException { 689 switch (tag) { 690 case "meta-data": 691 // note: application meta-data is stored off to the side, so it can 692 // remain null in the primary copy (we like to avoid extra copies because 693 // it can be large) 694 ParseResult<Bundle> metaDataResult = parseMetaData(pkg, res, parser, 695 pkg.getMetaData(), input); 696 if (metaDataResult.isSuccess()) { 697 pkg.setMetaData(metaDataResult.getResult()); 698 } 699 return metaDataResult; 700 case "uses-static-library": 701 return parseUsesStaticLibrary(input, pkg, res, parser); 702 case "uses-library": 703 return parseUsesLibrary(input, pkg, res, parser); 704 case "uses-package": 705 // Dependencies for app installers; we don't currently try to 706 // enforce this. 707 return input.success(null); 708 default: 709 return ParsingUtils.unknownTag("<application>", pkg, parser, input); 710 } 711 } 712 parseBaseApkTags(ParseInput input, ParsingPackage pkg, TypedArray sa, Resources res, XmlResourceParser parser, int flags)713 private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg, 714 TypedArray sa, Resources res, XmlResourceParser parser, int flags) 715 throws XmlPullParserException, IOException { 716 ParseResult<ParsingPackage> sharedUserResult = parseSharedUser(input, pkg, sa); 717 if (sharedUserResult.isError()) { 718 return sharedUserResult; 719 } 720 721 pkg.setInstallLocation(anInteger(PackageParser.PARSE_DEFAULT_INSTALL_LOCATION, 722 R.styleable.AndroidManifest_installLocation, sa)) 723 .setTargetSandboxVersion(anInteger(PackageParser.PARSE_DEFAULT_TARGET_SANDBOX, 724 R.styleable.AndroidManifest_targetSandboxVersion, sa)) 725 /* Set the global "on SD card" flag */ 726 .setExternalStorage((flags & PackageParser.PARSE_EXTERNAL_STORAGE) != 0); 727 728 boolean foundApp = false; 729 final int depth = parser.getDepth(); 730 int type; 731 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 732 && (type != XmlPullParser.END_TAG 733 || parser.getDepth() > depth)) { 734 if (type != XmlPullParser.START_TAG) { 735 continue; 736 } 737 738 String tagName = parser.getName(); 739 final ParseResult result; 740 741 // TODO(b/135203078): Convert to instance methods to share variables 742 // <application> has special logic, so it's handled outside the general method 743 if (PackageParser.TAG_APPLICATION.equals(tagName)) { 744 if (foundApp) { 745 if (PackageParser.RIGID_PARSER) { 746 result = input.error("<manifest> has more than one <application>"); 747 } else { 748 Slog.w(TAG, "<manifest> has more than one <application>"); 749 result = input.success(null); 750 } 751 } else { 752 foundApp = true; 753 result = parseBaseApplication(input, pkg, res, parser, flags); 754 } 755 } else { 756 result = parseBaseApkTag(tagName, input, pkg, res, parser, flags); 757 } 758 759 if (result.isError()) { 760 return input.error(result); 761 } 762 } 763 764 if (!foundApp && ArrayUtils.size(pkg.getInstrumentations()) == 0) { 765 ParseResult<?> deferResult = input.deferError( 766 "<manifest> does not contain an <application> or <instrumentation>", 767 DeferredError.MISSING_APP_TAG); 768 if (deferResult.isError()) { 769 return input.error(deferResult); 770 } 771 } 772 773 if (!ParsedAttribution.isCombinationValid(pkg.getAttributions())) { 774 return input.error( 775 INSTALL_PARSE_FAILED_BAD_MANIFEST, 776 "Combination <feature> tags are not valid" 777 ); 778 } 779 780 convertNewPermissions(pkg); 781 782 convertSplitPermissions(pkg); 783 784 // At this point we can check if an application is not supporting densities and hence 785 // cannot be windowed / resized. Note that an SDK version of 0 is common for 786 // pre-Doughnut applications. 787 if (pkg.getTargetSdkVersion() < DONUT 788 || (!pkg.isSupportsSmallScreens() 789 && !pkg.isSupportsNormalScreens() 790 && !pkg.isSupportsLargeScreens() 791 && !pkg.isSupportsExtraLargeScreens() 792 && !pkg.isResizeable() 793 && !pkg.isAnyDensity())) { 794 adjustPackageToBeUnresizeableAndUnpipable(pkg); 795 } 796 797 return input.success(pkg); 798 } 799 parseBaseApkTag(String tag, ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)800 private ParseResult parseBaseApkTag(String tag, ParseInput input, 801 ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags) 802 throws IOException, XmlPullParserException { 803 switch (tag) { 804 case PackageParser.TAG_OVERLAY: 805 return parseOverlay(input, pkg, res, parser); 806 case PackageParser.TAG_KEY_SETS: 807 return parseKeySets(input, pkg, res, parser); 808 case "feature": // TODO moltmann: Remove 809 case PackageParser.TAG_ATTRIBUTION: 810 return parseAttribution(input, pkg, res, parser); 811 case PackageParser.TAG_PERMISSION_GROUP: 812 return parsePermissionGroup(input, pkg, res, parser); 813 case PackageParser.TAG_PERMISSION: 814 return parsePermission(input, pkg, res, parser); 815 case PackageParser.TAG_PERMISSION_TREE: 816 return parsePermissionTree(input, pkg, res, parser); 817 case PackageParser.TAG_USES_PERMISSION: 818 case PackageParser.TAG_USES_PERMISSION_SDK_M: 819 case PackageParser.TAG_USES_PERMISSION_SDK_23: 820 return parseUsesPermission(input, pkg, res, parser); 821 case PackageParser.TAG_USES_CONFIGURATION: 822 return parseUsesConfiguration(input, pkg, res, parser); 823 case PackageParser.TAG_USES_FEATURE: 824 return parseUsesFeature(input, pkg, res, parser); 825 case PackageParser.TAG_FEATURE_GROUP: 826 return parseFeatureGroup(input, pkg, res, parser); 827 case PackageParser.TAG_USES_SDK: 828 return parseUsesSdk(input, pkg, res, parser); 829 case PackageParser.TAG_SUPPORT_SCREENS: 830 return parseSupportScreens(input, pkg, res, parser); 831 case PackageParser.TAG_PROTECTED_BROADCAST: 832 return parseProtectedBroadcast(input, pkg, res, parser); 833 case PackageParser.TAG_INSTRUMENTATION: 834 return parseInstrumentation(input, pkg, res, parser); 835 case PackageParser.TAG_ORIGINAL_PACKAGE: 836 return parseOriginalPackage(input, pkg, res, parser); 837 case PackageParser.TAG_ADOPT_PERMISSIONS: 838 return parseAdoptPermissions(input, pkg, res, parser); 839 case PackageParser.TAG_USES_GL_TEXTURE: 840 case PackageParser.TAG_COMPATIBLE_SCREENS: 841 case PackageParser.TAG_SUPPORTS_INPUT: 842 case PackageParser.TAG_EAT_COMMENT: 843 // Just skip this tag 844 XmlUtils.skipCurrentTag(parser); 845 return input.success(pkg); 846 case PackageParser.TAG_RESTRICT_UPDATE: 847 return parseRestrictUpdateHash(flags, input, pkg, res, parser); 848 case PackageParser.TAG_QUERIES: 849 return parseQueries(input, pkg, res, parser); 850 default: 851 return ParsingUtils.unknownTag("<manifest>", pkg, parser, input); 852 } 853 } 854 parseSharedUser(ParseInput input, ParsingPackage pkg, TypedArray sa)855 private static ParseResult<ParsingPackage> parseSharedUser(ParseInput input, 856 ParsingPackage pkg, TypedArray sa) { 857 String str = nonConfigString(0, R.styleable.AndroidManifest_sharedUserId, sa); 858 if (TextUtils.isEmpty(str)) { 859 return input.success(pkg); 860 } 861 862 if (!"android".equals(pkg.getPackageName())) { 863 ParseResult<?> nameResult = validateName(input, str, true, true); 864 if (nameResult.isError()) { 865 return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID, 866 "<manifest> specifies bad sharedUserId name \"" + str + "\": " 867 + nameResult.getErrorMessage()); 868 } 869 } 870 871 return input.success(pkg 872 .setSharedUserId(str.intern()) 873 .setSharedUserLabel(resId(R.styleable.AndroidManifest_sharedUserLabel, sa))); 874 } 875 parseKeySets(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)876 private static ParseResult<ParsingPackage> parseKeySets(ParseInput input, 877 ParsingPackage pkg, Resources res, XmlResourceParser parser) 878 throws XmlPullParserException, IOException { 879 // we've encountered the 'key-sets' tag 880 // all the keys and keysets that we want must be defined here 881 // so we're going to iterate over the parser and pull out the things we want 882 int outerDepth = parser.getDepth(); 883 int currentKeySetDepth = -1; 884 int type; 885 String currentKeySet = null; 886 ArrayMap<String, PublicKey> publicKeys = new ArrayMap<>(); 887 ArraySet<String> upgradeKeySets = new ArraySet<>(); 888 ArrayMap<String, ArraySet<String>> definedKeySets = new ArrayMap<>(); 889 ArraySet<String> improperKeySets = new ArraySet<>(); 890 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 891 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 892 if (type == XmlPullParser.END_TAG) { 893 if (parser.getDepth() == currentKeySetDepth) { 894 currentKeySet = null; 895 currentKeySetDepth = -1; 896 } 897 continue; 898 } 899 String tagName = parser.getName(); 900 switch (tagName) { 901 case "key-set": { 902 if (currentKeySet != null) { 903 return input.error("Improperly nested 'key-set' tag at " 904 + parser.getPositionDescription()); 905 } 906 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestKeySet); 907 try { 908 final String keysetName = sa.getNonResourceString( 909 R.styleable.AndroidManifestKeySet_name); 910 definedKeySets.put(keysetName, new ArraySet<>()); 911 currentKeySet = keysetName; 912 currentKeySetDepth = parser.getDepth(); 913 } finally { 914 sa.recycle(); 915 } 916 } break; 917 case "public-key": { 918 if (currentKeySet == null) { 919 return input.error("Improperly nested 'key-set' tag at " 920 + parser.getPositionDescription()); 921 } 922 TypedArray sa = res.obtainAttributes(parser, 923 R.styleable.AndroidManifestPublicKey); 924 try { 925 final String publicKeyName = nonResString( 926 R.styleable.AndroidManifestPublicKey_name, sa); 927 final String encodedKey = nonResString( 928 R.styleable.AndroidManifestPublicKey_value, sa); 929 if (encodedKey == null && publicKeys.get(publicKeyName) == null) { 930 return input.error("'public-key' " + publicKeyName 931 + " must define a public-key value on first use at " 932 + parser.getPositionDescription()); 933 } else if (encodedKey != null) { 934 PublicKey currentKey = PackageParser.parsePublicKey(encodedKey); 935 if (currentKey == null) { 936 Slog.w(TAG, "No recognized valid key in 'public-key' tag at " 937 + parser.getPositionDescription() + " key-set " 938 + currentKeySet 939 + " will not be added to the package's defined key-sets."); 940 improperKeySets.add(currentKeySet); 941 XmlUtils.skipCurrentTag(parser); 942 continue; 943 } 944 if (publicKeys.get(publicKeyName) == null 945 || publicKeys.get(publicKeyName).equals(currentKey)) { 946 947 /* public-key first definition, or matches old definition */ 948 publicKeys.put(publicKeyName, currentKey); 949 } else { 950 return input.error("Value of 'public-key' " + publicKeyName 951 + " conflicts with previously defined value at " 952 + parser.getPositionDescription()); 953 } 954 } 955 definedKeySets.get(currentKeySet).add(publicKeyName); 956 XmlUtils.skipCurrentTag(parser); 957 } finally { 958 sa.recycle(); 959 } 960 } break; 961 case "upgrade-key-set": { 962 TypedArray sa = res.obtainAttributes(parser, 963 R.styleable.AndroidManifestUpgradeKeySet); 964 try { 965 String name = sa.getNonResourceString( 966 R.styleable.AndroidManifestUpgradeKeySet_name); 967 upgradeKeySets.add(name); 968 XmlUtils.skipCurrentTag(parser); 969 } finally { 970 sa.recycle(); 971 } 972 } break; 973 default: 974 ParseResult result = ParsingUtils.unknownTag("<key-sets>", pkg, parser, 975 input); 976 if (result.isError()) { 977 return input.error(result); 978 } 979 break; 980 } 981 } 982 String packageName = pkg.getPackageName(); 983 Set<String> publicKeyNames = publicKeys.keySet(); 984 if (publicKeyNames.removeAll(definedKeySets.keySet())) { 985 return input.error("Package" + packageName 986 + " AndroidManifest.xml 'key-set' and 'public-key' names must be distinct."); 987 } 988 989 for (ArrayMap.Entry<String, ArraySet<String>> e : definedKeySets.entrySet()) { 990 final String keySetName = e.getKey(); 991 if (e.getValue().size() == 0) { 992 Slog.w(TAG, "Package" + packageName + " AndroidManifest.xml " 993 + "'key-set' " + keySetName + " has no valid associated 'public-key'." 994 + " Not including in package's defined key-sets."); 995 continue; 996 } else if (improperKeySets.contains(keySetName)) { 997 Slog.w(TAG, "Package" + packageName + " AndroidManifest.xml " 998 + "'key-set' " + keySetName + " contained improper 'public-key'" 999 + " tags. Not including in package's defined key-sets."); 1000 continue; 1001 } 1002 1003 for (String s : e.getValue()) { 1004 pkg.addKeySet(keySetName, publicKeys.get(s)); 1005 } 1006 } 1007 if (pkg.getKeySetMapping().keySet().containsAll(upgradeKeySets)) { 1008 pkg.setUpgradeKeySets(upgradeKeySets); 1009 } else { 1010 return input.error("Package" + packageName 1011 + " AndroidManifest.xml does not define all 'upgrade-key-set's ."); 1012 } 1013 1014 return input.success(pkg); 1015 } 1016 parseAttribution(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)1017 private static ParseResult<ParsingPackage> parseAttribution(ParseInput input, 1018 ParsingPackage pkg, Resources res, XmlResourceParser parser) 1019 throws IOException, XmlPullParserException { 1020 ParseResult<ParsedAttribution> result = ParsedAttributionUtils.parseAttribution(res, 1021 parser, input); 1022 if (result.isError()) { 1023 return input.error(result); 1024 } 1025 return input.success(pkg.addAttribution(result.getResult())); 1026 } 1027 parsePermissionGroup(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)1028 private static ParseResult<ParsingPackage> parsePermissionGroup(ParseInput input, 1029 ParsingPackage pkg, Resources res, XmlResourceParser parser) 1030 throws XmlPullParserException, IOException { 1031 ParseResult<ParsedPermissionGroup> result = ParsedPermissionUtils.parsePermissionGroup( 1032 pkg, res, parser, PackageParser.sUseRoundIcon, input); 1033 if (result.isError()) { 1034 return input.error(result); 1035 } 1036 return input.success(pkg.addPermissionGroup(result.getResult())); 1037 } 1038 parsePermission(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)1039 private static ParseResult<ParsingPackage> parsePermission(ParseInput input, 1040 ParsingPackage pkg, Resources res, XmlResourceParser parser) 1041 throws XmlPullParserException, IOException { 1042 ParseResult<ParsedPermission> result = ParsedPermissionUtils.parsePermission( 1043 pkg, res, parser, PackageParser.sUseRoundIcon, input); 1044 if (result.isError()) { 1045 return input.error(result); 1046 } 1047 return input.success(pkg.addPermission(result.getResult())); 1048 } 1049 parsePermissionTree(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)1050 private static ParseResult<ParsingPackage> parsePermissionTree(ParseInput input, 1051 ParsingPackage pkg, Resources res, XmlResourceParser parser) 1052 throws XmlPullParserException, IOException { 1053 ParseResult<ParsedPermission> result = ParsedPermissionUtils.parsePermissionTree( 1054 pkg, res, parser, PackageParser.sUseRoundIcon, input); 1055 if (result.isError()) { 1056 return input.error(result); 1057 } 1058 return input.success(pkg.addPermission(result.getResult())); 1059 } 1060 parseUsesPermission(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)1061 private ParseResult<ParsingPackage> parseUsesPermission(ParseInput input, 1062 ParsingPackage pkg, Resources res, XmlResourceParser parser) 1063 throws IOException, XmlPullParserException { 1064 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesPermission); 1065 try { 1066 // Note: don't allow this value to be a reference to a resource 1067 // that may change. 1068 String name = sa.getNonResourceString( 1069 R.styleable.AndroidManifestUsesPermission_name); 1070 1071 int maxSdkVersion = 0; 1072 TypedValue val = sa.peekValue( 1073 R.styleable.AndroidManifestUsesPermission_maxSdkVersion); 1074 if (val != null) { 1075 if (val.type >= TypedValue.TYPE_FIRST_INT && val.type <= TypedValue.TYPE_LAST_INT) { 1076 maxSdkVersion = val.data; 1077 } 1078 } 1079 1080 final String requiredFeature = sa.getNonConfigurationString( 1081 R.styleable.AndroidManifestUsesPermission_requiredFeature, 0); 1082 1083 final String requiredNotfeature = sa.getNonConfigurationString( 1084 R.styleable.AndroidManifestUsesPermission_requiredNotFeature, 1085 0); 1086 1087 XmlUtils.skipCurrentTag(parser); 1088 1089 // Can only succeed from here on out 1090 ParseResult<ParsingPackage> success = input.success(pkg); 1091 1092 if (name == null) { 1093 return success; 1094 } 1095 1096 if ((maxSdkVersion != 0) && (maxSdkVersion < Build.VERSION.RESOURCES_SDK_INT)) { 1097 return success; 1098 } 1099 1100 // Only allow requesting this permission if the platform supports the given feature. 1101 if (requiredFeature != null && mCallback != null && !mCallback.hasFeature( 1102 requiredFeature)) { 1103 return success; 1104 } 1105 1106 // Only allow requesting this permission if the platform doesn't support the given 1107 // feature. 1108 if (requiredNotfeature != null && mCallback != null 1109 && mCallback.hasFeature(requiredNotfeature)) { 1110 return success; 1111 } 1112 1113 if (!pkg.getRequestedPermissions().contains(name)) { 1114 pkg.addRequestedPermission(name.intern()); 1115 } else { 1116 Slog.w(TAG, "Ignoring duplicate uses-permissions/uses-permissions-sdk-m: " 1117 + name + " in package: " + pkg.getPackageName() + " at: " 1118 + parser.getPositionDescription()); 1119 } 1120 1121 return success; 1122 } finally { 1123 sa.recycle(); 1124 } 1125 } 1126 parseUsesConfiguration(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)1127 private static ParseResult<ParsingPackage> parseUsesConfiguration(ParseInput input, 1128 ParsingPackage pkg, Resources res, XmlResourceParser parser) { 1129 ConfigurationInfo cPref = new ConfigurationInfo(); 1130 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesConfiguration); 1131 try { 1132 cPref.reqTouchScreen = sa.getInt( 1133 R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen, 1134 Configuration.TOUCHSCREEN_UNDEFINED); 1135 cPref.reqKeyboardType = sa.getInt( 1136 R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType, 1137 Configuration.KEYBOARD_UNDEFINED); 1138 if (sa.getBoolean( 1139 R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard, 1140 false)) { 1141 cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD; 1142 } 1143 cPref.reqNavigation = sa.getInt( 1144 R.styleable.AndroidManifestUsesConfiguration_reqNavigation, 1145 Configuration.NAVIGATION_UNDEFINED); 1146 if (sa.getBoolean( 1147 R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav, 1148 false)) { 1149 cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV; 1150 } 1151 pkg.addConfigPreference(cPref); 1152 return input.success(pkg); 1153 } finally { 1154 sa.recycle(); 1155 } 1156 } 1157 parseUsesFeature(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)1158 private static ParseResult<ParsingPackage> parseUsesFeature(ParseInput input, 1159 ParsingPackage pkg, Resources res, XmlResourceParser parser) { 1160 FeatureInfo fi = parseFeatureInfo(res, parser); 1161 pkg.addReqFeature(fi); 1162 1163 if (fi.name == null) { 1164 ConfigurationInfo cPref = new ConfigurationInfo(); 1165 cPref.reqGlEsVersion = fi.reqGlEsVersion; 1166 pkg.addConfigPreference(cPref); 1167 } 1168 1169 return input.success(pkg); 1170 } 1171 parseFeatureInfo(Resources res, AttributeSet attrs)1172 private static FeatureInfo parseFeatureInfo(Resources res, AttributeSet attrs) { 1173 FeatureInfo fi = new FeatureInfo(); 1174 TypedArray sa = res.obtainAttributes(attrs, R.styleable.AndroidManifestUsesFeature); 1175 try { 1176 // Note: don't allow this value to be a reference to a resource 1177 // that may change. 1178 fi.name = sa.getNonResourceString(R.styleable.AndroidManifestUsesFeature_name); 1179 fi.version = sa.getInt(R.styleable.AndroidManifestUsesFeature_version, 0); 1180 if (fi.name == null) { 1181 fi.reqGlEsVersion = sa.getInt(R.styleable.AndroidManifestUsesFeature_glEsVersion, 1182 FeatureInfo.GL_ES_VERSION_UNDEFINED); 1183 } 1184 if (sa.getBoolean(R.styleable.AndroidManifestUsesFeature_required, true)) { 1185 fi.flags |= FeatureInfo.FLAG_REQUIRED; 1186 } 1187 return fi; 1188 } finally { 1189 sa.recycle(); 1190 } 1191 } 1192 parseFeatureGroup(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)1193 private static ParseResult<ParsingPackage> parseFeatureGroup(ParseInput input, 1194 ParsingPackage pkg, Resources res, XmlResourceParser parser) 1195 throws IOException, XmlPullParserException { 1196 FeatureGroupInfo group = new FeatureGroupInfo(); 1197 ArrayList<FeatureInfo> features = null; 1198 final int depth = parser.getDepth(); 1199 int type; 1200 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 1201 && (type != XmlPullParser.END_TAG 1202 || parser.getDepth() > depth)) { 1203 if (type != XmlPullParser.START_TAG) { 1204 continue; 1205 } 1206 1207 final String innerTagName = parser.getName(); 1208 if (innerTagName.equals("uses-feature")) { 1209 FeatureInfo featureInfo = parseFeatureInfo(res, parser); 1210 // FeatureGroups are stricter and mandate that 1211 // any <uses-feature> declared are mandatory. 1212 featureInfo.flags |= FeatureInfo.FLAG_REQUIRED; 1213 features = ArrayUtils.add(features, featureInfo); 1214 } else { 1215 Slog.w(TAG, 1216 "Unknown element under <feature-group>: " + innerTagName + 1217 " at " + pkg.getBaseCodePath() + " " + 1218 parser.getPositionDescription()); 1219 } 1220 } 1221 1222 if (features != null) { 1223 group.features = new FeatureInfo[features.size()]; 1224 group.features = features.toArray(group.features); 1225 } 1226 1227 pkg.addFeatureGroup(group); 1228 return input.success(pkg); 1229 } 1230 parseUsesSdk(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)1231 private static ParseResult<ParsingPackage> parseUsesSdk(ParseInput input, 1232 ParsingPackage pkg, Resources res, XmlResourceParser parser) 1233 throws IOException, XmlPullParserException { 1234 if (PackageParser.SDK_VERSION > 0) { 1235 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesSdk); 1236 try { 1237 int minVers = 1; 1238 String minCode = null; 1239 int targetVers = 0; 1240 String targetCode = null; 1241 1242 TypedValue val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_minSdkVersion); 1243 if (val != null) { 1244 if (val.type == TypedValue.TYPE_STRING && val.string != null) { 1245 minCode = val.string.toString(); 1246 } else { 1247 // If it's not a string, it's an integer. 1248 minVers = val.data; 1249 } 1250 } 1251 1252 val = sa.peekValue(R.styleable.AndroidManifestUsesSdk_targetSdkVersion); 1253 if (val != null) { 1254 if (val.type == TypedValue.TYPE_STRING && val.string != null) { 1255 targetCode = val.string.toString(); 1256 if (minCode == null) { 1257 minCode = targetCode; 1258 } 1259 } else { 1260 // If it's not a string, it's an integer. 1261 targetVers = val.data; 1262 } 1263 } else { 1264 targetVers = minVers; 1265 targetCode = minCode; 1266 } 1267 1268 ParseResult<Integer> targetSdkVersionResult = computeTargetSdkVersion( 1269 targetVers, targetCode, PackageParser.SDK_CODENAMES, input); 1270 if (targetSdkVersionResult.isError()) { 1271 return input.error(targetSdkVersionResult); 1272 } 1273 1274 int targetSdkVersion = targetSdkVersionResult.getResult(); 1275 1276 ParseResult<?> deferResult = 1277 input.enableDeferredError(pkg.getPackageName(), targetSdkVersion); 1278 if (deferResult.isError()) { 1279 return input.error(deferResult); 1280 } 1281 1282 ParseResult<Integer> minSdkVersionResult = computeMinSdkVersion(minVers, minCode, 1283 PackageParser.SDK_VERSION, PackageParser.SDK_CODENAMES, input); 1284 if (minSdkVersionResult.isError()) { 1285 return input.error(minSdkVersionResult); 1286 } 1287 1288 int minSdkVersion = minSdkVersionResult.getResult(); 1289 1290 pkg.setMinSdkVersion(minSdkVersion) 1291 .setTargetSdkVersion(targetSdkVersion); 1292 1293 int type; 1294 final int innerDepth = parser.getDepth(); 1295 SparseIntArray minExtensionVersions = null; 1296 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 1297 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { 1298 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 1299 continue; 1300 } 1301 1302 final ParseResult result; 1303 if (parser.getName().equals("extension-sdk")) { 1304 if (minExtensionVersions == null) { 1305 minExtensionVersions = new SparseIntArray(); 1306 } 1307 result = parseExtensionSdk(input, res, parser, minExtensionVersions); 1308 XmlUtils.skipCurrentTag(parser); 1309 } else { 1310 result = ParsingUtils.unknownTag("<uses-sdk>", pkg, parser, input); 1311 } 1312 1313 if (result.isError()) { 1314 return input.error(result); 1315 } 1316 } 1317 pkg.setMinExtensionVersions(exactSizedCopyOfSparseArray(minExtensionVersions)); 1318 } finally { 1319 sa.recycle(); 1320 } 1321 } 1322 return input.success(pkg); 1323 } 1324 1325 @Nullable exactSizedCopyOfSparseArray(@ullable SparseIntArray input)1326 private static SparseIntArray exactSizedCopyOfSparseArray(@Nullable SparseIntArray input) { 1327 if (input == null) { 1328 return null; 1329 } 1330 SparseIntArray output = new SparseIntArray(input.size()); 1331 for (int i = 0; i < input.size(); i++) { 1332 output.put(input.keyAt(i), input.valueAt(i)); 1333 } 1334 return output; 1335 } 1336 parseExtensionSdk( ParseInput input, Resources res, XmlResourceParser parser, SparseIntArray minExtensionVersions)1337 private static ParseResult<SparseIntArray> parseExtensionSdk( 1338 ParseInput input, Resources res, XmlResourceParser parser, 1339 SparseIntArray minExtensionVersions) { 1340 int sdkVersion; 1341 int minVersion; 1342 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestExtensionSdk); 1343 try { 1344 sdkVersion = sa.getInt(R.styleable.AndroidManifestExtensionSdk_sdkVersion, -1); 1345 minVersion = sa.getInt(R.styleable.AndroidManifestExtensionSdk_minExtensionVersion, -1); 1346 } finally { 1347 sa.recycle(); 1348 } 1349 1350 if (sdkVersion < 0) { 1351 return input.error( 1352 PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, 1353 "<extension-sdk> must specify an sdkVersion >= 0"); 1354 } 1355 if (minVersion < 0) { 1356 return input.error( 1357 PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, 1358 "<extension-sdk> must specify minExtensionVersion >= 0"); 1359 } 1360 1361 try { 1362 int version = SdkExtensions.getExtensionVersion(sdkVersion); 1363 if (version < minVersion) { 1364 return input.error( 1365 PackageManager.INSTALL_FAILED_OLDER_SDK, 1366 "Package requires " + sdkVersion + " extension version " + minVersion 1367 + " which exceeds device version " + version); 1368 } 1369 } catch (RuntimeException e) { 1370 return input.error( 1371 PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, 1372 "Specified sdkVersion " + sdkVersion + " is not valid"); 1373 } 1374 minExtensionVersions.put(sdkVersion, minVersion); 1375 return input.success(minExtensionVersions); 1376 } 1377 1378 /** 1379 * {@link ParseResult} version of 1380 * {@link PackageParser#computeMinSdkVersion(int, String, int, String[], String[])} 1381 */ computeMinSdkVersion(@ntRangefrom = 1) int minVers, @Nullable String minCode, @IntRange(from = 1) int platformSdkVersion, @NonNull String[] platformSdkCodenames, @NonNull ParseInput input)1382 public static ParseResult<Integer> computeMinSdkVersion(@IntRange(from = 1) int minVers, 1383 @Nullable String minCode, @IntRange(from = 1) int platformSdkVersion, 1384 @NonNull String[] platformSdkCodenames, @NonNull ParseInput input) { 1385 // If it's a release SDK, make sure we meet the minimum SDK requirement. 1386 if (minCode == null) { 1387 if (minVers <= platformSdkVersion) { 1388 return input.success(minVers); 1389 } 1390 1391 // We don't meet the minimum SDK requirement. 1392 return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, 1393 "Requires newer sdk version #" + minVers 1394 + " (current version is #" + platformSdkVersion + ")"); 1395 } 1396 1397 // If it's a pre-release SDK and the codename matches this platform, we 1398 // definitely meet the minimum SDK requirement. 1399 if (matchTargetCode(platformSdkCodenames, minCode)) { 1400 return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); 1401 } 1402 1403 // Otherwise, we're looking at an incompatible pre-release SDK. 1404 if (platformSdkCodenames.length > 0) { 1405 return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, 1406 "Requires development platform " + minCode 1407 + " (current platform is any of " 1408 + Arrays.toString(platformSdkCodenames) + ")"); 1409 } else { 1410 return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, 1411 "Requires development platform " + minCode 1412 + " but this is a release platform."); 1413 } 1414 } 1415 1416 /** 1417 * {@link ParseResult} version of 1418 * {@link PackageParser#computeTargetSdkVersion(int, String, String[], String[])} 1419 */ computeTargetSdkVersion(@ntRangefrom = 0) int targetVers, @Nullable String targetCode, @NonNull String[] platformSdkCodenames, @NonNull ParseInput input)1420 public static ParseResult<Integer> computeTargetSdkVersion(@IntRange(from = 0) int targetVers, 1421 @Nullable String targetCode, @NonNull String[] platformSdkCodenames, 1422 @NonNull ParseInput input) { 1423 // If it's a release SDK, return the version number unmodified. 1424 if (targetCode == null) { 1425 return input.success(targetVers); 1426 } 1427 1428 // If it's a pre-release SDK and the codename matches this platform, it 1429 // definitely targets this SDK. 1430 if (matchTargetCode(platformSdkCodenames, targetCode)) { 1431 return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); 1432 } 1433 1434 // Otherwise, we're looking at an incompatible pre-release SDK. 1435 if (platformSdkCodenames.length > 0) { 1436 return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, 1437 "Requires development platform " + targetCode 1438 + " (current platform is any of " 1439 + Arrays.toString(platformSdkCodenames) + ")"); 1440 } else { 1441 return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, 1442 "Requires development platform " + targetCode 1443 + " but this is a release platform."); 1444 } 1445 } 1446 1447 /** 1448 * Matches a given {@code targetCode} against a set of release codeNames. Target codes can 1449 * either be of the form {@code [codename]}" (e.g {@code "Q"}) or of the form 1450 * {@code [codename].[fingerprint]} (e.g {@code "Q.cafebc561"}). 1451 */ matchTargetCode(@onNull String[] codeNames, @NonNull String targetCode)1452 private static boolean matchTargetCode(@NonNull String[] codeNames, 1453 @NonNull String targetCode) { 1454 final String targetCodeName; 1455 final int targetCodeIdx = targetCode.indexOf('.'); 1456 if (targetCodeIdx == -1) { 1457 targetCodeName = targetCode; 1458 } else { 1459 targetCodeName = targetCode.substring(0, targetCodeIdx); 1460 } 1461 return ArrayUtils.contains(codeNames, targetCodeName); 1462 } 1463 parseRestrictUpdateHash(int flags, ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)1464 private static ParseResult<ParsingPackage> parseRestrictUpdateHash(int flags, ParseInput input, 1465 ParsingPackage pkg, Resources res, XmlResourceParser parser) { 1466 if ((flags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) { 1467 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestRestrictUpdate); 1468 try { 1469 final String hash = sa.getNonConfigurationString( 1470 R.styleable.AndroidManifestRestrictUpdate_hash, 1471 0); 1472 1473 if (hash != null) { 1474 final int hashLength = hash.length(); 1475 final byte[] hashBytes = new byte[hashLength / 2]; 1476 for (int i = 0; i < hashLength; i += 2) { 1477 hashBytes[i / 2] = (byte) ((Character.digit(hash.charAt(i), 16) 1478 << 4) 1479 + Character.digit(hash.charAt(i + 1), 16)); 1480 } 1481 pkg.setRestrictUpdateHash(hashBytes); 1482 } else { 1483 pkg.setRestrictUpdateHash(null); 1484 } 1485 } finally { 1486 sa.recycle(); 1487 } 1488 } 1489 return input.success(pkg); 1490 } 1491 parseQueries(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)1492 private static ParseResult<ParsingPackage> parseQueries(ParseInput input, ParsingPackage pkg, 1493 Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException { 1494 final int depth = parser.getDepth(); 1495 int type; 1496 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 1497 && (type != XmlPullParser.END_TAG 1498 || parser.getDepth() > depth)) { 1499 if (type != XmlPullParser.START_TAG) { 1500 continue; 1501 } 1502 if (parser.getName().equals("intent")) { 1503 ParseResult<ParsedIntentInfo> result = ParsedIntentInfoUtils.parseIntentInfo(null, 1504 pkg, res, parser, true /*allowGlobs*/, true /*allowAutoVerify*/, input); 1505 if (result.isError()) { 1506 return input.error(result); 1507 } 1508 1509 ParsedIntentInfo intentInfo = result.getResult(); 1510 1511 Uri data = null; 1512 String dataType = null; 1513 String host = null; 1514 final int numActions = intentInfo.countActions(); 1515 final int numSchemes = intentInfo.countDataSchemes(); 1516 final int numTypes = intentInfo.countDataTypes(); 1517 final int numHosts = intentInfo.getHosts().length; 1518 if ((numSchemes == 0 && numTypes == 0 && numActions == 0)) { 1519 return input.error("intent tags must contain either an action or data."); 1520 } 1521 if (numActions > 1) { 1522 return input.error("intent tag may have at most one action."); 1523 } 1524 if (numTypes > 1) { 1525 return input.error("intent tag may have at most one data type."); 1526 } 1527 if (numSchemes > 1) { 1528 return input.error("intent tag may have at most one data scheme."); 1529 } 1530 if (numHosts > 1) { 1531 return input.error("intent tag may have at most one data host."); 1532 } 1533 Intent intent = new Intent(); 1534 for (int i = 0, max = intentInfo.countCategories(); i < max; i++) { 1535 intent.addCategory(intentInfo.getCategory(i)); 1536 } 1537 if (numHosts == 1) { 1538 host = intentInfo.getHosts()[0]; 1539 } 1540 if (numSchemes == 1) { 1541 data = new Uri.Builder() 1542 .scheme(intentInfo.getDataScheme(0)) 1543 .authority(host) 1544 .path(IntentFilter.WILDCARD_PATH) 1545 .build(); 1546 } 1547 if (numTypes == 1) { 1548 dataType = intentInfo.getDataType(0); 1549 // The dataType may have had the '/' removed for the dynamic mimeType feature. 1550 // If we detect that case, we add the * back. 1551 if (!dataType.contains("/")) { 1552 dataType = dataType + "/*"; 1553 } 1554 if (data == null) { 1555 data = new Uri.Builder() 1556 .scheme("content") 1557 .authority(IntentFilter.WILDCARD) 1558 .path(IntentFilter.WILDCARD_PATH) 1559 .build(); 1560 } 1561 } 1562 intent.setDataAndType(data, dataType); 1563 if (numActions == 1) { 1564 intent.setAction(intentInfo.getAction(0)); 1565 } 1566 pkg.addQueriesIntent(intent); 1567 } else if (parser.getName().equals("package")) { 1568 final TypedArray sa = res.obtainAttributes(parser, 1569 R.styleable.AndroidManifestQueriesPackage); 1570 final String packageName = sa.getNonConfigurationString( 1571 R.styleable.AndroidManifestQueriesPackage_name, 0); 1572 if (TextUtils.isEmpty(packageName)) { 1573 return input.error("Package name is missing from package tag."); 1574 } 1575 pkg.addQueriesPackage(packageName.intern()); 1576 } else if (parser.getName().equals("provider")) { 1577 final TypedArray sa = res.obtainAttributes(parser, 1578 R.styleable.AndroidManifestQueriesProvider); 1579 try { 1580 final String authorities = sa.getNonConfigurationString( 1581 R.styleable.AndroidManifestQueriesProvider_authorities, 0); 1582 if (TextUtils.isEmpty(authorities)) { 1583 return input.error( 1584 PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, 1585 "Authority missing from provider tag." 1586 ); 1587 } 1588 StringTokenizer authoritiesTokenizer = new StringTokenizer(authorities, ";"); 1589 while (authoritiesTokenizer.hasMoreElements()) { 1590 pkg.addQueriesProvider(authoritiesTokenizer.nextToken()); 1591 } 1592 } finally { 1593 sa.recycle(); 1594 } 1595 } 1596 } 1597 return input.success(pkg); 1598 } 1599 1600 /** 1601 * Parse the {@code application} XML tree at the current parse location in a 1602 * <em>base APK</em> manifest. 1603 * <p> 1604 * When adding new features, carefully consider if they should also be 1605 * supported by split APKs. 1606 * 1607 * This method should avoid using a getter for fields set by this method. Prefer assigning 1608 * a local variable and using it. Otherwise there's an ordering problem which can be broken 1609 * if any code moves around. 1610 */ parseBaseApplication(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)1611 private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input, 1612 ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags) 1613 throws XmlPullParserException, IOException { 1614 final String pkgName = pkg.getPackageName(); 1615 int targetSdk = pkg.getTargetSdkVersion(); 1616 1617 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication); 1618 try { 1619 // TODO(b/135203078): Remove this and force unit tests to mock an empty manifest 1620 // This case can only happen in unit tests where we sometimes need to create fakes 1621 // of various package parser data structures. 1622 if (sa == null) { 1623 return input.error("<application> does not contain any attributes"); 1624 } 1625 1626 String name = sa.getNonConfigurationString(R.styleable.AndroidManifestApplication_name, 1627 0); 1628 if (name != null) { 1629 String packageName = pkg.getPackageName(); 1630 String outInfoName = ParsingUtils.buildClassName(packageName, name); 1631 if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(outInfoName)) { 1632 return input.error("<application> invalid android:name"); 1633 } else if (outInfoName == null) { 1634 return input.error("Empty class name in package " + packageName); 1635 } 1636 1637 pkg.setClassName(outInfoName); 1638 } 1639 1640 TypedValue labelValue = sa.peekValue(R.styleable.AndroidManifestApplication_label); 1641 if (labelValue != null) { 1642 pkg.setLabelRes(labelValue.resourceId); 1643 if (labelValue.resourceId == 0) { 1644 pkg.setNonLocalizedLabel(labelValue.coerceToString()); 1645 } 1646 } 1647 1648 parseBaseAppBasicFlags(pkg, sa); 1649 1650 String manageSpaceActivity = nonConfigString(Configuration.NATIVE_CONFIG_VERSION, 1651 R.styleable.AndroidManifestApplication_manageSpaceActivity, sa); 1652 if (manageSpaceActivity != null) { 1653 String manageSpaceActivityName = ParsingUtils.buildClassName(pkgName, 1654 manageSpaceActivity); 1655 1656 if (manageSpaceActivityName == null) { 1657 return input.error("Empty class name in package " + pkgName); 1658 } 1659 1660 pkg.setManageSpaceActivityName(manageSpaceActivityName); 1661 } 1662 1663 if (pkg.isAllowBackup()) { 1664 // backupAgent, killAfterRestore, fullBackupContent, backupInForeground, 1665 // and restoreAnyVersion are only relevant if backup is possible for the 1666 // given application. 1667 String backupAgent = nonConfigString(Configuration.NATIVE_CONFIG_VERSION, 1668 R.styleable.AndroidManifestApplication_backupAgent, sa); 1669 if (backupAgent != null) { 1670 String backupAgentName = ParsingUtils.buildClassName(pkgName, backupAgent); 1671 if (backupAgentName == null) { 1672 return input.error("Empty class name in package " + pkgName); 1673 } 1674 1675 if (PackageParser.DEBUG_BACKUP) { 1676 Slog.v(TAG, "android:backupAgent = " + backupAgentName 1677 + " from " + pkgName + "+" + backupAgent); 1678 } 1679 1680 pkg.setBackupAgentName(backupAgentName) 1681 .setKillAfterRestore(bool(true, 1682 R.styleable.AndroidManifestApplication_killAfterRestore, sa)) 1683 .setRestoreAnyVersion(bool(false, 1684 R.styleable.AndroidManifestApplication_restoreAnyVersion, sa)) 1685 .setFullBackupOnly(bool(false, 1686 R.styleable.AndroidManifestApplication_fullBackupOnly, sa)) 1687 .setBackupInForeground(bool(false, 1688 R.styleable.AndroidManifestApplication_backupInForeground, sa)); 1689 } 1690 1691 TypedValue v = sa.peekValue( 1692 R.styleable.AndroidManifestApplication_fullBackupContent); 1693 int fullBackupContent = 0; 1694 1695 if (v != null) { 1696 fullBackupContent = v.resourceId; 1697 1698 if (v.resourceId == 0) { 1699 if (PackageParser.DEBUG_BACKUP) { 1700 Slog.v(TAG, "fullBackupContent specified as boolean=" + 1701 (v.data == 0 ? "false" : "true")); 1702 } 1703 // "false" => -1, "true" => 0 1704 fullBackupContent = v.data == 0 ? -1 : 0; 1705 } 1706 1707 pkg.setFullBackupContent(fullBackupContent); 1708 } 1709 if (PackageParser.DEBUG_BACKUP) { 1710 Slog.v(TAG, "fullBackupContent=" + fullBackupContent + " for " + pkgName); 1711 } 1712 } 1713 1714 if (sa.getBoolean(R.styleable.AndroidManifestApplication_persistent, false)) { 1715 // Check if persistence is based on a feature being present 1716 final String requiredFeature = sa.getNonResourceString(R.styleable 1717 .AndroidManifestApplication_persistentWhenFeatureAvailable); 1718 pkg.setPersistent(requiredFeature == null || mCallback.hasFeature(requiredFeature)); 1719 } 1720 1721 // TODO(b/135203078): Should parsing code be responsible for this? Maybe move to a 1722 // util or just have PackageImpl return true if either flag is set 1723 // Debuggable implies profileable 1724 pkg.setProfileableByShell(pkg.isProfileableByShell() || pkg.isDebuggable()); 1725 1726 if (sa.hasValueOrEmpty(R.styleable.AndroidManifestApplication_resizeableActivity)) { 1727 pkg.setResizeableActivity(sa.getBoolean( 1728 R.styleable.AndroidManifestApplication_resizeableActivity, true)); 1729 } else { 1730 pkg.setResizeableActivityViaSdkVersion( 1731 targetSdk >= Build.VERSION_CODES.N); 1732 } 1733 1734 String taskAffinity; 1735 if (targetSdk >= Build.VERSION_CODES.FROYO) { 1736 taskAffinity = sa.getNonConfigurationString( 1737 R.styleable.AndroidManifestApplication_taskAffinity, 1738 Configuration.NATIVE_CONFIG_VERSION); 1739 } else { 1740 // Some older apps have been seen to use a resource reference 1741 // here that on older builds was ignored (with a warning). We 1742 // need to continue to do this for them so they don't break. 1743 taskAffinity = sa.getNonResourceString( 1744 R.styleable.AndroidManifestApplication_taskAffinity); 1745 } 1746 1747 ParseResult<String> taskAffinityResult = ComponentParseUtils.buildTaskAffinityName( 1748 pkgName, pkgName, taskAffinity, input); 1749 if (taskAffinityResult.isError()) { 1750 return input.error(taskAffinityResult); 1751 } 1752 1753 pkg.setTaskAffinity(taskAffinityResult.getResult()); 1754 String factory = sa.getNonResourceString( 1755 R.styleable.AndroidManifestApplication_appComponentFactory); 1756 if (factory != null) { 1757 String appComponentFactory = ParsingUtils.buildClassName(pkgName, factory); 1758 if (appComponentFactory == null) { 1759 return input.error("Empty class name in package " + pkgName); 1760 } 1761 1762 pkg.setAppComponentFactory(appComponentFactory); 1763 } 1764 1765 CharSequence pname; 1766 if (targetSdk >= Build.VERSION_CODES.FROYO) { 1767 pname = sa.getNonConfigurationString( 1768 R.styleable.AndroidManifestApplication_process, 1769 Configuration.NATIVE_CONFIG_VERSION); 1770 } else { 1771 // Some older apps have been seen to use a resource reference 1772 // here that on older builds was ignored (with a warning). We 1773 // need to continue to do this for them so they don't break. 1774 pname = sa.getNonResourceString( 1775 R.styleable.AndroidManifestApplication_process); 1776 } 1777 ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName( 1778 pkgName, null, pname, flags, mSeparateProcesses, input); 1779 if (processNameResult.isError()) { 1780 return input.error(processNameResult); 1781 } 1782 1783 String processName = processNameResult.getResult(); 1784 pkg.setProcessName(processName); 1785 1786 if (pkg.isCantSaveState()) { 1787 // A heavy-weight application can not be in a custom process. 1788 // We can do direct compare because we intern all strings. 1789 if (processName != null && !processName.equals(pkgName)) { 1790 return input.error( 1791 "cantSaveState applications can not use custom processes"); 1792 } 1793 } 1794 1795 String classLoaderName = pkg.getClassLoaderName(); 1796 if (classLoaderName != null 1797 && !ClassLoaderFactory.isValidClassLoaderName(classLoaderName)) { 1798 return input.error("Invalid class loader name: " + classLoaderName); 1799 } 1800 1801 pkg.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestApplication_gwpAsanMode, -1)); 1802 } finally { 1803 sa.recycle(); 1804 } 1805 1806 boolean hasActivityOrder = false; 1807 boolean hasReceiverOrder = false; 1808 boolean hasServiceOrder = false; 1809 final int depth = parser.getDepth(); 1810 int type; 1811 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 1812 && (type != XmlPullParser.END_TAG 1813 || parser.getDepth() > depth)) { 1814 if (type != XmlPullParser.START_TAG) { 1815 continue; 1816 } 1817 1818 final ParseResult result; 1819 String tagName = parser.getName(); 1820 boolean isActivity = false; 1821 switch (tagName) { 1822 case "activity": 1823 isActivity = true; 1824 // fall-through 1825 case "receiver": 1826 ParseResult<ParsedActivity> activityResult = 1827 ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg, 1828 res, parser, flags, PackageParser.sUseRoundIcon, input); 1829 1830 if (activityResult.isSuccess()) { 1831 ParsedActivity activity = activityResult.getResult(); 1832 if (isActivity) { 1833 hasActivityOrder |= (activity.getOrder() != 0); 1834 pkg.addActivity(activity); 1835 } else { 1836 hasReceiverOrder |= (activity.getOrder() != 0); 1837 pkg.addReceiver(activity); 1838 } 1839 } 1840 1841 result = activityResult; 1842 break; 1843 case "service": 1844 ParseResult<ParsedService> serviceResult = 1845 ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser, 1846 flags, PackageParser.sUseRoundIcon, input); 1847 if (serviceResult.isSuccess()) { 1848 ParsedService service = serviceResult.getResult(); 1849 hasServiceOrder |= (service.getOrder() != 0); 1850 pkg.addService(service); 1851 } 1852 1853 result = serviceResult; 1854 break; 1855 case "provider": 1856 ParseResult<ParsedProvider> providerResult = 1857 ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser, 1858 flags, PackageParser.sUseRoundIcon, input); 1859 if (providerResult.isSuccess()) { 1860 pkg.addProvider(providerResult.getResult()); 1861 } 1862 1863 result = providerResult; 1864 break; 1865 case "activity-alias": 1866 activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res, 1867 parser, PackageParser.sUseRoundIcon, input); 1868 if (activityResult.isSuccess()) { 1869 ParsedActivity activity = activityResult.getResult(); 1870 hasActivityOrder |= (activity.getOrder() != 0); 1871 pkg.addActivity(activity); 1872 } 1873 1874 result = activityResult; 1875 break; 1876 default: 1877 result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags); 1878 break; 1879 } 1880 1881 if (result.isError()) { 1882 return input.error(result); 1883 } 1884 } 1885 1886 if (TextUtils.isEmpty(pkg.getStaticSharedLibName())) { 1887 // Add a hidden app detail activity to normal apps which forwards user to App Details 1888 // page. 1889 ParseResult<ParsedActivity> a = generateAppDetailsHiddenActivity(input, pkg); 1890 if (a.isError()) { 1891 // Error should be impossible here, as the only failure case as of SDK R is a 1892 // string validation error on a constant ":app_details" string passed in by the 1893 // parsing code itself. For this reason, this is just a hard failure instead of 1894 // deferred. 1895 return input.error(a); 1896 } 1897 1898 pkg.addActivity(a.getResult()); 1899 } 1900 1901 if (hasActivityOrder) { 1902 pkg.sortActivities(); 1903 } 1904 if (hasReceiverOrder) { 1905 pkg.sortReceivers(); 1906 } 1907 if (hasServiceOrder) { 1908 pkg.sortServices(); 1909 } 1910 1911 // Must be run after the entire {@link ApplicationInfo} has been fully processed and after 1912 // every activity info has had a chance to set it from its attributes. 1913 setMaxAspectRatio(pkg); 1914 setMinAspectRatio(pkg); 1915 setSupportsSizeChanges(pkg); 1916 1917 pkg.setHasDomainUrls(hasDomainURLs(pkg)); 1918 1919 return input.success(pkg); 1920 } 1921 1922 /** 1923 * Collection of single-line, no (or little) logic assignments. Separated for readability. 1924 * 1925 * Flags are separated by type and by default value. They are sorted alphabetically within each 1926 * section. 1927 */ parseBaseAppBasicFlags(ParsingPackage pkg, TypedArray sa)1928 private void parseBaseAppBasicFlags(ParsingPackage pkg, TypedArray sa) { 1929 int targetSdk = pkg.getTargetSdkVersion(); 1930 //@formatter:off 1931 // CHECKSTYLE:off 1932 pkg 1933 // Default true 1934 .setAllowBackup(bool(true, R.styleable.AndroidManifestApplication_allowBackup, sa)) 1935 .setAllowClearUserData(bool(true, R.styleable.AndroidManifestApplication_allowClearUserData, sa)) 1936 .setAllowClearUserDataOnFailedRestore(bool(true, R.styleable.AndroidManifestApplication_allowClearUserDataOnFailedRestore, sa)) 1937 .setAllowNativeHeapPointerTagging(bool(true, R.styleable.AndroidManifestApplication_allowNativeHeapPointerTagging, sa)) 1938 .setEnabled(bool(true, R.styleable.AndroidManifestApplication_enabled, sa)) 1939 .setExtractNativeLibs(bool(true, R.styleable.AndroidManifestApplication_extractNativeLibs, sa)) 1940 .setHasCode(bool(true, R.styleable.AndroidManifestApplication_hasCode, sa)) 1941 // Default false 1942 .setAllowTaskReparenting(bool(false, R.styleable.AndroidManifestApplication_allowTaskReparenting, sa)) 1943 .setCantSaveState(bool(false, R.styleable.AndroidManifestApplication_cantSaveState, sa)) 1944 .setCrossProfile(bool(false, R.styleable.AndroidManifestApplication_crossProfile, sa)) 1945 .setDebuggable(bool(false, R.styleable.AndroidManifestApplication_debuggable, sa)) 1946 .setDefaultToDeviceProtectedStorage(bool(false, R.styleable.AndroidManifestApplication_defaultToDeviceProtectedStorage, sa)) 1947 .setDirectBootAware(bool(false, R.styleable.AndroidManifestApplication_directBootAware, sa)) 1948 .setForceQueryable(bool(false, R.styleable.AndroidManifestApplication_forceQueryable, sa)) 1949 .setGame(bool(false, R.styleable.AndroidManifestApplication_isGame, sa)) 1950 .setHasFragileUserData(bool(false, R.styleable.AndroidManifestApplication_hasFragileUserData, sa)) 1951 .setLargeHeap(bool(false, R.styleable.AndroidManifestApplication_largeHeap, sa)) 1952 .setMultiArch(bool(false, R.styleable.AndroidManifestApplication_multiArch, sa)) 1953 .setPreserveLegacyExternalStorage(bool(false, R.styleable.AndroidManifestApplication_preserveLegacyExternalStorage, sa)) 1954 .setRequiredForAllUsers(bool(false, R.styleable.AndroidManifestApplication_requiredForAllUsers, sa)) 1955 .setSupportsRtl(bool(false, R.styleable.AndroidManifestApplication_supportsRtl, sa)) 1956 .setTestOnly(bool(false, R.styleable.AndroidManifestApplication_testOnly, sa)) 1957 .setUseEmbeddedDex(bool(false, R.styleable.AndroidManifestApplication_useEmbeddedDex, sa)) 1958 .setUsesNonSdkApi(bool(false, R.styleable.AndroidManifestApplication_usesNonSdkApi, sa)) 1959 .setVmSafeMode(bool(false, R.styleable.AndroidManifestApplication_vmSafeMode, sa)) 1960 .setAutoRevokePermissions(anInt(R.styleable.AndroidManifestApplication_autoRevokePermissions, sa)) 1961 // targetSdkVersion gated 1962 .setAllowAudioPlaybackCapture(bool(targetSdk >= Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture, sa)) 1963 .setBaseHardwareAccelerated(bool(targetSdk >= Build.VERSION_CODES.ICE_CREAM_SANDWICH, R.styleable.AndroidManifestApplication_hardwareAccelerated, sa)) 1964 .setRequestLegacyExternalStorage(bool(targetSdk < Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_requestLegacyExternalStorage, sa)) 1965 .setUsesCleartextTraffic(bool(targetSdk < Build.VERSION_CODES.P, R.styleable.AndroidManifestApplication_usesCleartextTraffic, sa)) 1966 // Ints Default 0 1967 .setUiOptions(anInt(R.styleable.AndroidManifestApplication_uiOptions, sa)) 1968 // Ints 1969 .setCategory(anInt(ApplicationInfo.CATEGORY_UNDEFINED, R.styleable.AndroidManifestApplication_appCategory, sa)) 1970 // Floats Default 0f 1971 .setMaxAspectRatio(aFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, sa)) 1972 .setMinAspectRatio(aFloat(R.styleable.AndroidManifestApplication_minAspectRatio, sa)) 1973 // Resource ID 1974 .setBanner(resId(R.styleable.AndroidManifestApplication_banner, sa)) 1975 .setDescriptionRes(resId(R.styleable.AndroidManifestApplication_description, sa)) 1976 .setIconRes(resId(R.styleable.AndroidManifestApplication_icon, sa)) 1977 .setLogo(resId(R.styleable.AndroidManifestApplication_logo, sa)) 1978 .setNetworkSecurityConfigRes(resId(R.styleable.AndroidManifestApplication_networkSecurityConfig, sa)) 1979 .setRoundIconRes(resId(R.styleable.AndroidManifestApplication_roundIcon, sa)) 1980 .setTheme(resId(R.styleable.AndroidManifestApplication_theme, sa)) 1981 // Strings 1982 .setClassLoaderName(string(R.styleable.AndroidManifestApplication_classLoader, sa)) 1983 .setRequiredAccountType(string(R.styleable.AndroidManifestApplication_requiredAccountType, sa)) 1984 .setRestrictedAccountType(string(R.styleable.AndroidManifestApplication_restrictedAccountType, sa)) 1985 .setZygotePreloadName(string(R.styleable.AndroidManifestApplication_zygotePreloadName, sa)) 1986 // Non-Config String 1987 .setPermission(nonConfigString(0, R.styleable.AndroidManifestApplication_permission, sa)); 1988 // CHECKSTYLE:on 1989 //@formatter:on 1990 } 1991 1992 /** 1993 * For parsing non-MainComponents. Main ones have an order and some special handling which is 1994 * done directly in {@link #parseBaseApplication(ParseInput, ParsingPackage, Resources, 1995 * XmlResourceParser, int)}. 1996 */ 1997 private ParseResult parseBaseAppChildTag(ParseInput input, String tag, ParsingPackage pkg, 1998 Resources res, XmlResourceParser parser, int flags) 1999 throws IOException, XmlPullParserException { 2000 switch (tag) { 2001 case "meta-data": 2002 // TODO(b/135203078): I have no idea what this comment means 2003 // note: application meta-data is stored off to the side, so it can 2004 // remain null in the primary copy (we like to avoid extra copies because 2005 // it can be large) 2006 ParseResult<Bundle> metaDataResult = parseMetaData(pkg, res, parser, 2007 pkg.getMetaData(), input); 2008 if (metaDataResult.isSuccess()) { 2009 pkg.setMetaData(metaDataResult.getResult()); 2010 } 2011 2012 return metaDataResult; 2013 case "static-library": 2014 return parseStaticLibrary(pkg, res, parser, input); 2015 case "library": 2016 return parseLibrary(pkg, res, parser, input); 2017 case "uses-static-library": 2018 return parseUsesStaticLibrary(input, pkg, res, parser); 2019 case "uses-library": 2020 return parseUsesLibrary(input, pkg, res, parser); 2021 case "processes": 2022 return parseProcesses(input, pkg, res, parser, mSeparateProcesses, flags); 2023 case "uses-package": 2024 // Dependencies for app installers; we don't currently try to 2025 // enforce this. 2026 return input.success(null); 2027 case "profileable": 2028 return parseProfileable(input, pkg, res, parser); 2029 default: 2030 return ParsingUtils.unknownTag("<application>", pkg, parser, input); 2031 } 2032 } 2033 2034 @NonNull 2035 private static ParseResult<ParsingPackage> parseStaticLibrary( 2036 ParsingPackage pkg, Resources res, 2037 XmlResourceParser parser, ParseInput input) { 2038 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestStaticLibrary); 2039 try { 2040 // Note: don't allow this value to be a reference to a resource 2041 // that may change. 2042 String lname = sa.getNonResourceString( 2043 R.styleable.AndroidManifestStaticLibrary_name); 2044 final int version = sa.getInt( 2045 R.styleable.AndroidManifestStaticLibrary_version, -1); 2046 final int versionMajor = sa.getInt( 2047 R.styleable.AndroidManifestStaticLibrary_versionMajor, 2048 0); 2049 2050 // Since the app canot run without a static lib - fail if malformed 2051 if (lname == null || version < 0) { 2052 return input.error("Bad static-library declaration name: " + lname 2053 + " version: " + version); 2054 } else if (pkg.getSharedUserId() != null) { 2055 return input.error( 2056 PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID, 2057 "sharedUserId not allowed in static shared library" 2058 ); 2059 } else if (pkg.getStaticSharedLibName() != null) { 2060 return input.error("Multiple static-shared libs for package " 2061 + pkg.getPackageName()); 2062 } 2063 2064 return input.success(pkg.setStaticSharedLibName(lname.intern()) 2065 .setStaticSharedLibVersion( 2066 PackageInfo.composeLongVersionCode(versionMajor, version)) 2067 .setStaticSharedLibrary(true)); 2068 } finally { 2069 sa.recycle(); 2070 } 2071 } 2072 2073 @NonNull 2074 private static ParseResult<ParsingPackage> parseLibrary( 2075 ParsingPackage pkg, Resources res, 2076 XmlResourceParser parser, ParseInput input) { 2077 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestLibrary); 2078 try { 2079 // Note: don't allow this value to be a reference to a resource 2080 // that may change. 2081 String lname = sa.getNonResourceString(R.styleable.AndroidManifestLibrary_name); 2082 2083 if (lname != null) { 2084 lname = lname.intern(); 2085 if (!ArrayUtils.contains(pkg.getLibraryNames(), lname)) { 2086 pkg.addLibraryName(lname); 2087 } 2088 } 2089 return input.success(pkg); 2090 } finally { 2091 sa.recycle(); 2092 } 2093 } 2094 2095 @NonNull 2096 private static ParseResult<ParsingPackage> parseUsesStaticLibrary(ParseInput input, 2097 ParsingPackage pkg, Resources res, XmlResourceParser parser) 2098 throws XmlPullParserException, IOException { 2099 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesStaticLibrary); 2100 try { 2101 // Note: don't allow this value to be a reference to a resource that may change. 2102 String lname = sa.getNonResourceString( 2103 R.styleable.AndroidManifestUsesLibrary_name); 2104 final int version = sa.getInt( 2105 R.styleable.AndroidManifestUsesStaticLibrary_version, -1); 2106 String certSha256Digest = sa.getNonResourceString(R.styleable 2107 .AndroidManifestUsesStaticLibrary_certDigest); 2108 2109 // Since an APK providing a static shared lib can only provide the lib - fail if 2110 // malformed 2111 if (lname == null || version < 0 || certSha256Digest == null) { 2112 return input.error("Bad uses-static-library declaration name: " + lname 2113 + " version: " + version + " certDigest" + certSha256Digest); 2114 } 2115 2116 // Can depend only on one version of the same library 2117 List<String> usesStaticLibraries = pkg.getUsesStaticLibraries(); 2118 if (usesStaticLibraries.contains(lname)) { 2119 return input.error( 2120 "Depending on multiple versions of static library " + lname); 2121 } 2122 2123 lname = lname.intern(); 2124 // We allow ":" delimiters in the SHA declaration as this is the format 2125 // emitted by the certtool making it easy for developers to copy/paste. 2126 certSha256Digest = certSha256Digest.replace(":", "").toLowerCase(); 2127 2128 // Fot apps targeting O-MR1 we require explicit enumeration of all certs. 2129 String[] additionalCertSha256Digests = EmptyArray.STRING; 2130 if (pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O_MR1) { 2131 ParseResult<String[]> certResult = parseAdditionalCertificates(input, res, parser); 2132 if (certResult.isError()) { 2133 return input.error(certResult); 2134 } 2135 additionalCertSha256Digests = certResult.getResult(); 2136 } 2137 2138 final String[] certSha256Digests = new String[additionalCertSha256Digests.length + 1]; 2139 certSha256Digests[0] = certSha256Digest; 2140 System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests, 2141 1, additionalCertSha256Digests.length); 2142 2143 return input.success(pkg.addUsesStaticLibrary(lname) 2144 .addUsesStaticLibraryVersion(version) 2145 .addUsesStaticLibraryCertDigests(certSha256Digests)); 2146 } finally { 2147 sa.recycle(); 2148 } 2149 } 2150 2151 @NonNull 2152 private static ParseResult<ParsingPackage> parseUsesLibrary(ParseInput input, 2153 ParsingPackage pkg, Resources res, XmlResourceParser parser) { 2154 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesLibrary); 2155 try { 2156 // Note: don't allow this value to be a reference to a resource 2157 // that may change. 2158 String lname = sa.getNonResourceString(R.styleable.AndroidManifestUsesLibrary_name); 2159 boolean req = sa.getBoolean(R.styleable.AndroidManifestUsesLibrary_required, true); 2160 2161 if (lname != null) { 2162 lname = lname.intern(); 2163 if (req) { 2164 // Upgrade to treat as stronger constraint 2165 pkg.addUsesLibrary(lname) 2166 .removeUsesOptionalLibrary(lname); 2167 } else { 2168 // Ignore if someone already defined as required 2169 if (!ArrayUtils.contains(pkg.getUsesLibraries(), lname)) { 2170 pkg.addUsesOptionalLibrary(lname); 2171 } 2172 } 2173 } 2174 2175 return input.success(pkg); 2176 } finally { 2177 sa.recycle(); 2178 } 2179 } 2180 2181 @NonNull 2182 private static ParseResult<ParsingPackage> parseProcesses(ParseInput input, ParsingPackage pkg, 2183 Resources res, XmlResourceParser parser, String[] separateProcesses, int flags) 2184 throws IOException, XmlPullParserException { 2185 ParseResult<ArrayMap<String, ParsedProcess>> result = 2186 ParsedProcessUtils.parseProcesses(separateProcesses, pkg, res, parser, flags, 2187 input); 2188 if (result.isError()) { 2189 return input.error(result); 2190 } 2191 2192 return input.success(pkg.setProcesses(result.getResult())); 2193 } 2194 2195 @NonNull 2196 private static ParseResult<ParsingPackage> parseProfileable(ParseInput input, 2197 ParsingPackage pkg, Resources res, XmlResourceParser parser) { 2198 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProfileable); 2199 try { 2200 return input.success(pkg.setProfileableByShell(pkg.isProfileableByShell() 2201 || bool(false, R.styleable.AndroidManifestProfileable_shell, sa))); 2202 } finally { 2203 sa.recycle(); 2204 } 2205 } 2206 2207 private static ParseResult<String[]> parseAdditionalCertificates(ParseInput input, 2208 Resources resources, XmlResourceParser parser) 2209 throws XmlPullParserException, IOException { 2210 String[] certSha256Digests = EmptyArray.STRING; 2211 final int depth = parser.getDepth(); 2212 int type; 2213 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 2214 && (type != XmlPullParser.END_TAG 2215 || parser.getDepth() > depth)) { 2216 if (type != XmlPullParser.START_TAG) { 2217 continue; 2218 } 2219 2220 final String nodeName = parser.getName(); 2221 if (nodeName.equals("additional-certificate")) { 2222 TypedArray sa = resources.obtainAttributes(parser, 2223 R.styleable.AndroidManifestAdditionalCertificate); 2224 try { 2225 String certSha256Digest = sa.getNonResourceString( 2226 R.styleable.AndroidManifestAdditionalCertificate_certDigest); 2227 2228 if (TextUtils.isEmpty(certSha256Digest)) { 2229 return input.error("Bad additional-certificate declaration with empty" 2230 + " certDigest:" + certSha256Digest); 2231 } 2232 2233 2234 // We allow ":" delimiters in the SHA declaration as this is the format 2235 // emitted by the certtool making it easy for developers to copy/paste. 2236 certSha256Digest = certSha256Digest.replace(":", "").toLowerCase(); 2237 certSha256Digests = ArrayUtils.appendElement(String.class, 2238 certSha256Digests, certSha256Digest); 2239 } finally { 2240 sa.recycle(); 2241 } 2242 } 2243 } 2244 2245 return input.success(certSha256Digests); 2246 } 2247 2248 /** 2249 * Generate activity object that forwards user to App Details page automatically. 2250 * This activity should be invisible to user and user should not know or see it. 2251 */ 2252 @NonNull 2253 private static ParseResult<ParsedActivity> generateAppDetailsHiddenActivity(ParseInput input, 2254 ParsingPackage pkg) { 2255 String packageName = pkg.getPackageName(); 2256 ParseResult<String> result = ComponentParseUtils.buildTaskAffinityName( 2257 packageName, packageName, ":app_details", input); 2258 if (result.isError()) { 2259 return input.error(result); 2260 } 2261 2262 String taskAffinity = result.getResult(); 2263 2264 // Build custom App Details activity info instead of parsing it from xml 2265 return input.success(ParsedActivity.makeAppDetailsActivity(packageName, 2266 pkg.getProcessName(), pkg.getUiOptions(), taskAffinity, 2267 pkg.isBaseHardwareAccelerated())); 2268 } 2269 2270 /** 2271 * Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI 2272 */ 2273 private static boolean hasDomainURLs(ParsingPackage pkg) { 2274 final List<ParsedActivity> activities = pkg.getActivities(); 2275 final int activitiesSize = activities.size(); 2276 for (int index = 0; index < activitiesSize; index++) { 2277 ParsedActivity activity = activities.get(index); 2278 List<ParsedIntentInfo> filters = activity.getIntents(); 2279 final int filtersSize = filters.size(); 2280 for (int filtersIndex = 0; filtersIndex < filtersSize; filtersIndex++) { 2281 ParsedIntentInfo aii = filters.get(filtersIndex); 2282 if (!aii.hasAction(Intent.ACTION_VIEW)) continue; 2283 if (!aii.hasAction(Intent.ACTION_DEFAULT)) continue; 2284 if (aii.hasDataScheme(IntentFilter.SCHEME_HTTP) || 2285 aii.hasDataScheme(IntentFilter.SCHEME_HTTPS)) { 2286 return true; 2287 } 2288 } 2289 } 2290 return false; 2291 } 2292 2293 /** 2294 * Sets the max aspect ratio of every child activity that doesn't already have an aspect 2295 * ratio set. 2296 */ 2297 private static void setMaxAspectRatio(ParsingPackage pkg) { 2298 // Default to (1.86) 16.7:9 aspect ratio for pre-O apps and unset for O and greater. 2299 // NOTE: 16.7:9 was the max aspect ratio Android devices can support pre-O per the CDD. 2300 float maxAspectRatio = pkg.getTargetSdkVersion() < O 2301 ? PackageParser.DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0; 2302 2303 float packageMaxAspectRatio = pkg.getMaxAspectRatio(); 2304 if (packageMaxAspectRatio != 0) { 2305 // Use the application max aspect ration as default if set. 2306 maxAspectRatio = packageMaxAspectRatio; 2307 } else { 2308 Bundle appMetaData = pkg.getMetaData(); 2309 if (appMetaData != null && appMetaData.containsKey( 2310 PackageParser.METADATA_MAX_ASPECT_RATIO)) { 2311 maxAspectRatio = appMetaData.getFloat(PackageParser.METADATA_MAX_ASPECT_RATIO, 2312 maxAspectRatio); 2313 } 2314 } 2315 2316 List<ParsedActivity> activities = pkg.getActivities(); 2317 int activitiesSize = activities.size(); 2318 for (int index = 0; index < activitiesSize; index++) { 2319 ParsedActivity activity = activities.get(index); 2320 // If the max aspect ratio for the activity has already been set, skip. 2321 if (activity.getMaxAspectRatio() != null) { 2322 continue; 2323 } 2324 2325 // By default we prefer to use a values defined on the activity directly than values 2326 // defined on the application. We do not check the styled attributes on the activity 2327 // as it would have already been set when we processed the activity. We wait to 2328 // process the meta data here since this method is called at the end of processing 2329 // the application and all meta data is guaranteed. 2330 final float activityAspectRatio = activity.getMetaData() != null 2331 ? activity.getMetaData().getFloat(PackageParser.METADATA_MAX_ASPECT_RATIO, 2332 maxAspectRatio) 2333 : maxAspectRatio; 2334 2335 activity.setMaxAspectRatio(activity.getResizeMode(), activityAspectRatio); 2336 } 2337 } 2338 2339 /** 2340 * Sets the min aspect ratio of every child activity that doesn't already have an aspect 2341 * ratio set. 2342 */ 2343 private void setMinAspectRatio(ParsingPackage pkg) { 2344 final float minAspectRatio; 2345 float packageMinAspectRatio = pkg.getMinAspectRatio(); 2346 if (packageMinAspectRatio != 0) { 2347 // Use the application max aspect ration as default if set. 2348 minAspectRatio = packageMinAspectRatio; 2349 } else { 2350 // Default to (1.33) 4:3 aspect ratio for pre-Q apps and unset for Q and greater. 2351 // NOTE: 4:3 was the min aspect ratio Android devices can support pre-Q per the CDD, 2352 // except for watches which always supported 1:1. 2353 minAspectRatio = pkg.getTargetSdkVersion() >= Build.VERSION_CODES.Q 2354 ? 0 2355 : (mCallback != null && mCallback.hasFeature(FEATURE_WATCH)) 2356 ? PackageParser.DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH 2357 : PackageParser.DEFAULT_PRE_Q_MIN_ASPECT_RATIO; 2358 } 2359 2360 List<ParsedActivity> activities = pkg.getActivities(); 2361 int activitiesSize = activities.size(); 2362 for (int index = 0; index < activitiesSize; index++) { 2363 ParsedActivity activity = activities.get(index); 2364 if (activity.getMinAspectRatio() == null) { 2365 activity.setMinAspectRatio(activity.getResizeMode(), minAspectRatio); 2366 } 2367 } 2368 } 2369 2370 private void setSupportsSizeChanges(ParsingPackage pkg) { 2371 final Bundle appMetaData = pkg.getMetaData(); 2372 final boolean supportsSizeChanges = appMetaData != null 2373 && appMetaData.getBoolean(PackageParser.METADATA_SUPPORTS_SIZE_CHANGES, false); 2374 2375 List<ParsedActivity> activities = pkg.getActivities(); 2376 int activitiesSize = activities.size(); 2377 for (int index = 0; index < activitiesSize; index++) { 2378 ParsedActivity activity = activities.get(index); 2379 if (supportsSizeChanges || (activity.getMetaData() != null 2380 && activity.getMetaData().getBoolean( 2381 PackageParser.METADATA_SUPPORTS_SIZE_CHANGES, false))) { 2382 activity.setSupportsSizeChanges(true); 2383 } 2384 } 2385 } 2386 2387 private static ParseResult<ParsingPackage> parseOverlay(ParseInput input, ParsingPackage pkg, 2388 Resources res, XmlResourceParser parser) { 2389 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestResourceOverlay); 2390 try { 2391 String target = sa.getString(R.styleable.AndroidManifestResourceOverlay_targetPackage); 2392 int priority = anInt(0, R.styleable.AndroidManifestResourceOverlay_priority, sa); 2393 2394 if (target == null) { 2395 return input.error("<overlay> does not specify a target package"); 2396 } else if (priority < 0 || priority > 9999) { 2397 return input.error("<overlay> priority must be between 0 and 9999"); 2398 } 2399 2400 // check to see if overlay should be excluded based on system property condition 2401 String propName = sa.getString( 2402 R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyName); 2403 String propValue = sa.getString( 2404 R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyValue); 2405 if (!PackageParser.checkRequiredSystemProperties(propName, propValue)) { 2406 String message = "Skipping target and overlay pair " + target + " and " 2407 + pkg.getBaseCodePath() 2408 + ": overlay ignored due to required system property: " 2409 + propName + " with value: " + propValue; 2410 Slog.i(TAG, message); 2411 return input.skip(message); 2412 } 2413 2414 return input.success(pkg.setOverlay(true) 2415 .setOverlayTarget(target) 2416 .setOverlayPriority(priority) 2417 .setOverlayTargetName( 2418 sa.getString(R.styleable.AndroidManifestResourceOverlay_targetName)) 2419 .setOverlayCategory( 2420 sa.getString(R.styleable.AndroidManifestResourceOverlay_category)) 2421 .setOverlayIsStatic( 2422 bool(false, R.styleable.AndroidManifestResourceOverlay_isStatic, sa))); 2423 } finally { 2424 sa.recycle(); 2425 } 2426 } 2427 2428 private static ParseResult<ParsingPackage> parseProtectedBroadcast(ParseInput input, 2429 ParsingPackage pkg, Resources res, XmlResourceParser parser) { 2430 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProtectedBroadcast); 2431 try { 2432 // Note: don't allow this value to be a reference to a resource 2433 // that may change. 2434 String name = nonResString(R.styleable.AndroidManifestProtectedBroadcast_name, sa); 2435 if (name != null) { 2436 pkg.addProtectedBroadcast(name); 2437 } 2438 return input.success(pkg); 2439 } finally { 2440 sa.recycle(); 2441 } 2442 } 2443 2444 private static ParseResult<ParsingPackage> parseSupportScreens(ParseInput input, 2445 ParsingPackage pkg, Resources res, XmlResourceParser parser) { 2446 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestSupportsScreens); 2447 try { 2448 int requiresSmallestWidthDp = anInt(0, 2449 R.styleable.AndroidManifestSupportsScreens_requiresSmallestWidthDp, sa); 2450 int compatibleWidthLimitDp = anInt(0, 2451 R.styleable.AndroidManifestSupportsScreens_compatibleWidthLimitDp, sa); 2452 int largestWidthLimitDp = anInt(0, 2453 R.styleable.AndroidManifestSupportsScreens_largestWidthLimitDp, sa); 2454 2455 // This is a trick to get a boolean and still able to detect 2456 // if a value was actually set. 2457 return input.success(pkg 2458 .setSupportsSmallScreens( 2459 anInt(1, R.styleable.AndroidManifestSupportsScreens_smallScreens, sa)) 2460 .setSupportsNormalScreens( 2461 anInt(1, R.styleable.AndroidManifestSupportsScreens_normalScreens, sa)) 2462 .setSupportsLargeScreens( 2463 anInt(1, R.styleable.AndroidManifestSupportsScreens_largeScreens, sa)) 2464 .setSupportsExtraLargeScreens( 2465 anInt(1, R.styleable.AndroidManifestSupportsScreens_xlargeScreens, sa)) 2466 .setResizeable( 2467 anInt(1, R.styleable.AndroidManifestSupportsScreens_resizeable, sa)) 2468 .setAnyDensity( 2469 anInt(1, R.styleable.AndroidManifestSupportsScreens_anyDensity, sa)) 2470 .setRequiresSmallestWidthDp(requiresSmallestWidthDp) 2471 .setCompatibleWidthLimitDp(compatibleWidthLimitDp) 2472 .setLargestWidthLimitDp(largestWidthLimitDp)); 2473 } finally { 2474 sa.recycle(); 2475 } 2476 } 2477 2478 private static ParseResult<ParsingPackage> parseInstrumentation(ParseInput input, 2479 ParsingPackage pkg, Resources res, XmlResourceParser parser) 2480 throws XmlPullParserException, IOException { 2481 ParseResult<ParsedInstrumentation> result = ParsedInstrumentationUtils.parseInstrumentation( 2482 pkg, res, parser, PackageParser.sUseRoundIcon, input); 2483 if (result.isError()) { 2484 return input.error(result); 2485 } 2486 return input.success(pkg.addInstrumentation(result.getResult())); 2487 } 2488 2489 private static ParseResult<ParsingPackage> parseOriginalPackage(ParseInput input, 2490 ParsingPackage pkg, Resources res, XmlResourceParser parser) { 2491 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestOriginalPackage); 2492 try { 2493 String orig = sa.getNonConfigurationString( 2494 R.styleable.AndroidManifestOriginalPackage_name, 2495 0); 2496 if (!pkg.getPackageName().equals(orig)) { 2497 if (pkg.getOriginalPackages().isEmpty()) { 2498 pkg.setRealPackage(pkg.getPackageName()); 2499 } 2500 pkg.addOriginalPackage(orig); 2501 } 2502 return input.success(pkg); 2503 } finally { 2504 sa.recycle(); 2505 } 2506 } 2507 2508 private static ParseResult<ParsingPackage> parseAdoptPermissions(ParseInput input, 2509 ParsingPackage pkg, Resources res, XmlResourceParser parser) { 2510 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestOriginalPackage); 2511 try { 2512 String name = nonConfigString(0, R.styleable.AndroidManifestOriginalPackage_name, sa); 2513 if (name != null) { 2514 pkg.addAdoptPermission(name); 2515 } 2516 return input.success(pkg); 2517 } finally { 2518 sa.recycle(); 2519 } 2520 } 2521 2522 private static void convertNewPermissions(ParsingPackage pkg) { 2523 final int NP = PackageParser.NEW_PERMISSIONS.length; 2524 StringBuilder newPermsMsg = null; 2525 for (int ip = 0; ip < NP; ip++) { 2526 final PackageParser.NewPermissionInfo npi 2527 = PackageParser.NEW_PERMISSIONS[ip]; 2528 if (pkg.getTargetSdkVersion() >= npi.sdkVersion) { 2529 break; 2530 } 2531 if (!pkg.getRequestedPermissions().contains(npi.name)) { 2532 if (newPermsMsg == null) { 2533 newPermsMsg = new StringBuilder(128); 2534 newPermsMsg.append(pkg.getPackageName()); 2535 newPermsMsg.append(": compat added "); 2536 } else { 2537 newPermsMsg.append(' '); 2538 } 2539 newPermsMsg.append(npi.name); 2540 pkg.addRequestedPermission(npi.name) 2541 .addImplicitPermission(npi.name); 2542 } 2543 } 2544 if (newPermsMsg != null) { 2545 Slog.i(TAG, newPermsMsg.toString()); 2546 } 2547 } 2548 2549 private static void convertSplitPermissions(ParsingPackage pkg) { 2550 List<SplitPermissionInfoParcelable> splitPermissions; 2551 2552 try { 2553 splitPermissions = ActivityThread.getPermissionManager().getSplitPermissions(); 2554 } catch (RemoteException e) { 2555 throw e.rethrowFromSystemServer(); 2556 } 2557 2558 final int listSize = splitPermissions.size(); 2559 for (int is = 0; is < listSize; is++) { 2560 final SplitPermissionInfoParcelable spi = splitPermissions.get(is); 2561 List<String> requestedPermissions = pkg.getRequestedPermissions(); 2562 if (pkg.getTargetSdkVersion() >= spi.getTargetSdk() 2563 || !requestedPermissions.contains(spi.getSplitPermission())) { 2564 continue; 2565 } 2566 final List<String> newPerms = spi.getNewPermissions(); 2567 for (int in = 0; in < newPerms.size(); in++) { 2568 final String perm = newPerms.get(in); 2569 if (!requestedPermissions.contains(perm)) { 2570 pkg.addRequestedPermission(perm) 2571 .addImplicitPermission(perm); 2572 } 2573 } 2574 } 2575 } 2576 2577 /** 2578 * This is a pre-density application which will get scaled - instead of being pixel perfect. 2579 * This type of application is not resizable. 2580 * 2581 * @param pkg The package which needs to be marked as unresizable. 2582 */ 2583 private static void adjustPackageToBeUnresizeableAndUnpipable(ParsingPackage pkg) { 2584 List<ParsedActivity> activities = pkg.getActivities(); 2585 int activitiesSize = activities.size(); 2586 for (int index = 0; index < activitiesSize; index++) { 2587 ParsedActivity activity = activities.get(index); 2588 activity.setResizeMode(RESIZE_MODE_UNRESIZEABLE) 2589 .setFlags(activity.getFlags() & ~FLAG_SUPPORTS_PICTURE_IN_PICTURE); 2590 } 2591 } 2592 2593 private static ParseResult validateName(ParseInput input, String name, boolean requireSeparator, 2594 boolean requireFilename) { 2595 final int N = name.length(); 2596 boolean hasSep = false; 2597 boolean front = true; 2598 for (int i = 0; i < N; i++) { 2599 final char c = name.charAt(i); 2600 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { 2601 front = false; 2602 continue; 2603 } 2604 if (!front) { 2605 if ((c >= '0' && c <= '9') || c == '_') { 2606 continue; 2607 } 2608 } 2609 if (c == '.') { 2610 hasSep = true; 2611 front = true; 2612 continue; 2613 } 2614 return input.error("bad character '" + c + "'"); 2615 } 2616 if (requireFilename && !FileUtils.isValidExtFilename(name)) { 2617 return input.error("Invalid filename"); 2618 } 2619 return hasSep || !requireSeparator 2620 ? input.success(null) 2621 : input.error("must have at least one '.' separator"); 2622 } 2623 2624 public static ParseResult<Bundle> parseMetaData(ParsingPackage pkg, Resources res, 2625 XmlResourceParser parser, Bundle data, ParseInput input) { 2626 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestMetaData); 2627 try { 2628 if (data == null) { 2629 data = new Bundle(); 2630 } 2631 2632 String name = TextUtils.safeIntern( 2633 nonConfigString(0, R.styleable.AndroidManifestMetaData_name, sa)); 2634 if (name == null) { 2635 return input.error("<meta-data> requires an android:name attribute"); 2636 } 2637 2638 TypedValue v = sa.peekValue(R.styleable.AndroidManifestMetaData_resource); 2639 if (v != null && v.resourceId != 0) { 2640 //Slog.i(TAG, "Meta data ref " + name + ": " + v); 2641 data.putInt(name, v.resourceId); 2642 } else { 2643 v = sa.peekValue(R.styleable.AndroidManifestMetaData_value); 2644 //Slog.i(TAG, "Meta data " + name + ": " + v); 2645 if (v != null) { 2646 if (v.type == TypedValue.TYPE_STRING) { 2647 CharSequence cs = v.coerceToString(); 2648 data.putString(name, cs != null ? cs.toString() : null); 2649 } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) { 2650 data.putBoolean(name, v.data != 0); 2651 } else if (v.type >= TypedValue.TYPE_FIRST_INT 2652 && v.type <= TypedValue.TYPE_LAST_INT) { 2653 data.putInt(name, v.data); 2654 } else if (v.type == TypedValue.TYPE_FLOAT) { 2655 data.putFloat(name, v.getFloat()); 2656 } else { 2657 if (!PackageParser.RIGID_PARSER) { 2658 Slog.w(TAG, 2659 "<meta-data> only supports string, integer, float, color, " 2660 + "boolean, and resource reference types: " 2661 + parser.getName() + " at " 2662 + pkg.getBaseCodePath() + " " 2663 + parser.getPositionDescription()); 2664 } else { 2665 return input.error("<meta-data> only supports string, integer, float, " 2666 + "color, boolean, and resource reference types"); 2667 } 2668 } 2669 } else { 2670 return input.error("<meta-data> requires an android:value " 2671 + "or android:resource attribute"); 2672 } 2673 } 2674 return input.success(data); 2675 } finally { 2676 sa.recycle(); 2677 } 2678 } 2679 2680 /** 2681 * Collect certificates from all the APKs described in the given package. Also asserts that 2682 * all APK contents are signed correctly and consistently. 2683 * 2684 * TODO(b/155513789): Remove this in favor of collecting certificates during the original parse 2685 * call if requested. Leaving this as an optional method for the caller means we have to 2686 * construct a dummy ParseInput. 2687 */ 2688 @CheckResult 2689 public static SigningDetails getSigningDetails(ParsingPackageRead pkg, boolean skipVerify) 2690 throws PackageParserException { 2691 SigningDetails signingDetails = SigningDetails.UNKNOWN; 2692 2693 ParseInput input = ParseTypeImpl.forDefaultParsing().reset(); 2694 2695 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates"); 2696 try { 2697 ParseResult<SigningDetails> result = getSigningDetails( 2698 input, 2699 pkg.getBaseCodePath(), 2700 skipVerify, 2701 pkg.isStaticSharedLibrary(), 2702 signingDetails, 2703 pkg.getTargetSdkVersion() 2704 ); 2705 if (result.isError()) { 2706 throw new PackageParser.PackageParserException(result.getErrorCode(), 2707 result.getErrorMessage(), result.getException()); 2708 } 2709 2710 signingDetails = result.getResult(); 2711 2712 String[] splitCodePaths = pkg.getSplitCodePaths(); 2713 if (!ArrayUtils.isEmpty(splitCodePaths)) { 2714 for (int i = 0; i < splitCodePaths.length; i++) { 2715 result = getSigningDetails( 2716 input, 2717 splitCodePaths[i], 2718 skipVerify, 2719 pkg.isStaticSharedLibrary(), 2720 signingDetails, 2721 pkg.getTargetSdkVersion() 2722 ); 2723 if (result.isError()) { 2724 throw new PackageParser.PackageParserException(result.getErrorCode(), 2725 result.getErrorMessage(), result.getException()); 2726 } 2727 2728 2729 signingDetails = result.getResult(); 2730 } 2731 } 2732 return signingDetails; 2733 } finally { 2734 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); 2735 } 2736 } 2737 2738 @CheckResult 2739 public static ParseResult<SigningDetails> getSigningDetails(ParseInput input, 2740 String baseCodePath, boolean skipVerify, boolean isStaticSharedLibrary, 2741 @NonNull SigningDetails existingSigningDetails, int targetSdk) { 2742 int minSignatureScheme = ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk( 2743 targetSdk); 2744 if (isStaticSharedLibrary) { 2745 // must use v2 signing scheme 2746 minSignatureScheme = SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2; 2747 } 2748 SigningDetails verified; 2749 try { 2750 if (skipVerify) { 2751 // systemDir APKs are already trusted, save time by not verifying; since the 2752 // signature is not verified and some system apps can have their V2+ signatures 2753 // stripped allow pulling the certs from the jar signature. 2754 verified = ApkSignatureVerifier.unsafeGetCertsWithoutVerification( 2755 baseCodePath, SigningDetails.SignatureSchemeVersion.JAR); 2756 } else { 2757 verified = ApkSignatureVerifier.verify(baseCodePath, minSignatureScheme); 2758 } 2759 } catch (PackageParserException e) { 2760 return input.error(PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES, 2761 "Failed collecting certificates for " + baseCodePath, e); 2762 } 2763 2764 // Verify that entries are signed consistently with the first pkg 2765 // we encountered. Note that for splits, certificates may have 2766 // already been populated during an earlier parse of a base APK. 2767 if (existingSigningDetails == SigningDetails.UNKNOWN) { 2768 return input.success(verified); 2769 } else { 2770 if (!Signature.areExactMatch(existingSigningDetails.signatures, verified.signatures)) { 2771 return input.error(INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, 2772 baseCodePath + " has mismatched certificates"); 2773 } 2774 2775 return input.success(existingSigningDetails); 2776 } 2777 } 2778 2779 /* 2780 The following set of methods makes code easier to read by re-ordering the TypedArray methods. 2781 2782 The first parameter is the default, which is the most important to understand for someone 2783 reading through the parsing code. 2784 2785 That's followed by the attribute name, which is usually irrelevant during reading because 2786 it'll look like setSomeValue(true, R.styleable.ReallyLongParentName_SomeValueAttr... and 2787 the "setSomeValue" part is enough to communicate what the line does. 2788 2789 Last comes the TypedArray, which is by far the least important since each try-with-resources 2790 should only have 1. 2791 */ 2792 2793 // Note there is no variant of bool without a defaultValue parameter, since explicit true/false 2794 // is important to specify when adding an attribute. 2795 private static boolean bool(boolean defaultValue, @StyleableRes int attribute, TypedArray sa) { 2796 return sa.getBoolean(attribute, defaultValue); 2797 } 2798 2799 private static float aFloat(float defaultValue, @StyleableRes int attribute, TypedArray sa) { 2800 return sa.getFloat(attribute, defaultValue); 2801 } 2802 2803 private static float aFloat(@StyleableRes int attribute, TypedArray sa) { 2804 return sa.getFloat(attribute, 0f); 2805 } 2806 2807 private static int anInt(int defaultValue, @StyleableRes int attribute, TypedArray sa) { 2808 return sa.getInt(attribute, defaultValue); 2809 } 2810 2811 private static int anInteger(int defaultValue, @StyleableRes int attribute, TypedArray sa) { 2812 return sa.getInteger(attribute, defaultValue); 2813 } 2814 2815 private static int anInt(@StyleableRes int attribute, TypedArray sa) { 2816 return sa.getInt(attribute, 0); 2817 } 2818 2819 @AnyRes 2820 private static int resId(@StyleableRes int attribute, TypedArray sa) { 2821 return sa.getResourceId(attribute, 0); 2822 } 2823 2824 private static String string(@StyleableRes int attribute, TypedArray sa) { 2825 return sa.getString(attribute); 2826 } 2827 2828 private static String nonConfigString(int allowedChangingConfigs, @StyleableRes int attribute, 2829 TypedArray sa) { 2830 return sa.getNonConfigurationString(attribute, allowedChangingConfigs); 2831 } 2832 2833 private static String nonResString(@StyleableRes int index, TypedArray sa) { 2834 return sa.getNonResourceString(index); 2835 } 2836 2837 /** 2838 * Callback interface for retrieving information that may be needed while parsing 2839 * a package. 2840 */ 2841 public interface Callback { 2842 boolean hasFeature(String feature); 2843 2844 ParsingPackage startParsingPackage(@NonNull String packageName, 2845 @NonNull String baseCodePath, @NonNull String codePath, 2846 @NonNull TypedArray manifestArray, boolean isCoreApp); 2847 } 2848 } 2849