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